一、问题的提出
利用矩阵运算(《北师大版高等代数(第五版)》p205-207)可以求得斐波那契数列通项公式:
有望在复杂度O(n)下求解斐波那契数列。然而c++中根式的精度不够,从第十项开始就会间隔地出现1左右的误差:
项数 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
正确值 | 1 | 1 | 2 | 3 | 5 | 8 | 13 | 21 | 34 | 55 | 89 | 144 | 233 | 377 | 610 | 987 | 1597 | 2584 | 4181 |
计算值 | 1 | 1 | 2 | 3 | 5 | 8 | 13 | 21 | 34 | 54 | 89 | 143 | 232 | 377 | 610 | 986 | 1596 | 2584 | 4181 |
因此,必须尝试其他方法解决这一问题。
二、解决方案
考虑到 算得一定是整数,可见
最终一定会被约成 1 ,由此模拟人脑计算时的约分过程重定义除法,同时重定义加、减、乘,即可解决问题。
定义数 , 构成数域
,其中加减乘分别定义为:
特别地,只定义可整除的情况:
通项公式变形为:
源代码:
#include<bits/stdc++.h>
using namespace std;
struct Sqrt5{
//定义数(a,b)
long long a,b;
};
Sqrt5 mul(Sqrt5 a,Sqrt5 b){
//定义乘
Sqrt5 tmp;
tmp.a=a.a*b.a+5*a.b*b.b;
tmp.b=a.a*b.b+a.b*b.a;
return tmp;
}
Sqrt5 pow(Sqrt5 a,int n){
//定义乘方
Sqrt5 ans=a;
for(int i=2;i<=n;i++){
ans=mul(ans,a);
}
return ans;
}
Sqrt5 sub(Sqrt5 a,Sqrt5 b){
//定义减
Sqrt5 tmp;
tmp.a=a.a-b.a;
tmp.b=a.b-b.b;
return tmp;
}
long long div(Sqrt5 a,Sqrt5 b){
//定义除
if(a.a!=0) return a.a/b.a;
return a.b/b.b;
}
int main(){
int n;
Sqrt5 a,b,c,d,e;
a.a=1;a.b=0;b.a=1;b.b=1;c.a=1;c.b=-1;d.a=0;d.b=1;e.a=2;e.b=0;
for(int i=1;i<=38;i++){
long long ans=div(mul(a,sub(pow(b,i),pow(c,i))),mul(d,pow(e,i)));
cout<<ans<<" ";
}
return 0;
}
三、优化方向
虽然复杂度仅为 O(n) ,但由于long long int的范围限制,只能计算到第三十七项。需要计算更多值需要结合高精度算法重定义运算。