1 /*****************************************************************************2 ** Copyright: NEW NEU laboratory3 ** File name: SA_工作指派问题4 ** Description:模拟退火算法解决工作指派问题5 ** Author: 1702--GCJ6 ** Version: 1.07 ** Date: 2017/10/48 ** History: 无9 *****************************************************************************/
10
11 #include"stdio.h"
12 #include"stdlib.h"
13 #include"string.h"
14 #include "time.h"
15 #include "math.h"
16
17 /*----------------------------------------------------18 @brief 参数配置区19 */
20 #define WORK_NUM 100 //工作数量
21 #define WORKER_NUM 100 //工人数量
22 #define INIT_TEM (60 + WORK_NUM * 10) //初始温度
23 #define END_TEM 60 //终止温度
24 #define De_TEM 2 //降温函数
25 #define INTER_WHILE 500 //内循环次数 类似于邻居个数
26
27 typedef intElementType;28 ElementType **Time; //存储工人工作时间 指针
29 ElementType CurrentTem; //当前温度30
31 //定义解的存储类型 向量形式
32 typedef struct_Solve{33 ElementType *initSolution; //初始解//每个元素对应的序号表示工人 总序号表示工人总数 内部元素表示工人对应的工作
34 ElementType *currentSolution; //当前解
35 ElementType * optimalSolution; //最优解
36 ElementType *tempSolution; //临时解
37 ElementType OptimalSolutionValue; //记录最优解 (总时间)
38 ElementType CurrentSolutionValue; //记录上次的值
39 ElementType NextSolutionValue ; //记录交换后的总时间
40
41 }StrSolve;//存储解结构
42
43 StrSolve * SolutionSpace ; //解空间(包含当前解和初始解)指针
44
45 typedef struct_Tabu{46 intsmallNum;47 int bigNum; //存储数量大的元素
48 }Tabu; //禁忌表结构
49
50 typedef struct_MotionTable{51 Tabu tabu; //存储改变的元素
52 ElementType changedistance; //改变的距离
53 }MotionTable;//记录2opt邻域移动信息
54
55 /*************************************************56 **Function: MemBlockWork57 **Description: 申请存储工人工作时间的空间58 **Calls: 无59 **Called By: ReadDataTxt()60 **Input: 无61 **Output: 无62 **Return: 指向存储工人工作时间的指针63 **Others: 无64 *************************************************/
65 ElementType **MemBlockWork();66
67 /*************************************************68 **Function: ReadDataTxt69 **Description: 从txt文档中读取工人工作时间数据70 **Calls: MemBlockWork()71 **Called By: main()72 **Input: 无73 **Output: 无74 **Return: void75 **Others: 里面直接用的全局变量 指针Time76 *************************************************/
77 voidReadDataTxt();78
79 /*************************************************80 **Function: CreateSolutionSpace81 **Description: 创建并初始化解空间82 **Calls: 无83 **Called By: Init2Opt()84 **Input: worker_num 工人数量85 **Output: 无86 **Return: StrSolve *指针变量87 **Others: 不用这块内存的时候要逐一释放掉 !88 *************************************************/
89 StrSolve *CreateSolutionSpace(intworker_num);90
91 /*************************************************92 **Function: GetInitSolution93 **Description: 获得初始解94 **Calls: 无95 **Called By: Init2Opt()96 **Input: StrSolve * 指针变量97 **Output: 无98 **Return: StrSolve *指针变量99 **Others: 这里在初始化解的时候可以用其他元启发式算法得出一个较好的解 ! 工人工作不能重复及数组空间的数字不能重复100 *************************************************/
101 void GetInitSolution(StrSolve *strSolve);102
103 /*************************************************104 **Function: Get2optSolution105 **Description: 得到1个2邻域解 用tempSolution来存储106 **Calls:107 **Called By: SA()108 **Input: solutionSpace 解空间指针109 **Output: 无110 **Return: void111 **Others: 随机数要注意!112 *************************************************/
113 void Get2optSolution( StrSolve *solutionSpace );114
115 /*************************************************116 **Function: Init2Opt117 **Description: 初始化SA需要用的值118 **Calls: CreateSolutionSpace() GetInitSolution()119 **Called By: main()120 **Input: 无121 **Output: 无122 **Return: void123 **Others: 这里在初始化解的时候可以用其他元启发式算法得出一个较好的解 ! 不知道为什么只能在Main函数中调用否则 会出现段错误124 *************************************************/
125 voidInit2Opt();126
127 /*************************************************128 **Function: GetSumTime129 **Description: 获取当前解的总工作时间130 **Calls:131 **Called By: SA()132 **Input: distance 存储工人工作时间的矩阵指针 Solution 解指针133 **Output: 无134 **Return: 总工作时间135 **Others: 里面用到了WORKER_NUM 宏136 *************************************************/
137 int GetSumTime( ElementType **distance,ElementType *Solution);138
139 /*************************************************140 **Function: SA141 **Description: 模拟退火算法142 **Calls: GetSumTime() Get2optSolution() memcpy() rand() exp()143 **Called By: main()144 **Input: solutionSpace 解空间指针145 **Output: 最优值信息 工人工作分配146 **Return: 无147 **Others:148 *************************************************/
149 void SA( StrSolve *solutionSpace);150
151 /*************************************************152 **Function: MemFree153 **Description: 释放申请的动态内存154 **Calls: free()155 **Called By: main()156 **Input: distance 存储工人工作时间矩阵 strSolve 解空间的指针157 **Output: 无158 **Return: void159 **Others: 这里也可以一步一步的释放掉 各自的指针 因为就用一个.c所以释放内存的操作都在这里进行160 *************************************************/
161 void MemFree(ElementType ** distance,StrSolve *strSolve);162
163 /*******************************************************************************MAIN函数*************************************/
164 int main(int argc,char *argv[])165 {166 clock_t start, finish;167 doubleduration;168
169 //设置随机数种子 为以后使用rand()做准备
170 srand((unsigned int)time(0));171 Init2Opt();172
173 //从读取数据开始的起始时间
174 start =clock();175
176 //将工人工作时间的数据存储到Time指向的空间中
177 ReadDataTxt(Time);178
179 //模拟退火算法开始
180 SA(SolutionSpace);181
182 //第二次用模拟退火183 //memcpy( SolutionSpace->currentSolution,SolutionSpace->optimalSolution,sizeof(ElementType)*WORKER_NUM );184 //memcpy( SolutionSpace->initSolution,SolutionSpace->optimalSolution,sizeof(ElementType)*WORKER_NUM );185 //memcpy( SolutionSpace->tempSolution,SolutionSpace->optimalSolution,sizeof(ElementType)*WORKER_NUM );186 //GetInitSolution(SolutionSpace);//初始化解187 //SA(SolutionSpace);188
189 //结束时间
190 finish =clock();191 duration = (double)(finish - start) /CLOCKS_PER_SEC;192 printf("\n SA算法运行时间:%.4f秒 \n",duration);193
194 //释放申请的内存
195 MemFree(Time,SolutionSpace);196
197 return 0;198 }199
200 /*************************************************201 **Function: MemBlockWork202 **Description: 申请存储工人工作时间的空间203 **Calls: 无204 **Called By: ReadDataTxt()205 **Input: 无206 **Output: 无207 **Return: 指向存储工人工作时间的指针208 **Others: 无209 *************************************************/
210 ElementType **MemBlockWork()211 {212 ElementType **Distance;213 int i=0;214
215 //动态申请一块内存存储工人工作时间
216 Distance = (ElementType **)malloc(sizeof(ElementType *)*WORKER_NUM);217 for(i = 0;i< WORKER_NUM; i++){218 Distance[i] = (ElementType *)malloc(sizeof (ElementType )*WORK_NUM);219 }220 returnDistance;221 }222
223 /*************************************************224 **Function: ReadDataTxt225 **Description: 从txt文档中读取工人工作时间数据226 **Calls: MemBlockWork()227 **Called By: main()228 **Input: 无229 **Output: 无230 **Return: void231 **Others: 里面直接用的全局变量 指针Time232 *************************************************/
233 voidReadDataTxt()234 {235 //FILE *fpRead=fopen("F:\\GCJ\\Desktop\\智能优化方法作业\\data.txt","r");
236 FILE *fpRead=fopen("data.txt","r"); //从data.txt中读取数据
237 inti,j;238 if(fpRead==NULL){239 printf("open file data.txt failed!\n");240 exit(1);241 }242 Time = MemBlockWork(); //申请一块存储城市数量空间
243 for( i = 0;i < WORKER_NUM; i++){244 for(j=0 ;j < WORK_NUM ;j++){245 fscanf(fpRead,"%d",&Time[i][j]);//自动读取数据 只要自己能够控制好存储位置即可246 //printf("Time[%d][%d] = %d ",i,j,Time[i][j]);
247 }248 }249 fclose(fpRead);250 }251
252 /*************************************************253 **Function: CreateSolutionSpace254 **Description: 创建并初始化解空间255 **Calls: 无256 **Called By: Init2Opt()257 **Input: worker_num 工人数量258 **Output: 无259 **Return: StrSolve *指针变量260 **Others: 不用这块内存的时候要逐一释放掉 !261 *************************************************/
262 StrSolve *CreateSolutionSpace(intworker_num)263 {264 inti;265 StrSolve *strSolve = (StrSolve *)malloc( sizeof(StrSolve) ) ;266 strSolve->initSolution = ( ElementType *)malloc(sizeof(ElementType)*worker_num );267 strSolve->currentSolution = ( ElementType *)malloc(sizeof(ElementType)*worker_num );268 strSolve->optimalSolution = ( ElementType *)malloc(sizeof(ElementType)*worker_num );269 strSolve->tempSolution = ( ElementType *)malloc(sizeof(ElementType)*worker_num );270
271 //初始化解空间
272 for(i = 0;i< worker_num;i++){273 strSolve->initSolution[i] = (ElementType)0;274 strSolve->currentSolution[i] = (ElementType)0;275 strSolve->optimalSolution[i] = (ElementType)0;276 strSolve->tempSolution[i] = (ElementType)0;277 }278 strSolve->CurrentSolutionValue = 0;279 strSolve->NextSolutionValue = 0;280 strSolve->OptimalSolutionValue = 0;281
282 returnstrSolve;283 }284
285 /*************************************************286 **Function: GetInitSolution287 **Description: 获得初始解288 **Calls: 无289 **Called By: Init2Opt()290 **Input: StrSolve * 指针变量291 **Output: 无292 **Return: StrSolve *指针变量293 **Others: 这里在初始化解的时候可以用其他元启发式算法得出一个较好的解 ! 工人工作不能重复及数组空间的数字不能重复294 *************************************************/
295 void GetInitSolution(StrSolve *strSolve)296 {297 inti;298 //产生0- WORK_NUM-1 (工作数量减1) 之间的随机数不能重复299 //默认从0号工作顺序开始
300 for( i = 0;i < WORKER_NUM;i++){301 strSolve->initSolution[i] =i;302 strSolve->currentSolution[i] =i;303 strSolve->optimalSolution[i] =i;304 strSolve->tempSolution[i] =i;305 }306
307 }308
309 /*************************************************310 **Function: Get2optSolution311 **Description: 得到1个2邻域解 用tempSolution来存储312 **Calls:313 **Called By: SA()314 **Input: solutionSpace 解空间指针315 **Output: 无316 **Return: void317 **Others: 随机数要注意!318 *************************************************/
319 void Get2optSolution( StrSolve *solutionSpace )320 {321 //产生一个 0 - - WORKER-1之间的随机数 表示交换工人对应的工作数 [0,WORKER)
322 MotionTable motiontable;323 ElementType temp;324 //ElementType changeDistance;
325 intrand1,rand2;326 //rand1 = (CityNum-1) *rand()/(RAND_MAX + 1.0);
327 rand1 = rand()%WORKER_NUM; //[0,WORKER_NUM)
328 rand2 = rand()%WORKER_NUM;329 while( rand2 == rand1 ) //必须产生两个不同的随机数
330 rand2 = rand()%WORKER_NUM;331
332 //记录交换的两个工人编号
333 motiontable.tabu.smallNum = (rand2 >rand1)?rand1:rand2;334 motiontable.tabu.bigNum = (rand2 >rand1)?rand2:rand1;335
336 //更新当前解//用临时解作为j解
337 temp = solutionSpace->tempSolution[ motiontable.tabu.smallNum ];338 solutionSpace->tempSolution[ motiontable.tabu.smallNum] = solutionSpace->tempSolution[ motiontable.tabu.bigNum ];339 solutionSpace->tempSolution[ motiontable.tabu.bigNum ] =temp;340
341 //motiontable->changedistance = Get2OptChangeDistance( &motiontable->tabu ,strSolve->tempSolution );
342
343 }344
345 /*************************************************346 **Function: Init2Opt347 **Description: 初始化SA需要用的值348 **Calls: CreateSolutionSpace() GetInitSolution()349 **Called By: main()350 **Input: 无351 **Output: 无352 **Return: void353 **Others: 这里在初始化解的时候可以用其他元启发式算法得出一个较好的解 ! 不知道为什么只能在Main函数中调用否则 会出现段错误354 *************************************************/
355 voidInit2Opt()356 {357 SolutionSpace = CreateSolutionSpace(WORKER_NUM);//创建解空间
358 GetInitSolution(SolutionSpace);//初始化解
359 }360
361 /*************************************************362 **Function: GetSumTime363 **Description: 获取当前解的总工作时间364 **Calls:365 **Called By: SA()366 **Input: distance 存储工人工作时间的矩阵指针 Solution 解指针367 **Output: 无368 **Return: 总工作时间369 **Others: 里面用到了WORKER_NUM 宏370 *************************************************/
371 int GetSumTime( ElementType **distance,ElementType *Solution)372 {373 //只要保证Solution 里面的值不一样即可算出
374 inti;375 int SumLevel = 0;376 for(i = 0; i < WORKER_NUM ; i++){377 SumLevel +=distance[ i ][ Solution[i] ];378 }379
380 returnSumLevel;381 }382
383 /*************************************************384 **Function: SA385 **Description: 模拟退火算法386 **Calls: GetSumTime() Get2optSolution() memcpy() rand() exp()387 **Called By: main()388 **Input: solutionSpace 解空间指针389 **Output: 最优值信息 工人工作分配390 **Return: 无391 **Others:392 *************************************************/
393 void SA( StrSolve *solutionSpace)394 {395 int i;//当前内循环次数
396 ElementType ChangeValue = 0;397 doublerand1;398
399 //更新初始值和最优解/值
400 solutionSpace->OptimalSolutionValue = GetSumTime( Time,solutionSpace->initSolution );401 solutionSpace->CurrentSolutionValue = solutionSpace->OptimalSolutionValue;402 //memcpy( solutionSpace->optimalSolution,solutionSpace->initSolution,sizeof(ElementType)* WORKER_NUM );//初始化的时候已经完成403
404 //设定当前温度为初始温度
405 CurrentTem =INIT_TEM;406 while( CurrentTem >=END_TEM){407
408 for( i = 0;i < INTER_WHILE ;i++){409
410 //获取 2邻域一个解//里面修改了临时解的邻域 在下面的if else if else 处理应对好了
411 Get2optSolution( solutionSpace );412
413 //计算目标值改变大小
414 solutionSpace->NextSolutionValue = GetSumTime( Time,solutionSpace->tempSolution );415 ChangeValue = solutionSpace->NextSolutionValue - solutionSpace->CurrentSolutionValue ;416
417 //Metropolis准则
418 if( ChangeValue < 0 ){//接受该解419
420 //更新当前解//不用更新临时解了 因为已经更新好了
421 memcpy( solutionSpace->currentSolution,solutionSpace->tempSolution,sizeof(ElementType)*WORKER_NUM );422 solutionSpace->CurrentSolutionValue = solutionSpace->NextSolutionValue;423
424 //判断是否更新最优解
425 if( solutionSpace->CurrentSolutionValue < solutionSpace->OptimalSolutionValue ){426
427 //更新最优解
428 memcpy( solutionSpace->optimalSolution,solutionSpace->currentSolution,sizeof(ElementType)*WORKER_NUM );429 solutionSpace->OptimalSolutionValue = solutionSpace->CurrentSolutionValue;430 }431 }/*Metropolis 准则 end*/
432 else if( exp ( -(1.00*ChangeValue)/CurrentTem ) > (rand1 = rand()/(RAND_MAX+1.0) ) ){ //如果大于随机数 那么也接受该点433
434 //更新当前解//不用更新临时解了 因为已经更新好了
435 memcpy( solutionSpace->currentSolution,solutionSpace->tempSolution,sizeof(ElementType)*WORKER_NUM );436 solutionSpace->CurrentSolutionValue = solutionSpace->NextSolutionValue;437
438 //判断是否更新最优解//实际上在这里肯定不会更新的 但是先不改了
439 if( solutionSpace->CurrentSolutionValue < solutionSpace->OptimalSolutionValue ){440 //更新最优解
441 memcpy( solutionSpace->optimalSolution,solutionSpace->currentSolution,sizeof(ElementType)*WORKER_NUM );442 solutionSpace->OptimalSolutionValue = solutionSpace->CurrentSolutionValue;443 }444 }445 else{//没有满足准则的时候 就要更新临时解为原来的currentSolution 因为获得2邻域解的时候修改了tempSolution
446
447 memcpy( solutionSpace->tempSolution,solutionSpace->currentSolution,sizeof(ElementType)*WORKER_NUM );448
449 }/*if ...else if ..else end*/
450
451 }/*for end 内循环*/
452
453 //更新降温函数 根据外层的循环次数而定
454 CurrentTem -=De_TEM;455
456 } /*while end*/
457
458 //输出历史最优值及工作分配
459 printf("\n工人工作时间最优值为:%d\n",solutionSpace->OptimalSolutionValue);460 printf("工人分配的工作为:\n");461 for( i = 0;i < WORKER_NUM; i++){462 printf("工人:%d 分配工作:%d\n",i+1,1+solutionSpace->optimalSolution[i]);463 }464 printf("\n工人工作时间最优值为:%d\n",solutionSpace->OptimalSolutionValue);465 }466
467 /*************************************************468 **Function: MemFree469 **Description: 释放申请的动态内存470 **Calls: free()471 **Called By: main()472 **Input: distance 存储工人工作时间矩阵 strSolve 解空间的指针473 **Output: 无474 **Return: void475 **Others: 这里也可以一步一步的释放掉 各自的指针 因为就用一个.c所以释放内存的操作都在这里进行476 *************************************************/
477 void MemFree(ElementType ** distance,StrSolve *strSolve)478 {479 int i=0;480 int j = 0;481
482 //释放矩阵元素存储区
483 for(i = 0;i < WORKER_NUM; i++){484 free( distance[i] );485 }486 free(distance);487
488 //释放解空间
489 free(strSolve->initSolution);490 free(strSolve->currentSolution);491 free(strSolve->optimalSolution);492 free(strSolve->tempSolution);493 free(strSolve);494
495 }