参考书籍:算法设计与分析——C++语言描述(第二版)
算法设计策略-动态规划法
流水作业调度
问题描述
假定处理一个作业需要执行若干项不同类型的任务,每一类任务只能在某一台设备上执行。设一条流水线上有n个作业
对于流水线上的作业调度,有两种基本的方式,一种是非抢先调度(nonpreemptive schedule),它要求在一台设备上处理一项任务,必须等到该任务处理完毕才能处理另一项任务。另一种是抢先调度(preemptive schedule),它允许暂时中断当前任务,转而先处理其他任务,随后再接着处理被暂时中断的任务。
作业i的完成时间
平均流动时间(mean flow time)
一组给定的作业的最优完成时间是
OFT:表示非抢先调度最优完成时间
POMT:表示抢先调度最优完成时间
OMFT:表示非抢先调度最优平均完成时间
POMFT:表示抢先调度最优平均完成时间
对于
下面只讨论当
双机流水作业调度描述为:设有n个作业的集合
动态规划法求解
设全部作业的集合为
定理:流水作业调度问题具有最优子结构的性质
由流水作业调度问题的最优子结构性质可知:
上式推广到一般情况,对任意作业子集S和作业i有:
Johnson不等式
设R是关于作业集合S的任一调度方案。假定设备
式中
如果在调度方案的作业排序中,上述作业i和j满足
交换作业i和j大的次序,得到另一种调度方案
式中,
假定i和j满足Johnson不等式,则有:
所以如果两个作业i和j不满足Johnson不等式,可交换它们的处理次序使之满足,这样就不会增加完成时间。
因此,存在一个最优作业调度,使得对于任意相邻的两个作业i和j,作业i先于j处理,都有
根据以上的讨论,可以设计下列作业排序方法。这样做能得到最优调度方案:
- 如果
min{a0,a1,⋯,an−1,b0,b1,⋯,bn−1} 是ai ,则ai 应是最优排列的第一个作业; - 如果
min{a0,a1,⋯,an−1,b0,b1,⋯,bn−1} 是bj ,则bj 应是最优排列的最后一个作业; - 继续1和2的做法,直到完成所有作业的排序。
Johnson算法
求解流水作业调度问题的Johnson算法具体描述如下。
- 设a[i]和b[i](
0≤i<n ) 分别为作业i在两台设备上的处理时间。建立由三元组(作业号,处理时间,设备号)组成的三元组表d。其中,处理时间是指每个作业所包含的两个任务中时间较少的处理时间。 - 对三元组表按处理时间排序,得到排序后的三元组表d。
- 对三元组表的每一项d[i](
0≤i<n ),从左右两端生成最优作业排列c[j](0≤j<n ),c[j]是作业号。如果d[i]的设备号为1,则将作业i置于c的左端末尾,否则置于c的右端末尾。
//Johnson算法
struct Triplet{
//三元组结构
int operator <(Triplet b)const {return t<b.t;}
int jobNo,t,ab;//jobNo为作业号,t为处理时间,ab为设备号
};
void FlowShop(int n,int *a,int *b,int *c)
{
Triplet d[mSize]={{0,0,0}};
for(int i=0;i<n;i++){
//算法步骤一,生成三元组表d
if(a[i]<b[i]){
d[i].jobNo=i;
d[i].ab=0;
d[i].t=a[i];
} else {
d[i].jobNo=i;
d[i].ab=1;
d[i].t=b[i];
}
}
Sort(d,n);//算法步骤二,任意排序算法
int left=0,right=n-1;
for(i=0;i<n;i++){
//算法步骤三,生成最优解
if(d[i].ab==0)
c[left++]=d[i].jobNo;
else
c[right--]=d[i].jobNo;
}
}
Johnson算法的时间取决于对作业集的排序,因此最坏情况下算法的时间复杂度为