如何查找MASSIVE数组中的哪些项目不止一次出现?
这是一个非常简单的问题; 哪些项目不止一次出现在列表中?
array = ["mike", "mike", "mike", "john", "john", "peter", "clark"]
正确答案是["mike", "john"]
。
好像我们可以这样做:
array.select{ |e| ary.count(e) > 1 }.uniq
问题解决了。 可是等等! 如果arrays非常大,该怎么办:
1_000_000.times { array.concat("1234567890abcdefghijklmnopqrstuvwxyz".split('')) }
碰巧我需要弄清楚如何在合理的时间内完成这项工作。 我们正在谈论数以百万计的记录。
对于它的价值,这个庞大的arrays实际上是10-20个较小arrays的总和。 如果比较那些比较容易,请告诉我 – 我很难过。
我们正在谈论每个文件10,000到10,000,000行,数百个文件。
有类似的东西
items = 30_000_000 array = items.times.map do rand(10_000_000) end puts "Done with seeding" puts puts "Checking what items appear more than once. Size: #{array.size}" puts t1 = Time.now def more_than_once(array) counts = Hash.new(0) array.each do |item| counts[item] += 1 end counts.select do |_, count| count > 1 end.keys end res = more_than_once(array) t2 = Time.now p res.size puts "Took #{t2 - t1}"
为你工作?
我的机器上的持续时间约为40秒。
这里有两个解决方案,它们与@ Pascal方法的基准比较。
使用集
require 'set' def multi_set(arr) s1 = Set.new arr.each_with_object(Set.new) { |e, smulti| smulti.add(e) unless s1.add?(e) }.to_a end arr = ["mike", "mike", "mike", "john", "john", "peter", "clark"] multi(arr) #=> ["mike", "john"]
正在构建s1
以包含arr
所有不同元素。 s1.add?(e)
如果s1
已经包含e
,则返回nil
,在这种情况下,如果smulti
尚未包含该元素, smulti
e
添加到smulti
。 (请参阅Set smulti
)方法返回smulti
。
使用Array#difference
Array#difference
是我建议添加到Ruby核心的方法。 另见我的答案。
class Array def difference(other) h = other.each_with_object(Hash.new(0)) { |e,h| h[e] += 1 } reject { |e| h[e] > 0 && h[e] -= 1 } end end def multi_difference(arr) arr.difference(arr.uniq).uniq end
基准
def more_than_once(arr) counts = Hash.new { |hash, key| hash[key] = 0 } arr.each do |item| counts[item] += 1 end counts.select do |_, count| count > 1 end.keys end require 'fruity' items = 30_000_000 arr = items.times.map { rand 10_000_000 } compare do Pascal { more_than_once(arr) } Set { multi_set(arr) } Difference { multi_difference(arr) } end Running each test once. Test will take about 4 minutes. Pascal is faster than Set by 19.999999999999996% ± 10.0% Set is faster than Difference by 30.000000000000004% ± 10.0%
当然,如果Ruby核心的一部分, difference
将用C编码并进行优化。