斐波那契数列    即     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 矩阵乘法 代数 可视化 python的矩阵乘法_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 矩阵乘法 代数 可视化 python的矩阵乘法_斐波那契数列_02

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

python 矩阵乘法 代数 可视化 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 resdef 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]