【问题标题】:How can I generate all permutations of an array in Perl?如何在 Perl 中生成数组的所有排列?
【发布时间】:2010-10-12 18:07:05
【问题描述】:

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

例如,如果我有一个数组@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";
}

【问题讨论】:

  • 如果你想自己写,递归算法 s.t.它会从数组中挑选一项,并用较小的数组调用自身,直到数组的大小为 1。它应该很干净。
  • Perlmonks 有一些例子:http://www.perlmonks.org/?node_id=503904

标签: perl algorithm arrays permutation


【解决方案1】:

我建议你使用List::Permutor:

use List::Permutor;

my $permutor = List::Permutor->new( 0, 1, 2);
while ( my @permutation = $permutor->next() ) {
    print "@permutation\n";
}

【讨论】:

  • 这是一个更永久的链接吗?更规范?还是只是不同?
  • 更永久(它优雅地处理主要作者的更改)。
  • 我喜欢作者在 cpan 上的示例如何包含 my $perm :P
【解决方案2】:

来自perlfaq4"How do I permute N elements of a list?"


在 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;

【讨论】:

    【解决方案3】:

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

    【讨论】:

      【解决方案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
      

      【讨论】:

        【解决方案5】:

        我建议查看algorithm for generating permutations in lexicographical order,这是我最近解决Problem 24 的方法。当数组中的项目数变大时,稍后存储和排序排列变得昂贵。

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

        【讨论】:

          【解决方案6】:

          【讨论】:

            【解决方案7】:

            一个纯 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)
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2012-05-05
              • 1970-01-01
              • 2013-06-28
              • 1970-01-01
              • 2018-05-21
              • 1970-01-01
              相关资源
              最近更新 更多