簡單說,解決以上問題的思路是,循環(huán)執(zhí)行n*n-1,直到n=1時。
靖江ssl適用于網(wǎng)站、小程序/APP、API接口等需要進行數(shù)據(jù)傳輸應(yīng)用場景,ssl證書未來市場廣闊!成為成都創(chuàng)新互聯(lián)公司的ssl證書銷售渠道,可以享受市場價格4-6折優(yōu)惠!如果有意向歡迎電話聯(lián)系或者加微信:18982081108(備注:SSL證書合作)期待與您的合作!
#!/usr/local/bin/python3.3def recursion(n): #定義函數(shù) if n == 1: return 1 else:
return n * recursion(n-1)print(recursion(10))
該示例執(zhí)行結(jié)果是:
如何理解呢?第一點,函數(shù)中,調(diào)用自身函數(shù)的那部分句子,即return n *
recursion(n-1),把recursion(n-1)想像成另一個獨立的函數(shù),該函數(shù)的功能返回n-1的值,如果n的值是1,則返回1,函數(shù)運行結(jié)束。第二點,直觀的看,可以把return
n * recursion(n-1)看成return n*(n-1)*(n-2)...1。而遞歸函數(shù)無非是在指定的條件下做普通的循環(huán)而已。
以上的遞歸函數(shù)相當于:
def fact(n):
if n==1:
return 1
else:
return n*fact(n-1)
fact(1)
1
fact(5)
120
比如fact(5)的迭代過程可以表示為:
參考一下第一步:簡單實現(xiàn)裝飾器 def login(func): print("in Login") return func def tv(name): print("{name} in TV".format(name = name)) tv = login(tv) tv('Jack') # out: # in Login # Jack in TV 第二步:同上 效果相同,但是使用的是@login def login(func): print("in Login") return func @login def tv(name): print("{name} in TV".format(name = name)) #tv = login(tv) tv('Jack') # out: # in Login # Jack in TV 但是出現(xiàn)問題,注銷最后的執(zhí)行語句仍有輸出,原因在于@login的調(diào)用,即@login相當于執(zhí)行了tv = login(tv) 所以才有輸出。 def login(func): print("in Login") return func @login def tv(name): print("{name} in TV".format(name = name)) #tv = login(tv) #tv('Jack') # out: # in Login 如下調(diào)整可解決 def login(func): def inner(arg): print("in Login") # return func func(arg) return inner @login def tv(name): print("{name} in TV".format(name = name)) #tv = login(tv) tv('Jack') # out: # in Login # Jack in TV 簡單的遞歸函數(shù) #!/usr/bin/env python #遞歸函數(shù) def calc(num): print("Number:",num) if num/2 1: calc(num/2) print("After Number:",num/2) calc(10) # Number: 10 # Number: 5.0 # Number: 2.5 # Number: 1.25 # After Number: 1.25 # After Number: 2.5 # After Number: 5.0 遞歸實現(xiàn)斐波那契數(shù)列 # Fibonacci sequence # F[n]=F[n-1]+F[n-2](n=2,F[0]=1,F[1]=1) # 斐波那契數(shù)列:1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, ... fibList = [1,1] def getFib(fibList): print(fibList) if fibList[-1] + fibList[-2] 300: fibList.append(fibList[-1] + fibList[-2]) getFib(fibList) pass pass getFib(fibList) print("[FINAL]:",fibList) # [1, 1] # [1, 1, 2] # [1, 1, 2, 3] # [1, 1, 2, 3, 5] # [1, 1, 2, 3, 5, 8] # [1, 1, 2, 3, 5, 8, 13] # [1, 1, 2, 3, 5, 8, 13, 21] # [1, 1, 2, 3, 5, 8, 13, 21, 34] # [1, 1, 2, 3, 5, 8, 13, 21, 34, 55] # [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89] # [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144] # [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233] # [FINAL]: [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233]
函數(shù)的遞歸調(diào)用
遞歸問題是一個說簡單也簡單,說難也有點難理解的問題.我想非常有必要對其做一個總結(jié).
首先理解一下遞歸的定義,遞歸就是直接或間接的調(diào)用自身.而至于什么時候要用到遞歸,遞歸和非遞歸又有那些區(qū)別?又是一個不太容易掌握的問題,更難的是對于遞歸調(diào)用的理解.下面我們就從程序+圖形的角度對遞歸做一個全面的闡述.
我們從常見到的遞歸問題開始:
1 階層函數(shù)
#include iostream
using namespace std;
int factorial(int n)
{
if (n == 0)
{
return 1;
}
else
{
int result = factorial(n-1);
return n * result;
}
}
int main()
{
int x = factorial(3);
cout x endl;
return 0;
}
這是一個遞歸求階層函數(shù)的實現(xiàn)。很多朋友只是知道該這么實現(xiàn)的,也清楚它是通過不斷的遞歸調(diào)用求出的結(jié)果.但他們有些不清楚中間發(fā)生了些什么.下面我們用圖對此做一個清楚的流程:
根據(jù)上面這個圖,大家可以很清楚的看出來這個函數(shù)的執(zhí)行流程。我們的階層函數(shù)factorial被調(diào)用了4次.并且我們可以看出在調(diào)用后面的調(diào)用中,前面的調(diào)用并不退出。他們同時存在內(nèi)存中。可見這是一件很浪費資源的事情。我們該次的參數(shù)是3.如果我們傳遞10000呢。那結(jié)果就可想而知了.肯定是溢出了.就用int型來接收結(jié)果別說10000,100就會產(chǎn)生溢出.即使不溢出我想那肯定也是見很浪費資源的事情.我們可以做一個粗略的估計:每次函數(shù)調(diào)用就單變量所需的內(nèi)存為:兩個int型變量.n和result.在32位機器上占8B.那么10000就需要10001次函數(shù)調(diào)用.共需10001*8/1024 = 78KB.這只是變量所需的內(nèi)存空間.其它的函數(shù)調(diào)用時函數(shù)入口地址等仍也需要占用內(nèi)存空間??梢娺f歸調(diào)用產(chǎn)生了一個不小的開銷.
2 斐波那契數(shù)列
int Fib(int n)
{
if (n = 1)
{
return n;
}
else
{
return Fib(n-1) + Fib(n-2);
}
}
這個函數(shù)遞歸與上面的那個有些不同.每次調(diào)用函數(shù)都會引起另外兩次的調(diào)用.最后將結(jié)果逐級返回.
我們可以看出這個遞歸函數(shù)同樣在調(diào)用后買的函數(shù)時,前面的不退出而是在等待后面的結(jié)果,最后求出總結(jié)果。這就是遞歸.
3
#include iostream
using namespace std;
void recursiveFunction1(int num)
{
if (num 5)
{
cout num endl;
recursiveFunction1(num+1);
}
}
void recursiveFunction2(int num)
{
if (num 5)
{
recursiveFunction2(num+1);
cout num endl;
}
}
int main()
{
recursiveFunction1(0);
recursiveFunction2(0);
return 0;
}
運行結(jié)果:
1
2
3
4
4
3
2
1
該程序中有兩個遞歸函數(shù)。傳遞同樣的參數(shù),但他們的輸出結(jié)果剛好相反。理解這兩個函數(shù)的調(diào)用過程可以很好的幫助我們理解遞歸:
我想能夠把上面三個函數(shù)的遞歸調(diào)用過程理解了,你已經(jīng)把遞歸調(diào)用理解的差不多了.并且從上面的遞歸調(diào)用中我們可以總結(jié)出遞歸的一個規(guī)律:他是逐級的調(diào)用,而在函數(shù)結(jié)束的時候是從最后面往前反序的結(jié)束.這種方式是很占用資源,也很費時的。但是有的時候使用遞歸寫出來的程序很容易理解,很易讀.
為什么使用遞歸:
1 有時候使用遞歸寫出來的程序很容易理解,很易讀.
2 有些問題只有遞歸能夠解決.非遞歸的方法無法實現(xiàn).如:漢諾塔.
遞歸的條件:
并不是說所有的問題都可以使用遞歸解決,他必須的滿足一定的條件。即有一個出口點.也就是說當滿足一定條件時,程序可以結(jié)束,從而完成遞歸調(diào)用,否則就陷入了無限的遞歸調(diào)用之中了.并且這個條件還要是可達到的.
遞歸有哪些優(yōu)點:
易讀,容易理解,代碼一般比較短.
遞歸有哪些缺點:
占用內(nèi)存資源多,費時,效率低下.
因此在我們寫程序的時候不要輕易的使用遞歸,雖然他有他的優(yōu)點,但是我們要在易讀性和空間,效率上多做權(quán)衡.一般情況下我們還是使用非遞歸的方法解決問題.若一個算法非遞歸解法非常難于理解。我們使用遞歸也未嘗不可.如:二叉樹的遍歷算法.非遞歸的算法很難與理解.而相比遞歸算法就容易理解很多.
對于遞歸調(diào)用的問題,我們在前一段時間寫圖形學(xué)程序時,其中有一個四連同填充算法就是使用遞歸的方法。結(jié)果當要填充的圖形稍微大一些時,程序就自動關(guān)閉了.這不是一個人的問題,所有人寫出來的都是這個問題.當時我們給與的解釋就是堆棧溢出。就多次遞歸調(diào)用占用太多的內(nèi)存資源致使堆棧溢出,程序沒有內(nèi)存資源執(zhí)行下去,從而被操作系統(tǒng)強制關(guān)閉了.這是一個真真切切的例子。所以我們在使用遞歸的時候需要權(quán)衡再三.
def Sum(m): #函數(shù)返回兩個值:遞歸次數(shù),所求的值 if m==1:return 1,m return 1+Sum(m-1)[0],m+Sum(m-1)[1]cishu=Sum(10)[0] print cishu def Sum(m,n=1): ... if m==1:return n,m ... return n,m+Sum(m-1,n+1)[1] print Sum(10)[0] 10 print Sum(5)[0] 5
所謂基例就是不需要遞歸就能求解的,一般來說是問題的最小規(guī)模下的解。
例如:斐波那契數(shù)列遞歸,f(n) = f(n-1) + f(n-2),基例是1和2,f(1)和f(2)結(jié)果都是1
再比如:漢諾塔遞歸,基例就是1個盤子的情況,只需移動一次,無需遞歸
遞歸必須有基例,否則就是無法退出的遞歸,不能求解。