關於遞迴的總結——漢諾塔、素因數的求解(Python實現)
在Python函式的學習中,再次對函式的遞迴感到了迷惑,都說遞迴邏輯清晰,應用簡單,但是在應用中卻總有些不理解的地方,甚至感到很疑惑,在此進行總結,希望能理解。首先看一下階乘的遞迴求法:
def getNum(num): if num > 1: result = num * getNum(num-1) else: result = 1 return result a = getNum(5) print(a) #可以總結一下常見遞迴的套路 # def fun(i): # if 成立條件: # 遞迴呼叫 # else: # ***** # return *****
我們可以看出遞迴函式具有幾點很重要的要素,首先是遞迴的跳出條件,類似於迴圈,總要有跳出條件,沒有跳出條件,那麼便會無限迴圈,進入死結。其次便是對自身的呼叫,比如上面的第三行的result = num * getNum(num-1),便是對自身的呼叫,注意引數的傳遞,以及我們需要求得的結果。最後便是遞迴結束後的操作,在階乘裡遞迴結束後的操作便是返回result(有些函式裡我們需要返回空)。下面看一下素因數的求法。程式碼如下:
#-*- coding:utf-8 -*- import math def suyinshu(n): for i in range(2,int(math.sqrt(n+1))+1): #range裡必須是整數 if n%i == 0: print(i) suyinshu(n/i) return if(n > 1): #如果是素數 print(n) suyinshu(100)
很早以前便寫過求某一個數n的素因數遞迴求法的c++版,但是現在再看竟是一頭霧水,可見功底還是不到家。首先分析下,求素因數的思路:用n對i(2到根號n)取模,為零的便證明是n的因數,將其輸出,之後再用n/i作為引數,遞迴呼叫函式,再次求n(n/i)的因數,如果沒有因數(證明本身是素數),便將n輸出。注意return的位置,在呼叫結束後,函式開始返回,此時因為不需要返回值,便返回空。下面的圖片可以說明遞迴呼叫過程。
另外說到遞迴便不得不提漢諾塔,所謂的完美的遞迴,便是漢諾塔,程式碼如下:
def move(n, a, buffer, c): if(n == 1): print(a,"->",c) return move(n-1, a, c, buffer) move(1, a, buffer, c) move(n-1, buffer, a, c) move(3, "a", "b", "c")
理解起來很是抽象,但是原理很簡單,有a、b、c三個柱子,a上有n個盤子,我們的目的便是將n個盤子原封不動的放到c上,分為三步: move(n-1, a, c, buffer) 第一步,將a柱上面的n-1個盤子通過c按照從小到大的規則先移動到緩衝區buffer(b)。 move(1, a, buffer, c) 第二步,a上的n-1個盤子的遞迴移動完成之後,把a柱上的最後一個盤子通過b(buffer)移動到c,也就是所謂的最底下的盤子 move(n-1, buffer, a, c) 第三步,將b(buffer)上的n-1個盤子通過a移動到c上 遞迴跳出條件便是n==1,此時a上只有一個盤子,示意將a移動到c上便可結束。 這便是遞迴,看似不可思議,但就是如此執行。
同時我們要理解一點,用遞迴解決的問題,都可以轉化為迴圈執行,因為遞迴實質是呼叫棧,而棧和遞迴又可以相互轉化。因此我們可以將素因數的求法用迴圈表示(漢諾塔的遞迴方法不再給出):
def suyinshu2(n):
for i in range(2,int(math.sqrt(n))):
while n%i == 0:
print(i)
n = n/i
if(n > 1):
print(n)
suyinshu2(100)