TSP--模擬退火演算法(c++實現+詳細解釋)
阿新 • • 發佈:2019-01-12
利用模擬退火演算法解決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;
}