1. 程式人生 > >尾遞迴優化

尾遞迴優化

尾遞迴優化是利用上面的第一個特點“呼叫同一個方法”來進行優化的 
尾遞迴優化其實包括兩個東西: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