遞歸,就是在函數運行中本身調用本身
代碼示例:python
def recursion(n): # 定義遞歸函數 print(n) # 打印n recursion(n+1) # 在函數的運行種調用遞歸 recursion(1) # 調用函數
這個函數在不斷的本身調用本身,每次調用n+1,看下運行結果:數據結構
1 2 ..... 998Traceback (most recent call last): File "D:/py_study/day08-函數/python遞歸函數md/01-什麼是遞歸.py", line 11, in <module> recursion(1) File "D:/py_study/day08-函數/python遞歸函數md/01-什麼是遞歸.py", line 9, in recursion recursion(n+1) File "D:/py_study/day08-函數/python遞歸函數md/01-什麼是遞歸.py", line 9, in recursion recursion(n+1) File "D:/py_study/day08-函數/python遞歸函數md/01-什麼是遞歸.py", line 9, in recursion recursion(n+1) [Previous line repeated 993 more times] File "D:/py_study/day08-函數/python遞歸函數md/01-什麼是遞歸.py", line 8, in recursion print(n) RecursionError: maximum recursion depth exceeded while calling a Python object Process finished with exit code 1
可爲何執行了900屢次就報錯了呢?還說超過了最大遞歸深度限制,爲何要限制呢?框架
通俗來說,是由於每一個函數在調用本身的時候,尚未退出,佔內存,多了確定會致使內存崩潰.函數
本質上來將,在計算機中,函數調用是經過棧(stack)這樣數據結構實現的,每當進入一個函數調用,棧就會加一層棧幀,每當函數返回,棧就會少一層棧幀.因爲棧的大小不是無限的,因此,遞歸調用次數多了,會致使棧溢出.優化
咱們還能夠修改遞歸深度,代碼以下:code
import sys sys.setrecursionlimit(1500) # 修改遞歸調用深度 def cacl(n): print(n) cacl(n+1) cacl(1)
運行結果以下:繼承
1 2 ...... 1498Traceback (most recent call last): File "D:/py_study/day08-函數/python遞歸函數md/02-修改遞歸深度.py", line 11, in cacl cacl(n+1) File "D:/py_study/day08-函數/python遞歸函數md/02-修改遞歸深度.py", line 11, in cacl cacl(n+1) File "D:/py_study/day08-函數/python遞歸函數md/02-修改遞歸深度.py", line 11, in cacl cacl(n+1) [Previous line repeated 995 more times] File "D:/py_study/day08-函數/python遞歸函數md/02-修改遞歸深度.py", line 10, in cacl print(n) RecursionError: maximum recursion depth exceeded while calling a Python object
# 計算n! # 相信不少人都學過階乘,好比5! = 5*4*3*2*1 n! = n*(n-1)*(n-2)*...*1,那麼在遞歸中該如何實現呢? # 1.打好函數的框架 def factorial(n): # 定義一個計算階乘的函數 pass # 不作任何操做 factorial(3) # 調用 # 2.考慮兩種狀況,若是n=1,那麼1的階乘就是1了,若是這個傳遞的參數大於1,那麼就須要計算繼承了. def factorial(n): if n == 1: # 判斷若是傳遞的參數是1的狀況 return 1 # 返回1,return表明程序的終止 res = factorial(1) # res變量來接受函數的返回值 print(res) # 打印 2.1若是傳遞的參數不是1,怎麼作? def factorial(n): if n == 1: return 1 else: # 5*4! = 5*4*3! = 5*4*3*2! return n * factorial(n-1) # 傳遞的參數是n,那麼再次調用factorial(n-1) res = factorial(1) print(res)
# 讓10不斷除以2,直到0爲止。 int(10/2) = 5 int(5/2) = 2 int(2/2) = 1 int(1/2) = 0 # 1.一樣第一步先打框架 def cacl(n): # 定義函數 pass cacl(10) # 2.那麼咱們想從10開始打印而後一直到0,怎麼作? def cacl(n): # 定義函數 print(n) cacl(10) # 3.已經把打印的值傳遞進去了,那麼就是在裏面操做了 def cacl(n): # 定義函數 print(n) # 打印傳遞進去的值 v = int(n /2) # n/2 if v>0: # 若是v還大於0 cacl(v) # 遞歸,把v傳遞進去 print(n) # 打印v,由於已經調用遞歸了,因此此時的n是v cacl(10)
運行結果以下:遞歸
10 5 2 1 1 2 5 10
怎麼輸出會是這樣呢?我剛剛說過,什麼是遞歸?遞歸就是在一個函數的內部調用函數自己,咱們打個比方,遞歸一共有3層,那麼第二層就是調用第一層的結果,第三層又去調用第二層的結果,因此!當上面這個程序運行時,第一次打印的是10,而後除上2,等因而5,再繼續除,一直到了1,而後1/2是等於0的,此時就沒有調用了遞歸,可是我還在調用上一層函數,就須要把這個數給返回出來,因此就變成後來的1,2,5,10了。內存
遞歸特性it
- 1.必需要有一個明確的結束條件, 不然就變成死循環致使棧溢出
- 2.每次進入更深一層遞歸時,問題規模相比上次遞歸都應有所減小,這句話的以上就是說,每進入一次遞歸,就會解決一些東西,數據量就會愈來愈小,最終解決了全部的問題,若是進入一次遞歸沒有解決問題,那麼無論遞歸多少層都沒有意義,直到致使棧溢出。
- 3.遞歸效率比較低,遞歸層次過多會致使棧溢出,意思是:每當進入一次函數調用,棧就會加一層棧幀,每當函數返回,就減小一層棧幀,因爲棧不是無限大小的,因此,遞歸調用的次數過多,會致使棧溢出。
那麼有沒有優化方式呢?確定是有的
我在知乎上找了一個特別有意思的例子來講明下什麼是尾遞歸:
def story() { 從前有座山, 山上有座廟, 廟裏有個老和尚, 一天老和尚對小和尚講故事:story() // 尾遞歸,進入下一個函數再也不須要上一個函數的環境了,得出結果之後直接返回。 } def story() { 從前有座山, 山上有座廟, 廟裏有個老和尚, 一天老和尚對小和尚講故事:story(),小和尚聽了,找了塊豆腐撞死了 // 非尾遞歸,下一個函數結束之後此函數還有後續,因此必須保存自己的環境以供處理返回值。 }
尾遞歸,進入下一個函數再也不須要上一個函數的環境了,得出結果之後直接返回。
def cal(n): print(n) return cal(n+1) # return表明函數的結束 cal(1) # 這個會一直打印,直到致使棧溢出 # 調用下一層的同時,本身就退出了