最佳化遞迴的效率

來源:互聯網
上載者:User

函數遞迴調用是很常見的做法,但是它往往是低效的,本文探討最佳化遞迴效率的思路。

1.尾遞迴轉換成迭代

尾遞迴是一種簡單的遞迴,它可以用迭代來代替 比如 求階乘函數的遞迴表達

int f(int n)
...{
    if(n<=0)return 1;
    return n*f(n-1);
}

可以轉換成完全等價的迴圈迭代

int f(int n)
...{
    int r=0;
    while(n-->0)
        r*=n;
    return r;
}

尾遞迴是最簡單的情形,好的編譯器甚至可以自動的識別尾遞迴並把它轉換成迴圈迭代。

2.動態規劃

我一直把動態規劃看作尾遞迴的推廣(個人觀點),在2維或更高的情況下,直接使用遞迴會造成大量的重複計算,例如在斐波那契數列的遞迴關係 Fib(n+2)=Fib(n+1)+Fib(n)中 Fib(3)在計算Fib(4)和Fib(5)都會用到 他被重複計算了2遍,當數列長度增大時 這種浪費會變得越來越明顯。

動態規劃方法將可能需要的結果全部計算出來 並儲存 一般來說 動態規劃從最小數開始 自底向上計算所有值(因為後面的結果會用到前面的結果) 直到得到的結果中包含了要求的結果。

int Fib(unsigned n)
...{
    if(n==1)return 1;
    if(n==0)return 0;
    return Fib(n-1)+Fib(n-2);
}


int Fib(unsigned n)
...{
    int* f=new int[n+1];
    f[1]=1;f[0]=0;
    for(int i=0;i<=n;i++);
        f[i]=f[i-1]+f[i-2];  
    int r=f[n];
    delete[] f;
    return r;
}

動態規劃不會造成重複運算 但是 它可能計算不需要的結果 例如關係式
     a(n)=a(n-2)+a(n-4);
使用動態規劃會計算很多不需要的結果,儘管如此,它的效率遠遠高於直接遞迴運算。

實際上,大部分時候動態規劃把指數級時間複雜度的遞迴運算變成了多項式級時間複雜度的遞推。

3.備忘錄

減少重複值的另一個方法是使用備忘錄,每次成功計算一個結果 ,就將它存入備忘錄中,當再次遇到此問題時,無需重複計算,直接取出即可。

備忘錄方法和直接遞迴相似,只是在函數在計算之前先訪問備忘錄,如果在備忘錄中找到,就無須再計算,直接返回。

備忘錄結構可以使用關聯陣列 雜湊表等實現,特別地,當遞迴參數是整數時 直接用數組就可以了。

與動態規劃相比,備忘錄消耗了額外的備忘錄尋找時間,並且和直接遞迴一樣 有大量的多餘函數調用開銷,但它不會造成額外計算。

4.內聯

這是來自Efficient C++的方法,C++編譯器不會把遞迴函式內聯,這樣,函數調用的開銷變得很大。為了提高效率,必須手動內嵌函式。
遞迴函式無法完全內聯,但是我們可以把它展開,這是前面Fibnacci函數的一層展開

int Fib(unsigned n)
...{
    if(n==1)return 1;
    if(n==0)return 0;

    int fn1,fn2;
    if(n-1==1)fn1=1;
    else if(n-1==0)fn1=0;
    else fn1=Fib(n-2)+Fib(n-3);

    if(n-2==1)fn2=1;
    else if(n-2==0)fn2=0;
    else fn2=Fib(n-3)+Fib(n-4);

    return fn1+fn2
}

 5.解遞迴
儘管我們傾向於虐待電腦 讓它幫我們處理較複雜的問題,但是很多時候我們需要獲得效率,就必須自己動手,其實很多遞迴式可以手動解出,組合數學為我們提供了不少工具:
     (1)解齊次遞迴方程

簡單的遞迴方程可以按以下步驟求解:

解Fibonacci函數的遞迴方程首先把它轉換成下面的式子這個代數方程被稱為遞迴方程的特徵方程,下一步是解特徵方程求出它的所有解,對這個例子來說根據組合數學的結論再由F(0)=0和F(1)=1可以得出方程組解這個方程組可得到a和b的值代入前面的公式可以求出Fibnacci通項公式驗證後可以知道,這個公式是正確的,顯然用此公式做浮點運算,比使用遞迴方式快得多。 (2)母函數 組合數學中還提供了一種更強有力的處理手段:產生函數(又稱為母函數)仍然以求解Fibonacci數列為例,為了易於理解,我這裡把產生函數描述成一個以Fibonacci數列為係數的多項式。即現在考慮這樣一個多項式按g(x)的定義,可以這樣展開這個時候不要忘記我們的遞推公式,可以進一步化簡為這個式子與g(x)非常像,將它兩邊同時乘以x再整理一下等式利用F(0)=0,F(1)=1可以求出這樣 我們求出了g(x),等等,g(x)是什麼來著?不是以Fibonacci數列為係數的多項式嗎?現在求出的東西是什嗎?不要急,用泰勒級數展開就能得到通項公式了,結果跟上面的是一樣的。(太麻煩了,略過略過^_^)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.