我正在尝试更全面地了解动态编程中最佳子结构属性的使用情况,但我不明白为什么我们必须证明任何最佳解决问题的方法其中包含子问题的最佳解决方案。
显示某些问题的最佳解决方案是否具有此属性是不够的,然后使用它来证明由于我们的递归算法构建的解决方案至少与此一样好一个最佳解决方案,它本身是最优的吗?换句话说,我没有发现在我们算法的正确性论证中我们需要所有最优解包含子问题的最优解。
澄清:
CLRS对最优子结构的定义表明,“如果问题的任何最优解决方案中包含子问题的最优解,则问题表现出最优子结构”。
为什么说“如果某些问题的最优解决方案包含在其中的子问题的最优解决方案”中,“问题表现出最佳子结构”是不是不够?“
答案 0 :(得分:1)
在我对近似算法的研究中,我对此感到困扰,该算法涉及找到近似最优解的动态程序。我认为,思考动态程序正确性的正确方法是从问题到子问题的减少(在复杂性理论意义上)。这种减少通常是递归地应用和记忆,但现在这些都是细节。
让A成为问题,B成为子问题。只有一个子问题,因为我们可以通过广义的笛卡尔积将多个独立的子问题合并为一个子问题。减少包括两个函数:f,从A实例到B实例,以及h,从B解到A解。我们需要的正确性属性是,对于从每个B实例到相应的最佳B解(oracle)的每个函数g,组合h。 G 。 f是从每个A实例到相应的最优A解的函数。 (如果h需要访问A实例,则扩展B,使其实例包含一个必须逐字复制到相应B解决方案中的A实例。)
为了说明问题,对于特定的A实例和最优的A解决方案,不需要存在oracle g,以便管道h。 G 。 f从给定实例生成该解决方案。 (换句话说,h不需要是满射的。)另一方面,h必须能够处理由f构造的B实例的每个可能的最优B解。
确保h正确的一个常见策略是找到从A解到B解的“子结构”函数k和“拼接”子结构的方法,即,给出A解的证明x和B-溶液y不比k(x)差,存在不比x差的A-溶液x',使得k(x')= y。然后,h可以优化其输入k下的反转图像中的所有内容。拼接不一定适用于所有解x,只需一个最优解。
答案 1 :(得分:0)
在动态编程中,我们将问题分解为较小的子问题,做一些操作并为更大的答案提供答案 - 非常类似于递归方法(并非巧合)。
现在,当我们正式证明这种算法的正确性时,这是通过归纳来完成的。我们证明我们的“基本子句”是正确的(通常非常简单),然后我们假设任何小于当前问题的问题也是最优的。然后我们用这个假设来证明更大问题的正确性。
如果我们不知道所有解决方案都是最优的 - 我们将无法证明使用一个额外步骤我们能够将最小解决方案修改为更大问题的最佳解决方案 - 那就不会足以证明这一说法的信息。
如果我们知道某些子问题是最佳解决方案 - 仅仅确保使用这个子问题(我们有一个最佳的解决方案)也是不够的,我们需要为更大的问题找到最佳解决方案。
以背包为例,让我们来看看它的DP步骤:
f(x,i) = max(f(x-weight[i],i-1) +val[i], f(x,i-1))
如果我们知道其中只有一个是最优的 - 我们无法证明算法是正确的,因为我们可能需要“其他”情况,我们没有最佳解决方案。
如果我们在f(x,i-1)
中选择了max()
,那可能是错误的选择。通过确保我们对所有子问题都有最佳解决方案,我们确保不会发生这种情况。