21.遞歸

遞歸

遞歸的概念:函數包含了對自身的調用,那麼就是遞歸,函數運行會佔用內存。
使用的場景:若是你發現你將要作的事情就是你如今作的,那麼用遞歸
遞歸相似循環;在編寫或閱讀遞歸時,首先咱們關注的是遞歸的終止條件python

 

遞歸循環終止條件


 

##########33
def zhangan(index=0):
    if index < 100:    
        print('當前運行的此時',index)
        return zhangan(index+1)
    else:
        print('遞歸結束')
        return     1    
rt = zhangan()
print('返回值:',rt) 

當前運行的此時 95
當前運行的此時 96
當前運行的此時 97
當前運行的此時 98
當前運行的此時 99
遞歸結束
返回值: 1
   

 

 

遞歸求和

在接觸遞歸以前,咱們先來作這麼一個問題:若是說,要對一個數字列表求和(或者其餘序列)求和,除了咱們可使用內置的sum函數,還有什麼辦法?
若是你還有更優秀的辦法,能夠在關於頁面找到個人聯繫方式,有償提交給我函數

while循環:ui

1 L = [1,2,3,4,5]
2 mysum = 0 #保存和的變量
3 while L: #將列表最爲循環條件
4     mysum += L[0] #每次將列表第一個位置的值加到和中
5     L = L[1:] #去掉列表第一個元素

 


for
循環:spa


 

1 L = [1,2,3,4,5]
2 mysum = 0
3 for var in L:
4     mysum += var

 

遞歸求和:code


 

1 a = [1,2,3,4,5]
2 def zhangan(seq):
3     if not seq:  #空列表時遞歸終止返回值0
4         return 0
5     else:
6         return seq[0]+zhangan(seq[1:])
7          #在返回值中,咱們返回了一個函數的調用,而且傳遞的參數爲去掉當前列表第一個元素的新列表        
8 print(zhangan(a))

 

 1 a = [1,2,3,4,5]
 2 def get_sum(seq):
 3     if seq:  
 4         return seq[0] + get_sum(seq[1:])
 5     else:  #空列表時遞歸終止
 6         return 0
 7     #return seq[0] + get_sum(seq[1:]) if seq else 0 
 8 rt = get_sum(a)
 9 print(rt)
10 1#get_sum([1,2,3,4,5]): return 1 + get_sum([2,3,4,5])
11 2#get_sum([2,3,4,5])  : return 2 + get_sum([3,4,5])
12 3#get_sum([3,4,5])    : return 3 + get_sum([4,5])
13 4#get_sum([4,5])      : return 4 + get_sum([5])
14 5#get_sum([5])        : return 5 + get_sum([])
15 6#get([])             : return 0

 

 

遞歸處理非線性循環blog



遞歸還能夠處理一些非線性循環,而普通的循環是沒法處理的
好比這樣一個列表對其求和:遞歸

L = [1,[2,[3,4],5],6,[7,8]] 
因爲這個列表不是一個線性迭代,包含着複雜的元素嵌套
普通的循環語句處理起來將會很是難以控制遊戲

 

 1 L = [1,[2,[3,4],5],6,[7,8]]
 2 sum = 0
 3 def mysum(L):
 4     global sum
 5     for var in L:
 6         if not isinstance(var,list):   
 7         #若是其中元素不爲列表類型,則爲一個肯定的值
 8             sum += var
 9         else:
10             mysum(var)
11     return

 

第一種用global運算:內存

 1 mylist = [1,2,[3],5,[6,[7,8,9]],1,2] #-> 44
 2 #試一下用循環求和,
 3 #若是列表變化,那麼代碼能夠兼容,能夠直接複用,不能改變
 4 mysum = 0
 5 #for while 一層層的向下遍歷
 6 
 7 def get_sum(iter):#接收一個等待求和的多層序列
 8     #iter 中 無非兩種數據類型: list int
 9     global mysum
10     for var in iter:
11         if type(var) == int: #當前取出來的數據是int
12         #if type(var) == type([])
13             mysum += var
14         else:
15             get_sum(var) #遇到的又是一個列表,那麼咱們繼續遍歷
16     #for循環結束的時候,遞歸結束
17 get_sum(mylist)
18 print(mysum)
19 # get_sum([1,2,[3],5,[6,[7,8,9]],1,2])
20     #mysum += 1,2,5,1,2
21         # get_sum([3]) -> mysum += 3
22         # get_sum([6,[7,8,9]])
23             #mysum += 6
24             # get_sum([7,8,9])
25                 #mysum += 7,8,9

第二種用return返回值求和: 作用域

 1 a = [1,2,[1,2,3],5,[6,[6,7,8,9]],32,2] 
 2 def zhangan(a1):
 3     sum = 0     #每一次遞歸sum都爲0
 4     for var in a1:
 5         if type(var) == int: 
 6             sum += var
 7         else:
 8             dsum = zhangan(var) #每一次list遞歸的結果
 9             sum = sum + dsum    #遞歸的結果值和不走遞歸的結果值加起來
10     return sum    #函數運行結果要返回    
11 rt=zhangan(a)
12 print(rt)

 

花錢遞歸



思考:假如你有10000塊,天天花一半,毛錢直接捨棄,那麼這錢能夠花幾天?

遞歸解決:

 1 money = 10000
 2 def zhangan(money,day=1):
 3     if money > 0:
 4         print('今天是第:%d天' % day)
 5         money1 = money // 2  #每次花一半
 6         print('還剩餘:%d' % money1)
 7         day +=1     #花完天數+1
 8         return zhangan(money1,day)
 9     else:
10         return day
11 print(zhangan(money))

 

1 moeny = 10000
2 def func(moeny,day=0):
3     if moeny > 0:
4         func(moeny // 2,day+1)
5     else:
6         print(day)
7         return 0
8 func(moeny)

  

小猴子吃桃子遞歸:         


 

 1 #小猴子吃桃子
 2 #100個桃子,天天吃一半加一個,何時不能按照這樣的條件吃了。得花多少天
 3 
 4 peach_num = 100
 5 def eat_peach(peach_num,day = 1):
 6     print('今天是%d天' % day)
 7     print('如今還有%d個桃子' % (peach_num))
 8     eat_num = peach_num // 2 + 1
 9     peach_num = peach_num - eat_num
10     if peach_num > 0:
11         print('吃完了還剩下%d個桃子' % (peach_num))
12         day += 1
13         print('--------------')
14         return eat_peach(peach_num, day)
15     else:
16         return day
17 day = eat_peach(peach_num)
18 print(day)
19 
20 1#eat_peach(100,day = 1):
21 '''
22     eat_num = 51 要吃的
23     peach_num = 49 剩下的 >0
24     day = 1 當前的天數
25     return eat_peach(49, 2) 6
26 '''
27 2#eat_peach(49, 2)
28 '''
29     eat_num = 25 要吃的
30     peach_num = 24 剩下的 >0
31     day = 2 當前的天數
32     return eat_peach(24, 3)  6 
33 '''
34 3#eat_peach(24, 3)
35 '''
36     eat_num = 13 要吃的
37     peach_num = 11 剩下的 >0
38     day = 3 當前的天數
39     return eat_peach(11, 4)  6  
40 '''
41 4#eat_peach(11, 4)
42 '''
43     eat_num = 6 要吃的
44     peach_num = 5 剩下的 >0
45     day = 4 當前的天數
46     return eat_peach(5, 5) 6    
47 '''
48 5#eat_peach(5, 5)
49 '''
50     eat_num = 3 要吃的
51     peach_num = 2 剩下的 >0
52     day = 5 當前的天數
53     return eat_peach(2, 6)   6  
54 '''
55 6#eat_peach(2, 6)
56 '''
57     到了這一天,無法過了!
58     eat_num = 2 要吃的
59     peach_num = 0 剩下的 = 0
60     day = 6 當前的天數
61     return 6    
62 ''' 
 1 #小猴子吃桃子
 2 #100個桃子,天天吃一半加一個,何時不能按照這樣的條件吃了。得花多少天
 3 taozi = 100
 4 def func(taozi,day=1):
 5     if taozi > 0:
 6         eat_num = taozi // 2 + 1
 7         sum_num = taozi - eat_num
 8         day +=1
 9         func(sum_num,day)
10     else:
11         print(day)
12         return 0
13 func(taozi)

 

統計每個出現的字符出現的次數:


 

 1 mylist = ['asdazxc','adxzc',['12390145fcsdjfhzkjxcmnasd','123987189asjkdsajkb'],'asdqwewqerq',['asd890q8390'],'asdhquiweqysa','asdhjkzhxjkckjasdh']
 2 #把同樣的提出來
 3 #統計每個出現的字符出現的次數
 4 #for循環實現
 5 dict_num = {}
 6 #key:對應的字符
 7 #value:出現的次數
 8 def get_num(seq):
 9     #字典是可變數據類型,因此直接能夠在函數做用域內進行修改
10     for var in seq: #遍歷整個列表數據
11         if type(var) == list:
12             #若是取出來的仍是一個列表,那麼就繼續遞歸
13             get_num(var)
14         else: #若是碰到的是一個字符串
15             for i in var:  #遍歷字符串,記錄次數
16                 if i in dict_num:
17                     # 若是獲取到的字符,已經存在了字典中,那麼他的次數+1
18                     dict_num[i] = dict_num[i] + 1
19                 else:
20                     # 若是獲取到的字符沒出現過,那麼就建立默認值1就行
21                     dict_num[i] = 1
22 get_num(mylist)
23 for key in dict_num:
24     print(key,':',dict_num[key])
 1 mylist = ['asdazxc','adxzc',['12390145fcsdjfhzkjxcmnasd','123987189asjkdsajkb'],'asdqwewqerq',['asd890q8390'],'asdhquiweqysa','asdhjkzhxjkckjasdh']
 2 sum_dict = {}
 3 def func(seq):
 4     for var in seq:
 5         if type(var) == str:
 6             for i in var:
 7                 if sum_dict.get(i):
 8                     sum_dict[i] = sum_dict[i]+1
 9                 else:
10                     sum_dict[i] = 1
11         else:
12             func(var)
13 func(mylist)
14 print(sum_dict)

 

腳本模擬環境 爲如今很火的遊戲 病毒公司


  1,  有一種高繁殖 高傳播(只限空氣與水) 難治癒 但不危機生命的病毒

  2,  腳本測算它的傳播速度

  3,  假設一個城市有1000萬人口 日出生率與死亡率抵消後人口增加率爲日10萬分之一

  4,  病毒最初只有一個,以天天一倍的速度繁殖

  5,  1萬個病毒能夠感染一個病人

     計算多少天能夠感染全市人.

import time
import os
def func(day,viruses,population):
    day += 1
    viruses += viruses
    population = round(population * 1.00001)
    if viruses // 10000 > population:
        print('\n\n今天是第%s天,全市的人均可能會被感染,因此請在此時間前研究出疫苗' % day)
        return
    print('今天是第%s 天 , 有細菌 %s 個  人口 %s' % (day,viruses,population))
    time.sleep(1)
    os.system('cls')
    return func(day,viruses,population)
func(0,1,10000000)

 

遞歸注意事項



Python中,遞歸的最大上限次數差很少是1000次左右
一個沒有終止條件的遞歸會引起錯誤(相似一個死循環)
這是由於遞歸的每一次函數執行*,都會在內存中產生新的函數副本,遞歸的內存消耗大於普通循環;可是一樣的,消耗了內存效率高**於普通循環


 1 >>> def func():
 2 ...     return func()
 3 ...
 4 >>> func()
 5 Traceback (most recent call last):
 6   File "<stdin>", line 1, in <module>
 7   File "<stdin>", line 2, in func
 8   File "<stdin>", line 2, in func
 9   File "<stdin>", line 2, in func
10   [Previous line repeated 995 more times]
11 RecursionError: maximum recursion depth exceeded
12 #這裏咱們在995次遞歸以後,達到上線,從而報錯

 

咱們也能夠手動干預遞歸的上限,可是這是有風險的,要結合計算機自己內存來考慮

1 >>> import sys
2 >>> sys.setrecursionlimit(num)

在這裏,num即爲你想控制修改的最大遞歸上限次數

相關文章
相關標籤/搜索