斐波那契数列的两种求法及优化

(前置技能:高中数学知识,了解C/C++的函数)

求斐波那契数列的原理是十分十分之简单的…
这里认为“求斐波那契数列”这个编程题可以获得一些较为有用的算法思想,在这里分享一下自己学习后的理解.(语言为C++)
在这里插入代码片(新人发帖记录学习,只为更好地理解知识点.如有不对之处非常愿意受教ouo)

定义:对于数列F(n),
F(0)=0,F(1)=1, F(n)=F(n - 1)+F(n - 2)(n ≥ 2,n ∈ N*),
则称这个数列是一个斐波那契数列.
换言之,某一项的值应该等于前两项的和.特别地,F(1) = F(2) = 1.
除此之外.我们也可以规定F(0) = 0.

问题:输入一个n,输出斐波那契数列的F(n).

直接实现

对于这个具有递推公式的简单数列,可以直接写循环结构实现:

#include <iostream>
using namespace std;
int main()
{
	int n = 0;
	cin >> n;
	long long *F = new int[n];//新建一个长度为n的数组F[n].
	F[0] = 0 , F[1] = 1;
	for(int i = 2; i < n; ++i)
	{
		F[i] = F[i - 1] + F[i - 2];
	}
	cout << F[n] << endl;
	return 0;
}

可以清楚地看到该程序的复杂度为O(n).

递归实现

如果用递归方式来实现,会怎么样呢?

#include <iostream>
using namespace std;
long long Fib(int n)
{
	if(n == 1) return 1;
	if(n == 0) return 0;
	return Fib(n - 1) + Fib(n - 2);
}
int main()
{
	int n = 0;
	cin >> n;
	cout << Fib(n) << endl;
	return 0;
}

递归的问题

当输入为0~33的数时,递归方法和直接计算方法的速度差别很难发现. 但数字增加到34及以上时,很容易发现递归法存在卡顿,甚至在40以上已经远大于1秒.

为什么会出现这样的问题呢?

我们分析递归做法的二叉树结构. (通俗点讲,也就是树状图啦)

Fn
Fn-1
Fn-2
Fn-3
Fn-4
Fn-2
Fn-3
Fn-3
Fn-4
Fn-5
Fn-4
Fn-4
Fn-5
Fn-5
Fn-6

……
可以看出,调用F(n-1)的时候,函数也调用了F(n-2),而调用F(n-2)和F(n-1)的时候都需要调用F(n-3)… …继续下去,导致了数量巨大的函数重复调用.我们不难发现:
Fn会被调用1次、Fn-1会被调用1次、Fn-2会被调用2次、Fn-3会被调用3次、Fn-4会被调用5次… …

F(k) 会被调用F(n - k + 1)次!
因为函数调用次数过多,导致整个程序运算步骤过多.因此递归实现在数据量稍大时运算时间会成指数型增长.

这是一个非多项式复杂度的程序.

递归的优化

那么,有没有办法避免重复计算已经计算的F(k)呢?
我们先在程序中将所有F(n)初始化为-1,表示未计算.
再次遇到调用F(k)时,如果其值不是-1, 表示已经计算,不再向下调用,直接返回F(k).
这样就有效避免了重复调用的问题.
代码如下:

#include <iostream>
using namespace std;
long long f[100001] = {0};
long long fib(int n)
{
    if(n == 1) f[n] == 1;
    else if(n == 0) f[n] == 0;
    else
    {
        if(f[n] != -1) return f[n];
        else{
        f[n] = fib(n - 1) + fib(n - 2);
        return f[n];
        }
    }
}
int main()
{
    for(int i = 0; i < 100001; ++i)
        f[i] = -1;
    int n = 0;
    cin >> n;
    cout << fib(n) << endl;
    return 0;
}

这样就有效避免了重复调用, 效率提升极大.
在优化的工作中,我们学会了运用标记数组的方式做判断.同时也发现,很多时候递归其实不是优秀的算法. 除了容易重复计算以外,还有另一个原因:递归至终点需要回溯. 这又增加了运算的步骤. 另外,对简单的斐波那契数列使用函数结构本身就是一种对时间、空间的浪费.

总结:ACM的日子,得精打细算啊~OUO

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值