使用回溯法解决批处理作业调度问题

使用回溯法批处理作业调度问题(java实现)

  • 问题描述:设有 n个作业{J1,J2,……Jn}需要处理,每个作业Ji(1≤ i ≤ n)都有两项任务组成。两项任务需要分别在2台机器即机器1和机器2上处理。要求每个作业Ji 的第一项任务在机器1上处理,第二项任务在机器2上处理,并且第一项任务在机器1上处理完后,第二项任务才能在机器2上开始处理。规定每个作业Ji用 f(1,i)记录其在机器1上的处理时间(该时间是指从机器1启动到该作业完成的时间)。每个作业Ji用f(2,i)记录其在机器2上的处理时间(该时间是指从机器2启动到该作业完成的时间)。不同的作业调度方案处理完成所有作业所需的时间显然不同。批处理作业调度要求制定最佳作业调度方案,使其完成的时间和最小:
    在这里插入图片描述
    例如:有3个作业{J1,J2,J3}需要处理,作业Ji在机器1和机器2上的处理时间如下图。找出最优调度方案。
    使用回溯法解决批处理作业调度问题_第1张图片

  • 分析:
    1、定义解空间
    因为n个作业有n!种调度方案,因此,批处理作业调度问题的解空间对应n个作业的全排列。3个作业的调度方案对应3个作业的全排列。3个作业的全排列有6种,即 1,2,3、1,3,2、2,1,3、2,3,1、3,2,1、3,1,2。该问题的解决要求是,需要确定一种作业顺序,使得从第一个作业在机器1上开始处理到最后一个作业在机器2完成所需处理的时间最短。

    2、建立批处理解空间结构
    该问题的解空间可以表示为一颗排列树。其解空间树有 n!个叶结点,解空间树从树的根结点到叶节点的路径定义了批处理作业调度问题的一个调度方案。
    如图:
    使用回溯法解决批处理作业调度问题_第2张图片
    3、采用回溯法以深度优先的策略搜索解空间
    在搜索过程中,当 i > n时,表示搜索到达一个叶结点,得到一个新的调度方案,该方案由解空间树从树的根结点到叶结点的路径定义。
    如果 i≤ n,则当前的可扩展结点是解空间树中的一个内部结点。此时,选择下一个要安排的作业,以深度优先的方式递归地对想应的子树进行搜索。
    为了节省搜索成本,对于每个内部结点,在进入子树前,需要用限界函数检查其是否满足限界条件。若满足限界条件,则有可能产生新的作业调度方案,子结点为可行结点,搜索向其下一层进行;若不满足限界条件,则减去以其为根结点的子树,搜索往回移动,移至离其最近的活结点处,然后以相同的策略在解空间树中递归地进行搜索。

    4、时间复杂性分析
    由于算法backtrack在每个结点处耗费O(1)计算时间,所以在最坏情况下,批处理作业调度问题的回溯算法的总时间耗费是O(nn!)。

代码实现:

package backtrack;
public class FlowShop {
     
		 int n;//作业数
		 int f1;//机器1完成处理时间
		 int f;//完成时间和
		 int bestf;//当前最优值
		 int[][] m;//各作业所需的处理时间
		 int []x;//当前作业调度
		 int[] bestx;//当前最优作业调度
		 int[] f2;//机器2完成处理时间
		
		 //初始化数据成员
		public void bestFlow(int n,int[][] m) {
     
			this.n=n;
			this.m=m;
			f1=0;
			f=0;
			bestf=10000;//给定初始值
			bestx=new int[n+1];
			x=new int[n+1];
			
			//初始化,x[i]为原始排序
			for(int i=1;i<=n;i++){
     
				x[i]=i;
			}
			f2=new int[n+1];
			
			//调用backtrack函数,求解最优调度方案
			backtrack(1);
			
		}
		
		//swap函数实现更改作业调度顺序
		public  void swap(int[] x,int i,int j){
     
			int temp=x[i];
			x[i]=x[j];
			x[j]=temp;
		}
		
	 //回溯法实现批处理调度问题
		public  void backtrack(int i){
     
			//搜索第i个结点
			if(i>n){
      //i>n说明已到达叶结点
				for(int j=1;j<=n;j++) {
     
					bestx[j]=x[j];//获取当前的最优调度方案
				}
				bestf=f;//获取当前的最优值
			}
			else{
     
				
				for(int j=i;j<=n;j++){
     //调度每个作业
					//作业x[j]在第一台机器的时间
					f1+=m[x[j]][1];
					
					//f2[i]等于f2[i-1]和f1中较大者加上作业x[j]在第2台机器的时间
					f2[i]=((f2[i-1]>f1)?f2[i-1]:f1)+m[x[j]][2];
					f+=f2[i];
										
					if(f<bestf){
     //如果搜索结果小于当前最优解,则继续向下搜索
						swap(x,i,j);
						backtrack(i+1);
						swap(x,i,j);
					}
					//往回走时,还原数值
					f1-=m[x[j]][1];
					f-=f2[i];
				}
			}
		}
	
		public static void main(String[] args) {
     
			int n=3;
			int[][] m={
     {
     0,0,0},{
     0,2,1},{
     0,3,1},{
     0,2,3}};//m的下标从1开始,因此第一行的0和每一行第一列的0,仅为了方便开始顺序为1
			
			FlowShop f=new FlowShop();
	
			f.bestFlow(n, m);
			System.out.println("最优批处理作业调度顺序为:");
			for(int i=1;i<=n;i++) {
     
				System.out.print(f.bestx[i]+" ");
			}
			System.out.println();
			System.out.println("最优调度所需的最短时间为:"+f.bestf);
		}
	                  
}

你可能感兴趣的:(回溯法,#批处理作业调度问题,回溯法,批处理作业调度问题,java)