原創:TSP問題解決方案-----禁忌搜索算法C實現


本文着重於算法的實現,對於理論部分可自行查看有關資料可以簡略參考該博文: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 } 
View Code

試驗結果如下:

 

相關資源(源碼包以及數據)可在以下網址下載:

http://download.csdn.net/download/geself/10191257

運行環境:windows7

IDE:    DEVC++

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM