第三章 贪心方法
3.1 一般方法
1,问题的一般特征问题有 n个输入,问题的解是由这 n个输入的某个 子集 组成,这个子集必须满足某些事先给定的条件。
约束条件,子集必须满足的条件;
可 行 解,满足约束条件的子集;可行解可能不唯一;
目标函数,用来衡量可行解优劣的标准,一般以函数的形式给出;
最 优 解,能够使目标函数取极值(极大或极小)的可行解。
分类,根据描述问题约束条件和目标函数的 数学模型 的特性和问题的求解方法 的不同,可分为:线性规划、整数规划、非线性规划、动态规划等。
—— 最优化问题求解贪心方法,一种改进的 分级 的处理方法,可对满足上述特征的某些问题方便地求解。
2,贪心方法的一般策略问题的一般特征:问题的解是由 n个输入的、满足某些事先给定的条件的子集组成。
1)一般方法根据题意,选取一种 度量标准 。然后按照这种度量标准对 n个输入排序,并按序一次输入一个量。
如果这个输入和当前已构成在这种量度意义下的 部分最优解 加在一起不能产生一个可行解,则不把此输入加到这部分解中。否则,将当前输入合并到部分解中从而得到包含当前输入的 新 的 部分解 。
这一处理过程一直持续到 n个输入都被考虑完毕,则记入最优解集合中的输入子集构成这种量度意义下的问题的 最优解贪心方法,这种能够得到某种量度意义下的最优解的 分级处理 方法称为 贪心方法注:
贪心解 最优解
直接将目标函数作为 量度标准 也不一定能够得到问题的最优解
使用贪心策略求解的关键是选取能够得到问题最优解的 量度标准 。
?=
3,贪心方法的抽象化控制描述
procedure GREEDY(A,n)
//A(1:n)包含 n个输入 //
solution← Φ //将解向量 solution初始化为空 //
for i←1 to n do
x←SELECT(A) // 按照度量标准,从 A中选择一个输入,
其值赋予 x并将之从 A中删除 //
if FEASIBLE(solution,x) then //判定 x是否可以包含在当前解向量中,即是否能共同构成可行解 //
solution←UNION(solution,x) // 将 x和当前的解向量合并成新的解向量,并修改目标函数 //
endif
repeat
return
end GREEDY
3.2 背包问题
1.问题的描述已知 n种物品具有重量 (w1,w2,…,w n)和效益值 (p1,p2,…,p n),及一个可容纳 M重量的背包;设当物品 i全部或一部分 xi放入背包将得到 pi xi的效益,这里,0≤ xi ≤1,pi >0。
问题,采用怎样的装包方法才能使装入背包的物品的 总效益最大?
分析,
① 装入背包的总重量 不能超过 M
② 如果所有物品的 总重量 不超过 M,即 ≤ M,则把 所有 的物品都装入背包中将获得最大可能的效益值
③ 如果物品的总重量 超过了 M,则将有物品不能(部分 /全部)装入背包中。由于 0≤xi≤1,所以可以把物品的一部分装入背包,故最终背包中可刚好装入重量为 M的若干物品(整体或一部分)。这种情况下,
如果背包没有被装满,则显然不能获得最大的效益值。
目标,使装入背包的物品的 总效益 达到 最大 。
ni iixw1
问题的形式描述目标函数,
约束条件,
可 行 解,满足上述约束条件的 任一 (x1,x2,…,x n) 都是问题的一个可行解 —— 可行解可能为多个。
(x1,x2,…,x n)称为问题的一个 解向量最 优 解,能够使目标函数取 最大值 的可行解是问题的最优解
—— 最优解也可能为多个。
ni
iixp
1
niwpx
Mxw
iii
ni
ii



1,0,0,10
1
例 3.1 背包问题的实例设,n=3,M=20,
(p1,p2,p3) = (25,24,15),(w1,w2,w3) = (18,15,10)。
可能的可行解如下:
(x1,x2,x3)
① (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
iixp? iixw
2,贪心策略求解度量标准的选择:三种不同的选择
1)以 目标函数 作为度量即,每装入一件物品,就使背包获得 最大 可能的 效益增量 。
该度量标准下的处理规则是:
● 按效益值的 非增 次序将物品一件件地放入到背包;
● 如果正在考虑的物品放不进去,则只取其一部分装满背包:如果该物品的一部分不满足获得最大效益增量的度量标准,则在 剩下 的物品种选择可以获得最大效益增量的其它物品,将它或其一部分装入背包。
如:若 Δ M=2,背包外还剩两件物品 i,j,且有 (pi= 4,wi= 4) 和 (pj
= 3,wj= 2),则下一步应选择 j而非 i放入背包:
pi/2 = 2 < pj= 3
实例分析(例 3.1)
∵ p1> p2> p3
∴ 首先将 物品 1放入背包,此时 x1= 1,背包获得 p1= 25的效益增量,同时背包容量减少 w1= 18个单位,剩余空间 Δ M=2。
其次考虑 物品 2和 3。就 Δ M=2而言有,只能选择物品 2或 3的 一部分 装入背包。
物品 2,若 x2= 2/15,则 p2 x2= 16/5= 3.1
物品 3,若 x3= 2/10,则 p3 x3= 3
为使背包的效益有最大的增量,应选择 物品 2的 2/15装包,即
x2= 2/15
最后,背包装满,Δ M=0,物品 3不装包,即 x3= 0 。
背包最终可以获得 效益值 = x1 p1 + x2 p2+ x3 p3
= 28.2 (次优解,非问题的最优解 )
2)以 容量 作为度量标准以 目标函数 作为度量标准所存在的问题:尽管背包的效益值每次得到了最大的增加,但背包容量也过快地被消耗掉了,从而不能装入,更多” 的物品。
改进:让背包 容量 尽可能慢地消耗,从而可以尽量装入
“较多”的物品。
即,新的标准是:以 容量 作为度量该度量标准下的处理规则:
● 按物品 重量的非降次序 将物品装入到背包;
● 如果正在考虑的物品放不进去,则只取其一部分装满背包;
实例分析(例 3.1)
∵ w3< w2 < w1
∴ 首先将 物品 3放入背包,此时 x3= 1,背包容量减少 w3= 10个单位,还剩余空间 Δ M=10。 同时,背包获得 p3= 15的效益增量 。
其次考虑 物品 2。就 Δ M=10而言有,也只能选择物品 2的 一部分装入背包。 下一步将放入 物品 2的 10/15装包,即
x2= 10/15= 2/3
最后,背包装满 Δ M=0,物品 1将不能装入背包,故 x1= 0 。
背包最终可以获得 效益值 = x1 p1 + x2 p2+ x3 p3
= 31 (次优解,非问题的最优解 )
存在的问题:效益值没有得到“最大程度”的增加
3)最优的度量标准影响背包效益值得因素:
背包的 容量 M
放入背包中的物品的重量及其可能带来的效益值可能的策略 是:在背包效益值的增长速率和背包容量消耗速率之间取得 平衡,即每次装入的物品应使它所占用的每一 单位容量 能获得当前最大的单位效益。
在这种策略下的 量度 是:已装入的物品的累计效益值与所用容量之比。
故,新的 量度标准 是:每次装入要使累计效益值与所用容量的比值有最多的增加(首次装入)和最小的减小(其后的装入)。
此时,将按照物品的单位效益值,pi/wi 比值的非增次序考虑。
实例分析(例 3.1)
∵ p1/w1< p3/w3 < p2/w2
∴ 首先,将 物品 2放入背包,此时 x2= 1,背包容量减少 w2= 15
个单位,还剩余空间 Δ M=5。同时,背包获得 p2= 24的效益增量 。
其次,在剩下的 物品 1和 3中,应选择物品 3,且就 Δ M=5而言有,
只能放入物品 3的 一部分 到背包中 。 即
x3= 5/10= 1/2
最后,背包装满 Δ M=0,物品 1将不能装入背包,故 x1= 0 。
最终可以获得的背包 效益值 = x1 p1 + x2 p2+ x3 p3
= 31.5 (最优解 )
3,背包问题的贪心求解算法算法 3.2 背包问题的贪心算法
procedure GREEDY- 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 // 将解向量初始化为 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
4,最优解的证明即证明:由第三种策略所得到的贪心解是问题的 最优解 。
最优解 的含义:在满足约束条件的情况下,使目标函数取极(大或小)值的可行解。
贪心解是 可行解,故只需证明:贪心解可使目标函数取得极值。
证明的 基本思想,
将此贪心解与(假设中的)任一最优解 相比较 。
● 如果这两个解相同,则显然贪心解就是最优解。
● 如果这两个解不同,就设法去找两者开始 不同 的第一个分量位置 i,
然后设法用贪心解的这个 xi去 替换 最优解对应的分量,并证明最优解在分量代换前后总的效益值没有任何变化 (且不违反约束条件 )。
● 然后比较二者。若还不同,则 反复进行代换,直到代换后产生的
“最优解”与贪心解完全一样。
● 在上述 代换中,最优解的 效益值没有任何损失,从而证明贪心解的效益值与代换前后最优解的效益值相同。即,贪心解如同最优解一样可取得目标函数的最大 /最小值。
从而得证:该 贪心解 即是问题的 最优解 。
定理 3.1 如果 p1/w1≥ p2/w2≥…≥ p n/wn,则算法 GREEDY-
KNAPSACK对于给定的背包问题实例生成一个最优解。
证明,
设 X=(x1,x2,…,x n)是 GREEDY-KNAPSACK所生成的 贪心解 。
① 如果所有的 xi都等于 1,则显然 X就是问题的 最优解 。否则,
② 设 j是使 xi≠1的最小下标。由算法的执行过程可知,
xi=1 1≤i< j,
0≤xj < 1
xi=0 j< i≤n
假设 Y是问题的最优解,Y=(y1,y2,…,y n) 且有:
● 若 X = Y,则 X就是最优解。否则,
● X和 Y至少在 1个分量上存在不同。
设 k是使得 yk≠ xk的最小下标,则有 yk< xk。可分以下情况说明:
a) 若 k<j,则 xk=1。因为 yk≠ xk,从而有 yk < xk
b) 若 k=j,由于,且对 1≤i< j,有 yi=xi=1,而对 j
< i≤n,有 xi= 0;故此时若 yk> xk,则将有,与 Y是可行解相矛盾。而 yk≠ xk,所以 yk < xk
c) 若 k> j,则,不能成立
Mxw ii
Myw ii
Myw ii
My iiw
在 Y中作以下调整:将 yk增加到 xk,因为 yk< xk,为保持解的可行性,
必须从 (yk+1,…,y n)中减去同样多的量。设调整后的解为 Z=(z1,z2,…,z n),
其中 zi= xi,1≤i≤k,且有:
Z的效益值有:
nik kkkiii yzwzyw )()(
ni iiinik iikkkkkiini ii wpwzywpwyzypzp 11 /)(/)(
ni kkinik iikkkii wpwzywyzyp1 /])()[(
ni ii yp1
差值= 0
由以上分析得,
若,则 Y将不是最优解;
若,则或者 Z=X,则 X就是最优解;
或者 Z≠X,则重复以上替代过程,或者证明 Y不是最优解,或者把 Y转换成 X,从而证明 X是最优解
iiii ypzp
iiii ypzp
3.3 带有限期的作业排序
1,问题描述假定在一台资源无约束的机器上处理 n个作业,每个作业均可在单位时间内完成;同时每个作业 i都有一个 截至期限 di>0,
当且仅当作业 i在其截至期限以前被完成时,则获得 pi>0的效益。
问题,求这 n个作业的一个子集 J,其中的所有作业都可在其截至期限内完成。 —— J是问题的一个可行解。
可行解 J中的 所有作业的效益之和是,具有 最大效益值之和 的可行解是该问题的最优解。
Ji i
p
分析:
目标函数:
约束条件,所有的作业都应在其期限之前完成
如果 所有 的作业都能在其期限之内完成则显然可以获得当前最大效益值;
否则,将有作业无法完成 —— 决策应该执行哪些作业,以获得最大可能的效益值。
Ji ip
例 3.2 n=4,(p1,p2,p3,p4)= (100,10,15,20)
(d1,d2,d3,d4)= (2,1,2,1)。
可行解如下表所示:
问题的最优解是 ⑦。所允许的 处理次序 是:先处理作业 4
再处理作业 1。
可行解 处理顺序 效益值
① ( 1) 1 100
② ( 2) 2 10
③ ( 3) 3 15
④ ( 4) 4 20
⑤ ( 1,2) 2,1 110
⑥ ( 1,3) 1,3或 3,1 115
⑦ ( 1,4) 4,1 120
⑧ ( 2,3) 2,3 25
⑨ ( 3,4) 4,3 35
t1 t2
d2,d4
d1,d3
1,带有限期的作业排序算法
1) 度量标准的选择以 目标函数 作为量度。
量度标准:下一个要计入的作业将是使得在满足所产生的 J是一个可行解的限制条件下让 得到最大增加的作业。
处理规则,● 按 pi的 非增次序 来考虑这些作业;
● 同时因受作业期限的限制,必须为作业安排合理的 处理顺序 。
Ji ip
Ji i
p
例:例 3.2求解
① 首先令 J=Φ,p1< p4 < p3 < p2
② 作业 1具有当前的最大效益值,且 {1}是可行解,所以作业 1计入 J(一般情况下,假定至少有一个时间单元)。
③ 在剩下的作业中,作业 4具有最大效益值,且 {1,4}也是可行解,故作业 4计入 J,即 J={1,4};
④ 考虑 {1,3,4}和 {1,2,4}均不能构成新的可行解,作业 3和 2
将被舍弃。
故,最后的 J={1,4},加工顺序是,4,1。
最终效益值= 120(问题的 最优解 )
0
Ji i
p
2)作业排序算法的概略描述算法 3.3
procedure GREEDY-JOB(D,J,n)
//作业按 p1≥p2≥…≥p n的次序输入,它们的期限值 D(i)≥1,
1≤i≤n,n≥1。 J是在它们的截止期限前完成的作业的集合 //
J←{1} //用作业序号代表作业 //
for i←2 to n do
if J∪ {i}的 所有作业 能在它们的 截止期限前 完成
then J←J ∪ {i}
endif
repeat
end GREEDY-JOB
2,最优解证明定理 3.2 算法 3.3对于作业排序问题的解是问题的一个 最优解证明,
设 J是由算法 3.3所得的作业集合 —— 贪心解,
I是一个 最优解 的作业集合。
① 若 I=J,则 J就是最优解;否则
②,则至少存在两个作业 a和 b,使得 a∈ J
且,b∈ I且 。(为什么)
并设 a是 这样 的一个具有 最高效益值 的作业
(由算法的处理规则可得:对于在 I中而不在 J中的作业所有 b,有,pa≥pb)
Ia? Jb?
JI?
设 SJ和 SI分别是 J和 I的可行的 调度表 。因为 J和 I都是可行解,故这样的调度表一定存在;
设 i是既属于 J又属于 I地一个作业,并设 i在调度表 SJ中的调度时刻是 [t,t+1],而在 SI中的调度时刻是 [t’,t’+1]。
在 SJ和 SI中作如下调整:
● 若 t< t’,则将 SJ中在 [t’,t’+1]时刻调度的那个作业
(如果有的话)与 i相交换。如果 J中在 [t’,t’+1]时刻没有作业调度,则直接将 i移到 [t’,t’+1]调度。 —— 新的调度表也是可行的。
……i.………………k……
……h.………………i………
SJ:
SI:
t t’
● 若 t’< t,则在 SI中作类似的调换,即将 SI中在 [t,t+1]时刻调度的那个作业(如果有的话)与 i相交换。如果 I中在 [t,
t+1]时刻没有作业调度,则直接将 i移到 [t,t+1]调度。 —— 同样,新的调度表也是可行的。
对 J和 I中共有的所有作业作上述的调整。设调整后得到的调度表为 S’J和 S’I,则在 S’J和 S’I中 J和 I中所有的 共有作业 将在 相同时间 被调度。
……k.………………i……
……i.………………h………
SJ:
SI:
t’ t
设 a在 S’J中的调度时刻是 [ta,ta+1],b是 S’I中该时刻调度的作业,则有 pa≥pb(为什么?)。
在 S’I中,去掉作业 b,而去调度作业 a,得到的是作业集合 I’=( I-{b}) ∪ {a}的 一个可行的调度表,且 I’的效益值不小于 I的效益值。而 I’中比 I少了一个与 J不同的作业。
重复上述的转换,可使 I在 不减 效益值的情况下转换成 J。
从而 J至少有和 I一样的效益值。所以 J也是最优解。
证毕。
…….……….a……………
……..………b……………
SJ:
SI:
ta
3,如何判断 J的可行性方法一,枚举法,检验 J中作业所有可能的排列,对于任一种次序排列的作业序列,判断这些作业是否能够在其期限前完成
—— 若 J中有 k个作业,则将要检查 k!个序列判断策略:
对于一个给定 作业处理序列 σ = i1i2?i k,由于作业 ij完成的 最早时间 是 j,因此只要判断出 σ 排列中的每个作业的期限有 dij≥j,就可得知 σ 是一个允许的调度序列,从而 J是一可行解。
反之,如果 σ 排列中有一个作业有 dij< j,则 σ 将是一个行不通的调度序列,因为至少作业 ij不能在其期限之前完成。
方法二,检查 J中作业的一个 特定序列 就可判断 J的可行性:
这一特定序列是按照 作业期限 的 非降次序 排列的作业序列定理 3.3 设 J是 k个作业的集合,σ= i1i2…i k是 J中作业的一种排列,它使得 di1≤di2≤…≤d ik。则
J是一个可行解,当且仅当 J中的作业可以按照 σ
的次序而又不违反任何一个期限的情况来处理 。
证明:
① 如果 J中的作业可以按照 σ的次序而又不违反任何一个作业期限的情况来处理,则 J是一个 可行解
② 现证 若 J是一个可行解,σ是否代表一个可行的调度序列?
∵ J是可行解
∴ 必存在一可行的调度序列 σ’= r1r2…r k,使得 drj≥ j,
1≤j≤k。
★ 若 σ= σ’,则 σ即是可行的调度序列。否则,
★ σ≠σ’,令 a是使得 ra≠ia的最小下标(如下图)
σ= i1 i2 … ia … … ic … ik
σ’= r1 r2 … ra … rb … r k
设 rb=ia。则有:
b> a 且 dra≥drb
故,在 σ’中调换 ra与 rb,所得的新序列 σ’’= s1s2…s k的处理次序不违反任何一个期限,而 σ’’中位置 a及 a之前的作业均与 σ相同。
重复 上述过程,则可将 σ’转换成 σ且不违反任何一个期限,
故 σ是一个可行的调度序列故定理得证 。
4,带有限期的作业排序算法的实现对当前正在考虑的作业 j,按限期大小采用一种,插入排序” 的方式,
尝试将其“插入”到一个按限期从小到大顺序构造的作业调度序列中,
以此判断是否能够将作业 j合并到当前部分解 J中:
如果有合适的插入位置,则插入到序列中,形成新的可行解序列。
否则,舍弃该作业。
具体如下:
假设 n个作业已经按照效益值从大到小的次序,即 p1≥p2≥…≥p n的顺序排列好,每个作业可以在 单位时间 内完成,并具有相应的时间期限 di;
且至少有一个单位时间可以执行作业首先,将作业 1计入部分解 J中,此时 J是可行的;
然后,依次考虑作业 2到 n。假设已经处理了 i-1个作业,其中有 k个作业计入了部分解 J中,J(1),J(2),…,J(k),且有
D(J(1))≤D(J(2))≤…≤D(J(k))
对当前正在考虑的作业 i,将 D(i)依次和 D(J(k)),D(J(k-1)),?,D(J(1))相比较。若
1)找到位置 q:使得
★ D(i)< D(J(l)),q< l≤k,且
★ D(J(q))< D(i)
★ q < D(i)
此时,若 D(J(l))> l,q< l≤k,即 q位置之后的所有作业均可推迟 一个单位时间执行,而又不违反各自的执行期限。
执行“移位”处理:将 q位置之后的所有作业后移一位,将作业 i插入 到位置 q+ 1处,从而得到一个包含 k+1个作业的新的可行解。
2)若找不到这样的位置 q,作业 i将被舍弃。
对 i之后的其它作业重复上述过程,直到 n个作业处理完毕。 最后 J中所包含的作业集合是此时算法的贪心解,根据定理 3.2,也是问题的最优解。
算法 3.4 带有限期和效益的单位时间的作业排序贪心算法
procedure JS(D,J,n,k)
//n≥1。作业已按 p1≥p2≥…≥p n的顺序排序。 D(1),…,D(n) 是期限值,J(i)是最优解中的第 i个作业,1≤i≤k。终止时,D(J(i))≤D(J(i+ 1)),1≤i< k//
integer D(0:n),J(0:n),i,k,n,r
D(0)←J(0)←0 // 初始化 //
k←1;J(1)←1 // 计入作业 1//
for i←2 to n do //按 p的非增次序考虑作业 i。找 i的插入位置并检查可行性 //
r←k
while D(J(r))> D(i) and D(J(r)) ≠r do // D(J(r)) ≥r//
r←r -1 //这样的 r有 D(J(r)) > r//
repeat
If D(J(r))≤D(i) and D(i)> r then //把 i插入到 J中 //
for i←k to r+1 by -1 do
J(i+1) ←J(i) // 将插入点的作业后移一位 //
repeat
J(r+1) ←i; k←k+1 // 将 i计入 J中 //
endif
repeat
end JS
另一退出条件是
D(J(r))> D(i) 而
D(J(r)) =r
计算时间分析
for i←2 to n do? 将循环 n-1次------------------ ①
r←k
while D(J(r))> D(i) and D(J(r)) ≠r do?至多循环 k次,
k是当前计入 J中的作业数
1≤ k≤n -1 --------- ②
r←r -1
repeat
If D(J(r))≤D(i) and D(i)> r then
for i←k to r+1 by -1 do?循环 k-r次,r是插入点之前的位置最坏情况下循环 k次,插入到最头部-- ③
J(i+1) ←J(i)
repeat
J(r+1) ←I;k←k+1
endif
repeat
设 s是最终计入 J中的作业数,则算法 JS所需要的总时间是 O(sn)。 s≤n,故最坏情况,TJS = О(n2),特例情况,pi=di=n-i+1,1≤i≤n
最好情况,TJS = О(n),特例情况,pi=n-i+1,di=i,1≤i≤n
6,一种“更快”的作业排序问题判定作业 i可行的另一种方法:
对于作业 i,若还没有给 i分配处理时间,则分配给它时间片
[α-1,α],其中 α是尽量取大且 [α-1,α]为空的时间片。
即:尽量推迟作业 i的处理时间。这样在安排作业处理次序时不必每有一个作业加入就需移动 J中已有的作业。
如果不存在这样的时间片,作业 i被舍弃。
(如何按照该方法改进算法?)
使用不相交集合的 UNION和 FIND算法(见 1.4.3节),可以将 JS的计算时间降低到数量级接近 О(n)。
(略)
3.4 最优归并模式
1,问题的描述
1)两个文件的归并问题两个已知文件的一次归并所需的计算时间= O(两个文件的元素总数 )
例:
n个记录的文件
+ (n+m) 个记录的文件
m个记录的文件 О(n+m)
2)多个文件的归并已知 n个文件,将它们归并成一个单一的文件例:假定文件 X1,X2,X3,X4,采用两两归并的方式,可能的归并模式有:
① X1+X2=Y1+X3= Y2+X4= Y3
② X1+X2 = Y1
+ → Y3
X3+X4=Y2
二路归并模式:每次仅作两个文件的归并;当有多个文件时,采用两两归并的模式,最终得到一个完整的记录文件。
二元归并树:二路归并模式的归并过程可以用一个二元树的形式描述,称之为二元归并树。

60
50
30 20
10
X1
Z1
X
X3
X2
数字代表文件长度归并树的构造
外结点,n个原始文件
内结点:一次归并后得到的文件
在两路归并模式下,每个内结点刚好有两个儿子,代表把它的两个儿子表示的文件归并成其本身所代表的文件
★ 不同的归并顺序所需的计算时间是不同的。
例 3.5 已知 X1,X2,X3是分别为 30,20,10个记录长度的已分类文件。
将这 3个文件归并成长度为 60的文件。可能的归并过程和相应的记录移动次数如下:
问题:采用怎样的归并顺序才能使归并过程中元素的移动次数最小(或执行的速度最快)
X
X3
X2
X1
移动 50次移动 60次
X
X1
X2
X3
移动 30次移动 60次总移动次数,110次总移动次数,90次
2,贪心求解
1) 度量标准的选择
★ 任意两个文件的归并所需的 元素移动次数 与这两个文件的 长度之和 成 正比;
★ 度量,目标函数(元素移动数之和);
★ 度量标准,每次归并使目标函数有最小的增加;
★ 处理规则,每次选择长度最小的两个文件进行归并。
95
35
5
20 30
60
3015
10
F4 F3
Z1
Z2
Z4
Z3
F1 F5 F2
(F1,F2,F3,F4,F5) = (20,30,10,5,30)
2) 目标函数的定义目标,元素移动的次数最少分析:为得到归并树根结点表示的归并文件,外部结点中每个文件的记录需要移动的次数=该外部结点到根的距离,即根到该外部结点路径的长度。如,
F4,
则 F4中所有记录在整个归并过程中移动的总量= |F4|*3
带权外部路径长度,记 di是由根到代表文件 Fi的外部结点的距离,qi是 Fi的长度,则这棵树所代表的归并过程中元素移动总量是:
,称为这棵树的 带权外部路径长度最优的二路归并模式,与一棵具有 最小带权外部路径长度 的二元归并树相对应。
F4 Z1 Z2 Z4
ni iidq1
算法 3.6 生成二元归并树的算法
procedure TREE(L,n)
//L是 n个单结点的二元树表 //
for i←1 to n -1 do
call GETNODE(T) //构造一颗新树 T//
LCHILD(T) ←LEAST(L) //从表 L中选当前根 WEIGHT最小的树,
并从中删除 //
RCHILD(T) ←LEAST(L)
WEIGHT(T) ←WEIGHT(LCHILD(T))+WEIGHT(RCHILD(T))
call INSERT(L,T) //将归并的树 T加入到表 L中 //
repeat
return (LEAST(L)) //此时,L中的树即为归并的结果 //
end TREE
例 3.6 已知六个初始文件,长度分别为,2,3,5,7,9,13。
采用算法 TREE,各阶段的工作状态如图所示:
L迭代
2 3 5 7 9 130
2 3
5 7 9 131 5
2 3
5 7 9 132 5
10
2 3
5 7 9 133 5
10 16
2 3
5 7 9
13
4
5
10 16
23
2 3
5
7 9 13
5
5
10
16 23
39
时间分析
1) 循环体,n-1次
2) L以有序序列表示
LEAST(L),О(1)
INSERT(L,T),О(n)
总时间,О(n2)
3) L以 min-堆表示
LEAST(L),О(logn)
INSERT(L,T),О(logn)
总时间,О(nlogn)
3,最优解的证明定理 3.4 若 L最初包含 n≥1个单结点的树,这些树有 WEIGHT值为
(q1,q2,…,q n),则算法 TREE对于具有这些长度的 n个文件生成一棵最优的二元归并树。
证明,归纳法 证明
① 当 n=1时,返回一棵没有内部结点的树。定理得证。
② 假定算法对所有的 (q1,q2,…,q m),1≤m< n,生成一棵最优二元归并树。
③ 对于 n,假定 q1≤q2≤…≤q n,则 q1和 q2将是在 for循环的第一次迭代中首先选出的具有最小 WEIGHT值的两棵树(的 WEIGHT值);如图所示,
设 T是由这样的两棵树构成的子树:
q1 q2
q1+q2T
■ 设 T’是一棵对于 (q1,q2,…,q n)的 最优二元归并树 。
■ 设 P是 T’中距离根 最远 的一个 内部结点 。
若 P的两棵子树不是 q1和 q2,则用 q1和 q2代换 P当前的子树而不会增加 T’的带权外部路径长度。
故,T应是最优归并树中的子树。
则在 T’中用一个权值为 q1+ q2的外部结点代换 T,得到的是一棵关于 (q1+ q2,…,q n)最优归并树 T”。
而由归纳假设,在用权值为 q1+ q2的外部结点代换了 T之后,
过程 TREE将针对 (q1+ q2,…,q n)得到一棵最优归并树。将 T带入该树,根据以上讨论,将得到关于 (q1,q2,…,q n)的最优归并树。
故,TREE生成一棵关于 (q1,q2,…,q n)的 最优归并树 。
q1 q2
q1+q2T
·········
T’’
T’ F(T’) = F(T’’) + q1 + q2
则,
Fmin(T’) = min(F(T’))
= min(F(T’’) + q1 + q2)
= min(F(T’’)) + q1 + q2
= Fmin(T’’) + q1 + q2
5,k路归并模式
每次 同时 归并 k个文件。
k元归并树:可能需要增加,虚” 结点,以补充不足的外部结点。
★ 如果一棵树的所有内部结点的度都为 k,则外部结点数 n满足 n mod (k-1) = 1
★ 对于满足 n mod (k- 1) =1的整数 n,存在一棵具有 n
个外部结点的 k元树 T,且 T中所有结点的度为 k。
至多需要增加 k-2个外部结点。
k路最优归并模式的贪心规则:每一步选取 k个具有最小长度的文件归并。
3.5 最小生成树
1,问题的描述生成树,设 G=(V,E)是一个无向连通图。如果 G的生成子图 T=(V,E')是一棵树,则称 T是 G的一棵生成树 (spanning tree).
最小生成树,带有成本的图的生成树问题
2,贪心策略度量标准:选择能使迄今为止所计入的边的 成本和 有 最小增加 的那条边。
● Prim算法
● Kruskal算法
3,Prim算法策略:使得迄今所选择的边的集合 A构成一棵树;则将要计入到 A中的下一条边 (u,v),应是 E中一条当前不在 A中且使得 A∪{(u,v)} 也是一棵树的最小成本边。
1
4
6
2
5
3
10
30
20
45
25 55
40
50
15
35
1 2
1
6
2
1
6
2 3
1
6
2 3
4

(1,2)
(2,6)
(3,6)
(6,4)
成本
10
25
15
20
1
4
6
2
5
3
10
20
25
15
35边
(3,5)
成本
35
V(TP) = {1,2,3,4,5,6}
E(TP) = { (1,2),(2,6),(3,5),(4,6),(3,6) }
算法 3.7 Prim最小生成树算法
procedure PRIM(E,COST,n,T,mincost)
//E是 G的边集。 COST(n,n)是 n结点图 G的 成本邻接矩阵,矩阵元素 COST(i,j)
是一个正实数,如果不存在边 (i,j),则为+ ∞。计算一棵最小生成树并把它作为一个集合存放到数组 T(1:n-1,2)中 (T(i,1),T(i,2))是最小成本生成树的一条边。
最小成本生成树的总成本最后赋给 mincost//
real COST(n,n),mincost
integer NEAR(n),n,i,k,l,T(1:n-1,2)
(k,l)← 具有最小成本的边
mincost←COST(k,l)
(T(l,1),T(l,2)) ←(k,l)
for i←1 to n do // 将 NEAR置初值 //
if COST(i,l) < COST(i,k) then NEAR(i)←l
else NEAR(i) ←k
endif
repeat
NEAR(k)←NEAR(l)←0
for i←2 to n -1 do //找 T的其余 n-2条边 //
设 j是 NEAR(j)≠0 且 COST(j,NEAR(j))最小的下标
(T(i,1),T(i,2))←(j,NEAR(j))
mincost←mincost+COST(j,NEAR(j))
NEAR(j)←0
for k←1 to n do // 修改 NEAR//
if NEAR(k)≠0 and COST(k,NEAR(k))> COST(k,j)
then NEAR(k)←j
endif
repeat
repeat
if mincost> ∞ then print(‘no spanning tree’) endif
end PRIM
计算复杂性,)(
2n?
4,Kruskal算法
(连通)图的边按成本的非降次序排列,下一条计入生成树 T中的边是还没有计入的边中具有最小成本、且和 T中现有的边不会构成环路的边。
1
4
6
2
5
3
10
30
20
45
25 55
40
50
15
35
1 2
1
6
2
1
6
2 3
1
6
2 3
4

(1,2)
(3,6)
(4,6)
(2,6)
成本
10
15
20
25
1
6
2
3 4 5
63 4 5
3 4 5
4
5
5
1
4
6
2
5
3
10
20
25
15
35边
(3,5)
成本
35
V(TK) = {1,2,3,4,5,6}
E(TK) = { (1,2),(2,6),(3,5),(4,6),(3,6) }
算法 3.9 Kruskal算法
procedure KRUSKAL(E,COST,N,T,mincost)
//G有 n个结点,E是 G的边集。 COST(u,v)是边 (u,v)的成本。 T是最小成本生成树的边集,mincost是它的成本 //
real mincost,COST(1:n,1:n); integer PARENT(1:n),T(1:n-1,2),n
以边成本为元素构造一个 min堆
PARENT←1// 每个结点都在不同的集合中 //
i←mincost←0
while i< n-1 and 堆非空 do
从堆中删去最小成本边 (u,v)并重新构造堆
j←FIND(u); k←FIND(v)
if(j≠k) then i←i+1
T(i,1) ←u; T(i,2) ←v
mincost←mincost + COST(u,v)
call UNION(j,k)
endif
repeat
if i≠n-1 then print(‘no spanning tree’) endif
return
end KRUSKAL
注:
● 边集以 min-堆的形式保存,一条当前最小成本边可以在
О(loge)的时间内找到;
● 当且仅当图 G是不连通的,i≠n -1;此时算法具有最坏的执行时间;
● 算法的计算时间是 О(eloge)
实验内容
单源最短路径算法的完善和实现
书上的单源最短路径算法仅求出了从单源点到其它所有结点的最短路径长度。在此基础上,扩充算法功能,使得新算法在找出这些最短路径长度的同时,也能求出路径上的结点序列。
要求:
给出新算法的描述
用 C语言编写该算法的程序
用书上的实例(或自行设计测试数据)测试程序,输出测试结果。
基本形式如下:
start end length nodes list
v1 v2 20 v1v2
v1 v4 30 v1v4
v1 v6 80 v1 v2v3v5v6
交实验报告实验内容、算法描述、程序设计、结果测试及分析等
3.6 单源最短路径
1,问题描述
最短路径问题:
● 每对结点之间的路径问题
● 特定线路下的最短路径问题
● 单源最短路径问题等
单源最短路径问题已知一个 n结点有向图 G=(V,E)和边的权函数 c(e),
求由 G中某指定结点 v0到其它各结点的最短路径。
假定边的权值为正。
例 3.10 如图所示。设 v0是起始点,求 v0到其它各结点的最短路径。
路径 长度
(1) v0v2 10
(2) v0v2v3 25
(3) v0v2v3v1 45
(4) v0v4 45
注:路径按照长度的 非降次序 给出
v0
v1
v4
v5v3v2
45
50 10
15
20 10
15 3
3520 30
2,贪心策略求解
1) 度量标准量度的选择:迄今已生成的 所有路径长度之和 —— 为使之达到最小,其中任意一条路径都应具有“最小”长度:
假定已经构造了 i条这样的最短路径,则下一条要构造的路径应是 下一条最短 的路径。
处理规则:按照 路径长度 的 非降次序 依次生成从结点 v0到其它各结点的最短路径。
例:
v0→v 2
v0→v 2→v 3
v0→v 2→v 3→v 1
v0→v 4
问题:如何对尚未生成的路径长度进行排序,以确定其中最短者?
2) 贪心算法
设 S是已经对其生成了最短路径的 结点集合 (包括 v0)。
对于当前不在 S中的结点 w,记 DIST(w)是从 v0开始,
只经过 S中的结点而在 w结束的那条最短路径的长度。
则有,SW
① 如果下一条最短路径是到结点 u,则这条路径是从结点 v0出发,
在 u处终止,且 只经过 那些在 S中的结点,即由 v0至 u的这条最短路径上的所有 中间结点 都是 S中的结点,证明如下:
设 w是这条路径上的任意中间结点,则从 v0到 u的路径也包含了一条从 v0到 w的路径,且其长度小于从 v0到 u的路径长度。
v0,s1,s2,?,w,?,s m-1,u
均在 S中根据 生成规则,最短路径是按照路径长度的非降次序生成的,因此从 v0到 w的最短路径应该已经生成。从而 w也应该在 S中。
故,不存在 不在 S中的中间结点。
② 所生成的下一条路径的终点 u必定是所有不在 S内的结点中具有最小 DIST(u)值的结点。
③ 如果选出了这样结点 u并生成了从 v0到 u的最短路径之后,结点 u将成为
S中的一个成员。此时,那些从 v0出发,只经过 S中的结点并且在 S外的结点 w处结束的最短路径长度可能会 减小 —— DIST(w)的值变小:
这些长度发生改变的路径,必定是一条从 v0出发,经过 u然后到 w的更短 的路径。
★ 根据 DIST(w)的定义,DIST(w)所表示的最短路径上,所有中间结点都在 S中;故只考虑 <u,w>∈E 和 的情况
★ 对于从 v0至 w,且经过最后一个中间结点为 u的最短路径,有:
DIST(w) = DIST(u) + c(u,w)
★ 随着 u的加入,DIST(w)调整为
DIST(w) = min(DIST(w),DIST(u) + c(u,w))
Ewu,
S W
uv0
算法 3.10 生成最短路径的贪心算法
procedure SHORTEST-PATHS(v,COST,DIST,n)
//G是一个 n结点有向图,它由其成本邻接矩阵 COST(n,n)表示。 DIST(j)被置从结点 v到结点 j的最短路径长度,这里 1≤j≤n。 DIST(v)被置成零 //
boolean S(1:n);real COST(1:n,1:n),DIST(1:n)
integer u,v,n,num,i,w
for i←1 to n do // 将集合 S初始化为空 //
S(i) ←0;DIST(i) ←COST(v,i) //若,则 DIST(i)=∞ //
repeat
S(v) ←1;DIST(v) ←0 // 结点 v计入 S//
for num←2 to n -1 do //确定由结点 v出发的 n-1条路 //
选取结点 u,它使得 DIST(u)=
S(u) ←1 // 结点 u计入 S//
for 所有 S(w)= 0的结点 w do //修改 DIST(w)//
DIST(w) = min(DIST(w),DIST(u) + COST(u,w))
repeat
repeat
end SHORTEST-PATHS
)}({m in 0)( wD IS TwS?
Eiv,
3) 计算时间
⑴ 算法 3.10的计算时间是,О(n2)
⑴ for i←1 to n do
S(i) ←0;DIST(i) ←COST(v,i)
repeat
⑵ for num←2 to n -1 do
选取结点 u,它使得 DIST(u)=
S(u) ←1
for 所有 S(w)= 0的结点 w do
DIST(w) = min(DIST(w),DIST(u) + COST(u,w))
repeat
repeat
⑵ 最短路径算法的时间复杂度由于任何一条边 都有可能 是最短路径中的边,所以求任何最短路径的算法都必须至少检查图中的每条边一次,所以这样的算法的最小时间是 О(e)。
邻接矩阵表示的图,О(n2)
)}({m in 0)( wD IS TwS?
)(n?
)2( -n?
)(n?
)(n?
例 3.11 求下图中从 v1出发到其余各结点的最短路径
v1
v2
v4
v3
v5
v6 v7
20
50
25 70
50
70
10
50
55
40
30 25
图的成本邻接矩阵:
0 20 50 30 +∞ +∞ +∞
+∞ 0 25 +∞ +∞ 70 +∞
+∞ +∞ 0 40 25 50 +∞
+∞ +∞ +∞ 0 55 +∞ +∞
+∞ +∞ +∞ +∞ 0 10 70
+∞ +∞ +∞ +∞ +∞ 0 50
+∞ +∞ +∞ +∞ +∞ +∞ 0
算法的执行轨迹描述迭代 选取的结点
S DIST
(1) (2) (3) (4) (5) (6) (7)
置初值
1
2
3
4
5

2
4
3
5
6
1
1,2
1,2,4
1,2,4,3
1,2,4,3,5
1,2,4,3,5,6
0 20 50 30 +∞ +∞ +∞
0 20 45 30 +∞ 90 +∞
0 20 45 30 85 90 +∞
0 20 45 30 70 90 +∞
0 20 45 30 70 90 140
0 20 45 30 70 90 130
算法的执行在有 n-1个结点加入到 S中后终止,此时求出了 v0至其它各结点的最短路径。
问题:如何求出所有这些最短路径?
3,最短路径生成树对于无向连通图 G,由结点 v到其余各结点的最短路径的边构成 G的一棵 生成树,称为 最短路径生成树 。
注:不同起点 v的生成树可能不同。
2
1
3
5
6
7
4 8
55
5
25
40
35
15
10
5020
45
30
2
1
3
5
6
7
4 8
5
25
40
15
10
20
30
2
1
3
5
6
7
4 8
55
5
25
15
10
45
30
原始图 由结点 1出发的最短路径生成树 最小成本生成树实验 2
算法 3.10 SHORTEST-PATHS求出了 v0至其它各结点的最短路径,但是没有给出这些最短路径。
补充算法 3.10使之能够求出这些最短路径。
要求:
1)对上述算法编制程序
2)编制测试数据,验证程序的计算结果
3)输出 v0到其它各结点的最短路径的长度及其结点序列。
上机时间
第 10周星期天 (11.2) 晚上 1-6班
第 10周星期二 (11.4) 下午 7-12班
第 11周星期三 (11.12) 上午 1-6班
下午 7-12班