Fibonacci Numbers (各种不同解法)(谢庆皇)

来源:互联网 发布:正浩网络ceo万振涛 编辑:程序博客网 时间:2024/04/27 13:55

题目意思很简单,给定一个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)/2int 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;  }//****************************************8Mat 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;
}

原创粉丝点击