搜索二维数组的算法

时间:2017-01-23 15:36:46

标签: ruby algorithm

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 

这样做蛮力的方式是花费太多时间来完成。 还有其他建议吗?

3 个答案:

答案 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