求斐波那契数列的第n项
来源:互联网 发布:网络教育网络统考 编辑:程序博客网 时间:2024/06/08 06:14
提要
本文介绍了4种(3种?)求斐波那契数列第n项的方法。
斐波那契数列简介
斐波那契数列(Fibonacci sequence),又称黄金分割数列、因数学家列昂纳多·斐波那契(Leonardoda Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列:1、1、2、3、5、8、13、21、34、……在数学上,斐波纳契数列以如下被以递归的方法定义:
解法:
0.按照递推式直接计算,时间复杂度O(n)
1.矩阵快速幂
矩阵乘法和矩阵快速幂的简介请看我的另一篇博客:http://blog.csdn.net/George__Yu/article/details/77231013
现在我们有这样一个矩阵A:
然后我们构造一个矩阵
见证奇迹的时刻到了!
我们发现:
若i刚开始时等于1,即A=
又因为矩阵乘法具有结合律,所以我们可以把
这样,我们把递推斐波那契数列第 n 项的时间复杂度从
很神奇吧?
求斐波那契数列第n项的代码:
//矩阵快速幂求斐波那契数列第n项#include <iostream> #include <cstdio>#include <cstring>#include <cstdlib>#include <algorithm>#include <cmath>using namespace std;typedef long long LL;struct matrix{ LL z[5][5]; int m,n;}origin,res,fib;LL n;LL p=1e9+7;void print(matrix x){ for(int i=1;i<=x.m;i++) { for(int j=1;j<=x.n;j++) printf("%d ",x.z[i][j]); printf("\n"); } printf("\n");}matrix mul(matrix x,matrix y)//(AB)ij=∑Aik*Bkj=Ai1*B1j+Ai2*B2j+...+Aik*Bkj { matrix t; t.m=x.m; t.n=y.n; memset(t.z,0,sizeof(t.z)); for(int a=1;a<=x.m;a++) for(int b=1;b<=x.n;b++) for(int c=1;c<=y.n;c++) t.z[a][c]+=x.z[a][b]*y.z[b][c]; for(int i=1;i<=x.m;i++) for(int j=1;j<=x.n;j++) t.z[i][j]%=p; return t;}void matrixpow(LL n){ while(n) { if(n&1) res=mul(res,origin); origin=mul(origin,origin); n>>=1; }}void init(){ scanf("%lld",&n); origin.z[2][1]=origin.z[2][2]=origin.z[1][2]=1; //构造斐波那契数列的递推矩阵 | 0 1 | origin.m=origin.n=2;// | 1 1 | for(int i=1;i<=2;i++) res.z[i][i]=1;//构造单位矩阵 | 1 0 | res.m=res.n=2; // | 0 1 | fib.z[1][2]=1;//构造斐波那契数列第0项和第一项的矩阵| 0 1 | fib.m=1;fib.n=2;}int main(){ init(); matrixpow(n); fib=mul(fib,res); printf("%lld\n",fib.z[1][1]%p); return 0;}
推广:
然而问题还没结束。
矩阵B是怎么构造出来的?
若要求递推的数列是
这时怎么构造矩阵B?
其实很简单。
这时矩阵
我们不妨设矩阵
因为我们希望
根据矩阵乘法的定义又有
那么容易知道
所以
所以
大家可以直接记住这个结论,如果记不住的话,在a,b已知时可以按刚才的方法解方程求x,y,z,w。
再推广:
如果递推式不止两项怎么办?比如这样:
还是按刚才的办法求B矩阵!
这里我直接给出结果,有兴趣的朋友可以自己研究一下。
B矩阵是 k 行 k 列的矩阵。
这时用矩阵快速幂递推的时间复杂度是
一般 n 都是远大于 k 的,所以绝大多数情况还是矩阵快速幂快。
(PS:我应该没算错,如果大家发现我算错了可以在评论中指出来qwq)
2.利用斐波那契数列的二倍项公式
二倍项公式:
通过这个公式,可以分治地求出斐波那契数列的第n项。
若n是偶数,我们想要知道F[n],只需要知道F[n/2]和F[n/2-1];
若n是奇数,我们想要知道F[n],只需要知道F[n/2+1]和F[n/2](除法默认向下取整)。
在 n 极大的时候(如
由于我们每次都把数据折半,因此空间复杂度并不高,map所用的空间和递归所用的栈空间都是
然而在时间复杂度上,因为用了map和递归计算,所以要比矩阵快速幂慢一些。大概是
无论从空间上还是时间上,这种方法都要劣于矩阵快速幂。如果不会矩阵快速幂,可以先用这种方法。
代码:
#include <iostream>#include <cstdio>#include <algorithm>#include <cstdlib>#include <cstring>#include <map>#define mod 1000000007using namespace std;typedef long long LL;LL n;map<LL,LL>m;map<LL,LL>::iterator it;//定义一个迭代器 LL F(LL i)//递归求解 { LL res1,res2,res; if(i<3) return 1; //F[1]=F[2]=1 it=m.find(i); if(it==m.end())//未算过 { if(i&1)//奇数使用公式(2) { res1=F(i>>1); res2=F((i+1)>>1); res=(res1*res1+res2*res2)%mod; } else//偶数使用公式(1) { res1=F((i-2)>>1); res2=F(i>>1); res=((res1<<1)+res2)*res2%mod; } m[i]=res;//记录 return res; } else return it->second; //算过直接返回}int main(){ scanf("%lld", &n); printf("%lld\n", F(n)); return 0;}
3.分段打表
如果数据范围是 n <= x,那么我们可以先把
代码就不贴了,这种方法比较玄学,大家也不一定非要以
注意:这种方法只在n稍大于1e8的时候比较有用,比如1e10左右。n太大时还是乖乖用上面两种方法吧。
例题
洛谷【p1962】斐波那契数列
https://www.luogu.org/problem/show?pid=1962
题目大意:求斐波那契数列的第n项,n<=
大家可以当模板题交一下自己的代码。
The End
- 求斐波那契数列第n项
- 求斐波那契数列第n项
- 求斐波那契数列的第 n 项的值
- 求斐波那契 (Fibonacci) 数列第 n 项的算法
- 求斐波那契 (Fibonacci) 数列第 n 项的算法
- 求斐波那契数列的第n项
- C++求斐波那契数列的第n项
- 第十二周-求斐波那契数列的第N项
- 求斐波那契数列第n项的优化算法
- 求斐波那契数列的第n项
- 求斐波那契数列的第n项
- 求斐波那契数列的第N个数的值
- 求斐波那契数列的第n个数
- 求斐波那契数列的第n个数
- 1242 . 斐波那契数列的第N项
- 斐波那契数列的第N项
- 1242 斐波那契数列的第N项
- 求裴波那契数列的第n项---递归+改进
- Angular4.0通用指令
- selenium入门学习理论必知
- 数据库范式
- 2017多校训练Contest3: 1011 RXD's date hdu6066
- 分隔符
- 求斐波那契数列的第n项
- log4j2 jdbc的使用
- SpringMVC接收复杂集合参数
- P3390 【模板】矩阵快速幂
- Ubuntu安装Docker 入门
- 【Java源码分析】ConcurrentModificationException并发修改异常分析与解决方案(快速失败与安全失败)
- 教你阅读Python开源项目代码
- 卡尔曼滤波原理二:扩展卡尔曼
- HTML+Bootstrap实现类似CSDN博客页面