遞迴演算法想必大家都已經很熟悉了。遞迴演算法雖然簡單,但是容易導致一些效能問題,於是就有了尾遞迴這種優化演算法。
首先我們先看看遞迴演算法的效能問題是在**?
比如我們有乙個常見的演算法,叫做階乘演算法。
f (x
)=1⋅
2⋅3⋯
x
f(x)=1\cdot2\cdot3\cdots\!x
f(x)=1
⋅2⋅3
⋯x他的遞迴實現是這樣子的
katex parse error: unknown column alignment: 1 at position 15: \beginf(x)=x f(x-1)\…
實現**如下
//c#實現
intfoo
(int x)
return x*
foo(x-1)
;}
#python 實現
deffoo
(x):
if(x==1)
:return
1return x*foo(x-
1)
我們看到每次呼叫foo方法的時候,又會執行一次foo方法。
此時程式會將當前上下文壓棧,計算出下乙個foo的值,然後再出棧和x進行相乘
所以對於foo(3)的呼叫,整個棧的情況是這樣的
katex parse error: unknown column alignment: 1 at position 15: \beginfoo(3)\\3\cdot…
那麼尾遞迴呢?
它是指函式的最後乙個位置(或者動作)是呼叫自身
我們把上面的方法改一下尾遞迴
//c#尾遞迴實現
intfoo
(int x,
int result=1)
return
foo(x-
1,x*result)
;}
#python 尾遞迴實現
deffoo2
(x,result=1)
:if(x==1)
:return result
return foo2(x-
1,result*x)
這裡有兩個需要注意的點
那麼這麼有什麼好處呢?
好處就是「聰明」的編譯器在準備入棧時發現,咦,這裡的遞迴放回值不需要做任何計算,直接返回更上一層就好了。那麼儲存上下文沒有啥好處,不存了!!
所以此時的棧使用情況就會變成
katex parse error: unknown column alignment: 1 at position 15: \beginfoo2(3)\\foo2(…
記憶體占用,顯著減少
不過尾遞迴雖好,但是還是要依賴於各種編譯器的支援。
目前我知道的是python是支援的,探索c#之尾遞迴編譯器優化 - 蘑菇先生 - 文章中表示64位release下會進行尾遞迴優化
本文會經常更新,請閱讀個人部落格原文: ,以避免陳舊錯誤知識的誤導,同時有更好的閱讀體驗。
什麼是尾遞迴?
求階乘 n def fact n if n 1 return 1return n fact n 1 理論上,所有的遞迴函式都可以寫成迴圈的方式,但迴圈的邏輯不如遞迴清晰 在計算機中,函式呼叫是通過棧 stack 這種資料結構實現的,每當進入乙個函式呼叫,棧就會加一層棧幀,每當函式返回,棧就會減一層棧...
什麼是 遞迴 和 尾遞迴
一 生活中的例子 場景 問路 遞迴 問題 天安門怎麼走?等待回答 a 左拐。接下來怎麼走不知道了,你等下,我去問b a等待b的回答 b 右拐。接下來怎麼走不知道了,你等下,我去問c b等待c的回答 c 左拐。接下來怎麼走不知道了,你等下,我去問d c等待d的回答 d 直行就到了。提問者 a b c ...
什麼是尾遞迴 尾遞迴的底層實現原理
什麼是尾遞迴 尾遞迴的底層實現原理 什麼是尾遞迴?尾遞迴就是函式最後的語句是呼叫函式自身,但呼叫自己的時候,已經 不再需要上乙個函式的環境了。所以並非所有的遞迴都屬於尾遞迴,它需要通過上述的規則來編 寫遞迴 和普通的遞迴相比,尾遞迴即使遞迴呼叫數萬次,它的函式棧也僅為常數,不會出 現stack ov...