1. 程式人生 > 實用技巧 >分支限界,流水作業問題 批處理作業排程

分支限界,流水作業問題 批處理作業排程

>>> hot3.png

最優流水排程問題

問題描述:

設有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

。如果所有的tij(1≤i≤n, 1≤j≤m)均已給出,要找出一種安排任務的方法,使得完成這n個作業的加工時間為最少。已經證明,當機器數(或稱工序數)m≥3時,流水作業排程問題是一個NP-hard問題。

這裡就考慮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

在開始加工S中的作業之前需等待t個時間單位且t > ai,則作業i在P1上加工完畢(需時ai)之後,還要再等t-ai個時間單位才能開始在P2上加工;若t≤ai,則作業i在P1上加工完畢之後,立即可以在P2上加工,等待時間為0。故P2在開始對S-{i}中的作業進行加工之前,所需要的等待時間為t’= bi+max{t-ai,0}。(bi是作業i在P2上加工所需的時間)。所以,假定ai為已知的使得g(S,t)值最小的第一個執行的作業,可以得到

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