我正在为以下问题执行一些速度测试:
给定2个字符串s1和s2,它们只包含小写字母,如果可以重新排列s1的字母使得s2成为s1的子字符串,则输出。
我已经在ruby中获得了2个解决方案:
版本1:
def scramble(s1,s2)
if s1.length < s2.length
return false
end
a = Array.new(26) { 0 }
b = Array.new(26) { 0 }
t1 = Time.now
(0 ... s1.length).each do |x|
a[s1[x].ord - 97] += 1
end
(0 ... s2.length).each do |x|
b[s2[x].ord - 97] += 1
end
t2 = Time.now
(0 ... 26).each do |x|
if a[x] < b[x]
return false
end
end
puts t2 - t1
return true
end
此版本将s1和s2中的字符数保存在直接寻址表中,并比较每个字符的计数。应该清楚的是,该代码执行大约2 *(N + M)次操作,其中N是s1的长度,M是s2的长度。
第2版:
def scramble(s1,s2)
t1 = Time.now
c = s2.chars
c.uniq!
t = c.all?{|x| s2.count(x)<=s1.count(x)}
t2 = Time.now
puts t2 - t1
return t
end
此版本还使用s1和s2中的字符数,但不使用直接寻址表。根据我的理解,这个版本应该执行大约26 *(N + M)次操作,因为count()方法的复杂性是字符串中字符数的线性,并且为字符串中的每个不同字符调用它。
当我表演时
scramble('abcdefghijklmnopqrstuvwxyz'*500000, 'abcdefghijklmnopqrstuvwxyz'*500000)
第一个版本需要4.424207,而第二个版本只需2.574269。我用不同长度的s1和s2运行了几次测试,结果是一致的(版本2比版本1快得多)。由于它们的不同常数,我真的很困惑。为什么版本2中的代码运行速度比版本1快得多,尽管有更高的常量?
有人可以告诉我吗?
答案 0 :(得分:4)
我认为这是因为像String#count
这样的标准库方法是用C实现的,这比在循环中执行复杂的Ruby表达式a[s1[x].ord - 97] += 1
500000次要少。
要了解我的意思,请尝试更换这些循环:
(0 ... s1.length).each do |x|
a[s1[x].ord - 97] += 1
end
(0 ... s2.length).each do |x|
b[s2[x].ord - 97] += 1
end
调用String#count
:
(0 ... 26).each do |x|
a[x] = s1.count((x + 97).chr)
b[x] = s2.count((x + 97).chr)
end
通过此更改,它在我的机器上运行0.4秒(相比之前的6.3秒)!