一次搞懂費氏數列和遞迴(Python code)

--

目錄
1.前情提要
1.1題目:搭電梯
1.2輸入輸出範例
2.開始解題
2.1什麼是費式數列
2.2遞迴表示
2.3遞迴的複雜度(2^n)
2.4快取暫存的作法
2.5尾遞迴(tail recursive)的作法
2.6疊代(iterative)的作法-空間換取時間
3.全部的Code
4.小心得
Photo by Mario Mesaglio on Unsplash

前情提要

由於在刷題的時候遇到了一個題目,恍然大悟他原來在考費式數列啊!因此來多了解一些費式數列。

題目:搭電梯

出自:LIOJ 平台的#1003

有一台電梯部分故障,他每次只能往上一層樓或是往上兩層樓,其餘樓層不能直達,如果從一樓要去五樓,進入電梯有兩個選擇,分別是按二樓或三樓,假設按了三樓,那接下來只能按四樓或五樓,以此類推,依照電梯特殊運行的方式,要來計算到某一個樓層共有幾種方法。

輸入輸出範例

所以如果要到五樓,共有五種方法可以抵達。

看完題目不熟的時候,有點難聯想要用費氏數列來解,那先來看看~

開始解題

什麼是費式數列

我們常可以看到他的另外一個稱呼是:Fibonacci 斐波那契數列,他是一種數列,可以用遞迴的方式來表示:

  1. 第0個數為0
  2. 第1個數為1
  3. 接下來的數為前兩數之和
維基百科:費氏數列

也就是從頭開始為:
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233…。

遞迴表示

這就屬於以遞迴的方式表達,那對我而言也是最淺顯易懂,最直譯的方式。

遞迴的複雜度(2^n)

常見的有六種時間複雜度的分類,其中一類為O(2^n),這也是費氏數列所在的類,可參考: 初學者學演算法|從費氏數列認識何謂遞迴

有關於於當執行到第N層時,為什麼複雜度會變成2的N次方,可以參考以下影片 : 什麼是費氏數列 in python -part 1- recursive

這一個類別O(2^n),表示,執行步驟會是2的n次方,簡單想因為它都需要呼叫到最底層fibo(1)和fibo(0),可以想像當輸入數字持續增大時,所花費的時間不是線性增長,而是倍數增加了!

當我們要輸入一個很大的數,程式則終止無法運算了,為了要解決這個問題我們需要用空間換取時間。

快取暫存的作法

因為每一回合,都需要前兩個數相加,所以如果把每一個數列的數都存下來,接下來就不用遞迴到最底層0或是1的時候了!

可以參考 :什麼是費氏數列 in python -part 2- lru_cache

使用快取的方式,用functools package 存下每一個數值,讓他變成一個數列,這時候運算過的數值例如,n=3、n=4、n=5都會被依序存下來。

那來簡介一下 functools package 的 lru_cache,它是一個可以提供函數緩存(快取、暫存)功能的裝飾器,在費氏數列中使用它,就是為了把每次計算值都暫存下來,好讓後續繼續使用這些數值,而不用再次運算。

可以參考: Python|functools|lru_cache fibonacci numbers ( 費氏數列 )

尾遞迴(tail recursive)的作法

可參考: 什麼是費氏數列 in python -part 3- tail recursive

遞迴會面臨到一個問題是,會一直call已經計算過的,可以用尾遞迴解決這個問題,使用到的重要觀念是把當前的計算結果,傳到下一層去,這樣做和原本的遞迴的作法是不同,原本遞迴則是一直重複計算直到最底層,那尾遞迴可以想像成反過來,它把算好的丟到下一層去,就不用重複計算了!

疊代(iterative)的作法-空間換取時間

上面的幾個方法,其時間複雜度還是停留在2的N次方,那要把時間複雜度降低,降成N可以採用這樣的方式。

參考: 什麼是費氏數列 in python -part 4- iterative

這個方法真的最美,最乾淨的code,而且效率和可以的範圍也是最好的。

全部的Code

小心得

回到這一個搭電梯的題目,如果牽扯到1或2,往上一層往上兩層這樣的關鍵字時,就可以去和遞迴做聯想,另外遞迴的方法有這幾種,統整下來:

  1. 一般的遞迴
  2. 使用快取暫存( functools package 的 lru_cache)
  3. 尾遞迴
  4. 疊代

通過練習,更完整的認識遞迴,下次遇到遞迴問題時,可以把它寫得更美了!

❤️感恩看到這裡的你,希望這篇文章有幫上你👏歡迎拍手給我鼓勵,我是甜不辣馬拉松,我們下次見

--

--

甜不辣馬拉松
甜不辣馬拉松

Written by 甜不辣馬拉松

幻想自己是貝多芬,可是敲打的卻是機械鍵盤

No responses yet