动态计划入门(以爬楼梯为例)

观点

动态计划(dynamic programming)是运筹学的一个分支,是求处理议计划历程(decision process)最优化的数学要领。
动态计划算法一般基于一个递推公式及一个或多个初始状况。 当前子问题的解将由上一次子问题的解推出

基本思想

要处理一个给定的问题,我们须要处理其差别部份(即处理子问题),再兼并子问题的解以得出原问题的解。
一般许多子问题异常类似,为此动态计划法试图只处理每个子问题一次,从而削减盘算量。
一旦某个给定子问题的解已算出,则将其影象化存储,以便下次须要统一个子问题解之时直接查表。
这类做法在反复子问题的数量关于输入的范围呈指数增进时迥殊有效。
动态计划有三个中心元素:
1.最优子构造
2.边境
3.状况转移方程

我们来看一到问题

问题

有一座高度是10级台阶的楼梯,从下往上走,每跨一步只能向上1级或许2级台阶。求出一共有若干种走法。

比方,每次走1级台阶,一共走10步,这是个中一种走法。
再比方,每次走2级台阶,一共走5步,这是另一种走法。

然则如许一个个算太麻烦了,我们能够只去思索末了一步怎么走,如下图
《动态计划入门(以爬楼梯为例)》

如许走到第十个楼梯的走法 = 走到第八个楼梯 + 走到第九个楼梯
我们用f(n)来示意 走到第n个楼梯的走法,所以就有了f(10) = f(9) + f(8)
然后f(9) = f(8) + f(7), f(8) = f(7) + f(6)……

如许我们就得出来一个递归式
f(n) = f(n-1) + f(n-2);
另有两个初始状况
f(1) = 1;
f(2) = 2;

如许就得出了第一种解法

要领一:递归求解

function getWays(n) {

    if (n < 1) return 0;
    if (n == 1) return 1;
    if (n == 2) return 2;

    return getWays(n-1) + getWays(n-2); 
}

这类要领的时候庞杂度为O(2^n)
《动态计划入门(以爬楼梯为例)》

能够看到这是一颗二叉树,数的节点个数就是我们递归方程须要盘算的次数,
数的高度为N,节点个数近似于2^n
所以时候庞杂度近似于O(2^n)

然则这类要领能不能优化呢?
我们会发明有些值被反复盘算,如下图
《动态计划入门(以爬楼梯为例)》
雷同色彩代表着反复的部份,那末我们可不能够把这些反复盘算的值纪录下来呢?
如许的优化就有了第二种要领

要领二:备忘录算法

const map = new Map(); 
function getWays(n) {

    if (n < 1) return 0;
    if (n == 1) return 1;
    if (n == 2) return 2;

    if (map.has(n)) {
        return map.get(n);
    }
    const value = getWays(n-1) + getWays(n-2);
    map.set(n, value);
    return value; 
}

由于map里终究会寄存n-2个键值对,所以空间庞杂度为O(n),时候庞杂度也为O(n)

继承想想这就是最优的处理方案了吗?

我们回到一开始的思绪,我们是假定前面的楼梯已走完,只斟酌末了一步,所以才得出来f(n) = f(n-1) + f(n-2)的递归式,这是一个置顶向下求解的式子
一般来说,根据一般的思绪应该是一步一步往上走,应该是自底向上去求解才比较相符一般人的头脑,我们来看看行不行的通

《动态计划入门(以爬楼梯为例)》
这是一开始走的一个和两个楼梯的走法数,即之前说的初始状况

《动态计划入门(以爬楼梯为例)》
这是进行了一次迭代得出了3个楼梯的走法,f(3)只依赖于f(1) 和 f(2)

继承看下一步
《动态计划入门(以爬楼梯为例)》
这里又进行了一次迭代得出了4个楼梯的走法,f(4)只依赖于f(2) 和 f(3)

我们发明每次迭代只须要前两次迭代的数据,不必像备忘录一样去保留一切子状况的数据

要领三:动态计划求解

function getWays(n) {

    if (n < 1) return 0;
    if (n == 1) return 1;
    if (n == 2) return 2;

    // a保留倒数第二个子状况数据,b保留倒数第一个子状况数据, temp 保留当前状况的数据
    let a = 1, b = 2;
    let temp = a + b;
    for (let i = 3; i <= n; i++) {
        temp = a + b;
        a = b;
        b = temp; 
    }
    return temp; 
}

这是我们能够再看看当前的时候庞杂度和空间庞杂度
当前时候庞杂度仍为O(n),但空间庞杂度降为O(1)
这就是抱负的效果

总结

这只是动态计划里最简朴的问题之一,由于它只要一个变化维度
当变化维度变成两个、三个以至更多时,会越发庞杂,背包问题就是比较典范的多维度问题,有兴致的能够去网上看看《背包九讲》

    原文作者:幼稚
    原文地址: https://segmentfault.com/a/1190000015944750
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞