《内蒙古大学《算法与数据结构》讲义13贪婪算法》由会员分享,可在线阅读,更多相关《内蒙古大学《算法与数据结构》讲义13贪婪算法(29页珍藏版)》请在金锄头文库上搜索。
1、下载第1 3章贪 婪 算 法离开了数据结构的世界,现在进入算法设计方法的世界。从本章开始,我们来研究一些算法设计方法。虽然设计一个好的求解算法更像是一门艺术,而不像是技术,但仍然存在一些行之有效的能够用于解决许多问题的算法设计方法,你可以使用这些方法来设计算法,并观察这些算法是如何工作的。一般情况下,为了获得较好的性能,必须对算法进行细致的调整。但是在某些情况下,算法经过调整之后性能仍无法达到要求,这时就必须寻求另外的方法来求解该问题。本书的第1 3 1 7章提供了五种基本的算法设计方法:贪婪算法、分而治之算法、动态规划、回溯和分枝定界。而其他的常用高级方法如:线性规划、整数规划、遗传算法、模
2、拟退火等则没有提及。有关这些方法的详细描述请参见相关书籍。本章首先引入最优化的概念,然后介绍一种直观的问题求解方法:贪婪算法。最后,应用该算法给出货箱装船问题、背包问题、拓扑排序问题、二分覆盖问题、最短路径问题、最小代价生成树等问题的求解方案。13.1 最优化问题本章及后续章节中的许多例子都是最优化问题( optimization problem) ,每个最优化问题都包含一组限制条件(c o n s t r a i n t)和一个优化函数(optimization function) ,符合限制条件的问题求解方案称为可行解( feasible solution) ,使优化函数取得最佳值的可行解
3、称为最优解(optimal solution) 。例13-1 渴婴问题 有一个非常渴的、聪明的小婴儿,她可能得到的东西包括一杯水、一桶牛奶、多罐不同种类的果汁、许多不同的装在瓶子或罐子中的苏打水,即婴儿可得到 n 种不同的饮料。根据以前关于这n 种饮料的不同体验,此婴儿知道这其中某些饮料更合自己的胃口,因此,婴儿采取如下方法为每一种饮料赋予一个满意度值:饮用 1盎司第i 种饮料,对它作出相对评价,将一个数值si作为满意度赋予第i 种饮料。通常,这个婴儿都会尽量饮用具有最大满意度值的饮料来最大限度地满足她解渴的需要,但是不幸的是:具有最大满意度值的饮料有时并没有足够的量来满足此婴儿解渴的需要。设
4、 ai是第i 种饮料的总量(以盎司为单位) ,而此婴儿需要t 盎司的饮料来解渴,那么,需要饮用 n种不同的饮料各多少量才能满足婴儿解渴的需求呢?设各种饮料的满意度已知。令xi为婴儿将要饮用的第i 种饮料的量,则需要解决的问题是:找到一组实数xi(1in) ,使ni= 1si xi最大,并满足:ni=1xi =t 及0 xiai 。需要指出的是:如果ni= 1ai t,则不可能找到问题的求解方案,因为即使喝光所有的饮料也不能使婴儿解渴。第三部分算法设计方法对上述问题精确的数学描述明确地指出了程序必须完成的工作,根据这些数学公式,可以对输入 / 输出作如下形式的描述:输入:n,t,si ,ai(其
5、中1in,n 为整数,t、si 、ai为正实数) 。输出:实数xi(1in) ,使ni= 1si xi最大且ni=1xi =t(0 xiai) 。如果ni= 1ai t,则输出适当的错误信息。在这个问题中,限制条件是ni= 1xi =t且0 xiai,1in。而优化函数是ni= 1si xi。任何满足限制条件的一组实数xi都是可行解,而使ni= 1si xi最大的可行解是最优解。例13-2 装载问题 有一艘大船准备用来装载货物。所有待装货物都装在货箱中且所有货箱的大小都一样,但货箱的重量都各不相同。设第 i 个货箱的重量为wi(1in) ,而货船的最大载重量为c,我们的目的是在货船上装入最多的
6、货物。这个问题可以作为最优化问题进行描述:设存在一组变量xi,其可能取值为0或1。如xi 为0,则货箱i 将不被装上船;如xi为1,则货箱i 将被装上船。我们的目的是找到一组 xi ,使它满足限制条件ni= 1wi xic 且xi 0, 1, 1in。相应的优化函数是ni= 1xi 。满足限制条件的每一组xi都是一个可行解,能使ni= 1xi 取得最大值的方案是最优解。例13-3 最小代价通讯网络 这个问题曾在例1 2 - 2介绍过。城市及城市之间所有可能的通信连接可被视作一个无向图,图的每条边都被赋予一个权值,权值表示建成由这条边所表示的通信连接所要付出的代价。包含图中所有顶点(城市)的连通
7、子图都是一个可行解。设所有的权值都非负,则所有可能的可行解都可表示成无向图的一组生成树,而最优解是其中具有最小代价的生成树。在这个问题中,需要选择一个无向图中的边集合的子集,这个子集必须满足如下限制条件:所有的边构成一个生成树。而优化函数是子集中所有边的权值之和。13.2 算法思想在贪婪算法(greedy method)中采用逐步构造最优解的方法。在每个阶段,都作出一个看上去最优的决策(在一定的标准下) 。决策一旦作出,就不可再更改。作出贪婪决策的依据称为贪婪准则(greedy criterion) 。例13-4 找零钱 一个小孩买了价值少于1美元的糖,并将1美元的钱交给售货员。售货员希望用数
8、目最少的硬币找给小孩。假设提供了数目不限的面值为 2 5美分、1 0美分、5美分、及1美分的硬币。售货员分步骤组成要找的零钱数,每次加入一个硬币。选择硬币时所采用的贪婪准则如下:每一次选择应使零钱数尽量增大。为保证解法的可行性(即:所给的零钱等于要找的零钱数) ,所选择的硬币不应使零钱总数超过最终所需的数目。假设需要找给小孩6 7美分,首先入选的是两枚2 5美分的硬币,第三枚入选的不能是2 5美分的硬币,否则硬币的选择将不可行(零钱总数超过 6 7美分) ,第三枚应选择1 0美分的硬币,然后是5美分的,最后加入两个1美分的硬币。贪婪算法有种直觉的倾向,在找零钱时,直觉告诉我们应使找出的硬币数目
9、最少(至少是接近最少的数目) 。可以证明采用上述贪婪算法找零钱时所用的硬币数目的确最少(见练习 1) 。例13-5 机器调度 现有n 件任务和无限多台的机器,任务可以在机器上得到处理。每件任务的开始时间为si,完成时间为fi ,si k。寻第1 3章贪 婪 算 法4 0 9下载找 1 ,n范围内最小的整数j,使得xjyj 。若没有这样的j 存在,则ni= 1xi =ni= 1yi 。如果有这样的j 存在,则jk,否则y 就不是一个可行解,因为xjyj ,xj = 1且yj = 0。令yj = 1,若结果得到的y 不是可行解,则在 j+ 1 ,n范围内必有一个l 使得yl = 1。令yl = 0
10、,由于wjwl ,则得到的y 是可行的。而且,得到的新y 至少与原来的y 具有相同数目的1。经过数次这种转化,可将y 转化为x。由于每次转化产生的新y 至少与前一个y 具有相同数目的1,因此x 至少与初始的y 具有相同的数目1。货箱装载算法的C + +代码实现见程序1 3 - 1。由于贪婪算法按货箱重量递增的顺序装载,程序1 3 - 1首先利用间接寻址排序函数I n d i r e c t S o r t对货箱重量进行排序(见3 . 5节间接寻址的定义) ,随后货箱便可按重量递增的顺序装载。由于间接寻址排序所需的时间为O (nl o gn)(也可利用9 . 5 . 1节的堆排序及第1 4章的归
11、并排序) ,算法其余部分所需时间为 O (n),因此程序1 3 - 1的总的复杂性为O (nl o gn)。程序13-1 货箱装船templatevoid ContainerLoading(int x, T w, T c, int n)/ 货箱装船问题的贪婪算法/ xi = 1 当且仅当货箱 i被装载, 1=i=n / c是船的容量, w 是货箱的重量/ 对重量按间接寻址方式排序/ t 是间接寻址表int *t = new int n+1;I n d i r e c t S o r t ( w, t, n);/ 此时, wti = wti+1, 1=in/ 初始化 xfor (int i =
12、1; i = n; i+)xi = 0;/ 按重量次序选择物品for (i = 1; i = n & wti = c; i+) xti = 1;c -= wti; / 剩余容量delete t;13.3.2 0/1背包问题在0 / 1背包问题中,需对容量为c 的背包进行装载。从n 个物品中选取装入背包的物品,每件物品i 的重量为wi ,价值为pi 。对于可行的背包装载,背包中物品的总重量不能超过背包的容量,最佳装载是指所装入的物品价值最高,即ni=1pixi取得最大值。约束条件为ni=1wixic 和xi 0 , 1 ( 1in)。在这个表达式中,需求出xt的值。xi= 1表示物品i 装入背包
13、中,xi=0 表示物品i 不装入背包。4 1 0第三部分算法设计方法下载0 / 1背包问题是一个一般化的货箱装载问题,即每个货箱所获得的价值不同。货箱装载问题转化为背包问题的形式为:船作为背包,货箱作为可装入背包的物品。例13-8 在杂货店比赛中你获得了第一名,奖品是一车免费杂货。店中有 n 种不同的货物。规则规定从每种货物中最多只能拿一件,车子的容量为c,物品i 需占用wi的空间,价值为pi 。你的目标是使车中装载的物品价值最大。当然,所装货物不能超过车的容量,且同一种物品不得拿走多件。这个问题可仿照0 / 1背包问题进行建模,其中车对应于背包,货物对应于物品。0 / 1背包问题有好几种贪婪
14、策略,每个贪婪策略都采用多步过程来完成背包的装入。在每一步过程中利用贪婪准则选择一个物品装入背包。一种贪婪准则为:从剩余的物品中,选出可以装入背包的价值最大的物品, 利用这种规则, 价值最大的物品首先被装入 (假设有足够容量) ,然后是下一个价值最大的物品,如此继续下去。这种策略不能保证得到最优解。例如,考虑n=2, w=100,10,10, p =20,15,15, c= 1 0 5。当利用价值贪婪准则时,获得的解为x= 1 , 0 , 0 ,这种方案的总价值为2 0。而最优解为 0 , 1 , 1 ,其总价值为3 0。另一种方案是重量贪婪准则是:从剩下的物品中选择可装入背包的重量最小的物品
15、。虽然这种规则对于前面的例子能产生最优解,但在一般情况下则不一定能得到最优解。考虑n= 2 ,w=10,20, p=5,100, c= 2 5。当利用重量贪婪策略时,获得的解为x=1,0, 比最优解 0 , 1 要差。还可以利用另一方案,价值密度 pi/wi贪婪算法,这种选择准则为:从剩余物品中选择可装入包的 pi/wi值最大的物品,这种策略也不能保证得到最优解。利用此策略试解n= 3 ,w=20,15,15, p=40,25,25, c=30 时的最优解。我们不必因所考察的几个贪婪算法都不能保证得到最优解而沮丧,0 / 1背包问题是一个N P-复杂问题(见9 . 5 . 2节对N P-复杂问
16、题的讨论) 。对于这类问题,也许根本就不可能找到具有多项式时间的算法。虽然按 pi/wi非递(增)减的次序装入物品不能保证得到最优解,但它是一个直觉上近似的解。我们希望它是一个好的启发式算法,且大多数时候能很好地接近最后算法。在6 0 0个随机产生的背包问题中,用这种启发式贪婪算法来解有 2 3 9题为最优解。有5 8 3个例子与最优解相差1 0 %,所有6 0 0个答案与最优解之差全在2 5 %以内。该算法能在O (nl o gn)时间内获得如此好的性能。我们也许会问,是否存在一个x (x1 0 0 ),使得贪婪启发法的结果与最优值相差在x%以内。答案是否定的。为说明这一点,考虑例子 n=2, w = 1 ,y, p= 1 0 , 9y, 和c=y。贪婪算法结果为x=1,0, 这种方案的值为1 0。对于y1 0 / 9,最优解的值为9 y。因此,贪婪算法的值与最优解的差对最优解的比例为( ( 9y- 1 0)/9y* 1 0 0 ) %,对于大的y,这个值趋近于1 0 0 %。但是可以建立贪婪启发式方法来提供解,使解的结果与最优解的值之差在最优值的x% (x100) 之内。首先将最多