乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      漫談遞歸:從斐波那契開始了解尾遞歸

       herowuking 2015-10-10

      尾遞歸(tail recursive),看名字就知道是某種形式的遞歸。簡單的說遞歸就是函數(shù)自己調用自己。那尾遞歸和遞歸之間的差別就只能體現(xiàn)在參數(shù)上了。

      尾遞歸wiki解釋如下:

      尾部遞歸是一種編程技巧。遞歸函數(shù)是指一些會在函數(shù)內(nèi)調用自己的函數(shù),如果在遞歸函數(shù)中,遞歸調用返回的結果總被直接返回,則稱為尾部遞歸。尾部遞歸的函數(shù)有助將算法轉化成函數(shù)編程語言,而且從編譯器角度來說,亦容易優(yōu)化成為普通循環(huán)。這是因為從電腦的基本面來說,所有的循環(huán)都是利用重復移跳到代碼的開頭來實現(xiàn)的。如果有尾部歸遞,就只需要疊套一個堆棧,因為電腦只需要將函數(shù)的參數(shù)改變再重新調用一次。利用尾部遞歸最主要的目的是要優(yōu)化,例如在Scheme語言中,明確規(guī)定必須針對尾部遞歸作優(yōu)化??梢娢膊窟f歸的作用,是非常依賴于具體實現(xiàn)的。

      我們還是從簡單的斐波那契開始了解尾遞歸吧。

      用普通的遞歸計算Fibonacci數(shù)列:

      01#include "stdio.h"
      02#include "math.h"
      03 
      04int factorial(int n);
      05 
      06int main(void)
      07{
      08    int i, n, rs;
      09 
      10    printf("請輸入斐波那契數(shù)n:");
      11    scanf("%d",&n);
      12 
      13    rs = factorial(n);
      14    printf("%d \n", rs);
      15 
      16    return 0;
      17}
      18 
      19// 遞歸
      20int factorial(int n)
      21{
      22    if(n <= 2)
      23    {
      24        return 1;
      25    }
      26    else
      27    {
      28        return factorial(n-1) + factorial(n-2);
      29    }
      30}

      程序員運行結果如下:

      1請輸入斐波那契數(shù)n:20
      26765
      3 
      4Process returned 0 (0x0)   execution time : 3.502 s
      5Press any key to continue.

      在i5的CPU下也要花費 3.502 秒的時間。

      下面我們看看如何用尾遞歸實現(xiàn)斐波那契數(shù)。

      01#include "stdio.h"
      02#include "math.h"
      03 
      04int factorial(int n);
      05 
      06int main(void)
      07{
      08    int i, n, rs;
      09 
      10    printf("請輸入斐波那契數(shù)n:");
      11    scanf("%d",&n);
      12 
      13    rs = factorial_tail(n, 1, 1);
      14    printf("%d ", rs);
      15 
      16    return 0;
      17}
      18 
      19int factorial_tail(int n,int acc1,int acc2)
      20{
      21    if (n < 2)
      22    {
      23        return acc1;
      24    }
      25    else
      26    {
      27        return factorial_tail(n-1,acc2,acc1+acc2);
      28    }
      29}

      程序員運行結果如下:

      1請輸入斐波那契數(shù)n:20
      26765
      3Process returned 0 (0x0)   execution time : 1.460 s
      4Press any key to continue.

      快了一倍有多。當然這是不完全統(tǒng)計,有興趣的話可以自行計算大規(guī)模的值,這里只是介紹尾遞歸而已。

      我們可以打印一下程序的執(zhí)行過程,函數(shù)加入下面的打印語句:

      01int factorial_tail(int n,int acc1,int acc2)
      02{
      03    if (n < 2)
      04    {
      05        return acc1;
      06    }
      07    else
      08    {
      09        printf("factorial_tail(%d, %d, %d) \n",n-1,acc2,acc1+acc2);
      10        return factorial_tail(n-1,acc2,acc1+acc2);
      11    }
      12}

      程序運行結果:

      01請輸入斐波那契數(shù)n:10
      02factorial_tail(9, 1, 2)
      03factorial_tail(8, 2, 3)
      04factorial_tail(7, 3, 5)
      05factorial_tail(6, 5, 8)
      06factorial_tail(5, 8, 13)
      07factorial_tail(4, 13, 21)
      08factorial_tail(3, 21, 34)
      09factorial_tail(2, 34, 55)
      10factorial_tail(1, 55, 89)
      1155
      12Process returned 0 (0x0)   execution time : 1.393 s
      13Press any key to continue.

      從上面的調試就可以很清晰地看出尾遞歸的計算過程了。acc1就是第n個數(shù),而acc2就是第n與第n+1個數(shù)的和,這就是我們前面講到的“迭代”的精髓,計算結果參與到下一次的計算,從而減少很多重復計算量。

      fibonacci(n-1,acc2,acc1+acc2)真是神來之筆,原本樸素的遞歸產(chǎn)生的棧的層次像二叉樹一樣,以指數(shù)級增長,但是現(xiàn)在棧的層次卻像是數(shù)組,變成線性增長了,實在是奇妙,總結起來也很簡單,原本棧是先擴展開,然后邊收攏邊計算結果,現(xiàn)在卻變成在調用自身的同時通過參數(shù)來計算。

      小結

      尾遞歸的本質是:將單次計算的結果緩存起來,傳遞給下次調用,相當于自動累積。

      在Java等命令式語言中,尾遞歸使用非常少見,因為我們可以直接用循環(huán)解決。而在函數(shù)式語言中,尾遞歸卻是一種神器,要實現(xiàn)循環(huán)就靠它了。

      很多人可能會有疑問,為什么尾遞歸也是遞歸,卻不會造成棧溢出呢?因為編譯器通常都會對尾遞歸進行優(yōu)化。編譯器會發(fā)現(xiàn)根本沒有必要存儲棧信息了,因而會在函數(shù)尾直接清空相關的棧。

        本站是提供個人知識管理的網(wǎng)絡存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權內(nèi)容,請點擊一鍵舉報。
        轉藏 分享 獻花(0

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多