求斐波那契数,你还在用递归吗?
1、什么是斐波那契数?
- 斐波那契数,又称黄金分割数列、因数学家莱昂纳多·斐波那契(Leonardoda Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列:0、1、1、2、3、5、8、13、21、34、……
- 斐波那契数,通常用 F(n) 表示,形成的序列称为斐波那契数列。该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。也就是:
F(0) = 0,
F(1) = 1
F(N) = F(N - 1) + F(N - 2), 其中 N > 1.
给定 N,计算 F(N)。
2、经典解法-递归
根据题意,我们可以用递归求解。
public int fib(int N) {
if (N < 2) {
return N;
}
return fib(N-1) + fib(N-2);
}
但是这个是最好的解法吗?
- 假如我们要计算的N为10,我们要先计算fib(9),再计算fib(8);
- 计算fib(9), 要计算fib(8)和fib(7),如此递归下去
- 计算fib(8), 要计算fib(7)和fib(6),如此递归下去
发现什么问题没?
- 越往小了算,重复计算越多, 时间效率n的n次方,不可想象
- fib(30)耗时 4毫秒
- fib(50)耗时 62秒
- fib(100)耗时(1h没结果,停了)
那有没有其它的好办法呢?
- 你发现没有我们用递归是从大到小计算,从fib(N)到fib(N-1)...fib(0)
- 但是我们fib(N)不知道结果,我们只知道fib(0)和fib(1)
- 我们可不可以试着从小到大计算
3、动态规划
我们尝试从小往大推演,把计算结果保存下来,避免重复计算,这种方法一般称之为动态规划。
public int fib(int N) {
if (N < 2) {
return N;
}
int[] cache = new int[N+1];
cache[0] = 0;
cache[1] = 1;
for (int i = 2; i <= N; i++) {
cache[i] = cache[i-1] + cache[i-2];
}
return cache[N];
}
- 上面的代码: cache[i] = cache[i-1] + cache[i-2]称之为动态规划公式
- 时间效率n, 遍历一次即可
- 空间效率n,缓存n个数据
- 空间效率还可以改进
4、动态规划改进
- 我们用m代表fib(n-2), n代表fib(n-1), o代表fib(n)
- 每次计算结束再重置 m 和 n
public int fib(int N) {
if (N < 2) {
return N;
}
int m = 0, n = 1;
for (int i = 2; i <= N; i++) {
int o = n + m;
m = n;
n = o;
}
return n;
}
- 时间效率n, 遍历一次即可
- 空间效率3
- 还有一种数学解法,有兴趣的可以自己去了解
关注公众号:丰极,了解更多算法知识。