分支限界,流水作業問題 批處理作業排程
最優流水排程問題
問題描述:
設有n個作業,每一個作業i均被分解為m項任務: Ti1, Ti2, ┅ , Tim(1≤i≤n,故共有n*m個任務),要把這些任務安排到m臺機器上進行加工。
現在有三條限定:
1、 每個作業i的第j項任務Tij (1≤i≤n, 1≤j≤m)只能安排在機器Pj上進行加工;
2、 作業i的第j項任務Tij(1≤i≤n, 2≤j≤m)的開始加工時間均安排在第j-1項任務Ti,j-1加工完畢之後;
3、 任何一臺機器在任何一個時刻最多隻能承擔一項任務。
最優流水作業排程:設任務Tij在機器Pj上進行加工需要的時間為tij
這裡就考慮n個作業對於2個機器(P1,P2)的排程。現在問題就是:給定n個作業T,每個作業可以分成兩項任務A,B;其中A任務在P1處理,B任務在P2上處理。如何給出一個使得這個n個作業的加工時間最短?
問題解決:
分析:
首先考慮最優流水排程的性質:
1、 在所確定的最優排程的排列中去掉第一個執行作業後,剩下的作業排列仍然還是一個最優排程,即該問題具有最優子結構的性質。
2、 在計算規模為n的作業集合的最優排程時,需多次使用該作業集合的子集合的最優排程,即該問題亦具有高度重複性。
So… 考慮用動態規劃求解這個問題咯~
設N={1,2,┅,n}是全部作業的集合,作業集S是N的子集S∈N。在我們對S中的第一個作業開始進行加工時,機器P2上加工的其它作業可能還尚未完成,不能立即用來對S中的作業進行加工。
假設對機器P2需等待t個時間單位以後才可以用於S中的作業加工(t也可以為0即無須等待),記為g(S,t)。
現選定作業i為S中第一個加工作業之後,在機器P2上開始對S-{i}中的作業進行加工之前,所需要的等待時間為bi+max{t-ai,0}。這是因為,若P2
g(S,t)= ai+ g(S-{i},bi+max{t-ai,0}) (1)
將以上結論推廣,現在我們安排首先執行作業i,再執行作業j,P2需等待t個時間單位以後才可以用於S中的作業加工。則由(1)式的g(S,t)可寫為
g(S,t)=ai+g(S-{i}, t’)=ai+aj+g(S-{i,j}, bj+max{t’-aj,0})。 (2)
關於max{}運算有這個性質:x+ max{y1, y2,…,yn}= max{x+y1,x+y2,…,x+yn}。
所以,
bj+max{t’-aj,0}
= bj + max{bi+max{t-ai,0}-aj, 0}
= bj + bi - aj+ max{max{t-ai,0},aj-bi}
= bj + bi - aj + max{t-ai, 0, aj-bi}
= bj+ bi - aj - ai + max{t, ai, ai+aj-bi}。
記tij= bj+ bi - aj- ai +max{t, ai, ai+aj-bi},則g(S,t)= ai+aj+g(S-{i,j}, tij)。
還記得之前我們的安排了嗎?我們約定先排程作業i再排程作業j,有沒有發現,我們推到了半天沒有什麼實質性結果啊,因為現在我們還根本不知道i和j是什麼啊,如何確定?
因此,既然說先i後j是最優的,憑什麼?將j和i對調下,那就不是最優的咯,那對調後不是最優的根源處在哪兒呢?我們試試。
若是先排程j作業再排程i作業,我們可以得到g’(S,t)=ai+aj+g(S-{i,j}, tji)。
比較g(S,t)與g’(S,t)兩式,顯然,就是比較tij和tji。
tij= bj+ bi - aj - ai +max{t, ai, ai+aj-bi},
tji= bj+ bi - aj - ai + max{t, aj, ai+aj-bj},
故tij-tji= max{t, ai, ai+aj-bi} - max{t, aj, ai+aj-bj}。
因此我們只要比較max{t, ai, ai+aj-bi}與max{t, aj, ai+aj-bj}的大小就可以了,即tij≦ tji當且僅當max{t, ai, ai+aj-bi} ≤max{t, aj, ai+aj-bj}。
由於max{t, ai, ai+aj-bi}≤ max{t, aj, ai+aj-bj}
對任何t≧ 0成立,當且僅當
max{ai, ai+aj-bi}≤ max{aj, ai+aj-bj}成立,當且僅當
ai+aj+max{-aj, -bi}≦ai+aj+max{-ai, -bj}成立,當且僅當
max{-aj, -bi}≦ max{-ai, -bj}成立,當且僅當
min{aj, bi}≧ min{ai, bj}成立(此式稱為Johnson不等式)。
當min{ ai , aj , bi , bj}為ai或者bj時,有tij≤ tji,此時把i排在前j排在後的排程用時較少;
反之,若min{ ai , aj , bi, bj}為aj或者bi時,則j排在前i排在後的排程用時較少。
將此情況推廣到一般。
當min{ a1, a2,┅, an , b1, b2,┅, bn }=ak時,則對任何i≠k,都有min{ai, bk} min{ak, bi}成立,故此時應將作業k安排在最前面,作為最優排程的第一個執行的作業;
當min{ a1, a2,┅, an , b1, b2,┅, bn}= bk時,則對任何i≠k,也都有min{ak, bi}min{ai, bk}成立,故此時應將作業k安排在最後面,作為最優排程的最後一個執行的作業。
演算法描述:
1. 建立長為2n的陣列C,
將a1, a2,┅, an依次放入C[1]~ C[n]中,
b1, b2,┅, bn依次放入C[n+1]~ C[2n]中。 /* O(n),下面將對這2n個數進行排序*/
2. 對長為2n的陣列D進行初始化:
D[1]~D[n]中依次放1,2,┅,n,
D[n+1]~D[2n]中依次放-1,-2,┅,-n。 /* O(n),分別對應於a1, a2,┅, an和b1, b2,┅, bn的下標*/
3. 對陣列C進行排序,
D[k]始終保持與C[k]的對應關係。(O(n log n),若C[i]與C[j]對換,則D[i]也與D[j]對換。)
當a1, a2,┅, an及b1, b2,┅, bn按從小到大排好序之後,D[1]~ D[2n]也就按從小到大的次序記錄了這些ai和bj的下標即作業號(bj的下標前有一負號以區別於ai))。
4. 將E[1]~ E[n]全部置為“No”。/* O(n),表示所有任務均尚未被安排*/
5. 下標變數初始化:i←1;j←n;k←1; /*O(1),*/
/*i指向當前最左空位F[i],*/
/*放當前應最先安排的作業號;*/
/* j指向當前最右空位F[j],*/
/*放當前應最後安排的作業號;*/
/* k從1開始逐次增1,D[k](或-D[k])*/
/*按ai和bj從小到大的次序依次給出作業號。*/
6. while i ≤ j do
{ /* 作業尚未安排完畢,i從小到大, j從大到小*/
if D[k]>0 then /*D[k]>0即D[k]中放的是ai下標*/
{ if E[D[k]]為“No”then /*作業D[k]尚未安排*/
{ F[i]←D[k]; i增1; E[D[k]] 置為“Yes”}
}/*作業D[k]放在當前最左空位*/
else /*D[k]<0,則–D[k]是bj下標*/
{ if E[–D[k]]為“No”then /*作業–D[k]尚未安排*/
{ F[j]←–D[k]; j減1; E[–D[k]] 置為“Yes”}
} /*作業–D[k]放在當前最右空位*/
k增1; /*準備檢查下一個D[k]以便後續作業安排*/
}
C語言實現:
#include <stdio.h>#include <stdlib.h>
#define MAX_LEN 128
void heap_adjust(int *array,int *index,int n,int loc)
{
int tmp,i,j,k;
tmp=array[loc];
k=index[loc];
for(i=loc;2*i<n;i=j)
{
j=2*i;
if(j+1<n && array[j]<array[j+1]) j++;
if(tmp < array[j]) {
array[i]=array[j];
index[i]=index[j];
}
else break;
}
array[i]=tmp;
index[i]=k;
}
void heap_sort(int *array,int *index,int n)
{
int i,j,tmp;
for(i=n/2;i>0;i--)
heap_adjust(array,index,n,i);
for(i=n;i>1;i--)
{
tmp=array[i];
array[i]=array[1];
array[1]=tmp;
j=index[i];
index[i]=index[1];
index[1]=j;
heap_adjust(array,index,i,1);
}
}
int main()
{
int n,i,j,k;
int A[MAX_LEN],B[MAX_LEN],C[MAX_LEN*2],D[MAX_LEN*2],E[MAX_LEN],F[MAX_LEN];
while(1==scanf("%d",&n)){
if(n > 0 && n < MAX_LEN){
for(i=1;i<=n;i++)
scanf("%d%d",A+i,B+i);
break;
}
printf("invalid n\n");
}
//initial tabs
for(i=1;i<=2*n;i++){
if(i<=n){
C[i]=A[i];
D[i]=i;
E[i]=0;
}
else{
C[i]=B[i-n];
D[i]=-(i-n);
}
}
//sort it!
heap_sort(C,D,2*n);
//dp find
for(k=1,i=1,j=n;i<=j;k++){
if(D[k] > 0){
if(E[D[k]] == 0){
F[i++]=D[k];
E[D[k]]=1;
}
}
else{
if(E[-D[k]] == 0){
F[j--]=-D[k];
E[-D[k]]=1;
}
}
}
printf("scheduled tasks:");
for(i=1;i<=n;i++)
printf("%-3d",F[i]);
printf("\n");
return 0;
}
演算法實現結果:
that's all~
分享到:轉載於:https://my.oschina.net/durong/blog/134466