1. 程式人生 > >TSP--模擬退火演算法(c++實現+詳細解釋)

TSP--模擬退火演算法(c++實現+詳細解釋)

利用模擬退火演算法解決TSP問題,能看到這篇文章的應該知道TSP是什麼,在此就不贅述了,模擬退火演算法思想網路上優質結束很多,因此直接講講如何用C++實現它

演算法步驟

1.初始化:起始溫度,終止溫度,溫度變化率,最優路徑頂點集 ,最短長度S
2.利用蒙特卡洛法得到一個比較好的初始解
3.當前溫度大於終止溫度時,利用兩點交換法在當前最優路徑基礎上構造一條新路徑,計算新路徑的長度S1,差值dE= S1 - S
4.若 dE < 0,當前最優路徑更新為新路徑,最短長度更新為S1
否則,任意產生一個介於0~1的概率rt,並計算exp(-dE/T),T為當前溫度;若exp(-dE/T) > rt, 當前最優路徑更新為新路徑,最短長度更新為S1,更新溫度

C++程式碼(內附詳細註釋)

#include<iostream>
using namespace std;
#include<cstdlib>
#include<ctime>
#include<vector>
#include<cmath>

//思路:
//1.初始化:起始溫度,終止溫度,溫度變化率,最優路徑頂點集 ,最短長度S 
//2.利用蒙特卡洛法得到一個比較好的初始解
//3.當前溫度大於終止溫度時,利用兩點交換法在當前最優路徑基礎上構造一條新路徑,計算新路徑的長度S1,差值dE= S1 - S 
//4.若 dE < 0,當前最優路徑更新為新路徑,最短長度更新為S1
//否則,任意產生一個介於0~1的概率rt,並計算exp(-dE/T),T為當前溫度;若exp(-dE/T) > rt, 當前最優路徑更新為新路徑,最短長度更新為S1 //更新溫度 void TSP(vector<vector<int> > W) { int n = W.size();//頂點數量 //----------------------初始化引數------------------- int startT = 3000;//初始溫度 double endT = 1e-8;//結束溫度;科學計數法不需要標頭檔案,字元間不允許有空格 double delta =
0.999;//溫度變化率 int limit = 10000;//概率選擇上限,表示已經接近最優解 //vector<vector<int> > w = W; vector<int> path;//最優路徑(頂點集) int length_sum = 0;//最優路徑長度和 //初始化最優路徑 for(int i = 0; i < n; i++) { path.push_back(i);//必須使用入棧 } //swap(path[3],path[1]);//修改可用過載 [] //計算初始最優路徑和 for(int i = 0; i < n - 1; i++) { // cout<<path[i]<<" "; length_sum += W[path[i]][path[i + 1]]; } length_sum += W[path[n - 1]][path[0]]; //----------------使用蒙特卡洛得到一個較好的初始解---------------------- vector<int> cur = path; int cur_sum = 0; for(int i = 0; i < 8000; i++) { for(int k = 0; k < n; k++) { int j = rand() % n; swap(cur[k],cur[j]); } //計算初始最優路徑和 for(int i = 0; i < n - 1; i++) { cur_sum += W[cur[i]][cur[i + 1]]; } cur_sum += W[cur[n - 1]][cur[0]]; if(cur_sum < length_sum) { path = cur; length_sum = cur_sum; } } // cout<<endl<<length_sum<<endl; //-------------------模擬退火具體過程-------------------------------- //1.若是沒有此函式,每次執行該程式碼產生結果都相同,預設1是種子 //2.有此函式,若是放在迴圈內部,則每次迴圈都設定同一個種子 srand((int)(time(NULL)));//確定一個隨機種子 //退火模擬過程 while(startT > endT)//控制迴圈次數 { // cout<<endl<<count++<<endl; //----------------構造新路徑---------------------------------------- vector<int> path_new = path;//為構造一條新路徑準備 int length_sum_new = 0;//新的路徑總和 int P_L = 0;//以一定概率接受次數 //隨機產生兩個點,交換,得到一條新的路徑 int x = rand() % n; int y = rand() % n; while(x == y)//直到隨機產生兩個互異頂點才繼續向下執行 { x = rand() % n; y = rand() % n; } // cout<<" x:"<<x<<" y: "<<y<<endl; swap(path_new[x], path_new[y]);//等價於在原最優路徑上隨機交換兩個互異頂點得到新路徑 //計算新路徑和 for(int i = 0; i < n - 1; i++) { // cout<<path_new[i]<<" "; length_sum_new += W[path_new[i]][path_new[i + 1]]; } length_sum_new += W[path_new[n - 1]][path_new[0]]; // cout<<endl<<length_sum_new<<endl; //--------------------------比較新舊路徑,取優-------------------------- double dE = length_sum_new - length_sum; if(dE < 0)//新路徑更短,直接取用 { path = path_new; length_sum = length_sum_new; } else //新路徑不會更優,按一定概率接受 { double rd = rand() / (RAND_MAX + 1.0);//隨機產生概率:0~1 if(exp(- dE / startT) > rd ) { path = path_new; length_sum = length_sum_new; P_L++; //一定概率接受次數 } } if(P_L == limit)break;//達到限制,直接退出 startT *= delta;//溫度變化,降低 } //--------------------輸出---------------------------------- cout<<endl<<endl<<"--------說明:鄰接矩陣頂點從 0 開始儲存,而輸出時頂點從 1 開始--------"<<endl<<endl; for(int i = 0; i < n - 1; i++) { cout<<"當前點序號為: "<<path[i]+1<<" 下個點序號為: "<<path[i + 1]+1<<" 權值為: "<<W[path[i]][path[i+1]]<<endl; } cout<<"當前點序號為: "<<path[n -1]+1<<" 下個點序號為: "<<path[0]+1<< " 權值為: "<<W[path[n - 1]][path[0]]<<endl; cout<<endl<<"最優解:"<<length_sum<<endl; } int main() { int n; vector<vector<int> > w; vector<int> v; cout<<"please input the number of vertex n:"<<endl; cin>>n; //輸入方法,先分解為一維向量 for(int i=0;i<n;i++) { w.push_back(v); } int temp; for(int i=0;i<n;i++) { for(int j=0;j<n;j++) { cin>>temp; w[j].push_back(temp); } } TSP(w); return 0; }