批处理作业调度

tji 机器1 机器2
作业1 2 1
作业2 3 1
作业3 2 3

作业1在机器1上完成的时间为2,在机器2上完成的时间为3
作业3在机器1上完成的时间为4,在机器2上完成的时间为7
作业2在机器1上完成的时间为7,在机器2上完成的时间为8

3+7+8=18,所以时间和为18

一般情况下,对1个已经安排的作业集合M,|M|=k,M为{1,2,..,n}的子集,设sum1[k]表示机器1完成k个作业的处理时间, sum2[k]表示机器2完成k个作业的处理时间,现在需要处理作业k+1,此时该部分解的目标函数的下界为:
当第k+1步恰好调度第k+1个作业时,
(1) sum1[k+1] = sum1[k] + tk+1,1
sum2[k+1] = max{sum1[k+1], sum2[k]} +tk+1,2

(2) 如果第k个作业在机器1上完成处理后能立即在机器2上开始处理,机器1无空闲时间:
剩下的作业中,第i个作业完成需要的时间为
fi2 = sum1[k] /* i不在M中 */
+ ∑tj1 /* j不在M中,且位于第k个作业之后,i个作业之前, 即 k < j <= i */
+ ti,2

剩余作业完成时间和:
S1 = ∑fi2
   = ∑{ sum1[k] + (n-r+1)tr,1 + tr,2 }   // k+1 =< r <=n

(3) 如果机器2无空闲时间:
剩下的作业中,第i个作业完成需要的时间为
fi2 = max{ sum2[k], sum1[k]+min{ti,1} } /* i不在M中 */
+ ∑tj,2 /* j不在M中,且位于第k个作业之后,i个作业之前, 即 k < j <= i */

剩余作业完成时间和:
S2 = ∑fi2
   = ∑{ max{sum2[k], sum1[k]+min{ti,1}} + (n-r+1)tr,2 }  // i不在M中, k+1 =< r <=n

(4) 注意到如果选择r,使tr1在r>=k+1时依非减序排列,S1则取得极小值s1。同理如果选择r使tr2依非减序排列,则 S2取得极小值s2。

(5) 所有作业完成时间和下限:
lb = ∑fm2 + max(s1, s2) /* m在M中,fm2为第m个作业的完成时间 */

#include "cstdio"
#include "queue"
#define MAX 10
#define MACHINE 2
using namespace std;    

int n;  //作业数
int M[MAX][MACHINE];  //各作业所需的处理时间数组
int b[MAX][MACHINE];  //各作业所需的处理时间排序数组
int a[MAX][MACHINE];  //数组M和b的对应关系数组
int bestx[MAX];  //最优解
int bestc;   //最小完成时间和
int y[MAX][MACHINE];   //工作数组

struct Node
{
    int s;  //已安排作业数
    int f1;  //机器1上最后完成的时间
    int f2;  //机器2上最后完成的时间
    int sf2; //当前机器2上的完成时间和
    int bb;  //当前完成时间下界
    int *x;  //当前作业调度

    //当前完成时间下界小的结点先出队列
    bool operator < (const Node &node) const
    {
        return bb > node.bb;
    }
};

priority_queue<Node> pq;

//n为作业个数
void initNode(Node &node, int n)
{
    node.x = new int[n];
    int i;
    for(i=0; i<n; i++)
        node.x[i] = i;
    node.s = 0;    //已安排作业数
    node.f1 = 0;   //机器1上最后完成的时间
    node.f2 = 0;  //机器2上最后完成的时间
    node.sf2 = 0;  //当前机器2上的完成时间和
    node.bb = 0;  //当前完成时间下界
}

//最小堆新结点
void newNode(Node &node, Node E, int Ef1, int Ef2, int Ebb, int n)
{
    node.x = new int[n];
    int i;
    for(i=0; i<n; i++)
        node.x[i] = E.x[i];
    node.f1 = Ef1;  //机器1上最后完成的时间
    node.f2 = Ef2;  //机器2上最后完成的时间
    node.sf2 = E.sf2 + Ef2;  //当前机器2上的完成时间和
    node.bb = Ebb;  //当前完成时间下界
    node.s = E.s + 1;  //已安排作业数
}

void swap(int &a, int &b)
{
    int temp = a;
    a = b; 
    b = temp;
}

//对各作业在机器1和机器2上所需时间冒泡排序
void sort()
{
    int *c = new int[n];
    int i, j, k;
    for(j=0; j<2; j++)
    {
        for(i=0; i<n; i++)
        {
            b[i][j] = M[i][j];
            c[i] = i;
        }
        for(i=0; i<n-1; i++)
            for(k=n-1; k>i; k--)  //如果后面的比前面的小,交换
                if(b[k][j]<b[k-1][j])
                {
                    swap(b[k][j], b[k-1][j]);
                    swap(c[k], c[k-1]);  //下标也交换
                }
        for(i=0; i<n; i++)
            a[c[i]][j] = i;  
        //排序结束后,机器1上运行时间第i小的作业是a[c[i]][0] (i从0开始)
        //即机器1上作业运行时间小到大排序为a[c[0]][0], a[c[1]][0], a[c[2]][0]...
        // 机器2上作业运行时间小到大排序为a[c[0]][1], a[c[1]][1], a[c[2]][1]...
    }
    delete []c;
}

//计算完成时间和下界
int bound(Node E, int &f1, int &f2, int y[MAX][MACHINE])
{
    int k, j;
    for(k=0; k<n; k++)
        for(j=0; j<2; j++)
            y[k][j] = 0; 

    //标记包含于已安排作业集合M中中的作业
    //即作业E.s及之前的作业
    for(k=0; k<=E.s; k++)
        for(j=0; j<2; j++)
            y[a[E.x[k]][j]][j] = 1;

    f1 = E.f1 + M[E.x[E.s]][0];   //sum1[k+1] = sum1[k] + tk+1,1 
    f2 = ((f1>E.f2)?f1:E.f2) + M[E.x[E.s]][1];  //sum2[k+1] = max{sum1[k+1], sum2[k]} +tk+1,2
    int sf2 = E.sf2 + f2;
    int s1 = 0, s2 = 0;
    int k1 = n - E.s,  k2 = n - E.s;  //不包含于已安排作业集合M中作业的个数
    int f3 = f2;

    //计算s1值 
    //如果第k个作业在机器1上完成处理后能立即在机器2上开始处理,机器1无空闲时间
    //s1 = ∑{ sum1[k] + (n-r+1)tr,1 + tr,2 } // k+1 =< r <=n
    for(j=0; j<n; j++)
        if(!y[j][0])  //不包含于已安排作业集合M中
        {
            k1--;
            if(k1 == n-E.s-1)
                f3 = (f2>f1+b[j][0])?f2:f1+b[j][0];
            s1 += f1 + k1 * b[j][0];
        }

    //计算s2值
    //如果机器2无空闲时间
    //s2 = ∑{ max{sum2[k], sum1[k]+min{ti,1}} + (n-r+1)tr,2 } // i不在M中, k+1 =< r
    for(j=0; j<n; j++)
        if(!y[j][1])
        {
            k2--;
            s1 += b[j][1];
            s2 += f3 + k2 * b[j][1];
        }
    //∑fm2 + max(s1, s2) /* m在M中,fm2为第m个作业的完成时间 */
    return sf2 + (s1>s2?s1:s2);  
}

int flowShop()
{
    //解批处理作业调度问题的优先队列分支界限法
    sort();  //对各作业在机器1和机器2上所需时间的排序
    Node E;
    initNode(E, n);  //初始化
    bestc = 1000000;
    //搜索排列空间树
    while(E.s<=n)
    {
        if(E.s==n)  //叶结点
        {
            if(E.sf2 < bestc){ //更新最优值
                bestc = E.sf2; 
                int i;
                for(i=0; i<n; i++)
                    bestx[i] = E.x[i];
                delete []E.x;
            }
        }
        else  //扩展当前结点
        {
            int i;
            for(i=E.s; i<n; i++)
            {
                swap(E.x[E.s], E.x[i]);
                int f1, f2;
                int bb = bound(E, f1, f2, y); 
                if(bb<bestc) //如果可能产生最优解,结点入队列
                {
                    Node node;
                    newNode(node, E, f1, f2, bb, n);
                    pq.push(node);
                }
                swap(E.x[E.s], E.x[i]);
            }
            delete []E.x;  //完成结点扩展
        }

        //取下一个结点
        if(pq.empty())
            break;
        E = pq.top();
        pq.pop();
    }
    return bestc;
}

void init(int n1, int M1[3][2])
{
    n = n1;
    int i, j;
    for(i=0; i<n; i++)
        for(j=0; j<2; j++)
            M[i][j] = M1[i][j];
}

int main()
{
    int n1 = 3;
    int M1[3][2] = {
        {2, 1},
        {3, 1},
        {2, 3}
    };
    init(n1, M1);
    int i, j; 
    printf("作业在机器上的运行时间为:\n");
    printf("\t\t机器1\t\t机器2\n");
    for(i=0; i<n; i++)
    {
        printf("作业%d\t\t", i+1);
        for(j=0; j<2; j++)
            printf("%d\t\t", M[i][j]);
        printf("\n");
    }
    int best = flowShop();
    printf("最少完成时间和:%d\n", best);
    printf("最优调度:\n");
    for(i=0; i<n; i++)
        printf("%d ", bestx[i]+1);
    printf("\n");
    return 0;
}

《批处理作业调度》

    原文作者:分支限界法
    原文地址: https://blog.csdn.net/u012319493/article/details/49308125
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞