模拟退火解单机调度问题
1.模拟退火简介
请参考:https://www.cnblogs.com/GuoJiaSheng/p/4192301.html
百度百科:模拟退火
模拟退火还是比较好理解的。
2.单机调度问题简介
已知:有n个工件{ J1,J2, …, Jn }需要在一台机器M0上加工。工件i会在时刻ai到达机器处,并期望在时刻di前完成加工,另已知该工件的加工时长pi。
工件上的约束:每个工件上只可被机器加工一次,且只有在工件到达机器处后才可以被加工。
机器上的约束:每个机器某一个时刻最多只能执行一个工件,而且执行过程是非抢占的。
目标:给出调度方案,使调度总延迟时间最小。
总结:就是说一台机器加工一堆零件,每个零件加工一次,然后每个工件有到达时间,加工时间和期望完成时间,合理安排工件加工顺序使得最终的延迟时间之和最小。
测试用例:
转化为输入如下:
8
0 15 21
0 19 25
0 26 30
0 9 13
15 13 28
15 15 40
24 12 65
34 6 75
3.测试代码如下:
代码注释说明的很清楚了…
/**
* date: 2018-06-10
* author: silvester
* (utf-8) 编译环境:gcc version 5.3.0 (GCC) -std=c++11
*/
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#include<time.h>
#define INF 0x3f3f3f3f
#define T 2000//初始温度
#define MIN_T 1e-8//最低温度
#define DELATE 0.98//温度降低速率
#define ILOOP 100//每个温度下搜索后继节点的个数
struct Path{//一种调度方案
int obj[100];//这种调度方案给出的工件加工排列
int delay;//这种调度方案延时
};
struct Path bestpath;//最优调度方案
typedef struct object{//对工件定义一个结构体
int a,p,d;//三个变量分别是到达时间,加工时间,期望完成时间
}Obj;
Obj J[100];//工件集合
int n;//工件数量
void printPath(Path path)//输出一种调度方案的结果
{
int i;
for(i=0;i<n;i++){//输出依次加工的工件信息
printf("No.%d : %d %d %d\n",i+1,J[path.obj[i]].a,J[path.obj[i]].p,J[path.obj[i]].d);
}
printf("delay:%d\n",path.delay);//输出延时
}
void Init()//初始化,给出一种排列
{
int now=0;
bestpath.delay=0;
for(int i=0;i<n;i++)
{
//按输入工件的顺序做初始化排列顺序
bestpath.obj[i]=i;
//对这种排列进行延时计算
if(now<J[i].a) now=J[i].a;
now+=J[i].p;
if(now>J[i].d) bestpath.delay+=(now-J[i].d);
}
}
/*获取某一策略的后继节点*/
Path getNext(Path path)
{
Path ansP=path;//先把path整体赋值给后继
int i,now=0,xx,yy;
//这里获取后继节点的方式为:对传入的path中的排列,随机交换排列中的两个位置对应的工件编号
xx=rand()%n;
yy=rand()%n;//获取两个随机位置
//交换这两个位置的工件编号
int temp=ansP.obj[xx];
ansP.obj[xx]=ansP.obj[yy];
ansP.obj[yy]=temp;
//重新计算延时
ansP.delay=0;
for(i=0;i<n;i++)
{
int x=ansP.obj[i];
if(now<J[x].a) now=J[x].a;
now+=J[x].p;
if(now>J[x].d) ansP.delay+=(now-J[x].d);
}
return ansP;//返回
}
//模拟退火
void SA()
{
Init();//初始化
//int k=1;
int i;
double t=T;//t为初始温度
Path newPath=bestpath;//newPath作为后继
srand((unsigned)(time(NULL)));//随机种子
while(MIN_T<t){//当前温度大于最低温度时一直循环
for(i=0;i<ILOOP;i++){//获取ILOOP个后继节点
newPath=getNext(bestpath);//获取当前最优策略的后继
double dE=(double)(newPath.delay-bestpath.delay);//计算后继与当前最优的delay差值
if(dE<0){//dE为负,说明找到了更优策略,则更新最优
bestpath=newPath;
}
else{//否则以一定概率跳出该最优,这个概率为exp(-dE/t)
int rd=rand()%101;
if(rd<(int)(exp(-dE/t)*100)){
bestpath=newPath;
}
}
}
//printf("No.%d : delay=%d\n",k++,bestpath.delay);
t=t*DELATE;//温度降低
}
}
int main()
{
int i=0,x,y,z;
freopen("ins2.txt","r",stdin);
// freopen("out.txt","w",stdout);
scanf("%d",&n);
while(i<n){//读取输入
scanf("%d %d %d",&x,&y,&z);
J[i].a=x;
J[i].p=y;
J[i].d=z;
i++;
}
long start_time=clock();
SA();//调用模拟退火算法
long end_time=clock();
//输出最优解
printPath(bestpath);
//输出SA算法运行时间
printf("time = %ld ms\n",end_time-start_time);
return 0;
}
结果:
整个迭代过程图示如下:
注:以上所有操作均在作者在网上搜集资料后,在个人电脑上实验成功,若读者实验时失败,可能由一些未知因素导致,可与作者联系。编写的教程可能由于疏忽出错,请与作者联系。