我想生成一个相当大的集合(约30-50个元素)的powerset,我知道存储powerset需要2^n
。
是否可以一次生成一个子集?
即。使用迭代生成集合的powerset,将每个生成的子集保存到磁盘/数据库,将其从堆栈/内存中删除,然后继续生成其他子集?
答案 0 :(得分:5)
生成列表的powerset(实际上是您的Erlang示例使用的那个)的一种方法是迭代所有数字x
从0到2 ^ n(不包括)和每个{{ 1}},当且仅当x
的{{1}}位被设置时,生成包含原始列表的i
元素的列表。
由于使用此方法生成当前列表仅取决于i
的值而不取决于以前生成的任何列表,因此在使用它们之后不必将列表保留在内存中。所以这种方法可以用来做你想做的事。
答案 1 :(得分:5)
编辑:如果没有给出阻止,则添加枚举器(如@JörgWMittag)。
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 :(得分:1)
这使用标准的“位阵列”技巧来生成功率集(并且它使用Ruby的Integer
表现为位数组的事实)。但更重要的是,它使用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