算法---->贪心算法

贪心算法

一、贪心算法定义

贪婪算法是一种改进了的分级处理方法。其核心是根据题意选取一种量度标准。然后将这多个输入排成这种量度标准所要求的顺序,按这种顺序一次输入一个量。如果这个输入和当前已构成在这种量度意义下的部分最佳解加在一起不能产生一个可行解,则不把此输入加到这部分解中。这种能够得到某种量度意义下最优解的分级处理方法称为贪婪算法。

贪心算法总是作出在当前看来是最好的选择。也就是说贪心算法不从整体最优上加以考虑,它所作出的选择只是在某种意义上的局部最优选择。虽然贪心算法不是对所有问题都能得到整体最优解(0/1背包问题),但对范围相当广的许多问题它都能产生最优解。如单源最短路径问题,最小生成树问题等。

二、一般方法

       贪心方法适合的问题:它有n个输入,而它的解就由这n个输入满足某些事先给定的约束条件的某个子集组成,而把满足约束条件的子集称为该问题的可行解。显然,可行解一般来说是不唯一的。那些使目标函数取极值(极大或极小)的可行解,称为最优解。

       贪心方法是求解这一类需求取最优解的问题的一种直接有效的方法。贪心方法是一种分级处理方法,它首先根据题意,选取一种量度标准。然后按这种量度标准对这n个输入排序,并按序一次输入一个量。如果这个输入量的加入,不满足约束条件,则不把此输入加到这部分解中。

        对于一个给定的问题,往往可能有好几种量度标准。用其中的大多数量度标准作贪心处理所得到该量度意义下的最优解并不是问题的最优解,而是次优。选择能产生问题最优解的最优量度标准是使用贪心法设计求解的核心问题。

三、贪心方法的抽象化控制

贪心算法设计的基本控制路线

procedure GREEDY(A,n)

     //A(1:n)包含n个输入//

     solutions←φ  //将解向量solution初始化为空/

     for i←1  to  n do

      x←SELECT(A)

      if  FEASIBLE(solution,x)

       then solutions←UNION(solution,x)

      endif

     repeat

     return(solution)

end GREEDY

 

SELECT     按照某种量度标准从A中选择一个输入,把它的值赋给X,并将其从A中删除

FEASIBLE   判定X是否可以包含在解向量中

UNION      将X与解向量合并,并修改目标函数

四、贪心算法的基本要素

可以用贪心算法求解的问题中可以看到它们一般具有两个重要的性质:贪心选择性质和最优子结构性质

  • 所谓贪心选择性质是指所求问题的整体最优解可以通过局部最优的选择,即贪心选择达到。这是贪心算法可行的第一个基本要素。
  • 当一个问题的最优解包含着它的子问题的最优解时,称此问题具有最优子结构性质。这是问题能用贪心算法求解地一个关键特征。

适合用贪心法的问题应具有最优子结构性质:原问题的最优解包含了其子问题的最优解。例如背包问题,原问题的最优解是在背包的容积的限定下利润达到最大,实际上是一个单位容积利润最大的问题。它包含子问题的最优。带限期的作业调度,是在限期的约束下,利润最大。子问题得最优符合这一原则。但不是所有的问题都能找到贪心算法。例如,0/1背包问题。

五、背包问题

已知有n种物品和一个可容纳M重量的背包,每种物品i的重量为wi。假定将物品i的一部分xi放入背包就会得到pixi的效益,这里,0≤xi≤1,pi>0。如果这些物品重量的和大于M,要求所有选中要装入背包的物品总重量不得超过M,而装入背包物品获得的总效益最大

n=3,M=20,P=(25,24,15),W (18,15,10)。最优解为(0,1,1/2) 最大效益为31.5

贪心策略: 利润/重量为量度     即每一次装入的物品应使它占用的每一单位容量获得当前最大的单位效益。这就需使物品的装人次序按pi/wi比值的非增次序来考虑。

  背包问题的贪心算法

   procedure  KNAPSACK(P,W,M,X,n)

     //P(1:n)和W(1;n)分别含有按

       P(i)/W(i)≥P(i+1)/W(i+1)排序的n件物品的效益值

       和重量。M是背包的容量大小,而x(1:n)是解向量//

     real P(1:n),W(1:n),X(1:n),M,cu;

     integer i,n;

     X←0  //将解向量初始化为零//

     cu←M  //cu是背包剩余容量//

     for i←1 to n do

       if  W(i)>cu  then   exit  endif

       X(i) ←1

        cu←cu-W(i)

     repeat

     if  i≤n  then   X(i) ←cu/ W(i)

     endif

   end GREEDY-KNAPSACK

六、带有限期和效益的单位时间的作业排序1

假定只能在一台机器上处理n个作业,每个作业均可在单位时间内完成,假定作业i有一个截止期限di>0(它是整数),当且仅当作业i在它的期限截止以前被完成时,则获得pi>0的效益。这个问题的一个可行解是这n个作业的一个子集合J,J中的每个作业都能在各自的截止期限之前完成。可行解的效益值是J中这些作业的效益之和,即。具有最大效益值的可行解就是最优解。

选择下一个作业的量度标准:按pi 的非增次序来考虑这些作业

n=4,(p1,p2,p3,p4)=(100,10,15,20)和 (d1,d2,d3,d4)=(2,1,2,1)

最优的处理次序是:先处理作业4,再处理作业1。

            J={1},      p1=100              作业1有最大效益

            J={1,4}   p1+p4=120             作业4有第二大效益

                                              作业3,2都被舍弃

算法描述

procedure  GREEDY-JOB(D,J,n)

    //作业按p1≥p2≥…≥pn的次序输入,它们的期限值D(i)≥1,1≤i≤n,n≥1。J是在它们的截止期限前完成的作业的集合//

1     J←{1}

2     for I←2   to  n  do

3       if    J∪{i}的所有作业都能在它们的截止期限前完成 

           then  J←J∪{i}

4       endif

5     repeat

     end GREEDY-JOB

七、一种更快的带有限期和效益的单位时间的作业排序算法

如果J是作业的可行子集,那末以可以按如下规则来确定这些作业中的每一个作业的处理时间:若还没给作业I分配处理时间,则分配给它时间片[a-1,a],其中a应尽量取大且时间片[a-1,a]是空的(如果没有这样的时间片[a-1,a]可分配,则作业不不能计入J)。此规则就是尽可能推迟对作业的的处理。于是,在将作业一个一个地装配互J中时,就不必为接纳新作业而去移动J中那些已分配了时间片的作业。

设n=5,(p1,p2,…,p5)=(20,15,10,5,1)和(d1,d2,…,d5)=(2,2,1,3,3)

     使用上述可行性规则,得:

J

已分配的时间

正被考虑的作业

动作

1

分配[1,2]

{1}

[1,2]

2

分配[0,1]

{1、2}

[0,1],[1,2]

3

舍弃

{1,2}

[0,1],[1,2]

4

分配[2,3]

{1,2,4}

[0,1],[1,2]  ,[2,3]

5

舍弃

所以,最优解是J={1,2,4}..

八、最优归并 (哈夫曼树、最优二叉树)

将两分别有n个记录和m个记录的已分类文件可以在O(n+m)时间内归并成一个已分类文件。但现在假定X1,X2,X3,X4,X5是需要归并,从而得到想要的归并文件。-如何归并?(归并需要移动记录—归并的代价, 移动记录总量最少的归并方法为最优归并—代价最小

二元归并树生成算法

    Procedure  TREE(L,n)

                  //L是n个单结点的二元树的表//

1      for i←1 to n-1 do

2         call getnode(T)  //生成一个结点T,用于归并两棵树//

3         Lchild(T) ←LEAST(L)

4         Rchild(T) ←LEAST(L)

5         Weight(T) ←WEIGHT(Lchild(T))+WEIGHT(Rchild(T) )

6         call INSERT(L,T)

7      repeat

8   return (LEAST(L))

9   end  TREE

结点的带权路径长度是从根结点到该结点之间的路径长度与结点权的乘积。树的带权路径长度定义为树中所有叶子结点的带权路径长度之和WPL = ∑WiLi 1≤i≤n其中:n为叶子结点个数,Wi为第i个叶子结点的权,Li为从根到第i个叶子结点路径的长度。当引入以上概念以后,求最佳编码方案实际上就抽象为求在叶子结点个数与权确定时带权路径长度最小的二叉树。那么什么样的树带权路径长度最小呢?

对于给定n个权值w1, w2, … wn(n≥2),求一棵具有n个叶子结点的二叉树,使其带权路径长度∑WiLi最小。由于Huffman给出了构造具有这种树的方法,因此这种树称为Huffman树。

Huffman树:它是由n个带权叶子结点构成的所有二叉树中带权路径长度最小的二叉树,Huffman 树又称最优二叉树

构造 Huffman树的算法步骤如下:

①根据给定的n个权值,构造n棵只有一个根结点的二叉树, n个权值分别是这些二叉树根结点的权,F是由这n棵二叉树构成的集合;

②在F 中选取两棵根结点树值最小的树作为左、右子树,构造一颗新的二叉树,置新二叉树根的权值=左子树根结点权值+右子树根结点权值;

③从F中删除这两颗树,并将新树加入F;

④重复②③,直到F中只含一棵树为止。

直观地看,先选择权值小的,所以权值小的结点被放置在树的较深层,而权值较大的离根较近,这样自然在Huffman树中权越大的叶子离根越近,这样一来,在计算树的带权路径长度时,自然会具有最小的带权路径长度,这种生成算法就是一种典型的贪心算法。 

哈夫曼树

九、最小生成树

最小生成树

十、单源点最短路径

单源点最短路径

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
背包问题贪心算法,背包问题 ---- * 已知有n种物品和一个可容纳M重量的背包,每种物品i的重量是w[i]。假定将物品i的一部分x[i]放入背包就会得到p[i]x[i]的效益,这里, * 0<=x[i]<=1,p[i]>0.采用怎样的方法才能使装包的效益最大呢? * 考虑以下情况下的背包问题:n = 3,M = 20,(p0,p1,p2) = (25,24,15),(w0,w1,w2) = * (18,15,10).其中的4个可行解是 * (x0,x1,x2) w0x0 + w1x1 + w2x2 p0x0 + p1x1 + p2x2 * (1/2,1/3,1/4) 16.5 24.25 * (1,2/15,0) 20 28.2 * (0,2/3,1) 20 31 * (0,1,1/2) 20 31.5 * 在这4个可行解中第四个的效益值最大。 定理:如果 p1/w1>=p2/w2>=...>=pn/wn,则算法对于给定的背包问题实例生成一个最优解。 证明: * 设X= (x1,...,xn)是最优解。如果所有的xi = 1,显然这个解是最优解。于是,设j是使xj != 1 的最小下标。由算法可知,对于1<=i<=j * ,xi=1;对于 j<i<=n,xi =0;对于j, 0<=xj<1.如果X不是一个最优解,则必定存在一个可行解Y=(y1,...yn),使得 * piyi > pixi.不失 一般性,可以假定 wiyi =M.设k是使得yk!=xk的最小下标。显然,这样的k必定存在。由上面的假设,可以推得yk<xk. * 这可从3种可能发生的情况,即k<j,k=j,k>j分别得到证明: (1)若k<j,则xk = 1.因yk!=xk,从而yk<xk. (2)若k=j ,由于 ∑wjxi = * M,且对1<=i<j,有xi=yi=1,而对j<i<=n,有xi =0.若yk>xk,显然有∑wiyi>M,与Y是可行解矛盾。若yk=xk * ,与假设yk!=xk矛盾,故yk<xk. (3)若k>j,则∑wiyi>m,这是不可能的。 * 现在,假定把yk增加到xk,那么必须从(yk+1,...,yn)中减去同样多的量,使得所有的总容量仍是M。这导致一个新的解Z=(z1,...zn), * 其中,zi = xi , 1<=i<=k,并且∑(k<i<=n)wi(yi-zi)= wk(zk-yk).因此,对于Z有 * ∑pizi = ∑piyi + (zk-yk)wkpk/wk-∑(k<i<=n)(yi-zi)wipi/wi * >= ∑piyi +[(zk-yk)wk-∑(yi-zi)wi]pk/wk * = ∑piyi * 如果∑pizi>∑piyi,则Y不可能是最优解。如果这两个和数相等,同时Z=X,则X就是最优解;若Z!=X,则重复上面的讨论,或者证明Y不是最 * 优解,或者把Y转换成X,从而证明了X也是最优解。证毕。 */ public class BinSerch { //对数组buf降序排列 同时 index 数组记录排序前的数组索引 public static void order(double[] buf, int[] index) { int count = 1; while (count++ < buf.length) { for (int i = buf.length - 1; i > 0; i--) { if (buf[i] > buf[i - 1]) { double temp = buf[i]; buf[i] = buf[i - 1]; buf[i - 1] = temp; int temp1 = index[i]; index[i] = index[i - 1]; index[i - 1] = temp1; } else continue; } } for (int j = 0; j < buf.length; j++) { System.out.print(buf[j] + "(" + j + ")"); } System.out.println(); } public static void main(String[] args) { //对上述背包问题求最优解 int n = 3; //物品数量 double[] p = { 25, 24, 15 }; //效益数组 double[] w = { 18, 15, 10 }; //重量数组 double[] pw = { p[0] / w[0], p[1] / w[1], p[2] / w[2] }; //选取pi/wi为其量度标准 int[] index = { 0, 1, 2 }; //数组索引 double[] record = new double[3];//记录排序前数组下标 double cu = 20; //背包剩余容量 order(pw, index); //排序 //背包问题贪心算法 int i = 0; for (i = 0; i < n; i++) { if (w[index[i]] < cu) { record[i] = 1; cu = cu - w[index[i]]; } else { break; } } if (i < n) { record[i] = cu / w[index[i]]; } for (int j = 0; j < record.length; j++) { System.out.print("x" + j + "\t"); System.out.print(record[j] + "\t"); } } }

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值