用模拟退火算法解旅行商问题
1.問題描述
旅行商問題(Travelling Salesman Problem, 簡記TSP,亦稱貨郎擔問題):設 有n個城市和距離矩陣D=[dij],其中dij表示 城市i到城市j的距離,i,j=1,2 … n,則問 題是要找出遍訪每個城市恰好一次的一條 回路并使其路徑長度為最短。
2.算法設計
旅行商問題是一個十分經典的NP難度問題,如果想找到真正的唯一最優的解復雜度是O(N!)的,所以求解這一類問題的策略就是找一個相對最優的解,也就是最優化問題。模擬退火算法就是一種啟發式的組合優算法,通過不斷迭代來尋找最優解。
旅行商問題的解可以表述為一個循環排列,我們可以把求解旅行商問題當做把n個城市的全排列進行對比,求最短的一條路徑,但是這樣復雜度太高,這里使用一個叫Mapkob鏈的東西,也就是城市的最排列次數不能超過Mapkob鏈長度(L = 20000)。對于當前已有的排列,產生新排列的過程使用2變換法,也就是在當前的隊列中任選兩個序號u和v(u<v),將u和v及其之間的順序逆轉。在逆轉之后需要重新計算新路徑長度,若新路徑長度小于當前路徑,就用新路徑代替當前路徑,否則使用模擬退火算法根據概率決定是否轉移。
數據結構:
struct Path{ //路徑,包括路徑和路徑長度int city[MaxCitylenth];double len; }; double d[MaxCitylenth][MaxCitylenth]; //城市之間的距離 struct City{ //每個城市坐標double x;double y; };1、新解產生:
新解的產生使用2變換法,即通過任選兩個序號u和v(u<v),將u和v及其之間的順序逆轉:
(π1 …πu-1πuπu+1 …πv-1πvπv+1 … πn) 變為
(π1 …πu-1πvπv-1 …πu-1πuπv+1 … πn)
在逆轉之后需要重新計算路徑長度,但是通過觀察,其實在u之前和v之后的長度已經計算過了,為了節約計算復雜度,這里我先將路徑長度減去u和v之間的長度,然后加上逆轉后u-v的路徑長度能得到新的路徑長度。
2、新解更新:
如果新解的路徑長度小于當前解,就用新解替換當前解,否則使用模擬退火算法計算概率,通過概率確定是否接受這個新解。公式如下:
其中t我初試設置為100,每次更新是ti+1 = a*ti,其中a是溫度下降控制參數,在代碼里為0.9。對每個t計算L次路徑,當對于臨近的兩個t的值,路徑沒有變化時,此時找到的路徑就是這次優化的結果,退出程序。圖1描述了新解產生與更新過程。
圖1 新解產生更新流程圖
###3.程序流程
1、按順序1-n的順序初始化path;
2、將s置為0,L置為20000,從0到L循環,由當前路徑生成新路徑;
3、如圖一更新當前解,若新解路徑長度小于當前解,就用新解替換當前解,否則用模擬退火算法判斷是否接受新解;
4、若循環L次后解都沒被替換,則s++,否則s = 0;
5、當s為2的時候,也就是連續兩個Mapkob鏈沒有變化時,退出程序,當前找到解就是最優解。
4.核心偽代碼
Path GenerateNewpath(){ //生成新路徑,使用2變換法Path newpath = path;int u = 0,v = 0;while(u == v){u = (int)(n * (rand() / (RAND_MAX + 1.0)));v = (int)(n * (rand() / (RAND_MAX + 1.0)));}int x = min(u,v);int y = max(u,v);if(x == 0 || y == n-1){ //若x是起點或y是終點,則要減去終點到起點的路徑長度newpath.len -= d[newpath.city[n-1]][newpath.city[0]];}if(x != 0){newpath.len -= d[newpath.city[x-1]][newpath.city[x]];}for(int i = x;i <= y;i++){ //減去u-v的路徑長度if(i < n-1) //如果v不是終點,則要減去v到v+1的長度newpath.len -= d[newpath.city[i]][newpath.city[i+1]];}for(int i = x,j = y;i < j;i++,j--){swap(newpath.city[i],newpath.city[j]);}if(x == 0||y == n-1){newpath.len += d[newpath.city[n-1]][newpath.city[0]];}if(x != 0){newpath.len += d[newpath.city[x-1]][newpath.city[x]];}for(int i = x;i <= y;i++){if(i < n-1){newpath.len += d[newpath.city[i]][newpath.city[i+1]];}}return newpath; } bool Accept(int l,double t){ //用模擬退火算法判斷是否接受目前路徑double rd = rand()/(RAND_MAX + 1.0);double e = exp(l/t);if(e > rd && e < 1){return true;}return false; }void SA(double a){ //模擬退火算法double t = T;int s = 0;while(s < 2){ //當s = 2時結束搜索//printf("s%d\n",s);int bchange = 0;int l = L;while(l--){ //對每個不同的t值遍歷l次Path newpath = GenerateNewpath();if(newpath.len < path.len){path = newpath;bchange++;}else if(Accept(path.len-newpath.len,t)){path = newpath;bchange++;}}t = a*t;if(!bchange){s++;}else{s = 0;}}return ;}void Calculatedis(){ //利用歐式距離計算兩個城市之間距離for(int i = 0;i < n;i++){for(int j = 0;j < n;j++){d[i][j] = sqrt((city[i].x-city[j].x)*(city[i].x-city[j].x)+(city[i].y-city[j].y)*(city[i].y-city[j].y));}}return ; }5.代碼運行及測試
我在做的時候是不固定起點且默認所有城市都有一條路,若沒有路就將路徑長度置為無窮大。這里我的L設置為20000,t初試設置為100。數據第一行是表示城市個數,第二行表示衰減率。在網上找了一些測試樣例,結果如下:
測試樣例:
樣例1:
5
0.9
100000 3 1 5 8
3 100000 6 7 9
1 6 100000 4 2
5 7 4 100000 3
8 9 2 3 100000
結果:
上面兩行表示初試化的路徑與長度,找到的最短路徑是4 -> 2 -> 0 -> 1 -> 3 -> 4,路徑長度為16。
當將衰減率為0.8時結果還是一樣,當衰減率為0.5時結果還是一樣,應該是數據比較簡單。
樣例2:
4
0.9
0 3 6 7
5 0 2 3
6 4 0 2
3 7 5 0
結果:
樣例2:
這個樣例是我在網上找到的,里面提供了各個城市的坐標,所以我用歐式距離算了一下城市距離矩陣。
27
0.9
41 94
37 84
53 67
25 62
7 64
2 99
68 58
71 44
54 62
83 69
64 60
18 54
22 60
83 46
91 38
25 38
24 42
58 69
71 71
74 78
87 76
18 40
13 40
82 7
62 32
58 35
45 21
結果:
這個跑出來最短距離是408.368。
樣例3:
52
0.9
565
575
25
185
345
750
945
685
845
655
880
660
25
230
525
1000
580
1175
650
1130
1605
620
1220
580
1465
200
1530
5
845
680
725
370
145
665
415
635
510
875
560
365
300
465
520
585
480
415
835
625
975
580
1215
245
1320
315
1250
400
660
180
410
250
420
555
575
665
1150
1160
700
580
685
595
685
610
770
610
795
645
720
635
760
650
475
960
95
260
875
920
700
500
555
815
830
485
1170
65
830
610
605
625
595
360
1340
725
1740
245
結果:
一共52個城市,衰減率為0.9,最終結果為7753.772,運行時間0.881s。
同一個數據,當衰減率為0.7時,結果如下:
可以發現結果變差了,我們可以從中推出當衰減率為0.7時,算法陷入一個局部最優出不來了,而且此時花費的時間也比之前衰減率為0.9時快了將近一倍。
樣例4:
34
0.9
103.73 36.03
101.74 36.56
104.06 30.67
114.48 38.03
102.73 25.04
106.71 26.57
114.31 30.52
113.65 34.76
117 36.65
118.78 32.04
117.27 31.86
120.19 30.26
115.89 28.68
119.3 26.08
113.23 23.16
113 28.21
110.35 20.02
123.38 41.8
125.35 43.88
126.63 45.75
112.53 37.87
108.95 34.27
121.3 25.03
116.46 39.92
121.48 31.22
106.54 29.59
117.2 39.13
111.65 40.82
108.33 22.84
91.11 29.97
106.27 38.47
87.68 43.77
114.17 22.28
113.54 22.19
這是中國34個城市的坐標,跑完結果如下:
最短的距離為157.64,運行時間0.988秒,算法效率還是挺高的。
總結
以上是生活随笔為你收集整理的用模拟退火算法解旅行商问题的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux system NFS、iSC
- 下一篇: V2X仿真测试平台技术研究