如何查找MASSIVE数组中的哪些项目不止一次出现?

时间:2016-08-19 11:12:06

标签: arrays ruby performance sorting unique

这是一个非常简单的问题;哪些项目不止一次出现在列表中?

array = ["mike", "mike", "mike", "john", "john", "peter", "clark"]

正确答案是["mike", "john"]

似乎我们可以这样做:

array.select{ |e| ary.count(e) > 1 }.uniq

问题解决了。可是等等!如果数组非常大,该怎么办:

1_000_000.times { array.concat("1234567890abcdefghijklmnopqrstuvwxyz".split('')) }

事实上,我需要弄清楚如何在合理的时间内完成这项工作。我们正在谈论数以百万计的记录。

对于它的价值,这个庞大的阵列实际上是10-20个较小阵列的总和。如果比较那些比较容易,请告诉我 - 我很难过。

我们正在谈论每个文件10,000到10,000,000行,数百个文件。

2 个答案:

答案 0 :(得分:2)

喜欢

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秒。

答案 1 :(得分:1)

以下是另外两个解决方案,对这些方法和@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)已包含nil,则s1会返回e,如果e尚未smultismulti会被添加到smulti包含该元素。 (请参阅Set#add?。)方法返回Array#difference

使用Array#difference

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 是我将proposed添加到Ruby核心的方法。另请参阅我的回答here

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%

<强>基准

difference

当然,var what = ["jokes", "cats", "news", "weather", "sport"]; var do; function start() { do = what[Math.floor((Math.random() * what.length) + 1)]; } start(); Document.write(do); ,如果是Ruby核心的一部分,将用C编码并进行优化。