求斐波那契数,你还在用递归吗?

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
  • 还有一种数学解法,有兴趣的可以自己去了解

关注公众号:丰极,了解更多算法知识。

posted @ 2020-11-12 14:52  丰极  阅读(302)  评论(0编辑  收藏  举报