迭代演算法與遞迴演算法概述

2021-06-25 12:53:35 字數 3152 閱讀 2748

遞迴(recursive)的基本概念:程式呼叫自身的程式設計技巧稱為遞迴,是函式自己呼叫自己。 

迭代(iterative)的基本概念:利用變數的原值推算出變數的乙個新值,如果遞迴是自己呼叫自己的話,迭代就是a不停的呼叫b。 

遞迴與迭代都是基於控制結構:迭代用重複結構,而遞迴用選擇結構。遞迴與迭代都涉及重複:迭代顯式使用重複結構,而遞迴通過重複函式呼叫實現重複。遞迴與迭代都涉及終止測試:迭代在迴圈條件失敗時終止,遞迴在遇到基本情況時終止。使用計數器控制重複的迭代和遞迴都逐漸到達終止點:迭代一直修改計數器,直到計數器值使迴圈條件失敗;遞迴不斷產生最初問題的簡化副本,直到達到基本情況。迭代和遞迴過程都可以無限進行:如果迴圈條件測試永遠不變成false,則迭代發生無限迴圈;如果遞迴永遠無法回推到基本情況,則發生無窮遞迴。 

簡單示例**: 

//這是遞迴 

int funca(int n)   

//這是迭代 

int funcb(int n)   

在演算法分析與設計中,遞迴與迭代是我們解決迴圈問題常用的兩種方法。那麼,在既可以用遞迴演算法又可以用迭代演算法解決的問題中,我們究竟該選用哪種演算法呢?在程式設計中,我們不但講求**所能實現的功能,而且在實現相同功能的同時,更注重優化**、提高**的執行效率。這也是我們在選擇遞迴還是迭代思想時考慮的主要因素。 

1、遞迴和迭代概述 

如果乙個問題剛開始難以解決,可以將其簡化後再嘗試解決。如果這個過程可以重複進行,問題最終會變得容易處理。由此引出兩種不同的方法:遞迴和迭代。迴圈或迭代,是一種重複執行乙個過程的方法;遞迴是另一種方法。遞迴函式是通過呼叫函式自身來完成任務,而且在每次呼叫自身時減少任務量。而迭代是迴圈的一種形式,這種迴圈不是由使用者輸入而控制,每次迭代步驟都必須將剩餘的任務減少;也就是說,迴圈的每一步都必須執行乙個有限的過程,並留下較少的步驟。迴圈的進度通常用乙個在每次迭代時都進行自增或自減的變數的值來衡量,直到到達預定的目標為止。用遞迴演算法表示許多問題的求解方法時演算法思想非常簡潔。但是遞迴演算法不僅時間效率非常差,而且由於遞迴演算法是不斷的函式呼叫和函式返回過程,因此其實際的計算機執行時間通常遠大於迴圈方式演算法的計算機執行時間,甚至在有限的時間內無法求解。這就存在乙個把遞迴演算法化為非遞迴演算法的問題。 

2、需要用迭代消解遞迴的情況  

遞迴演算法特別適合於所研究的問題或所處理的資料本身是遞迴定義的情況。然而,並不意味著這種遞迴定義保證遞迴演算法是解決該問題的最好方法。事實上,主要是因為拿那種不合適的例子來解釋遞迴演算法概念,從而造成了對程式設計中使用遞迴的普遍懷疑和否定態度,並把遞迴同低效等同起來。而且在遞迴演算法中,往往會因為追求**短或者在求解問題時一味追求規律性,多用了無用的壓棧和出棧的操作。比如用迴圈消解的尾遞迴,是多了無用的壓棧和出棧才使速度受損的;斐波那契數列計算的遞迴改迴圈迭代所帶來的速度大幅提公升,是因為改掉了重複計算的毛病。假使乙個遞迴過程中本身包含了大量冗餘的操作,並且這個過程又可以用迭代來達到相同的效果。這時,我們就一般用迭代來消解遞迴。也就是說尾遞迴演算法和單向遞迴演算法可用迭代演算法來代替。可以用乙個方案來描述人們力圖在其中避免使用演算法遞迴的程式,這個方案展示了其構成的模型。(1)式或等價的(2)式就是這個方案: 

p≡ if b then (s;p) (1) 

p≡(s; if b then p) (2) 

要計算用簡單遞迴關係定義的值時,這種方案是很自然的。如下例: 

function f (i: integer ):integer; 

begin if i>0 then f:=i*f(i-1) 

else f:=1; 

end (3) 

很明顯,在這種情況下,遞迴可由簡單迭代代替,即由下面的程式代替。 

i:=0;f:=1; 

while i

begin i:=i+1;f:=i*f 

end (4) 

一般地,對應於方案(1)或(2)的程式應按照下列方案改寫: 

p ≡(x:=x0;while b do s) 

還有更複雜的遞迴構造方案,這些方案可以並且應該改寫成迭代計算的形式。乙個例子就是fibonacci 數的計算,這些數是按遞迴關係定義的: 

fibn+1=fibn+fibn-1對n>0 

而fib1=1,fib0=0,乙個直接的自然的解法導致程式 

function fib (n: integer): integer; 

begin if n=0 then fib :=0else 

if n=1 then fib :=1 else 

fib:=fib(n-1)+fib(n-2) 

end 

以呼叫fib(n)來計算fibn ,就引起這個函式過程的遞迴活動。頻繁程度如何呢?我們注意到,對於n>1,每呼叫一次引起兩個新的調。因此,呼叫的總次數按指數增長,如下圖所示fib(5)的遞迴樹。這樣乙個程式顯然是不實用的。 

但顯見,利用輔助變數x=fibi與y=fibi-1,就可以用迭代方案來計算fibonicca 數,而避免同一值的重複計算。  

i:=1;x:=1;y:=0; 

while i

begin z:=x; i:=i+1; 

x:=x + y; y:=z 

end 

(注意,對x, y, z的三個賦值,可以只表示成兩個賦值,而不需要輔助變數z :x:=x +y ; y:=x-y).由上述例子可以看出,當存在明顯的迭代解法時要避免使用遞迴。 

3、不需要消解的遞迴 

那種盲目的消解遞迴,不惜一切代價躲避遞迴,認為「遞迴的速度慢,為了提高速度,必須用棧或者其他的方法來消解」的說法是很片面的。如果乙個遞迴過程用非遞迴的方法實現後,速度提高了,那只是因為遞迴做了一些無用功。假使乙個遞迴過程必須要用棧才能消解,那麼完全模擬後的結果根本就不會對速度有任何提公升,只會減慢;如果你改完後速度提公升了,那只證明你的遞迴函式寫的有問題,如多了許多重複操作——開啟關閉檔案、連線斷開資料庫,而這些完全可以放到遞迴外面。可以在本質上是非遞迴的機器上實現遞迴過程這一事實本身就證明:為著實際目的,每乙個遞迴程式都可以翻譯成純粹迭代的形式,但這包含著對遞迴棧的顯式處理,而這些運算常常模糊了程式的本質,以致使它非常難以理解。 

因此,是遞迴的而不是迭代的演算法應當表述成遞迴過程。如漢諾塔問題等。漢諾塔問題的遞迴演算法中有兩處遞迴呼叫,並且其中一處遞迴呼叫語句後還有其他語句,因此該遞迴演算法不是尾遞迴或單向遞迴。要把這樣的遞迴演算法轉化為非遞迴演算法,並沒有提高程式執行的速度,反而會使程式變得複雜難懂,這是不可取的。也就是說,很多遞迴演算法並不容易改寫成迭代程式:它們本質上是遞迴的,沒有簡單的迭代形式。這樣的遞迴演算法不宜轉化為非遞迴演算法。 

遞迴演算法與迭代演算法的區別

舉個例子 我想求1 2 3 4 100的值。迭代的做法 從1到100,順著往下累加。1 2 3,3 3 6,6 4 10,10 5 15 程式表示,int i 1,sum 0 while i 100 遞迴的做法 我要求1到100的累加值,如果我已經得到1到99的累加值,將這個值加上100就是1到10...

演算法之美 遞迴與迭代

演算法之美 遞迴與迭代 其實遞迴最大的有點就是把乙個複雜的演算法分解成若干相同的可重複的步驟。所以,使用遞迴實現乙個計算邏輯往往只需要很短的 就能解決,並且這樣的 也比較容易理解。但是,遞迴就意味著大量的函式呼叫。函式呼叫的區域性狀態之所以用棧來記錄的。所以,這樣就可能浪費大量的空間,如果遞迴太深的...

演算法 迭代和遞迴

在日常程式的編寫中,複雜的專案日益增多,在後期的 優化上需要花更多的時間和精力。在前期的規劃上也越來越重要,前期良好的規劃可以避免後期遇到些奇怪的問題。這次部落格我希望通過講解下迭代和遞迴的具體應用場景,來表達寫程式是前期規劃的作用和解決具體問題所需要的方法。遞迴和迭代,都是一種迴圈演算法。遞迴在迴...