函數(shù)的遞歸調(diào)用
成都創(chuàng)新互聯(lián)于2013年成立,是專(zhuān)業(yè)互聯(lián)網(wǎng)技術(shù)服務(wù)公司,擁有項(xiàng)目網(wǎng)站設(shè)計(jì)、成都做網(wǎng)站網(wǎng)站策劃,項(xiàng)目實(shí)施與項(xiàng)目整合能力。我們以讓每一個(gè)夢(mèng)想脫穎而出為使命,1280元歙縣做網(wǎng)站,已為上家服務(wù),為歙縣各地企業(yè)和個(gè)人服務(wù),聯(lián)系電話:13518219792
遞歸問(wèn)題是一個(gè)說(shuō)簡(jiǎn)單也簡(jiǎn)單,說(shuō)難也有點(diǎn)難理解的問(wèn)題.我想非常有必要對(duì)其做一個(gè)總結(jié).
首先理解一下遞歸的定義,遞歸就是直接或間接的調(diào)用自身.而至于什么時(shí)候要用到遞歸,遞歸和非遞歸又有那些區(qū)別?又是一個(gè)不太容易掌握的問(wèn)題,更難的是對(duì)于遞歸調(diào)用的理解.下面我們就從程序+圖形的角度對(duì)遞歸做一個(gè)全面的闡述.
我們從常見(jiàn)到的遞歸問(wèn)題開(kāi)始:
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;
}
這是一個(gè)遞歸求階層函數(shù)的實(shí)現(xiàn)。很多朋友只是知道該這么實(shí)現(xiàn)的,也清楚它是通過(guò)不斷的遞歸調(diào)用求出的結(jié)果.但他們有些不清楚中間發(fā)生了些什么.下面我們用圖對(duì)此做一個(gè)清楚的流程:
根據(jù)上面這個(gè)圖,大家可以很清楚的看出來(lái)這個(gè)函數(shù)的執(zhí)行流程。我們的階層函數(shù)factorial被調(diào)用了4次.并且我們可以看出在調(diào)用后面的調(diào)用中,前面的調(diào)用并不退出。他們同時(shí)存在內(nèi)存中??梢?jiàn)這是一件很浪費(fèi)資源的事情。我們?cè)摯蔚膮?shù)是3.如果我們傳遞10000呢。那結(jié)果就可想而知了.肯定是溢出了.就用int型來(lái)接收結(jié)果別說(shuō)10000,100就會(huì)產(chǎn)生溢出.即使不溢出我想那肯定也是見(jiàn)很浪費(fèi)資源的事情.我們可以做一個(gè)粗略的估計(jì):每次函數(shù)調(diào)用就單變量所需的內(nèi)存為:兩個(gè)int型變量.n和result.在32位機(jī)器上占8B.那么10000就需要10001次函數(shù)調(diào)用.共需10001*8/1024 = 78KB.這只是變量所需的內(nèi)存空間.其它的函數(shù)調(diào)用時(shí)函數(shù)入口地址等仍也需要占用內(nèi)存空間??梢?jiàn)遞歸調(diào)用產(chǎn)生了一個(gè)不小的開(kāi)銷(xiāo).
2 斐波那契數(shù)列
int Fib(int n)
{
if (n = 1)
{
return n;
}
else
{
return Fib(n-1) + Fib(n-2);
}
}
這個(gè)函數(shù)遞歸與上面的那個(gè)有些不同.每次調(diào)用函數(shù)都會(huì)引起另外兩次的調(diào)用.最后將結(jié)果逐級(jí)返回.
我們可以看出這個(gè)遞歸函數(shù)同樣在調(diào)用后買(mǎi)的函數(shù)時(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;
}
運(yùn)行結(jié)果:
1
2
3
4
4
3
2
1
該程序中有兩個(gè)遞歸函數(shù)。傳遞同樣的參數(shù),但他們的輸出結(jié)果剛好相反。理解這兩個(gè)函數(shù)的調(diào)用過(guò)程可以很好的幫助我們理解遞歸:
我想能夠把上面三個(gè)函數(shù)的遞歸調(diào)用過(guò)程理解了,你已經(jīng)把遞歸調(diào)用理解的差不多了.并且從上面的遞歸調(diào)用中我們可以總結(jié)出遞歸的一個(gè)規(guī)律:他是逐級(jí)的調(diào)用,而在函數(shù)結(jié)束的時(shí)候是從最后面往前反序的結(jié)束.這種方式是很占用資源,也很費(fèi)時(shí)的。但是有的時(shí)候使用遞歸寫(xiě)出來(lái)的程序很容易理解,很易讀.
為什么使用遞歸:
1 有時(shí)候使用遞歸寫(xiě)出來(lái)的程序很容易理解,很易讀.
2 有些問(wèn)題只有遞歸能夠解決.非遞歸的方法無(wú)法實(shí)現(xiàn).如:漢諾塔.
遞歸的條件:
并不是說(shuō)所有的問(wèn)題都可以使用遞歸解決,他必須的滿(mǎn)足一定的條件。即有一個(gè)出口點(diǎn).也就是說(shuō)當(dāng)滿(mǎn)足一定條件時(shí),程序可以結(jié)束,從而完成遞歸調(diào)用,否則就陷入了無(wú)限的遞歸調(diào)用之中了.并且這個(gè)條件還要是可達(dá)到的.
遞歸有哪些優(yōu)點(diǎn):
易讀,容易理解,代碼一般比較短.
遞歸有哪些缺點(diǎn):
占用內(nèi)存資源多,費(fèi)時(shí),效率低下.
因此在我們寫(xiě)程序的時(shí)候不要輕易的使用遞歸,雖然他有他的優(yōu)點(diǎn),但是我們要在易讀性和空間,效率上多做權(quán)衡.一般情況下我們還是使用非遞歸的方法解決問(wèn)題.若一個(gè)算法非遞歸解法非常難于理解。我們使用遞歸也未嘗不可.如:二叉樹(shù)的遍歷算法.非遞歸的算法很難與理解.而相比遞歸算法就容易理解很多.
對(duì)于遞歸調(diào)用的問(wèn)題,我們?cè)谇耙欢螘r(shí)間寫(xiě)圖形學(xué)程序時(shí),其中有一個(gè)四連同填充算法就是使用遞歸的方法。結(jié)果當(dāng)要填充的圖形稍微大一些時(shí),程序就自動(dòng)關(guān)閉了.這不是一個(gè)人的問(wèn)題,所有人寫(xiě)出來(lái)的都是這個(gè)問(wèn)題.當(dāng)時(shí)我們給與的解釋就是堆棧溢出。就多次遞歸調(diào)用占用太多的內(nèi)存資源致使堆棧溢出,程序沒(méi)有內(nèi)存資源執(zhí)行下去,從而被操作系統(tǒng)強(qiáng)制關(guān)閉了.這是一個(gè)真真切切的例子。所以我們?cè)谑褂眠f歸的時(shí)候需要權(quán)衡再三.
遞歸的思想主要是能夠重復(fù)某些動(dòng)作,比如簡(jiǎn)單的階乘,次方,回溯中的八皇后,數(shù)獨(dú),還有漢諾塔,分形。
由于堆棧的機(jī)制,一般的遞歸可以保留某些變量在歷史狀態(tài)中,比如你提到的return x * power..., 但是某些或許龐大的問(wèn)題或者是深度過(guò)大的問(wèn)題就需要盡量避免遞歸,因?yàn)榭赡軙?huì)棧溢出。還有一個(gè)問(wèn)題是~python不支持尾遞歸優(yōu)化?。。。∷浴€是盡量避免遞歸的出現(xiàn)。
def power(x, n)
if n 0:
return 1
return x * power(x, n - 1)
power(3, 3)
3 * power(3, 2)
3 * (3 * power(3, 1))
3 * (3 * (3 * power(3, 0)))
3 * (3 * (3 * 1)) 這里n = 0, return 1
3 * (3 * 3)
3 * 9
27
當(dāng)函數(shù)形參n=0的時(shí)候,開(kāi)始回退~直到第一次調(diào)用power結(jié)束。
一、使用遞歸的背景
先來(lái)看一個(gè)??接口結(jié)構(gòu):
這個(gè)孩子,他是一個(gè)列表,下面有6個(gè)元素
展開(kāi)children下第一個(gè)元素[0]看看:
發(fā)現(xiàn)[0]除了包含一些字段信息,還包含了 children 這個(gè)字段(喜當(dāng)?shù)?,同時(shí)這個(gè)children下包含了2個(gè)元素:
展開(kāi)他的第一個(gè)元素,不出所料,也含有children字段(人均有娃)
可以理解為children是個(gè)對(duì)象,他包含了一些屬性,特別的是其中有一個(gè)屬性與父級(jí)children是一模一樣的,他包含父級(jí)children所有的屬性。
比如每個(gè)children都包含了一個(gè)name字段,我們要拿到所有children里name字段的值,這時(shí)候就要用到遞歸啦~
二、find_children.py
拆分理解:
1.首先import requests庫(kù),用它請(qǐng)求并獲取接口返回的數(shù)據(jù)
2.若children以上還有很多層級(jí),可以縮小數(shù)據(jù)范圍,定位到children的上一層級(jí)
3.來(lái)看看定義的函數(shù)
我們的函數(shù)調(diào)用:find_children(node_f, 'children')
其中,node_f:json字段
??? children:遞歸對(duì)象
?以下這段是實(shí)現(xiàn)遞歸的核心:
?? if items['children']:
?items['children']不為None,表示該元素下的children字段還有子類(lèi)數(shù)據(jù)值,此時(shí)滿(mǎn)足if條件,可理解為 if 1。
?items['children']為None,表示該元素下children值為None,沒(méi)有后續(xù)可遞歸值,此時(shí)不滿(mǎn)足if條件,可理解為 if 0,不會(huì)再執(zhí)行if下的語(yǔ)句(不會(huì)再遞歸)。
至此,每一層級(jí)中children的name以及下一層級(jí)children的name就都取出來(lái)了
希望到這里能幫助大家理解遞歸的思路,以后根據(jù)這個(gè)模板直接套用就行
(晚安啦~)
源碼參考:
可以看出來(lái)的是,該題可以用斐波那契數(shù)列解決。
樓梯一共有n層,每次只能走1層或者2層,而要走到最終的n層。不是從n-1或者就是n-2來(lái)的。
F(1) = 1
F(2) = 2
F(n) = F(n-1) + F(n-2) (n=3)
這是遞歸寫(xiě)法,但是會(huì)導(dǎo)致棧溢出。在計(jì)算機(jī)中,函數(shù)的調(diào)用是通過(guò)棧進(jìn)行實(shí)現(xiàn)的,如果遞歸調(diào)用的次數(shù)過(guò)多,就會(huì)導(dǎo)致棧溢出。
針對(duì)這種情況就要使用方法二,改成非遞歸函數(shù)。
將遞歸進(jìn)行改寫(xiě),實(shí)現(xiàn)循環(huán)就不會(huì)導(dǎo)致棧溢出
for 變量 in range(次數(shù)):被執(zhí)行的語(yǔ)句? ? ? ? ? ? ? ? ? ? ? ?變量:表示每次循環(huán)的次數(shù),0-1之間
range(n)n表示產(chǎn)生0到n-1的整數(shù)序列共N個(gè)? ? ? ? ? ? ? ?range(m,n)? 產(chǎn)生m到n-1的整數(shù)序列,共n-m個(gè)
循環(huán)for語(yǔ)句? :for 循環(huán)變量 in遍歷結(jié)構(gòu):語(yǔ)句體1? else:語(yǔ)句體2?
無(wú)限循環(huán): while條件: 語(yǔ)句塊
while 條件:語(yǔ)句體1 else: 語(yǔ)句體2
循環(huán)保留字:break? ? ?continue
方法1:from random import random
from time import perf_counter
DARTS=1000
hits=0.0
start =perf_counter()
for i in range(1,DARTS+1):
x,y=random(),random()
dist=pow(x**2+y**2,0.5)
if dist=1.0:
? ? hits =hits+1
pi=4*(hits/DARTS)
print("圓周率是:{}".format(pi))
print("運(yùn)行時(shí)間是{:.5f}s".format(perf_counter()-start))
方法2:
pi=0
n=100
for k in range(n):
pi += 1/pow(16,k)*(\
? ? 4/(8*k+1)-2/(8*k+4) - \
? ? 1/(8*k+5) - 1/(8*k+6))
print("圓周率值是:{}".format(pi))
def 函數(shù)名 (0個(gè)或者多個(gè)):函數(shù)體? renturn 返回值
def 函數(shù)名 (非可選參數(shù),可選參數(shù)):函數(shù)體? renturn 返回值
參數(shù)傳遞的兩種方式:位置傳遞,名稱(chēng)傳遞
科赫雪花:
import turtle
def koch(size,n):
if n==0:
? ? turtle.fd(size)
else:
? ? for angle in [0,60,-120,60]:
? ? ? ? turtle.left(angle)
? ? ? ? koch(size/3,n-1)
def main():
turtle.setup(400,200)
turtle.penup()
turtle.pendown()
turtle.pensize(2)
l=3
koch(600,l)
turtle.right(120)
turtle.pencolor('blue')
koch(600,l)
turtle.right(120)
turtle.pencolor('red')
koch(600,l)
turtle.speed(3000)
turtle.hideturtle()
main()
階乘:
def fact(n):
s=1
for i in range(1,n+1):
? ? s*=i
return s
c=eval(input("從鍵盤(pán)輸入一個(gè)數(shù)字"))
print("階乘結(jié)果",fact(c))
文章名稱(chēng):python3遞歸函數(shù),python寫(xiě)遞歸函數(shù)
標(biāo)題網(wǎng)址:http://jinyejixie.com/article8/hsooip.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供響應(yīng)式網(wǎng)站、網(wǎng)站設(shè)計(jì)公司、關(guān)鍵詞優(yōu)化、微信小程序、外貿(mào)建站、網(wǎng)站內(nèi)鏈
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶(hù)投稿、用戶(hù)轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)