调度算法(二)
续调度算法(一)
线性规划
现在我们介绍线性规划算法在调度问题中的应用。一个线性规划问题通常以如下形式出现:
寻找长度为\(n\)的解向量\(x=(x_1,...,x_n)\),满足\(m\)个线性约束\(a_{i1}x1+a_{i2}x_2+...+a_{in}x_n\le b_i\),其中\(1\le i\le n\),并使得\(c_1x_1+c_2x_2+...+c_nx_n\)最小化,其中\(c=(c_1,...c_n)\)为成本向量。
有的时候,上面的一些约束可能是等式而非不等式,也有的时候甚至不存在需要最小化的目标函数,也就是说目标函数是任意的,我们只需要寻找到满足约束条件的解\(x\)即可。这些都算是线性规划问题的不同形式。许多优化问题都可以转化为线性规划求解,而线性规划问题存在多项式时间算法。本节主要考虑\(R|pmtn|C_{max}\)问题的线性规划模型。
\(R|pmtn|C_{max}\)问题
在不相关并行环境下进行抢占式调度,优化最终完成时间,不熟悉问题定义的建议回顾一下前面章节。抢占式调度下,单个任务可能在任何机器上运行一部分,为了形式化这个问题,我们使用\(nm\)个变量\(x_{ij}\),代表任务\(j\)的总任务量在机器\(i\)上执行的比例。例如,如果\(x_{1j}=x_{2j}=1/2\),则说明任务\(j\)在机器1和机器2上各完成了一半的任务量。
现在我们考虑怎样的线性约束能够使\(x_{ij}\)的解符合\(R|pmtn|C_{max}\)问题的定义。显然,每部分任务的比例必须是非负的,从而有:
同时,任意有效调度下,每个任务都必须被完成,这意味着:
我们的设计目标是最小化调度的最终完成时间\(D\),显然任何机器上的总处理时间不超过\(D\),根据之前的定义,不相关并行环境下,任务\(j\)在机器\(i\)上的处理时间记为\(p_{ij}\):
最后,我们还需要保证,所有任务的总处理时间都不超过\(D\):
我们需要在满足上述约束条件下最小化\(D\)。显然,任何一个有效调度下的\(x_{ij}\)必然满足这样的约束,但是有一事不明:一个满足约束条件的解并没有确切指定具体的调度策略——\(x_{ij}\)仅能指定任务在不同机器上运行的比例,而不能保证调度时同一时刻只能在一个机器上运行(这一条件无法写成线性约束)。
有趣的是,这个问题可以通过定义一个开放车间问题\(O\)来解决。我们为每个\(x_{ij}\)创建一个任务\(j\)在机器\(i\)上的操作,操作的处理时间为\(o_{ij}=x_{ij}p_{ij}\),现在对于已经通过线性规划求出的最优的\(x_{ij}\),我们构造了一个抢占式的开放车间问题,优化目标仍是\(C_{max}\),此问题概括为\(O|pmtn|C_{max}\),此问题已在上一节通过一个二分图匹配的形式化问题解决了,同时由于有\(D\)约束了任务的最长处理时间和机器的最长负载时间,也就是说\(P_{max},\Pi_{max} \le D\),新问题的解就是原问题的最优调度。
线性规划问题不止这点应用,它还能用于设计\(NP\)难问题的近似算法,这将在下一节介绍。
使用松弛法设计近似算法
现在我们转向设计一些\(NP\)难的调度难题的近似算法。近似算法的设计通常基于相应的\(NP\)难问题的松弛版本。问题的松弛指的是从原问题中去除一些约束形成的新问题。例如\(1|r_j,pmtn|\sum C_j\)就是\(1|r_j|\sum C_j\)的松弛版本,因为实际上抢占式调度比非抢占式调度简单多了,更容易计算最优解。另一种松弛的思路是移除“一个机器只能同时处理一个任务”的条件,让一个机器可以在同一时间运行多个任务。
显然,原问题的解一定是松弛问题的解,反之不一定。我们在上面章节提到的所有非抢占式调度算法都能用于解决抢占式调度,其解虽然可能不最优,但合法。同样地,松弛问题的最优解即使是合法的,也不一定达到原问题的最优解。
在这一课题上一个有效的想法是:设计一个可以在多项式时间内解决的松弛问题,然后再设计一个将松弛问题的解转化为原问题可行解的算法,并保证解尽量接近最优解。问题的关键是设计的松弛问题尽可能包含原问题较多的信息,使得松弛问题的最优解与原问题最优解尽可能接近。
本节将介绍两种对调度问题的松弛方法,一是通过非抢占式到抢占式的松弛,二是通过构造线性规划问题,松弛某些线性约束来实现。将松弛解转化为原问题解的方法也有两种,一是根据任务与机器的配对,二是根据任务在机器上的执行顺序。后面会详细介绍。
在开始本节内容之前,我们先介绍一个概念:松弛决策过程(relaxed decision procedure, RDP)。一个最小化问题的\(\rho\)-\(RDP\)定义为,接受一个目标值\(T\),如果松弛问题没有小于等于\(T\)的解则返回\(None\),否则返回一个不超过\(\rho T\)的原问题解。一个多项式时间的\(\rho\)-\(RDP\)能够很容易的转化为原问题的\(\rho\)-近似算法:通过对\(T\)进行二分查找,寻找最小的\(T\),使得松弛问题有解并将此解转化为原问题的解。
为什么是\(\rho\)-近似的呢?逻辑是这样:假如原问题有最优解\(T^*\),则松弛问题在\(T^*\)下必有解,将这个解转化为不超过\(\rho T\)的原问题解,这个过程构成\(\rho\)-近似算法。下面将应用这个设计思路。
\(R||C_{max}\)问题
本节给出一个\(R||C_{max}\)的\(2\)-近似算法。我们刚刚用线性规划解决过的\(R|pmtn|C_{max}\)问题,是这个问题的抢占式松弛版本,实际上,如果我们加一条约束\(x_{ij}\in \{0,1\}\),就是非抢占式调度的线性规划模型了。实际上加上这个条件后,原先的第3条约束可以使得第4条约束多余,因此同样的建模下,本问题的线性约束为:
这是一个整数线性规划问题,相比于一般的线性规划问题,整数线性规划是\(NP\)难的,因此问题并没有变简单。但是,我们提出的模型是有用的,可以用来松弛某些约束,例如得到一个非整数解,然后舍入到整数。
现在我们再次将整数约束松弛为\(x_{ij}\ge 0\),但这就太松了一点,我们还要添加一个约束:如果一个任务\(j\)在机器\(i\)上的效率过慢,即\(p_{ij}\ge D\),就不把这个任务分配到机器\(i\):
这个约束在原来的整数线性规划模型中是天然成立的,但我们移除整数约束后就需要手动添加。注意到,当\(D\)固定时,这是个简单的只有线性约束的规划问题(没有成本函数),我们只要找可行解即可。如果对于给定的\(D\),问题没有解,则输出\(None\),如果有解,我们就尝试将非整数解转化为原问题的可行整数解。
根据一个线性规划问题的基本结论:我们可以找到一个此问题的可行解,其中最多只有\(n+m\)个正值。这\(n+m\)个正的\(x_{ij}\)指定了\(n\)个任务的去向,假设这些\(x_{ij}\)中等于1的数量为\(k\),则\(k\)个任务已经被完全指定去向,剩余\(n-k\)个任务需要至少\(2(n-k)\)个\(x_{ij}\)指定去向(因为非整数意味着至少被分配在两台机器上),故\(k+2n-2k\le m+n\),故\(n-k\le m\),意味着至多有\(m\)个任务被分配在不止一台机器上。
现在,我们将所有\(x_{ij}=1\)的任务\(j\)直接分配到机器\(i\),这部分调度记为\(S_1\),剩余还有至多\(m\)个任务需要调度。我们简单地按照\(x_{ij}>0\)的解将任务\(j\)匹配到机器\(i\),且保证每个机器至多一个任务(因为这部分任务不超过\(m\)个),这部分任务的调度记为\(S_2\)。关于\(S_2\)的存在性证明稍微有点复杂,感兴趣的请读[1]。
我们分析一下这样形成的调度策略的完成时间,它显然不超过\(S_1\)的完成时间加上\(S_2\)的完成时间。而由于\(x_{ij}\)是松弛问题的可行解,因此\(S_1\)的完成时间是\(\le D\)的,在\(S_2\)中,由于每个机器至多一个任务,且由于约束\((4)\),\(x_{ij}>0\)意味着\(p_{ij}\le D\),从而\(S_2\)的完成时间也是不超过\(D\)的,故这样调度的时间不超过\(2D\)。因此假设原问题的最优解是\(D\),则该松弛问题必有解,且该解不超过\(2D\)。
\(1|r_j|\sum C_j\)问题
前面已经提到,带发布时间的单机调度问题是\(NP\)难问题,本节将设计一个解决此问题的近似算法。对于此问题的抢占式版本,前面已经证明了最短剩余时间优先算法\(SRPT\)的最优性。利用这个松弛,我们将从其最优解中找出各任务的完成时间顺序,然后以相同的顺序构建非抢占式调度策略。此算法称为Convert-Preempt-Schedule,反转抢占式调度。
我们首先使用\(SRPT\)算法构建抢占式调度版本的最优解,然后按照该最优解中各任务的完成时间排序,按照\(C_1^P\le ...\le C_n^P\)给这些任务重新编号。而后按照相同顺序非抢占式的调度这些任务,如果某个时间点后继任务还未发布,就让机器空闲即可。可以证明,这个算法是\(2\)-近似的。
证明:对于任务\(j\),由于其在抢占式调度中能在\(C_j^P\)时完成,因此假如没有其他任务,则它最晚也能在\(C_j^P\)时完成,当存在其他任务后,我们在其完成前插入了排在它前面的任务,因此它的最晚完成时间延长为:
而由于在抢占式调度中,所有\(k\le j\)的任务都在\(C_j^P\)之前完成了,因此\(\sum_{k\le j}p_k\le C_j^P\),从而有:
假设非抢占式调度的最优解是\(\sum C_j^P\),则对于抢占式的松弛版本必然有不超过\(\sum C_j^P\)的最优解,而通过上述分析可知此算法给出的解不超过抢占式最优解的两倍,证毕。
\(1|r_j,prec|\sum w_jC_j\)问题
这一节依然使用松弛法来解决这个难题(基本上是目前遇到的最难的单机调度问题了),我们采用线性规划来设计一个2-近似算法。
首先我们描述这个问题的线性规划约束,和之前不同,我们不从整数/非整数的角度松弛,而是从问题定义中寻找必要条件,形成一个线性规划问题。
对这个问题来说,任务运行的顺序极其重要,因此我们要寻找一种能表示任务顺序的形式化描述。很容易想到用时间来表示:定义\(C_j\)为任务\(j\)在调度策略中的完成时间。实际上这就能够完确定一个调度了(比之前的\(x_{ij}\)更清晰)。显然,最小化的成本函数是\(\sum w_jC_j\)。并且满足如下条件:
这三个条件很容易理解,首先任务必须在发布时间后才能开始,其次需要等待任务所有的前驱任务完成才能开始,最后,任意两个不同的任务存在先后关系(处理时间不重叠)。
不幸的是,最后一个约束有“或”运算,不是一个确定的线性约束,这导致这个问题不是线性规划问题。我们尝试用不等式代替它。定义任务集\(J=\{1,...,n\}\),对任意子集\(S\subseteq J\),定义\(p(S)=\sum_{j\in S}p_j\),\(p^2(S)=\sum_{j\in S}p_j^2\)。对任何可行的单机调度策略,可以证明下面的不等式成立:
证明:假设\(S\)中的任务按照完成时间升序编号。由于任意的任务\(j\)的完成时间大于在其前面完成的所有任务的处理时间之和,即\(C_j\ge \sum_{k=1}^j p_k\),代入上式左边得:
对于没有发布时间,没有任务依赖的问题\(1||\sum w_jC_j\)问题,上面这个不等式的约束是完备的[37,41],可以形成一个可行解。现在把上面这个不等式加上原先的第一个和第二个不等式,组成\(1|r_j,prec|\sum w_jC_j\)的线性约束,这个新的线性规划问题是原问题的松弛问题。
注意到我们新提出来的不等式似乎有\(2^n\)个,但是它仍然可以被线性规划的椭圆算法[37,41]在多项式时间内解决。
现在为了简单起见,我们移除发布时间约束,只探讨\(1|prec|\sum w_jC_j\)问题,其对应的松弛问题将移除上面提到的第一个不等式。解上面的线性规划问题得到的解为\(C_1\le C_2\le...\le C_n\),我们将证明按照这个顺序来调度任务是2-近似的。
假设上面的调度形成的新的完成时间为\(\tilde{C_j}\),按照惯例,我们只需证明\(\tilde{C_j}\le 2C_j\)就行。首先,设\(S=\{1,...,j\}\),没有了发布时间机器是没有空闲的,因此\(\tilde{C_j}=p(S)\)。同时根据上面提到的不等式\((*)\),我们有:
又因为\(C_1\le C_2\le...\le C_n\),我们有:
故\(\tilde{C_j}=p(S)\le 2C_j\),证毕。