题源:PTA
题目描述:7-26 函数-斐波那契数列
分数 20 作者 韩玫瑰 单位 济南大学
斐波那契数列(Fibonacci Sequence),又称黄金分割数列,指的是这样一个数列:1、1、2、3、5、8、13、21、……。在数学上,斐波纳契数列以递推的方法定义为:F(1)=1,F(2)=1,F(n)=F(n-1)+F(n-2)(n≥2,n∈N)。计算斐波那契数列第n项的值。
输入格式:输入一个大于等于1,小于等于60的整数n。
输出格式:输出第n项的数列值,数列值为double类型,不输出小数位数。
输入样例:
20
输出样例:
6765
输入样例:
1
输出样例:
1
代码长度限制 16 KB
时间限制 400 ms
内存限制 64 MB
这道题首先会想到使用递归函数的方法,递归一法确实可行,但递归一法也存在一些缺陷(这点我会在稍后提出),使用递归函数代码如下:
#include<stdio.h>
double fib(double n)
{
double f;
if(n==1||n==2)
{f=1;}
else
{f=fib(n-1)+fib(n-2);}
return f;
}
int main()
{
double n,f;
scanf("%lf",&n);
f=fib(n);
printf("%.lf",f);
return 0;
}
运行代码后,我们发现了一些问题,即在输入数据较小时使用递归函数一法确实可行,但是当数据稍大时(在PTA上输入大于等于41的数据)就会出现运行超时的问题,所以递归函数一法并不是该题的最优解
所以我们引进另外几种方法:
①动态规划法:即将每一个计算出来的数据保存在数组中,需要时直接总数组中取出即可,能避免重复计算。其中会使用到一个for循环,所以时间复杂度为Ο(n),使用到一个长度为n的数组,所以空间复杂度也为Ο(n)。该方法代码如下:
#include<stdio.h>
int main()
{
int n;
scanf("%d",&n);
double fib[61];
fib[0]=1;
fib[1]=0;
int i;
for(i=3;i<=n;i++)
{
fib[i]=fib[i-1]+[i+1];
}
printf("%.lf",fib[n]);
}
运行后我们发现超时问题被有效解决了;但我们发现尽管上述算法已经很高效了,但还是有一个问题,即整个数组中,每次计算时都只需要最新的3个值,前面的值计算完后就不再需要了。所以我们还可以改进,这便是我们的第二个算法。
②迭代法:即通过3个变量来存储数据,时间复杂度仍然为O(n),而空间复杂度为常量级别3,即空间复杂度为0,代码如下:
#include<stdio.h>
int main()
{
double n,first=1,second=1,third;
scanf("%lf",&n);
if(n==1||n==2)
{third=1;}
int i;
for(i=3;i<=n;i++)
{
third=second+first;
first=second;
second=third;
}
printf("%.lf",third);
return 0;
}
由一道题引发的思考:斐波那契数列是递归函数的经典运用之一,但通过这道题我们发现递归算法尽管非常实用,但它存在很大的缺陷,即时间复杂度和空间复杂度都很高,所以在使用递归时,要使用一些手段去优化它,就例如上述的动态规划法和迭代法等,另外算法也不是说越简短越好,我们更应该追求的应该是效率,一个好的算法应该要兼顾效率和简洁,需要不断地去思考和打磨