我看到了三種不同的方式來編寫斐波那契函數的遞歸形式:數學內聯,數學內聯結果緩存和一個使用尾遞歸。我知道使用memoization將O(N)算法轉換爲O(1),緩存後的答案。但我無法理解tail call優化如何能夠提供如此大的幫助。我的印象是它可能會阻止一些副本或類似的東西。它幾乎和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
}
時報紅寶石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)
這裏是一個memoizes答案版本,這很明顯爲什麼這對我來說很快。一旦它已經做任何以下請求運行在O數學(1)的時間,所以當包括在一個循環中它它仍然運行在O(N)時間在最壞的情況下:
puts Benchmark.measure {
# Fibonacci numbers WITH memoization.
# Initialize the memoization array.
@scratchpad = []
@max_fibo_size = 50
([email protected]_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)}"
}
}
時報紅寶石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
}
時報紅寶石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)
我不能相信我錯過了!我讀過它,它根本不會發生在我身上,那就是它在做什麼。 – Sqeaky
「Ruby不會做任何事情來優化尾部呼叫」 - 但是,它也不會對*優化尾部呼叫做任何事情。 (與Python不同,TCO被明確禁止)。對於Ruby實現來說,執行TCO是完全合法的,有些則是這樣。 YARV可以執行TCO,並且在執行TCO的JVM(如IBM J9)上運行JRuby可能會或可能不會導致某些方法獲得TCO'd。 –