data = [[0,1], [1,6,10], [], [1,2,4,5], [7,8], [], [], [8], [2], [0,3], [9]]
考虑到上面的2d数组,我需要选择五个给我最独特数字的数组。
例如
# returns 11 (optimal output, the number of subclasses)
(data[1] | data[3] | data[4] | data[9] | data[10]).length
# returns 10 (less optimal output)
(data[0] | data[1] | data[3] | data[4] | data[10]).length
这样做蛮力的方式是花费太多时间来完成。 还有其他建议吗?
答案 0 :(得分:4)
以下是这样做的事情:
data = [[0,1], [1,6,10], [], [1,2,4,5], [7,8], [], [], [8], [2], [0,3], [9]]
best = data.combination(5).max_by do |combo|
combo.flatten.uniq.length
end
best
# => [[1, 6, 10], [1, 2, 4, 5], [7, 8], [0, 3], [9]]
best.flatten.uniq.length
# => 11
计算时间不长,如果您准备使用Benchmark进行测试,可能有更好的方法来优化内循环。
如果你需要更高性能的数量级,那么可能是一个C ++库linked in via FFI。
如果您正在处理相对较小的数字,例如在0..31或甚至0..63的范围内,那么您可以使用位掩码执行此操作。这会将每个数组减少为单个值,并且将值与OR组合在计算方面是微不足道的。计算给定值中的位数同样非常简单。
答案 1 :(得分:2)
这是一个greedy算法。
对于每次迭代,它只需要使用最新元素的子数组。它适用于您的示例,但对于更复杂的示例可能会有一些元素。
对于大型数组和大型n
,它应该比使用combination
的任何解决方案快得多。
您没有提供任何代码,因此我将其留作练习来寻找反例;)。
data = [[0, 1], [1, 6, 10], [], [1, 2, 4, 5], [7, 8], [], [], [8], [2], [0, 3], [9]]
def trim(array, already_taken)
array.map { |sub_array| sub_array - already_taken }.reject(&:empty?)
end
def find_best_cover(array, n)
array = array.map{ |subarray| subarray.uniq }
Array.new(n) do
next_best = array.max_by { |subarray| subarray.size }
array = trim(array, next_best)
next_best
end
end
p find_best_cover(data, 5).flatten
#=> [1, 2, 4, 5, 6, 10, 7, 8, 0, 3, 9]
答案 2 :(得分:1)
您可以通过减少data
数组来减少计算时间。
最初,共有462种组合:
data.combination(5).size
#=> 462
删除空数组会将其减少为56:
data.reject!(&:empty)
data.combination(5).size
#=> 56
删除完全包含在其他数组中的数组只会产生6种组合:
data -= [[2], [8]]
data.combination(5).size
#=> 6