Используется стандартный трюк "битовый массив" для генерации наборов мощности (и тот факт, что Integer
в Ruby ведут себя как битовые массивы).Но что еще более важно, он использует 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