回溯法是一个既带有系统性又带有跳跃性的搜索算法。它在问题的解空间树中按深度优先策略,从根节点出发搜索解空间树。算法搜索至解空间树的任一结点时,先判断该结点是否包含问题的解。如果不包含则跳过,逐层向其祖先结点回溯;否则进入子树看,继续按深度优先策略搜索。回溯法求问题的所有解时,要回溯到根,且根结点的所有子树都搜索遍才结束。
批处理作业调度
一、问题描述
1.给定n个作业的集合{J1, J2, …, Jn}。每一个作业Ji都有两项任务需要分别在2台机器上完成。每个作业必须先由机器1处理,然后由机器2处理。
2.三个作业的调度方案对应三个作业的全排列。三个作业的6种可能的调度方案是1, 2, 3;1, 3, 2;2, 1, 3;2, 3, 1;3, 2, 1;3, 1, 2。批处理作业调度问题要求制定最佳作业调度方案,使其完成时间和达到最小。
3.举例:
设有一调度方案 x =[3, 1, 2] ,
对于x1 (作业3): f2,1=2+3=5;
对于x2 (作业1) :f2,2=5+1=6;
对于x3 (作业2) :f2,3=7+1=8;
F=f2,1+f2,2+f2,3=5+6+8=19
19就是该作业调度方案 x =[3, 1, 2] 的时间和。
规定每一个作业用 记录其在机器2处理的时间,则
二、问题解决
package backtrack;
public class FlowShop {
static int n=3; //作业数
static int f1=0; //机器1完成处理的时间
static int f=0; //完成时间和
static int bestf=9999; //当前最优值
static int[][] m=new int[n+1][n+1]; //各个作业的处理时间,如m[x[2]][1]表示,(其中x[2]=3)第2个作业所代表的作业3,在机器1上运行的时间,
//即前代表作业,后代表机器
static int[] x=new int[n+1]; //当前作业的调度,如x={1,2,3}
static int[] bestx=new int[n+1]; //当前最优作业调度
static int[] f2=new int[n+1]; //机器2完成处理时间
public static void backtrack(int i) { //表示安排的第几个作业
if(i>n) { //当i大于n时说明已经到达了叶结点,找到当前最优值
// System.out.print("当前最优值:");
for(int j=1;j<=n;j++) { //记录最优作业调度和最优完成时间
bestx[j]=x[j];
// System.out.print(bestx[j]+" ");
bestf=f;
}
// System.out.println();
// System.out.println("bestf="+bestf);
}
else //未达到叶结点时,继续搜索
for(int j=i;j<=n;j++) {
// System.out.println("i="+i);
// System.out.println("j="+j);
f1+=m[x[j]][1]; //作业在机器1上花费的时间(等待时间加自己本身的运行时间)
// System.out.println("f1="+f1);
f2[i]=(f2[i-1]>f1?f2[i-1]:f1)+m[x[j]][2];
// System.out.println("f2="+f2[i]);
/*
* 作业在机器2上花费的时间受两个因素影响:1.作业在机器1上花费的时间。2.上一个作业在机器2上花费的时间。
* 这两者的较大者就是作业在机器2上花费的时间。如作业1,2在机器1上的运行时间分别为{1,3},在机器2上运行的时间分别为{1,2},
* 那么如果作业1先运行,当祖业1在机器2上已经完成运行,作业2也不能立即在机器2上运行,因为这时作业2在机器1上还未运行完。
*/
f+=f2[i]; //所有作业的最终完成时间实际上就是在机器2上运行完所花费的时间
// System.out.println("完成时间f="+f);
if(f<bestf) { //限界函数,看当前的调度方案是否优于当前最优值
// System.out.println("进入子树");
// System.out.println("第一次swap");
swap(i,j);
// System.out.print("x=");
// for(int k=1;k<=n;k++)
// System.out.print(x[k]+" ");
// System.out.println();
backtrack(i+1);
// System.out.println("第二次swap");
swap(i,j);
// for(int k=1;k<=n;k++)
// System.out.print(x[k]+" ");
}
// System.out.println();
f1-=m[x[j]][1];
// System.out.println("回溯f1="+f1);
f-=f2[i];
// System.out.println("回溯f="+f);
}
}
public static void swap(int i,int j) {
int temp=x[i];
x[i]=x[j];
x[j]=temp;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
n=3;
int[] fa= {2,3,2}; //在机器1上的运行时间
int[] fb={1,1,3}; //在机器2上的运行时间
for(int i=1;i<=n;i++) {
m[i][1]=fa[i-1];
m[i][2]=fb[i-1];
}
for(int j=1;j<=n;j++)
x[j]=j;
for(int k=0;k<=n;k++)
f2[k]=0;
backtrack(1);
System.out.print("最优调度方案:");
for(int m=1;m<=n;m++)
System.out.print("作业"+bestx[m]+" ");
System.out.println();
System.out.print("最少花费时间:"+bestf);
}
}
运行结果
运算过程
三、时间复杂度分析
批作业处理调度的解空间结构是一棵排列树,如果作业总数为n,那么排列树共有n!叶个结点,backtrack算法在每一个结点处耗费O(1)计算时间,所以在最坏情况下,整个算法的时间复杂度为O(n!)。

京公网安备 11010502036488号