line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
=head1 NAME |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
Math::Combinatorics - Perform combinations and permutations on lists |
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
=head1 SYNOPSIS |
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
Available as an object oriented API. |
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
use Math::Combinatorics; |
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
my @n = qw(a b c); |
12
|
|
|
|
|
|
|
my $combinat = Math::Combinatorics->new(count => 2, |
13
|
|
|
|
|
|
|
data => [@n], |
14
|
|
|
|
|
|
|
); |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
print "combinations of 2 from: ".join(" ",@n)."\n"; |
17
|
|
|
|
|
|
|
print "------------------------".("--" x scalar(@n))."\n"; |
18
|
|
|
|
|
|
|
while(my @combo = $combinat->next_combination){ |
19
|
|
|
|
|
|
|
print join(' ', @combo)."\n"; |
20
|
|
|
|
|
|
|
} |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
print "\n"; |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
print "permutations of 3 from: ".join(" ",@n)."\n"; |
25
|
|
|
|
|
|
|
print "------------------------".("--" x scalar(@n))."\n"; |
26
|
|
|
|
|
|
|
while(my @permu = $combinat->next_permutation){ |
27
|
|
|
|
|
|
|
print join(' ', @permu)."\n"; |
28
|
|
|
|
|
|
|
} |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
output: |
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
Or available via exported functions 'permute', 'combine', and 'factorial'. |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
use Math::Combinatorics; |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
my @n = qw(a b c); |
37
|
|
|
|
|
|
|
print "combinations of 2 from: ".join(" ",@n)."\n"; |
38
|
|
|
|
|
|
|
print "------------------------".("--" x scalar(@n))."\n"; |
39
|
|
|
|
|
|
|
print join("\n", map { join " ", @$_ } combine(2,@n)),"\n"; |
40
|
|
|
|
|
|
|
print "\n"; |
41
|
|
|
|
|
|
|
print "permutations of 3 from: ".join(" ",@n)."\n"; |
42
|
|
|
|
|
|
|
print "------------------------".("--" x scalar(@n))."\n"; |
43
|
|
|
|
|
|
|
print join("\n", map { join " ", @$_ } permute(@n)),"\n"; |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
Output: |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
combinations of 2 from: a b c |
49
|
|
|
|
|
|
|
------------------------------ |
50
|
|
|
|
|
|
|
a b |
51
|
|
|
|
|
|
|
a c |
52
|
|
|
|
|
|
|
b c |
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
permutations of 3 from: a b c |
55
|
|
|
|
|
|
|
------------------------------ |
56
|
|
|
|
|
|
|
a b c |
57
|
|
|
|
|
|
|
a c b |
58
|
|
|
|
|
|
|
b a c |
59
|
|
|
|
|
|
|
b c a |
60
|
|
|
|
|
|
|
c a b |
61
|
|
|
|
|
|
|
c b a |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
Output from both types of calls is the same, but the object-oriented approach consumes |
64
|
|
|
|
|
|
|
much less memory for large sets. |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
=head1 DESCRIPTION |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
Combinatorics is the branch of mathematics studying the enumeration, combination, |
69
|
|
|
|
|
|
|
and permutation of sets of elements and the mathematical relations that characterize |
70
|
|
|
|
|
|
|
their properties. As a jumping off point, refer to: |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
http://mathworld.wolfram.com/Combinatorics.html |
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
This module provides a pure-perl implementation of nCk, nCRk, nPk, nPRk, !n and n! |
75
|
|
|
|
|
|
|
(combination, multiset, permutation, string, derangement, and factorial, respectively). |
76
|
|
|
|
|
|
|
Functional and object-oriented usages allow problems such as the following to be solved: |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
=over |
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
=item combine - nCk |
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
http://mathworld.wolfram.com/Combination.html |
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
"Fun questions to ask the pizza parlor wait staff: how many possible combinations |
85
|
|
|
|
|
|
|
of 2 toppings can I get on my pizza?". |
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
=item derange - !n |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
http://mathworld.wolfram.com/Derangement.html |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
"A derangement of n ordered objects, denoted !n, is a permutation in which none of the |
92
|
|
|
|
|
|
|
objects appear in their "natural" (i.e., ordered) place." |
93
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
=item permute - nPk |
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
http://mathworld.wolfram.com/Permutation.html |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
"Master Mind Game: ways to arrange pieces of different colors in a |
99
|
|
|
|
|
|
|
certain number of positions, without repetition of a color". |
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
=back |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
Object-oriented usage additionally allows solving these problems by calling L |
104
|
|
|
|
|
|
|
with a B vector: |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
=over |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
=item string - nPRk |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
http://mathworld.wolfram.com/String.html |
111
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
"Morse signals: diferent signals of 3 positions using the two symbols - and .". |
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
$o = Math::Combinatorics->new( count=>3 , data=>[qw(. -)] , frequency=>[3,3] ); |
115
|
|
|
|
|
|
|
while ( my @x = $o->next_multiset ) { |
116
|
|
|
|
|
|
|
my $p = Math::Combinatorics->new( data=>\@x , frequency=>[map{1} @x] ); |
117
|
|
|
|
|
|
|
while ( my @y = $p->next_string ) { |
118
|
|
|
|
|
|
|
#do something |
119
|
|
|
|
|
|
|
} |
120
|
|
|
|
|
|
|
} |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
=item multiset/multichoose - nCRk |
123
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
http://mathworld.wolfram.com/Multiset.html |
125
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
"ways to extract 3 balls at once of a bag with 3 black and 3 white balls". |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
$o = Math::Combinatorics->new( count=>3 , data=>[qw(white black)] , frequency=>[3,3] ); |
129
|
|
|
|
|
|
|
while ( my @x = $o->next_multiset ) { |
130
|
|
|
|
|
|
|
#do something |
131
|
|
|
|
|
|
|
} |
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
=back |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
=head2 EXPORT |
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
the following export tags will bring a single method into the caller's |
138
|
|
|
|
|
|
|
namespace. no symbols are exported by default. see pod documentation below for |
139
|
|
|
|
|
|
|
method descriptions. |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
combine |
142
|
|
|
|
|
|
|
derange |
143
|
|
|
|
|
|
|
multiset |
144
|
|
|
|
|
|
|
permute |
145
|
|
|
|
|
|
|
string |
146
|
|
|
|
|
|
|
factorial |
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
=head1 AUTHOR |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
Allen Day , with algorithmic contributions from Christopher Eltschka and |
151
|
|
|
|
|
|
|
Tye. |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
Copyright (c) 2004-2005 Allen Day. All rights reserved. This program is free software; you |
154
|
|
|
|
|
|
|
can redistribute it and/or modify it under the same terms as Perl itself. |
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
=head1 ACKNOWLEDGEMENTS |
157
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
A sincere thanks to everyone for helping to make this a better module. After initial |
159
|
|
|
|
|
|
|
development I've only had time to accept patches and improvements. Math::Combinatorics |
160
|
|
|
|
|
|
|
continues to be developed and improved by the community. Contributors of note include: |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
For adding new features: Carlos Rica, David Coppit, Carlos Segre, Lyon Lemmens |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
For bug reports: Ying Yang, Joerg Beyer, Marc Logghe, Yunheng Wang, |
165
|
|
|
|
|
|
|
Torsten Seemann, Gerrit Haase, Joern Behre, Lyon Lemmens, Federico Lucifredi |
166
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
=head1 BUGS / TODO |
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
Report them to the author. |
170
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
* Need more extensive unit tests. |
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
* tests for new()'s frequency argment |
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
* A known bug (more of a missing feature, actually) does not allow parameterization of k |
176
|
|
|
|
|
|
|
for nPk in permute(). it is assumed k == n. L for details. You can work |
177
|
|
|
|
|
|
|
around this by making calls to both L and L |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
* Lots of really interesting stuff from Mathworld.Wolfram.com. MathWorld rocks! Expect |
180
|
|
|
|
|
|
|
to see implementation of more concepts from their site, e.g.: |
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
http://mathworld.wolfram.com/BellNumber.html |
183
|
|
|
|
|
|
|
http://mathworld.wolfram.com/StirlingNumberoftheSecondKind.html |
184
|
|
|
|
|
|
|
http://mathworld.wolfram.com/Word.html |
185
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
* Other combinatorics stuff |
187
|
|
|
|
|
|
|
http://en.wikipedia.org/wiki/Catalan_number |
188
|
|
|
|
|
|
|
http://en.wikipedia.org/wiki/Stirling_number |
189
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
=head1 SEE ALSO |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
L |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
L |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
L (alas misnamed, it actually returns permutations on a string). |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
http://perlmonks.thepen.com/29374.html |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
http://groups.google.com/groups?selm=38568F79.13680B86%40physik.tu-muenchen.de&output=gplain |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
=cut |
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
package Math::Combinatorics; |
206
|
|
|
|
|
|
|
|
207
|
3
|
|
|
3
|
|
162407
|
use strict; |
|
3
|
|
|
|
|
8
|
|
|
3
|
|
|
|
|
115
|
|
208
|
3
|
|
|
3
|
|
16
|
use Data::Dumper; |
|
3
|
|
|
|
|
6
|
|
|
3
|
|
|
|
|
13169
|
|
209
|
|
|
|
|
|
|
require Exporter; |
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
our @ISA = qw(Exporter); |
212
|
|
|
|
|
|
|
our @EXPORT = qw( combine derange factorial permute ); |
213
|
|
|
|
|
|
|
our $VERSION = '0.09'; |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
=head1 EXPORTED FUNCTIONS |
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
=head2 combine() |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
Usage : my @combinations = combine($k,@n); |
220
|
|
|
|
|
|
|
Function: implements nCk (n choose k), or n!/(k!*(n-k!)). |
221
|
|
|
|
|
|
|
returns all unique unorderd combinations of k items from set n. |
222
|
|
|
|
|
|
|
items in n are assumed to be character data, and are |
223
|
|
|
|
|
|
|
copied into the return data structure (see "Returns" below). |
224
|
|
|
|
|
|
|
Example : my @n = qw(a b c); |
225
|
|
|
|
|
|
|
my @c = combine(2,@n); |
226
|
|
|
|
|
|
|
print join "\n", map { join " ", @$_ } @c; |
227
|
|
|
|
|
|
|
# prints: |
228
|
|
|
|
|
|
|
# b c |
229
|
|
|
|
|
|
|
# a c |
230
|
|
|
|
|
|
|
# a b |
231
|
|
|
|
|
|
|
Returns : a list of arrays, where each array contains a unique combination |
232
|
|
|
|
|
|
|
of k items from n |
233
|
|
|
|
|
|
|
Args : a list of items to be combined |
234
|
|
|
|
|
|
|
Notes : data is internally assumed to be alphanumeric. this is necessary |
235
|
|
|
|
|
|
|
to efficiently generate combinations of large sets. if you need |
236
|
|
|
|
|
|
|
combinations of non-alphanumeric data, or on data |
237
|
|
|
|
|
|
|
C would not be appropriate, use the |
238
|
|
|
|
|
|
|
object-oriented API. See L and the B option. |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
Identical items are assumed to be non-unique. That is, calling |
241
|
|
|
|
|
|
|
C
|
242
|
|
|
|
|
|
|
L
|
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
=cut |
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
sub combine { |
247
|
2
|
|
|
2
|
1
|
6721
|
my($k,@n) = @_; |
248
|
|
|
|
|
|
|
|
249
|
2
|
|
|
|
|
6
|
my @result = (); |
250
|
|
|
|
|
|
|
|
251
|
2
|
|
|
|
|
17
|
my $c = __PACKAGE__->new(data => [@n], count => $k); |
252
|
2
|
|
|
|
|
9
|
while(my(@combo) = $c->next_combination){ |
253
|
10
|
|
|
|
|
41
|
push @result, [@combo]; |
254
|
|
|
|
|
|
|
} |
255
|
|
|
|
|
|
|
|
256
|
2
|
|
|
|
|
25
|
return @result; |
257
|
|
|
|
|
|
|
} |
258
|
|
|
|
|
|
|
|
259
|
|
|
|
|
|
|
=head2 derange() |
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
Usage : my @deranges = derange(@n); |
262
|
|
|
|
|
|
|
Function: implements !n, a derangement of n items in which none of the |
263
|
|
|
|
|
|
|
items appear in their originally ordered place. |
264
|
|
|
|
|
|
|
Example : my @n = qw(a b c); |
265
|
|
|
|
|
|
|
my @d = derange(@n); |
266
|
|
|
|
|
|
|
print join "\n", map { join " ", @$_ } @d; |
267
|
|
|
|
|
|
|
# prints: |
268
|
|
|
|
|
|
|
# a c b |
269
|
|
|
|
|
|
|
# b a c |
270
|
|
|
|
|
|
|
# b c a |
271
|
|
|
|
|
|
|
# c a b |
272
|
|
|
|
|
|
|
# c b a |
273
|
|
|
|
|
|
|
Returns : a list of arrays, where each array contains a derangement of |
274
|
|
|
|
|
|
|
k items from n (where k == n). |
275
|
|
|
|
|
|
|
Args : a list of items to be deranged. |
276
|
|
|
|
|
|
|
Note : k should really be parameterizable. this will happen |
277
|
|
|
|
|
|
|
in a later version of the module. send me a patch to |
278
|
|
|
|
|
|
|
make that version come out sooner. |
279
|
|
|
|
|
|
|
Notes : data is internally assumed to be alphanumeric. this is necessary |
280
|
|
|
|
|
|
|
to efficiently generate combinations of large sets. if you need |
281
|
|
|
|
|
|
|
combinations of non-alphanumeric data, or on data |
282
|
|
|
|
|
|
|
C would not be appropriate, use the |
283
|
|
|
|
|
|
|
object-oriented API. See L, and the B option. |
284
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
=cut |
286
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
sub derange { |
288
|
1
|
|
|
1
|
1
|
279
|
my(@n) = @_; |
289
|
|
|
|
|
|
|
|
290
|
1
|
|
|
|
|
3
|
my @result = (); |
291
|
|
|
|
|
|
|
|
292
|
1
|
|
|
|
|
4
|
my $c = __PACKAGE__->new(data => [@n]); |
293
|
1
|
|
|
|
|
5
|
while(my(@derange) = $c->next_derangement){ |
294
|
9
|
|
|
|
|
44
|
push @result, [@derange]; |
295
|
|
|
|
|
|
|
} |
296
|
|
|
|
|
|
|
|
297
|
1
|
|
|
|
|
18
|
return @result; |
298
|
|
|
|
|
|
|
} |
299
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
=head2 next_derangement() |
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
Usage : my @derangement = $c->next_derangement(); |
303
|
|
|
|
|
|
|
Function: get derangements for @data. |
304
|
|
|
|
|
|
|
Returns : returns a permutation of items from @data (see L), |
305
|
|
|
|
|
|
|
where none of the items appear in their natural order. repeated calls |
306
|
|
|
|
|
|
|
retrieve all unique derangements of @data elements. a returned empty |
307
|
|
|
|
|
|
|
list signifies all derangements have been iterated. |
308
|
|
|
|
|
|
|
Args : none. |
309
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
=cut |
311
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
sub next_derangement { |
313
|
20
|
|
|
20
|
1
|
62
|
my $self = shift; |
314
|
20
|
|
|
|
|
38
|
my $data = $self->data(); |
315
|
|
|
|
|
|
|
|
316
|
20
|
|
|
|
|
45
|
my $cursor = $self->_permutation_cursor(); |
317
|
20
|
|
|
|
|
27
|
my $values = @$cursor; |
318
|
20
|
100
|
|
|
|
42
|
if($self->{pin}){ |
319
|
2
|
|
|
|
|
4
|
$self->{pin} = 0; |
320
|
|
|
|
|
|
|
|
321
|
2
|
|
|
|
|
2
|
my $i; |
322
|
2
|
|
|
|
|
8
|
for ($i = 1; $i < $values; $i += 2) { |
323
|
4
|
|
|
|
|
7
|
$$cursor[$i - 1] = $i; |
324
|
4
|
|
|
|
|
12
|
$$cursor[$i] = $i - 1; |
325
|
|
|
|
|
|
|
} |
326
|
2
|
50
|
|
|
|
12
|
if ($values % 2 != 0) { |
327
|
0
|
|
|
|
|
0
|
$$cursor[$values - 1] = $values - 3; |
328
|
0
|
|
|
|
|
0
|
$$cursor[$values - 2] = $values - 1; |
329
|
|
|
|
|
|
|
} |
330
|
2
|
|
|
|
|
57
|
goto RESULT; |
331
|
|
|
|
|
|
|
} |
332
|
|
|
|
|
|
|
else { |
333
|
18
|
|
|
|
|
19
|
my $values = @$cursor; |
334
|
18
|
|
|
|
|
17
|
my $i; |
335
|
|
|
|
|
|
|
my @found; # stores for each element if it has been found previously |
336
|
18
|
|
|
|
|
36
|
for ($i = 0; $i < $values; $i++) { $found[$i] = 0 } |
|
72
|
|
|
|
|
139
|
|
337
|
18
|
|
|
|
|
17
|
my $e; |
338
|
18
|
|
|
|
|
21
|
my $elemfound = 0; |
339
|
18
|
|
|
|
|
38
|
for ($i = $values - 1; $i > -1; $i--) { |
340
|
56
|
|
|
|
|
64
|
$found[$$cursor[$i]] = 1; |
341
|
56
|
100
|
|
|
|
86
|
if ($i > $values - 3) { # $values-1 or $values-2 |
342
|
36
|
100
|
|
|
|
58
|
if ($i == $values - 2) { |
343
|
|
|
|
|
|
|
#print "i=$i (values-2)\n";## |
344
|
18
|
|
|
|
|
29
|
$e = $$cursor[$i + 1]; |
345
|
18
|
100
|
100
|
|
|
73
|
if ($e > $$cursor[$i] && $e != $i |
|
|
|
66
|
|
|
|
|
346
|
|
|
|
|
|
|
&& $$cursor[$i] != $i + 1) { |
347
|
4
|
|
|
|
|
6
|
$$cursor[$i + 1] = $$cursor[$i]; |
348
|
4
|
|
|
|
|
5
|
$$cursor[$i] = $e; |
349
|
|
|
|
|
|
|
#print "!\n";## |
350
|
4
|
|
|
|
|
113
|
goto RESULT; |
351
|
|
|
|
|
|
|
} |
352
|
|
|
|
|
|
|
} |
353
|
32
|
|
|
|
|
68
|
next; |
354
|
|
|
|
|
|
|
} |
355
|
20
|
|
|
|
|
42
|
for ($e = $$cursor[$i] + 1; $e < $values; $e++) { |
356
|
22
|
100
|
100
|
|
|
83
|
if ($found[$e] && $e != $i) { |
357
|
12
|
|
|
|
|
13
|
$elemfound = 1; |
358
|
12
|
|
|
|
|
17
|
last; |
359
|
|
|
|
|
|
|
} |
360
|
|
|
|
|
|
|
} |
361
|
20
|
100
|
|
|
|
39
|
last if ($elemfound); |
362
|
|
|
|
|
|
|
} |
363
|
14
|
100
|
|
|
|
53
|
if ($elemfound) { |
364
|
12
|
|
|
|
|
16
|
$$cursor[$i] = $e; |
365
|
12
|
|
|
|
|
18
|
$found[$e] = 0; |
366
|
12
|
|
|
|
|
13
|
$i++; |
367
|
12
|
|
|
|
|
13
|
my $j; |
368
|
|
|
|
|
|
|
my @elems; |
369
|
12
|
|
|
|
|
24
|
for ($j = 0; $j < $values; $j++) { |
370
|
48
|
100
|
|
|
|
98
|
if ($found[$j]) { push(@elems, $j) } |
|
28
|
|
|
|
|
55
|
|
371
|
|
|
|
|
|
|
} |
372
|
12
|
|
|
|
|
26
|
for ($j = 0; $j < @elems; $j++) { |
373
|
24
|
50
|
|
|
|
44
|
if ($elems[$j] != $i) { |
|
|
0
|
|
|
|
|
|
374
|
|
|
|
|
|
|
# if the next is the last and it will be wrong: |
375
|
24
|
100
|
100
|
|
|
91
|
if ($j + 2 == @elems |
376
|
|
|
|
|
|
|
&& $elems[$j + 1] == $i + 1) { |
377
|
|
|
|
|
|
|
# interchange them: |
378
|
4
|
|
|
|
|
5
|
$$cursor[$i] = $elems[$j + 1]; |
379
|
4
|
|
|
|
|
5
|
$$cursor[$i + 1] = $elems[$j]; |
380
|
4
|
|
|
|
|
4
|
last; |
381
|
|
|
|
|
|
|
} |
382
|
20
|
|
|
|
|
31
|
$$cursor[$i] = $elems[$j]; |
383
|
|
|
|
|
|
|
} |
384
|
|
|
|
|
|
|
elsif ($j + 1 < @elems) { |
385
|
|
|
|
|
|
|
# use the next element: |
386
|
0
|
|
|
|
|
0
|
$$cursor[$i] = $elems[$j + 1]; |
387
|
0
|
|
|
|
|
0
|
$elems[$j + 1] = $elems[$j]; |
388
|
|
|
|
|
|
|
} |
389
|
0
|
|
|
|
|
0
|
else { die() } |
390
|
20
|
|
|
|
|
41
|
$i++; |
391
|
|
|
|
|
|
|
} |
392
|
12
|
|
|
|
|
225
|
goto RESULT; |
393
|
|
|
|
|
|
|
} |
394
|
2
|
|
|
|
|
9
|
return (); |
395
|
|
|
|
|
|
|
} |
396
|
18
|
|
|
|
|
19
|
RESULT: |
397
|
|
|
|
|
|
|
# map cursor to data array |
398
|
|
|
|
|
|
|
my @result; |
399
|
18
|
|
|
|
|
30
|
foreach my $c (@$cursor){ |
400
|
72
|
|
|
|
|
70
|
push @result, $${ $data->[$c] }; |
|
72
|
|
|
|
|
148
|
|
401
|
|
|
|
|
|
|
} |
402
|
18
|
|
|
|
|
91
|
return @result; |
403
|
|
|
|
|
|
|
} |
404
|
|
|
|
|
|
|
|
405
|
|
|
|
|
|
|
=head2 factorial() |
406
|
|
|
|
|
|
|
|
407
|
|
|
|
|
|
|
Usage : my $f = factorial(4); #returns 24, or 4*3*2*1 |
408
|
|
|
|
|
|
|
Function: calculates n! (n factorial). |
409
|
|
|
|
|
|
|
Returns : undef if n is non-integer or n < 0 |
410
|
|
|
|
|
|
|
Args : a positive, non-zero integer |
411
|
|
|
|
|
|
|
Note : this function is used internally by combine() and permute() |
412
|
|
|
|
|
|
|
|
413
|
|
|
|
|
|
|
=cut |
414
|
|
|
|
|
|
|
|
415
|
|
|
|
|
|
|
sub factorial { |
416
|
0
|
|
|
0
|
1
|
0
|
my $n = shift; |
417
|
0
|
0
|
0
|
|
|
0
|
return undef unless $n >= 0 and $n == int($n); |
418
|
|
|
|
|
|
|
|
419
|
0
|
|
|
|
|
0
|
my $f; |
420
|
|
|
|
|
|
|
|
421
|
0
|
|
|
|
|
0
|
for($f = 1 ; $n > 0 ; $n--){ |
422
|
0
|
|
|
|
|
0
|
$f *= $n |
423
|
|
|
|
|
|
|
} |
424
|
|
|
|
|
|
|
|
425
|
0
|
|
|
|
|
0
|
return $f; |
426
|
|
|
|
|
|
|
} |
427
|
|
|
|
|
|
|
|
428
|
|
|
|
|
|
|
=head2 permute() |
429
|
|
|
|
|
|
|
|
430
|
|
|
|
|
|
|
Usage : my @permutations = permute(@n); |
431
|
|
|
|
|
|
|
Function: implements nPk (n permute k) (where k == n), or n!/(n-k)! |
432
|
|
|
|
|
|
|
returns all unique permutations of k items from set n |
433
|
|
|
|
|
|
|
(where n == k, see "Note" below). items in n are assumed to |
434
|
|
|
|
|
|
|
be character data, and are copied into the return data |
435
|
|
|
|
|
|
|
structure. |
436
|
|
|
|
|
|
|
Example : my @n = qw(a b c); |
437
|
|
|
|
|
|
|
my @p = permute(@n); |
438
|
|
|
|
|
|
|
print join "\n", map { join " ", @$_ } @p; |
439
|
|
|
|
|
|
|
# prints: |
440
|
|
|
|
|
|
|
# b a c |
441
|
|
|
|
|
|
|
# b c a |
442
|
|
|
|
|
|
|
# c b a |
443
|
|
|
|
|
|
|
# c a b |
444
|
|
|
|
|
|
|
# a c b |
445
|
|
|
|
|
|
|
# a b c |
446
|
|
|
|
|
|
|
Returns : a list of arrays, where each array contains a permutation of |
447
|
|
|
|
|
|
|
k items from n (where k == n). |
448
|
|
|
|
|
|
|
Args : a list of items to be permuted. |
449
|
|
|
|
|
|
|
Note : k should really be parameterizable. this will happen |
450
|
|
|
|
|
|
|
in a later version of the module. send me a patch to |
451
|
|
|
|
|
|
|
make that version come out sooner. |
452
|
|
|
|
|
|
|
Notes : data is internally assumed to be alphanumeric. this is necessary |
453
|
|
|
|
|
|
|
to efficiently generate combinations of large sets. if you need |
454
|
|
|
|
|
|
|
combinations of non-alphanumeric data, or on data |
455
|
|
|
|
|
|
|
C would not be appropriate, use the |
456
|
|
|
|
|
|
|
object-oriented API. See L, and the B option. |
457
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
Identical items are assumed to be non-unique. That is, calling |
459
|
|
|
|
|
|
|
C
|
460
|
|
|
|
|
|
|
L
|
461
|
|
|
|
|
|
|
|
462
|
|
|
|
|
|
|
=cut |
463
|
|
|
|
|
|
|
|
464
|
|
|
|
|
|
|
sub permute { |
465
|
1
|
|
|
1
|
1
|
1210
|
my(@n) = @_; |
466
|
|
|
|
|
|
|
|
467
|
1
|
|
|
|
|
4
|
my @result = (); |
468
|
|
|
|
|
|
|
|
469
|
1
|
|
|
|
|
10
|
my $c = __PACKAGE__->new(data => [@n]); |
470
|
1
|
|
|
|
|
6
|
while(my(@permu) = $c->next_permutation){ |
471
|
24
|
|
|
|
|
112
|
push @result, [@permu]; |
472
|
|
|
|
|
|
|
} |
473
|
|
|
|
|
|
|
|
474
|
1
|
|
|
|
|
18
|
return @result; |
475
|
|
|
|
|
|
|
} |
476
|
|
|
|
|
|
|
|
477
|
|
|
|
|
|
|
=head1 CONSTRUCTOR |
478
|
|
|
|
|
|
|
|
479
|
|
|
|
|
|
|
=cut |
480
|
|
|
|
|
|
|
|
481
|
|
|
|
|
|
|
=head2 new() |
482
|
|
|
|
|
|
|
|
483
|
|
|
|
|
|
|
Usage : my $c = Math::Combinatorics->new( count => 2, #treated as int |
484
|
|
|
|
|
|
|
data => [1,2,3,4] #arrayref or anonymous array |
485
|
|
|
|
|
|
|
); |
486
|
|
|
|
|
|
|
Function: build a new Math::Combinatorics object. |
487
|
|
|
|
|
|
|
Returns : a Math::Combinatorics object |
488
|
|
|
|
|
|
|
Args : count - required for combinatoric functions/methods. number of elements to be |
489
|
|
|
|
|
|
|
present in returned set(s). |
490
|
|
|
|
|
|
|
data - required for combinatoric B permutagenic functions/methods. this is the |
491
|
|
|
|
|
|
|
set elements are chosen from. B: this array is modified in place; make |
492
|
|
|
|
|
|
|
a copy of your array if the order matters in the caller's space. |
493
|
|
|
|
|
|
|
frequency - optional vector of data frequencies. must be the same length as the B |
494
|
|
|
|
|
|
|
constructor argument. These two constructor calls here are equivalent: |
495
|
|
|
|
|
|
|
|
496
|
|
|
|
|
|
|
$a = 'a'; |
497
|
|
|
|
|
|
|
$b = 'b'; |
498
|
|
|
|
|
|
|
|
499
|
|
|
|
|
|
|
Math::Combinatorics->new( count=>2, data=>[\$a,\$a,\$a,\$a,\$a,\$b,\$b] ); |
500
|
|
|
|
|
|
|
Math::Combinatorics->new( count=>2, data=>[\$a,\$b], frequency=>[5,2] ); |
501
|
|
|
|
|
|
|
|
502
|
|
|
|
|
|
|
so why use this? sometimes it's useful to have multiple identical entities in |
503
|
|
|
|
|
|
|
a set (in set theory jargon, this is called a "bag", See L). |
504
|
|
|
|
|
|
|
compare - optional subroutine reference used in sorting elements of the set. examples: |
505
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
#appropriate for character elements |
507
|
|
|
|
|
|
|
compare => sub { $_[0] cmp $_[1] } |
508
|
|
|
|
|
|
|
#appropriate for numeric elements |
509
|
|
|
|
|
|
|
compare => sub { $_[0] <=> $_[1] } |
510
|
|
|
|
|
|
|
#appropriate for object elements, perhaps |
511
|
|
|
|
|
|
|
compare => sub { $_[0]->value <=> $_[1]->value } |
512
|
|
|
|
|
|
|
|
513
|
|
|
|
|
|
|
The default sort mechanism is based on references, and cannot be predicted. |
514
|
|
|
|
|
|
|
Improvements for a more flexible compare() mechanism are most welcome. |
515
|
|
|
|
|
|
|
|
516
|
|
|
|
|
|
|
=cut |
517
|
|
|
|
|
|
|
|
518
|
|
|
|
|
|
|
sub new { |
519
|
19
|
|
|
19
|
1
|
18090
|
my($class,%arg) = @_; |
520
|
19
|
|
|
|
|
76
|
my $self = bless {}, $class; |
521
|
|
|
|
|
|
|
|
522
|
19
|
|
100
|
669
|
|
229
|
$self->{compare} = $arg{compare} || sub { $_[0] cmp $_[1] }; |
|
669
|
|
|
|
|
1795
|
|
523
|
19
|
|
|
|
|
44
|
$self->{count} = $arg{count}; |
524
|
|
|
|
|
|
|
|
525
|
|
|
|
|
|
|
#convert bag to set |
526
|
19
|
|
|
|
|
34
|
my $freq = $arg{frequency}; |
527
|
19
|
100
|
66
|
|
|
116
|
if(ref($freq) eq 'ARRAY' and scalar(@$freq) == scalar(@{$arg{data}})){ |
|
4
|
50
|
|
|
|
21
|
|
528
|
4
|
|
|
|
|
11
|
$self->{frequency}++; |
529
|
4
|
|
|
|
|
9
|
my @bag = @{$arg{data}}; |
|
4
|
|
|
|
|
18
|
|
530
|
4
|
|
|
|
|
9
|
my @set = (); |
531
|
|
|
|
|
|
|
|
532
|
|
|
|
|
|
|
#allow '0 but defined' elements (Yunheng Wang) |
533
|
4
|
|
|
|
|
8
|
foreach my $type ( @bag ) { |
534
|
16
|
|
|
|
|
24
|
my $f = shift @$freq; |
535
|
16
|
50
|
|
|
|
32
|
next if $f < 1; |
536
|
16
|
|
|
|
|
25
|
for(1..$f){ |
537
|
|
|
|
|
|
|
#we push on a reference to make sure, for instance, that objects |
538
|
|
|
|
|
|
|
#are identical and not copied |
539
|
18
|
|
|
|
|
52
|
push @set, \$type; |
540
|
|
|
|
|
|
|
} |
541
|
|
|
|
|
|
|
} |
542
|
4
|
|
|
|
|
11
|
$arg{data} = \@set; |
543
|
|
|
|
|
|
|
} |
544
|
|
|
|
|
|
|
elsif(!ref($freq)){ |
545
|
15
|
|
|
|
|
26
|
$arg{data} = [map { \$_ } @{$arg{data}}]; |
|
72
|
|
|
|
|
165
|
|
|
15
|
|
|
|
|
44
|
|
546
|
|
|
|
|
|
|
} |
547
|
|
|
|
|
|
|
|
548
|
|
|
|
|
|
|
#warn join ' ', @{$arg{data}}; |
549
|
|
|
|
|
|
|
|
550
|
|
|
|
|
|
|
#OK, this is hokey, but I don't have time to fix it properly right now. |
551
|
|
|
|
|
|
|
#We want to allow both user-specified sorting as well as our own |
552
|
|
|
|
|
|
|
#reference-based internal sorting -- the latter only because unit tests |
553
|
|
|
|
|
|
|
#are failing if we don't have it. Additionally, we don't want to require |
554
|
|
|
|
|
|
|
#the triple derefernce necessary for comparison of the pristine data in |
555
|
|
|
|
|
|
|
#the user-supplied compare coderef. The solution for now is to do an |
556
|
|
|
|
|
|
|
#if/else. If you're staring at this please fix it! |
557
|
19
|
|
|
|
|
46
|
my $compare = $self->{compare}; |
558
|
19
|
100
|
|
|
|
48
|
if ( defined $arg{compare} ) { |
559
|
1
|
|
|
|
|
1
|
$self->{data} = [sort {&$compare($$$a,$$$b)} map {\$_} @{$arg{data}}]; |
|
5
|
|
|
|
|
18
|
|
|
4
|
|
|
|
|
9
|
|
|
1
|
|
|
|
|
3
|
|
560
|
|
|
|
|
|
|
} |
561
|
|
|
|
|
|
|
else { |
562
|
18
|
|
|
|
|
33
|
$self->{data} = [sort {&$compare($a,$b)} map {\$_} @{$arg{data}}]; |
|
114
|
|
|
|
|
151
|
|
|
86
|
|
|
|
|
180
|
|
|
18
|
|
|
|
|
34
|
|
563
|
|
|
|
|
|
|
} |
564
|
|
|
|
|
|
|
|
565
|
|
|
|
|
|
|
#warn Dumper($self->{data}); |
566
|
|
|
|
|
|
|
|
567
|
19
|
|
|
|
|
48
|
$self->{cin} = 1; |
568
|
19
|
|
|
|
|
40
|
$self->{pin} = 1; |
569
|
|
|
|
|
|
|
|
570
|
19
|
|
|
|
|
71
|
return $self; |
571
|
|
|
|
|
|
|
} |
572
|
|
|
|
|
|
|
|
573
|
|
|
|
|
|
|
=head1 OBJECT METHODS |
574
|
|
|
|
|
|
|
|
575
|
|
|
|
|
|
|
=cut |
576
|
|
|
|
|
|
|
|
577
|
|
|
|
|
|
|
=head2 next_combination() |
578
|
|
|
|
|
|
|
|
579
|
|
|
|
|
|
|
Usage : my @combo = $c->next_combination(); |
580
|
|
|
|
|
|
|
Function: get combinations of size $count from @data. |
581
|
|
|
|
|
|
|
Returns : returns a combination of $count items from @data (see L). |
582
|
|
|
|
|
|
|
repeated calls retrieve all unique combinations of $count elements. |
583
|
|
|
|
|
|
|
a returned empty list signifies all combinations have been iterated. |
584
|
|
|
|
|
|
|
Note : this method may only be used if a B argument is B |
585
|
|
|
|
|
|
|
given to L, otherwise use L. |
586
|
|
|
|
|
|
|
Args : none. |
587
|
|
|
|
|
|
|
|
588
|
|
|
|
|
|
|
=cut |
589
|
|
|
|
|
|
|
|
590
|
|
|
|
|
|
|
sub next_combination { |
591
|
145
|
|
|
145
|
1
|
618
|
my $self = shift; |
592
|
145
|
50
|
|
|
|
322
|
if ( $self->{frequency} ) { |
593
|
0
|
|
|
|
|
0
|
print STDERR "must use next_multiset() if 'frequency' argument passed to constructor\n"; |
594
|
0
|
|
|
|
|
0
|
return (); |
595
|
|
|
|
|
|
|
} |
596
|
145
|
|
|
|
|
257
|
return $self->_next_combination; |
597
|
|
|
|
|
|
|
} |
598
|
|
|
|
|
|
|
|
599
|
|
|
|
|
|
|
sub _next_combination { |
600
|
163
|
|
|
163
|
|
466
|
my $self = shift; |
601
|
163
|
|
|
|
|
268
|
my $data = $self->data(); |
602
|
163
|
|
|
|
|
273
|
my $combo_end = $self->count(); |
603
|
|
|
|
|
|
|
|
604
|
163
|
|
|
|
|
194
|
my $begin = 0; |
605
|
163
|
|
|
|
|
153
|
my $end = $#{$data} + 1; |
|
163
|
|
|
|
|
862
|
|
606
|
|
|
|
|
|
|
|
607
|
163
|
|
|
|
|
176
|
my @result; |
608
|
|
|
|
|
|
|
|
609
|
163
|
50
|
|
|
|
277
|
return () if scalar(@$data) < $self->count(); |
610
|
|
|
|
|
|
|
|
611
|
163
|
100
|
|
|
|
465
|
if($self->{cin}){ |
612
|
13
|
|
|
|
|
18
|
$self->{cin} = 0; |
613
|
|
|
|
|
|
|
|
614
|
13
|
|
|
|
|
28
|
for(0..$self->count-1){ |
615
|
30
|
|
|
|
|
37
|
push @result, $${ $data->[$_] }; |
|
30
|
|
|
|
|
67
|
|
616
|
|
|
|
|
|
|
} |
617
|
|
|
|
|
|
|
#warn 1; |
618
|
13
|
|
|
|
|
70
|
return @result; |
619
|
|
|
|
|
|
|
} |
620
|
|
|
|
|
|
|
|
621
|
150
|
50
|
33
|
|
|
615
|
if ($combo_end == $begin || $combo_end == $end) { |
622
|
0
|
|
|
|
|
0
|
return (); |
623
|
|
|
|
|
|
|
} |
624
|
|
|
|
|
|
|
|
625
|
150
|
|
|
|
|
172
|
my $combo = $combo_end; |
626
|
150
|
|
|
|
|
235
|
my $total_set; |
627
|
|
|
|
|
|
|
|
628
|
150
|
|
|
|
|
136
|
--$combo; |
629
|
150
|
|
|
|
|
323
|
$total_set = $self->upper_bound($combo_end,$end,$data->[$combo]); |
630
|
150
|
100
|
|
|
|
285
|
if ($total_set != $end) { |
631
|
101
|
|
|
|
|
186
|
$self->swap($combo,$total_set); |
632
|
|
|
|
|
|
|
|
633
|
101
|
|
|
|
|
165
|
for(0..$self->count-1){ |
634
|
204
|
|
|
|
|
204
|
push @result, $${ $data->[$_] }; |
|
204
|
|
|
|
|
402
|
|
635
|
|
|
|
|
|
|
} |
636
|
|
|
|
|
|
|
#warn 2; |
637
|
101
|
|
|
|
|
396
|
return @result; |
638
|
|
|
|
|
|
|
} |
639
|
|
|
|
|
|
|
|
640
|
49
|
|
|
|
|
47
|
--$total_set; |
641
|
49
|
|
|
|
|
107
|
$combo = $self->lower_bound($begin, $combo_end, $data->[$total_set]); |
642
|
|
|
|
|
|
|
|
643
|
49
|
100
|
|
|
|
99
|
if ($combo == $begin) { |
644
|
12
|
|
|
|
|
27
|
$self->rotate($begin, $combo_end, $end); |
645
|
|
|
|
|
|
|
#warn 3; |
646
|
12
|
|
|
|
|
46
|
return (); |
647
|
|
|
|
|
|
|
} |
648
|
|
|
|
|
|
|
|
649
|
37
|
|
|
|
|
37
|
my $combo_next = $combo; |
650
|
37
|
|
|
|
|
35
|
--$combo; |
651
|
37
|
|
|
|
|
78
|
$total_set = $self->upper_bound($combo_end, $end, $data->[$combo]); |
652
|
|
|
|
|
|
|
|
653
|
37
|
|
|
|
|
42
|
my $sort_pos = $end; |
654
|
37
|
|
|
|
|
46
|
$sort_pos += $combo_end - $total_set - 1; |
655
|
|
|
|
|
|
|
|
656
|
37
|
|
|
|
|
79
|
$self->rotate($combo_next, $total_set, $end); |
657
|
37
|
|
|
|
|
66
|
$self->rotate($combo, $combo_next, $end); |
658
|
37
|
|
|
|
|
74
|
$self->rotate($combo_end, $sort_pos, $end); |
659
|
|
|
|
|
|
|
|
660
|
37
|
|
|
|
|
70
|
for(0..$self->count-1){ |
661
|
78
|
|
|
|
|
698
|
push @result, $${ $data->[$_] }; |
|
78
|
|
|
|
|
167
|
|
662
|
|
|
|
|
|
|
} |
663
|
|
|
|
|
|
|
#warn 4; |
664
|
37
|
|
|
|
|
164
|
return @result; |
665
|
|
|
|
|
|
|
} |
666
|
|
|
|
|
|
|
|
667
|
|
|
|
|
|
|
=head2 next_multiset() |
668
|
|
|
|
|
|
|
|
669
|
|
|
|
|
|
|
Usage : my @multiset = $c->next_multiset(); |
670
|
|
|
|
|
|
|
Function: get multisets for @data. |
671
|
|
|
|
|
|
|
Returns : returns a multiset of items from @data (see L). |
672
|
|
|
|
|
|
|
a multiset is a special type of combination where the set from which |
673
|
|
|
|
|
|
|
combinations are drawn contains items that are indistinguishable. use |
674
|
|
|
|
|
|
|
L when a B argument is passed to L. |
675
|
|
|
|
|
|
|
repeated calls retrieve all unique multisets of @data elements. a |
676
|
|
|
|
|
|
|
returned empty list signifies all multisets have been iterated. |
677
|
|
|
|
|
|
|
Note : this method may only be used if a B argument is given to |
678
|
|
|
|
|
|
|
L, otherwise use L. |
679
|
|
|
|
|
|
|
Args : none. |
680
|
|
|
|
|
|
|
|
681
|
|
|
|
|
|
|
=cut |
682
|
|
|
|
|
|
|
|
683
|
|
|
|
|
|
|
sub next_multiset { |
684
|
15
|
|
|
15
|
1
|
78
|
my $self = shift; |
685
|
|
|
|
|
|
|
|
686
|
15
|
50
|
|
|
|
34
|
if ( ! $self->{frequency} ) { |
687
|
0
|
|
|
|
|
0
|
print STDERR "must use next_combination() if 'frequency' argument not passed to constructor\n"; |
688
|
0
|
|
|
|
|
0
|
return (); |
689
|
|
|
|
|
|
|
} |
690
|
|
|
|
|
|
|
|
691
|
15
|
|
|
|
|
27
|
my $data = $self->data(); |
692
|
15
|
|
|
|
|
30
|
my $compare = $self->compare(); |
693
|
|
|
|
|
|
|
|
694
|
15
|
|
|
|
|
42
|
while ( my @combo = $self->_next_combination ) { |
695
|
16
|
|
|
|
|
97
|
my $x = join '', map {scalar($$_)} sort @$data; |
|
74
|
|
|
|
|
131
|
|
696
|
16
|
|
|
|
|
36
|
my $y = join '', map {scalar($_) } sort @combo; |
|
32
|
|
|
|
|
58
|
|
697
|
|
|
|
|
|
|
|
698
|
16
|
100
|
|
|
|
67
|
next if $self->{'cache_multiset'}{$y}++; |
699
|
13
|
|
|
|
|
53
|
return @combo; |
700
|
|
|
|
|
|
|
} |
701
|
2
|
|
|
|
|
3
|
$self->{'cache_multiset'} = undef; |
702
|
2
|
|
|
|
|
9
|
return (); |
703
|
|
|
|
|
|
|
} |
704
|
|
|
|
|
|
|
|
705
|
|
|
|
|
|
|
=head2 next_permutation() |
706
|
|
|
|
|
|
|
|
707
|
|
|
|
|
|
|
Usage : my @permu = $c->next_permutation(); |
708
|
|
|
|
|
|
|
Function: get permutations of elements in @data. |
709
|
|
|
|
|
|
|
Returns : returns a permutation of items from @data (see L). |
710
|
|
|
|
|
|
|
repeated calls retrieve all unique permutations of @data elements. |
711
|
|
|
|
|
|
|
a returned empty list signifies all permutations have been iterated. |
712
|
|
|
|
|
|
|
Note : this method may only be used if a B argument is B |
713
|
|
|
|
|
|
|
given to L, otherwise use L. |
714
|
|
|
|
|
|
|
Args : none. |
715
|
|
|
|
|
|
|
|
716
|
|
|
|
|
|
|
=cut |
717
|
|
|
|
|
|
|
|
718
|
|
|
|
|
|
|
sub next_permutation { |
719
|
50
|
|
|
50
|
1
|
150
|
my $self = shift; |
720
|
50
|
50
|
|
|
|
107
|
if ( $self->{frequency} ) { |
721
|
0
|
|
|
|
|
0
|
print STDERR "must use next_string() if 'frequency' argument passed to constructor\n"; |
722
|
0
|
|
|
|
|
0
|
return (); |
723
|
|
|
|
|
|
|
} |
724
|
50
|
|
|
|
|
86
|
return $self->_next_permutation; |
725
|
|
|
|
|
|
|
} |
726
|
|
|
|
|
|
|
|
727
|
|
|
|
|
|
|
sub _next_permutation { |
728
|
196
|
|
|
196
|
|
233
|
my $self = shift; |
729
|
196
|
|
|
|
|
316
|
my $data = $self->data(); |
730
|
|
|
|
|
|
|
|
731
|
196
|
100
|
|
|
|
426
|
if($self->{pin}){ |
732
|
4
|
|
|
|
|
7
|
$self->{pin} = 0; |
733
|
4
|
|
|
|
|
27
|
return map {$$$_} @$data; |
|
17
|
|
|
|
|
44
|
|
734
|
|
|
|
|
|
|
} |
735
|
|
|
|
|
|
|
|
736
|
192
|
|
|
|
|
8964
|
my $cursor = $self->_permutation_cursor(); |
737
|
|
|
|
|
|
|
|
738
|
192
|
|
|
|
|
725
|
my $last= $#{$cursor}; |
|
192
|
|
|
|
|
278
|
|
739
|
|
|
|
|
|
|
|
740
|
192
|
50
|
|
|
|
377
|
if($last < 1){ |
741
|
0
|
|
|
|
|
0
|
return (); |
742
|
|
|
|
|
|
|
} |
743
|
|
|
|
|
|
|
|
744
|
|
|
|
|
|
|
# Find last item not in reverse-sorted order: |
745
|
192
|
|
|
|
|
237
|
my $i = $last - 1; |
746
|
192
|
|
100
|
|
|
1244
|
$i-- while 0 <= $i && $cursor->[$i] >= $cursor->[$i+1]; |
747
|
|
|
|
|
|
|
|
748
|
192
|
100
|
|
|
|
391
|
if($i == -1){ |
749
|
4
|
|
|
|
|
17
|
return (); |
750
|
|
|
|
|
|
|
} |
751
|
|
|
|
|
|
|
|
752
|
|
|
|
|
|
|
|
753
|
|
|
|
|
|
|
# Re-sort the reversely-sorted tail of the list: |
754
|
188
|
100
|
|
|
|
1055
|
@{$cursor}[$i+1..$last] = reverse @{$cursor}[$i+1..$last] |
|
92
|
|
|
|
|
173
|
|
|
92
|
|
|
|
|
170
|
|
755
|
|
|
|
|
|
|
if $cursor->[$i+1] > $cursor->[$last]; |
756
|
|
|
|
|
|
|
|
757
|
|
|
|
|
|
|
# Find next item that will make us "greater": |
758
|
188
|
|
|
|
|
237
|
my $j = $i+1; |
759
|
188
|
|
|
|
|
486
|
$j++ while $cursor->[$i] >= $cursor->[$j]; |
760
|
|
|
|
|
|
|
|
761
|
|
|
|
|
|
|
# Swap: |
762
|
188
|
|
|
|
|
216
|
@{$cursor}[$i,$j] = @{$cursor}[$j,$i]; |
|
188
|
|
|
|
|
6419
|
|
|
188
|
|
|
|
|
271
|
|
763
|
|
|
|
|
|
|
|
764
|
|
|
|
|
|
|
# map cursor to data array |
765
|
188
|
|
|
|
|
228
|
my @result; |
766
|
188
|
|
|
|
|
8377
|
foreach my $c (@$cursor){ |
767
|
871
|
|
|
|
|
1216
|
push @result, $${ $data->[$c] }; |
|
871
|
|
|
|
|
1693
|
|
768
|
|
|
|
|
|
|
} |
769
|
188
|
|
|
|
|
1192
|
return @result; |
770
|
|
|
|
|
|
|
} |
771
|
|
|
|
|
|
|
|
772
|
|
|
|
|
|
|
=head2 next_string() |
773
|
|
|
|
|
|
|
|
774
|
|
|
|
|
|
|
Usage : my @string = $c->next_string(); |
775
|
|
|
|
|
|
|
Function: get strings for @data. |
776
|
|
|
|
|
|
|
Returns : returns a multiset of items from @data (see L). |
777
|
|
|
|
|
|
|
a multiset is a special type of permutation where the set from which |
778
|
|
|
|
|
|
|
combinations are drawn contains items that are indistinguishable. use |
779
|
|
|
|
|
|
|
L when a B argument is passed to L. |
780
|
|
|
|
|
|
|
repeated calls retrieve all unique multisets of @data elements. a |
781
|
|
|
|
|
|
|
returned empty list signifies all strings have been iterated. |
782
|
|
|
|
|
|
|
Note : this method may only be used if a B argument is given to |
783
|
|
|
|
|
|
|
L, otherwise use L. |
784
|
|
|
|
|
|
|
Args : none. |
785
|
|
|
|
|
|
|
|
786
|
|
|
|
|
|
|
=cut |
787
|
|
|
|
|
|
|
|
788
|
|
|
|
|
|
|
sub next_string { |
789
|
86
|
|
|
86
|
1
|
460
|
my $self = shift; |
790
|
86
|
|
|
|
|
151
|
my $data = $self->data(); |
791
|
|
|
|
|
|
|
|
792
|
86
|
50
|
|
|
|
192
|
if ( ! $self->{frequency} ) { |
793
|
0
|
|
|
|
|
0
|
print STDERR "must use next_permutation() if 'frequency' argument not passed to constructor\n"; |
794
|
0
|
|
|
|
|
0
|
return (); |
795
|
|
|
|
|
|
|
} |
796
|
|
|
|
|
|
|
|
797
|
|
|
|
|
|
|
|
798
|
86
|
|
|
|
|
159
|
while ( my @permu = $self->_next_permutation ) { |
799
|
144
|
|
|
|
|
216
|
my $x = join '', map {scalar($$_)} @$data; |
|
696
|
|
|
|
|
1184
|
|
800
|
144
|
|
|
|
|
236
|
my $y = join '', map {scalar($_) } @permu; |
|
696
|
|
|
|
|
968
|
|
801
|
|
|
|
|
|
|
|
802
|
144
|
100
|
|
|
|
669
|
next if $self->{'cache_string'}{$y}++; |
803
|
84
|
|
|
|
|
381
|
return @permu; |
804
|
|
|
|
|
|
|
} |
805
|
|
|
|
|
|
|
|
806
|
2
|
|
|
|
|
3
|
$self->{'cache_string'} = undef; |
807
|
2
|
|
|
|
|
34
|
return (); |
808
|
|
|
|
|
|
|
} |
809
|
|
|
|
|
|
|
|
810
|
|
|
|
|
|
|
=head1 INTERNAL FUNCTIONS AND METHODS |
811
|
|
|
|
|
|
|
|
812
|
|
|
|
|
|
|
=head2 sum() |
813
|
|
|
|
|
|
|
|
814
|
|
|
|
|
|
|
Usage : my $sum = sum(1,2,3); # returns 6 |
815
|
|
|
|
|
|
|
Function: sums a list of integers. non-integer list elements are ignored |
816
|
|
|
|
|
|
|
Returns : sum of integer items in arguments passed in |
817
|
|
|
|
|
|
|
Args : a list of integers |
818
|
|
|
|
|
|
|
Note : this function is used internally by combine() |
819
|
|
|
|
|
|
|
|
820
|
|
|
|
|
|
|
=cut |
821
|
|
|
|
|
|
|
|
822
|
|
|
|
|
|
|
sub sum { |
823
|
0
|
|
|
0
|
1
|
0
|
my $sum = 0; |
824
|
0
|
|
|
|
|
0
|
foreach my $i (@_){ |
825
|
0
|
0
|
|
|
|
0
|
$sum += $i if $i == int($i); |
826
|
|
|
|
|
|
|
} |
827
|
0
|
|
|
|
|
0
|
return $sum; |
828
|
|
|
|
|
|
|
} |
829
|
|
|
|
|
|
|
|
830
|
|
|
|
|
|
|
=head2 compare() |
831
|
|
|
|
|
|
|
|
832
|
|
|
|
|
|
|
Usage : $obj->compare() |
833
|
|
|
|
|
|
|
Function: internal, undocumented. holds a comparison coderef. |
834
|
|
|
|
|
|
|
Returns : value of compare (a coderef) |
835
|
|
|
|
|
|
|
|
836
|
|
|
|
|
|
|
|
837
|
|
|
|
|
|
|
=cut |
838
|
|
|
|
|
|
|
|
839
|
|
|
|
|
|
|
sub compare { |
840
|
251
|
|
|
251
|
1
|
276
|
my($self,$val) = @_; |
841
|
251
|
|
|
|
|
438
|
return $self->{'compare'}; |
842
|
|
|
|
|
|
|
} |
843
|
|
|
|
|
|
|
|
844
|
|
|
|
|
|
|
|
845
|
|
|
|
|
|
|
=head2 count() |
846
|
|
|
|
|
|
|
|
847
|
|
|
|
|
|
|
Usage : $obj->count() |
848
|
|
|
|
|
|
|
Function: internal, undocumented. holds the "k" in nCk or nPk. |
849
|
|
|
|
|
|
|
Returns : value of count (an int) |
850
|
|
|
|
|
|
|
|
851
|
|
|
|
|
|
|
=cut |
852
|
|
|
|
|
|
|
|
853
|
|
|
|
|
|
|
sub count { |
854
|
477
|
|
|
477
|
1
|
526
|
my($self) = @_; |
855
|
477
|
|
|
|
|
1045
|
return $self->{'count'}; |
856
|
|
|
|
|
|
|
} |
857
|
|
|
|
|
|
|
|
858
|
|
|
|
|
|
|
|
859
|
|
|
|
|
|
|
=head2 data() |
860
|
|
|
|
|
|
|
|
861
|
|
|
|
|
|
|
Usage : $obj->data() |
862
|
|
|
|
|
|
|
Function: internal, undocumented. holds the set "n" in nCk or nPk. |
863
|
|
|
|
|
|
|
Returns : value of data (an arrayref) |
864
|
|
|
|
|
|
|
|
865
|
|
|
|
|
|
|
=cut |
866
|
|
|
|
|
|
|
|
867
|
|
|
|
|
|
|
sub data { |
868
|
1455
|
|
|
1455
|
1
|
1636
|
my($self) = @_; |
869
|
1455
|
|
|
|
|
2350
|
return $self->{'data'}; |
870
|
|
|
|
|
|
|
} |
871
|
|
|
|
|
|
|
|
872
|
|
|
|
|
|
|
|
873
|
|
|
|
|
|
|
=head2 swap() |
874
|
|
|
|
|
|
|
|
875
|
|
|
|
|
|
|
internal, undocumented. |
876
|
|
|
|
|
|
|
|
877
|
|
|
|
|
|
|
=cut |
878
|
|
|
|
|
|
|
|
879
|
|
|
|
|
|
|
sub swap { |
880
|
610
|
|
|
610
|
1
|
887
|
my $self = shift; |
881
|
610
|
|
|
|
|
572
|
my $first = shift; |
882
|
610
|
|
|
|
|
636
|
my $second = shift; |
883
|
610
|
|
|
|
|
1912
|
my $data = $self->data(); |
884
|
|
|
|
|
|
|
|
885
|
610
|
|
|
|
|
802
|
my $temp = $data->[$first]; |
886
|
610
|
|
|
|
|
680
|
$data->[$first] = $data->[$second]; |
887
|
610
|
|
|
|
|
966
|
$data->[$second] = $temp; |
888
|
|
|
|
|
|
|
} |
889
|
|
|
|
|
|
|
|
890
|
|
|
|
|
|
|
=head2 reverse() |
891
|
|
|
|
|
|
|
|
892
|
|
|
|
|
|
|
internal, undocumented. |
893
|
|
|
|
|
|
|
|
894
|
|
|
|
|
|
|
=cut |
895
|
|
|
|
|
|
|
|
896
|
|
|
|
|
|
|
sub reverse { |
897
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
898
|
0
|
|
|
|
|
0
|
my $first = shift; |
899
|
0
|
|
|
|
|
0
|
my $last = shift; |
900
|
0
|
|
|
|
|
0
|
my $data = $self->data(); |
901
|
|
|
|
|
|
|
|
902
|
0
|
|
|
|
|
0
|
while (1) { |
903
|
0
|
0
|
0
|
|
|
0
|
if ($first == $last || $first == --$last) { |
904
|
0
|
|
|
|
|
0
|
return; |
905
|
|
|
|
|
|
|
} else { |
906
|
0
|
|
|
|
|
0
|
$self->swap($first++, $last); |
907
|
|
|
|
|
|
|
} |
908
|
|
|
|
|
|
|
} |
909
|
|
|
|
|
|
|
} |
910
|
|
|
|
|
|
|
|
911
|
|
|
|
|
|
|
=head2 rotate() |
912
|
|
|
|
|
|
|
|
913
|
|
|
|
|
|
|
internal, undocumented. |
914
|
|
|
|
|
|
|
|
915
|
|
|
|
|
|
|
=cut |
916
|
|
|
|
|
|
|
|
917
|
|
|
|
|
|
|
sub rotate { |
918
|
123
|
|
|
123
|
1
|
135
|
my $self = shift; |
919
|
123
|
|
|
|
|
127
|
my $first = shift; |
920
|
123
|
|
|
|
|
116
|
my $middle = shift; |
921
|
123
|
|
|
|
|
114
|
my $last = shift; |
922
|
123
|
|
|
|
|
307
|
my $data = $self->data(); |
923
|
|
|
|
|
|
|
|
924
|
123
|
100
|
66
|
|
|
477
|
if ($first == $middle || $last == $middle) { |
925
|
14
|
|
|
|
|
23
|
return; |
926
|
|
|
|
|
|
|
} |
927
|
|
|
|
|
|
|
|
928
|
109
|
|
|
|
|
111
|
my $first2 = $middle; |
929
|
|
|
|
|
|
|
|
930
|
109
|
|
|
|
|
108
|
do { |
931
|
412
|
|
|
|
|
849
|
$self->swap($first++, $first2++); |
932
|
|
|
|
|
|
|
|
933
|
412
|
100
|
|
|
|
969
|
if ($first == $middle) { |
934
|
305
|
|
|
|
|
1248
|
$middle = $first2; |
935
|
|
|
|
|
|
|
} |
936
|
|
|
|
|
|
|
} while ($first2 != $last); |
937
|
|
|
|
|
|
|
|
938
|
109
|
|
|
|
|
139
|
$first2 = $middle; |
939
|
|
|
|
|
|
|
|
940
|
109
|
|
|
|
|
248
|
while ($first2 != $last) { |
941
|
97
|
|
|
|
|
197
|
$self->swap($first++, $first2++); |
942
|
97
|
100
|
|
|
|
258
|
if ($first == $middle) { |
|
|
100
|
|
|
|
|
|
943
|
46
|
|
|
|
|
112
|
$middle = $first2; |
944
|
|
|
|
|
|
|
} elsif ($first2 == $last) { |
945
|
37
|
|
|
|
|
74
|
$first2 = $middle; |
946
|
|
|
|
|
|
|
} |
947
|
|
|
|
|
|
|
} |
948
|
|
|
|
|
|
|
} |
949
|
|
|
|
|
|
|
|
950
|
|
|
|
|
|
|
=head2 upper_bound() |
951
|
|
|
|
|
|
|
|
952
|
|
|
|
|
|
|
internal, undocumented. |
953
|
|
|
|
|
|
|
|
954
|
|
|
|
|
|
|
=cut |
955
|
|
|
|
|
|
|
|
956
|
|
|
|
|
|
|
sub upper_bound { |
957
|
187
|
|
|
187
|
1
|
207
|
my $self = shift; |
958
|
187
|
|
|
|
|
180
|
my $first = shift; |
959
|
187
|
|
|
|
|
191
|
my $last = shift; |
960
|
187
|
|
|
|
|
187
|
my $value = shift; |
961
|
187
|
|
|
|
|
308
|
my $compare = $self->compare(); |
962
|
187
|
|
|
|
|
421
|
my $data = $self->data(); |
963
|
|
|
|
|
|
|
|
964
|
187
|
|
|
|
|
238
|
my $len = $last - $first; |
965
|
187
|
|
|
|
|
185
|
my $half; |
966
|
|
|
|
|
|
|
my $middle; |
967
|
|
|
|
|
|
|
|
968
|
187
|
|
|
|
|
365
|
while ($len > 0) { |
969
|
457
|
|
|
|
|
505
|
$half = $len >> 1; |
970
|
457
|
|
|
|
|
421
|
$middle = $first; |
971
|
457
|
|
|
|
|
428
|
$middle += $half; |
972
|
|
|
|
|
|
|
|
973
|
457
|
100
|
|
|
|
752
|
if (&$compare($value,$data->[$middle]) == -1) { |
974
|
210
|
|
|
|
|
587
|
$len = $half; |
975
|
|
|
|
|
|
|
} else { |
976
|
247
|
|
|
|
|
294
|
$first = $middle; |
977
|
247
|
|
|
|
|
234
|
++$first; |
978
|
247
|
|
|
|
|
749
|
$len = $len - $half - 1; |
979
|
|
|
|
|
|
|
} |
980
|
|
|
|
|
|
|
} |
981
|
|
|
|
|
|
|
|
982
|
187
|
|
|
|
|
342
|
return $first; |
983
|
|
|
|
|
|
|
} |
984
|
|
|
|
|
|
|
|
985
|
|
|
|
|
|
|
=head2 lower_bound() |
986
|
|
|
|
|
|
|
|
987
|
|
|
|
|
|
|
internal, undocumented. |
988
|
|
|
|
|
|
|
|
989
|
|
|
|
|
|
|
=cut |
990
|
|
|
|
|
|
|
|
991
|
|
|
|
|
|
|
sub lower_bound { |
992
|
49
|
|
|
49
|
1
|
53
|
my $self = shift; |
993
|
49
|
|
|
|
|
52
|
my $first = shift; |
994
|
49
|
|
|
|
|
46
|
my $last = shift; |
995
|
49
|
|
|
|
|
47
|
my $value = shift; |
996
|
49
|
|
|
|
|
84
|
my $compare = $self->compare(); |
997
|
49
|
|
|
|
|
84
|
my $data = $self->data(); |
998
|
|
|
|
|
|
|
|
999
|
49
|
|
|
|
|
61
|
my $len = $last - $first; |
1000
|
49
|
|
|
|
|
55
|
my $half; |
1001
|
|
|
|
|
|
|
my $middle; |
1002
|
|
|
|
|
|
|
|
1003
|
49
|
|
|
|
|
112
|
while ($len > 0) { |
1004
|
98
|
|
|
|
|
92
|
$half = $len >> 1; |
1005
|
98
|
|
|
|
|
90
|
$middle = $first; |
1006
|
98
|
|
|
|
|
90
|
$middle += $half; |
1007
|
|
|
|
|
|
|
|
1008
|
98
|
100
|
|
|
|
165
|
if (&$compare($data->[$middle],$value) == -1) { |
1009
|
37
|
|
|
|
|
34
|
$first = $middle; |
1010
|
37
|
|
|
|
|
37
|
++$first; |
1011
|
37
|
|
|
|
|
89
|
$len = $len - $half - 1; |
1012
|
|
|
|
|
|
|
} else { |
1013
|
61
|
|
|
|
|
131
|
$len = $half; |
1014
|
|
|
|
|
|
|
} |
1015
|
|
|
|
|
|
|
} |
1016
|
|
|
|
|
|
|
|
1017
|
49
|
|
|
|
|
82
|
return $first; |
1018
|
|
|
|
|
|
|
} |
1019
|
|
|
|
|
|
|
|
1020
|
|
|
|
|
|
|
=head2 _permutation_cursor() |
1021
|
|
|
|
|
|
|
|
1022
|
|
|
|
|
|
|
Usage : $obj->_permutation_cursor() |
1023
|
|
|
|
|
|
|
Function: internal method. cursor on permutation iterator order. |
1024
|
|
|
|
|
|
|
Returns : value of _permutation_cursor (an arrayref) |
1025
|
|
|
|
|
|
|
Args : none |
1026
|
|
|
|
|
|
|
|
1027
|
|
|
|
|
|
|
=cut |
1028
|
|
|
|
|
|
|
|
1029
|
|
|
|
|
|
|
sub _permutation_cursor { |
1030
|
212
|
|
|
212
|
|
278
|
my($self,$val) = @_; |
1031
|
|
|
|
|
|
|
|
1032
|
212
|
100
|
|
|
|
426
|
if(!$self->{'_permutation_cursor'}){ |
1033
|
6
|
|
|
|
|
16
|
my $data = $self->data(); |
1034
|
6
|
|
|
|
|
11
|
my @tmp = (); |
1035
|
6
|
|
|
|
|
8
|
my $i = 0; |
1036
|
6
|
|
|
|
|
32
|
push @tmp, $i++ foreach @$data; |
1037
|
6
|
|
|
|
|
20
|
$self->{'_permutation_cursor'} = \@tmp; |
1038
|
|
|
|
|
|
|
} |
1039
|
|
|
|
|
|
|
|
1040
|
212
|
|
|
|
|
343
|
return $self->{'_permutation_cursor'}; |
1041
|
|
|
|
|
|
|
} |
1042
|
|
|
|
|
|
|
|
1043
|
|
|
|
|
|
|
1; |
1044
|
|
|
|
|
|
|
|