高次遞迴轉換為迭代方式實現後 對計算效率的影響

2022-09-16 21:36:31 字數 3840 閱讀 6170

一句話解釋遞迴:自己呼叫自己

eg.

define function(x)

else

}

舉例:階乘、斐波拉契數列

define factorial(x)

else if x == 1 else

}define fibonacci(x)

else if x == 1 || x == 2 else

}

一句話解釋迭代:每次結果都是由上次結果進行相同的運算得到

eg.

define function(res, count, x)

else

}

其呼叫需要給出初始態,由初始態向後計算達到需要的狀態。由於初始態固定,其呼叫時rescount為固定值,所以大多時候都會選擇做進一步封裝。

舉例:階乘、斐波拉契數列

define factorial(res, count, x)

else if count < x else

}define fibonacci(res, res_1, count, x)

else if x == 1 || x == 2 else if count < x else

}

其呼叫方式為factorial(1, 1, x)fibonacci(1, 1, 2, x),進一步封裝為

define factorial(x)

define fibonacci(x)

對比計算效率的差異,最簡單的方式當然是實際跑**了。

廢話少說,show you my code。這裡使用python,以斐波拉契數列為例,比較兩種方式計算耗時。

遞迴方式及迭代方式的實現如下:

def recursion(x):

if x < 1:

return none

elif x == 1 or x == 2:

return 1

else:

return recursion(x-1) + recursion(x-2)

def iteration(result, result_1, count, x):

if x < 1:

return none

elif x == 1 or x == 2:

return 2

elif count < x:

return iteration(result + result_1, result, count+1, x)

else:

return result

def iteration(x):

return iteration(1, 1, 2, x)

測試函式:

def test(msg, func, x, times):

start = time.clock()

for index in range(times):

func(x)

end = time.clock()

print('computing the %dth number of fibolacci, %s running [%7d] times, time cost: %s seconds'%(x, msg, times, end-start))

測試結果:

>>>test("recursion",recursion,30,1)

>computing the 30th number of fibolacci, recursion running [ 1] times, time cost: 0.1959006259738926 seconds

>>>test("iteration",iteration,30,1000)

>computing the 30th number of fibolacci, iteration running [ 1000] times, time cost: 0.0047303810543576075 seconds

>>>test("recursion",recursion,40,1)

>computing the 40th number of fibolacci, recursion running [ 1] times, time cost: 23.777185128182722 seconds

>>>test("iteration",iteration,40,1000)

>computing the 40th number of fibolacci, iteration running [ 1000] times, time cost: 0.0064214635387642716 seconds

由於兩種實現方式在計算時間上的巨大差異,測試函式裡面寫了計算n次,迭代方式計算1000次計算總時間,不然數量級相差太大不好比較。python畢竟是使用直譯器的指令碼語言,用編譯語言可能會更快一點。

剛才實際跑**看了一下他們的耗時差距,在運算第30位時其實耗時還比較少,沒什麼明顯的感覺。但是運算第40位的時候遞迴方式使用了二十多秒才計算出來,等待這麼久才有輸出,都不需要認真做對比。那到底是什麼導致了它們的差距會這麼大呢?

我們可以從**實現上來分析一下原因。

遞迴方式的實現為函式為recursion,我們看一下recursion的時間複雜度:

函式內部的運算時間複雜度為o(1),加上函式跳轉消耗的時間遠大於這幾個運算消耗的時間,我們其實只需要關注發生了幾次遞迴呼叫就可以了。在count < x的條件判斷分支,可以看到發生了兩次遞迴呼叫,每多計算一輪需要多發生兩次遞迴呼叫,所以這個函式的時間複雜度應該是o(2^n)。時間複雜度呈指數增長,如果用它來計算斐波拉契數列的第50位,我們很有可能會被耗死在電腦前。

迭代方式的實現為函式為iteration,是對iteration的二次封裝,那我們也一樣,分析一下iteration的時間複雜度:

同上,函式內部的運算時間複雜度為o(1),我們一樣只需要關注發生了幾次遞迴呼叫。在count < x的條件判斷分支,可以看到發生了一次遞迴呼叫,每多計算一輪需要多發生一次遞迴呼叫,所以這個函式的時間複雜度是o(n)。時間複雜度呈線性增長,很明顯優於遞迴方式的指數增長。

那分析完複雜度,其實就很明白了,為什麼我要用斐波拉契數枚舉例,而不用階乘。因為階乘的遞迴實現也只發生了一次遞迴呼叫,和迭代實現一樣,時間複雜度也為o(n)。所以如果用階乘舉例是看不到什麼優化效果的。如果有人有興趣,也可以自己寫**對比一下階乘的兩種實現方式是不是真的沒有太大差異。

同時,分析完複雜度,也能很清晰的明白,當需要我們使用遞迴的時候,什麼情況下需要對遞迴進行迭代方式的優化,什麼時候不需要。

當發生高次遞迴呼叫的時候,可以將實現方式切換為迭代,使方法的時間複雜度由指數級優化到線性級;而在一次遞迴的情況下,就可以不做額外轉換,畢竟遞迴方式的**可讀性實在是比迭代方式友好太多了。

轉換為Json資料的幾種方式

1.匯入json lib 2.4 jdk15.jar 但是這個包得依賴很多其他包,比較臃腫 裡面有兩個方式 測試 1.簡單的物件和集合 結果如下 2.物件中某個屬性是物件以及物件集合 最終結果 也是ok的 pid 1 pinyin 手機 pname shouji pid 1 pinyin 手機 pn...

C 將遞迴演算法轉換為非遞迴演算法提高效能

遞迴是指某個函式或過程直接或間接的呼叫自身。一般地乙個遞迴包括遞迴出口和遞迴體兩部分,遞迴出口確定遞迴到何時結束,而遞迴體確定遞迴求解時的遞推關係。遞迴演算法有兩個基本特徵 一是遞迴演算法是一種分而治之的 把複雜問題分解為簡單問題的求解問題方法,對於求解某些複雜問題,遞迴演算法分析問題的方法是有效地...

(演算法專題)使用常微分方程將遞迴轉換為非遞迴

用遞推方法解遞迴方程,也就是我們常用的數學歸納法,用生成函式解遞迴方程,也就是我們使用迴圈代替遞迴。這節,我們利用高等數學的常微分方程,來進行求解遞迴式。k階常係數線性齊次遞迴方程形如 其中,bi為常數,第2項為方程初始條件。在上式中,用xn取代f n 有 兩邊分別處以xn k,得 特徵方程如下 練...