It uses the standard bit-array trick to generate power sets (and it uses the fact that Ruby Integer behaves like bit-arrays). But more importantly, it uses the Enumerator to generate 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 great even for thousands of items:
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 completely forgot about Array#combination , as I was too focused on finding a solution that would work for any Enumerable . However, my solution requires that Enumerable be finite anyway, and any finite Enumerable should probably be representable as Array , so that is not much of a limitation.
module Enumerable def powerset ary = to_a Enumerator.new {|ps| ary.size.times {|n| ary.combination(n).each(&ps.method(:yield)) } } end end
source share