斐波那契數列 即 1、1、2、3、5、8、13、21、34、以此類推,在很多面試題中,面試官都會讓你手寫斐波那契數列的實現。對於一些有編程經驗的人來說,這很容易,他們可以很快寫出類似以下代碼:
設 n 為 大於0的正整數,求第n個斐波那契數(1為第一個,2為第二個...8為第五個)
def feb(n): if n == 1 or n == 2: return n else: return feb(n - 1) + feb(n - 2)但是這裡有個很嚴重的問題就是重複計算
例如,在計算feb(5) 時,feb(1) 會調用多少次?feb(2)呢?
運行結果可見下圖
可以看到 feb(2)feb(1) 被多次調用
如何能避免這個問題呢?
避免重複 的關鍵在於 要實現檢查即將進行的計算是否已經經歷過。
有同學會想到使用列表,每計算一個feb(n),就將結果存儲到列表的下標n 處。
result=[1,,1,2,3,5,8]
feb(n)=result[n]
這看起來似乎是一個好方法,但因為斐波那契數列可以是無限長的,如果計算feb(10000)是否真的需要長度10000的列表?(況且在python中實際列表所佔地址空間會大於其可見長度。)所以這種方式顯然不是可取的方式。
但是在斐波那契數列數列需要經常進行運算且n較小的時候,直接採取已經定義好的列表看起來的確是個一勞永逸的方法。
但是如果面試官要求你不使用列表,即儘可能減少內存佔用呢?
這裡問題就可以簡化為只使用兩個變量。本身斐波那契數列 中 第n個就是 前兩個數相加的和。
只需要一直更新 feb(n-1) 和 feb(n-2) 的值即可。
現在我們可以使用for循環來實現:
def feb(n): if n == 1 or n == 2: return n else: n1,n2=1,2 for i in range(3, n): n1,n2=n2,n1+n2 return n1 + n2這樣做已經效果很好了,不需要藉助數組等大存儲 對象,時間複雜度也只是O(n)級別。
但是有沒有更好的解法?
學習高等數學、線性代數等課程時,可能有數學老師提到過斐波那契數列的另類解法--利用矩陣求解。
數列的遞推公式為:f(1)=1,f(2)=2,f(n)=f(n-1)+f(n-2)(n>=3)
用矩陣表示為:
進一步,可以得出直接推導公式:
接下來,我們先思考,如何用python實現矩陣相乘?
如果接觸過numpy 庫的話,你也許會想到numpy中的 dot 方法。
利用這個方法可以直接計算兩個矩陣(列表)的乘積。但是如果讓你自己去實現呢?
先提醒大家,矩陣可以相乘是有前提的
設有矩陣A*B
矩陣只有當左邊矩陣A的列數等於右邊矩陣B的行數時,才可以相乘。乘積結果矩陣的行數等於左邊矩陣的行數,乘積矩陣的列數等於右邊矩陣的列數
矩陣的乘法是左行乘右列def matrixMul(A, B): res = [[0] * len(B[0]) for i in range(len(A))] for i in range(len(A)): for j in range(len(B[0])): for k in range(len(B)): res[i][j] += A[i][k] * B[k][j] return res
上面的代碼是可用的,但是還有什麼可以優化的地方嗎?
注意到循環的層數足足三層,每一層都需要計算 len()。
所以最完美的寫法是應該是先計算好長度再直接用長度值去遍歷。def matrixMul(A, B): line_A,line_B,column_B=len(A),len(B),len(B[0]) res = [[0] * column_B for i in range(line_A)] for i in range(line_A): for j in range(column_B): for k in range(line_B): res[i][j] += A[i][k] * B[k][j] return res現在,我們可以用 矩陣乘法來 實現 斐波那契數列了
def matrixMul(A, B): line_A,line_B,column_B=len(A),len(B),len(B[0]) res = [[0] * column_B for i in range(line_A)] for i in range(line_A): for j in range(column_B): for k in range(line_B): res[i][j] += A[i][k] * B[k][j] return res
def feb(n): if n <= 1: return max(n, 0) res = [[1, 1], [0, 1]] A = [[1, 1], [1, 0]] while n: if n & 1: res = matrixMul(res, A) A = matrixMul(A, A) n >>= 1 return res[0][1]