前言
最近又看到一篇文章,说到算法对前端的重要性,遂抽出时间(玩的时间),逼着自己看一些相关的知识,当看到动态规划相关文献时,也勾起了很大的兴趣,学习各位大神的博客,有了初步理解,借此记载,可能比较浅显,对于同样初学相关知识的同学,或许会有帮助,如有问题,望大神指出
正文
学习的最好方法便是带着问题学习,所以首先先采用网上普遍的、对理解动态规划很友好的一个问题作为学习切入点,问题解决后再总结归纳
问题:
现在有10级台阶,每次只能上1级或2级台阶,问:到达10级台阶的方法有多少?
解析:
首先按照正常逻辑,看到这个问题或许会懵,因为情况太多无从下手,但是最优解往往需要更改思考方式
- 这里可以从结果考虑,最后我们踩在第10级台阶上,由于每次只能上1 或 2级台阶,所以上一步,我门应该在第9级台阶,或第8级台阶;
- 基于1的结论,从0 到 9 级台阶有多少种方法,对应的从9 到 10级台阶就有多少种方法(这里需要理解下),同理从0 到 8级台阶的方法总数等于从8 到 10级台阶的方法总数,可得从0 到 10级台阶的方法总数(以下简称methods(10))等于 从0 到 9级台阶的方法总数 加 从0 到 8级台阶的方法总数,即methods(10) = methods(9) + methods(8);这里的methods(9) 和 methods(8)就是methods(10)的最优子结构(思维导图有点大,只展示一部分)
- 从第2点我门已经发现了问题的解决逻辑和一点思路,按照第2点的思路,我们将以二叉树的形式将问题分裂成“无数”的子问题,直到分裂成 methods(2) 和 methods(1)时,methods(2)(从0 到 2级台阶的方法数)等于2,而methods(1)等于1,这里的methods(2) 和 methods(1)也就是动态规划里边界
- 由3的结论,我们只需要将所有子问题的解相加便是问题结果,这里将思路转为代码,通过递归实现, methods(n) = methods(n - 1) + methods(n - 2),methods(n - 1) = ......,以此类推
function methods(n) {
if (n == 1) return 1
if (n == 2) return 2
console.log('计算')
return methods(n - 1) + methods(n - 2)
}
console.log(methods(10)); // 89
问题到这看似已经解决了,但是我想同学在思维导图中应该也发现了,很多子问题是重复的,但是上面代码却做了很多重复工作,很浪费 冗余(如下图),我们希望已经计算过的就不要在重复计算
所以优化下, 保存计算过的结果:
var resultList = [] // 保存计算过的结果,以计算的台阶数做索引下标
function methods(n) {
if (n == 1) return 1
if (n == 2) return 2
if (!resultList[n]) { // 没有就保存
console.log('计算', n)
resultList[n] = methods(n - 1) + methods(n - 2)
}
return resultList[num]
}
这样看,就节省了很多计算,台阶总数越大,越明显
然后咱们再精简下代码:
var resultList = [0, 1, 2]; // 对于本题的结果预存了,方便点
function fnSumF(n) {
if (!resultList[n]) resultList[n] = fnSumF(n - 1) + fnSumF(n - 2);
return resultList[n]
}
总结
动态规划的英文名是Dynamic Programming,是一种分阶段求解决策问题的数学思想;与分治方法类似,都是通过组合子问题的解来来求解原问题的。再来了解一下什么是分治方法,以及这两者之间的差别,分治方法将问题划分为互不相交的子问题,递归的求解子问题,再将它们的解组合起来,求出原问题的解。而动态规划与之相反,动态规划应用与子问题重叠的情况,即不同的子问题具有公共的子子问题(子问题的求解是递归进行的,将其划分为更小的子子问题)。在这种情况下,分治方法会做许多不必要的工作,他会反复求解那些公共子子问题。而动态规划对于每一个子子问题只求解一次,将其解保存在一个表格里面,从而无需每次求解一个子子问题时都重新计算,避免了不必要的计算工作。
其实关于和分治的区别,在我们解决问题优化时已经可以看出
初学的浅层理解,如有问题希望大家指出,指点,如果问题或者好文章,也希望可以评论推荐
学的越多,才发现自己懂得越少
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。