为什么`str.reverse == str`比`str [0]!= str [-1]`更快?

时间:2015-06-03 06:30:20

标签: ruby

我运行了一些基准测试,并且想知道为什么反转字符串并将其与自身进行比较似乎比比较单个字符要快。

反转字符串是最坏情况O(n)和比较O(n),导致O(n),除非比较同一个对象,它应该是O(1)。但是

str = "test"
str.reverse.object_id == str.object_id # => false

字符比较最坏情况是O(1)吗?我错过了什么?

修改

我提取并简化了问题,但这里是我运行的代码。

def reverse_compare(str)
  str.reverse == str
end

def iterate_compare(str)
  # can test with just str[0] != str[-1]
  (str.length/2).times do |i|
    return false if str[i] != str[-i-1]
  end  
end

require "benchmark"

n = 2000000
Benchmark.bm do |x|
  str = "cabbbbbba" # best case single comparison
  x.report("reverse_compare") { n.times do reverse_compare(str)  ; a = "1"; end }  
  x.report("iterate_compare") { n.times do iterate_compare(str)  ; a = "1"; end }  
end

       user     system      total        real
reverse_compare  0.760000   0.000000   0.760000 (  0.769463)
iterate_compare  1.840000   0.010000   1.850000 (  1.855031)

2 个答案:

答案 0 :(得分:2)

有两个因素支持反向方法:

  • String#reverseString#==都是用纯C而不是ruby编写的。它们的内部循环已经使用了字符串的长度已知的事实,因此没有不必要的边界检查。
  • String#[]但是需要在每次调用时检查字符串边界。主循环也用ruby编写,因此也有点慢。此外,它总是会创建一个新的(一个字符长)字符串对象,以便返回需要处理的字符串,GCd等。

看起来这两个因素比通过更好的算法获得的性能提升更大,但这是在ruby中完成的。

另请注意,在测试中,您不会测试随机字符串,而是测试特定字符串,这也非常短。如果你尝试更大的那个,那么ruby实现可能会更快。

答案 1 :(得分:0)

证明@SztupY的一些想法。如果你改变了一下代码,就像这样:

A

你会得到一些不同的结果:

def reverse_compare(str)
  str.reverse == str
end

def iterate_compare(a, b)
  a != b
end

require "benchmark"

n = 2_000_000
Benchmark.bm do |x|
  str = "cabbbbbba" # best case single comparison
  a = str[0]; b = str[-1]
  x.report("reverse_compare") { n.times do reverse_compare(str)  ; end }  
  x.report("iterate_compare") { n.times do iterate_compare(a, b)  ; end }  
end

所以,你现在可以猜到从String创建2个字符串对象需要一些时间。