Python入門篇-遞歸函數(recursion)
python
做者:尹正傑算法
版權聲明:原創做品,謝絕轉載!不然將追究法律責任。app
一.遞歸概述ide
(1)函數直接或者間接調用自身就是遞歸;
(2)遞歸須要有邊界,遞歸前進段,遞歸返回段;
(3)遞歸必定要有邊界條件;
(4)當邊界條件不知足的時候,遞歸前進;
(5)當邊界條件知足的時候,遞歸返回;
二.遞歸案例函數
1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ 5 #EMAIL:y1053419035@qq.com 6 7 import sys 8 9 print("默認的遞歸調用次數限制0是:{} 次".format(sys.getrecursionlimit())) 10 11 sys.setrecursionlimit(20000) #咱們修改默認的遞歸調用次數 12 13 print("當前遞歸調用次數限制是:{} 次".format(sys.getrecursionlimit())) 14 """ 15 遞歸要求: 16 遞歸必定要有退出條件,遞歸調用必定要執行到這個退出條件。沒有退出條件的遞歸調用,就是無限調用。 17 遞歸調用的深度不宜過深: 18 Python對遞歸調用的深度作了限制,以保護解釋器 19 超過遞歸深度限制,拋出:"RecursionError: maxinum recursion depth exceeded" 即超過了最大遞歸深度 20 sys.getrecursionlimit() 21 """ 22 23 24 25 """ 26 斐波拉契數字推理公式:F(0)=0, F(1)=1, F(n)=F(n-1)+F(n-2) 27 """ 28 def fib(n): 29 return 1 if n < 2 else fib(n-1) + fib(n-2) 30 31 for i in range(20): 32 print(fib(i), end=' ') 33 34 35 #以上代碼執行結果以下: 36 默認的遞歸調用次數限制0是:1000 次 37 當前遞歸調用次數限制是:20000 次 38 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765
三.遞歸的性能性能
1>.使用for循環打印斐波拉契前35個數字優化
1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ 5 #EMAIL:y1053419035@qq.com 6 7 8 import datetime 9 10 start = datetime.datetime.now() 11 12 pre = 0 13 14 cur = 1 # No1 15 16 print(pre, cur, end=' ') 17 18 n = 35 19 20 for i in range(n-1): 21 pre, cur = cur, pre + cur 22 print(cur, end=' ') 23 24 delta = (datetime.datetime.now() - start).total_seconds() 25 26 print("\n遞歸調用打印斐波拉契錢35個數字的時間爲:{}".format(delta)) 27 28 29 30 #以上代碼直接結果以下: 31 0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765 10946 17711 28657 46368 75025 121393 196418 317811 514229 832040 1346269 2178309 3524578 5702887 9227465 32 遞歸調用打印斐波拉契錢35個數字的時間爲:0.0
2>.使用遞歸方式打印斐波拉契前35個數字編碼
1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ 5 #EMAIL:y1053419035@qq.com 6 7 import datetime 8 9 n = 35 10 11 start = datetime.datetime.now() 12 13 def fib(n): 14 return 1 if n < 2 else fib(n-1) + fib(n-2) 15 16 for i in range(n): 17 print(fib(i), end=' ') 18 19 delta = (datetime.datetime.now() -start).total_seconds() 20 21 print("\n遞歸調用打印斐波拉契錢35個數字的時間爲:{}".format(delta)) 22 23 24 25 #以上代碼直接結果以下: 26 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765 10946 17711 28657 46368 75025 121393 196418 317811 514229 832040 1346269 2178309 3524578 5702887 9227465 27 遞歸調用打印斐波拉契錢35個數字的時間爲:5.93134
3>.遞歸優化方案 spa
循環稍微複雜一些,可是隻要不是死循環,能夠屢次迭代直至算出結果
fib函數代碼極簡易懂,可是隻能獲取到最外層的函數調用,內部遞歸都是中間結果。並且給定一個n都要進行2n次遞歸,深度月神,效率月底。爲了獲取斐波那契數列須要外面在套一個n次的循環,效率就更低了。
遞歸還有深度限制,若是遞歸複雜,函數反覆壓棧,棧內存很快就溢出了
思考:這個極簡的遞歸代碼可否提升性能呢?下面是優化後的代碼,效率有明顯的提示,代碼以下所示:
#!/usr/bin/env python #_*_coding:utf-8_*_ #@author :yinzhengjie #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ #EMAIL:y1053419035@qq.com import datetime start = datetime.datetime.now() pre = 0 cur = 1 # No1 print(pre, cur, end=' ') """ 改進後的fib函數和循環的思想相似 參數n是邊界條件,用n來計算 上一次的結果直接做爲參數的實參 效率很高 和循環比較,性能相近。因此並非說遞歸必定效率低下。可是遞歸有深度限制。 """ def fib(n, pre=0,cur=1): # recursion pre, cur = cur, pre + cur print(cur, end=' ') if n == 2: return fib(n-1, pre, cur) fib(35) delta = (datetime.datetime.now() -start).total_seconds() print("\n遞歸調用打印斐波拉契錢35個數字的時間爲:{}".format(delta)) #以上代碼直接結果以下: 0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765 10946 17711 28657 46368 75025 121393 196418 317811 514229 832040 1346269 2178309 3524578 5702887 9227465 遞歸調用打印斐波拉契錢35個數字的時間爲:0.0
4>.間接遞歸3d
#!/usr/bin/env python #_*_coding:utf-8_*_ #@author :yinzhengjie #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ #EMAIL:y1053419035@qq.com def foo(): bar() def bar(): foo() """ 咱們這裏看起來只是調用foo函數,可是進入foo函數中的函數體中咱們發現調用bar函數,進入bar函數中的函數體中咱們發現它 又再次調用foo函數。所以咱們執行該代碼後會發現跑出來異常:「RecursionError: maximum recursion depth exceeded」. 這就造成了間接遞歸,是經過別的函數調用了函數自身。可是,若是構成了循環遞歸調用是很是危險的,可是每每這種狀況在代碼 複雜的狀況下,仍是可能發生這種調用。要用代碼的規範來避免這種遞歸調用的發生。 """ foo()
5>.遞歸總結
(1)遞歸是一種很天然的表達,符合邏輯思惟 (2)遞歸相對運行效率低,每一次調用函數都要開闢棧幀 (3)遞歸有深度限制,若是遞歸層次太深,函數反覆壓棧,棧內存很快就溢出了 (4)若是有限次數的遞歸,可使用遞歸調用,或者使用循環代替,循環代碼稍微複雜一些,可是隻要不是死循環,能夠屢次迭代直至算出結果 (5)絕大多數遞歸,均可以使用循環實現 (6)即便遞歸代碼很簡潔,可是能不用則不用遞歸
四.遞歸練習
1>.求N的階乘
#!/usr/bin/env python #_*_coding:utf-8_*_ #@author :yinzhengjie #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ #EMAIL:y1053419035@qq.com def fac(n): if n == 1: return 1 return n * fac(n-1) N = 5 print("{} 的階乘爲:{}".format(N,fac(N))) #以上代碼執行結果以下: 5 的階乘爲:120
#!/usr/bin/env python #_*_coding:utf-8_*_ #@author :yinzhengjie #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ #EMAIL:y1053419035@qq.com def fac(n,p = 1): if n == 1: return p p *= n # print(p) return fac(n - 1,p) N = 5 print("{} 的階乘爲:{}".format(N,fac(N))) #以上代碼執行結果以下: 5 的階乘爲:120
#!/usr/bin/env python #_*_coding:utf-8_*_ #@author :yinzhengjie #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ #EMAIL:y1053419035@qq.com def fac(n,p = None): if p is None: p = [1] if n == 1: return p[0] p[0] *= n return fac(n - 1,p) N = 5 print("{} 的階乘爲:{}".format(N,fac(N))) #以上代碼執行結果以下: 5 的階乘爲:120
2>.將一個數逆序放入列表中,例如:1234=>【4,3,2,1】
#!/usr/bin/env python #_*_coding:utf-8_*_ #@author :yinzhengjie #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ #EMAIL:y1053419035@qq.com data = str(1234) def revert(x): if x == -1: return "" return data[x] + revert(x -1) print(revert(len(data) - 1)) #以上代碼執行結果以下: 4321
#!/usr/bin/env python #_*_coding:utf-8_*_ #@author :yinzhengjie #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ #EMAIL:y1053419035@qq.com def revert(n,list_1 = None): if list_1 is None: list_1 = [] x,y = divmod(n,10) list_1.append(y) if x == 0: return list_1 return revert(x,list_1) print(revert(12345)) #以上代碼執行結果以下: [5, 4, 3, 2, 1]
#!/usr/bin/env python #_*_coding:utf-8_*_ #@author :yinzhengjie #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ #EMAIL:y1053419035@qq.com num = 1234 def revert(num,target=[]): if num: target.append(num[len(num) - 1]) #等效於target.append(num[-1:]) revert(num[:len(num) - 1]) return target print(revert(str(num))) #以上代碼執行結果以下: ['4', '3', '2', '1']
3>.解決猴子吃桃問題
猴子第一天摘下若干個桃子,立即吃了一半,還不過癮,又吃了一個。次日早上有將剩下的桃子吃掉一半,又多吃了一個。之後天天早上都吃了前一天剩下的一半零一個。到第十天早上想吃時,只剩下一個桃子了。求第一天共摘了多少個桃子。
#!/usr/bin/env python #_*_coding:utf-8_*_ #@author :yinzhengjie #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ #EMAIL:y1053419035@qq.com def peach(days = 10): if days == 1: return 1 return (peach(days - 1) + 1) * 2 print(peach()) #以上代碼執行結果以下: 1534
#!/usr/bin/env python #_*_coding:utf-8_*_ #@author :yinzhengjie #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ #EMAIL:y1053419035@qq.com def peach(days = 1): if days == 10: return 1 return (peach(days + 1) + 1) * 2 print("第一天共摘了{}個桃子".format(peach())) #以上代碼執行結果以下: 第一天共摘了1534個桃子
4>.把字典扁平化
源字典 = {'a':{'b':1,'c':2}, 'd':{'e':3,'f':{'g':4}}} 目標字典 = {'a.c':2,'d.e':3,'d.f.g':4,'a.b':1}
1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ 5 #EMAIL:y1053419035@qq.com 6 7 8 source = {'a':{'b':1,'c':2},'d':{'e':3,'f':{'g':4}}} 9 10 target = {} 11 12 13 def flatmap(src,prefix=''): 14 for k,v in src.items(): 15 if isinstance(v,(list,tuple,set,dict)): 16 flatmap(v,prefix=prefix + k + '.') #遞歸調用 17 else: 18 target[prefix + k] = v 19 20 flatmap(source) 21 22 print(target) 23 24 25 26 #以上代碼輸出結果以下: 27 {'a.b': 1, 'a.c': 2, 'd.e': 3, 'd.f.g': 4}
1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ 5 #EMAIL:y1053419035@qq.com 6 7 8 source = {'a':{'b':1,'c':2},'d':{'e':3,'f':{'g':4}}} 9 10 11 def flatmap(src,dest=None,prefix=''): 12 if dest == None: 13 dest = {} 14 for k,v in src.items(): 15 if isinstance(v,(list,tuple,set,dict)): 16 flatmap(v,dest,prefix=prefix + k + '.') #遞歸調用 17 else: 18 dest[prefix + k] = v 19 20 return dest 21 22 target = flatmap(source) 23 24 print(target) 25 26 27 28 #以上代碼輸出結果以下: 29 {'a.b': 1, 'a.c': 2, 'd.e': 3, 'd.f.g': 4}
1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ 5 #EMAIL:y1053419035@qq.com 6 7 8 source = {'a':{'b':1,'c':2},'d':{'e':3,'f':{'g':4}}} 9 10 11 def flatmap(src,dest=None,prefix=''): 12 def _flatmap(src,dest=None,prefix=''): 13 for k,v in src.items(): 14 key = prefix + k 15 if isinstance(v,(list,tuple,set,dict)): 16 _flatmap(v,dest,key + ".") #遞歸調用 17 else: 18 dest[key] = v 19 dest = {} 20 _flatmap(src,dest) 21 return dest 22 23 target = flatmap(source) 24 25 print(target) 26 27 28 29 #以上代碼輸出結果以下: 30 {'a.b': 1, 'a.c': 2, 'd.e': 3, 'd.f.g': 4}
5>.實現Base64編碼(要求本身實現算法,不用庫)
索引
|
對應字符
|
索引
|
對應字符
|
索引
|
對應字符
|
索引
|
對應字符
|
0
|
A
|
17
|
R
|
34
|
i
|
51
|
z
|
1
|
B
|
18
|
S
|
35
|
j
|
52
|
0
|
2
|
C
|
19
|
T
|
36
|
k
|
53
|
1
|
3
|
D
|
20
|
U
|
37
|
l
|
54
|
2
|
4
|
E
|
21
|
V
|
38
|
m
|
55
|
3
|
5
|
F
|
22
|
W
|
39
|
n
|
56
|
4
|
6
|
G
|
23
|
X
|
40
|
o
|
57
|
5
|
7
|
H
|
24
|
Y
|
41
|
p
|
58
|
6
|
8
|
I
|
25
|
Z
|
42
|
q
|
59
|
7
|
9
|
J
|
26
|
a
|
43
|
r
|
60
|
8
|
10
|
K
|
27
|
b
|
44
|
s
|
61
|
9
|
11
|
L
|
28
|
c
|
45
|
t
|
62
|
+
|
12
|
M
|
29
|
d
|
46
|
u
|
63
|
/
|
13
|
N
|
30
|
e
|
47
|
v
|
||
14
|
O
|
31
|
f
|
48
|
w
|
||
15
|
P
|
32
|
g
|
49
|
x
|
||
16
|
Q
|
33
|
h
|
50
|
y
|
1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ 5 #EMAIL:y1053419035@qq.com 6 7 8 import base64 9 10 s1 = "abcdfg" 11 12 print(base64.b64encode(s1.encode())) 13 14 15 #以上代碼輸出結果以下: 16 b'YWJjZGZn'
6>.求2個字符串的最長公共子串
1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ 5 #EMAIL:y1053419035@qq.com 6 7 8 def findit(str1,str2): 9 count = 0 10 length = len(str1) 11 12 for sublen in range(length,0,-1): 13 for start in range(0,length - sublen +1): 14 substr = str1[start:start + sublen] 15 count += 1 16 if str2.find(substr) > -1: 17 print("count={},substrlen={}0".format(count,sublen)) 18 return substr 19 20 s1 = "abcdefg" 21 s2 = "defabcdoabcdeftw" 22 s3 = "1234a" 23 24 print(findit(s1,s2)) 25 print(findit(s1,s3)) 26 27 28 29 #以上代碼輸出結果以下: 30 count=2,substrlen=60 31 abcdef 32 count=22,substrlen=10 33 a