優化遞迴的效率

2021-09-05 17:55:16 字數 1308 閱讀 1561

函式遞迴呼叫是很常見的做法,但是它往往是低效的,本文**優化遞迴效率的思路。

1.尾遞迴轉換成迭代

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

f(int

n)...

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

f(int

n)...

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

2.動態規劃

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

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

fib(unsigned n)

...

intfib(unsigned n)

...

動態規劃不會造成重複運算 但是 它可能計算不需要的結果 例如關係式

a(n)=a(n-2)+a(n-4);

使用動態規劃會計算很多不需要的結果,儘管如此,它的效率遠遠高於直接遞迴運算。

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

3.備忘錄

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

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

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

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

4.內聯

這是來自efficient c++的方法,c++編譯器不會把遞迴函式內聯,這樣,函式呼叫的開銷變得很大。為了提高效率,必須手動內聯函式。

遞迴函式無法完全內聯,但是我們可以把它展開,這是前面fibnacci函式的一層展開

fib(unsigned n)

...

5.解遞迴

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

(1)解齊次遞迴方程

(2)母函式

遞迴效率與非遞迴效率

題目1384 二維陣列中的查詢時間限制 1 秒 記憶體限制 32 兆 特殊判題 否 提交 10472 解決 2093 題目描述 在乙個二維陣列中,每一行都按照從左到右遞增的順序排序,每一列都按照從上到下遞增的順序排序。請完成乙個函式,輸入這樣的乙個二維陣列和乙個整數,判斷陣列中是否含有該整數。輸入 ...

漫談遞迴 遞迴的效率問題

遞迴在解決某些問題的時候使得我們思考的方式得以簡化,也更加精煉,容易閱讀。那麼既然遞迴有這麼多的優點,我們是不是什麼問題都要用遞迴來解決呢?難道遞迴就沒有缺點嗎?今天我們就來討論一下遞迴的不足之處。談到遞迴就不得不面對它的效率問題。為什麼遞迴是低效的 還是拿斐波那契 fibonacci 數列來做例子...

遞迴 遞迴的優化

遞迴演算法在工作或者各種資料結構中使用比較頻繁,遞迴演算法的簡化常見有自頂向下還有備忘錄法 自頂向下 t n t1 n t2 n t3 n 25c t1 n r11p1 r12p2 r13p3 r14p4 r15p5 r16p6 r17p7 r18p8 r19p9 t1 n 1 x tau1 t1 ...