您的位置:首页 > 其它

HDU 1568 Fibonacci

2014-02-21 22:02 477 查看
转载自 :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
的前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
);
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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: