解决最优化问题的算法一般包含一系列的步骤,每一步都有若干的选择。对于很多最优化问题,只需要采用简单的贪心算法就可以解决,而不需要采用动态规划方法。贪心算法使所做的局部选择看起来都是当前最佳的,通过局部的最优化选择来产生全局最优解。本文将介绍贪心算法的理论基础和一些简单应用。
一般地,可以根据以下步骤来设计贪心算法:
- 将最优化问题转化为这样一个问题,即先做出选择,再解决剩下的一个子问题。
- 证明原问题的最优解之一可以由贪心选择得到。
- 证明在做出贪心选择后,将剩余的子问题的最优解和我们所做的贪心选择组合起来,可以得到原问题的一个最优解。
一般,如果我们能证明一个问题具有以下两个关键的性质,那么就可以设计出它的一个贪心算法。
(1)贪心选择性质:一个全局最优解可以通过局部最优解(贪心)选择来达到。即,当考虑做选择时,只考虑对当前问题最佳的选择而不考虑子问题的结果。而在动态规划中,每一步都要做出选择,这些选择依赖于子问题的解。动态规划一般是自底向上,从小问题到大问题。贪心算法通常是自上而下,一个一个地做贪心选择,不断地将给定的问题实例规约为更小的子问题。
(2)最优子结构:如果问题的一个最优解包含了其子问题的最优解,则称该问题具有最优子结构。
贪心算法的理论基础
关于贪心算法有一种理论可用于确定在何时使用贪心算法能给出最优解,虽然这种理论并没有包含贪心算法所适用的所有情况,但确实描述了许多非常有意义的情况。
拟阵
拟阵
:- 是一个有穷非空集合。
- 是的一个非空子集族。这些子集称为的独立子集,使得如果且,则。如果满足这个性质,我们称是遗传的。(空集一定是的一个成员)
- 如果,,且,则存在使得。我们称满足交换性质。
第一个条件要求
有穷的,且是非空的。第二个条件要求一个满足遗传性质的独立子集。这意味着独立子集必须有“自由”的结构,即独立子集中任何若干个元素都是独立子集,而不存在其他限制(它是自由的)。第三个条件给出了独立子集间的相关限制的信息,即拥有更多元素的独立子集必包含一个元素。这个元素可以扩张拥有更少元素的独立子集。拟阵最早用于研究矩阵拟阵:的元素是矩阵的各个行,是指以所有线性无关行组合为元素的集合。如果且,我们称Ϊ的一个扩张。如果一个独立子集没有任何扩张,则称这个独立子集是最大的。如果存在一个权重函数
,他对每个都 赋予一个正的权重,则我们称拟阵是加权的。权重函数对的子集的权重和为:加权拟阵的贪心算法伪代码:
定理一:拟阵中所有最大的独立子集都具有相同的大小。
证明:假设
是的一个最大独立子集,且B是的另一个比更大的最大独立子集。由交换性质可得,可以扩张成一个更大的独立子集,其中。这与是引理1(拟阵具有贪心选择性质)
设是一个具有权重函数的加权拟阵,且被按权值的递减顺序排列。设是的第一个使独立的元素(如果存在这样的)。如果存在,则存在一个包含的最优子集。
证明:如果不存在这样的
,则空集是其唯一的独立子集。否则,设为任意非空的最优子集。我们假设(否则取,证毕)。显然中没有一个元素的权值比大,否则由遗传性可知此元素构成独立子集,这将会造成与是的第一个使独立的元素的假设矛盾。可以应用以下策略构造。首先令。(1)根据交换性,可知存在
使得,令。重复步骤(1)直到
。此时,其中。因为中没有一个元素的权值比大,所以。因为是最优的,所以引理2
设为一个拟阵。如果是的独立子集的一个可扩展元素,那么也必然是空集的一个可扩展元素。
证明:因为
是的独立子集的一个可扩展元素,所以。根据遗传性,有也是独立的。所以这意味着任何元素如果不能被立即用到,则以后也绝不会被用到。所以,在开始时,对S中的那些不是空集的可扩展元素的略过没有漏掉解)
引理3(拟阵具有最优子结构性质)
设Ϊ中被作用于加权拟阵的算法第一个选择的元素。找一个包含的具有最大权值的独立子集问题,可以化归为找出加权拟阵的一个具有最大权值的独立子集问题,这里:
其中,的权重函数为受限于的的权重函数。
证明:如果为任意包含的的具有最大权值的独立子集,那么就是的一个独立子集。反之,由的任意独立子集可得的一个独立子集。因为有,所以的最大化完全取决于
定理二:如果是一个加权函数为的加权拟阵,则返回一个最优子集。
证明:根据引理2,开始时被略去的不是空集的元素不予考虑,因为他们不会被用到。一旦选择了第一个元素,将它加入是正确的(引理1)。根据引理3,余下的问题就是一个在
的由引起的收缩中寻找一个最优子集。在过程GREEDY将A置为后,余下的各步骤都可解释为是在拟阵中进行的。因为在中是独立的,当且仅当在中是独立的,其中为任意属于的集合。这样,的后续操作就会找出中的一个具有最大权值的独立子集。而的全部操作就可找出贪心算法的应用(一)
一、活动选择问题
假设我们有一个需要以独占的方式使用某一资源(教室)的
个活动组成的集合,其中每一个活动都有一个开始时间和结束时间,其中(活动一旦被选中,其占用的时间为)。就是这些活动会在某一时间段里面进行安排,如果区间和互不重叠,则称活动和是兼容的。活动选择问题就是要选择出一个由互相兼容的活动组成的最大子集合。设是中活动的子集,其中的元素为所有在活动结束之后开始,且在活动开始之前结束。两个关键的发现可以帮助简化我们的解。则子问题就是要在子集选择出一个由互相兼容的活动组成的最大子集合。
定理三:对于任意非空子问题
,设是中具有最早结束时间的活动,那么:- 活动在的某个最大兼容活动子集中被使用。
- 子问题为空,所以选择将使子问题为唯一可能非空的子问题。
证明:其中,2 是显然的,因为没有活动比
更早结束,故而为空。假设为一个最大兼容活动子集,则用替代中最早结束的活动。显然替换后的子集仍然是兼容的,所以也是最大兼容活动子集。即 1得证。1 保证了贪心选择是有效的(贪心选择性质)。根据 2 ,贪心选择
和子问题的解将构成原集合的一个最大兼容活动子集。这意味着贪心算法是有效的。伪代码如下:二、赫夫曼编码
赫夫曼编码(Huffman Coding)又译为哈夫曼编码、霍夫曼编码,是一种用于无损数据压缩的熵编码(权编码)算法。赫夫曼编码使用变长编码表对源符号(如文件中的一个字母)进行编码,其中变长编码表是通过一种评估来源符号出现机率的方法得到的,出现机率高的字母使用较短的编码,反之出现机率低的则使用较长的编码,这便使编码之后的字符串的平均长度、期望值降低,从而达到无损压缩数据的目的。
霍夫曼树又称最优二叉树,是一种带权路径长度最短的二叉树。所谓树的带权路径长度,就是树中所有的叶结点的权值乘上其到根结点的路径长度(若根结点为0层,叶结点到根结点的路径长度为叶结点的层数)。树的路径长度是从树根到每一结点的路径长度之和。
例如,在英文中,e的出现几率最高,而z的出现概率则最低。当利用霍夫曼编码对一篇英文进行压缩时,e极有可能用一个比特来表示,而z则可能花去25个比特(不是26)。用普通的表示方法时,每个英文字母均占用一个字节,即8个比特。与普通方法相比,e使用了一般编码的1/8的长度,z则使用了一般编码3倍多的长度。倘若我们能实现对于英文中各个字母出现概率的较准确的估算,就可以大幅度提高无损压缩的比例。
在编码表中,没有一个编码是另一个编码的前缀,这样的编码称为前缀编码。前缀编码的解码过程需要有一种关于前缀编码的有效表示,使编码可以很容易地被识别出来。有一种表示方法是叶子为给定字符的二叉树。在这棵树中,一个字符的编码被解释为从根至该字符的路径(其中0表示“转向左子节点,1表示“转向右子节点”)。
赫夫曼设计了一个可以用来构造一个最优前缀编码(称为赫夫曼编码)的贪心算法。这个算法的正确性也要依赖于贪心选择性质和最优子结构性质。
- 将要编码的字符集中每个字符初始化为由该字符表示根节点,字符频度表示权重的最小优先队列(只有一个节点)。
- 取出1中两个频度最低的优先队列。以这两个优先队列的根节点作为叶节点构造一个二叉树,其中该二叉树根节点的权重为两个子节点权重之和。(这个步骤为合并两个优先队列)
- 在经过步骤2处理后,对所存在的所有优先队列集合重复执行步骤2,直至最终合并为一个优先队列。
很容易证明这个优先队列是一个最优前缀编码。具体证明可以参见《算法导论》第三版(P234――P235)。
References
1.(美)科曼(Cormen,T.H.) 等,算法导论(第三版),机械工业出版社