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

题目意思很简单,给定一个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;
}

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值