上課時給學生講到遞迴實現的缺陷時,舉過下面的例子:
使用遞迴方法來計算組合數:
從m個不同元素中,任取n(n≤m)個元素並成一組,叫做從m個不同元素中取出n個元素的乙個組合;從m個不同元素中取出n(n≤m)個元素的所有組合的個數,叫做從m個不同元素中取出n個元素的組合數。
公 式: c(m,n)=n!/((m-n)!*n!)(n≤m)
性 質:c(m,n)= c(m,m-n) --可以推導出--》 c(m,0) = c(m,m) = 1
c(m,n)=c(m-1,n-1)+c(m-1,n)
請使用遞迴的方法來計算組合數。
使用遞迴的方法很簡單,使用我們之前講過的遞迴的原則:
如上圖,當我們來計算c(6,4)時,我們需要計算c(5,3)和c(5,4),而計算c(5,3)又要計算c(4,2)和c(4,3),依次 類推。
我們發現這裡有大量的重複預算。這也就是為什麼遞迴在時間和空間(遞迴要開闢棧空間)上有大量的資源消耗。
那麼問題來了,我們需要怎麼來優化?
根據上一章所講的內容,我們可以嘗試用迴圈來優化遞迴。這裡我們使用另一種方法,dp(dynamical programming),也就是動態規劃的方法來優化。
我們考慮:這裡有很多中間結果被反覆計算,從而引起了時間和空間上(遞迴需要分配棧空間)的浪費,
所以我們可以考慮將計算的中間結果記錄在某處,然後當再次需要這個結果時,從已經計算的結果中來查詢,從而得到結果,
這樣在時間上可以節省大量的成本,當然,空間上則需要一定輔助空間。
按照這個思路,我們嘗試用一張大的表來有序的儲存中間結果。
如下圖,我們讓行對應m,讓列對應n,這樣我們根據c(m,0) = c(m,m) = 1,可以將下標中
的黑色區域填上初始值1;
然後根據性質:c(i,j)=c(i-1,j-1)+c(i-1,j)
我們找到了計算出c(i,j)的方法,所以在計算c(5,2)
時可以使用c(4,1)+c(4,2)=4+6=10
依次類推:
我們可以計算出表中的c(m,n);
於是,有了下面的演算法2(dynamical programming)實現:
import math
import time
def combinationnum2(m,n):
#arr = [[0]*m]*n # row m, coloumn n
t1 = time.time()
mylist = [([0] * (n+1)) for i in range(m+1)]
#print(mylist)
for i in range(0, m-n+1):
mylist[i][0] = 1
for i in range(0,n+1):
mylist[i][i] = 1
#print(mylist)
for j in range(1, n+1):
for i in range(j+1, m-n+j+1):
mylist[i][j] = mylist[i-1][j-1]+mylist[i-1][j]
#print(mylist)
print("combinationnum2 costed %f seconds"%(time.time()-t1))
return mylist[m][n]
#return combinationnum(m-1,n-1)+ combinationnum(m-1,n)
這裡我們就是乙個使用dp的思想來優化遞迴的案例。
# 當然,我們可以從數學公式的角度來解決這個問題,**如下:
def combinationnum3(m,n):
t1 = time.time()
result = math.factorial(m)/(math.factorial(m-n)*math.factorial(n))
print("combinationnum3 costed %f seconds"%(time.time()-t1))
return result
# 測試**:
m = int(raw_input("please input m:"))
n = int(raw_input("please input n:"))
print(combinationnum2(m, n))
print(combinationnum3(m, n))
對python演算法,爬蟲和資料分析感興趣的朋友可以加入qq群:748905525,一起學習討論
遞迴 遞迴的優化
遞迴演算法在工作或者各種資料結構中使用比較頻繁,遞迴演算法的簡化常見有自頂向下還有備忘錄法 自頂向下 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 ...
遞迴 遞迴演算法的非遞迴優化
一 遞迴 在方法內部呼叫自身方法的過程稱為遞迴,下面給出乙個遞迴方法的示例。class program 使用遞迴,實現求前n項和 public static int getsum int n int result getsum n 1 在方法體中呼叫方法本身 return result n 需要注意...
普通遞迴與優化遞迴
function factorial n factorial 5 120 上面 是乙個階乘函式,計算n的階乘,最多需要儲存n個呼叫記錄,複雜度 o n 如果改寫成尾遞迴,只保留乙個呼叫記錄,複雜度 o 1 函式呼叫自身,稱為遞迴。如果尾呼叫自身,就稱為尾遞迴。遞迴非常耗費記憶體,因為需要同時儲存成千...