aartist has asked for the
wisdom of the Perl Monks concerning the following question:
I am looking for combinations from multiple sets. For exmaple, I need 3 letters from 'a'..'z' and 2 digits from '0'..'9'. I can get single combination with Math::Combinatorics easily. Is there one for multiple set? ( Above sets are just examples, so a quick solution, for above case may not be useful).
Update:
My sets can overlap too. For exmaple I may have 4 sets like '0..7','16', '612',and '3..9'. I also need to eliminate duplicate items. In the case of overlapping sets, every resulting row should satisfy the initial condition. ie.. if I ask for 2 from '0..7' and 3 from '6..12', I should NOT have "6,7,6,7,8" or even "4,5,6,7,8" ( Here 0..7 has 4 entries rather than 2, as asked for.)
Re: Multiple Combinatorics
by davido (Archbishop) on Apr 15, 2012 at 08:46 UTC

I hope I'm understanding the problem. Regardless, it was an interesting puzzle (and a great question).
I understand you to be saying that you want all possible merges of the combinations of set A with the combinations of set B. And I think that by mentioning the potential for overlap you're saying that any merge that would result in an overlap should be disqualified. I don't think it is sufficient to just generate all combinations of set A, and all combinations of set B, merge, and shuffle. That would not result in a result set of all possible nonoverlapping merges.
My solution creates all possible combinations of set A (3 at a time), and then iterates over that list in an outer loop while merging all possible combinations of set B (taken 2 at a time) that don't result in an overlap of any elements. The implementation sacrifices speed efficiency in favor of memory efficiency; we never actually hold onto any full list of combinations of either set A or set B. We just use the iterators for each set. That means that the set B combination object will be created and destroyed on every iteration from set A.
Here's the code:
use strict;
use warnings;
use Math::Combinatorics;
use List::Util qw( first );
my @A = 'A' .. 'Z';
my @B = 0 .. 9;
my $count = 0;
my $comb_A = Math::Combinatorics>new( count => 3, data => [@A] );
while( my @combo_A = $comb_A>next_combination ) {
my $comb_B = Math::Combinatorics>new( count => 2, data => [@B] );
INNER: while( my @combo_B = $comb_B>next_combination ) {
my %overlap;
@overlap{@combo_B} = ();
next INNER if first{ exists $overlap{$_} } @combo_A;
print join( ' ', @combo_A, @combo_B ), "\n";
$count++;
}
}
print "Whew, that resulted in $count resultproducing iterations!\n";
The run:
A B C 0 1
A B C 0 2
A B C 0 3
A B C 0 4
A B C 0 5
A B C 0 6
...
...
...
X Y Z 7 6
X Y Z 4 0
X Y Z 4 3
X Y Z 4 6
X Y Z 0 3
X Y Z 0 6
X Y Z 3 6
Whew, that resulted in 117000 resultproducing iterations!
If overlaps between set A and set B are permitted, delete these three lines:
my %overlap;
@overlap{@combo_B} = ();
next INNER if first{ exists $overlap{$_} } @combo_A;
...and if I totally missed the boat I'm anxious to see a correct interpretation and the resulting solution code! :)
Update: In the update to your original post, and further clarified in a message to me, I see that you may be needing to merge multiple sets of combinations (ie, more than just two). I don't see any solution that doesn't involve diving deeper and deeper into nested loops, driving your bigO complexity higher with each additional set of combinations.
Is this just one way of approaching a more general problem that you're trying to solve?
 [reply] [d/l] [select] 
Re: Multiple Combinatorics
by BrowserUk (Pope) on Apr 15, 2012 at 09:45 UTC

#! perl slw
use strict;
sub nFor(&@) {
my $code = shift;
die "First argument must be a code ref" unless ref( $code ) eq 'CO
+DE';
my @limits = @_;
my @indices = ( 0 ) x @limits;
for( my $i = $#limits; $i >= 0; ) {
$i = $#limits;
$code>( @indices ), ++$indices[ $i ]
while $indices[ $i ] < $limits[ $i ];
$i = $#limits;
$indices[ $i ] = 0, ++$indices[ $i ]
while $i >= 0 and $indices[ $i ] == $limits[ $i ];
}
}
my @a = 'a'..'z';
my @a3;
nFor{
push @a3, [ @a[ @_ ] ];
} ( scalar @a ) x 3;
my @b = 0 .. 9;
my @b2;
nFor{
push @b2, [ @b[ @_ ] ];
} ( scalar @b ) x 2;
nFor {
print join '', @{ $a3[ $_[0] ] }, @{ $b2[ $_[1] ] };
} scalar @a3, scalar @b2;
__END__
C:\test>965138  wc l
1757600
C:\test>965138
aaa00
aaa01
aaa02
aaa03
...
zzz96
zzz97
zzz98
zzz99
With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
Examine what is said, not who speaks  Silence betokens consent  Love the truth but pardon error.
"Science is about questioning the status quo. Questioning authority".
 [reply] [d/l] 
Re: Multiple Combinatorics
by BrowserUk (Pope) on Apr 15, 2012 at 11:14 UTC

Update: My sets can overlap too. For exmaple I may have 4 sets like '0..7','16', '612',and '3..9'.
You don't say how many from each of those four sets, so I did 2 from each, but it should be obvious where to change the numbers supplied to permuteSets for different requirements.
The following runs in 1.7MB and takes a few seconds for wc l to count the results:
#! perl slw
use strict;
sub nFor(&@) {
my $code = shift;
die "First argument must be a code ref" unless ref( $code ) eq 'CO
+DE';
my @limits = @_;
my @indices = ( 0 ) x @limits;
for( my $i = $#limits; $i >= 0; ) {
$i = $#limits;
$code>( @indices ), ++$indices[ $i ]
while $indices[ $i ] < $limits[ $i ];
$i = $#limits;
$indices[ $i ] = 0, ++$indices[ $i ]
while $i >= 0 and $indices[ $i ] == $limits[ $i ];
}
}
sub permuteSets (&@) {
my $code = shift;
my @subsets;
while( @_ ) {
my( $n, $set ) = ( shift, shift );
push @subsets, [];
nFor { push @{ $subsets[1] }, [ @{ $set }[ @_ ] ] } (scalar @
+{ $set } ) x $n;
}
nFor{
$code>( map{ @{ $subsets[ $_ ][ $_[ $_ ] ] } } 0 .. $#subsets
+ );
} map scalar @$_, @subsets;
}
permuteSets{
print join '', @_;
} 2, [0..7], 2, [1..6], 2, [6..12], 2, [3..9];
__END__
C:\test>965138  wc l
5531908
C:\test>965138
00116633
00116634
00116635
00116636
00116637
00116638
00116639
00116643
00116644
00116645
00116646
00116647
00116648
00116649
00116653
...
7766121296
7766121297
7766121298
7766121299
I also need to eliminate duplicate items. In the case of overlapping sets, every resulting row should satisfy the initial condition.
I don't suppose you'd care to describe the application?
AFAIK, there in no combinatorics algorithm that would address those requirements, so it is going to be a postproduction step and is left as a exercise.
With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
Examine what is said, not who speaks  Silence betokens consent  Love the truth but pardon error.
"Science is about questioning the status quo. Questioning authority".
 [reply] [d/l] [select] 
Re: Multiple Combinatorics
by mrguy123 (Hermit) on Apr 15, 2012 at 07:53 UTC

Basically Math::Combinatorics returns a list as output.
Why not get two list from two different sets, and then merge and shuffle them?  [reply] 

It's technically not possible, with things I like to do. Each set has specific # of picks and sets can overlap each other.
 [reply] 
Re: Multiple Combinatorics
by JavaFan (Canon) on Apr 15, 2012 at 15:06 UTC

My sets can overlap too. For exmaple I may have 4 sets like '0..7','16', '612',and '3..9'. I also need to eliminate duplicate items. In the case of overlapping sets, every resulting row should satisfy the initial condition. ie.. if I ask for 2 from '0..7' and 3 from '6..12', I should NOT have "6,7,6,7,8" or even "4,5,6,7,8" ( Here 0..7 has 4 entries rather than 2, as asked for.)
With those conditions, nothing will satisfy if you'd have to pick at least one from '1..6'. Because any pick from that set will be a possible pick from the set '0..7', making that the count of picks from that set will be off.
 [reply] 

If '07' has less number of picks compared to '16', then we will have no solution at all. But, it is possible that ('07','16') has (3,2) picks or even (2,2) picks respectively. In the first case there will be exactly 1 selection from 0 and 7 in any solution. In the 2nd case, 0 and 7 will not be selected in any solution.
 [reply] 

Care to clarify that? Maybe add some examples? Cos like JavaFan, I don't get it.
As soon as you pick any two values from group 2 (1..6), you've also already picked two values from group 1 (0..7), so there is no way to comply with your "every resulting row should satisfy the initial condition" requirement.
You would either a) a pick two from both groups and therefore have 4 from the larger group; or b) pick nothing from the larger group, in which case you have nothing from the larger group.
With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
Examine what is said, not who speaks  Silence betokens consent  Love the truth but pardon error.
"Science is about questioning the status quo. Questioning authority".
 [reply] 


Re: Multiple Combinatorics
by tobyink (Abbot) on Apr 15, 2012 at 21:05 UTC

use 5.010;
use List::MapMulti qw/iterator_multi/;
my @letters = 'a' .. 'z';
my @numbers = 0 .. 9;
my $iter = iterator_multi \@letters, \@letters, \@letters, \@numbers,
+\@numbers;
# This loops through every possible combination
# of those lists.
#
while ( my @row = $iter>() ) {
say join q(), @row;
}
perl E'sub Monkey::do{say$_,for@_,do{($monkey=[caller(0)]>[3])=~s{::}{ }and$monkey}}"Monkey say">Monkey::do'
 [reply] [d/l] 
Re: Multiple Combinatorics
by brx (Pilgrim) on Apr 15, 2012 at 17:38 UTC

my @set1=(0..7);
my @set2=(6..12);
my @comb;
{
local $"=",";
@comb = glob"{@set1}{@set1}{@set2}{@set2}{@set2}"'
}
print join "\n",@comb;
Then you can grep {...} the result to exclude some results. Of course, this is not very good with big sets or a lot of combinations (time and memory).
Update: I probably post the worst solution beacause you don't want to duplicate elements from set1 and set2. It's possible to "grep" the result to avoid duplicate but it is not beautiful/effective... Anyway, this usage of glob must be known, so I don't delete my contribution :)  [reply] [d/l] [select] 

