从0-1背包问题到动态规划
2016-02-17 17:35
369 查看
算法——贪心算法解0-1背包问题
局部最优策略(locally optimal decisions,比如贪心算法),并不保证全局最优(global optimums)
“在我的后园,可以看见墙外有两株树,一株是枣树,还有一株也是枣树。”,同样如果有两个手表,为了达到0/1背包问题的要求,也可转化为一个手表,还有一个手表。
brute force(暴力法)的本质是 exhaustive enumeration(穷举法)
一个算法的时间复杂度为指数级时,将会产生十分恐怖的计算量,而动态规划算法可以用来解决指数级时间复杂度的问题,只是说,有些指数级时间复杂度的问题可以通过动态规划算法求解,并非全部。
动态规划(Dynamic programming)核心概念有二:
overlapping subproblems: 重叠子问题
或者说,子问题(subproblems)出现了重叠,子问题的“重叠”意味着计算机的“重复计算”。比如,斐波那契数列的例子不像二分搜索(子问题之间比如独立,也即不存在子问题重叠的问题),可参考 每周一刷——从斐波那契数列到动态规划 。
optimal substructure: 最优子结构
所谓动态规划即是寻找这样的一个最优子结构(substructure),它通过引入 memo 查找表(look-up table)的形式实现对重叠的子问题只进行有限次的计算。也即:record value 1st time,look it up the subsequent times we need it(一次记录,多次使用)
背包问题(knapsack problem)又叫装箱问题(bin packing)。所谓0-1背包问题,对于一件物品要么拿走,要么不拿,不存在拿部分的情况,自然可与二进制对应起来,所谓当物品为 n 时,样本空间的大小为:2n。
为了实现用动态规划的方法进行背包问题的求解,我们首先来看使用动态规划求解斐波那契数列的问题,关于用动态规划求解斐波那契数列的详细讨论请见 每周一刷——从斐波那契数列到动态规划 。
values = [9, 7, 8],三个物品各自的价值;
max = 5,背包的最大承重;
所谓0/1背包问题,也即对每一件物品只有选和不选两种选择,也即分二叉,从当前节点出发,有两个分支。
我们定义如下的树的节点(node)结构:
![](https://img-blog.csdn.net/20160217204930370)
上例中的重叠子问题(overlapping subproblem)还不明显,当物品较多时,画出其决定树,便会出现大量的重叠子问题,又因为
局部最优策略(locally optimal decisions,比如贪心算法),并不保证全局最优(global optimums)
“在我的后园,可以看见墙外有两株树,一株是枣树,还有一株也是枣树。”,同样如果有两个手表,为了达到0/1背包问题的要求,也可转化为一个手表,还有一个手表。
brute force(暴力法)的本质是 exhaustive enumeration(穷举法)
一个算法的时间复杂度为指数级时,将会产生十分恐怖的计算量,而动态规划算法可以用来解决指数级时间复杂度的问题,只是说,有些指数级时间复杂度的问题可以通过动态规划算法求解,并非全部。
动态规划(Dynamic programming)核心概念有二:
overlapping subproblems: 重叠子问题
或者说,子问题(subproblems)出现了重叠,子问题的“重叠”意味着计算机的“重复计算”。比如,斐波那契数列的例子不像二分搜索(子问题之间比如独立,也即不存在子问题重叠的问题),可参考 每周一刷——从斐波那契数列到动态规划 。
optimal substructure: 最优子结构
所谓动态规划即是寻找这样的一个最优子结构(substructure),它通过引入 memo 查找表(look-up table)的形式实现对重叠的子问题只进行有限次的计算。也即:record value 1st time,look it up the subsequent times we need it(一次记录,多次使用)
背包问题(knapsack problem)又叫装箱问题(bin packing)。所谓0-1背包问题,对于一件物品要么拿走,要么不拿,不存在拿部分的情况,自然可与二进制对应起来,所谓当物品为 n 时,样本空间的大小为:2n。
为了实现用动态规划的方法进行背包问题的求解,我们首先来看使用动态规划求解斐波那契数列的问题,关于用动态规划求解斐波那契数列的详细讨论请见 每周一刷——从斐波那契数列到动态规划 。
def fib(n, m): if n not in m: m = fib(n-1, m) + fib(n-2, m) return m if __name__ == '__main__': m = {0:0, 1:1} print([fib(n, m) for n in range(10)]) # [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
decision tree(决策树)
weights = [5, 3, 2],三个物品各自的重量;values = [9, 7, 8],三个物品各自的价值;
max = 5,背包的最大承重;
所谓0/1背包问题,也即对每一件物品只有选和不选两种选择,也即分二叉,从当前节点出发,有两个分支。
我们定义如下的树的节点(node)结构:
物品的编号,
背包还能容纳的最大重量,
当前背包所放物品的价值构成的三元组,我们以逆序遍历每件物品,所以根节点结构为:[2, 5, 0]。
def maxVal(i, w, v, c): if i == 0: return v[i] if w[i] <= 0 else 0 without_i = maxVal(i-1, w, v, c) if w[i] > c: return without_i with_i = v[i] + maxVal(i-1, w, v, c-w[i]) return max(without_i, with_i) if __name__ == '__main__': weights = [5, 3, 2] values = [9, 7, 8] n, c = len(weights), 5 print(maxVal(n-1, weights, values, c))
上例中的重叠子问题(overlapping subproblem)还不明显,当物品较多时,画出其决定树,便会出现大量的重叠子问题,又因为
weights和
values是固定不变的,真正变化的是物品编号以及当前背包还能容纳的物品重量。
def fastMaxVal(i, w, v, c, m): try: return m[(i, c)] except KeyError: if i == 0: if w[i] <= c: m[(i, c)] = v[i] return m[(i, c)] m[(i, c)] = 0 return 0 without_i = fastMaxVal(i-1, w, v, c, m) if w[i] > c: m[(i, c)] = without_i return m[(i, c)] with_i = v[i] + fastMaxVal(i-1, w, v, c-w[i], m) m[(i, c)] = max(with_i, without_i) return m[(i, c)] if __name__ == '__main__': ws = [5, 3, 2] vs = [9, 7, 8] n, c = len(ws), 5 m = {} print(fastMaxVal(n-1, ws, vs, c, m))
相关文章推荐
- 国内做“人脸识别”的机构
- java基础-jdbc
- 第五十三天:优化网站的常用方法
- android sqlite更新版本并记录旧数据
- 解决Activity启动黑屏及设置android:windowIsTranslucent不兼容activity切换动画问题
- Jar中Manifest.mf详解
- 深入理解Linux内核 Chapter1 introduction
- TabbarItem设置红点大小、自定义宽高
- iOS架构模式-揭秘MVC,MVP,MVVM和VIPER
- LeetCode - 22. Generate Parentheses
- swoole-1.7.18 版本已发布,支持 PHP7
- 开发自定义控件的步骤
- 深入解析C#设计模式中对桥接模式的具体运用
- Android实现照片墙背景
- MYSQL授权用户远程登陆
- phpstudy www文件夹下面的项目文件夹名字不能为中文
- hosts.deny
- Stimulsoft.Report 2、 web报表的使用
- Javascript 事件冒泡捕获实验
- Android应用主题动态切换