本文着重於算法的實現,對於理論部分可自行查看有關資料可以簡略參考該博文:http://blog.csdn.net/u013007900/article/details/50379135
本文代碼部分基於C實現,源碼如下:

1 /***************************************************************************** 2 ** Copyright: NEW NEU laboratory 3 ** File name: CTSP 4 ** Description:禁忌搜索算法解決TSP問題 5 ** Author: 1702-GCJ 6 ** Version: 1.1 7 ** Date: 2017/10/3 8 ** History: 無 9 ** Modification: IsFindTabu(Queue * Q,const Tabu *item) 10 *****************************************************************************/ 11 12 #include"stdio.h" 13 #include"stdlib.h" 14 #include"string.h" 15 #include "time.h" 16 17 #define CityNum 31 //城市的數量 18 #define TabuLength 21 //禁忌表長度(根號下的 種類) 19 #define Neighbor 400 //鄰居個數 20 #define MaxNG 400 //迭代次數 21 22 int currentNG = 0; //當前迭代次數 23 int DesireLevel = 0; //渴望水平 (即最短距離) 24 25 typedef int ElementType; 26 ElementType **Distance; //存儲各個城市之間的距離矩陣的指針 數據都是取整的 27 28 /***************************************************************************************讀取數據區******************************************/ 29 30 /************************************************* 31 **Function: MemBlockCity 32 **Description: 申請存儲城市距離空間 33 **Calls: 無 34 **Called By: ReadDataTxt() 35 **Input: 無 36 **Output: 無 37 **Return: 指向存儲城市距離空間的指針 38 **Others: 用完需要釋放掉相應內存 39 *************************************************/ 40 ElementType ** MemBlockCity(); 41 42 /************************************************* 43 **Function: PrintCityDistance 44 **Description: 顯示Distance信息 45 **Calls: 無 46 **Called By: main() 47 **Input: Distance 全局變量的指針 48 **Output: 無 49 **Return: void 50 **Others: 無 51 *************************************************/ 52 void PrintCityDistance( ElementType ** distance); 53 54 /************************************************* 55 **Function: ReadDataTxt 56 **Description: 從txt文檔中讀取數據 57 **Calls: MemBlockCity() 58 **Called By: main 59 **Input: 無 60 **Output: 無 61 **Return: void 62 **Others: 里面直接用的全局變量 指針Distance 63 *************************************************/ 64 void ReadDataTxt(); 65 66 /************************************************* 67 **Function: WriteDataTxt 68 **Description: 將Distance全局數組數據寫到txt文檔中去 69 **Calls: 無 70 **Called By: main() 71 **Input: 無 72 **Output: 無 73 **Return: void 74 **Others: 里面用到了宏值CityNum值 75 *************************************************/ 76 void WriteDataTxt(); 77 /**********************************************************************************禁忌表操作區*******************************************/ 78 typedef struct _Tabu{ 79 int smallNum; 80 int bigNum; //存儲數量大的元素 81 }Tabu; //禁忌表結構 82 83 typedef struct _Queue{ 84 Tabu *tabuList;//隊列空間指針 85 int rear; //指向尾部 86 int front; //指向隊列的頭部 87 int maxSize; //記錄隊列的最大個數 88 int count; //記錄資源個數 判斷隊列空滿 89 int tabuIndex; //在禁忌表中找到禁忌元素時 存儲該值在禁忌表中的位置 90 }Queue;//循環隊列的形式 91 92 /************************************************* 93 **Function: CreateQueue 94 **Description: malloc一個禁忌表隊列並初始化 95 **Calls: 無 96 **Called By: main() 97 **Input: tabuLength 禁忌表數據長度 98 **Output: 無 99 **Return: Queue * 隊列變量 100 **Others: 里面用到了宏值CityNum值 ,用完需要釋放掉相應內存 101 *************************************************/ 102 Queue * CreateQueue(int tabuLength); 103 104 /************************************************* 105 **Function: UpdateTabu 106 **Description: 更新禁忌表 107 **Calls: IsFindTabu() 108 **Called By: TSP() 109 **Input: Q 禁忌表隊列 item 加入禁忌表的Tabu結構的變量 110 **Output: 無 111 **Return: void 112 **Others: 113 *************************************************/ 114 void UpdateTabu(Queue *Q,Tabu *item); 115 116 /************************************************* 117 **Function: IsFindTabu 118 **Description: 禁忌表中是否找到這個元素 119 **Calls: 無 120 **Called By: UpdateTabu() TSP() 121 **Input: Q 禁忌表隊列 item 判斷其是否在禁忌表中的Tabu結構的變量的指針 122 **Output: 無 123 **Return: 0 沒有找到這個元素 1 找到這個元素了 124 **Others: 125 *************************************************/ 126 static int IsFindTabu(Queue * Q,const Tabu *item); 127 128 /****************************************************************************2Opt鄰域+TSp核心算法*********************************************/ 129 //定義解的存儲類型 向量形式 130 typedef struct _Solve{ 131 ElementType *initSolution; //初始解 132 ElementType *currentSolution; //當前解 133 ElementType * optimalSolution; //最優解 134 ElementType *tempSolution; //臨時解 135 ElementType lastdistance; //上次記錄的總距離 136 ElementType initdistance; //定義初始距離 137 }StrSolve; 138 typedef struct _MotionTable{ 139 Tabu tabu; //存儲改變的元素 140 ElementType changedistance; //改變的距離 141 }MotionTable;//記錄2opt鄰域移動信息 142 143 StrSolve * SolutionSpace ; //解空間(包含當前解和初始解)指針 144 MotionTable *motionTable; //移動元素信息 (一個表格) 145 146 /************************************************* 147 **Function: CreateMotionStruct 148 **Description: 創建並初始化2-Opt 移動信息表格 149 **Calls: 無 150 **Called By: Init2Opt() 151 **Input: neighbor 鄰居數量 152 **Output: 無 153 **Return: MotionTable *指針變量 154 **Others: 不用這塊內存的時候要釋放掉 ! 155 *************************************************/ 156 MotionTable* CreateMotionStruct(int neighbor); 157 158 /************************************************* 159 **Function: CreateSolutionSpace 160 **Description: 創建並初始化解空間 161 **Calls: 無 162 **Called By: Init2Opt() 163 **Input: cityNum 城市數量 164 **Output: 無 165 **Return: StrSolve *指針變量 166 **Others: 不用這塊內存的時候要逐一釋放掉 ! 167 *************************************************/ 168 StrSolve *CreateSolutionSpace(int cityNum); 169 170 /************************************************* 171 **Function: GetInitSolution 172 **Description: 獲得初始解 173 **Calls: 無 174 **Called By: Init2Opt() 175 **Input: StrSolve * 指針變量 176 **Output: 無 177 **Return: StrSolve *指針變量 178 **Others: 這里在初始化解的時候可以用其他元啟發式算法得出一個較好的解 ! 179 *************************************************/ 180 void GetInitSolution(StrSolve * strSolve); 181 182 /************************************************* 183 **Function: Init2Opt 184 **Description: 初始化TSP需要用的值 185 **Calls: CreateMotionStruct() CreateSolutionSpace() GetInitSolution() 186 **Called By: main 187 **Input: 無 188 **Output: 無 189 **Return: void 190 **Others: 這里在初始化解的時候可以用其他元啟發式算法得出一個較好的解 ! 不知道為什么只能在Main函數中調用否則 會出現段錯誤 191 *************************************************/ 192 void Init2Opt(); 193 194 /************************************************* 195 **Function: FindPosition 196 **Description: 在數組中找到指定元素值的位置 197 **Calls: 198 **Called By: Get2OptChangeDistance() TSP() 199 **Input: solution 一維數組指針 tabu Tabu結構指針 200 **Output: 無 201 **Return: void 202 **Others: 這里是從solution[1]開始查找到的! 203 *************************************************/ 204 static void FindPosition(const ElementType * solution,Tabu *tabu); 205 206 /************************************************* 207 **Function: FindPosition 208 **Description: 獲得2鄰域變化值 209 **Calls: FindPosition() 210 **Called By: Get2optSolution() 211 **Input: tabu Tabu結構指針 solution 一維數組指針 212 **Output: 無 213 **Return: ElementType 2鄰域城市變化值 214 **Others: 返回的值越小越好 ! 215 *************************************************/ 216 static ElementType Get2OptChangeDistance(Tabu * tabu,const ElementType * solution); 217 218 /************************************************* 219 **Function: Get2optSolution 220 **Description: 得到1個2鄰域解 將移動元素,及其導致路徑的變化值 存儲到移動表中 221 **Calls: Get2OptChangeDistance() 222 **Called By: TSP() 223 **Input: strSolve 解空間指針 motiontable 移動表指針 224 **Output: 無 225 **Return: void 226 **Others: 隨機數要注意! 227 *************************************************/ 228 void Get2optSolution(const StrSolve * strSolve,MotionTable *motiontable ); 229 230 /************************************************* 231 **Function: Insert_Sort 232 **Description: 按從小到大排序 插入排序 將制定的類數組變量 的內容進行排序 233 **Calls: 無 234 **Called By: FindBestMotionValue() 235 **Input: motiontable 移動表指針 n為類數組 元素個數 236 **Output: 無 237 **Return: void 238 **Others: 239 *************************************************/ 240 void Insert_Sort (MotionTable * motiontable,int n); 241 242 /************************************************* 243 **Function: FindBestMotionValue 244 **Description: 找到移動表中最小的值 即為最優值 245 **Calls: Insert_Sort() 246 **Called By: TSP() 247 **Input: motiontable 移動表指針 248 **Output: 無 249 **Return: MotionTable *型的指針 存儲移動表中最好值的表格指針 250 **Others: 251 *************************************************/ 252 MotionTable * FindBestMotionValue( MotionTable * motiontable); 253 254 /************************************************* 255 **Function: GetInitLevel 256 **Description: 獲取初始解的渴望水平 257 **Calls: 258 **Called By: TSP() 259 **Input: distance 存儲城市的矩陣指針 initSolution 初始解指針 260 **Output: 無 261 **Return: 初始解的渴望水平 262 **Others: 263 *************************************************/ 264 int GetInitLevel( ElementType **distance,ElementType * initSolution); 265 266 /************************************************* 267 **Function: TSP 268 **Description: TSP核心算法 269 **Calls: GetInitLevel() 270 **Called By: TSP() Get2optSolution() FindBestMotionValue() UpdateTabu() FindPosition() memcpy() 271 **Input: distance 存儲城市的矩陣指針 solutionSpace 解空間指針 motiontable 移動表 desireLevel 渴望水平 queue 禁忌表隊列指針 272 **Output: 最優解信息 273 **Return: void 274 **Others: 275 *************************************************/ 276 void TSP( ElementType **distance, StrSolve * solutionSpace ,MotionTable *motiontable,int *desireLevel,Queue *queue); 277 278 /************************************************* 279 **Function: MemFree 280 **Description: 釋放申請的動態內存 281 **Calls: free() 282 **Called By: main() 283 **Input: distance 存儲城市距離的變量指針 queue 禁忌表隊列 motiontable 移動表的指針 strSolve 解空間的指針 284 **Output: 無 285 **Return: void 286 **Others: 這里也可以一步一步的釋放掉 各自的指針 因為就用一個.c所以釋放內存的操作都在這里進行 287 *************************************************/ 288 void MemFree(ElementType ** distance,Queue *queue,MotionTable *motiontable,StrSolve *strSolve); 289 290 291 /*******************************************************************************MAIN函數*************************************/ 292 int main(int argc,char *argv[]) 293 { 294 // Tabu item; 295 clock_t start, finish; 296 double duration; 297 Queue * queue = CreateQueue(TabuLength); //創建一個禁忌表隊列 本身就初始化好了 298 Init2Opt();//初始化相關 299 // 設置隨機數種子 為以后使用rand()做准備 300 srand((unsigned int)time(0)); 301 302 start = clock(); 303 ReadDataTxt(Distance);//必須放在前面 讀取數據后 才能操作 304 // PrintCityDistance(Distance); //顯示二維數組的數據 只顯示5X5 305 // WriteDataTxt(Distance);//將distance 數據寫入txt 306 TSP( Distance,SolutionSpace ,motionTable,&DesireLevel,queue); 307 308 //// //將得到的最優解 從新用TSP算法算 309 // memcpy( SolutionSpace->initSolution,SolutionSpace->optimalSolution,sizeof(ElementType)*CityNum ); //將臨時解空間值復制到當前解空間 310 // printf("\n新初始解的渴望水平:%d \n",GetInitLevel(Distance,SolutionSpace->optimalSolution)); 311 // TSP( Distance,SolutionSpace ,motionTable,&DesireLevel,queue); 312 // 313 finish = clock(); 314 duration = (double)(finish - start) / CLOCKS_PER_SEC; 315 printf("\n TSP算法運行時間:%.4f秒 \n",duration); 316 MemFree(Distance, queue,motionTable,SolutionSpace); 317 return 0; 318 } 319 320 321 /************************************************************************讀取數據區***********************************************/ 322 323 /************************************************* 324 **Function: MemBlockCity 325 **Description: 申請存儲城市距離空間 326 **Calls: 無 327 **Called By: ReadDataTxt() 在txt文檔中讀取數據 328 **Input: 無 329 **Output: 無 330 **Return: 指向存儲城市距離空間的指針 331 **Others: 無 332 *************************************************/ 333 ElementType ** MemBlockCity() 334 { 335 ElementType ** Distance; 336 int i=0; 337 338 //動態申請一塊內存存儲城市之間的數據 339 Distance = (ElementType **)malloc(sizeof(ElementType *)*CityNum); 340 for(i = 0;i< CityNum ; i++){ 341 Distance[i] = (ElementType *)malloc(sizeof (ElementType )* CityNum); 342 } 343 return Distance; 344 } 345 346 /************************************************* 347 **Function: PrintCityDistance 348 **Description: 顯示Distance信息 這里僅僅顯示了CityNum-25個元素 因為屏幕顯示不開 349 **Calls: 無 350 **Called By: main() 351 **Input: Distance 全局變量的指針 352 **Output: 無 353 **Return: void 354 **Others: 無 355 *************************************************/ 356 void PrintCityDistance( ElementType ** distance) 357 { 358 int i,j; 359 for(i = 0; i< CityNum-25;i++){ 360 for(j = 0;j<CityNum-25;j++) 361 printf("%d ",distance[i][j]); 362 printf("\n"); 363 } 364 } 365 366 /************************************************* 367 **Function: ReadDataTxt 368 **Description: 從txt文檔中讀取數據 369 **Calls: MemBlockCity() 370 **Called By: main() 371 **Input: 無 372 **Output: 無 373 **Return: void 374 **Others: 里面直接用的全局變量 指針Distance 375 *************************************************/ 376 void ReadDataTxt() 377 { 378 // FILE *fpRead=fopen("F:\\GCJ\\Desktop\\智能優化方法作業\\data.txt","r"); 379 FILE *fpRead=fopen("data.txt","r"); //從data.txt中讀取數據 380 int i,j; 381 if(fpRead==NULL){ 382 printf("open file data.txt failed!\n"); 383 exit(1); 384 } 385 Distance = MemBlockCity(); //申請一塊存儲城市數量空間 386 for(i=0;i<CityNum;i++){ 387 Distance[i][i] = 0; 388 for(j=i+1 ;j < CityNum;j++ ){ 389 fscanf(fpRead,"%d",&Distance[i][j]);//自動讀取數據 只要自己能夠控制好存儲位置即可 390 Distance[j][i] = Distance[i][j]; 391 } 392 } 393 fclose(fpRead); 394 } 395 396 /************************************************* 397 **Function: WriteDataTxt 398 **Description: 將Distance全局數組數據寫到txt文檔中去 399 **Calls: 無 400 **Called By: main() 401 **Input: 無 402 **Output: 無 403 **Return: void 404 **Others: 里面用到了宏值CityNum值 405 *************************************************/ 406 void WriteDataTxt(ElementType **distance) 407 { 408 FILE *fpWrite; 409 int i,j; 410 fpWrite=fopen("F:\\GCJ\\Desktop\\智能優化方法作業\\data.txt","w"); //從data.txt中寫數據 411 for(i = 0;i< CityNum;i++){ 412 for(j=0;j<CityNum;j++) 413 fprintf(fpWrite,"%d ",distance[i][j]);//這里%d后面必須要有空格 否則 直接輸出連續的數字 414 fprintf(fpWrite,"\n"); 415 } 416 fclose(fpWrite); 417 } 418 419 /**************************************************************禁忌表操作區*****************************************************/ 420 421 /************************************************* 422 **Function: CreateQueue 423 **Description: malloc一個禁忌表隊列並初始化 424 **Calls: 無 425 **Called By: main() 426 **Input: tabuLength 禁忌表數據長度 427 **Output: 無 428 **Return: Queue * 隊列變量 429 **Others: 里面用到了宏值CityNum值 430 *************************************************/ 431 Queue * CreateQueue(int tabuLength) 432 { 433 Queue * queue = (Queue *)malloc(sizeof(struct _Queue));//申請一塊隊列變量 434 //queue->tabuList =(ElementType *)malloc(sizeof(ElementType)*MaxSize);//申請一塊數組空間 435 queue->tabuList =(Tabu *)malloc(sizeof(Tabu)*tabuLength);//21的長度 436 queue->front = 0; 437 queue->rear = 0;//頭尾 都為0 438 queue->maxSize = tabuLength; 439 queue->count =0; 440 queue->tabuList[0].smallNum = 0; 441 queue->tabuList[0].bigNum = 0; 442 return queue; 443 } 444 445 /************************************************* 446 **Function: IsFindTabu 447 **Description: 禁忌表中是否找到這個元素 448 **Calls: 無 449 **Called By: UpdateTabu() TSP() 450 **Input: Q 禁忌表隊列 item 判斷其是否在禁忌表中的Tabu結構的變量的指針 451 **Output: 無 452 **Return: 0 沒有找到這個元素 1 找到這個元素了 453 **Others: 454 *************************************************/ 455 static int IsFindTabu(Queue * Q,const Tabu *item) 456 { 457 Tabu tabu; 458 int i; 459 int IsFindFlag = 0 ; 460 461 //將要禁忌的值按順序放在中間變量中 方便加入到禁忌表中 462 if( (*item).bigNum >= (*item).smallNum ){ 463 tabu.bigNum = (*item).bigNum; 464 tabu.smallNum = (*item).smallNum; 465 } 466 else{ 467 tabu.bigNum = (*item).smallNum; 468 tabu.smallNum = (*item).bigNum; 469 } 470 471 //查找禁忌表中是否有這個禁忌元素 沒有的話 插入元素在頭部 否則把這個元素加上懲罰政策加入到禁忌表的頭部 其他依次降序 472 for(i = Q->front; (i%TabuLength)!= Q->rear; ){//這個查找函數有問題了 因為循環隊列的話 隊列慢點話 rear = front 如何解決? 473 if( (tabu.smallNum == Q->tabuList[i].smallNum ) && ( tabu.bigNum == Q->tabuList[i].bigNum ) ){ 474 //說明在禁忌表中找到這個元素了 那么就懲罰這個 放在最前面 475 //把第一個元素放入 這個值 剩下的依次 遞減排列 476 // printf("在禁忌表中找到了%d %d\n",tabu.bigNum,tabu.smallNum); 477 478 //新加 記錄位置 479 Q->tabuIndex = i; 480 481 IsFindFlag = 1; 482 return IsFindFlag ; //表示不管了 483 } 484 if(++i >= TabuLength)//僅僅讓i 在 0 - Tabulength范圍內遍歷 485 i = 0; 486 } 487 if( Q->count >= TabuLength ){//說明禁忌表滿 那么rear值就需要訪問了 否則不需要訪問 488 if( i%TabuLength == Q->rear )//因為循環隊列尋找的時候 最后一個元素 無法通過for循環遍歷到 489 if( (tabu.smallNum == Q->tabuList[i].smallNum ) && ( tabu.bigNum == Q->tabuList[i].bigNum ) ){ 490 // printf("找到了最新的了%d %d\n",tabu.smallNum,tabu.bigNum); 491 492 //新加 記錄位置 493 Q->tabuIndex = Q->rear; 494 495 IsFindFlag = 1; 496 return IsFindFlag ; //表示不管了 497 } 498 } 499 500 return IsFindFlag;//之前這里就忘了加了 注意這點 !! 501 } 502 503 /************************************************* 504 **Function: UpdateTabu 505 **Description: 更新禁忌表 506 **Calls: IsFindTabu() 507 **Called By: TSP() 508 **Input: Q 禁忌表隊列 item 加入禁忌表的Tabu結構的變量的指針 509 **Output: 無 510 **Return: void 511 **Others: 512 *************************************************/ 513 void UpdateTabu(Queue *Q,Tabu *item) 514 { 515 Tabu tabu; 516 Tabu temptabu; 517 int i; 518 519 //將要禁忌的值按順序放在中間變量中 方便加入到禁忌表中 520 if( (*item).bigNum >= (*item).smallNum ){ 521 tabu.bigNum = (*item).bigNum; 522 tabu.smallNum = (*item).smallNum; 523 } 524 else{ 525 tabu.bigNum = (*item).smallNum; 526 tabu.smallNum = (*item).bigNum; 527 } 528 529 if( !IsFindTabu(Q,item) ){ 530 //如果沒有找到 那么直接在隊列插入這個元素 531 if( Q->count < TabuLength ){ //說明隊列不滿 那就直接插入元素 532 Q->count++ ;//最后滿的時候為21個元素 533 Q->tabuList[Q->rear++] = tabu;//在后面插入 然后從前面取出元素 534 if( Q->rear >= TabuLength)//到了尾部的話 就直接從前面開始存儲 尾部先存儲后+1 535 --Q->rear ;//說明禁忌表滿了的時候 讓rear指向最后一個元素即可 536 } 537 else{//滿了的話 就直接頭部刪除 尾部加入 //不是真正的刪除 僅僅是釋放掉這塊存儲空間 538 if( ++Q->front >= TabuLength ) 539 Q->front =0; 540 if( ++Q->rear >= TabuLength)//到了尾部的話 就直接從前面開始存儲 尾部先存儲后+1 541 Q->rear = 0; 542 Q->tabuList[Q->rear] = tabu; 543 } 544 } 545 else{//在禁忌表中找到這個元素的時候 需要進行懲罰 將這個值放在頭部,而該值前面的數依次向后排 546 int j,k; 547 j = Q->tabuIndex ; //禁忌表中找到的該值的索引 548 k = Q->front; //禁忌表頭部索引 549 550 if( Q->tabuIndex >= Q->front ){ 551 552 //說明禁忌表沒有滿 或者 禁忌表滿了 但是移動僅僅在Q->front 到這個索引即可 553 for( --j ;j >= k ; --j){ 554 Q->tabuList[j+1] = Q->tabuList[j]; 555 }/*for end*/ 556 557 } 558 else{ 559 //禁忌表滿了且 Q->front 值大於 Q->tabuIndex 560 for( ;j == Q->front; --j ){ 561 if( j >= 1) 562 Q->tabuList[j] =Q->tabuList[j-1]; 563 else{ //j == 0 564 j = TabuLength ; 565 Q->tabuList[0] = Q->tabuList[j-1]; 566 } 567 }/*for ...end */ 568 } 569 //懲罰策略 570 Q->tabuList[Q->front] = tabu; 571 572 }/*if find .. else ..end*/ 573 574 } 575 576 /******************************************************************************************2Opt鄰域+TSp核心算法***********************************/ 577 578 /************************************************* 579 **Function: CreateMotionStruct 580 **Description: 創建並初始化2-Opt 移動信息表格 581 **Calls: 無 582 **Called By: Init2Opt() 583 **Input: neighbor 鄰居數量 584 **Output: 無 585 **Return: MotionTable *指針變量 586 **Others: 不用這塊內存的時候要釋放掉 ! 587 *************************************************/ 588 MotionTable* CreateMotionStruct(int neighbor) 589 { 590 int i; 591 MotionTable * motiontable = (MotionTable *)malloc(sizeof(MotionTable)*neighbor ); 592 for(i = 0;i< neighbor;i++){ 593 motiontable->tabu.smallNum =0; 594 motiontable->tabu.bigNum = 0; 595 motiontable->changedistance = 0; 596 } 597 return motiontable; 598 } 599 600 /************************************************* 601 **Function: CreateSolutionSpace 602 **Description: 創建並初始化解空間 603 **Calls: 無 604 **Called By: Init2Opt() 605 **Input: cityNum 城市數量 606 **Output: 無 607 **Return: StrSolve *指針變量 608 **Others: 不用這塊內存的時候要逐一釋放掉 ! 609 *************************************************/ 610 StrSolve *CreateSolutionSpace(int cityNum) 611 { 612 int i; 613 StrSolve *strSolve = (StrSolve *)malloc( sizeof(StrSolve) ) ; 614 strSolve->initSolution = ( ElementType *)malloc(sizeof(ElementType)*cityNum ); 615 strSolve->currentSolution = ( ElementType *)malloc(sizeof(ElementType)*cityNum ); 616 strSolve->optimalSolution = ( ElementType *)malloc(sizeof(ElementType)*cityNum ); 617 strSolve->tempSolution = ( ElementType *)malloc(sizeof(ElementType)*cityNum ); 618 619 //初始化解空間 620 for(i = 0;i< cityNum;i++){ 621 strSolve->initSolution[i] = (ElementType)0; 622 strSolve->currentSolution[i] = (ElementType)0; 623 strSolve->optimalSolution[i] = (ElementType)0; 624 strSolve->tempSolution[i] = (ElementType)0; 625 } 626 strSolve->lastdistance = 0;//記錄上次迭代獲得最好的距離值 627 return strSolve; 628 } 629 630 /************************************************* 631 **Function: GetInitSolution 632 **Description: 獲得初始解 633 **Calls: 無 634 **Called By: Init2Opt() 635 **Input: StrSolve * 指針變量 636 **Output: 無 637 **Return: StrSolve *指針變量 638 **Others: 這里在初始化解的時候可以用其他元啟發式算法得出一個較好的解 ! 639 @brief :思路 可以用一個記錄初始解的類數組(申請的內存 大小為初始解的元素個數),之后循環 CityNum-1次,不斷的產生1-CityNum-1的隨機數 640 沒產生一個就記錄這個值 之后再次產生與上次不同的隨機數 ,依次這樣循環即可 不過速度上會很慢 641 *************************************************/ 642 void GetInitSolution(StrSolve * strSolve) 643 { 644 int i; 645 646 //默認從0號城市順序開始 這里的0是固定不動的 647 for( i = 0;i<CityNum;i++){ 648 strSolve->initSolution[i] = i; 649 strSolve->currentSolution[i] = i; 650 strSolve->optimalSolution[i] = i; 651 strSolve->tempSolution[i] = i; 652 } 653 654 } 655 656 /************************************************* 657 **Function: Init2Opt 658 **Description: 初始化TSP需要用的值 659 **Calls: CreateMotionStruct() CreateSolutionSpace() GetInitSolution() 660 **Called By: main() 661 **Input: 無 662 **Output: 無 663 **Return: void 664 **Others: 這里在初始化解的時候可以用其他元啟發式算法得出一個較好的解 ! 不知道為什么只能在Main函數中調用否則 會出現段錯誤 665 *************************************************/ 666 void Init2Opt() 667 { 668 motionTable = CreateMotionStruct(Neighbor);//初始化變化表 記錄變化鄰居值 669 SolutionSpace = CreateSolutionSpace(CityNum);//創建解空間 670 GetInitSolution(SolutionSpace);//初始化解 671 } 672 673 /************************************************* 674 **Function: MemFree 675 **Description: 釋放申請的動態內存 676 **Calls: 677 **Called By: main() 678 **Input: distance 存儲城市距離的變量指針 queue 禁忌表隊列 motiontable 移動表的指針 strSolve 解空間的指針 679 **Output: 無 680 **Return: void 681 **Others: 這里也可以一步一步的釋放掉 各自的指針 因為就用一個.c所以釋放內存的操作都在這里進行 682 *************************************************/ 683 void MemFree(ElementType ** distance,Queue *queue,MotionTable *motiontable,StrSolve *strSolve) 684 { 685 int i=0; 686 int j = 0; 687 688 //釋放矩陣元素存儲區 689 for(i = 0;i < CityNum; i++){ 690 free( distance[i] ); 691 } 692 free(distance); 693 694 //釋放移動表 695 free(motiontable); 696 697 //釋放掉隊列區 698 free(queue->tabuList); 699 free(queue); 700 701 //釋放解空間 702 free(strSolve->initSolution); 703 free(strSolve->currentSolution); 704 free(strSolve->optimalSolution); 705 free(strSolve->tempSolution); 706 free(strSolve); 707 708 } 709 710 /************************************************* 711 **Function: FindPosition 712 **Description: 在數組中找到指定元素值的位置 713 **Calls: 714 **Called By: Get2OptChangeDistance() TSP() 715 **Input: solution 一維數組指針 tabu Tabu結構指針 716 **Output: 無 717 **Return: void 718 **Others: 這里是從solution[1]開始查找到的! 719 *************************************************/ 720 static void FindPosition(const ElementType * solution,Tabu *tabu) 721 { 722 int i; 723 Tabu tempTabu; 724 for(i = 1; i< CityNum;i++){ 725 if( solution[i] == tabu->smallNum ) 726 tempTabu.smallNum = i; 727 if( solution[i] == tabu->bigNum ) 728 tempTabu.bigNum = i; 729 } 730 *tabu = tempTabu;//不能直接返回&tempTabu 因為這個是一個局部的變量 會有懸掛指針的后果 731 } 732 733 /************************************************* 734 **Function: FindPosition 735 **Description: 獲得2鄰域變化值 736 **Calls: FindPosition() 737 **Called By: Get2optSolution() 738 **Input: tabu Tabu結構指針 solution 一維數組指針 739 **Output: 無 740 **Return: ElementType 2鄰域城市變化值 741 **Others: 返回的值越小越好 ! 742 *************************************************/ 743 static ElementType Get2OptChangeDistance(Tabu * tabu,const ElementType * solution) 744 { 745 ElementType change1,change2; 746 Tabu tempTabu1 = *tabu; 747 Tabu tempTabu; 748 change1 = change2 = 0; 749 FindPosition(solution,&tempTabu1); //此時這里的tempTabu1存儲的就是指定元素在 解空間中的位置 750 tempTabu.bigNum = ( tempTabu1.bigNum >tempTabu1.smallNum )? tempTabu1.bigNum: tempTabu1.smallNum; 751 tempTabu.smallNum = ( tempTabu1.bigNum >tempTabu1.smallNum )? tempTabu1.smallNum: tempTabu1.bigNum; 752 753 if( tempTabu.smallNum == tempTabu.bigNum-1){//兩個元素在解空間中的 位置相差1 754 if( tempTabu.bigNum == CityNum-1 ){ //最大值位置 在最后一個位置 755 change1 = Distance[ solution[tempTabu.smallNum -1] ][ solution[tempTabu.smallNum] ]+\ 756 Distance[ solution[tempTabu.bigNum] ][ solution[ 0] ]; 757 change2 = Distance[ solution[tempTabu.smallNum -1] ][ solution[tempTabu.bigNum] ] +\ 758 Distance[ solution[tempTabu.smallNum] ][ solution[0] ]; 759 return (change2 - change1);//這個值越小越好 760 } 761 else{ 762 change1 = Distance[ solution[tempTabu.smallNum -1] ][ solution[tempTabu.smallNum] ] +\ 763 Distance[ solution[tempTabu.bigNum] ][ solution[ tempTabu.bigNum +1] ]; 764 change2 = Distance[ solution[tempTabu.smallNum -1] ][ solution[tempTabu.bigNum] ] +\ 765 Distance[ solution[tempTabu.smallNum] ][ solution[tempTabu.bigNum +1] ]; 766 767 return (change2 - change1); 768 } 769 } 770 else{//兩個元素位置 不挨着 771 if( tempTabu.bigNum == CityNum-1 ){ //最大值位置 在最后一個位置 772 change1 = Distance[ solution[tempTabu.smallNum -1] ][ solution[tempTabu.smallNum] ] +\ 773 Distance[ solution[tempTabu.smallNum] ][ solution[tempTabu.smallNum +1] ] +\ 774 Distance[ solution[tempTabu.bigNum-1] ][ solution[ tempTabu.bigNum ] ] +\ 775 Distance[ solution[tempTabu.bigNum] ][ solution[ 0] ]; 776 change2 = Distance[ solution[tempTabu.smallNum -1] ][ solution[tempTabu.bigNum] ] +\ 777 Distance[ solution[tempTabu.bigNum] ][ solution[tempTabu.smallNum+1] ] +\ 778 Distance[ solution[tempTabu.bigNum-1] ][ solution[ tempTabu.smallNum ] ]+\ 779 Distance[ solution[tempTabu.smallNum] ][ solution[0] ]; 780 return (change2 - change1);//這個值越小越好 781 } 782 else{ 783 784 change1 = Distance[ solution[tempTabu.smallNum -1] ][ solution[tempTabu.smallNum] ] +\ 785 Distance[ solution[tempTabu.smallNum] ][ solution[tempTabu.smallNum +1] ] +\ 786 Distance[ solution[tempTabu.bigNum-1] ][ solution[ tempTabu.bigNum ] ] +\ 787 Distance[ solution[tempTabu.bigNum] ][ solution[ tempTabu.bigNum +1] ]; 788 change2 = Distance[ solution[tempTabu.smallNum -1] ][ solution[tempTabu.bigNum] ] +\ 789 Distance[ solution[tempTabu.bigNum] ][ solution[tempTabu.smallNum+1] ] +\ 790 Distance[ solution[tempTabu.bigNum-1] ][ solution[ tempTabu.smallNum ] ]+\ 791 Distance[ solution[tempTabu.smallNum] ][ solution[tempTabu.bigNum +1] ]; 792 return (change2 - change1); 793 } 794 } 795 796 } 797 798 /************************************************* 799 **Function: Get2optSolution 800 **Description: 得到1個2鄰域解 將移動元素,及其導致路徑的變化值 存儲到移動表中 801 **Calls: Get2OptChangeDistance() 802 **Called By: TSP() 803 **Input: strSolve 解空間指針 motiontable 移動表指針 804 **Output: 無 805 **Return: void 806 **Others: 隨機數要注意! 807 *************************************************/ 808 void Get2optSolution(const StrSolve * strSolve,MotionTable *motiontable ) 809 { 810 //產生一個1-CityNum-1之間的隨機數 因為默認0為初始位置 不能動 811 ElementType temp; 812 ElementType changeDistance; 813 int rand1,rand2; 814 815 // rand1 = (CityNum-1) *rand()/(RAND_MAX + 1.0); 816 rand1 = rand()%(CityNum-1)+1; 817 rand2 = rand()%(CityNum-1)+1; 818 while( rand2 == rand1 )//必須產生兩個不同的隨機數 切不能為0 819 rand2 = rand()%(CityNum-1) +1; 820 821 //記錄交換的兩個元素 (不是位置) 822 motiontable->tabu.smallNum = (rand2 >rand1)? rand1:rand2; 823 motiontable->tabu.bigNum = (rand2 >rand1)? rand2:rand1; 824 motiontable->changedistance = Get2OptChangeDistance( &motiontable->tabu ,strSolve->tempSolution ); 825 826 } 827 828 /************************************************* 829 **Function: Insert_Sort 830 **Description: 按從小到大排序 插入排序 將制定的類數組變量 的內容進行排序 831 **Calls: 無 832 **Called By: FindBestMotionValue() 833 **Input: motiontable 移動表指針 n為類數組 元素個數 834 **Output: 無 835 **Return: void 836 **Others: 837 *************************************************/ 838 void Insert_Sort (MotionTable * motiontable,int n) 839 { 840 //進行N-1輪插入過程 841 int i,k; 842 for(i=1; i<n; i++){ 843 //首先找到元素a[i]需要插入的位置 844 845 int j=0; 846 while( (motiontable[j].changedistance < motiontable[i].changedistance ) && (j <i ) ) 847 j++; 848 849 //將元素插入到正確的位置 850 if(i != j){ //如果i==j,說明a[i]剛好在正確的位置 851 MotionTable temp = motiontable[i]; 852 for(k = i; k > j; k--){ 853 motiontable[k] = motiontable[k-1]; 854 } 855 motiontable[j] = temp; 856 } 857 } 858 } 859 860 /************************************************* 861 **Function: FindBestMotionValue 862 **Description: 找到移動表中最小的值 即為最優值 863 **Calls: Insert_Sort() 864 **Called By: TSP() 865 **Input: motiontable 移動表指針 866 **Output: 無 867 **Return: MotionTable *型的指針 存儲移動表中最好值的表格指針 868 **Others: 869 *************************************************/ 870 MotionTable * FindBestMotionValue( MotionTable * motiontable) 871 { 872 //下面是僅僅找到一個最好的值 不管在不在禁忌表中 873 // MotionTable *bestMotion= motiontable; 874 // MotionTable *start = motiontable; 875 // MotionTable *end = motiontable + Neighbor-1; 876 // while(start++ < end ){ 877 // if( start->changedistance < bestMotion->changedistance){ 878 // bestMotion = start;//保存最好的結構 879 // } 880 // } 881 // if( start->changedistance < bestMotion->changedistance ) 882 // bestMotion = start; 883 // return bestMotion;//f返回最好結構的指針 884 Insert_Sort(motiontable,Neighbor);//選擇排序算法 從小到大排 885 886 return motiontable;//返回最好元素的地址 887 } 888 889 /************************************************* 890 **Function: GetInitLevel 891 **Description: 獲取初始解的渴望水平 892 **Calls: 893 **Called By: TSP() 894 **Input: distance 存儲城市的矩陣指針 initSolution 初始解指針 895 **Output: 無 896 **Return: 初始解的渴望水平 897 **Others: 898 *************************************************/ 899 int GetInitLevel( ElementType **distance,ElementType * initSolution) 900 { 901 int i; 902 int SumLevel = 0; 903 for(i = 0; i < CityNum-2 ; i++){ 904 SumLevel += distance[ initSolution[i] ][ initSolution[i+1] ]; 905 } 906 SumLevel+= distance[ initSolution[i] ][0];//最后在加上 最后一個值和初始值的 距離 才是循環的總距離距離 907 908 return SumLevel; 909 } 910 911 /************************************************* 912 **Function: TSP 913 **Description: TSP核心算法 914 **Calls: GetInitLevel() 915 **Called By: TSP() Get2optSolution() FindBestMotionValue() UpdateTabu() FindPosition() memcpy() 916 **Input: distance 存儲城市的矩陣指針 solutionSpace 解空間指針 motiontable 移動表 desireLevel 渴望水平 queue 禁忌表隊列指針 917 **Output: 最優解信息 918 **Return: void 919 **Others: 920 *************************************************/ 921 void TSP( ElementType **distance, StrSolve * solutionSpace ,MotionTable *motiontable,int *desireLevel,Queue *queue) 922 { 923 int i; 924 int temp; 925 int neighborNum = 0; 926 MotionTable * BestMotionStruct; 927 ElementType BestChangeDistance;//最好的改變的值 928 // Init2Opt();//初始化相關 929 *desireLevel = GetInitLevel(distance,solutionSpace->initSolution); 930 solutionSpace->lastdistance = *desireLevel;//初始最優值為上次移動的最好的距離 931 solutionSpace->initdistance = solutionSpace->lastdistance;//將初始值給初始距離 之后再判斷 減少的距離 932 printf("初始距離:%d ",*desireLevel); 933 // printf("初始最好的距離是%d,solutionSpace->lastdistance = %d\n",*desireLevel,solutionSpace->lastdistance); 934 printf("城市數量:%d 迭代次數:%d 鄰居個數:%d\n",CityNum,MaxNG,Neighbor); 935 //迭代 次數作為停止條件 936 while( currentNG++ < MaxNG ){ 937 //獲得鄰居最好解 938 for( neighborNum = 0; neighborNum < Neighbor; neighborNum++ ){//循環Neighbor那么多次 939 Get2optSolution(SolutionSpace,&motionTable[neighborNum] );//將鄰域 移動放在移動表中 940 } 941 942 //找到移動表中最小的值 此時解若是 < 渴望水平 則更新最優解 否則找到不在禁忌表中的 最好的解 更新當前解 943 BestMotionStruct = FindBestMotionValue( motiontable); 944 BestChangeDistance = BestMotionStruct->changedistance; 945 946 if( solutionSpace->lastdistance + BestChangeDistance < *desireLevel){//當前迭代出的最好的解 小於渴望水平 更新最優解T表當前解 947 int temp; 948 //更新T表 949 UpdateTabu(queue,&BestMotionStruct->tabu); 950 //更新渴望水平 951 *desireLevel = solutionSpace->lastdistance +BestChangeDistance; 952 //更新上次迭代的最優值 953 solutionSpace->lastdistance = *desireLevel; 954 //更新當前解和最優解 955 FindPosition(solutionSpace->tempSolution,&BestMotionStruct->tabu);//找到當前解 對應的解空間的位置 956 temp = solutionSpace->tempSolution[ BestMotionStruct->tabu.smallNum ]; 957 solutionSpace->tempSolution[ BestMotionStruct->tabu.smallNum] = solutionSpace->tempSolution[ BestMotionStruct->tabu.bigNum ]; 958 solutionSpace->tempSolution[ BestMotionStruct->tabu.bigNum ] = temp; 959 memcpy( solutionSpace->currentSolution,solutionSpace->tempSolution,sizeof(ElementType)*CityNum ); //將臨時解空間值復制到當前解空間 960 memcpy( solutionSpace->optimalSolution,solutionSpace->tempSolution,sizeof(ElementType)*CityNum ); 961 962 } 963 else{//沒有小於渴望水平 找到不在禁忌表中最好的移動 964 //在移動表中找到不在禁忌表中最好元素 因為拍好序了 所以從表的第二個值開始找即可 965 int i; 966 for(i = 0;i< Neighbor; i++){ 967 if( !IsFindTabu(queue,&motiontable[i].tabu) ){ 968 int temp; 969 //不在禁忌表中 則這個值就是目前來說最好的值 970 BestMotionStruct = &motiontable[i]; 971 //更新T表 972 UpdateTabu(queue,&BestMotionStruct->tabu); 973 solutionSpace->lastdistance = solutionSpace->lastdistance + BestMotionStruct->changedistance; 974 //更新當前解 975 FindPosition(solutionSpace->tempSolution,&BestMotionStruct->tabu);//找到當前解 對應的解空間的位置 976 temp = solutionSpace->tempSolution[ BestMotionStruct->tabu.smallNum ]; 977 solutionSpace->tempSolution[ BestMotionStruct->tabu.smallNum] = solutionSpace->tempSolution[ BestMotionStruct->tabu.bigNum ]; 978 solutionSpace->tempSolution[ BestMotionStruct->tabu.bigNum ] = temp; 979 memcpy( solutionSpace->currentSolution,solutionSpace->tempSolution,sizeof(ElementType)*CityNum ); //將臨時解空間值復制到當前解空間 980 981 break;//跳出循環 982 } 983 } 984 } 985 986 } 987 currentNG = 0;//將全局迭代次數變量值清零 988 printf("\n初始值:%d 最優解值:%d 優化距離:%d\n最優解元素:\n\n",\ 989 solutionSpace->initdistance,\ 990 GetInitLevel(distance,solutionSpace->optimalSolution),solutionSpace->initdistance - *desireLevel); 991 for(i = 0 ;i< CityNum;i++){ 992 printf("%d-> ",solutionSpace->optimalSolution[i]); 993 } 994 printf( "%d \n",solutionSpace->optimalSolution[0] ); 995 }
試驗結果如下:
相關資源(源碼包以及數據)可在以下網址下載:
http://download.csdn.net/download/geself/10191257
運行環境:windows7
IDE: DEVC++