斐波那契数列    即     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)呢?

运行结果可见下图

python numpy 矩阵乘积 点乘 叉乘 python中的矩阵乘法_斐波那契数列

可以看到 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 矩阵乘积 点乘 叉乘 python中的矩阵乘法_矩阵相乘_02

  进一步,可以得出直接推导公式:

python numpy 矩阵乘积 点乘 叉乘 python中的矩阵乘法_矩阵相乘_03

 

接下来,我们先思考,如何用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]