算法之矩阵计算斐波那契数列
算法之矩阵计算斐波那契数列
本节内容
- 斐波那契介绍
- 普通方式求解斐波那契
- 矩阵概念
- 矩阵求幂
- 矩阵求解斐波那契
1.斐波那契介绍
斐波那契数列有关十分明显的特点,那是:前面相邻两项之和,构成了后一项。即f(n)=f(n-1)+f(n-2),f(0)=0,f(1)=f(2)=1,推导下去f(3)=2,f(4)=3,f(5)=5。。。。。。
2.普通方式求解斐波那契
按照上面提供的推导公式,普通方式求解斐波那契数列代码如下:
1 def normal(n): 2 a,b,c=0,1,1 3 while n: 4 a,b,c=b,c,b+c 5 n-=1 6 return a
使用上面的方式求解第n项斐波那契数列的时间复杂度为O(n),也就是说,时间复杂度随着n的增长而线性增长。
3.矩阵概念
开始,先来介绍一下矩阵的概念:在数学中,矩阵(Matrix)是一个按照长方阵列排列的复数或实数集合,最早来自于方程组的系数及常数所构成的方阵。
这里不介绍矩阵的各方面知识了,如果那样的话。。。就是一篇数学笔记了。。。这里只讲解矩阵相乘的概念。
矩阵相乘:矩阵相乘最重要的方法是一般矩阵乘积。它只有在第一个矩阵的列数(column)和第二个矩阵的行数(row)相同时才有意义。一般单指矩阵乘积时,指的便是一般矩阵乘积。一个m×n的矩阵就是m×n个数排成m行n列的一个数阵。由于它把许多数据紧凑的集中到了一起,所以有时候可以简便地表示一些复杂的模型。
设A为m*p的矩阵,B为p*n的矩阵,那么称m*n的矩阵C为矩阵A与B的乘积,记作C=AB:
4.矩阵求幂
上面已经介绍过了矩阵相乘的概念了,那么,斐波那契该怎么由矩阵标示呢?
从第三项开始,每一项都是前两项之和。 F(n)=F(n−1)+F(n−2), n⩾3 把斐波那契数列中 相邻的两项F(n)和F(n−1)写成一个2×1的矩阵。
斐波那契数列用矩阵推导如下:
求F(n)等于求二阶矩阵的n - 1次方,结果取矩阵第一行第一列的元素。
问题转换为二阶矩阵的n次幂。
而计算二阶矩阵的N次幂运算,由于二阶矩阵乘法满足结合律,这样,可以快速计算二阶矩阵的n次幂运算。
假设A为一个二阶矩阵,则A的幂运算满足下面的条件:
A**6=A**3∗A**3
A**7=A**3∗A**3∗A**1=A**4*A**2*A**1
在这里,我们可以类似地把A看做是二进制中的2,2**7=2**4*2**2*2**1也就是说可以把矩阵的幂转换成二进制来表示。从而可以将n次幂拆解成长度为logn的二进制数来表示:7=111(二进制)。
这就是快速求二阶矩阵的核心方法。
5. 矩阵求解斐波那契
前戏做足了,下面就该秀代码了。
1 def multi(a,b): # 计算二阶矩阵的相乘 2 c=[[0,0],[0,0]] # 定义一个空的二阶矩阵 3 for i in range(2): 4 for j in range(2): 5 for k in range(2): # 新二阶矩阵的值计算 6 c[i][j]=c[i][j]+a[i][k]*b[k][j] 7 return c 8 9 10 def matrix(n): 11 base=[[1,1],[1,0]] # 元矩阵,这里可以把元矩阵看做是2**0=1 12 ans=[[1,0],[0,1]] # 结果矩阵 最开始的结果矩阵也可以看做是1,因为这个矩阵和任意二阶A矩阵相乘结果都是A 13 while n: 14 if n&1: # 取n的二进制的最后一位和1做与运算,如果最后一位是1,则进入if体内部 15 ans=multi(ans,base) # 如果在该位置n的二进制为1,则计算ans和base矩阵 16 base=multi(base,base) # base矩阵相乘,相当于初始base矩阵的幂*2 17 n>>=1 # n的二进制往右移一位 18 return ans[0][1] # 最后获取到的二阶矩阵的[0][1]即f(n)的值
最后把例子的完整代码贴出来:
1 import time 2 3 4 def multi(a,b): 5 c=[[0,0],[0,0]] 6 for i in range(2): 7 for j in range(2): 8 for k in range(2): 9 c[i][j]=c[i][j]+a[i][k]*b[k][j] 10 return c 11 12 13 def matrix(n): 14 base=[[1,1],[1,0]] 15 ans=[[1,0],[0,1]] 16 while n: 17 if n&1: 18 ans=multi(ans,base) 19 base=multi(base,base) 20 n>>=1 21 # for i in range(2): 22 # print(ans[i]) 23 return ans[0][1] 24 25 def normal(n): 26 a,b,c=0,1,1 27 while n: 28 a,b,c=b,c,b+c 29 n-=1 30 return a 31 32 n=int(input(">>>")) 33 start=time.time() 34 print("Normal:",normal(n)) 35 print("use:",time.time()-start) 36 start=time.time() 37 print("Matrix:",matrix(n)) 38 print("use:",time.time()-start) 39 #计算结果 40 >>>65536 41 Normal: 731992144602...... 42 use: 0.07219505310058594 43 Matrix: 731992144602...... 44 use: 0.023076772689819336
可以看出来当n的值越来越大的时候,两种方式计算出结果的时间差距将越来越大,正常的计算时间复杂度是O(n),矩阵求值的时间复杂度是O(logn)。
后记:
由此可以看出,使用推导式f(n)=f(n-1)+f(n-2)求斐波那契的第n项的算法复杂度极限为O(n),这是一维世界下的极限。将其从一维上升到二维,用二阶矩阵推导斐波那契数列时,计算的算法复杂度为O(logn),也就是说,使用升维的手段将一维空间进行扭曲从而将距离缩短,可以更快的计算出结果。
由此推导出如果人来要突破光速的极限,需要将现有的三维空间升级到四维空间,扭曲空间从而缩短距离,达到突破光速的目的。