题目
Question1 2017年9月17日
Description:
查找斐波纳契数列中第 N 个数。
所谓的斐波纳契数列是指:
前2个数是 0 和 1 。
第 i 个数是第 i-1 个数和第i-2 个数的和。
斐波纳契数列的前10个数字是:
0, 1, 1, 2, 3, 5, 8, 13, 21, 34 …
分析
1.递归
根据题目描述就可以列出求斐波那契数列的公式:
f(n) = f(n-1) + f(n-2)
def fibonacci1(self, n):
if n == 1 or n == 2:
return n-1
else:
return self.fibonacci1(n-1) + self.fibonacci1(n-2)
分析一下,求第n个数要执行多少次?
要执行
1+2+4+8+...+2n−2=2n−2
次
时间复杂度:O( 2N )
2.循环
当然按照公式,一开始我们就定义两个数,做一次n级的循环,每次循环给这两个数重新赋值,这样就能把执行次数维持在n的时间复杂度上:
def fibonacci2(self,n):
if n == 1 or n == 2:
return n-1
f1 = 0
f2 = 1
f3 = 1
i = 3
for i in range(i-1,n):
f3 = f2
f2 = f1 + f3
f1 = f3
re*turn f2
时间复杂度:O(N)
3.矩阵
仔细观察一下,其实斐波那契数列前后项的关系可以用矩阵相乘实现:
我们通过不断迭代,很显然能得出公式:
所以要求f(n),只需要求出矩阵
的值就行了。
计算二阶矩阵的N次幂运算,由于二阶矩阵乘法满足结合律,这样,可以快速计算二阶矩阵的n次幂运算。
假设A为一个二阶矩阵,则A的幂运算满足下面的条件:
A6=A3∗A3
A7=A3∗A3∗A1=A4∗A2∗A1
在这里,我们可以类似地把A看做是二进制中的2, 27=24∗22∗21 也就是说可以把矩阵的幂转换成二进制来表示。从而可以将n次幂拆解成长度为logn的二进制数来表示:7=111(二进制)。
这就是快速求二阶矩阵的核心方法。
def matrix(n):
base=[[1,1],[1,0]] # 基础矩阵
ans=[[1,0],[0,1]] # 结果矩阵
while n:
if n&1: # 取n的二进制的最后一位和1做与运算,如果最后一位是1,则进入if体内部
ans=multi(ans,base) # 如果在该位置n的二进制为1,则计算ans和base矩阵
base=multi(base,base) # base矩阵相乘,相当于初始base矩阵的幂*2
n>>=1 # n的二进制往右移一位
return ans[0][1] # 最后获取到的二阶矩阵的[0][1]即f(n)的值
def multi(a,b): # 计算二阶矩阵的相乘
c=[[0,0],[0,0]] # 定义一个空的二阶矩阵
for i in range(2):
for j in range(2):
for k in range(2): # 新二阶矩阵的值计算
c[i][j]=a[i][k]*b[k][j]
return c
时间复杂度:O(logN)
总结
这个是Lintcode上的入门题,原本以为一个晚上就能写完的,结果发现从慢到快的算法研究弄了我好几天,最后的矩阵乘法也是网上看到的,毕竟数学荒废好多年了。正好新学了一点点python,虽然算法题对我现在的工作感觉没什么帮助,本着练手python编程和活动脑筋的目的,还是决定每个星期研究个一个来玩玩。