【问题标题】:Fastest method to see if all elements in an array have a particular value查看数组中所有元素是否具有特定值的最快方法
【发布时间】:2011-08-31 05:35:25
【问题描述】:

我需要一种非常快速的方法来确定数组是否仅包含值为9 的整数。这是我目前的解决方案:

input = [9,9,9,9,9,9,9,9,9,9,9,9]
input.uniq == [9]

你能做得更快吗?

【问题讨论】:

  • 要正确回答这个问题,需要知道实际数组有多大,以及非 9 值的概率是多少。对一种输入更快的解决方案可能对其他输入更慢。
  • 好点,数组可以很大,超过 2M 个元素,9 的概率是 1/10 ;)
  • 那么听起来拒绝非 9 值是您想要优化的,这是 @steenslag 的解决方案。全阵列扫描很昂贵。
  • @bashman:你真的希望 2M 元素相同,任何元素都有给定值的概率为 10%? :)
  • 看起来人们忽略了我的答案,因为它有 0 票,尽管它给出了最好的表现。将此评论放在这里是为了邀请人们看看。

标签: ruby arrays performance


【解决方案1】:
require 'benchmark'

n = 50000
Benchmark.bm do |x|
  x.report "uniq  " do
    n.times do 
      input = [9,9,9,9,9,9,9,9,9,9,9,9]
      input.uniq == [9]
    end
  end
  x.report "delete" do
    n.times do 
      input = [9,9,9,9,9,9,9,9,9,9,9,9]
      input.delete 9
      input == []
    end  
  end
  x.report "count " do
    n.times do
      input = [9,9,9,9,9,9,9,9,9,9,9,9]
      input.count(9)==input.size
    end
  end
  x.report "select" do
    n.times do
      input = [9,9,9,9,9,9,9,9,9,9,9,9]
      input.select{|x| x != 9}.empty?
    end
  end  
  x.report "detect" do
    n.times do
      input = [9,9,9,9,9,9,9,9,9,9,9,9]
      input.detect { |i| i != 9 }.nil?
    end
  end 

  x.report "all?  " do
    n.times do
      input = [9,9,9,9,9,9,9,9,9,9,9,9]
      input.all?{|x| x == 9} 
    end
  end 

end

它是上述答案和我的一些答案的基准

        user       system      total        real
uniq    0.313000   0.000000   0.313000 (  0.312500)
delete  0.140000   0.000000   0.140000 (  0.140625)
count   0.079000   0.000000   0.079000 (  0.078125)
select  0.234000   0.000000   0.234000 (  0.234375)
detect  0.234000   0.000000   0.234000 (  0.234375)
all?    0.219000   0.000000   0.219000 (  0.218750)

如果input = [1]+[9]*9:

        user     system      total        real
uniq    0.328000   0.000000   0.328000 (  0.328125)
delete  0.188000   0.000000   0.188000 (  0.203125)
count   0.187000   0.000000   0.187000 (  0.218750)
select  0.281000   0.016000   0.297000 (  0.296875)
detect  0.203000   0.000000   0.203000 (  0.203125)
all?    0.204000   0.000000   0.204000 (  0.203125)

如果input = [9]*9 + [1]:

        user     system      total        real
uniq    0.313000   0.000000   0.313000 (  0.328125)
delete  0.187000   0.000000   0.187000 (  0.187500)
count   0.172000   0.000000   0.172000 (  0.187500)
select  0.297000   0.000000   0.297000 (  0.312500)
detect  0.313000   0.000000   0.313000 (  0.312500)
all?    0.281000   0.000000   0.281000 (  0.281250)

如果input = [1,2,3,4,5,6,7,8,9]:

        user     system      total        real
uniq    0.407000   0.000000   0.407000 (  0.406250)
delete  0.125000   0.000000   0.125000 (  0.125000)
count   0.125000   0.000000   0.125000 (  0.125000)
select  0.218000   0.000000   0.218000 (  0.234375)
detect  0.110000   0.000000   0.110000 (  0.109375)
all?    0.109000   0.000000   0.109000 (  0.109375)

【讨论】:

  • 你还能为元素都不同的情况生成结果吗?全部?”解决方案,在这种情况下比“uniq”更快。
  • 如果您要标记报告(例如x.report "detect" do),并且还针对未通过测试的数组进行测试(例如[1]+[9]*9[9]*9+[1]),这将是一个非常好的答案。
  • 对这些结果进行平均是个好主意,因此人们不需要梳理四个基准时间表。除此之外,非常好!
【解决方案2】:

你有几个选择:

>> input.count(9)==input.size
=> true

>> input.select{|x| x != 9}.empty?
=> true

或者你上面的解决方案。

【讨论】:

  • 谢谢,第一个是最快的(到目前为止:)),第二个是痛苦的慢。
  • 当您将非常大的输入放入其中时,第一个会很慢。这里的其他解决方案会更快。
【解决方案3】:

这会循环数组并在找到非 9 时中断(返回 false}。

[9,9,9,9,9,9,9,9,9,9,9,9].all?{|x| x == 9} # => true

【讨论】:

  • 当输入非常大并且可能包含非 9 值时速度很快。
【解决方案4】:

编辑:找到完整的源代码here。支持@nash 的原始想法。


找到一个元素后立即迭代并返回 false != 匹配。

def all_matches(arr, match)
  arr.each do |element|
    return false if element != match
  end
  true
end

从 0 到 9 的 2M 随机整数,50 次循环 (n=50):

用户系统总真实 唯一 5.230000 0.010000 5.240000 (5.219444) 计数 2.680000 0.010000 2.690000 ( 2.677923) 选择 7.580000 0.060000 7.640000 (7.634620) 检测 0.000000 0.000000 0.000000 ( 0.000068) 全部? 0.000000 0.000000 0.000000 ( 0.000046) 我的 0.000000 0.000000 0.000000 ( 0.000032) 删除 5.090000 0.020000 5.110000 ( 5.101290) 任何? 0.000000 0.000000 0.000000 ( 0.000041)

用于生成数组的代码:

input = []
2000000.times { input << (rand*10).to_i }

有 2M 个 9(所有个 9),50 个循环:

用户系统总真实 独特的 4.900000 0.000000 4.900000 (4.890030) 计数 0.350000 0.000000 0.350000 ( 0.351340) 选择 5.400000 0.010000 5.410000 ( 5.393489) 检测 6.720000 0.000000 6.720000 (6.685539) 全部? 6.070000 0.000000 6.070000 ( 6.061914) 我的 5.510000 0.010000 5.520000 ( 5.500186) 删除 1.080000 0.010000 1.090000 ( 1.084125) 任何? 6.200000 0.000000 6.200000 ( 6.197529)

【讨论】:

  • 请注意,mine 基准测试结果是我的算法的结果。
  • 另请注意,如果您将return false if arr.empty? 添加到我的方法中(这可能是也可能不是必需的),它仍然比不这样做的all? 方法执行得更好。跨度>
  • 我不明白为什么在最后一个基准测试中计数这么快。
  • @steenslag:一定和branch prediction有关。
【解决方案5】:

我假设您的意思是input.uniq == [9],因为您实际上对我返回了 false。你说的更快是什么意思?这段代码需要快速运行吗?我想检测更快,因为它将返回与测试匹配的第一个元素:

input = [9,9,9,9,9,9,9,9,9,9,9,9]
input.detect { |i| i != 9 }.nil?

【讨论】:

  • pastie.org/1961526,您的代码与我的代码,感谢您提供有关大括号的信息,我已经更正了上面的示例
  • 与实际基准相比,您的想象力毫无意义。 :)
【解决方案6】:

也许是最慢的,但这是我想到的

input = [9,9,9,9,9,9,9,9,9,9,9,9]
!(input.any { |a| a != 9 })

【讨论】:

  • 另一种方法! :) 查看我对比较基准的回答(我刚刚添加了您的方法,标记为 any?)。
【解决方案7】:

这里还有一个更快的(上面的count方法还是最快的):

arr = [9,9,9,9,9,9,9,9,9,9,9,9]
arr.reject { |i| i==9 }.count == 0

还有一个慢一点的:

arr.inject(:&) == 9

这是“果味”宝石比较:

require 'fruity'
compare do
  count { arr.count(9) == arr.size }
  uniq { arr.uniq == [9] }  
  bitwise_and { arr.inject(:&) == 9 }  
  reject { arr.reject { |i| i==9 }.count == 0 }
end  


Running each test 8192 times. Test will take about 3 seconds.
count is faster than reject by 39.99999999999999% ± 10.0%
reject is faster than uniq by 10x ± 1.0
uniq is faster than bit_and by 30.000000000000004% ± 1.0%

【讨论】:

    【解决方案8】:

    这很好用:

    > array = ['apple', 'banana']
    > includes = array.uniq.include? 'banana'
    > => true
    

    此外,通过扩展,我们可以在不知道它们是什么的情况下检查所有值是否相同:

    > array = ['apple', 'banana', 'apple']
    > all_same_values = (array.uniq.length > 1) ? false : true
    > => false
    

    这里有一个相关的答案:https://stackoverflow.com/a/1986398/1886534

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-12-16
      • 2021-11-03
      • 2017-11-01
      • 2016-07-02
      • 2023-04-05
      • 1970-01-01
      相关资源
      最近更新 更多