这是一个很好的机会,可以将我在大学时代的“算法推导”课程,即 Dijkstra 方法,从大学时代回到实践中。这是'干净'版本
require 'set'
def generate_perms(str)
if str.length == 1
return Set.new([str.downcase, str.upcase])
else
head = str[0..0]
tail = str[1..-1]
perms_sub = generate_perms(tail)
d = Set.new(perms_sub.collect{|p| head.downcase + p})
u = Set.new(perms_sub.collect{|p| head.upcase + p})
return d | u
end
end
编辑:Dijkstra 教我们不要过早优化,所以我认为最好单独添加 Array-version :-) :
def perms(str)
if str.length == 1
return Array.new([str.downcase, str.upcase])
else
head = str[0..0]
tail = str[1..-1]
perms_sub = perms(tail)
d = perms_sub.collect{|p| head.downcase + p}
u = perms_sub.collect{|p| head.upcase + p}
return (d + u).uniq
end
end
为了让它飞快地转换成尾递归,在一个额外的方法参数的帮助下:
# tail recursion version, no stack-overflows :-)
def perms_tail(str, perms)
if str.length == 1
return perms.collect{|p| [p + str.upcase, p+ str.downcase]}.flatten.uniq
else
tail = perms.collect{|p| [p + str[0..0].upcase, p+ str[0..0].downcase]}.flatten
perms_tail(str[1..-1], tail)
end
end
begin
perms_tail("tst1",[""]).each{|p| puts p}
end
现在这实际上非常慢,但尾递归允许简单地重写(自己查看)到优化版本:
def perms_fast_tail(str)
perms = [""]
tail = str.downcase
while tail.length > 0 do
head, tail, psize = tail[0..0], tail[1..-1], perms.size
hu = head.upcase
for i in (0...psize)
tp = perms[i]
perms[i] = tp + hu
if hu != head
perms.push(tp + head)
end
end
end
perms
end
这有多重要?好吧,让我们运行一些定时测试,以获得乐趣:
begin
str = "Thequickbrownfox"
start = Time.now
perms_tail(str,[""])
puts "tail: #{Time.now - start}"
start2 = Time.now
perms(str)
puts "normal: #{Time.now - start2}"
start3 = Time.now
perms_fast_tail(str)
puts "superfast: #{Time.now - start3}"
end
在我的机器上,这显示了差异:
tail: 0.982241
normal: 0.285104
superfast: 0.168895
从不平凡的字符串中可以看出速度的提高和性能的好处; “tst1”将在干净版本中快速运行。因此,Dijkstra 是对的:不需要优化。虽然这样做很有趣。