尾遞迴優化
尾遞迴優化是利用上面的第一個特點“呼叫同一個方法”來進行優化的
尾遞迴優化其實包括兩個東西:1)尾遞迴的形式;2)編譯器對尾遞迴的優化
尾遞迴的形式
尾遞迴其實只是一種對遞迴的特殊寫法,這種寫法原本並不會帶來跟遞迴不一樣的影響,它只是寫法不一樣而已,寫成這樣不會有任何優化效果,該爆的棧和幀都還會爆
具體不一樣在哪裡
前面說了,遞迴的本質是某個方法呼叫了自身,尾遞迴這種形式就要求:某個方法呼叫自身這件事,一定是該方法做的最後一件事(所以當有需要返回值的時候會是return f(n),沒有返回的話就直接是f(n)了)
要求很簡單,就一條,但是有一些常見的誤區
這個f(n)外不能加其他東西,因為這就不是最後一件事了,值返回來後還要再幹點其他的活,變數空間還需要保留
比如如果有返回值的,你不能:乘個常數 return 3f(n);乘個n return n*f(n);甚至是 f(n)+f(n-1)
另外,使用return的尾遞迴還跟函數語言程式設計有一點關係
編譯器對尾遞迴的優化
上面說了,你光手動寫成尾遞迴的形式,並沒有什麼卵用,要實現優化,還需要編譯器中加入了對尾遞迴優化的機制
有了這個機制,編譯的時候,就會自動利用上面的特點一來進行優化
具體是怎麼優化的:
簡單說就是重複利用同一個棧幀,不僅不用釋放上一個,連下一個新的都不用開,效率非常高(有人做實驗,這個比遞推比迭代都要效率高)
為什麼寫成尾遞迴的形式,編譯器就能優化了?或者說【編譯器對尾遞迴的優化】的一些深層思想
說是深層思想,其實也是因為正好編譯器其實在這裡沒做什麼複雜的事,所以很簡單
由於這兩方面的原因,尾遞迴優化得以實現,而且效果很好
因為在遞迴呼叫自身的時候,這一層函式已經沒有要做的事情了,雖然被遞迴呼叫的函式是在當前的函式裡,但是他們之間的關係已經在傳參的時候了斷了,也就是這一層函式的所有變數什麼的都不會再被用到了,所以當前函式雖然沒有執行完,不能彈出棧,但它確實已經可以出棧了,這是一方面
另一方面,正因為呼叫的是自身,所以需要的儲存空間是一毛一樣的,那乾脆重新重新整理這些空間給下一層利用就好了,不用銷燬再另開空間
有人對寫成尾遞迴形式的說法是【為了告訴編譯器這塊要尾遞迴】,這種說法可能會導致誤解,因為不是隻告訴編譯器就行,而是你需要做優化的前半部分,之後編譯器做後半部分
所以總結:為了解決遞迴的開銷大問題,使用尾遞迴優化,具體分兩步:1)你把遞迴呼叫的形式寫成尾遞迴的形式;2)編譯器碰到尾遞迴,自動按照某種特定的方式進行優化編譯
https://blog.csdn.net/yan_chou/article/details/59488871
斐波那契數列
常規的斐波那契數列演算法可能是這樣的:
1 2 3 4 5 6 7 |
int
fib(
int
n) {
if
(n <= 2) {
return 1;
}
return
fib(n - 1) + fib(n - 2);
}
|
上面的這種遞迴計算最終的return操作是加法操作。所以不是尾遞迴。
如果用尾遞迴就是這樣的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
/**
計算第n位斐波那契數列的值
@param n 第n個數
@param acc1 第n個數
@param acc2 第n與第n+1個數的和
@return 返回斐波那契數列值
*/
int
tailfib(
int
n,
int
acc1,
int
acc2) {
if
(n < 2) {
return
acc1;
}
return
tailfib(n-1,acc2,acc1 + acc2);
}
|
比如我們想計算第10位斐波那契數列的值,只需要fib(10,1,1)即可。
看一下測試效果,測試程式如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
int
main(
int
argc,
const
char
* argv[]) {
clock_t start,finish;
start = clock();
printf(
"計算結果:%d\n"
, fib(45));
finish = clock();
printf(
"花費時間--------%lu\n"
,finish - start);
start = clock();
printf(
"計算結果:%d\n"
, tailfib(45,1,1));
finish = clock();
printf(
"花費時間--------%lu\n"
,finish - start);
return
0;
}
|
計算結果如下:
1 2 3 4 5 |
計算結果:1134903170
花費時間--------5540692
計算結果:1134903170
花費時間--------4
Program ended with exit code: 0
|
https://www.cnblogs.com/zhanggui/p/7722541.html