贪心算法:
一、贪心算法的思想:
是一种在每一步选择中都采取在当前状态下最好或最优(即最有利)的选择,从而希望导致结果是最好或最优的算法。
二、贪心算法的应用:一般背包问题
问题描述:
已知:
- 有 n 种物品、有一个承重量为 M M M 的背包
- n 种物品的重量分别为 ( w 1 , w 2 , … , w n ) (w_1, w_2, …, w_n) (w1,w2,…,wn)
- n 种物品放入背包所获得的效益分别是 ( p 1 , p 2 , … , p n ) ( p i > 0 ) (p_1, p_2, …, p_n)\quad (p_i>0) (p1,p2,…,pn)(pi>0)
若将物品i的一部分 x i x_i xi ( 0 ≦ x i ≦ 1 0\leqq x_i\leqq1 0≦xi≦1)放入背包,则获得效益 p i x i p_ix_i pixi
问题:
在背包可承重范围内,采用怎样的装包方法会使装入背包物品的总效益为最大?
问题分析:
求出一个( x 1 , x 2 , … , x n x_1, x_2, …, x_n x1,x2,…,xn),要求在满足 ∑ i = 1 n w i x i ≦ M \sum_{i=1}^nw_ix_i\leqq M ∑i=1nwixi≦M的条件下,
使得总效益 ∑ i = 1 n p i x i \sum_{i=1}^np_ix_i ∑i=1npixi 最大。
满足约束条件的( x 1 , x 2 , … , x n x_1, x_2, …, x_n x1,x2,…,xn),称为“可行解”。
使得总效益最大的可行解,称为“最优解”。
一般背包问题中度量标准的选择:
效益值 p i p_i pi、重量 w i w_i wi等都可以作为度量标准。
我们采用效益值和重量之比做为量度标准
一般背包问题的实例:
有3个物品, 即 n = 3 n=3 n=3
背包能承载的最大重量为20, 即 M = 20 M=20 M=20
物品的价值和重量: ( p 1 , p 2 , p 3 ) = ( 25 , 24 , 15 ) , ( w 1 , w 2 , w 3 ) = ( 18 , 15 , 10 ) (p_1, p_2, p_3)=(25, 24, 15), \\ (w_1, w_2, w_3)=(18, 15, 10) (p1,p2,p3)=(25,24,15),(w1,w2,w3)=(18,15,10)
- 步骤1. 将物品按单位重量效益的非增次序排序(递减), ( p 2 w 2 , p 3 w 3 , p 1 w 1 ) = ( 24 15 , 15 10 , 25 18 ) (\frac{p_2}{w_2},\frac{p_3}{w_3}, \frac{p_1}{w_1}) =(\frac{24}{15}, \frac{15}{10}, \frac{25}{18}) (w2p2,w3p3,w1p1)=(1524,1015,1825)
- 步骤2. 按该次序逐一放物品
首先放入物品2, x 2 = 1 x_2=1 x2=1
然后放入物品3,因剩余容量为 20 − w 2 = 5 20-w_2=5 20−w2=5,故只能放入物品3的 5 10 \frac{5}{10} 105,即 1 2 \frac{1}{2} 21
总效益值为 ∑ i = 1 n p i x i = 24 + 15 × 1 2 = 31.5 \sum_{i=1}^np_ix_i = 24+15\times\frac{1}{2}=31.5 ∑i=1npixi=24+15×21=31.5
一般背包问题的贪心算法:
首先给出定理:在物品排序后,通过Greedy-Knapsack算法得到的贪心解X是背包问题的一个最优解。
证明一般背包的贪心算法中贪心解是最优解:
证明的基本思想:
- 把贪心解X与任一最优解Y相比较,如果这两个解不同,就去找第一个不同的 x i x_i xi
- 在最优解Y中,设法用贪心解的 x i x_i xi去替换最优解的 y i y_i yi ,并证明最优解在分量代换前后的总效益值无任何变化。
- 反复进行这种代换, 直到新产生的最优解与贪心解完全一样, 从而证明了贪心解就是最优解。
证明:
附—— y k < x k y_k<x_k yk<xk 的证明:
我们继续证明:
贪心方法的核心:
选择能产生问题最优解的最优量度标准是使用贪婪算法的核心。
三、贪心算法的应用:带有限期的作业排序问题
问题描述:
假定只能在一台机器上处理 n n n个作业:
- 每个作业均可在单位时间内完成;
- 每个作业i都有一个截止期限 d i > 0 d_i>0 di>0(di是整数), 当且仅当作业 i i i 在它的期限截止之前被完成时,方可获得 p i > 0 p_i>0 pi>0 的效益。
问题的可行解:
- 可行解是这 n n n 个作业的一个子集合 J J J, J J J 中的每个作业都能在各自的截止期限之前完成
- 可行解的效益值是 J J J 中这些作业的效益之和 ∑ p j ∑p_j ∑pj
问题的最优解:
具有最大效益值的可行解就是最优解
作业排序问题的标准度量的选择:
选择目标函数 ∑ p j ∑p_j ∑pj 作为量度标准,则下一个要计入的作业将是使 ∑ p j ∑p_j ∑pj 得到最大增加的作业,只需将各作业按效益 p j p_j pj降序来排列,即: p 1 ≦ p 2 ≦ … ≦ p n p_1\leqq p_2 \leqq…\leqq p_n p1≦p2≦…≦pn
作业排序问题的贪心算法:
给出定理:
对于作业排序问题,算法 GREEDY_JOB 所描述的贪心方法总是得到一个最优解。
证明GREEDY——JOB算法中贪心解是最优解:
考虑最优解不等于贪心解的情况
- 情况一:
若 𝐼⊂𝐽,则 𝐼 的效益 < 𝐽 的效益,这与 I I I 是最优解矛盾,故 𝐼⊂𝐽 不可能。- 情况二:
- 情况三:
先考虑 I I I 和 J J J 的交集中的作业。
再考虑 J ∩ I J∩I J∩I以外的作业.
如何判断一个子集合是否是一个可行解?
定理:
设 J J J 是 k k k 个作业的集合, δ = i 1 , i 2 , … , i k \delta=i_1, i_2,…,i_k δ=i1,i2,…,ik是 J J J 中作业的一种排列, 它使得 d i 1 ≦ d i 2 ≦ … ≦ d i k d_{i_1}\leqq d_{i_2}\leqq …\leqq d_{i_k} di1≦di2≦…≦dik。则:
J J J 是一个可行解 当且仅当 J J J 中的作业可以按照 δ \delta δ 的次序排列是一个可行调度表。
定理证明:
证明完毕。
如何判断(保证) J ∪ { i } J∪ \left\{ i \right\} J∪{i}是否为可行解?
方法一:向 J J J 中插入 i i i
- while循环中红字 D ( J ( r ) ) ≠ r D(J(r))\ne r D(J(r))=r 为了保证 J J J 中向后移动一位的作业不会超时。
方法二:对作业 i i i 分配时间时, 尽可能推迟对作业 i i i 的处理。 (在其截止期前最靠后的空时间片)
为了更好的理解这一方法,我们给出方法二的实例:
并查集的引用:
- 加权规则:并查集合并时,集合中元素数少的合并到元素数多的集合中。
- 合并操作 U N I O N UNION UNION( i i i , j j j):使用加权规则,合并根为 i i i 和根为 j j j 的两个集合
- 查找算法 F I N D ( i ) FIND( i ) FIND(i):查找并返回含有元素 i i i 的树根 j j j,并使用压缩规则压缩 i i i 到根 j j j 的所有节点,使其父节点均为 j j j
利用并查集实现方法二:
时间片区间为 [ 0 , b ] [0,b] [0,b]
并查集的操作是红字和粉框中的语句。其中粉框中有对并查集的更新操作,这是重点,将在实例中进一步展现出来。
方法二的实例:
最优解 J = { 1 , 2 , 3 , 4 , 6 } J= \left\{1,2,3,4,6\right\} J={1,2,3,4,6},处理次序 4,2,3,1,6,效益值120