斐波拉契数列2的63次方项
首先,斐波拉契数列最简单的做法就是利用递推公式。这里就不写代码了。
但是递推计算还是太慢了,项数一大就算不了,比如10的5次方以上就算不出来了。
然后我们就想出了矩阵加速。
利用递推关系可以构造成矩阵相乘。
| a(n+1) an | = | an a(n-1) | * | 1 1 |
| 0 0 | | 0 0 | | 1 0 |
则可以得到
| a(n+1) an | = | a2 a1 | * ( | 1 1 | )^(n-1)
| 0 0 | | 0 0 | | 1 0 |
再利用快速幂的思想,就有了下面的代码。
#include<stdio.h>
#include<math.h>
#include<stdlib.h>
struct jz
{
unsigned long long at[2][2];
};
jz ttt,jsjz;
jz mutil(jz p,jz q) //自定义的矩阵乘法
{
jz pq;
pq.at[0][0]=(p.at[0][0]*q.at[0][0]+p.at[0][1]*q.at[1][0])%1000000007;
pq.at[0][1]=(p.at[0][0]*q.at[0][1]+p.at[0][1]*q.at[1][1])%1000000007;
pq.at[1][0]=(p.at[1][0]*q.at[0][0]+p.at[1][1]*q.at[1][0])%1000000007;
pq.at[1][1]=(p.at[1][0]*q.at[0][1]+p.at[1][1]*q.at[1][1])%1000000007;
return pq;
}
jz jiasu(unsigned long long a) //加速幂的思想
{
if(a==1) return jsjz;
else if(a%2==0) return mutil(jiasu(a/2),jiasu(a/2));
else return mutil(mutil(jiasu(a/2),jiasu(a/2)),jsjz);
}
int main()
{
jsjz.at[0][0]=2;ttt.at[0][0]=1;
jsjz.at[0][1]=1;ttt.at[0][1]=1;
jsjz.at[1][0]=1;ttt.at[1][0]=1;
jsjz.at[1][1]=1;ttt.at[1][1]=0;
unsigned long long n;
scanf("%llu",&n);
jz p;
if(n==1) p=ttt;
else if(n%2==0) p=jiasu(n/2);
else p=mutil(ttt,jiasu(n/2));
printf("%llu",p.at[0][1]);
return 0;
}
但是这样还是达不到2的63次方的要求。
我们再想一想还有什么可以优化的地方。
很明显我们在加速幂的运算中还是重复了很多运算。我们现在就需要想有没有什么办法去除这些重复的运算。
这里就用到了另一种加速幂的思路。
#include<stdio.h>
#include<math.h>
#include<stdlib.h>
struct jz
{
unsigned long long at[2][2];
};
jz ttt,jsjz;
jz mutil(jz p,jz q)
{
jz pq;
pq.at[0][0]=(p.at[0][0]*q.at[0][0]+p.at[0][1]*q.at[1][0])%1000000007;
pq.at[0][1]=(p.at[0][0]*q.at[0][1]+p.at[0][1]*q.at[1][1])%1000000007;
pq.at[1][0]=(p.at[1][0]*q.at[0][0]+p.at[1][1]*q.at[1][0])%1000000007;
pq.at[1][1]=(p.at[1][0]*q.at[0][1]+p.at[1][1]*q.at[1][1])%1000000007;
return pq;
}
/*jz jiasu(unsigned long long a)
{
if(a==1) return jsjz;
else if(a%2==0) return mutil(jiasu(a/2),jiasu(a/2));
else return mutil(mutil(jiasu(a/2),jiasu(a/2)),jsjz);
}*/
int main()
{
jsjz.at[0][0]=1;ttt.at[0][0]=1;
jsjz.at[0][1]=1;ttt.at[0][1]=0;
jsjz.at[1][0]=1;ttt.at[1][0]=0;
jsjz.at[1][1]=0;ttt.at[1][1]=1;
unsigned long long n;
scanf("%llu",&n);
for(int i=0;i<=63;i++) //加速幂思想
{
if(i>=1) jsjz=mutil(jsjz,jsjz);
if((n>>i)&1==1) ttt=mutil(ttt,jsjz);
}
printf("%llu",ttt.at[0][1]);
return 0;
}
一个数总可以表示成多个2的次方项相加,也就是2进制表示。于是就把次方项给拆开,只求对应的需要的项。
其中可以利用加速矩阵的自乘,免去所有的重复操作。这也就是比第一次的加速幂思想更简化的地方。
于是现在复杂度就是肉眼可见的低了。
只需要进行一百多次的矩阵乘法。
现在就可以求出斐波拉契数列的2的63次方项了。