Generate a powerset of a set without keeping a stack in Erlang or Ruby

前端 未结 3 920
感情败类
感情败类 2020-12-10 19:50

I would like to generate a powerset of a rather big set (about 30-50 elements) and I know that it takes 2^n to store the powerset.

Is it possible to gen

3条回答
  •  天涯浪人
    2020-12-10 20:10

    This uses the standard "bit array" trick for generating power sets (and it uses the fact that Ruby's Integers behave as bit arrays). But more importantly, it uses an Enumerator to generate the sets lazily.

    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
    

    This works perfectly fine even for thousands of elements:

    enum = (1..10_000).powerset
    enum.next # => #
    enum.next # => #
    enum.next # => #
    enum.next # => #
    enum.next # => #
    enum.next # => #
    enum.next # => #
    enum.next # => #
    enum.next # => #
    enum.next # => #
    enum.next # => #
    enum.next # => #
    enum.next # => #
    enum.next # => #
    enum.next # => #
    enum.next # => #
    enum.next # => #
    # ...
    

    EDIT: This is based on @steenslag's solution. I totally forgot about Array#combination, since I was too focused on finding a solution that would work for any Enumerable. However, my solution requires that the Enumerable be finite anyway, and any finite Enumerable should probably be representable as an Array, so that's not much of a restriction.

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

提交回复
热议问题