20

n!在 perl 中生成数组的所有排列的最佳(优雅、简单、高效)方法是什么?

例如,如果我有一个数组@arr = (0, 1, 2),我想输出所有排列:

0 1 2
0 2 1
1 0 2
1 2 0
2 0 1
2 1 0

它可能应该是一个返回迭代器的函数(延迟/延迟评估,因为n!可能变得如此之大),因此可以这样调用它:

my @arr = (0, 1, 2);
my $iter = getPermIter(@arr);
while (my @perm = $iter->next() ){
    print "@perm\n";
}
4

7 回答 7

22

我建议你使用List::Permutor

use List::Permutor;

my $permutor = List::Permutor->new( 0, 1, 2);
while ( my @permutation = $permutor->next() ) {
    print "@permutation\n";
}
于 2009-03-11T18:56:52.647 回答
20

来自perlfaq4“如何置换列表的 N 个元素?”


在 CPAN 上使用 List::Permutor 模块。如果列表实际上是一个数组,请尝试 Algorithm::Permute 模块(也在 CPAN 上)。它是用 XS 代码编写的,非常高效:

use Algorithm::Permute;

my @array = 'a'..'d';
my $p_iterator = Algorithm::Permute->new ( \@array );

while (my @perm = $p_iterator->next) {
   print "next permutation: (@perm)\n";
}

为了更快的执行,你可以这样做:

use Algorithm::Permute;

my @array = 'a'..'d';

Algorithm::Permute::permute {
    print "next permutation: (@array)\n";
} @array;

这是一个小程序,它生成每行输入中所有单词的所有排列。permute() 函数中包含的算法在 Knuth 的计算机编程艺术的第 4 卷(仍未出版)中进行了讨论,并且适用于任何列表:

#!/usr/bin/perl -n
# Fischer-Krause ordered permutation generator

sub permute (&@) {
    my $code = shift;
    my @idx = 0..$#_;
    while ( $code->(@_[@idx]) ) {
        my $p = $#idx;
        --$p while $idx[$p-1] > $idx[$p];
        my $q = $p or return;
        push @idx, reverse splice @idx, $p;
        ++$q while $idx[$p-1] > $idx[$q];
        @idx[$p-1,$q]=@idx[$q,$p-1];
    }
}


permute { print "@_\n" } split;

Algorithm::Loops 模块还提供了 NextPermute 和 NextPermuteNum 函数,它们可以有效地找到数组的所有唯一排列,即使它包含重复值,也可以就地修改它:如果它的元素是反向排序的,那么数组是反向的,使其排序,并返回false;否则返回下一个排列。

NextPermute 使用字符串顺序和 NextPermuteNum 数字顺序,因此您可以像这样枚举 0..9 的所有排列:

use Algorithm::Loops qw(NextPermuteNum);

my @list= 0..9;
do { print "@list\n" } while NextPermuteNum @list;
于 2009-03-12T09:47:26.607 回答
14

您可以使用Algorithm::Permute并且也许Iterate Over Permutations(Perl 杂志,1998 年秋季)对您来说是一本有趣的读物。

于 2009-03-11T18:34:00.687 回答
4

试试这个,

use strict;
use warnings;

print "Enter the length of the string - ";
my $n = <> + 0;

my %hash = map { $_ => 1 } glob "{0,1,2}" x $n;

foreach my $key ( keys %hash ) {
    print "$key\n";
}

输出:这将给出所有可能的数字组合。您可以添加逻辑以过滤掉不需要的组合。

$ perl permute_perl.pl 
Enter the length of the string - 3
101
221
211
100
001
202
022
021
122
201
002
212
011
121
010
102
210
012
020
111
120
222
112
220
000
200
110
于 2015-09-22T03:38:02.860 回答
2

看看Iterator::Array::Jagged

于 2009-03-11T20:13:18.267 回答
2

我建议查看一种按字典顺序生成排列的算法,这就是我最近解决问题 24的方法。当数组中的项目数变大时,稍后存储和排序排列变得昂贵。

它看起来像List::PermutorManni 建议的,生成了数字排序的排列。这就是我使用 Perl 的目的。让我们知道结果如何。

于 2009-03-11T21:34:44.247 回答
0

一个纯 Perl 的答案,如果想要比 CPAN 模块允许的更高级:

use strict;
use warnings;

print "(@$_)\n" for permutate('a'..'c');

sub permutate {
  return [@_] if @_ <= 1;
  map {
    my ($f, @r) = list_with_x_first($_, @_);
    map [$f, @$_], permutate(@r);
  } 0..$#_;
}

sub list_with_x_first {
  return if @_ == 1;
  my $i = shift;
  ($_[$i], @_[0..$i-1], @_[$i+1..$#_]);
}

印刷:

(a b c)
(a c b)
(b a c)
(b c a)
(c a b)
(c b a)
于 2020-12-20T18:04:40.607 回答