贪心算法就是让计算机模拟一个「贪心的人」来做出决策。这个贪心的人是目光短浅的,他每次总是:
并且他一旦做出了选择,就没有办法反悔(不可回溯),所以为了利益最大化,他需要保证绝不能做出错误的选择。
贪心算法不是从整体最优的角度上考虑问题,而是只在意某种意义上的局部最优解。因此,贪心算法并不能保证在所有情况下都能获得最优解。所以在使用贪心算法时,我们需要确保自己能证明最优解的正确性。
可以用贪心算法解决的题目需要满足以下性质:
贪心算法最难的部分不在于问题的求解,而在于正确性的证明,常用的证明方法有归纳法和交换论证法。
因篇幅有限,本篇我们主要说说归纳证明。归纳证明的本质其实就是数学归纳法[1],我们先来复习下数学归纳法吧。
数学归纳法(Mathematical Induction)是一种数学证明[2]方法,通常被用于证明某个给定命题在整个(或者局部)自然数范围内成立。
最简单和常见的数学归纳法是证明当 n 等于任意一个自然数时某命题成立。证明分下面两步:
n = 1
时,命题成立n = m
(m 为任意自然数)时命题成立,那么可以推导出 n = m + 1
时命题也成立1 为归纳基础,2 为归纳步骤。
该方法的原理在于:一旦我们证明了在某个起点值(例如 n = 1
)时命题成立,且证明出从一个值到下一个值的过程有效(即 n = m
到 n = m + 1
),那么任意值都可以通过反复使用这个方法推导出来。即:
为真,
且为真为真
那么:
为真为真
为真为真
如果我们要证明对于任意自然数,都满足:
找到起始点,即 n = 1
时,此时等式左侧等于 1,右侧等于:
左右两侧相等,因此在 n = 1
时,命题成立。
先假设:对于任意自然数 n 命题均成立。
那么,当 n = n + 1
时:
因此,在 n = n + 1
时,命题也成立。证毕。
归纳证明的证明步骤如下:
n
的命题,该命题断定贪心策略的执行最终将导致最优解,其中自然数 n
可以代表算法步数或者问题规模。其中,步骤二使用数学归纳法证明,即践行归纳基础与归纳步骤。
下面我们就来看下如何使用归纳法来证明 Kruskal 算法的正确性。
Kruskal 算法[3]是一种常见并且好写的最小生成树[4]算法,由 Kruskal 发明。该算法基于贪心思想,基本思想是从小到大加入边。
图源:维基百科
首先,给出命题:对于任意 n,该算法对 n 阶图都能得到一棵最小生成树。
当 n = 2
时,此时只有一条边,命题显然为真。
假设对于 n 个顶点的图,该算法正确,考虑 n + 1 个定点的图 ,假设 中最小边权为 。
此时,在图 中连接点 与点 ,得到图 。
根据归纳假设,由算法可推出:存在 的最小生成树 。令 ,则 是关于 的最小生成树。
反证:若 不是 的最小生成树,那么必然存在某包含 边的最小生成树 ,使得 (即 的边权小于 )。
此时,在 中删除 边,可得到 G' 的最小生成树 ,且有:
该表达式与 是最优解相互矛盾,所以 必然是 的最小生成树,证毕。
[1]
数学归纳法: https://zh.wikipedia.org/wiki/%E6%95%B0%E5%AD%A6%E5%BD%92%E7%BA%B3%E6%B3%95
[2]
数学证明: https://zh.wikipedia.org/wiki/%E6%95%B8%E5%AD%B8%E8%AD%89%E6%98%8E
[3]
Kruskal 算法: https://zh.wikipedia.org/wiki/%E5%85%8B%E9%B2%81%E6%96%AF%E5%85%8B%E5%B0%94%E6%BC%94%E7%AE%97%E6%B3%95
[4]
最小生成树: https://zh.wikipedia.org/wiki/%E6%9C%80%E5%B0%8F%E7%94%9F%E6%88%90%E6%A0%91