1. 程式人生 > >《零基礎入門學習Python》(22)--函式:遞迴是神馬

《零基礎入門學習Python》(22)--函式:遞迴是神馬

前言

普通程式設計師用迭代,天才程式設計師用遞迴

知識點

  • 遞迴是神馬? 

遞迴是屬於演算法的範疇。 

遞迴就是函式呼叫自身的一種行為。

>>> def g():
    return g()

>>> g()
Traceback (most recent call last):
  File "<pyshell#6>", line 1, in <module>
    g()
  File "<pyshell#5>", line 2, in g
    return g()
  File "<pyshell#5>", line 2, in g
    return g()
  File "<pyshell#5>", line 2, in g
    return g()
  [Previous line repeated 990 more times]
RecursionError: maximum recursion depth exceeded
  • 設定遞迴的深度,可以使用如下
  • python3預設100層
>>> import sys
>>> sys.setrecursionlimit(1000000) #設定遞迴深度為1000000層
  • 遞迴求階乘 
寫一個求階乘的函式 
正整數階乘指從1乘以2乘以3乘以4一直乘到所要求的數。 
例如所給的數是5,則階乘式是1*2*3*4*5 = 120,所以120 就是5的階乘
#常規寫法:

def factorial(n):
    result = n
    for i in range(1, n):
        result *= i

    return result

number = int(input('請輸入一個正整數:'))
result = factorial(number)
print("%d 的階乘是:%d" % (number, result))


------------------------------------------------
#遞迴寫法:

def factorial(n):
    if n == 1:
        return 1
    else:
        return n * factorial(n-1)
number = int(input('請輸入一個正整數:'))
result = factorial(number)
print("%d 的階乘是:%d" % (number, result))
  • 遞迴實現過程如下圖所示: 

課後習題

測試題

  • 遞迴在程式設計上的形式是如何表現的呢?
#程式設計上,遞迴表現為函式呼叫本身這麼一個行為。

#舉個例子(遞迴求階層):

def jieceng(n):
    if n == 1:
        return 1
    else:
        return n * jieceng(n - 1)
number = int(input('請輸入一個正整數:'))
result = jieceng(number)
print('%d的階層是:%d' % (number,result))
  • 遞迴必須滿足哪兩個基本條件?

1. 函式呼叫自身 2. 設定了正確的返回條件

  • 思考一下,按照遞迴的特性,在程式設計中有沒有不得不使用遞迴的情況?

例如漢諾塔,目錄索引(因為你永遠不知道這個目錄裡邊是否還有目錄), 快速排序(二十世紀十大演算法之一),樹結構的定義等如果使用遞迴,會事半功倍, 否則會導致程式無法實現或相當難以理解。

  • 用遞迴去計算階層問題或斐波那契數列是很糟糕的演算法,你知道為什麼嗎?

小甲魚在課程的開頭說“普通程式設計師用迭代,天才程式設計師用遞迴”這句話是不無道理的。

但是你不要理解錯了,不是說會使用遞迴,把所有能迭代的東西用遞迴來代替就是“天才程式設計師”了,
恰好相反,如果你真的這麼做的話,那你就是“烏龜程式設計師”啦。

為什麼這麼說呢?不要忘了,遞迴的實現可以是函式自個兒呼叫自個兒,每次函式的呼叫
都需要進行壓棧、彈棧、儲存和恢復暫存器的棧操作,所以在這上邊是非常消耗時間和空間的。

另外,如果遞迴一旦忘記了返回,或者錯誤的設定了返回條件,那麼執行這樣的遞迴程式碼就會變
成一個無底洞:只進不出!所以在寫遞迴程式碼的時候,千萬要記住口訣:
遞迴遞迴,歸去來兮!出來混,總有一天是要還的!

  • 請聊一聊遞迴的優缺點

優點:

1. 遞迴的基本思想是把規模大的問題轉變成規模小的問題組合,從而簡化問題的解決難度(例如漢諾塔遊戲)。

2. 有些問題使用遞迴使得程式碼簡潔易懂(例如你可以很容易的寫出前中後的二叉樹遍歷的遞迴演算法,但如果要寫出相應的非遞迴演算法就不是初學者可以做到的了。)

缺點:

1. 由於遞迴的原理是函式呼叫自個兒,所以一旦大量的呼叫函式本身空間和時間消耗是“奢侈的”

2. 初學者很容易錯誤的設定了返回條件,導致遞迴程式碼無休止呼叫,最終棧溢位,程式奔潰。

動動手

  • 使用遞迴編寫一個power()函式模擬內建函式pow(),即power(x,y)為計算返回x的y次冪的值。
#常規寫法:
def power(x,y):
    result = 1
    for i in range(y):
        result *= x
    return result

power(3,4)
81

#遞迴寫法:
def power(x,y):
    if y == 0:
        return 1
    else:
        return x * power(x,y-1)

power(3,4)
81
  • 使用遞迴編寫一個函式,利用歐幾里得演算法求最大公約數,例如gcd(x,y)返回值為引數x和引數y的最大公約數。
#常規寫法
def gcd(x, y):
    while y:            # -->6        -->4        -->2         -->0
        t = x % y       # -->4%6=4    -->6%4=2    -->4%2 =0     
        x = y           #  -->6        -->4        -->2
        y = t           # -->4        -->2        -->0

    return x

print(gcd(4, 6))

#遞迴寫法
def gcd(x,y):
    if y:
        return gcd(y,x%y)
    else:
        return x


gcd(4,6)
2