如何证明贪心算法是正确的


29

我有一个贪心算法,我怀疑这可能是正确的,但我不确定。如何检查是否正确?证明贪婪算法正确的技术有哪些?有共同的模式或技术吗?

我希望这将成为一个参考问题,可以用来指导初学者。因此其范围比平常大。请小心给出一般的,有说服力的答案,至少由一个示例说明了这一点,但仍然涵盖了许多情况。谢谢!



我们可以通过使用拟阵或贪婪来证明贪婪算法是正确的吗?
zdm

Answers:


24

最终,您将需要数学上的正确性证明。我将在下面介绍一些证明技术,但首先,在深入探讨之前,让我为您节省一些时间:在寻找证明之前,请尝试进行随机测试。

随机测试

首先,我建议您使用随机测试来测试算法。它的有效性令人惊讶:根据我的经验,对于贪婪的算法,随机测试似乎是不合理的有效。花5分钟编写算法代码,可能会节省一两个小时的时间来提出一个证明。

基本思想很简单:实现您的算法。另外,实施一种您知道是正确的参考算法(例如,穷举尝试所有可能并采取最佳方法的参考算法)。如果您的参考算法渐近低效,那很好,因为您只会在小问题实例上运行它。然后,随机生成一百万个小问题实例,在每个实例上运行两种算法,并检查每种情况下候选算法是否给出正确答案。

根据经验,如果您的候选贪婪算法不正确,通常您会在随机测试中发现这一点。如果在所有测试用例上似乎都正确,那么您应该继续下一步:提出正确性的数学证明。

正确性的数学证明

好的,因此我们需要证明我们的贪婪算法是正确的:它输出最优解(或者,如果有多个同等好的最优解,则输出其中之一)。

基本原理是一个直观的原则:

原理:如果您从未做出错误的选择,那您就可以了。

贪婪算法通常涉及一系列选择。基本的证明策略是,我们将尝试证明该算法绝不会做出错误的选择。贪婪算法无法回溯-一旦做出选择,他们就会做出承诺,并且永远不会撤消该选择-因此,至关重要的是,他们绝不能做出错误的选择。

什么算是一个好选择?如果只有一个最佳解决方案,那么很容易看出什么是一个好的选择:任何与最佳解决方案相同的选择。换句话说,我们将尝试证明,在执行贪心算法的任何阶段,到目前为止,该算法做出的选择顺序与最佳解决方案的某些前缀完全匹配。如果存在多个同样好的最优解,那么一个好的选择就是至少与最优解之一保持一致的选择。换句话说,如果到目前为止算法的选择顺序与最佳解决方案之一的前缀匹配,那么到目前为止一切都很好(还没有出错)。

为了简化生活并消除干扰,让我们关注没有联系的情况:只有一个独特的最佳解决方案。所有的机器都将延续到在没有任何根本变化的情况下可以有多个均等的最佳状态的情况,但是您在技术细节上必须多加注意。首先,忽略那些细节,并专注于最佳解决方案是唯一的情况;这将帮助您专注于基本要素。

我们使用一种非常常见的证明模式。我们将努力证明该算法的以下特性:

要求:令为算法输出的解,为最优解。如果与不同,那么我们可以调整以获得与不同并且严格优于另一个解。O S O O O * O OSOSOOOOO

注意为什么这很有用。如果该说法是正确的,则说明该算法是正确的。这基本上是矛盾的证明。或者相同或它是不同的。如果不同,那么我们可以找到另一个比严格更好的解决方案,但这是矛盾的,因为我们将定义为最佳解决方案,并且没有任何解决方案比这更好。因此我们被迫得出结论,与不能相同; 必须始终等于O O * O O S O S OSOOOOSOSO,即贪心算法始终输出正确的解。如果我们可以证明上述要求,那么我们已经证明了我们的算法正确。

精细。那么,我们如何证明索赔?我们将解视为向量,该向量对应于算法所做出的选择的序列,并且类似地,我们将最优解视为向量对应于导致的选择顺序。如果与不同,则必须存在一些索引,其中 ; 我们将重点放在最小的。然后,我们通过在稍微改变调整S 1S nn O O 1O nO S O i S iO i i O O i S i O i O *S(S1,,Sn)nO(O1,,On)OSOiSiOiiOOi匹配位置,即,通过将第个选择更改为贪婪算法选择的一个,来调整最优解,然后我们将证明这会带来更好的解决方案。特别是,我们将定义为类似SiOiO

O=(O1,O2,,Oi1,Si,Oi+1,Oi+2,,On),

除非经常需要稍微修改部分以保持全局一致性。证明策略的一部分涉及适当定义一些技巧。然后,以某种方式利用有关算法的事实和问题来证明证明的本质,即证明严格优于 ; 那就是您需要针对特定​​问题的见解的地方。在某些时候,您需要深入研究特定问题的细节。但这使您对贪婪算法的正确性的典型证明的结构有所了解。O O OOi+1,Oi+2,,OnOOO

一个简单的例子:具有最大和的子集

通过详细研究一个简单的示例,这可能更容易理解。让我们考虑以下问题:

输入:一组整数,一个整数输出:一组大小为的,其和应尽可能大ķ 小号ù ķUk
SUk

有一个自然的贪婪算法可以解决此问题:

  1. 设置。S:=
  2. 对于: i:=1,2,,k
    • 令为中尚未被选择的最大数字(即第个最大数字)。添加到。 U i U x i SxiUiUxiS

随机测试表明这总是可以提供最佳解决方案,因此让我们正式证明该算法是正确的。请注意,最佳解决方案是唯一的,因此我们不必担心联系。让我们证明以上概述的主张:

要求:令为该算法在输入和的最优解。如果,则我们可以构造另一种解决方案其总和甚至比大。U k O S O O * OSU,kOSOOO

证明。假设,并且让成为的第一次迭代的索引。(这样的索引必须存在,因为我们假设并且根据算法的定义,我们有。)由于(假设)极小,因此我们必须具有尤其是的形式为,其中数字降序排列。查看算法如何选择X ö 小号ö 小号= { X 1... X ķ } X 1... X - 1ø ø ø = { X 1X 2... X - 1X ' X ' + 1SOixiOiSOS={x1,,xk}ix1,,xi1OOX 1... X - 1X ' ... X ' Ñ X 1... X X > X ' Ĵ Ĵ X > X ' Ô = ø { X } { X ' O={x1,x2,,xi1,xi,xi+1,,xn}x1,,xi1,xi,,xnx1,,xi,我们看到对于所有,我们都必须具有。特别地,。这样,定义,即,我们得到通过删除在第数目和添加。现在,的元素之和是加上且的元素之和,因此的和严格大于的和。这证明了要求。xi>xjjixi>xiø *ø X ø * ø X - X ' X - X ' > 0 ö * ö O=O{xi}{xi}OiOxiOOxixixixi>0OO

这里的直觉是,如果贪心算法曾经做出过与不一致的选择,那么我们可以证明,如果将修改为包含贪婪算法在该阶段选择的元素,可能会更好。由于是最优的,因此不可能有任何方法使其变得更好(这将是一个矛盾),因此唯一剩下的可能性是我们的假设是错误的:换句话说,贪心算法将永远不会做出选择与不一致。O O OOOOO

此论点通常称为交换论点交换引理。我们找到了最优解与贪婪解不同的第一位,并且我们设想将元素交换为相应的贪婪选择(将交换为)。一些分析表明,这种交换只能改善最佳解决方案-但根据定义,最佳解决方案无法改善。因此,唯一的结论是,最佳解决方案与贪婪解决方案不应存在任何地方。如果您有其他问题,请寻找在特定情况下应用此交换原则的机会。X ' X Oxixi


这是一个古老的问题,但这对我来说是Google的第一个结果。这条线then we can tweak O to get another solution O∗ that is different from O and strictly better than O使我感到困惑。如果有多个最优解,则可能S != O同时都最优。我们可以调整O操作是“更喜欢” S(创建的O *),但有一样好(不strictly better than)O.
citelao

@citelao,很抱歉听到它让您感到困惑。las,我不确定如何更清楚地解释它。是的,可能有多个最优解决方案,它们的价值都相同。那是正确的。你写的和我写的都是有效的。没有矛盾。不同之处在于您编写的内容无助于证明贪婪算法的正确性。我写的是。我只能建议再次阅读我写的内容,看看是否可以弄清楚我写的内容是否有用。如果那没有帮助,也许找到其他文章。我意识到这很棘手且令人困惑。
DW

1
感谢您及时的回复!我错过了仅关注于证明算法的问题a single, unique optimal solution。由于这个问题是关于证明任何贪婪算法都是正确的,因此我想为可能存在多个最优解的情况提供答案。自从我研究了所有这些以来已经有一段时间了,但是不足以证明您可以在不同于算法的任何最优解O中交换每个元素O_i。解S与S_i并仍然有一个不小于 O 的解O' ?
citelao

@citelao,该技术还适用于存在多个最佳解决方案的情况。我建议着眼于最佳解决方案是唯一的情况,这是因为,当您第一次看到这种情况时,就更容易理解这些证明在这种情况下的工作原理。但是,即使有多个最佳解决方案,也可以使用相同的策略。我建议您对此进行研究,以确保您了解只有一个最佳解决方案时它是如何工作的,然后将其应用于一般情况。另外,我认为研究贪婪算法的一些示例证明可能对您有所帮助。
DW

要回答您的后一个问题,不,那还不够。那不能证明S是最优的。(如果只要求O'不比O差,则在某些情况下S是次优的,但可以进行这种交换。因此证明有可能实现不比O差的O' “不能证明S是否最优,也不能证明贪婪算法是正确的。我建议对答案中描述的方法进行更多研究。这很棘手。通过矛盾证明常常很难理解。)
DW

14

我将使用以下简单的排序算法作为示例:

repeat:
  if there are adjacent items in the wrong order:
     pick one such pair and swap
  else
     break

为了证明正确性,我使用了两个步骤。

  • 首先,我证明算法总是终止。
  • 然后,我证明终止的解决方案是我想要的解决方案。

对于第一点,我选择了一个合适的成本函数,对于该成本函数,我可以证明算法在每个步骤中都对其进行了改进。

在此示例中,我选择输入列表中的反转数。列表的反转是一对条目,,使得但。反转数始终为非负数,并且排序列表的反转数为0。A [ i ] A [ j ] A [ i ] > A [ j ] i < jAA[i]A[j]A[i]>A[j]i<j

显然,以错误顺序交换两个相邻项,会删除反转但不会影响任何其他反转。因此,每次迭代都减少了反转次数。A [ i + 1 ] A [ i ] A [ i + 1 ]A[i]A[i+1]A[i],A[i+1]

这证明算法最终终止。

排序列表中的反转次数为0。如果一切顺利,该算法会将反转次数降低到0。我们只需要证明它不会陷入局部最小值。

我通常通过矛盾来证明这一点。我认为算法已停止,但是解决方案不正确。在示例中,这意味着列表尚未排序,但是没有相邻的项目以错误的顺序排列。

如果列表未排序,则必须至少有两个位置不正确的项目。令和,,为两个这样的项目,和之间的差异最小。由于算法没有停止,因此它们不是相邻的,所以。因为我们假定极小,所以和,但是我们有一个矛盾。A [ j ] i < j A [ i ] > A [ j ] i j i + 1 < j A [ i ] < A [ i + 1 ] A [ i + 1 ] < A [ j ] A [ i ] < A [ j ]A[i]A[j]i<jA[i]>A[j]iji+1<jA[i]<A[i+1]A[i+1]<A[j]A[i]<A[j]

这证明算法仅在列表排序时停止。因此,我们完成了。


所解释的技术是如此笼统,以至于它们实际上对贪婪算法(这个问题的主题)没有什么特别的要求。
Apass.Jack
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.