標籤:ons 結果 特定 sharp 寫法 常見 複雜 個數 尾遞迴
尾遞迴最佳化是利用上面的第一個特點“調用同一個方法”來進行最佳化的
尾遞迴最佳化其實包括兩個東西: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)編譯器碰到尾遞迴,自動按照某種特定的方式進行最佳化編譯
59488871
斐波那契數列
常規的斐波那契數列演算法可能是這樣的:
| 1234567 |
int fib(int n) { if (n <= 2) { return 1; } return fib(n - 1) + fib(n - 2);} |
上面的這種遞迴計算最終的return操作是加法操作。所以不是尾遞迴。
如果用尾遞迴就是這樣的:
| 123456789101112131415 |
/** 計算第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)即可。
看一下測試效果,測試程式如下:
| 1234567891011121314151617 |
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; } |
計算結果如下:
| 12345 |
計算結果:1134903170花費時間--------5540692計算結果:1134903170花費時間--------4Program ended with exit code: 0 |
https://www.cnblogs.com/zhanggui/p/7722541.html
尾遞迴最佳化