http://acm.hdu.edu.cn/showproblem.php?pid=3117
题目意思很简单,给定一个K(K很大很大),请你输出第K个Fibonacci数的前四位和后四位。
针对后四位,很容易我们就想到了一个O(K)的算法,即利用公式F[i]=(F[i-1]+F[i-2]) mod 10000来递推。可是K很大很大,所以O(K)的算法无法满足需要。这时我们可以构造一个矩阵A,
使得 F[i] F[i+1] 乘以A得到F[i+1] F[i+2],由于矩阵乘法满足交换律结合律,所以原问题就变成了F[0] F[1] * A^(k-1),此时可以用log(K)的算法来求出A^(k-1)。
但是还有更快的方法,经过测试我们发现,Fibonacci的尾数构成了一个15000长度的循环,所以我们求出了F[0]至F[14999]后,对于任意的K,输出F[K mod 15000]即可……
后四位的问题解决了,现在还剩下前四位的问题,前四位就不能用Mod来限制长度了。我们被迫搬出Fibonacci的通项公式,对,fibonacci数是有通项公式的——
f(n)=1/sqrt(5)(((1+sqrt(5))/2)^n+((1-sqrt(5))/2)^n)
假设F[n]可以表示成 t * 10^k(t是一个小数),那么对于F[n]取对数log10,答案就为log10 t + K,此时很明显log10 t<1,于是我们去除整数部分,就得到了log10 t ,
再用pow(10,log10 t)我们就还原回了t。将t×1000就得到了F[n]的前四位。 具体实现的时候Log10 F[n]约等于((1+sqrt(5))/2)^n/sqrt(5),这里我们把((1-sqrt(5))/2)^n这一项忽略了,
因为当N>=40时,这个数已经小的可以忽略。于是log10 F[n]就可以化简成log10 1/sqrt(5) + n*log10 (1+sqrt(5))/2
(1)****************************公式法*******************
#include<stdio.h>
#include<math.h>
#define aa (sqrt(5.0)+1.0)/2
int last[15000]={0,1,1,2,3,5};
int main()
{
int n,f[40]={0,1,1,2,3,5},i,j;
double ans;
/*
测试周期
int T = 0;
for (i=3;i<=120000;i++)
{
last[i] = (last[i-2]+last[i-1])%10000;
if(last[i] == last[2]&&last[i-1] == last[1])
{
T = i;
break;
}
}
printf("%d/n",T-2);
//*/
for (i=3;i<15000;i++)
last[i]=(last[i-2]+last[i-1])%10000; //后四位。
for(i=6;i<40;i++)
f[i] = f[i-1]+f[i-2];
while(scanf("%d",&n)!=EOF)
{
if(n<40)
printf("%d\n",f[n]);
else
{
ans=-0.5*(log10(5.0))+n*log10(aa);
ans-=(int)ans;
ans=pow(10.0,ans);
while(ans<1000)
ans*=10;
printf("%d...",(int)ans);
printf("%4.4d\n",last[n%15000]);
}
}
return 0;
}
(2)********************************************矩阵相乘法**********************************
#include <iostream>
#include <stdio.h>
#include <math.h>
using namespace std;
//const int N = 2;
struct Mat
{
int matrix[N][N];
};
Mat mat, mt;
int n, m = 10000;
Mat mul(Mat a, Mat b) ///***********************
{
int i, j, k;
Mat c;
for (i = 0; i <2 ; i++) //N
for (j = 0; j < 2; j++) //N
{
c.matrix[i][j] = 0;
for (k = 0; k < 2; k++) //N
{
c.matrix[i][j] += a.matrix[i][k] * b.matrix[k][j];
if (c.matrix[i][j] >= m) //m=10000,即取最后四位。
c.matrix[i][j] %= m;
}
}
return c;
}//****************************************8
Mat solve(int m)
{
Mat mt;
if (m==1)
return mat;
if (m%2==1) //m为奇数时。(m&1)
return mul(solve(m-1), mat);
else //m 为偶数。
{
mt = solve(m / 2);
return mul(mt, mt);
}
}
//*********************************************
inline void init()
{
mat.matrix[0][0] = mat.matrix[0][1] = mat.matrix[1][0] = 1;
mat.matrix[1][1] = 0;
}
int main()
{
int f[40] = { 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55 };
int i;
for (i = 11; i < 40; ++i)
{
f[i] = f[i - 1] + f[i - 2];
}
//freopen("t", "r", stdin);
while (scanf("%d", &n) != EOF)
{
if (n < 40) //小于八位的直接输出!
{
printf("%d\n", f[n]);
continue;
}
double temp = (1 + sqrt(5.0)) / 2.0;
double val = n * log10(temp) - 0.5 * log10(5.0);
temp = val - (int) val;
temp = pow(10.0, temp);
//while(temp < 1000)
temp *= 1000;
printf("%d...", (int) temp); //输出前四位!!
Mat ans; //[1 1]
init(); //********** // [1 0]给矩阵赋值。
ans = solve(n - 1); //*********************???????????????????????????
i = ans.matrix[0][0];
if (i < 10)
printf("000%d\n", i);
else if (i < 100)
printf("00%d\n", i);
else if (i < 1000)
printf("0%d\n", i);
else
printf("%d\n", i);
}
return 0;
}
(3)这是一种把数全部输出的一个程序,n最大可为9999
#include<stdio.h>
#include<string.h>
int a[10000][300];
int main()
{
int i,j,n,b,k,s;
a[1][299]=1;
a[2][299]=1; //初始化第一二项,他们的值为1
for(i=3;i<10000;i++)
{
for(j=299;j>0;j--)
{
a[i][j]+=a[i-1][j]+a[i-2][j];//每项的每一位都是由前两项对应位置的数值相加得到的。
while(a[i][j]>9999) //每个最大存9999,超出的前一位加1
{
a[i][j]-=10000;
a[i][j-1]++;
}
}
}
while(scanf("%d",&n)!=EOF)
{
for(i=1;i<300;i++) //找到该项的第一个非0位置
if(a[n][i]) break;
for(j=i;j<300;j++)
{
if(j!=i) //对除第一个位置的其他位置,如果不足四位,说明之前的为0,则用0补足四位。
{
b=a[n][j];
s=0;
while(b)
{
s++;
b/=10;
}
if(s<4) for(k=0;k<4-s;k++) printf("0");
}
printf("%d",a[n][j]);
}
printf("\n");
}
return 0;
}