This uses the standard "bit array" trick for generating power sets (and it uses the fact that Ruby's Integer
s 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 # => #<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}>
# ...
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