斐波那契數列與矩陣乘法的聯繫以及其python實現

 斐波那契數列    即     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)
    #這裏設數列從 1,2,3,5,8 開始

但是這裏有個很嚴重的問題就是重複計算

 例如,在計算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)): #對於A的每行
    for j in range(len(B[0])): #對於B的每列
      for k in range(len(B)):   #對於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): #對於A的每行
    for j in range(column_B): #對於B的每列
      for k in range(line_B):   #對於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): #對於A的每行
    for j in range(column_B): #對於B的每列
      for k in range(line_B):   #對於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]]  
    # 單位矩陣,等價於1 。如果  res 取值爲[[1,0],[0,1]] 則會得1,1,2,3,5,8...這個無所謂
    A = [[1, 1], [1, 0]]  # A矩陣
    while n:
        if n & 1: res = matrixMul(res, A)  # 如果n是奇數,或者直到n=1停止條件
        A = matrixMul(A, A)  # 快速冪
        n >>= 1  # 整除2,向下取整
    return res[0][1]

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章