第八章-贪心算法
- 格式:doc
- 大小:494.00 KB
- 文档页数:15
贪⼼算法1、如何理解贪⼼算法贪⼼算法的思想是:每次都做出当前最优的选择,通过多步选择得出最终的最优解。
它适合解决上⼀步的选择不会影响下⼀步的选择的问题。
如果上⼀步的选择会影响下⼀步的选择,则使⽤贪⼼算法不⼀定能求出最优解。
1.1 能够使⽤贪⼼算法求解的问题举例问题:假如我们有⼀个能够容纳100Kg物品的袋⼦,可以装各种物品,不管物品的体积。
现在我们有5种⾖⼦,每种⾖⼦的总重量和总价值各不相同。
那如何往背包⾥⾯装这些⾖⼦,使得最后背包⾥物品的总价值最⼤呢?我们⼀眼就能知道这个问题的解法,先求出每种⾖⼦的单价,然后从单价⾼的⾖⼦开始装,装完单价⾼的,再去装次⾼的,直到袋⼦装满为⽌。
这个问题就适合⽤贪⼼算法来解,实际上上⾯的做法体现的就是贪⼼算法的思想(每次都做出当前最优的选择)。
这⾥第⼀步选择装单价最⾼的⾖⼦之后,不会影响第⼆步去选择装次⾼的⾖⼦的选择,同样第⼆步也不会影响第三步去选择单价排名第三的⾖⼦。
1.2 不能使⽤贪⼼算法来求解的问题举例问题:假如我们要在⼀个有权图中,从顶点S开始,找⼀条到顶点T的最短路径。
贪⼼算法的解决思路是,每次都选择⼀条根当前顶点相连的权最⼩的边(每次都做出当前最优的选择),直到找到顶点T。
按照这种思路,我们求出的最短路径是S->A->E->T,路径长度1+4+4=9;⽽实际的最短路径是S->B->D->T,路径长度2+2+2=6 。
为什么这⾥贪⼼算法得不到最优解呢?我们第⼀步从S->A和第⼀步从S->B,下⼀步⾯对的顶点和边是不⼀样的。
也就是我们前⾯的选择会影响后⾯的选择,所以得不出最优解。
⽐如:钱币找零'''假设现在市⾯上有 6 种不同⾯值的硬币,各硬币的⾯值分别为 5 分、1 ⾓、2 ⾓、5 ⾓、1 元、2 元,要找零 10.5 元,求出最少硬币的数量。
'''def getChange(coins, amount):coins.sort();# 从⾯值最⼤的硬币开始遍历i = len(coins)-1while i >= 0:if amount >= coins[i]:n = int(amount // coins[i])change = n * coins[i]amount -= changeprint (n, coins[i])i -= 1getChange([0.05,0.1,0.2,0.5,1.0,2.0], 10.5)再⽐如:使⽤贪⼼算法实现哈夫曼编码3.1 什么是哈夫曼编码哈夫曼编码是⼀种⼗分有效的编码⽅法,⼴泛应⽤于数据压缩中,其压缩率通常在20%~90%之间。
五⼤常⽤算法之三:贪⼼算法贪⼼算法的定义:贪⼼算法是指在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,只做出在某种意义上的局部最优解。贪⼼算法不是对所有问题都能得到整体最优解,关键是贪⼼策略的选择,选择的贪⼼策略必须具备⽆后效性,即某个状态以前的过程不会影响以后的状态,只与当前状态有关。解题的⼀般步骤是:1.建⽴数学模型来描述问题;2.把求解的问题分成若⼲个⼦问题;3.对每⼀⼦问题求解,得到⼦问题的局部最优解;4.把⼦问题的局部最优解合成原来问题的⼀个解。如果⼤家⽐较了解动态规划,就会发现它们之间的相似之处。最优解问题⼤部分都可以拆分成⼀个个的⼦问题,把解空间的遍历视作对⼦问题树的遍历,则以某种形式对树整个的遍历⼀遍就可以求出最优解,⼤部分情况下这是不可⾏的。贪⼼算法和动态规划本质上是对⼦问题树的⼀种修剪,两种算法要求问题都具有的⼀个性质就是⼦问题最优性(组成最优解的每⼀个⼦问题的解,对于这个⼦问题本⾝肯定也是最优的)。动态规划⽅法代表了这⼀类问题的⼀般解法,我们⾃底向上构造⼦问题的解,对每⼀个⼦树的根,求出下⾯每⼀个叶⼦的值,并且以其中的最优值作为⾃⾝的值,其它的值舍弃。⽽贪⼼算法是动态规划⽅法的⼀个特例,可以证明每⼀个⼦树的根的值不取决于下⾯叶⼦的值,⽽只取决于当前问题的状况。换句话说,不需要知道⼀个节点所有⼦树的情况,就可以求出这个节点的值。由于贪⼼算法的这个特性,它对解空间树的遍历不需要⾃底向上,⽽只需要⾃根开始,选择最优的路,⼀直⾛到底就可以了。话不多说,我们来看⼏个具体的例⼦慢慢理解它:1.活动选择问题 这是《算法导论》上的例⼦,也是⼀个⾮常经典的问题。有n个需要在同⼀天使⽤同⼀个教室的活动a1,a2,…,an,教室同⼀时刻只能由⼀个活动使⽤。每个活动ai都有⼀个开始时间si和结束时间fi 。⼀旦被选择后,活动ai就占据半开时间区间[si,fi)。如果[si,fi]和[sj,fj]互不重叠,ai和aj两个活动就可以被安排在这⼀天。该问题就是要安排这些活动使得尽量多的活动能不冲突的举⾏。例如下图所⽰的活动集合S,其中各项活动按照结束时间单调递增排序。
有人说贪心算法是最简单的算法,原因很简单:你我其实都很贪,根本不用学就知道怎么贪。
有人说贪心算法是最复杂的算法,原因也很简单:这世上会贪的人太多了,那轮到你我的份?贪心算法详解贪心算法思想:顾名思义,贪心算法总是作出在当前看来最好的选择。
也就是说贪心算法并不从整体最优考虑,它所作出的选择只是在某种意义上的局部最优选择。
当然,希望贪心算法得到的最终结果也是整体最优的。
虽然贪心算法不能对所有问题都得到整体最优解,但对许多问题它能产生整体最优解。
如单源最短路经问题,最小生成树问题等。
在一些情况下,即使贪心算法不能得到整体最优解,其最终结果却是最优解的很好近似。
贪心算法的基本要素:1.贪心选择性质。
所谓贪心选择性质是指所求问题的整体最优解可以通过一系列局部最优的选择,即贪心选择来达到。
这是贪心算法可行的第一个基本要素,也是贪心算法与动态规划算法的主要区别。
动态规划算法通常以自底向上的方式解各子问题,而贪心算法则通常以自顶向下的方式进行,以迭代的方式作出相继的贪心选择,每作一次贪心选择就将所求问题简化为规模更小的子问题。
对于一个具体问题,要确定它是否具有贪心选择性质,必须证明每一步所作的贪心选择最终导致问题的整体最优解。
2. 当一个问题的最优解包含其子问题的最优解时,称此问题具有最优子结构性质。
问题的最优子结构性质是该问题可用动态规划算法或贪心算法求解的关键特征。
贪心算法的基本思路:从问题的某一个初始解出发逐步逼近给定的目标,以尽可能快的地求得更好的解。
当达到算法中的某一步不能再继续前进时,算法停止。
该算法存在问题:1. 不能保证求得的最后解是最佳的;2. 不能用来求最大或最小解问题;3. 只能求满足某些约束条件的可行解的范围。
实现该算法的过程:从问题的某一初始解出发;while 能朝给定总目标前进一步do求出可行解的一个解元素;由所有解元素组合成问题的一个可行解;用背包问题来介绍贪心算法:背包问题:有一个背包,背包容量是M=150。
贪心算法一、算法思想贪心法的基本思路:—-从问题的某一个初始解出发逐步逼近给定的目标,以尽可能快的地求得更好的解。
当达到某算法中的某一步不能再继续前进时,算法停止.该算法存在问题:1. 不能保证求得的最后解是最佳的;2. 不能用来求最大或最小解问题;3. 只能求满足某些约束条件的可行解的范围。
实现该算法的过程:从问题的某一初始解出发;while 能朝给定总目标前进一步do求出可行解的一个解元素;由所有解元素组合成问题的一个可行解;二、例题分析1、[背包问题]有一个背包,背包容量是M=150。
有7个物品,物品可以分割成任意大小。
要求尽可能让装入背包中的物品总价值最大,但不能超过总容量。
分析:目标函数:∑pi最大约束条件是装入的物品总重量不超过背包容量:∑wi<=M( M=150)(1)根据贪心的策略,每次挑选价值最大的物品装入背包,得到的结果是否最优?(2)每次挑选所占重量最小的物品装入是否能得到最优解?(3)每次选取单位重量价值最大的物品,成为解本题的策略。
?值得注意的是,贪心算法并不是完全不可以使用,贪心策略一旦经过证明成立后,它就是一种高效的算法。
贪心算法还是很常见的算法之一,这是由于它简单易行,构造贪心策略不是很困难。
可惜的是,它需要证明后才能真正运用到题目的算法中。
一般来说,贪心算法的证明围绕着:整个问题的最优解一定由在贪心策略中存在的子问题的最优解得来的。
对于例题中的3种贪心策略,都是无法成立(无法被证明)的,解释如下:(1)贪心策略:选取价值最大者.反例:W=30物品:A B C重量:28 12 12价值:30 20 20根据策略,首先选取物品A,接下来就无法再选取了,可是,选取B、C则更好.(2)贪心策略:选取重量最小.它的反例与第一种策略的反例差不多。
(3)贪心策略:选取单位重量价值最大的物品。
反例:W=30物品:A B C重量:28 20 10价值:28 20 10根据策略,三种物品单位重量价值一样,程序无法依据现有策略作出判断,如果选择A,则答案错误。
第8章 贪心算法 8.1简介 8.1.1 例子
例子: 有4种硬币,面值分别为2角5分、一角、五分和一分。现在要求以最少的硬币个数找给顾客6角三分。 通常是:先拿两个2角5分的+1个一角+3个一分 这种找法所拿出的硬币个数最少。这种算法其实就是贪心算法。顾名思义:
该算法总是作出在当前看来最好的选择,并且不从整体上考虑最优
问题,所作出的选择只是在某种意义上的局部最优解。
上面的解法得到的恰好也是最优解。因此,贪心方法的效率往往是很高的。 假如,面值有一角一分、五分和一分,要找给顾客一角五分。如果还是使用上述贪心算法,得到的解是:一个一角一分+4个一分。而最优解是3个5分。 又例如:
求节点1到节点6的最短路径。用贪心法得19,而实际为17。 显然贪心算法不能对所有问题都能得到最优解,但是对很多问题(包括很多著名的问题)都能产生整体最优解。例如,我们今天要讲的图的单元最短路径问题、最小生成树问题。在有些情况下,算法不能得到整体最优解,但是结果确是最优解的很好近似。
8.1.2 贪心选择性质 所谓贪心选择性质是指所求问题的整体最优解可以通过一系列局部最优的
1 2 4 3 5 6 1
12 7 3 4 5 13 4 15 选择来达到,即贪心选择来达到。这是贪心算法可行的第一个基本要素.也是贪心算法与动态规划算法的主要区别: 动态规划算法:每步所做出的选样往往依赖于相关子问题的解,因而只有在解出相关子问题后.才能做出选择。 贪心算法:仅在当前状态下做出最好选样,即局部最优选则。然后再去解做出这个选择后产生的相应的子问题。贪心算法所做出的贪心选则可以依赖于以往做过的选则,但决不依赖于将来所做的选样,也不依赖于子问题的解。 动态规划:是自底向上的方法解各子问题; 贪心算法:是自顶向下的方法。每做出一次贪心选择就将所求问题简化为规模更小的子问题.
使用贪心法的难点在于:证明所设计的算法确实能够正确解决所求解的问题。即对于一个具体的问题,必须证明每一步所做出的贪心选择最终导致问题的整体最优解。 首先,考察问题的一个整体最优解,并证明可修改这个最优解,使其以贪心选择开始。做出贪心选择后.原问题简化为规模更小的类似子问题。然后,用数学归纳法证明、通过每一步做贪心选择.最终可得到问题的整体最优解。 其中,证明贪心选择后的问题简化为规模最小的类似子问题的关键在于利用该问题的最优子结构性质。
最优子结构性质: 当一个问题的最优解包含其子问题的最优解时,称此问题具有最优子结构性质。 贪心算法和动态规划算法都要求问题具有最优子结构性质,这是两类算法的一个共同点。但是都具有最优子结构的问题该选则贪心法还是动态规划算法求解?是否能用动态规划法的也能用贪心算法解?
背包问题: 给定n种物品12{,,,}nuuu和一个背包,物品i的体积为is,价值为iv。已经知道背包的承重量为C。 假设i
x是物体i被装入背包的部分,10ix;要求装满背包且
背包内物体价值最大。
Cxsinii1
maxiniixv1
选择方法:目标函数的值增加最快,而包载容量的消耗较慢的物体装入包中。故优先选择价值体积比最大的物体装入背包。
背包与0-1背包的区别: 结论0-1背包问题能用动态规划法解,但不能用贪心法解。 8.3单源最短路径问题(狄斯奎诺Dijkstra’s算法) 一、问题描述 ),(EVG是一个有向图,每条边上有一个非负整数表示长度值,其中有一个顶点s称为源节点。所谓的单源最短路径问题就是:求解该源节点到所有其它节点的最短路径值。不失去一般性,我们假设},...,3,2,1{nV并且s=1。那么该问题可以使用Dijkstra’s算法来求解,该算法是一种贪心算法,并且能求得最优解。
二、算法思想 开始时,我们将所有的顶点划分为两个集合},..,4,3,2{},1{nYX。所有已
经计算好的顶点存放在X中,Y中表示还没有计算好的。Y中的每个顶点y有一个对应的量][y,该值是从源顶点到y(并且只经由X中的顶点)的最短路径值。 (1) 下面就是选择一个][y最小顶点Yy,并将其移动到X中。
(2) 一旦y被从Y移动到X中,Y中每个和y相邻的顶点w的][w都要更新:表示经由y到w的一条更短的路径被发现了。
对于任意一个顶点 Vv,假如我们使用][v表示源顶点到顶点v的最短路径,最后,我们可以证明上述的贪心法结束后有][][vv。
三、算法DIJKSTRA 输入:含权有向图G=(V,E), V={1,2,…n} 输出:G中顶点1到其他顶点的距离 1. }1{X;}1{VY;0]1[ 2. For y←2 to n If y相邻于1 then ],1[][ylengthy
Else ][v; End if 6. end for 7. for j←1 to n-1
8. 令Yy,找到最小][y
9. X←X∪{y} /*将y从Y移动到X中 10 Y←Y-{y} For 每条边{y,w}
If w∈Y and ][y+length[y,w]< ][w then
],[][][wylenthyw /*更新 Y中和y相邻顶点的值 14. endfor 15.end for
四、举例说明:
(手工解该题目) 124
3561
12934513415
1
24
3561
12934513415
0
184
1317
123456
213955346431243
513615 算法的详细实现: (1) 有向图用邻接表来表示 如图 (2) 假设每条边是非负的 (3) X和Y集合用两个向量来表示X[1..n] ,Y[1..n]。初始时X[1]=1,Y[1]=0.并且对于2<=i<=n, X[i]=0,Y[i]=1. 因此,执行
}{yXX的操作,就是X[y]=1, }{yYY的操作就是,Y[y]=0。
五、证明 下面来证明,使用该DIJKSTRA方法得到的是最优解,也就是][][yy。
其中,][v表示源顶点到顶点v的最短路径;][y是从源顶点到y(并且只经由X中的顶点)的最短路径值。
证明: 归纳法 (1) 显然0]1[]1[
(2) 假设,当前将y移动到X中,并且在y之前移动到X中的任何一个顶点c,都有][][cc。由于][y是有限的,也就是说必定存在一条从1到y路径,长度为][y(我们需要来证明][][yy)。 那么这条路径总可以写作: :1→[ ]→[ ]→。。。→[ ]→[ ]→[ ]→y
在上述序列中,我们总是可以找到一个顶点,不妨称之为x(xX),使得x之后的顶点均属于Y,并且x是在y前最迟离开Y的顶点。所以有以下两种情形: 1→[ ]→[ ]→。。。→[ ]→[x ]→y (A) 或是 1→[ ]→[ ]→。。。→[x ]→[ ]…→y (B) √对于情形(A),
[][][,]yxlengthxy 算法要求
[](,)xlengthxy 归纳假设 []y (A)是最短路径 √对于情形(B), 我们将x之后的顶点不妨称之为w,即: 1→[ ]→[ ]→。。。→[x ]→[ w]…→y (B) 于是有:
][][wy 由于y在w之前离开Y
),(][wxlengthx 算法要求
),(][wxlengthx 归纳假设
][w 因为是最短路径 ][y因为是最短路径 我们已经说了,][y是最短路径了,因此][][yy。
六、算法分析 O(n2) 或 O(mn)
8.2.1 稠图的线性时间算法
把算法的复杂度从O(n2)降到O(m logn). 基本思想是用最小堆数据结构来保持集合Y中的顶点,使Y组中离V-Y最近的顶点y可以在O(logn)时间内被选出. 算法8.2 SHORTESTPATH 输入:含权有向图G=(V,E), V={1,2,…n} 输出:G中顶点1到其他顶点的距离.假设已有一个空堆H
1. }1{VY;0]1[;]1[)1(key 2. For y←2 to n If y相邻于1 then ],1[][ylengthy
][)(yykey INSERT(H,y) Else ][v;
][)(yykey End if 6. end for 7. for j←1 to n-1 8. Y←DELETEMIN(H) 10 Y←Y-{y} For 对每个邻接于y的顶点w∈Y
If ][y+length[y,w]< ][w then
],[][][wylenthyw
/*更新 Y中和y相邻顶点的值 ][)(wwkey endif If Hw then INSERT(H,w) ELSE SIFTUP(H,H-1(w))
endif
endfor 15.end for
其中,H-1(w)返回w再H中的位置,可以用一个数组实现。
8.3 最小生成树 设G =(V,E)是无向连通带权图,(V,T)是是G的一个子图,并且T是一颗树,那么称(V,T)是G的生成树。如果T的权之和是所有生成树中最小的,那么则称之为最小生成树。