如何在Perl中生成列表的所有子集?

时间:2009-06-15 01:39:35

标签: perl set subset

我在Perl数组中有一个数学集:(1,2,3)。我想找到那一组的所有子集:(1),(2),(3),(1,2),(1,3),(2,3)。

使用3个元素这不是太难,但如果设置有10个元素,这会变得棘手。

思想?

6 个答案:

答案 0 :(得分:12)

您可以像Matthew提到的那样使用 Data::PowerSet 。但是,如果如您的示例中所示,您只需要适当的子集而不是每个子集,那么您需要做更多的工作。

  # result: all subsets, except {68, 22, 43}.
  my $values = Data::PowerSet->new({max => 2}, 68, 22, 43);

同样,如果要省略空集,只需添加min参数:

  # result: all subsets, except {} and {68, 22, 43}.
  my $values = Data::PowerSet->new({min => 1, max => 2}, 68, 22, 43);

否则,要获取所有子集,只需省略两个参数:

  # result: every subset.
  my $values = Data::PowerSet->new(68, 22, 43);

答案 1 :(得分:4)

答案 2 :(得分:3)

既然你说“数学集”,我认为你的意思是没有重复。

一个适用于多达32个元素的简单实现:

my $set = [1,2,3];
my @subsets;
for my $count ( 1..(1<<@$set)-2 ) {
    push @subsets, [ map $count & (1<<$_) ? $set->[$_] : (), 0..$#$set ];
}

(对于整个范围的子集,循环从0到(1&lt;&lt;&lt; @ $ set)-1;排除0排除空集,排除(1&lt;&lt;&lt; @ $ set)-1排除原始集。)

更新:我并不是主张使用模块,只是在你想要了解如何解决这个问题时提出建议。通常,每个元素都包含在任何给定子集中或从中排除。您想要选择一个元素并首先生成其他元素的所有可能子集,不包括您选择的元素,然后生成其他元素的所有可能子集,包括您选择的元素。递归地将其应用于“生成所有可能的子集”。最后,丢弃空子集和非正确子集。在上面的代码中,每个元素都分配了一个位。首先是所有子集 高位开启,然后所有关闭它的人。对于这些替代方案中的每一个,首先生成子集,然后关闭下一个最高位,然后打开。继续这个直到你只是在最低位工作,你最终得到的是所有可能的数字,按顺序。

答案 3 :(得分:1)

答案 4 :(得分:1)

如果您不想使用现有模块或不能使用现有模块,则只需使用bit-mask and a binary counter编写自己的子集生成算法。示例代码如下 -

#!/usr/bin/perl
use strict;
use warnings;

my @set     = (1, 2, 3);
my @bitMask = (0, 0, 0); #Same size as @set, initially filled with zeroes

printSubset(\@bitMask, \@set) while ( genMask(\@bitMask, \@set) );

sub printSubset {
  my ($bitMask, $set) = @_;

  for (0 .. @$bitMask-1) {
    print "$set->[$_]" if $bitMask->[$_] == 1;
  }
  print"\n";

}

sub genMask {
  my ($bitMask, $set) = @_;

  my $i;
  for ($i = 0; $i < @$set && $bitMask->[$i]; $i++) {
    $bitMask->[$i] = 0;
  }

  if ($i < @$set) {
    $bitMask->[$i] = 1;
    return 1;
  }

  return 0;
}

注意:我无法测试代码,可能需要解决一些错误。

答案 5 :(得分:1)

这是一个计数问题 - 对于N个元素,确实有2 ^ N个子集,你必须从0到2 ^ N - 1计算二进制数,以便全部列出。

对于例如3个项目,有8个可能的子集:000,001,010,011,100,101,110和111 - 数字显示哪些成员存在。