【问题标题】:Generate a powerset of a set without keeping a stack in Erlang or Ruby在 Erlang 或 Ruby 中生成集合的幂集而不保留堆栈
【发布时间】:2011-12-17 07:57:49
【问题描述】:

我想生成一个相当大的集合(大约 30-50 个元素)的 powerset,我知道需要 2^n 来存储 powerset。

是否可以一次生成一个子集?

即生成具有迭代的集合的幂集,将每个生成的子集保存到磁盘/数据库,将其从堆栈/内存中删除,然后才继续生成其他子集?

很遗憾,我未能根据自己的需要修改 ErlangRuby 示例。

【问题讨论】:

    标签: ruby erlang subset powerset


    【解决方案1】:

    编辑:如果没有给出块,则添加枚举器(如@Jörg W Mittag)。

    class Array
      def powerset
        return to_enum(:powerset) unless block_given?
        1.upto(self.size) do |n|
          self.combination(n).each{|i| yield i}
        end
      end
    end
    # demo
    ['a', 'b', 'c'].powerset{|item| p item} # items are generated one at a time
    ps = [1, 2, 3, 4].powerset # no block, so you'll get an enumerator 
    10.times.map{ ps.next } # 10.times without a block is also an enumerator
    

    输出

    ["a"]
    ["b"]
    ["c"]
    ["a", "b"]
    ["a", "c"]
    ["b", "c"]
    ["a", "b", "c"]
    [[1], [2], [3], [4], [1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]]
    

    【讨论】:

      【解决方案2】:

      生成列表的幂集(实际上是您的 Erlang 示例使用的)的一种方法是迭代所有数字 x 从 0 到 2^n(不包括)和每个 x,当且仅当设置了xith 位时,生成包含原始列表的ith 元素的列表。

      由于使用这种方法生成当前列表仅取决于x 的值,而不取决于任何先前生成的列表,因此您不必在使用后将列表保存在内存中。所以这种方法可以用来做你想做的事。

      【讨论】:

      • 我了解如何在 Erlang 中从 2^n 迭代所有数字 x,但我不明白这个条件(我不擅长位操作)。你能提供一个简单的例子吗?
      • @Martin:条件是I band (1 bsl Pos) =/= 0(取自您链接的 Erlang 代码)。实际上,您可以从链接的代码中获取整个内部列表,即[lists:nth(Pos+1,Lst) || Pos <- lists:seq(0,N-1), I band (1 bsl Pos) =/= 0](其中 I 是您从 0 迭代到 2^n 的数字)。虽然仔细想想,这在 Erlang 中效率很低,因为 lists:nthO(n)...
      【解决方案3】:

      这使用标准的“位数组”技巧来生成幂集(它使用 Ruby 的 Integers 表现为位数组这一事实)。但更重要的是,它使用Enumerator 懒惰地生成集合。

      require 'set'
      
      module Enumerable
        def powerset
          number_of_sets = 2 ** count
      
          Enumerator.new {|ps|
            number_of_sets.times {|i|
              ps << Set[*reject.with_index {|_, j| i[j].zero? }]
            }
          }
        end
      end
      

      即使对于 数千 个元素,这也能正常工作:

      enum = (1..10_000).powerset
      enum.next # => #<Set: {}>
      enum.next # => #<Set: {1}>
      enum.next # => #<Set: {2}>
      enum.next # => #<Set: {1, 2}>
      enum.next # => #<Set: {3}>
      enum.next # => #<Set: {1, 3}>
      enum.next # => #<Set: {2, 3}>
      enum.next # => #<Set: {1, 2, 3}>
      enum.next # => #<Set: {4}>
      enum.next # => #<Set: {1, 4}>
      enum.next # => #<Set: {2, 4}>
      enum.next # => #<Set: {1, 2, 4}>
      enum.next # => #<Set: {3, 4}>
      enum.next # => #<Set: {1, 3, 4}>
      enum.next # => #<Set: {2, 3, 4}>
      enum.next # => #<Set: {1, 2, 3, 4}>
      enum.next # => #<Set: {5}>
      # ...
      

      编辑:这是基于@steenslag 的解决方案。我完全忘记了Array#combination,因为我太专注于寻找适合任何 Enumerable 的解决方案。但是,我的解决方案要求Enumerable 无论如何都是有限的,并且任何有限的Enumerable 都应该可以表示为Array,所以这并没有太大的限制。

      module Enumerable
        def powerset
          ary = to_a
      
          Enumerator.new {|ps|
            ary.size.times {|n|
              ary.combination(n).each(&ps.method(:yield))
            }
          }
        end
      end
      

      【讨论】:

      • require 'set' 不需要(不再需要)。
      • 你是对的,当然。如果您想生成一个实际的幂集而不是“幂数组”,则需要先将组合转换为集合,然后再将它们提供给枚举器。
      • @JörgWMittag 我无法让你的代码工作,我实际上无法完全理解它。
      猜你喜欢
      • 2012-07-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-01-18
      • 2021-09-02
      • 1970-01-01
      • 2011-02-10
      • 2015-09-03
      相关资源
      最近更新 更多