Fibonacchi Sequence === 本篇:https://hackmd.io/@IncredibleCactus/SkRGbcMmp ## 定義 #### 費波那契數列 \begin{equation*} F_0 = 0, \ F_1 = 1, \ F_n = F_{n-1} + F_{n-2} \ \ (n ≥ 2) \end{equation*} ## 遞迴 ### 想法 數列的公式看起來很遞迴。對於傳入的 $0$ 和 $1$ 回傳 $F_0$ 和 $F_1$,否則回傳 $F_{n-1}+F_{n-2}$。 ### Code ```python= def fibonacci(n): if n <= 0: return 0 if n == 1: return 1 return fibonacci(n-1) + fibonacci(n-2) print(fibonacci(7)) # should be 13 ``` ### 複雜度 但遞迴的複雜度如何? 經過測試 $n$ 大約每增加 $10$,時間會變成約 $122$ 倍。實際上消耗的時間約為 $1.618^n$,這個複雜度肯定是不行的,算 100 項十萬年都算不完,用手算都比較快。那要如何改進呢? ### 優化 我們可以開一個陣列計算第 $i$ 項有沒有被算過。如果有,那就直接拿陣列裡的東西來用,如果沒有,就計算出來後丟到陣列裡。這樣的複雜度就變成 $O(n)$ 了。 ## $O(n)$ ### 想法 既然我們可以透過第零項和第一項得出第二項,那我們可不可以透過第一項和第二項算出第三項呢?當然可以。同理,當我們有第 $n-2$ 項和第 $n-1$ 項,我們就能算出第 $n$ 項。 ### Code ```python= def fibonacci(n): a, b = 0, 1 for i in range(0, n): b = a + b a = b - a return a print(fibonacci(8)) # should be 21 ``` ### 複雜度 迴圈跑了 $n$ 次,算出下一項 $O(1)$,所以總共 $O(n)$。 ## $O(log n)$ ### 想法 考慮一個 2x1 的矩陣 \begin{equation*} F_n = \begin{pmatrix} a_{n} \\ a_{n+1} \end{pmatrix} \end{equation*} 若有辦法找出 2x2 矩陣 A 使得 \begin{equation*} AF_n = F_{n+1} = \begin{pmatrix} a_{n+1} \\ a_{n+2} \end{pmatrix} \end{equation*} 則有 \begin{equation*} F_n = A^nF_0 = A^n \begin{pmatrix} 0 \\ 1 \end{pmatrix} \end{equation*} 我們不難求出 $2\times2$ 矩陣 $A$ 為 \begin{equation*} A = \begin{pmatrix} 0 & 1 \\ 1 & 1 \end{pmatrix} \end{equation*} \begin{equation*} AF_n = \begin{pmatrix} 0 & 1 \\ 1 & 1 \end{pmatrix} \begin{pmatrix} a_{n} \\ a_{n+1} \end{pmatrix} = \begin{pmatrix} a_{n+1} \\ a_{n+2} \end{pmatrix} = F_{n+1} \end{equation*} 因此,我們可以算出矩陣 $A$ 的 $n$ 次方,從而得到第 $n$ 項。那我們要如何用 $O(log n)$ 的複雜度算出 $A$ 的 $n$ 次方呢?用快速冪就可以在 $O(log n)$ 的時間內算出來。 我懶得寫了所以請參考 https://cp.wiwiho.me/pow/ 的概念。 ### Code ```python= def matrix_multiply(a, b): result = [[None, None], [None, None]] result[0][0] = a[0][0] * b[0][0] + a[0][1] * b[1][0] result[0][1] = a[0][0] * b[0][1] + a[0][1] * b[1][1] result[1][0] = a[1][0] * b[0][0] + a[1][1] * b[1][0] result[1][1] = a[1][0] * b[0][1] + a[1][1] * b[1][1] return result def fast_fibonacci(n): t = [[0, 1], [1, 1]] A = [[0, 1], [1, 1]] while (n >= 1): if n % 2 == 1: A = matrix_multiply(A, t) t = matrix_multiply(t, t) n = n // 2 return A[0][0] print(fast_fibonacci(9)) # should be 34 ``` ### 複雜度 快速冪 $O(log n)$,將 $A^n$ 乘上 $F_0$ 需要 $O(1)$。總時間複雜度 $O(log n)$。