Lecture 12: More about debugging, knapsack problem, introduction to dynamic programming
本课主要分为三个部分,第一部分介绍了更多在调试时候的注意点;第二部分例举了多个经典优化问题;而第三部分则简要介绍了动态规划。
第一部分提出了许多在调试时应该注意的地方,下面大概有三个重要点,将逐个讲明。
第一个要点是在调试的时候,各位可以注意程序是否出现了以下错误(有些可能非常简单,但仍需要小心谨慎):
第二个要点,是在平时写程序并调试的时候,要注意养成时时记录自己错误的习惯,总结成一个个人犯错模型(Personal model of mistakes)。那么在调试的时候,就可以首先查找是否又出现了以前犯过的错误。
第三个要点是在调试的时候,要记录下已经尝试过的修改。不是所有修改都会一次成功,而且有时候调试和修改会花很长时候。因此,这些记录就可以避免重复进行同样的无效修改。另外,由于修改可能会导致程序变得更加糟糕,确保程序可以回转则非常重要。因此,要保存原本的未修改程序和每个修改过的版本(可命名为V1.0, V1.1, V2.1等等)
第二部分则提出了新的问题类型,即最优化问题(Optimization problem)。这些问题都有以下两个显著的特点:
最优化问题包括以下五个非常经典的子类型:
大多数新的优化问题可以简化为以上五个子类型,而运用这些已知问题的解法可以解决新的问题。
本课重点讲述的是第五种题型,即背包问题。背包问题又分为两种类型,一是连续背包问题,二是0/1背包问题。
背包问题类型1:连续背包问题
例:假设一个贼闯入一户人家,他有一个背包可以装10kg的东西。屋里有以下值钱的东西:
那么我们就来系统化一下这类问题:
① 创建一个函数,要求这个函数的最大值或最小值。在这个例子中,就是求背包里面装的东西的最大价值。我们就可以由函数:f = 金沙的价值 x 金沙的重量 + 银沙的价值 x 银沙的重量 + 葡萄干的价值 + 葡萄干的重量。求这个函数f的最大值。
② 考虑约束条件。在这个例子中,金沙,银沙和葡萄干的重量总和 <= 10kg
这个问题的解法非常简单:我们先取4kg金沙,3kg银沙,再取3kg葡萄干。这就是一个简单的贪心算法(Greedy algorithm),即每一步都是最优解的算法。
然而局部最优策略并不代表全局最优策略,我们可以另一种背包问题,即0/1背包问题。
背包问题类型2:0/1背包问题
(基本上这是不连续背包问题)
例:假设这个小偷的背包仍然只能装10kg的东西,但是他闯入的屋主家只有如下物品:
我们也来系统化这个问题:
① 我们有n件物品,但每一次只能选择拿整件物品或者不拿这件物品;
② 每个物品都有重量和价值,我们要找到最优解,即最大值。
那么,我们会有如下两种方法去解决这个问题:
贪心算法前面已经提到过了,在每一步都取最优解,即取价值最高的物品。所以就是拿一个手表,再拿一幅油画,所以总价值为10 + 9 = 19。这样的算法即快速又简单,但并不是最优解。
最优解是什么?其实就是一个手表,一个收音机加一个花瓶,总价值为10 + 5 + 7 = 22。这个用穷举法也可以做,但是非常慢,在这里一共有2^4 = 16种可能性。但当输入值n很大的时候,就有2^n种可能性,当n足够大的时候,比如n=50,穷举法就需要花很长很长的时候去算出最优解。
那么,这时我们为了提高算法效率,就可以运用动态规划。
什么是动态规划?这就是第三部分讲的内容。由于动态规划是下一节课的内容重点,我就会在下一个笔记中详细讲述,并且写出0/1背包问题的两种解法。
总结:介绍最优化问题系列,重点讲述背包问题,非常重要,初学者必看!