您好,欢迎访问三七文档
这里主要是介绍一种证明贪心算法是最优的一种方法:ExchangeArgument(不知道应该怎么翻译到中文,交换参数?感觉听起来挺别扭的,不像是一个方法的名字~o(╯□╰)o)ExchangeArgument的主要的思想也就是先假设存在一个最优的算法和我们的贪心算法最接近,然后通过交换两个算法里的一个步骤(或元素),得到一个新的最优的算法,同时这个算法比前一个最优算法更接近于我们的贪心算法,从而得到矛盾,原命题成立。下面来看一个更为formal的解释:步骤:Step0:给出贪心算法A的描述Step1:假设O是和A最相似(假设O和A的前k个步骤都相同,第k+1个开始不同,通常这个临界的元素最重要)的最优算法Step2:[Key]修改算法O(用ExchangeArgument,交换A和O中的一个元素),得到新的算法O’Step3:证明O’是feasible的,也就是O’是对的Step4:证明O’至少和O一样,即O’也是最优的Step5:得到矛盾,因为O’比O更和A相似。证毕。当然上面的步骤还有一个变种,如下:Step0:给出贪心算法A的描述Step1:假设O是一个最优算法(随便选,arbitrary)Step2:找出O和A中的一个不同。(当然这里面的不同可以是一个元素在O不再A,或者是一个pair的顺序在A的和在O的不一样。这个要根据具体题目)Step3:Exchange这个不同的东西,然后argue现在得到的算法O'不必O差。Step4:Argue这样的不同一共有Polynomial个,然后我exchangePolynomial次就可以消除所有的不同,同时保证了算法的质量不比O差。这也就是说A是asgoodas一个O的。因为O是arbitrary选的,所以A是optimal的。证毕下面给几个例子:例MaximumCardinalityDisjointIntervalProblem问题描述:给一些时间片段集合T={(a1,b1)(a2,b2),。。。,(an,bn)},找出一个元素个数最多的子集S,子集中的每个元素的时间片段没有交叉。GreedyAlgorithm:每次都选所有interval中bi最小的那个,把(ai,bi)加入S,然后把(ai,bi)在T中删除,同时把T中所有和(ai,bi)有交叉的interval删除,然后再在T中找最小的bj,循环上面的操作,直到没有可以在添加的。证明上面说的GreedyAlgorithm是最优的。下面就用第一个证明的步骤来证。我们的GreedyAlgorithm记为A,假设A不是最优的,那么就一定存在一个O,O是和A最相近的一个最优的算法,最相近是指和O和A的前K-1个选择都相同,第K个是不同的。假设对于A,A第k个选择的是(ai,bi);而O第K个选择的是(aj,bj)。从A的定义我们可以直到,bi=bj。现在我们构造一个O',O'=O-(aj,bj)+(ai,bi)。1)很显然,O'是这个问题的一个解,也就是说O'中的intervals没有重叠的。在O'中,(ai,bi)前的intervals和A中的一样,所以前一部分没有重叠。在(ai,bi)后的intervals和O中的一样,所以也没有重叠,同时bi=bj,所以(ai,bi)也不会和它相邻的重叠,所以O'中的所有intervals都没有重叠。2)O'是一个最优解,因为他的intervals的个数和O一样。综上,我们找到了一个最优解O',它和A具有的共同的intervals有K个,这和我们前提假设最多有k-1个相矛盾,所以,A是最优的。证毕。例SchedulingwithDeadline问题描述:有一系列的tasks{a1,a2,。。。,an},每个task需要在cpu上跑{p1,p2,。。。,pn}个units的时间,cpu是非抢占式的,也就是task一旦获得cpu,就运行直到他完成,{c1,c2,。。。,cn}是每个task的完成时间,我们现在要minimize的就是(c1+c2+。。。+cn)/n。所有的task一起release。GreedyAlgorithm:每次选运行时间最小的那个task运行。证明上面说的GreedyAlgorithm是最优的。同样还是用第一个证明的步骤来证。假设A不是最优的,那么一定存在一个最优的O,和A最接近,他们选择的前k-1个task是相同的。如下:No.123。。。k。。。。。。。。。A。。。。。。。。。。。ai。。aj。。。。。O。。。。。。。。。。。aj。。。。ai。。。根据A的定义,我们知道,pi=pj的。现在我们就来构造O',当然,O'就是把O中的ai和aj两个元素交换一下,即No.123。。。k。。。。。t。。。A。。。。。。。。。。。ai。。aj。。。。。O。。。。。。。。。。。aj。。。。ai。。。O'。。。。。。。。。。。ai。。。。aj。。。对于整个的完成时间,我们可以给出计算公式的,假设b1,b2,。。。,bn是一个调度序列(bi是task的index,也就是{bi}是所有task的index的一个permutation),即task执行顺序是a_{bi},a_{b2},...,a_{bn}。举个例子如果只有5个task,a1,a2,a3,a4,a5。{bi}={2,1,3,5,4},那也就是task的执行序列为a2,a1,a3,a5,a4。(博客里面不能打数学公式就是不爽啊~)还是用上面的5个例子算了,对于a2,a1,a3,a5,a4,我们知道总时间其实是5个a2的时间,即p2,4个a1的时间,即p1,依次类推,所以总时间是5*p2+4*p1+3*p3+2*p5+p4。好,有了上面的计算总时间的概念,下面就来分别计算一下O和O'的总时间,其实我们需要比较它们谁大谁小,通常只要比较一下他们的差就行了,即Time(O)-Time(O')。同时很显然,他们的差只是由aj和ai引起的,其他的并没有改变。我们知道在O中,aj是第k个选的,ai是第t个选的,而在O'中,ai是第k个选的,aj是第t个选的,所以Time(O)-Time(O')=(n-k+1)*pj+(n-t+1)*pi-(n-k+1)*pi-(n-t+1)*pj=k*(pj-pi)-t*(pj-pi)=(pi-pj)*(k-t)=0,即我们得到Time(O)=Time(O'),这也就是说我们找到了一个O',他是最优的,同时O'和A有K个commonelement,所以得出矛盾。证毕。例KruskalAlgorithmforMinimumSpanningTree(最小生成树)问题描述:对于边集合E,先按每个边的cost排序,从小到大,然后按如下方法构造一个MST,每次选cost最小的那个边,添加进我们的MST,如果一个边使我们现有的MST出现环路,则把这条边舍弃,然后重复上面的操作。(感觉现在表达越来越搓了,没看明白怎么回事的童鞋看这里吧)下面我们就用第二种证明方法来证明KruskalAlgorithm算法是最优的。假设我们的graph是G(V,E),有一个optimal的算法O,它生成的MST是T1,Kruskal生成的树是T2,这样,因为T2!=T1(如果相等我们的Kruskal就最优了,就不用证了),所以至少有一条边e,e在T1中,同时e不在T2中。这个与众不同的e就是我们的切入点。可以想象,如果我们把e在T1中去掉,T1就变成了两部分,即Ta和Tb,我们知道{Ta中的点}V{Tb中的点}=V(就是Ta中的点并上Tb中的点就是我们G中的点集V),所以在T2中,一定有一条边f(f!=e),f的一个点在{Ta中的点},f的另一个点在{Tb中的点}。根据我们的Greedy算法Kruskal,我们没有选e,是因为e在我们的图中造成了回路。在进一步想,有回路是因为我们先选了f,这就说明cost(f)=cost(e)。(恩,这就是我们的重点啊,笑一个O(∩_∩)O~)下面的也就很显然了,我们考虑构造一棵新的树,T3,对于T3=E(T1)-e+f,也就是T3中的边是T1中的所有边除了e,同时加上边f。1)很显然,我们知道T3是一棵spanningtree,f连通了由去掉e而分隔的两部分。2)很显然,T3的cost是=T1的cost的。因为cost(f)=cost(e),且其他不变。即T3是MST。所以同个上面的构造,我们就构造出了一棵树,这个树是MST,同时它比T1更接近于T2。(多了一条common的边)我们知道T1和T2对多有n条边是不同的,通过上面的步骤,我们可以经过n次变换,得到T2,同时cost=T1,所以,T2是MST。证毕。
本文标题:如何证明贪心算法
链接地址:https://www.777doc.com/doc-2482308 .html