回溯法 批处理作业调度问题

Posted JeffCoding

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了回溯法 批处理作业调度问题相关的知识,希望对你有一定的参考价值。

问题:

给定n个作业的集合J1,J2,…,Jn。每个作业必须先由机器1处理,然后由机器2处理。作业Ji需(1≤i≤n)要机器j(1≤j≤2)的处理时间为 tji。对于一个确定的作业调度,设Fji是作业i在机器j上完成处理的时间。所有作业在机器2上完成处理的时间和称为 该作业调度的完成时间和。
要求对于给定的n个作业,制定最佳作业调度方案,使其完成时间和达到最小。

tji机器1机器2
作业121
作业231
作业323

分析:

由于机器有两个,每个作业必须从机器1处理,再到机器2处理,所以会两个作业同时处理,例如对上面的表格按照(1,2,3)的顺序调度:
(括号内代表作业序号)

 机器1     机器2
  2(1)
  3(2)      1(1)
  2(3)      1(2)
            3(3)

每一个作业的处理时间 = 机器2的处理时间 + 机器2等待处理的时间(包括了之前作业的时间 和 本作业在机器1处理的时间)
所以我们求作业的处理时间,就是求该作业从开始到机器2处理完的时间:

作业1的处理时间:2+1 = 3
作业2的处理时间:2 + 3 + 1 = 6 (作业1的在机器1的处理时间,作业2的在机器1的处理时间 和 作业1在机器2的处理时间的较大时间(因为两者是同时进行的),作业2在机器2的处理时间)
作业3的处理时间:2 + 3 + 2 + 3 = 10

所以(1,2,3)顺序的作业调度得到的作业调度时间为19

再看问题,问题要求得到最优作业调度方案和最小调度时间,也就是要得到使作业调度时间最小的作业处理顺序
因此这是一个 排列树 的问题,用排列树的算法框架:

代码实现

排列树的算法框架有两种实现方式:
(1)用visted方式

#define MAX 200
#include<iostream>
using namespace std;

int number;//作业数量
int x1[10];//作业在机器1运行的时间
int x2[10];//作业在机器2运行的时间
int sum = 0;//作业完成的总时间 
int bestsum = MAX;//作业完成的最优时间 
int order[10];//作业完成的次序,要用于交换
int bestorder[10];//作业完成的最优的顺序
int f1 = 0;//机器1累计的时间 
int f2[10];//作业在机器2处理完累计的时间,即每一个作业的调度时间
int vis[10];//记录作业以否已被选 

void backtrack(int cur) //cur表示正在赋值的位置,cur+1去到下一层子节点,i递增,在当前层遍历兄弟节点
    if(cur>number) 
        for(int i=1; i<=number; i++) bestorder[i]=order[i];
        bestsum=sum;
     else 
        for(int i=1; i<=number; i++)  //遍历number,尝试填第一位
            if(!vis[i]) 
                vis[i] = 1;
                f1+=x1[i];
                //本作业的在机器1的处理时间 和 上一个作业在机器2的处理时间的较大时间(因为两者是同时进行的)
                f2[cur]=( f2[cur-1] > f1 ? f2[cur-1] : f1) + x2[i];
                sum+=f2[cur];
                order[cur] = i;
                if(sum<bestsum) //剪枝,如果当前sum都大于bestsum了,则不再遍历此节点 
                    backtrack(cur+1);
                
                //每计算一次,为了不影响父节点和兄弟节点,运算完都要复位
                sum-=f2[cur];
                f1-=x1[i];
                vis[i] = 0;
            
        
    


int main() 
    cout << "请输入作业的数量: "; 
    cin >> number;
    int i;
    cout << "请输入每个作业在机器1的运行时间:" << endl;
    for(i=1; i<=number; i++)
        cin >> x1[i];
    
    cout << endl << "请输入每个作业在机器2的运行时间:" << endl;
    for(i=1; i<=number; i++)
        cin >> x2[i];
    
    //初始化第一个序列,从1开始到number 
    for(i=1; i<=number; i++)
        order[i] = i;
    
    backtrack(1);
    cout<<"最节省的时间为:"<<bestsum<<endl;  
    cout<<"对应的方案为:";  
    for(i=1;i<=number;i++) cout<<bestorder[i]<<"  ";  
    cout<<endl;  

(2)用swap交换方式
除了backtrack,其它代码都没有改变,只贴出backtranck函数的代码:

void backtrack(int cur)
    //到达边界 
    if(cur > number)
        for(int i=1; i<=number; i++)
            bestorder[i] = order[i];
        
        bestsum = sum;
    else
        //cur表示正在赋值的位置,cur+1去到下一层子节点,i递增,在当前层遍历兄弟节点 
        for(int i=cur; i<=number; i++)
            f1 += x1[order[i]];
            f2[cur] = (f2[cur - 1] > f1 ? f2[cur -1] : f1) + x2[order[i]];
            sum += f2[cur];
            /*例如cur为2,i=3时,表示正在为作业队列的第2个位置赋作业3的值,所以此时的队列作业顺序应该是1,3,2, 所以要把i和cur的位置换掉;
            同理例如cur为1,i=2时,表示正在为作业队列的第1个位置赋作业2的值,此时的顺序是2,1,3,好好理解这一点 
            swap函数就是要做这个事情
            */ 
            swap(order[cur], order[i]);
            if(sum < bestsum)//剪枝,如果当前sum都大于bestsum了,则不再遍历此节点 
                backtrack(cur+1);
            

            swap(order[cur], order[i]);
            sum -= f2[cur];
            f1 -= x1[order[i]];
        
    

输入上面表格的数据,得出结果:

以上是关于回溯法 批处理作业调度问题的主要内容,如果未能解决你的问题,请参考以下文章

算法与程序设计:回溯法

回溯法

暴力穷举和回溯法(八皇后问题)

数独1--暴力回溯法(时间超)

回溯法编程技巧

算法入门经典-第七章 例题7-4-1 拓展 n皇后问题 回溯法