问题
I would like to get all the possible partitions (disjoint subsets of a set which union is the original set) of a multiset (some elements are equal and non-distinguishable from each other).
Simpler case when one would like to yield the partitions of a simple set, in which there are no elements with multiplicity, in other words all elements are different. For this scenario I found this Ruby code on StackOwerflow which is very efficient, as not storing all the possible partitions, but yielding them to a block:
def partitions(set)
yield [] if set.empty?
(0 ... 2 ** set.size / 2).each do |i|
parts = [[], []]
set.each do |item|
parts[i & 1] << item
i >>= 1
end
partitions(parts[1]) do |b|
result = [parts[0]] + b
result = result.reject do |e|
e.empty?
end
yield result
end
end
end
Example:
partitions([1,2,3]){|e| puts e.inspect}
outputs:
[[1, 2, 3]]
[[2, 3], [1]]
[[1, 3], [2]]
[[3], [1, 2]]
[[3], [2], [1]]
As there are 5 different partitioning of the set [1,2,3]
(Bell-number anyway: https://en.wikipedia.org/wiki/Bell_number)
However the another set which is in fact a multiset contains elements with multiplicity, then above code doesn't work of course:
partitions([1,1,2]){|e| puts e.inspect}
outputs:
[[1, 1, 2]]
[[1, 2], [1]] *
[[1, 2], [1]] *
[[2], [1, 1]]
[[2], [1], [1]]
One can see two identical partitions, denoted with *, which should be yielded only once.
My question is: how can I modify the def partitions()
method to work with multisets too, or how can I filter out the identical partitionings, duplications in an efficient way? Are those identical partitionings coming always followed by each other in a consecutive manner?
My goal is to organize images with different aspect ratio to a montage, and the picture rows of the montage would be those set partitions. I would like to minimalize the difference of the heights between the picture rows (or the standard deviation equivalently) among the possible partitionings, but many times there are pictures with same aspect ratios this is why I try to deal with a multiset.
Yielding not partitons but powersets (all possibe subsets) of a multiset, filtering out the duplicates by simple memoization:
Montage optimization by backtracking on YouTube
回答1:
You could put it in an array and use uniq
:
arr = []
partitions([1,1,2]) { |e| arr << e }
puts arr.to_s
#-> [[[1, 1, 2]], [[1, 2], [1]], [[1, 2], [1]], [[2], [1, 1]], [[2], [1], [1]]]
puts arr.uniq.to_s
#-> [[[1, 1, 2]], [[1, 2], [1]], [[2], [1, 1]], [[2], [1], [1]]]
来源:https://stackoverflow.com/questions/42215165/yielding-partitions-of-a-multiset-with-ruby