算法之矩阵计算斐波那契数列

算法之矩阵计算斐波那契数列

本节内容

  1. 斐波那契介绍
  2. 普通方式求解斐波那契
  3. 矩阵概念
  4. 矩阵求幂
  5. 矩阵求解斐波那契

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),也就是说,使用升维的手段将一维空间进行扭曲从而将距离缩短,可以更快的计算出结果。

由此推导出如果人来要突破光速的极限,需要将现有的三维空间升级到四维空间,扭曲空间从而缩短距离,达到突破光速的目的。

posted @ 2016-10-25 10:13  随风飘雪012  阅读(14884)  评论(0编辑  收藏  举报
返回顶部 $(function(){ $('#returnTop').click(function () { $('html,body').animate({ scrollTop: '0px' }, 800); returnfalse; }); });