算法基礎 II — 遞歸(迴文、斐波那契、歐幾里得算法、漢諾塔)

什麼是遞歸?

遞歸(Recursion),又譯爲遞迴,在數學與計算機科學中,是指在函數的定義中使用函數自身的方法。遞歸一詞還較經常使用於描述以自類似方法重複事物的過程。例如,當兩面鏡子相互之間近似平行時,鏡中嵌套的圖像是以無限遞歸的形式出現的。也能夠理解爲自我複製的過程。python

遞歸是將一個大問題分解成小問題,任何遞歸函數均可以用迭代函數實現。遞歸通常比迭代開銷更大,可是遞歸函數很是好理解它的意思。算法

用遞歸解決問題要注意終止條件,沒有終止條件函數一直運行下去直到報錯。ide

迴文字符串

迴文(palindrome)字符串是一個字符串從左到右讀和從右到左讀徹底同樣,好比abcba函數

用遞歸的方法怎麼判斷一個字符串是否是迴文字符串?一個字符串是迴文字符串那麼它第一個字符就等於最後一個字符,第二個字符就等於倒數第二個字符...。那麼什麼是終止條件?那就是當一個字符串只有一個字符的時候(從左到右讀和從右到左讀徹底同樣)。post

def pal(s):
    if len(s) <= 1: return True
    return s[0] == s[len(s) - 1] and pal(s[1:len(s) - 1])
複製代碼

階乘

階乘(factorial)是全部小於及等於該數的正整數的積。n的階乘計爲n!0! = 1,當大於0n!=n*(n-1)idea

def fac(n):
    if n == 0: return 1
    return n * fac(n - 1)
複製代碼

能夠看到用遞歸的方式寫很是的簡單,表達的意思和定義它的意思同樣。spa

斐波那契數列

斐波那契(Fibonacci)數列是遞歸的方法來定義的:.net

F_0 = 0; F_1 = 1; F_n=F_{n-1}+F_{n-2}

0,1,1,2,3,5,7... n大於2n等於前兩個數的相加。3d

def fib(n):
    if n <= 1: return 0
    elif n == 2: return 1
    else: return fib(n-1) + fib(n - 2)
複製代碼

和定義的意思同樣一目瞭然,雖然效率不高。code

歐幾里得算法

在數學中,展轉相除法,又稱歐幾里得算法(Euclidean algorithm),是求最大公約數的算法。展轉相除法首次出現於歐幾里得的《幾何本來》(第VII卷,命題i和ii)中,而在中國則能夠追溯至東漢出現的《九章算術》。

簡單來講a,b的最大公約數等於其中較小數和兩數的差的最大公約數,好比100,10它們的最大公約數爲100-10,1090-10,10一直持續下去。也能夠看出兩個數的最大公約數等於較小數和較小數除以較大數的餘數的最大公約數。

歐幾里得算法將一個大問題不斷縮小,直到小到咱們能夠輕鬆解決。

def gcd(a,b):
    if b == 0:
        return a
    return gcd(b, a % b)
複製代碼

漢諾塔

漢諾塔(港臺:河內塔)是根據一個傳說造成的數學問題:

有三根杆子A,B,C。A杆上有 N 個 (N>1) 穿孔圓盤,盤的尺寸由下到上依次變小。要求按下列規則將全部圓盤移至 C 杆:

  1. 每次只能移動一個圓盤;
  2. 大盤不能疊在小盤上面。

傳說越南河內某間寺院有三根銀棒,上串 64 個金盤。寺院裏的僧侶依照一個古老的預言,以上述規則移動這些盤子;預言說當這些盤子移動完畢,世界就會滅亡。這個傳說叫作梵天寺之塔問題(Tower of Brahma puzzle)。但不知道是盧卡斯自創的這個傳說,仍是他受他人啓發。

用代碼怎麼表示n個圓盤是怎麼移動的呢?

咱們能夠發現要把所有圓盤移動到目標柱就必須先把最大的圓盤移動到目標柱(由於大盤不能疊在小盤上面),而後把第二大的移動到目標柱上...這樣一直持續下去直到全部圓盤放到目標柱上。

要把大的圓盤移動到目標柱就必須把它上面小的圓盤移動到備用柱(必需要解決這個子問題才能把大圓盤放到目標柱上),移動到備用柱後再把大圓盤放到目標柱上,最後把其餘小圓盤放到目標柱,這樣就OK了。

def hanoi(n, form, to, spare):
    if n == 1:
        print('從 %s 移動到 %s' % (form, to))
    else:
        hanoi(n-1, form, spare, to) # 除最底下的大圓盤把其餘移動到備用柱
        hanoi(1, form, to, spare) # 把最底下的大圓盤移動到目標柱
        hanoi(n-1, spare, to, form) # 再把備用柱上的圓盤移動到目標柱
hanoi(3, '起始柱', '目標柱', '備用柱')
複製代碼

對於一個圓盤只須要一步(直接從從起始柱移動到目標柱)。

對於兩個圓盤就須要把它上面的一個圓盤移動到備用盤,而後把最大的圓盤放到目標柱,最後把備用柱上的放到目標柱子上。一共是3步。

若是有三個圓盤就要把上面兩個圓盤移動到備用柱子(把兩個圓盤放到目標柱上須要3步),而後把最大的圓盤放到目標柱(須要1步),最後把備用柱上的放到目標柱子上(須要3步)。一共7步。

依次類推有n個圓盤就須要兩倍n-1個圓盤須要的步數再+1。對於n個圓盤的步數就爲2^n-1步。能夠看出來它是指數複雜度

若是n等於64最少就須要移動2^{64}-1步,若是一秒鐘能移動一塊圓盤,仍將需 5845.54 億年。目前按照宇宙大爆炸理論的推測,宇宙的年齡僅爲 137 億年。

相關文章
相關標籤/搜索