• 斐波那契数列解法


    什么是斐波那契数列?
    斐波那契数列是这样一个数列,它满足:
    f(0) = 0;
    f(1) = 1;
    f(n) = f(n-1) + f(n-2) (当n>=2时)

    到底有几种方法,这些思路里蕴含的优化思路究竟是怎么样的,今天和大家聊一聊。

    一、递归法
    伪代码:
    uint32_t f(uint32_t n){
    if(n0) return 0;
    if(n
    1) return 1;
    return f(n-1)+f(n-2);
    }

    思路:这是一个递归的代码,非常清晰,直接把斐波那契数列的定义翻译成了代码。

    例如:
    假设要求f(5)
    f(5) = f(4) + f(3);
    于是会递归计算f(4)和f(3);

    接着要求f(4)
    f(4) = f(3)+ f(2);
    于是会递归计算f(3)和f(2);

    可以看到,计算f(5)和f(4)中都要计算f(3),但这两次f(3)会重复计算,这就是递归的最大问题,对于同一个f(a),不能复用。

    计算一个f(n)到底需要有多少次递归调用呢?
    我们可以在代码里加一个计数验证一下。

    伪代码:
    static uint32_t count=0; // 加一个全局变量计数

    uint32_t f(uint32_t n){
    count++; // 递归一次,计数加一
    if(n0) return 0;
    if(n
    1) return 1;
    return f(n-1)+f(n-2);
    }

    实验的结果:
    f(5) count = 15
    f(10) count = 177
    f(15) count = 1K+
    f(20) count = 2W+
    f(25) count = 24W+
    f(30) count = 269W+
    f(35) count = 2986W+
    f(40) count = 3.3Y+

    画外音:
    (1)这个count,是函数递归了对少次;
    (2)f(45),机器居然算不出来;
    (3)对结论有疑问的,自己可以run一把;

    启示:
    (1)斐波那契数列求解,如果用直接法,时间复杂度是指数级的,不可行;
    (2)如果没有太大的把握,工程中尽量少使用递归,容易把自己玩死;

    二、正推法
    从斐波那契数列的定义:
    f(0) = 0;
    f(1) = 1;
    f(n) = f(n-1) + f(n-2) n>=2时

    可以看出,每一个新的f(n),是前两个旧的f(n-1)和f(n-2)之和,一路递归下去,最终都将递归到f(0)和f(1)上来。

    反过来想,我们不倒着f(n),f(n-1),f(n-2)这么计算,而是f(0),f(1),f(2)…f(n)这么正向计算,岂不是更快么?

    伪代码:
    uint32_t f(uint32_t n){
    uint32_t arr[n];
    arr[0]=0;
    arr[1]=1;
    for(uint32_t i=2;i<=n;i++){
    arr[i]=arr[i-1]+arr[i-2];
    }
    return arr[n];
    }

    这么正向的计算,只需要一个for循环,就能够计算出f(n)的值,时间复杂度是O(n)。

    三、通项公式法
    f(0) = 0;
    f(1) = 1;
    f(n) = f(n-1) + f(n-2) (当n>=2时)
    大学学过相关课程,可解出f(n)通项公式。
    画外音:额,是不是有朋友读了个假大学。

    线性递推数列:
    f(n) = f(n-1) + f(n-2)
    对应的特征方程是:
    x^2 = x + 1

    求解特征方程得到:
    x1=(1+√5)/2
    x2=(1-√5)/2

    于是得到:
    f(n) = a1(x1)^n + a2(x2)^n

    将:
    f(0) = 0;
    f(1) = 1;
    代入上述通项公式。

    于是得到:
    a1=1/√5
    a2=-1/√5

    于是最终得到:
    f(n)=(1/√5)*{[(1+√5)/2]^n -[(1-√5)/2]^n}
    画外音:别问我为什么懂这些,我TM作为计算机信息安全专业,也被数论,有限域,加密解密这些数学学科折磨过。

    可忽略上述吹牛*的过程,百度一下能得到答案。

    总之,得到了斐波那契数列通项公式:
    f(n) = a1(b1)^n + a2(b2)^n
    其中a1, b1, a2, b2四个数字都是常数。

    想求f(45),把n=45带入上述通项公式即可。

    那么,带入通项公式求解,时间复杂度是多少呢?是O(1)么?
    通项公式的计算,并不能O(1)得到,而是一个a^n,即power(a, n)的求解过程。

    那么,如何求解a的n次方呢?
    最粗暴的方法,将a不断的自乘n次。

    伪代码:
    uint64_t power(uint64_t a, uint64_t n){
    uint64_t result=a;
    for(uint64_t i=1;i<n;i++){
    result *=a;
    }
    return result;
    }

    很容易知道,a通过for循环不断自乘,求解a^n的时间复杂度是O(n)。

    通过“正推”法,求解f(n)的时间复杂度是O(n)。
    转载拜托,面试别再问我斐波那契数列了!!!

  • 相关阅读:
    Statspack之十四"log file sync" 等待事件
    log file sync(日志文件同步) 与 Log file parallel write 等待事件(1)
    OWI诊断的步骤(基础)
    linux下防火墙配置
    linux中oracle开机启动(2)
    Linux开机自动启动ORACLE设置
    linux find mtime参数详解
    linux 下 rman自动备份
    JSTL 标签库 下载及配置
    JSTL I18N 格式标签库 使用之一_____数字日期格式化
  • 原文地址:https://www.cnblogs.com/liugangjiayou/p/12514672.html
Copyright © 2020-2023  润新知