Python入門篇-遞歸函數Recursion

            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是:100037 當前遞歸調用次數限制是:2000038 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'
使用base64代碼實現方式 

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
參考案例
相關文章
相關標籤/搜索