基础-10:贪心算法

概述

前文中解释了动态规划的基本思想,动态规划通过将一个问题划分为规模更小的有限个子问题进行求解,一般用于求解最优问题,对于每个子问题都要遍历其所有可能。如果一个问题包含若干子问题,且明确知道(或能证明推导)某个子问题一定是其解的一部分,则不用遍历所有的子问题,直接选择确定的子问题作为当前的选择,可以极大地提高算法的效率,贪心算法正是为解决此类问题而提出,典型的问题包括霍夫曼编码、小数背包问题、最少硬币数等,均可用贪心算法求解。

所谓贪心算法,就是在每一步中选择当前的最优解,本文剩余部分就用这几个例子对贪心算法的思想进行说明。

例1: 最少硬币数

假设现有1分、2分、5分、1角的硬币若干枚,给定任意一个金额m,求最少需要多少枚硬币?(注:这与前文中的求最少硬币数是不一样的)

假如有2角8分,求最少硬币数的思维是:首先选择最大面值的硬币数,需要2个1角,还剩8分;针对8分,选择当前可以选择的最大面值数,需要1个5分;以此再选择1个2分和1个1分。因此,共需要5枚硬币(注意与前文例子的不同)。这个问题对应的贪心算法程序本身非常简单,如下所示:

func LeastCoinNum(money int) int {
    tenNum := money / TEN
    money = money - tenNum*TEN

    fiveNum := money / FIVE
    money = money - fiveNum*FIVE

    twoNum := money / TWO
    oneNum := money - twoNum*TWO

    return tenNum + fiveNum + twoNum + oneNum
}

难点在于:怎么证明这个算法得到的最少硬币数。

分析:假设金额为m分,分两种情况进行讨论:

  • 若m<10, 可以列出所有的最小硬币数,显然与上面的程序一致;
  • 若m>=10, 假设1分、2分、5分和1角的最少硬币数分别为k1、k2、k5、k10, 则需要证明k10=m/10是最优解。由于1角是1分、2分、5分的整数倍,假设存在另一个最少的组合中1角的数量k10‘ < k10且k10-k10' = d,则必有k1' + k2' + k5' >= k1 + k2 + k5 + 2d,显然,k1'、k2'、k5'、k10'不是最优解。(思考:前文中的最小硬币数为什么不能用贪心算法?

利用贪心算法时,需要关注问题是否具有两个性质:

  • 贪心选择性质。即通过局部最优选择可以构造全局最优解。如例1中的最小硬币数,每次都选取最大面值的硬币
  • 最优子结构。即如果一个问题的最优解包含其子问题的最优解,则称此问题具有最优子结构性质。(动态规划和贪心选择都具有的性质

例1中的分析验证了这一点,但是分析过程没有严格去证明贪心选择性质和最优子结构。

例2: 霍夫曼编码

霍夫曼编码Huffman Coding)是一种编码方式,是一种用于无损数据压缩熵编码(权编码)算法。1952年,David A. Huffman麻省理工攻读博士时所发明的,并发表于《一种构建极小多余编码的方法》(A Method for the Construction of Minimum-Redundancy Codes)一文。

霍夫曼编码的基本思想包括:1)编码后长度尽可能小;2)能方便高效地解码。图1展示了在一段文本中字符出现的频率(只包含a-f)


图1:某段文本中a-f出现的频率、固定编码和变长编码

显然,变长编码能获得更好的效率。在霍夫曼编码中,涉及的一个重要概念是前缀码,即没有任何字符的二进制编码是其它字符二进制编码的前缀(否则,将会导致解码时非常低效,违反了霍夫曼编码的基本思想),如上图中变长编码中的abc被编码为:0.101.100=0101100.

前缀码用于简化解码过程。如果利用二叉树显示每个字符的编码形式,则更容易理解和处理,这种形式称为编码方案的二叉树。如图1示例中的定长和变长编码的两种二叉树编码形式如图2所示:

图2: 二叉树编码

最优编码方案总是对应一颗满二叉树(即不存在悬空分支的非叶子结点),(分析:假设不是满二叉树,则其中悬空的非叶子结点n,可以将n删除,用n的左孩子或右孩子替代,则n的左孩子或有孩子的编码长度将比原来少1,因此,非满二叉树得到的编码方案不是最短的,即对应的不是最优编码方案)。若存在前缀码对应的一刻满二叉树,对应字符表,对其中每个字符c,用c.freq表示c在文件中出现的次数,dT(c)表示c在叶子结点中的深度,则编码文件需要的二进制长度为:B(T) = sumall c(c.freq * dT(c)),称B(T) 为代价。

在前缀码及对应的二叉树编码方案的基础上,霍夫曼编码就相对简单了,本文直接从算法导论中摘取相应的算法,如图3所示:

图3: 霍夫曼编码贪心算法

第1行获取文本中涉及的字符数,然后根据每个字符在文中出现的次数构造一个列表(第2行),每次从列表中挑出出现次数最低的两个字符,作为一个非叶子结点的左右孩子(第5、6、7行),然后将非叶子结点插入队列(第8行),循环n-1次,即构造完毕,最后返回编码方案(第9行)。图4更直观地显示了图1中的文本的霍夫曼编码全过程。

图4: 图1示例的编码方案

为了证明图3算法的正确性,需要对其贪心选择性质和最优子结构性质进行说明。贪心选择性质说明如下:

若C为字符表,其中每个字符c出现的次数为c.freq。令x和y是C中频率最低的两个字符,则必定存在一个最优前缀码,x和y的二进制编码长度相同,且只有最后的一位二进制不同。(符合直觉,分析也较为简单,在此不再赘述)

最优子结构的性质说明如下:

若C为字符表,其中每个字符c出现的次数为c.freq。令x和y是C中频率最低的两个字符。令C'是C去掉x和y,加入一个新字符z后得到的字母表,即C'=C-{x, y} + {z}。z.freq = x.freq + y.freq。令T'为字母表C'的任意一个最优前缀码对应的编码树。可以将T'中叶结点z替换为一个以x和y为左右孩子的内部结点,得到树T,而T是字母表C的一个最优前缀码。(对应的是算法中的5,6,7,8行,利用上文定义T(B),结合贪心选择性质和反证法进行分析)

在贪心选择性质和最优子结构的前提下,霍夫曼编码贪心算法得到的显然是一个最优解。

例3:小数背包问题

小数背包问题是算法中非常常见的一个问题,与之对应的0-1背包问题,小数背包问题可以用贪心算法,0-1背包问题无法用贪心算法(用动态规划),下面先看小数背包问题的描述:

给定n种物品和一个背包。物品i的重量是Wi,其价值为Vi, 背包的容量为C。应如何选择装入背包的物品,使得装入背包中物品的 总价值最大? 这里,在选择物品i装入背包时,可以选择物品i的一部分, 而不一定要全部装入背包。

针对小数背包问题的说明,由于涉及众多公式编辑,并且庄老师(http://staff.ustc.edu.cn/~lszhuang/alg/ch16.pdf) 写得比我好多了,直接摘抄他的PPT,感兴趣的童鞋,可以直接查阅此PPT

小结

从概率分布的角度看,不能用贪心算法解决的问题比能用贪心算法解决的问题多得多,如在最小硬币数问题中,能否使用贪心算法取决于提供的硬币的额度之间的关系。然而,在现实中,特别是在具体问题中,很多问题都可归结为本文中提到的最小硬币数、霍夫曼编码或小数背包问题。到底是用动态规划还是用贪心算法,在具体的问题中,很多时候依赖于我们的直觉,用动态规划肯定可以得到最优解,但是有可能碰到的是一个NP问题,如列出N个元素的所有排列或组合;用贪心算法,通常可以得到一个次优解,但是不一定是最优解。如何使用,还需要各位童鞋在具体使用过程中体会。

动态规划和贪心算法,是算法中非常重要的思想,是后续稍复杂的问题的分析和处理的基础。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,029评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,395评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,570评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,535评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,650评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,850评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,006评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,747评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,207评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,536评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,683评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,342评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,964评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,772评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,004评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,401评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,566评论 2 349

推荐阅读更多精彩内容

  • 目录 1.贪心算法步骤 2.两个关键要素 3.两种背包问题3.1 0-1背包问题(适用于动态规划,不满足贪心选择性...
    王侦阅读 4,915评论 2 3
  • 贪心算法 贪心算法总是作出在当前看来最好的选择。也就是说贪心算法并不从整体最优考虑,它所作出的选择只是在某种意义上...
    fredal阅读 9,223评论 3 52
  • 9.3.3 快速排序   快速排序将原数组划分为两个子数组,第一个子数组中元素小于等于某个边界值,第二个子数组中的...
    RichardJieChen阅读 1,839评论 0 3
  • 贪心算法 当具有最优子结构性质的时候,可以使用动态规划算法也可以使用贪心算法 最优子结构性质、贪心选择性质 虽然贪...
    冰源阅读 1,012评论 0 0
  • 动态规划和贪心算法都是用来求最优化问题,且二者都必须具有最有子结构。贪心算法可以解决的问题,动态规划都能解决,可以...
    sereny阅读 6,597评论 0 7