【发布时间】:2023-03-16 07:05:01
【问题描述】:
我看到了三种不同的方式来编写斐波那契函数的递归形式:使用数学内联、使用数学内联和结果缓存以及一种使用尾递归。我知道在缓存答案后,使用 memoization 会将 O(N) 算法转换为 O(1)。但我不明白尾调用优化如何能有这么大的帮助。我的印象是它可能会阻止一些副本或类似的东西。它几乎与 O(1) 一样快。 Ruby 做了什么让这个速度如此之快?
这是内联数学的缓慢幼稚实现。这显然是运行最慢的 O(N) 时间,然后在 O(N^2) 时间内循环显示。
puts Benchmark.measure {
# Calculate the nth Fibonacci number, f(n).
def fibo (n)
if n <= 1
return n
else
value = fibo(n-1) + fibo(n-2)
return value
end
end
# Display the Fibonacci sequence.
(1..40).each do |number|
puts "fibo(#{number}) = #{fibo(number)}"
end
}
时代 Ruby 1.9.3:55.989000 0.000000 55.989000 ( 55.990000)
时代 JRuby 1.7.9:51.629000 0.000000 51.629000 (51.629000)
来源(http://rayhightower.com/blog/2014/04/12/recursion-and-memoization/?utm_source=rubyweekly)
这是记忆答案的版本,很清楚为什么这对我来说很快。一旦它完成了数学运算,任何后续请求都会在 O(1) 时间内运行,因此当它包含在循环中时,在最坏的情况下它仍会在 O(N) 时间内运行:
puts Benchmark.measure {
# Fibonacci numbers WITH memoization.
# Initialize the memoization array.
@scratchpad = []
@max_fibo_size = 50
(1..@max_fibo_size).each do |i|
@scratchpad[i] = :notcalculated
end
# Calculate the nth Fibonacci number, f(n).
def fibo (n)
if n > @max_fibo_size
return "n must be #{@max_fibo_size} or less."
elsif n <= 1
return n
elsif @scratchpad[n] != :notcalculated
return @scratchpad[n]
else
@scratchpad[n] = fibo(n-1) + fibo(n-2)
return @scratchpad[n]
end
end
# Display the Fibonacci sequence.
(1..40).each { |number|
puts "fibo(#{number}) = #{fibo(number)}"
}
}
时代 Ruby 1.9.3:0.000000 0.000000 0.000000 ( 0.025000)
次 JRuby 1.7.9:0.027000 0.000000 0.027000 (0.028000)
来源(http://rayhightower.com/blog/2014/04/12/recursion-and-memoization/?utm_source=rubyweekly)
这个版本的尾调用递归版本几乎可以立即运行:
puts Benchmark.measure {
# Calculate the nth Fibonacci number, f(n). Using invariants
def fibo_tr(n, acc1, acc2)
if n == 0
0
elsif n < 2
acc2
else
return fibo_tr(n - 1, acc2, acc2 + acc1)
end
end
def fibo (n)
fibo_tr(n, 0, 1)
end
# Display the Fibonacci sequence.
(1..50).each do |number|
puts "fibo(#{number}) = #{fibo(number)}"
end
}
时代 Ruby 1.9.3:0.000000 0.000000 0.000000 ( 0.021000)
次 JRuby 1.7.9:0.041000 0.000000 0.041000 ( 0.041000)
来源(https://gist.github.com/mvidaurre/11006570)
【问题讨论】:
标签: ruby recursion jruby tail-recursion