旅行商問題的模擬退火解法
阿新 • • 發佈:2018-11-26
問題描述:
一名商人要去n個城市推銷產品,他需要經過每個城市一次且僅一次,最後回到出發的城市,問最短路徑長度是多少。
資料:
20
A 2.5 4.0
B 1.2 -2.4
C 8.7 1.2
D 3.6 12.1
E -5.5 0.94
F -6.6 -12.6
G 0.18 5.219
H 12.5 14.3609
I 22.5 -5.26
J 1.61 4.5
K 2.1 -5.6
L 0 25
M 9.2 -32
N -1 7
O -5 -8
P 21 35
Q 16 7.5
R -21 5
S -7 -25.5
T 12 17.5
程式碼:
struct city//城市
{
char name;//名字
int id;//下標
double x;
double y;
};
int n;//城市數
vector<city> v;//路徑
double dist[110][110];//鄰接矩陣
double sum = 0;//路徑長度
double dis(city a,city b)//計算兩點間的距離
{
return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}
double get_sum(vector<city> v)//獲取當前路徑的長度
{
double sum = 0;
for(int i = 1; i <= v.size() - 1; ++i)
sum += dist[v[i].id][v[i - 1].id];
sum += dist[v[0].id][v[v.size() - 1].id];//注意是迴路
return sum;
}
void monte_carlo()//蒙特卡洛
{
vector<city> cur = v;
for(int k = 1; k <= 8000; ++k)//8000次迴圈
{
for(int i = 0; i <= n - 1 ; ++i)
{
int j = rand() % n;
swap(cur[i],cur[j]);//隨機交換兩個數
}
}
double cur_sum = get_sum(cur);//計算路徑長度
if(cur_sum < sum)//如果比當前路徑短
{
sum = cur_sum;//更新
v = cur;//更新
}
return;
}
double end_of_t = 1e-16;//結束時的溫度
double rate_of_t = 0.99999999;//溫度下降的比率
double T = 1.0;//溫度,初始為1.0
int cnt = 200000;//最多迭代2w次
void simulated_annealing()//模擬退火
{
while(cnt--)
{
vector<city> cur = v;
int r1 = rand() % n;
int r2 = rand() % n;
if(r1 == r2)//如果隨機數相等
{
cnt++;
continue;//重新生成
}
swap(cur[r1],cur[r2]);//交換
double df = get_sum(cur) - sum;//計算路徑長度的改變數
double r3 = (rand() % 10000) / 10000.0;//隨機數r3
if(df < 0)//如果更短,則接受
{
v = cur;
sum += df;// +=
}
else if(exp(-df / T) > r3)//否則按照一定的概率(不等式的左邊)接受
{
v = cur;
sum += df;// +=
}
T = T * rate_of_t;//溫度下降
if(T < end_of_t)//如果到達最低溫度
break;
}
return;
}
void print_ans()//輸出答案
{
cout << "路徑長度為:" << sum << endl;
cout << "路徑為:";
for(int i = 0; i <= n - 1; ++i)
cout << v[i].name << ' ';
cout << endl;
return;
}
int main()
{
cin >> n;
srand((unsigned int)time(NULL));//初始化隨機數種子
for(int i = 0; i <= n - 1; ++i)
{
city t;
cin >> t.name >> t.x >> t.y;
t.id = i;
v.push_back(t);
}
for(int i = 0; i <= n - 2; ++i)//求出鄰接矩陣
for(int j = i + 1; j <= n - 1; ++j)
dist[i][j] = dist[j][i] = dis(v[i],v[j]);
sum = get_sum(v);//初始路徑長度
monte_carlo();//生成初始解
simulated_annealing();//模擬退火
print_ans();//輸出答案
return 0;
}
解決方法:
模擬退火演算法