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


    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
    • 还有一种数学解法,有兴趣的可以自己去了解
  • 相关阅读:
    Python-深浅拷贝
    Python-生成式
    Python-手写web应用
    Python-为什么产生了GIL锁?
    Python-文件处理
    Python-线程
    10大网站设计错误 足以毁掉你的网站【转】
    [转]ASP.NET验证发生前无法调用 Page.IsValid。应在 CausesValidation=True 且已启动回发的控件
    jquery操作字符串常用方法总结及工作代码
    C#中的序列化和反序列化是什么、有什么作用、使用方法详解
  • 原文地址:https://www.cnblogs.com/ma13461749958/p/14248416.html
Copyright © 2020-2023  润新知