【问题标题】:How can I generate all subsets of a list in Perl?如何在 Perl 中生成列表的所有子集?
【发布时间】:2009-06-15 01:39:34
【问题描述】:

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

使用 3 个元素这并不太难,但如果 set 有 10 个元素,这会变得很棘手。

想法?

【问题讨论】:

    标签: perl set subset


    【解决方案1】:

    您可以像 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);
    

    【讨论】:

    • 从示例中,我假设他想要适当的子集。
    【解决方案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

    更新:我并不是在提倡使用模块,只是建议以防万一您想了解如何解决此类问题。一般来说,每个元素要么被包含在任何给定的子集中,要么被排除在外。您想选择一个元素并首先生成其他元素的所有可能子集,不包括您选择的元素,然后生成其他元素的所有可能子集,包括您选择的元素。递归地将其应用于“生成所有可能的子集”。最后,丢弃空子集和非正确子集。在上面的代码中,每个元素都被分配了一个位。首先所有子集 是在高位打开的情况下生成的,然后是所有关闭的。对于这些备选方案中的每一个,首先生成子集,将次高位关闭,然后再打开。继续这个直到你只处理最低位,你最终得到的是所有可能的数字,按顺序。

    【讨论】:

    • 我不知道它有多重要,但我通常更喜欢使用 1
    • 想详细说明循环的工作原理,或者更具体地说,地图功能?
    • @muteW: map 使 $_ 在 @$set 的索引上循环(示例中为 0 到 2),查看 $count 的第 $_' 位是否已设置,如果是,则包括结果列表中@$set 的第$_'个元素。
    • @Nathan Fellman:你是对的,为了保持一致性,我应该在两个地方(或都不用)使用 1
    【解决方案4】:

    如果您不想或不能使用现有模块,那么您可以使用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】:

      【讨论】:

        【解决方案6】:

        这是一个计数问题 - 对于 N 个元素,恰好有 2^N 个子集,您必须以二进制形式从 0 计数到 2^N - 1 才能将它们全部列出。

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2012-05-05
          • 2010-10-12
          • 2021-05-10
          • 2010-12-04
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-11-26
          相关资源
          最近更新 更多