淺談python中的遞歸


python 淺談 遞歸函數
最近在自學一些python,找了些資料。自己慢慢研究到了遞歸函數這一章,碰到個很經典的例子。漢諾塔的移動。一開始嘗試自己寫的時候發現,這東西怎么可能寫的出來。但是看到別人寫出來以后發現,這東西真的能寫出來。
本着借鑒的目的想去分析一下別人寫的東西。覺得很有意思想給大家分享一下,如果有誤請大家指正首先大家可以先自己想想如何能寫出來。
先說一下:所謂的遞歸,我認為就是不斷重復調用。直到return 出當前的遞歸循環。在我拆分的過程中,大家不妨先自己想一下結果,然后看一下我執行出來的結果,是否和各位所想的一樣呢。
本文僅代表自己觀點,如有錯誤大家指出。

	--------------python 環境為3.6.4-------- 下面為高手寫的代碼
def move(n, a, b, c):
	if n == 1:
		return print(a, '-->', c)
	move(n-1,a,c,b)
	move(1,a,b,c)
	move(n-1,b,a,c)
----------------------------------------------
	乍一看真的是一個很簡單很簡單的三層遞歸函數。我第一眼看到的時候都不認為這個是對的。但是真的執行以后發現。寫的很有道理。輸入2和3執行一下。發現都能出來。
>>> move(2,'a','b','c')
a --> b
a --> c
b --> c
>>> move(3,'a','b','c')
a --> c
a --> b
c --> b
a --> c
b --> a
b --> c
a --> c


直接看,我的水平着實有限,所以打算拆開來一步步看。首先改寫一下格式。我准備一個一個拆分來看。首先將第一個遞歸拆出來。帶入幾個值看一下
def move(n, a, b, c):
    if n == 1:
        return print(a, '-->', c)
    move(n-1,a,c,b)	
>>> move(2,'a','b','c')
a --> b
>>> move(3,'a','b','c')
a --> c
>>> move(4,'a','b','c')
a --> b
>>> move(5,'a','b','c')
a --> c
發現了一個很有意思的結果,就是在輸入值(n>=2)且n為偶數的時候 是 a --> b,輸入值為奇數的時候 是 a --> c。原因也很簡單。在這個函數做遞歸的時候,因為n不等於1,所以遞歸函數一直在自己調用自己。所以字符 b和c的位置一直在互換。  

繼續拆分。
def move(n, a, b, c):
    if n == 1:
        return print(a, '-->', c)
    move(1,a,c,b)
>>> move(2,'a','b','c')
a --> b
>>> move(3,'a','b','c')
a --> b
>>> move(4,'a','b','c')
a --> b
因為,這個n值為定值1.所以無論n(n>=2)值輸入的值為何值都只會輸出 a --> b。

最后拆分最后一個遞歸函數來看,
def move(n, a, b, c):
	if n == 1:
		return print(a, '-->', c)
	move(n-1,b,a,c)
	根據之前的兩個可以想象得出來,這個函數在n(n>=2)輸入奇數和偶數的時候輸出值一定不一樣。這里不做多解釋。
>>> move(2,'a','b','c')
b --> c
>>> move(3,'a','b','c')
a --> c
>>> move(4,'a','b','c')
b --> c
>>> move(5,'a','b','c')
a --> c



接着如果兩兩結合呢。
執行 n=2、3、4的結果分別為:
def move(n, a, b, c):
    if n == 1:
        return print(a, '-->', c)
    move(n-1,a,c,b)
	move(1,a,b,c)
>>> move(2,'a','b','c')
a --> b
a --> c
>>> move(3,'a','b','c')
a --> c
a --> b
a --> c
>>> move(4,'a','b','c')
a --> b
a --> c
a --> b
a --> c
一旦開始兩兩結合。立馬開始變得復雜了,這里有如下幾種可能:
1 順序執行,即傳輸一個值進去,會完整的走一次函數。
如果傳輸值為2,執行move(2-1,a,c,b) return ,move1。輸出結果為如上所示。
如果傳輸值為3,則執行為,move(3-1,a,c,b) pass, move1, move(2-1,a,c,b) return, move(1,a,b,c)。 輸出結果為如上所示
如果傳輸值為4,則執行為,move(4-1,a,c,b) pass,move1,move(3-1,a,c,b),move1,move(2-1,a,c,b) return,move(1,a,b,c)。 這里很明顯輸出結果並不是這樣的。所以並不是順序執行這種。

2 組內循環,即傳輸一個n值進去,在帶有n值運算的條件中,一直循環調用到能return時再跳出當前循環。我認為這種方式應該是正確的。(句末帶--為輸出行,代碼中abc為實際代表的abc)
如輸入的n為2,則
move(2,a,b,c)
	move(1,a,c,b)--return跳出當前循環,abc值為上一層所變化的值
move(1,a,b,c)--
輸出結果為: a-b a-c
輸入的n為3,則
move(3,a,b,c)
	move(3-1,a,c,b)
		move(3-1-1,a,b,c)--return跳出當前循環,abc值為上一層所變化的值
	move(1,a,c,b)--
move(1,a,b,c)	--
輸出結果為 : a-c a-b a-c 
輸入n為4,則
move(4,a,b,c)
	move(4-1,a,c,b)
		move(4-1-1,a,b,c)
			move(4-1-1-1,a,c,b)-return跳出當前循環,abc值為上一層所變化的值
		move(1,a,b,c)--
	move(1,a,c,b)--
move(1,a,b,c)--
輸出結果為:a-b a-c a-b a-c 
這類型的遞歸類似於一個凹字。




兩兩結合還有一種情況就是:這類的循環是先做一次 move1 然后做 move n 時會去調用move1。結果如下所示。
def move(n, a, b, c):
    if n == 1:
		return print(a, '-->', c)
	move(1,a,b,c)
    move(n-1,a,c,b)
執行 n=2、3、4的結果分別為:
>>> move(2,'a','b','c')
a --> c
a --> b
>>> move(3,'a','b','c')
a --> c
a --> b
a --> c
>>> move(4,'a','b','c')
a --> c
a --> b
a --> c
a --> b

這種情況下如果n為2 
move(2,a,b,c)
	move(1,a,b,c)--
	move(2-1,a,c,b)--
輸出結果為:a-c a-b
n為3則
move(3,a,b,c)
move(1,a,b,c)--
	move(3-1,a,c,b)
		move(1,a,c,b)--
			move(3-1-1,a,b,c)--
輸出結果為 a-c a-b a-c 
n為4則
move(4,a,b,c)
	move(1,a,b,c)--
	move(4-1,a,c,b)
		move(1,a,c,b)--
			move(4-1-1,a,b,c)
				move(1,a,b,c)--
					move(4-1-1-1,a,c,b)--
輸出結果為:a-c a-b a-c a-b
這類型的遞歸類似於一個不斷增加的直線(語言很笨拙)


兩兩結合最后一種情況如下所示:這類遞歸中,會先調用n-1,然后,后續函數在當前循環中使用n-1后的值。一組遞歸函數完成之后。重新調用第二組時,會重新初始化輸入值。
def move(n, a, b, c):
    if n == 1:
            return print(a, '-->', c)
    move(n-1,a,c,b)
    move(n-1,b,a,c)
>>> move(2,'a','b','c')
a --> b
b --> c
>>> move(3,'a','b','c')
a --> c
c --> b
b --> a
a --> c
>>> move(4,'a','b','c')
a --> b
b --> c
c --> a
a --> b
b --> c
c --> a
a --> b
b --> c

如果輸入的n為2
move(2,a,b,c)
	move(2-1,a,c,b)--循環1 
	move(2-1,b,a,c)--循環2 
輸出結果為: a-b a-c	
如果輸入的n為3
move(3,a,b,c)
	move(3-1,a,c,b)
		move(3-1-1,a,b,c)--
	move(3-1-1,b,a,c)--循環1結束
	move(3-1,b,a,c)#2組循環開始循環2為2時
		move(3-1-1,b,c,a)--
	move(3-1-1,a,b,c)--
輸出結果為:a-c b-c b-a c-a
如果輸入的n為4
move(4,a,b,c)
	move(4-1,a,c,b)
		move(4-1-1,a,b,c)
			move(4-1-1-1,a,c,b)--
		move(4-1-1-1,b,a,c)--
	move(4-1-1,c,a,b)
		move(4-1-1-1,c,b,a)--
	move(4-1-1-1,a,c,b)--至此循環1結束
	move(4-1,b,a,c)#2組循環開始循環2為3時
		move(4-1-1,b,c,a)
			move(4-1-1-1,b,a,c)--
		move(4-1-1-1,c,b,a)--
	move(4-1-1,a,b,c)#組循環開始循環2為2時
		move(4-1-1-1,a,c,b)--
	move(4-1-1-1,b,a,c)--
輸出結果為:a-b b-c c-a a-b b-c c-a a-b b-c 
這類遞歸類似於波形。	
	
	
	
個人認為我這種想法應該是對的,下面是我自己突發奇想想到的一個驗證我的這個想法的代碼。這個代碼中可以觀察n的各個階段中各個值的實際值。
def move(n, a, b, c):
    print(n,a, b, c)
    if n == 1:
        return print(a, '-->', c)
    move(n-1,a,c,b)
    move(1,a,b,c)
這個代碼可以在任意步驟顯示,a,b,c中的值。可以參考。



接下來就是三個遞歸函數的調用了。根據兩個遞歸函數的調用來猜測一下。
def move(n, a, b, c):
	if n == 1:
		return print(a, '-->', c)
	move(n-1,a,c,b)
	move(1,a,b,c)
	move(n-1,b,a,c)
>>> move(2,'a','b','c')
a --> b
a --> c
b --> c
>>> move(3,'a','b','c')
a --> c
a --> b
c --> b
a --> c
b --> a
b --> c
a --> c
如果n的傳輸值為2時
move(2,a,b,c)
	move(2-1,a,c,b)--
	move(1,a,b,c)--
	move(2-1,b,a,c)--
	
如果n的傳輸值為3時
move(3,a,b,c)
	move(3-1,a,c,b)
		move(3-1-1,a,b,c)--
	move(1,a,c,b)--
	move(3-1-1,c,a,b)--
move(1,a,b,c)--
	move(3-1,b,a,c)
		move(3-1-1,b,c,a)--
	move(1,b,a,c)--
	move(3-1-1,a,b,c)--
詳細說一下這個吧:根據之前所說的,
1.首先帶入(3,a,b,c)進入遞歸調用,先走第一個小循環move(n-1,a,c,b),此時n=3。
小循環中走n=3-1條件不滿足,繼續走小小循環n=3-1-1 return,得出結果,退出當前小小循環。
用當前小循環中的值走(1,a,b,c)輸出一個值,然后用當前的n=2走小循環中的move(n-1,b,a,c)。走完之后。第一個小循環整體走完。
2.然后在整體遞歸函數中走move(1,a,b,c)。
3.然后開始move(n-1,b,a,c)的遞歸調用,同1類似。

move(4,a,b,c)
	move(4-1,a,c,b)
		move(4-1-1,a,b,c)
			move(4-1-1-1,a,c,b)--
		move(1,a,b,c)--
		move(4-1-1-1,b,a,c)--
	move(1,a,c,b)--
	move(4-1-1,c,a,b)
		move(4-1-1-1,c,b,a)--
	move(1,c,a,b)--
	move(4-1-1-1,a,c,b)--
move(1,a,b,c)--
	move(4-1,b,a,c)
		move(4-1-1,b,c,a)
			move(4-1-1-1,b,a,c)--
		move(1,b,c,a)--
		move(4-1-1-1,c,b,a)--
	move(1,b,a,c)--
	move(4-1-1,a,b,c)
		move(4-1-1-1,a,c,b)--
	move(1,a,b,c)--
	move(4-1-1-1,b,a,c)--

以上是n=4時漢諾塔的具體執行步驟。我自己能寫明白。講就算了。大家自行理解吧。
寫在最后,可能我的語言表述並不是特別好,但是步驟寫的很清楚了。每一次的遞歸,遞歸調用遞歸,遞歸調用常函數。常函數調遞歸。以及各個階段值的變換。
因為我也才接觸python沒多久。都是自己在摸索。各位如有問題請及時聯系我。我再進行相應的修改。

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM