贪心算法(一)

匿名 (未验证) 提交于 2019-12-02 22:56:40
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/huangsiqian/article/details/82831592

解决最优化问题的算法一般包含一系列的步骤,每一步都有若干的选择。对于很多最优化问题,只需要采用简单的贪心算法就可以解决,而不需要采用动态规划方法。贪心算法使所做的局部选择看起来都是当前最佳的,通过局部的最优化选择来产生全局最优解。本文将介绍贪心算法的理论基础和一些简单应用。

一般地,可以根据以下步骤来设计贪心算法:

  1. 将最优化问题转化为这样一个问题,即先做出选择,再解决剩下的一个子问题。
  2. 证明原问题的最优解之一可以由贪心选择得到。
  3. 证明在做出贪心选择后,将剩余的子问题的最优解和我们所做的贪心选择组合起来,可以得到原问题的一个最优解。

一般,如果我们能证明一个问题具有以下两个关键的性质,那么就可以设计出它的一个贪心算法。

(1)贪心选择性质:一个全局最优解可以通过局部最优解(贪心)选择来达到。即,当考虑做选择时,只考虑对当前问题最佳的选择而不考虑子问题的结果。而在动态规划中,每一步都要做出选择,这些选择依赖于子问题的解。动态规划一般是自底向上,从小问题到大问题。贪心算法通常是自上而下,一个一个地做贪心选择,不断地将给定的问题实例规约为更小的子问题。

(2)最优子结构:如果问题的一个最优解包含了其子问题的最优解,则称该问题具有最优子结构。

贪心算法的理论基础

关于贪心算法有一种理论可用于确定在何时使用贪心算法能给出最优解,虽然这种理论并没有包含贪心算法所适用的所有情况,但确实描述了许多非常有意义的情况。

拟阵

拟阵

  • 是一个有穷非空集合。
  • 的一个非空子集族。这些子集称为

    的独立子集,使得如果

    ,则

    。如果

    满足这个性质,我们称

    遗传的。(空集一定是

    的一个成员)
  • 如果

    ,且

    ,则存在

    使得

    。我们称

    满足交换性质

第一个条件要求

有穷的,且是非空的。第二个条件要求一个满足遗传性质的独立子集。这意味着独立子集必须有“自由”的结构,即独立子集中任何若干个元素都是独立子集,而不存在其他限制(它是自由的)。第三个条件给出了独立子集间的相关限制的信息,即拥有更多元素的独立子集必包含一个元素。这个元素可以扩张拥有更少元素的独立子集。拟阵最早用于研究矩阵拟阵:

的元素是矩阵的各个行,

是指以所有线性无关行组合为元素的集合。如果

,我们称

Ϊ

的一个扩张。如果一个独立子集没有任何扩张,则称这个独立子集是最大的

如果存在一个权重函数

,他对每个

都 赋予一个正的权重

,则我们称拟阵

是加权的。权重函数对

的子集

的权重和为:

加权拟阵的贪心算法伪代码:

定理一:拟阵中所有最大的独立子集都具有相同的大小。

证明:假设

的一个最大独立子集,且B是

的另一个比

更大的最大独立子集。由交换性质可得,

可以扩张成一个更大的独立子集

,其中

。这与

引理1(拟阵具有贪心选择性质)

是一个具有权重函数

的加权拟阵,且

被按权值的递减顺序排列。设

的第一个使

独立的元素(如果存在这样的

)。如果

存在,则存在一个包含

的最优子集

证明:如果不存在这样的

,则空集是其唯一的独立子集。否则,设

为任意非空的最优子集。我们假设

(否则取

,证毕)。显然

中没有一个元素的权值比

大,否则由遗传性可知此元素构成独立子集,这将会造成与

的第一个使

独立的元素的假设矛盾。可以应用以下策略构造

。首先令

(1)根据交换性,可知存在

使得

,令

重复步骤(1)直到

。此时

,其中

。因为

中没有一个元素的权值比

大,所以

。因为

是最优的,所以

引理2

为一个拟阵。如果

的独立子集

的一个可扩展元素,那么

也必然是空集的一个可扩展元素。

证明:因为

的独立子集

的一个可扩展元素,所以

。根据遗传性,有

也是独立的。所以

这意味着任何元素如果不能被立即用到,则以后也绝不会被用到。所以,在开始时,对S中的那些不是空集的可扩展元素的略过没有漏掉解)

引理3(拟阵具有最优子结构性质)

Ϊ

中被作用于加权拟阵

算法第一个选择的元素。找一个包含

的具有最大权值的独立子集问题,可以化归为找出加权拟阵

的一个具有最大权值的独立子集问题,这里:

其中,

的权重函数为受限于

的权重函数。

证明:如果

为任意包含

的具有最大权值的独立子集,那么

就是

的一个独立子集。反之,由

的任意独立子集

可得

的一个独立子集

。因为有

,所以

的最大化完全取决于

定理二:如果

是一个加权函数为

的加权拟阵,则

返回一个最优子集。

证明:根据引理2,开始时被略去的不是空集的元素不予考虑,因为他们不会被用到。一旦选择了第一个元素

,将它加入

是正确的(引理1)。根据引理3,余下的问题就是一个在

的由

引起的收缩

中寻找一个最优子集。在过程GREEDY将A置为

后,余下的各步骤都可解释为是在拟阵

中进行的。因为

中是独立的,当且仅当

中是独立的,其中

为任意属于

的集合。这样,

的后续操作就会找出

中的一个具有最大权值的独立子集。而

的全部操作就可找出

贪心算法的应用(一)

一、活动选择问题

假设我们有一个需要以独占的方式使用某一资源(教室)的

个活动组成的集合

,其中每一个活动

都有一个开始时间

和结束时间

,其中

(活动

一旦被选中,其占用的时间为

)。就是这些活动会在某一时间段里面进行安排,如果区间

互不重叠,则称活动

是兼容的。活动选择问题就是要选择出一个由互相兼容的活动组成的最大子集合。

中活动的子集,其中的元素为所有在活动

结束之后开始,且在活动

开始之前结束。两个关键的发现可以帮助简化我们的解。则子问题

就是要在子集

选择出一个由互相兼容的活动组成的最大子集合。

定理三:对于任意非空子问题

,设

中具有最早结束时间的活动,那么:
  1. 活动

    的某个最大兼容活动子集中被使用。
  2. 子问题

    为空,所以选择

    将使子问题

    为唯一可能非空的子问题。

证明:其中,2 是显然的,因为没有活动比

更早结束,故而

为空。假设

为一个最大兼容活动子集,则用

替代

中最早结束的活动。显然替换后的子集

仍然是兼容的,所以也是最大兼容活动子集。即 1得证。

1 保证了贪心选择是有效的(贪心选择性质)。根据 2 ,贪心选择

和子问题

的解将构成原集合的一个最大兼容活动子集。这意味着贪心算法是有效的。伪代码如下:

二、赫夫曼编码

赫夫曼编码(Huffman Coding)又译为哈夫曼编码、霍夫曼编码,是一种用于无损数据压缩的熵编码(权编码)算法。赫夫曼编码使用变长编码表对源符号(如文件中的一个字母)进行编码,其中变长编码表是通过一种评估来源符号出现机率的方法得到的,出现机率高的字母使用较短的编码,反之出现机率低的则使用较长的编码,这便使编码之后的字符串的平均长度、期望值降低,从而达到无损压缩数据的目的。

霍夫曼树又称最优二叉树,是一种带权路径长度最短的二叉树。所谓树的带权路径长度,就是树中所有的叶结点的权值乘上其到根结点的路径长度(若根结点为0层,叶结点到根结点的路径长度为叶结点的层数)。树的路径长度是从树根到每一结点的路径长度之和。

例如,在英文中,e的出现几率最高,而z的出现概率则最低。当利用霍夫曼编码对一篇英文进行压缩时,e极有可能用一个比特来表示,而z则可能花去25个比特(不是26)。用普通的表示方法时,每个英文字母均占用一个字节,即8个比特。与普通方法相比,e使用了一般编码的1/8的长度,z则使用了一般编码3倍多的长度。倘若我们能实现对于英文中各个字母出现概率的较准确的估算,就可以大幅度提高无损压缩的比例。

在编码表中,没有一个编码是另一个编码的前缀,这样的编码称为前缀编码。前缀编码的解码过程需要有一种关于前缀编码的有效表示,使编码可以很容易地被识别出来。有一种表示方法是叶子为给定字符的二叉树。在这棵树中,一个字符的编码被解释为从根至该字符的路径(其中0表示“转向左子节点,1表示“转向右子节点”)。

赫夫曼设计了一个可以用来构造一个最优前缀编码(称为赫夫曼编码)的贪心算法。这个算法的正确性也要依赖于贪心选择性质和最优子结构性质。

  1. 将要编码的字符集中每个字符初始化为由该字符表示根节点,字符频度表示权重的最小优先队列(只有一个节点)。
  2. 取出1中两个频度最低的优先队列。以这两个优先队列的根节点作为叶节点构造一个二叉树,其中该二叉树根节点的权重为两个子节点权重之和。(这个步骤为合并两个优先队列)
  3. 在经过步骤2处理后,对所存在的所有优先队列集合重复执行步骤2,直至最终合并为一个优先队列。

很容易证明这个优先队列是一个最优前缀编码。具体证明可以参见《算法导论》第三版(P234――P235)。

References


1.(美)科曼(Cormen,T.H.) 等,算法导论(第三版),机械工业出版社

文章来源: 贪心算法(一)
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!