HDU 1568 Fibonacci

转载自 :http://blog.sina.com.cn/s/blog_9bf748f301019q3t.html

斐波拉契数列:(f[0]=0,f[1]=1;f[i] = f[i-1]+f[i-2](i>=2))

输入若干数字n(0 <= n <= 100000000),每个数字一行。读到文件尾。
输出f[n]的前4个数字(若不足4个数字,就全部输出)。
 分析一:暴力运算
1.      斐波拉契数列在第63位之前还可以用__int64来跑。
2.      此处显然超内存,所以采用大数,即数列存储的方式来完成加法。
3.      如果要存储每一个大数数列,则数列溢出。数列最大容量大概是2亿多:total image size 1028411392 exceeds max (268435456),当n=100000000时,其斐波拉契数列长度*n肯定超过了数列容量。
4.      采用边丢边存的方法:即只采用2个数组f[2][LEN],才用轮换的方式存储最新的运算值,丢弃更旧的值。
5.      采用边丢边存后,程序能够运行。但因为循环太多,导致明显超时了:n太大,LEN不可能很小;运算时要不断地把字符串数组提取到一维数组中,采用循环进行运算,运算后又要重新写回字符串数组。
采用上述方法的程序,功能满足,缺陷是超时:
分析二:求大数前几位的方法
当一个数非常大时,如何求出其前几位呢?
如果是给定一个特定的数,当然可以逐步取出每一位即可。如
a得个位,a/10得百位,a/10/10得千位。
但是,当求x^y的前几位时怎么办呢?若x,y都非常大,则显然很难解决:也许可以用大数乘法,暴力求解,结果自然是既占内存,又耗时间。
还有,此题斐波拉契数列的前几位,显然求出每个斐波拉契数是不现实的。因此,可以采用取对数的方法来解决。
先看对数的性质,loga(b^c)=c*loga(b),loga(b*c)=loga(b)+loga(c);假设给出一个数10234432,那么log10(10234432)=log10(1.0234432*10^7)=log10(1.0234432)+7
log10(1.0234432)就是log10(10234432)的小数部分.
log10(1.0234432)=0.010063744
10^0.010063744=1.023443198,
要求该数的前4位,则将1.023443198*1000即可。
因此,pow(10.0,x的小数部分)即可方便求出x的前几位。
求一数的前4位的对数方法可以表述为:
double x,temp;
while(scanf("%lf",&x)!=EOF)
{
temp=log(x)/log(10.0);
temp=temp-floor(temp); //floor(temp)函数求出小于temp的最大整数
temp=pow(10.0,temp);
while(temp<1000)
temp*=10;
//printf("%.0lf\n",temp); //采用浮点表达法时会四舍五入
printf("%d\n",(int)temp);//此处不需四舍五入,直接舍弃后面的位
}
}
分析三:采用斐波拉契数列通项公式
由上节可知,可以采用对数法解决该斐波拉契问题。以下为斐波拉契数列的通项公式。
F(n)=(1/√5)*[((1+√5)/2)^n-((1-√5)/2)^n](n=1,2,3.....)
改变通项的形式
F(n)=(1/√5)*[((1+√5)/2)^n-((1-√5)/2)^n](n=1,2,3.....)
=(1/√5)*[((1+√5)/2)^n*(1-((1-√5)/(1+√5))^n)](n=1,23.....)
即F(n)的各项可以由以上通项公式求得,而不是采用迭代。
对变化后的通项取对数,则得下式:
log10(F(n))=-0.5*log10(5.0)+((double)n)*log(f)/log(10.0)+log10(1-((1-√5)/(1+√5))^n)
其中第三部分非常小,当n很大时趋近于0,可以忽略掉。
因此,采用下式即可:
temp=-0.5*log(5.0)/log(10.0)+((double)n)*log(s)/log(10.0);

代码:

#include<stdio.h>
#include<math.h>
int f[21]={0,1}; //前20位都在四位数以内,直接输出即可
int main(void)
{
	int i,n;
	double temp;
	double s=(sqrt(5.0)+1.0)/2.0;
	for(i=2;i<21;i++)
		f[i]=f[i-1]+f[i-2];   //前20位直接计算输出
	while(scanf("%d",&n)!=EOF)
	{
		if(n<21)
		{
			printf("%d\n",f[n]);
			continue;
		}
		else
		{
			temp=-0.5*log(5.0)/log(10.0)+((double)n)*log(s)/log(10.0);
			temp-=floor(temp);
			temp=pow(10.0,temp);
			while(temp<1000)
				temp*=10;
			printf("%d\n",(int)temp);
		}
	}
	return 0;
}


posted @ 2014-02-21 22:02  kewowlo  阅读(126)  评论(0编辑  收藏  举报