题目
Fibonacci 数列是非常著名的数列:
F[1] = 1,F[2] = 1,
对于 i > 3,F[i] = F[i − 1] + F[i − 2]
Fibonacci 数列有一个特殊的性质,前一项与后一项的比值,F[i]/F[i + 1], 会趋近于黄金分割。
为了验证这一性质,给定正整数 N,请你计算 F[N]/F[N + 1],并保留 8 位 小数。
输入
一个正整数 N。(1 ≤ N ≤ 2000000000)
输出
F[N]/F[N + 1]。答案保留 8 位小数。
样例输入
2
样例输出
0.50000000
解题思路
矩阵快速幂
快速幂常用于快速计算次数较大的乘方,比如题目 2088: 蓝桥杯算法提高VIP-快速幂。斐波那契数列最常用的方法就是矩阵快速幂,根据递推公式(矩阵具体构造过程请见矩阵构造方法)可以得到下式:
[
F
(
N
+
1
)
F
(
N
)
]
=
[
F
(
2
)
F
(
1
)
]
∗
A
N
+
1
−
2
\begin{aligned} [F(N+1)\quad F(N)]=[F(2)\quad F(1)]*{A}^{N+1-2} \end{aligned}
[F(N+1)F(N)]=[F(2)F(1)]∗AN+1−2由此,递推公式转换成了从已知的F(1)、F(2)直接经过乘方映射到了待求的F(N+1)、F(N)。对于一般的单个整数快速幂的模板如下:
long int(int n, int a){ //a^n
long int temp = 1;
while (n){
if (n&1)//二进制最后一位为1
temp*=a;
a*=a;
n>>=1;//乘方次数除以2
}
return temp;
}
矩阵快速幂即将返回值、变量、乘法均换成与矩阵匹配的即可。
本题思路
回到本题,开始我用的是上述方法解题,但是发现N很大时,中间变量太大了,会超出long int范围而出错。之后打算先利用矩阵快速幂+矩阵乘法打表,N∈[1,50]的结果如下:
N=1 : 1.00000000
N=2 : 0.50000000
N=3 : 0.66666667
N=4 : 0.60000000
N=5 : 0.62500000
N=6 : 0.61538462
N=7 : 0.61904762
N=8 : 0.61764706
N=9 : 0.61818182
N=10 : 0.61797753
N=11 : 0.61805556
N=12 : 0.61802575
N=13 : 0.61803714
N=14 : 0.61803279
N=15 : 0.61803445
N=16 : 0.61803381
N=17 : 0.61803406
N=18 : 0.61803396
N=19 : 0.61803400
N=20 : 0.61803399
N=21 : 0.61803399
N=22 : 0.61803399
N=23 : 0.61803399
N=24 : 0.61803399
N=25 : 0.61803399
N=26 : 0.61803399
N=27 : 0.61803399
N=28 : 0.61803399
N=29 : 0.61803399
N=30 : 0.61803399
N=31 : 0.61803399
N=32 : 0.61803399
N=33 : 0.61803399
N=34 : 0.61803399
N=35 : 0.61803399
N=36 : 0.61803399
N=37 : 0.61803399
N=38 : 0.61803399
N=39 : 0.61803399
N=40 : 0.61803399
N=41 : 0.61803399
N=42 : 0.61803399
N=43 : 0.61803399
N=44 : 0.61803399
N=45 : 0.61803399
N=46 : -1.38720213
N=47 : -2.58263042
N=48 : -0.63185946
N=49 : 2.71635390
N=50 : 0.26908094
发现当N在46时,就已经发生了中间过程的数值越界,但是这不影响我们观察到,N>=20时,输出的值应该都是0.61803399,因此根据输入的N的值分为两类:一是N∈[1,20)时,直接运算;二是N>=20时,直接输出0.61803399
。
当然,本题数据量较小,更好的方法是:在N∈[1,20)时直接利用函数递推式进行计算;N>=20时直接输出0.61803399
。
代码
#include<bits/stdc++.h>
using namespace std;
struct Node{
int a[2][2];
};
struct Node fac;
Node multiple(Node m1, Node m2){//结果存放在b中
int i,j,k;
struct Node b;
for (i=0;i<2;i++)
{
for (j=0;j<2;j++)
{
b.a[i][j] = 0;//初始化
for (k=0;k<2;k++)
b.a[i][j]+= m1.a[i][k]*m2.a[k][j];
}
}
return b;
}
Node power(int k){
int i,j;
struct Node A,temp;
for (i=0;i<2;i++)//初始化
{
for (j=0;j<2;j++)
{
A.a[i][j] = (i==j)?1:0;//单位矩阵
temp.a[i][j] = fac.a[i][j];
}
}
while (k!=0){
if (k&1)//最低位为1
A = multiple(A,temp);
temp = multiple(temp,temp);
k>>=1;//除以二
}
return A;
}
int main()
{
int i,j,N;//(1 ≤ N ≤ 2000000000)
int result[2],FF[i] = {1,1};//FF是已知的两个值
scanf("%d",&N);
struct Node f;
fac.a[0][0] = 1,fac.a[0][1] = 1,fac.a[1][0] = 1,fac.a[1][1] = 0;//初始化
if (N<20)
{
f = power(N+1-2);//第二个乘数矩阵
for (i=0;i<2;i++)
{
result[i] = 0;
for (j=0;j<2;j++)
result[i] += FF[j]*f.a[j][i];
}
double r = result[1]*1.0/result[0];
printf("%.8lf\n",r);
}
else
printf("0.61803399");
return 0;
}