贪心算法基础

本文开始记录贪心算法的学习。

贪心算法的例子——活动选择问题

输入:S={1,2,...,n}S=\{1,2,...,n\}nn项活动的集合,sis_ifif_i分别为活动ii的开始和结束时间。

活动iijj相容     sifj\iff s_i \ge f_jsjfis_j \ge f_i

求:最大的两两相容的活动集AA

输入实例:

ii 1 2 3 4 5 6 7 8 9 10
sis_i 1 3 2 5 4 5 6 8 8 2
fif_i 4 5 6 7 9 9 10 11 12 13

解:{1,4,8}\{1,4,8\}

贪心算法

贪心算法(greedy algorithm)的挑选过程是多步判断,每步依据某种“短视”的策略进行选择,选择时注意满足条件相容性条件。

策略1:开始时间早的优先排序使s1s2...sns_1\le s_2 \le ... \le s_n,从前向后挑选;

策略2:占用时间少的优先排序使得f1s1f2s2...fnsnf_1-s_1 \le f_2 - s_2 \le ... \le f_n - s_n,从前向后挑选。

策略3:结束早的优先排序使得f1f2...fnf_1 \le f_2 \le ... \le f_n,从前向后挑选。

策略1的反例

策略1:开始早的优先

反例:S={1,2,3}S=\{1,2,3\}

s1=0,f1=20,s2=2,f2=5,s3=8,f3=15s_1=0,f_1=20,s_2=2,f_2=5,s_3=8,f_3=15

strategy 1

策略2的反例

策略2:占时少的优先

反例:S={1,2,3}S=\{1,2,3\}

s1=0,f1=8,s2=7,f2=9,s3=8,f3=15s_1=0,f_1=8,s_2=7,f_2=9,s_3=8,f_3=15

strategy 2

策略3伪码

算法:Greedy Select

输入:活动集S,si,fi,i=1,2,...,n,f1...fnS, s_i, f_i, i=1,2,...,n, f_1 \le ... \le f_n

输出:ASA \subseteq S,选中的活动子集

  1. nlength[S]n \gets length[S]

  2. A{1}A \gets \{1\}

  3. j1j \gets 1

  4. for i2i \gets 2 to nn do

  5. ifsifj\quad \quad if \quad s_i \ge f_j

  6. thenAA{i}\quad \quad then \quad A \gets A \cup \{i\}

  7. ji\quad \quad \quad \quad \quad j \gets i

  8. return A

完成事件 t=max{fk:kA}t=\max \{f_k: k \in A\}

运行实例

输入:S={1,2,...,10}S=\{1,2,...,10\}

ii 1 2 3 4 5 6 7 8 9 10
sis_i 1\fcolorbox{red}{aqua}{1} 3 0 5\fcolorbox{red}{aqua}{5} 3 5 6 8\fcolorbox{red}{aqua}{8} 8 2
fif_i 4\fcolorbox{red}{aqua}{4} 5 6 7\fcolorbox{red}{aqua}{7} 8 9 10 11\fcolorbox{red}{aqua}{11} 12 13

解:A={1,4,8},t=11A=\{1,4,8\},\quad t=11

时间复杂度:O(nlogn)+O(n)=O(nlogn)O(n \log n) + O(n)=O(n\log n)

贪心算法的特点

设计要素:

  1. 贪心算法适用于组合优化问题;

  2. 求解过程是多步判断过程,最终的判断序列对应于问题的最优解;

  3. 依据某种“短视”的贪心选择性质判断,性质好坏决定算法的成败;

  4. 贪心法必须进行正确性证明;

  5. 证明贪心法不正确的技巧:举反例。

贪心算法的正确性证明

一个数学归纳法的例子

例:证明对于任何自然数nn1+2+...+n=n(n+1)/21+2+...+n=n(n+1)/2

n=1n=1,左边=1=1,右边=1×(1+1)/2=1=1 \times (1+1)/2=1

假设对任意自然数nn等式成立,则

1+2+...+(n+1)=(1+2+...+n)+(n+1)=n(n+1)/2+(n+1)=(n+1)(n/2+1)=(n+1)(n+2)/2\large 1+2+...+(n+1) \\ =(1+2+...+n)+(n+1) \\ =n(n+1)/2 + (n+1) \\ =(n+1)(n/2+1) \\ =(n+1)(n+2)/2

第一数学归纳法

适合证明涉及自然数的命题P(n)P(n)

归纳基础:证明P(1)P(1)为真(或者P(0)P(0)为真)

归纳步骤:若对所有nnP(n)P(n)为真,证明P(n+1)P(n+1)为真

n,P(n)P(n+1)P(1)n=1,P(1)    P(2)n=2,P(2)    P(3)...\forall n, P(n) \to P(n+1) \\ \quad \quad \quad P(1) \\ n=1,\quad P(1) \implies P(2) \\ n=2,\quad P(2) \implies P(3) \\ \quad \quad \quad ...

第二数学归纳法

适合证明涉及自然数的命题P(n)P(n)

归纳基础:证明P(1)P(1)为真(或P(0)P(0)为真)

归纳步骤:若对所有小于nnkkP(k)P(k)真,证明P(n)P(n)为真

k(k<nP(k))P(n)P(1)n=2,P(1)    P(2)n=3,P(1)P(2)    P(3)...\forall k(k<n \land P(k)) \to P(n) \\ \quad \quad \quad P(1) \\ n=2, P(1) \implies P(2) \\ n=3, P(1) \land P(2) \implies P(3) \\ \quad \quad \quad ...

两种归纳法的区别

归纳基础一样:P(1)P(1)为真

归纳步骤不同:

证明逻辑

logic

算法正确性归纳证明

证明步骤:

  1. 叙述一个有关自然数nn的命题,该命题断定该贪心策略的执行最终将导致最优解。其中自然数nn可以代表算法步数或问题规模。

  2. 证明命题对所有的自然数为真。

    归纳基础(从最小实例规模开始)

    归纳步骤(第一或第二数学归纳法)

活动选择算法的命题

命题:

算法Select执行到第kk步,选择kk项活动

i1=1,i2,...,ik\quad \quad i_1=1,i_2,...,i_k

则存在最优解AA包含活动i1=1,i2,...,iki_1=1,i_2,...,i_k

根据上述命题:对于任何kk,算法前kk步的选择都将导致最优解,至多到第nn步将得到问题实例的最优解。

归纳证明:归纳基础

S={1,2,...,n}S=\{1,2,...,n\}是活动集,且fi...fnf_i\le ...\le f_n

归纳基础:k=1k=1,证明存在最优解包含活动11

证: 任取最优解A,A中的活动按截止时间递增排列。如果AA的第一个活动为j,jj,j \not 1,用11替换AA的活动j得到解AA',即

A=(A{j}){1}\quad \quad \quad A' = (A-\{j\}) \cup \{1\}

由于f1fjf_1 \le f_jAA'也是最优解,且含有1。

归纳步骤

假设命题对kk为真,证明对k+1k+1也为真

证明:算法执行到第k步,选择了活动i1=1,i2,...,iki_1=1,i_2,...,i_k,根据归纳假设存在最优解A包含i1=1,i2,...,iki_1=1,i_2,...,i_kAA中剩下活动选自集合SS'

S={iiS,sifk}A={i1,i2,...,ik}BS'=\{i|i \in S, s_i \ge f_k\} \\ A=\{i_1,i_2,...,i_k\} \cup B

BBSS'的最优解。(若不然,SS'的最优解为BB^*BB^*的活动比B多,那么

B{1,i2,...,ik}\quad \quad \quad B^* \cup \{1,i_2,...,i_k\}

SS的最优解,且比AA的活动多,与AA的最优性矛盾。)

SS'看成子问题,根据归纳基础,

存在SS'的最优解BB'SS'中的第一个活动ik+1i_{k+1},且B=B|B'|=|B|,于是

{i1,i2,...,ik}B={i1,i2,...,ik,ik+1}(B{ik+1})\{i_1,i_2,...,i_k\} \cup B' \\ =\{i_1,i_2,...,i_k,i_{k+1}\} \cup (B'-\{i_{k+1}\})

也是原问题的最优解。

最优装载问题

问题:nn个集装箱1,2,...,n1,2,...,n装上轮船,集装箱ii的重量wiw_i,轮船装载限制为CC,无体积限制。问如何装使得上船的集装箱最多?不妨设每个箱子的重量wiCw_i\le C

算法设计

  • 贪心策略:轻者优先

  • 算法设计

    将集装箱排序,使得

    w1w2...wnw_1 \le w_2 \le ... \le w_n

    按照标号从小到大装箱,直到装入下一个箱子使得集装箱总重量超过轮船装载重量限制,则停止。

正确性证明思路

  • 命题:对装载问题任何规模为nn的输入实例,算法得到最优解。

  • 设集装箱从轻到重记为1,2,...,n1,2,...,n

    归纳基础 证明对任何只含1个箱子的输入实例,贪心法得到最优解,显示正确。

  • 归纳步骤 证明:假设对于任何n个箱子的输入实例贪心法都能得到最优解,那么对于n+1n+1个箱子的输入实例也能得到最优解。

归纳步骤证明思路

N={1,2,...,n+1},w1w2...wn+1N=\{1,2,...,n+1\},w_1\le w_2 \le ... \le w_{n+1}

    \implies

去掉箱子11,令C=Cw1C'=C-w_1,得到规模为nn的输入N={2,3,...,n+1}N'=\{2,3,...,n+1\}

    \implies

关于输入NN'CC'的最优解II'

    \implies

II'加入箱子11,得到II

    \implies

证明II是关于输入NN的最优解

正确性证明

假设对于nn个集装箱的输入,贪心法都可以得到最优解,考虑输入

N={1,2,...,n+1}N=\{1,2,...,n+1\},其中w1w2...wn+1w_1\le w_2 \le ... \le w_{n+1}

由归纳假设,对于

N={2,3,...,n+1},C=Cw1N'=\{2,3,...,n+1\}, \quad C'=C-w_1

贪心法得到最优解II'。令

I=I{1}I=I'\cup \{1\}


II(算法解)是关于NN的最优解。

若不然,存在包含1的关于N的最优解II*(如果II*
若不然,存在包含1的关于N的最优解II*(如果II*中没有1,用1替换中的第一个元素得到的解也是最优解),且I>I|I*|>|I|;那么I{1}I*-\{1\}NN'CC'的解且

I{1}>I{1}=I|I*-\{1\}|>|I-\{1\}|=|I'|

II'是关于NN'CC'的最优解矛盾。

小结

  • 装载问题是0-1背包的子问题(每件物品的重量为1),NP难的问题存在多项式时间可解的子问题。

  • 贪心算法证明:对规模归纳

最小延迟调度

客户集合AAiA\forall i \in Atit_i为服务时间,did_i为客户要求完成时间,ti,dit_i, d_i为正整数,一个调度f:ANf:A\to Nf(i)f(i)为客户ii的开始时间。求最大延迟达到最小的调度,即求ff使得

minf{maxiA{f(i)+tidi}}i,jA,ij,f(i)+tif(j)orf(j)+tjf(i)\min_f\{\max_{i \in A}\{f(i)+t_i-d_i\}\} \\ \forall i,j \in A, i \neq j,f(i)+t_i \le f(j) \\ or \quad f(j)+t_j\le f(i)

实例:调度1

A={1,2,3,4,5},T={5,8,4,10,3},D=<10,12,15,11,20>A=\{1,2,3,4,5\},T=\{5,8,4,10,3\},D=<10,12,15,11,20>

调度1:顺序安排

f1(1)=0,f1(2)=5,f1(3)=13,f1(4)=17,f1(5)=27f_1(1)=0,f_1(2)=5,f_1(3)=13,f_1(4)=17,f_1(5)=27

各任务延迟:0,1,2,16,10;

最大延迟:16。

优化的调度2

A={1,2,3,4,5},T={5,8,4,10,3},D=<10,12,15,11,20>A=\{1,2,3,4,5\},T=\{5,8,4,10,3\},D=<10,12,15,11,20>

调度2:按截至时间从前到后安排:

f2(1)=0,f2(2)=5,f2(3)=13,f2(4)=17,f2(5)=27f_2(1)=0,f_2(2)=5,f_2(3)=13,f_2(4)=17,f_2(5)=27

各个任务延迟:0,11,12,4,10;

最大延迟:12。

贪心策略

贪心策略1:按照tit_i从小到大安排

贪心策略2:按照ditid_i-t_i从小到大安排

贪心策略3:按照did_i从小到大安排

策略1对某些实例得不到最优解。

  • 反例:t1=1,d1=100,t2=10,d2=10t_1=1,d_1=100,t_2=10,d_2=10

策略2对某些实例得不到最优解。

  • 反例:t1=1,d1=2,t2=10,d2=10t_1=1,d_1=2,t_2=10,d_2=10

策略3

伪代码

算法 Schedule

输入:A,T,D

输出:f

  1. 排序A使得d1d2...dnd_1\le d_2 \le ... \le d_n

  2. f(1)0f(1)\gets 0 // 从0时刻起

  3. i2i \gets 2

  4. whileindowhile \quad i \le n \quad do

  5. f(i)f(i1)+ti1\quad \quad \quad f(i) \gets f(i-1)+t_{i-1}

  6. ii+1\quad \quad \quad i\gets i+1


排序思想:按照完成时间从早到晚安排任务,没有空闲。

交换论证:正确性证明

证明思路:

  • 分析一般最优解与算法解的区别(成分,排列顺序不同);

  • 设计一种转换操作(替换成份或者交换次序),可以在有限步将任意一个普通最优解逐步替换成算法的解;

  • 上述每部转换都不降低解的最优性质。

贪心算法的解的性质:没有空闲时间,没有逆序

逆序(i,j):f(i)<f(j)di>dj(i,j): f(i)<f(j)且d_i>d_j

引理

引理1:所有没有逆序、没有空闲时间的调度具有相同的最大延迟。

证:设ff没有逆序,在ff中具有相同的完成时间dd的客户i1,i2,...,iki_1,i_2,...,i_k连续安排,其安排时刻为t0t_0,完成这些任务的时刻是tt,最大延迟为最后任务延迟tdt-d,与i1,i2,...,iki_1,i_2,...,i_k的排列次序无关。

t=t0+(ti1+ti2)+...+tikt=t_0+(t_{i_1}+t_{i_2})+...+t_{i_{k}}

证明要点

从一个没有空闲的最优解出发,逐步转变成没有逆序的解。根据引理1,这个解和算法解具有相同的最大延迟。

  1. 如果一个最优调度存在逆序,那么存在i<ni<n使得(i,i+1)(i,i+1)构成一个逆序,称为相邻的逆序。

  2. 交换相邻逆序iijj,得到的解仍旧最优。

  3. 每次交换后逆序数减11,至多经过n(n1)/2n(n-1)/2次交换得到一个没有逆序的最优调度——等价于算法的解。

交换相邻逆序仍旧最优

f1f_1是一个任意最优解,存在相邻逆序(i,j)(i,j)。交换iijj的顺序,得到解f2f_2。那么f2f_2的最大延迟不超过f1f_1的最大延迟。

理由:

  1. 交换iijj与其他客户延迟时间无关。

  2. 交换后不增加jj的延迟,但可能增加ii的延迟。

  3. iif2f_2的延迟小于jjf1f_1的延迟。因此小于f1f_1的最大延迟ff

iif2f_2的延迟不超过jjf1f_1的延迟

delay(f2,i)=s+tj+tididelay(f_2,i)=s+t_j+t_i-d_i

delay(f1,j)=s+ti+tjdjdelay(f_1,j)=s+t_i+t_j-d_j

dj<did_j<d_i

delay(f2,i)<delay(f1,j)rdelay(f_2,i)<delay(f_1,j)\le r

小结

贪心法的正确性证明方法:交换论证

  • 分析算法解与一般最优解的区别,找到把一般解改造成算法解的一系列操作(替换成份、交换次序)。

  • 证明操作步数有限。

  • 证明每步操作后的得到解仍旧保持最优。

得不到最优解的处理方法

  • 输入参数分析

    考虑输入参数在什么取值范围内使用贪心法可以得到最优解。

  • 误差分析

    估计贪心法——近似算法所得到的解与最优解的误差(对所有的输入实例在最坏的情况下误差的上界)。

找零钱问题

问题:设由nn种零钱,重量分别为w1,w2,...,wnw_1,w_2,...,w_n,价值分别为v1=1,v2,...,vnv_1=1,v_2,...,v_n。需要付的总钱数是yy。不妨设币值和钱数都为正整数。问:如何付钱使得所付钱的总重量最轻?

实例:v1=1,v2=5,v3=14,v4=18,wi=1,i=1,2,3,4.y=28v_1=1,v_2=5,v_3=14,v_4=18,w_i=1,i=1,2,3,4. \\ y=28

最优解:x3=2,x1=x2=x4=0x_3=2,\quad x_1=x_2=x_4=0,总重为22

建模

令选用第ii种硬币的数目是xi,i=1,2,3,...,nx_i,\quad i=1,2,3,...,n

min{i=1nwixi}i=1nvixi=y,xiN,i=1,2,...,n\min \{\sum\limits_{i=1}^{n}w_ix_i\} \\ \sum\limits_{i=1}^{n}v_ix_i=y, \quad x_i \in N, \quad i=1,2,...,n

动态规划算法

Fk(y)F_k(y)表示用前kk种零钱,总钱数为yy的最小重量

Fk(y)=min0xkyvk{Fk1(yvkxk)+wkxk}F_k(y)=\min\limits_{0\le x_k \le \lfloor \frac{y}{v_k}\rfloor}\{F_{k-1}(y-v_kx_k)+w_kx_k\}

F1(y)=w1yv1=w1yF_1(y)=w_1\lfloor \frac{y}{v_1}\rfloor = w_1y

贪心法

单位价值重量轻的货币优先,设

w1v1w2v2...wnvn\frac{w_1}{v_1} \ge \frac{w_2}{v_2} \ge ... \ge \frac{w_n}{v_n}

使用前kk种零钱,总钱数为yy,贪心法的总重量为Gk(y)G_k(y)

Gk(y)=wkyvk+Gk1(ymodvk),k>1G_k(y)=w_k\lfloor \frac{y}{v_{k}}\rfloor +G_{k-1}(y \mod v_k),\quad k>1

G1(y)=w1yv1=w1yG_1(y)=w_1\lfloor \frac{y}{v_{1}}\rfloor =w_1y

n=1,2贪心法是最优解

n=1n=1,只有一种零钱,F1(y)=G1(y)F_1(y)=G_1(y)

n=2n=2x2x_2越大,得到的解越好。

F2(y)=min0x2y/v2{F1(yv2x2)+w2x2}[F1(yv2(x2+δ))+w2(x2+δ)][F1(yv2x2)+w2x2]=[w1(yv2x2v2δ)+w2x2+w2δ][w1(yv2x2)+w2x2]=w1v2δ+w2δ=δ(w1v2+w2)0\large F_2(y)=\min\limits_{0\le x_2 \le \lfloor y/v_2\rfloor} \{F_1(y-v_2x_2)+w_2x_2\} \quad \\ [F_1(y-v_2(x_2+\delta))+w_2(x_2+\delta)] \\ \quad - [F_1(y-v_2x_2)+w_2x_2] \\ = [w_1(y-v_2x_2-v_2\delta)+w_2x_2+w_2\delta] \\ \quad - [w_1(y-v_2x_2)+w_2x_2] \\ = -w_1v_2\delta+w_2\delta \\ = \delta(-w_1v_2+w_2)\le 0

判别条件

定理:对每个正整数kk,假设对所有非负数yyGk(y)=Fk(y)G_k(y)=F_k(y),且存在ppδ\delta满足

vk+1=pvkδv_{k+1}=pv_k-\delta

其中0δ<vk,vkvk+10\le \delta < v_k, v_k \le v_{k+1}pp为正整数,

则下面的命题等价:

  1. Gk+1(y)=Fk+1(y)G_{k+1}(y)=F_{k+1}(y)\quad对一切正整数yy

  2. Gk+1(pvk)=Fk+1(pvk)G_{k+1}(pv_k)=F_{k+1}(pv_k)

  3. wk+1+Gk(δ)pwkw_{k+1}+G_k(\delta)\le pw_k

几点说明

  • 根据条件1和3的等价性,可以对k=3,4,...,nk=3,4,...,n,依次利用条件3对贪心法是否得到最优解做出判别。

  • 条件3验证1次需要O(k)O(k)时间,k=O(n)k=O(n),整个验证时间O(n2)O(n^2)

  • 条件2是条件1在y=pvky=pv_k时的特殊情况。若条件1成立,显然有条件2成立。反之,若条件2不成立,则条件1不成立,钱数y=pvky=pv_k恰好提供了一个贪心法不正确的反例。

验证实例

例1:v1=1,v2=5,v3=14,v4=18,wi=1,i=1,2,3,4v_1=1,v_2=5,v_3=14,v_4=18,w_i=1,i=1,2,3,4。对一切yy

G1(y)=F1(y),G2(y)=F2(y)G_1(y)=F_1(y),G_2(y)=F_2(y).

验证G3(y)=F3(y)G_3(y)=F_3(y)

v3=pv2δ    p=3,δ=1.w3+G2(δ)=1+1=2pw2=3×1=3w3+G2(δ)pw2v_3=pv_2-\delta \implies p=3,\delta =1. \\ w_3+G_2(\delta)=1+1=2 \\ pw_2=3 \times 1 =3 \\ w_3 + G_2(\delta) \le pw_2

贪心法对n=3n=3的实例得到最优解


例2 v1=1,v2=5,v3=14,v4=18,wi=1,i=1,2,3,4v_1=1,v_2=5,v_3=14,v_4=18,w_i=1,i=1,2,3,4.对一切yy

G1(y)=F1(y),G2(y)=F2(y),G3(y)=F3(y)G_1(y)=F_1(y),G_2(y)=F_2(y),G_3(y)=F_3(y),

验证G4(y)=F4(y)G_4(y)=F_4(y),

v4=pv3δ    p=2,δ=10w4+G3(δ)=1+2=3pw3=2×1=2w4+G3(δ)>pw3v_4=pv_3-\delta \implies p=2,\delta = 10 \\ w_4 +G_3(\delta)=1+2=3 \\ pw_3=2 \times 1=2 \\ w_4+G_3(\delta) > pw_3

n=4,y=pv3=28n=4,y=pv_3=28

最优解:x3=2x_3=2,贪心法:x4=1,x2=2x_4=1,x_2=2

小结

  1. 贪心策略不一定得到最优解,在这种情况下有两种解决方法:

    • 参数化分析:分析参数取什么值可以得到最优解。

    • 估计贪心法得到的解在最坏情况下与最优解的误差。

  2. 一个参数化分析的例子:找零钱问题。

作者

Hu

发布于

2021-09-18

更新于

2021-09-18

许可协议

评论