关于ruby:查看数组中所有元素是否具有特定值的最快方法

Fastest method to see if all elements in an array have a particular value

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

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

你能快点吗?


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
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

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

1
2
3
4
5
6
7
        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

1
2
3
4
5
6
7
        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]

1
2
3
4
5
6
7
        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]

1
2
3
4
5
6
7
        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)


您有几个选择:

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

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

或者你上面的解决方案。


这将循环数组并中断(当发现非9的内容时返回false)。

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


编辑:在这里找到完整的源代码。向@nash提供原始想法的支持。

一找到元素就迭代并返回false!=匹配。

1
2
3
4
5
6
def all_matches(arr, match)
  arr.each do |element|
    return false if element != match
  end
  true
end

对于0到9之间的2 m随机整数,50个循环(n=50

1
2
3
4
5
6
7
8
9
        user       system     total       real
uniq    5.230000   0.010000   5.240000 (  5.219444)
count   2.680000   0.010000   2.690000 (  2.677923)
select  7.580000   0.060000   7.640000 (  7.634620)
detect  0.000000   0.000000   0.000000 (  0.000068)
all?    0.000000   0.000000   0.000000 (  0.000046)
mine    0.000000   0.000000   0.000000 (  0.000032)
delete  5.090000   0.020000   5.110000 (  5.101290)
any?    0.000000   0.000000   0.000000 (  0.000041)

用于生成数组的代码:

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

2米9秒(全部9秒),50圈:

1
2
3
4
5
6
7
8
9
        user       system     total       real
uniq    4.900000   0.000000   4.900000 (  4.890030)
count   0.350000   0.000000   0.350000 (  0.351340)
select  5.400000   0.010000   5.410000 (  5.393489)
detect  6.720000   0.000000   6.720000 (  6.685539)
all?    6.070000   0.000000   6.070000 (  6.061914)
mine    5.510000   0.010000   5.520000 (  5.500186)
delete  1.080000   0.010000   1.090000 (  1.084125)
any?    6.200000   0.000000   6.200000 (  6.197529)


这很有效:

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

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

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

相关答案:https://stackoverflow.com/a/1986398/1886534


这是另一个更快的方法(上面的计数方法仍然是最快的):

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

还有一个稍微慢一点的:

1
arr.inject(:&) == 9

以下是"果味"宝石对比:

1
2
3
4
5
6
7
8
9
10
11
12
13
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%


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

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


我想你的意思是"以东十一号"(0),因为你所得到的对我来说是假的。"更快"是什么意思?这个代码需要运行得很快吗?我认为检测速度更快,因为它将返回与测试匹配的第一个元素:

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