[英]In Perl, how can I iterate over the Cartesian product of multiple sets?
給定x
個數組,每個數組可能具有不同數量的元素,我該如何遍歷從每個數組中選擇一項的所有組合?
例:
[ ] [ ] [ ]
foo cat 1
bar dog 2
baz 3
4
退貨
[foo] [cat] [ 1 ]
[foo] [cat] [ 2 ]
...
[baz] [dog] [ 4 ]
我正在Perl,順便說一句。
我的Set :: CrossProduct模塊完全可以滿足您的需求。 請注意,您並不是真正在尋找排列,排列是一組元素的順序。 您正在尋找叉積,它是來自不同集合的元素的組合。
我的模塊為您提供了一個迭代器,因此您不必在內存中全部創建它。 僅在需要時才創建一個新的元組。
use Set::Crossproduct;
my $iterator = Set::CrossProduct->new(
[
[qw( foo bar baz )],
[qw( cat dog )],
[qw( 1 2 3 4 )],
]
);
while( my $tuple = $iterator->get ) {
say join ' ', $tuple->@*;
}
對於任意數量的列表的簡單遞歸解決方案:
sub permute {
my ($first_list, @remain) = @_;
unless (defined($first_list)) {
return []; # only possibility is the null set
}
my @accum;
for my $elem (@$first_list) {
push @accum, (map { [$elem, @$_] } permute(@remain));
}
return @accum;
}
任意數量的列表的不太簡單的非遞歸解決方案:
sub make_generator {
my @lists = reverse @_;
my @state = map { 0 } @lists;
return sub {
my $i = 0;
return undef unless defined $state[0];
while ($i < @lists) {
$state[$i]++;
last if $state[$i] < scalar @{$lists[$i]};
$state[$i] = 0;
$i++;
}
if ($i >= @state) {
## Sabotage things so we don't produce any more values
$state[0] = undef;
return undef;
}
my @out;
for (0..$#state) {
push @out, $lists[$_][$state[$_]];
}
return [reverse @out];
};
}
my $gen = make_generator([qw/foo bar baz/], [qw/cat dog/], [1..4]);
while ($_ = $gen->()) {
print join(", ", @$_), "\n";
}
可在http://www.perlmonks.org/?node_id=7366上找到用於做笛卡爾積的遞歸和更流利的Perl示例(包括注釋和文檔) 。
例:
sub cartesian {
my @C = map { [ $_ ] } @{ shift @_ };
foreach (@_) {
my @A = @$_;
@C = map { my $n = $_; map { [ $n, @$_ ] } @C } @A;
}
return @C;
}
我首先想到的一種方法是使用一對循環,而沒有遞歸。
例:
給定A [3],B [2],C [3],
for (index = 0..totalpermutations) {
print A[index % 3];
print B[(index / 3) % 2];
print C[(index / 6) % 3];
}
當然可以用for循環替換[ABC ...],然后可以記住一小部分。 當然,遞歸比較整潔,但是這對於遞歸受堆棧大小嚴格限制的語言可能很有用。
您可以使用嵌套循環。
for my $e1 (qw( foo bar baz )) {
for my $e2 (qw( cat dog )) {
for my $e3 (qw( 1 2 3 4 )) {
my @choice = ($e1, $e2, $e3);
...
}}}
當需要任意數量的嵌套循環時,可以使用Algorithm :: Loops的NestedLoops
。
use Algorithm::Loops qw( NestedLoops );
my @lists = (
[qw( foo bar baz )],
[qw( cat dog )],
[qw( 1 2 3 4 )],
);
my $iter = NestedLoops(\@lists);
while ( my @choice = $iter->() ) {
...
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.