最有效的方法?
比较排序的字符串当然是最简单的方法,但如果效率是最重要的,你能做得更好吗?上个月@raph 发布了一个comment,它提出了一种对我来说听起来不错的方法。我打算将它与标准测试进行基准测试,但一直没有解决。我回答的目的是将建议的方法与标准方法进行基准测试。
挑战者
这个想法是为其中一个字符串中的字符创建一个计数哈希h,以便h['c'] 等于'c' 在字符串中出现的次数。然后一个遍历第二个字符串的字符。假设'c' 是这些字符之一。然后false方法返回h.key?('c') => false或h['c'] == 0(也可以写成h['c'].to_i == 0,如nil.to_i => 0);否则,将根据散列检查第二个字符串的下一个字符。假设字符串长度相等,当且仅当在检查完第二个字符串的所有字符后没有返回 false 时,它们才是彼此的字谜。为两个字符串中较短的一个创建哈希可能会提供进一步的改进。这是我的方法代码:
def hcompare(s1,s2)
return false unless s1.size == s2.size
# set `ss` to the shorter string, `sl` to the other.
ss, sl = (s1.size < s2.size) ? [s1, s2] : [s2, s1]
# create hash `h` with letter counts for the shorter string:
h = ss.chars.each_with_object(Hash.new(0)) { |c,h| h[c] += 1}
#decrement counts in `h` for characters in `sl`
sl.each_char { |c| return false if h[c].to_i == 0; h[c] -= 1 }
true
end
现任
def scompare(s1,s2)
s1.chars.sort == s2.chars.sort
end
帮助者
methods = [:scompare, :hcompare]
def compute(m,s1,s2)
send(m,s1,s2)
end
def shuffle_chars(s)
s.chars.shuffle.join
end
测试数据
reps = 20
ch = [*'b'..'z']
基准
require 'benchmark'
[50000, 100000, 500000].each do |n|
t1 = Array.new(reps) { (Array.new(n) {ch.sample(1) }).join}
test_strings = { true=>t1.zip(t1.map {|s| shuffle_chars(s)})}
test_strings[false] = t1.zip(t1.map {|t| shuffle_chars((t[1..-1] << 'a'))})
puts "\nString length #{n}, #{reps} repetitions"
[true, false].each do |same|
puts "\nReturn #{same} "
Benchmark.bm(10) do |bm|
methods.each do |m|
bm.report m.to_s do
test_strings[same].each { |s1,s2| compute(m,s1,s2) }
end
end
end
end
end
进行的比较
我比较了scompare(使用sort)和hcompare(使用hash)这两种方法,针对三个字符串长度执行基准测试:50,000、100,000 和 500,000 个字符。对于每个字符串长度,我通过从[*('b'..'z')] 中随机选择每个字符来创建两个字符串中的第一个。然后我创建了两个字符串与第一个进行比较。一个只是对第一个字符串的字符进行混洗,因此当比较这两个字符串时,这些方法将返回true。在第二种情况下,我做了同样的事情,只是我用'a' 替换了一个随机选择的字符,所以这些方法将返回false。这两种情况在下面标记为true 和false。
结果
String length 50000, 20 repetitions
Return true
user system total real
scompare 0.620000 0.010000 0.630000 ( 0.625711)
hcompare 0.840000 0.010000 0.850000 ( 0.845548)
Return false
user system total real
scompare 0.530000 0.000000 0.530000 ( 0.532666)
hcompare 1.370000 0.000000 1.370000 ( 1.366293)
String length 100000, 20 repetitions
Return true
user system total real
scompare 1.420000 0.100000 1.520000 ( 1.516580)
hcompare 2.280000 0.010000 2.290000 ( 2.284189)
Return false
user system total real
scompare 1.020000 0.010000 1.030000 ( 1.034887)
hcompare 1.960000 0.000000 1.960000 ( 1.962655)
String length 500000, 20 repetitions
Return true
user system total real
scompare 10.310000 0.540000 10.850000 ( 10.850988)
hcompare 9.960000 0.180000 10.140000 ( 10.153366)
Return false
user system total real
scompare 8.120000 0.570000 8.690000 ( 8.687847)
hcompare 9.160000 0.030000 9.190000 ( 9.189997)
结论
如您所见,使用计数哈希的方法仅在一种true 情况下(n => 500,000)优于使用sort。即使在那里,胜利的幅度也很小,比大多数其他基准比较中的相对差异要小得多,标准方法在这些比较中获胜。虽然哈希计数方法可能在不同的测试中表现更好,但似乎传统的排序方法很难被击败。
这个答案有趣吗?我不确定,但由于在看到结果之前我已经完成了大部分工作(我预计这将有利于计数哈希),所以我决定继续进行。