好像我自己找到了解决方案。
我改编了这个 stackoverflow 答案中的 Ruby 代码以满足我的需求:
class Array
def distribute_to_bins(bins_left)
Enumerator.new do |yielder|
if self.empty?
yielder.yield([])
else
# If there is only one bin left, fill all remaining items in it
min_elements_in_bin = if bins_left == 1
self.size
else
1
end
# Make sure that there are sufficient items left to not get any empty bins
max_elements_in_bin = self.size - (bins_left - 1)
(min_elements_in_bin..max_elements_in_bin).to_a.each do |number_of_elements_in_bin|
self.drop(1).combination(number_of_elements_in_bin - 1).map { |vs| [self.first] + vs }.each do |values|
(self - values).distribute_to_bins(bins_left - 1).each do |group|
yielder.yield([values] + group)
end
end
end
end
end
end
end
像这样执行:
pp (1..5).to_a.distribute_to_bins(3).to_a
将产生没有空箱或重复的所有可能性:
[[[1], [2], [3, 4, 5]],
[[1], [2, 3], [4, 5]],
[[1], [2, 4], [3, 5]],
[[1], [2, 5], [3, 4]],
[[1], [2, 3, 4], [5]],
[[1], [2, 3, 5], [4]],
[[1], [2, 4, 5], [3]],
[[1, 2], [3], [4, 5]],
[[1, 2], [3, 4], [5]],
[[1, 2], [3, 5], [4]],
[[1, 3], [2], [4, 5]],
[[1, 3], [2, 4], [5]],
[[1, 3], [2, 5], [4]],
[[1, 4], [2], [3, 5]],
[[1, 4], [2, 3], [5]],
[[1, 4], [2, 5], [3]],
[[1, 5], [2], [3, 4]],
[[1, 5], [2, 3], [4]],
[[1, 5], [2, 4], [3]],
[[1, 2, 3], [4], [5]],
[[1, 2, 4], [3], [5]],
[[1, 2, 5], [3], [4]],
[[1, 3, 4], [2], [5]],
[[1, 3, 5], [2], [4]],
[[1, 4, 5], [2], [3]]]