TSP数据集:bayg29
已知最优解:bayg29.opt
1,28,6,12,9,26,3,29,5,21,2,20,10,4,15,18,14,17,22,11,19,25,7,23,8,27,16,13,24,-1,
- stdafx.h:包含要用到的头文件
- point2d.h:存放每个数据点,包括序号和横纵坐标以及相关函数的实现
- point2d.cpp:点类成员函数定义
- SA-TSP.cpp:解决TSP问题
stdafx.h
#pragma once
#include <iostream>
#include <string>
#include <cstdlib>
#include <cstdio>
#include <cmath>
#include <fstream>
#include <ostream>
#include <vector>
#include <random>
#include <ctime>
using namespace std;
point2d.h
#pragma once
#include "stdafx.h"
class CPoint2d {
public:
CPoint2d(int iID, double x, double y);
int getID();
double getX() const;
double getY() const;
private:
int _iID;
double _fX;
double _fY;
};
point2d.cpp
#include "stdafx.h"
#include "point2d.h"
//两点构造
CPoint2d::CPoint2d(int iID, double x, double y) {
_iID = iID;
_fX = x;
_fY = y;
}
//城市ID
int CPoint2d::getID() {
return _iID;
}
//城市横纵坐标
double CPoint2d::getX() const {
return _fX;
}
double CPoint2d::getY() const {
return _fY;
}
SA-TSP.cpp
#include "stdafx.h"
#include "point2d.h"
//城市数量
const int CITY_NUM = 29;
//const int CNT = 30;//若终止条件设置为连续若干步的函数值不变,取消下面的所有对代码的注释即可,其他条件亦可自己增加
//随机数引擎
default_random_engine e;
uniform_real_distribution<float> uf(0, 1);
uniform_int_distribution<unsigned> ui(1, CITY_NUM - 1);
//产生新路径
void genNewPath(float p = 0.5);
//计算两点间距离
double cal2PDist(CPoint2d point1, CPoint2d point2);
//计算当前路径下的距离
double calTotalDist(int* path, vector<CPoint2d> vecPoint);
//模拟退火,初始参数的设置对结果有一定的影响
void SA(vector<CPoint2d> vecPoint, float T0 = 1000.0, float alpha = 0.99, int maxgen = 3000, int LK = 1000);
//全局存放路径以便做出修改
int pathNew[CITY_NUM] = { 0 };
//初始路径,若对初始解的要求不是很严格,可以随机产生初始解
//但有时初始解很重要,不可随意设置,可以先用蒙特卡罗模拟产生优秀的初始解
int path0[CITY_NUM] = { 3, 26, 28, 12, 6, 1, 8, 24, 16, 13, 19, 27, 23, 7, 25, 11, 15, 22, 17, 18, 14, 4, 10, 20, 2, 21, 5, 9, 29 };
int main() {
//从文件读取坐标到点
vector<CPoint2d> vecPoint;
ifstream file("data.txt", ios::in);
if (!file.is_open()) {
cout << "文件打开失败" << endl;
}
else {
cout << "文件打开成功" << endl;
}
while (!file.eof()) {
int iID;
double fx;
double fy;
file >> iID >> fx >> fy;
CPoint2d point(iID, fx, fy);
vecPoint.push_back(point);
}
for (auto i : vecPoint) {
printf("%-6d%-16f%-16f\n", i.getID(), i.getX(), i.getY());
}
//计时开始
clock_t time_start = clock();
//SA
cout << "模拟退火算法开始,请等待!\n";
SA(vecPoint);
//计时结束
cout << "\n模拟退火算法结束:\n";
clock_t time_end = clock();
cout << "\n运行时间:" << ((double)time_end - (double)time_start) / (double)CLOCKS_PER_SEC << "s" << endl;
//输出最佳结果
cout << "our best path: " << endl;
for (int i = 0; i < CITY_NUM; ++i) {
cout << path0[i] << " ";
}
double bestDist = calTotalDist(path0, vecPoint);
cout << endl << "our best dist: " << bestDist << endl;
system("pause");
return 0;
}
//计算两点间距离
double cal2PDist(CPoint2d point1, CPoint2d point2) {
double fDist_2 = pow(point1.getX() - point2.getX(), 2) + pow(point1.getY() - point2.getY(), 2);
return sqrt(fDist_2);
}
//产生新路径
void genNewPath(float p) {
memcpy(pathNew, path0, sizeof(int) * CITY_NUM);
//交换法
if (uf(e) > p) {
int c1 = ui(e);
int c2 = ui(e);
if (c1 != c2) {
pathNew[c1 - 1] = path0[c2 - 1];
pathNew[c2 - 1] = path0[c1 - 1];
}
}
//倒置法
else {
int c1 = ui(e);
int c2 = ui(e);
if (c1 != c2) {
int* pathTemp = new int[sizeof(int) * ((int)fmax(c1, c2) - (int)fmin(c1, c2) + 1)];
for (int i = 0; i <= (fmax(c1, c2) - fmin(c1, c2)); i++) {
pathTemp[i] = path0[(int)fmax(c1, c2) - i - 1];
}
memcpy(pathNew + (int)fmin(c1, c2) - 1, pathTemp, sizeof(int) * ((int)fmax(c1, c2) - (int)fmin(c1, c2) + 1));
if (pathTemp) {
delete[]pathTemp;
pathTemp = nullptr;
}
}
}
}
//计算当前路径下的距离
double calTotalDist(int* path, vector<CPoint2d> vecPoint) {
int cnt = 0;
double totalDist = 0;
while (cnt != CITY_NUM) {
if (cnt != (CITY_NUM - 1)) {
double dist = cal2PDist(vecPoint[path[cnt] - 1], vecPoint[path[cnt + 1] - 1]);
totalDist += dist;
}
else {
double dist = cal2PDist(vecPoint[path[cnt] - 1], vecPoint[path[0] - 1]);
totalDist += dist;
}
cnt++;
}
return totalDist;
}
//模拟退火
void SA(vector<CPoint2d> vecPoint, float T0, float alpha, int maxgen, int LK) {
//T0:初温 alpha:降温系数 maxgen:最大迭代次数 LK:每个温度下的迭代次数
float T = T0;
double dist0 = calTotalDist(path0, vecPoint);
//存储最短路程和最优解
double minDist = 1e10;
int minPath[CITY_NUM] = { 0 };
//存储路径和路程
ofstream outfile("res.txt", ios::out);
if (!outfile.is_open()) {
cout << "文件打开失败!\n";
exit(0);
}
for (int i = 0; i < CITY_NUM; ++i) {
outfile << path0[i] << " ";
}
outfile << dist0 << endl;
//算法开始
/*若终止条件设置为连续若干步的函数值不变,取消下面的注释即可,其他条件亦可自己增加*/
//bool isSame = true;
//int i = 0;
//double tempDist[CNT] = { 0 }; //存放最后CNT个结果
//tempDist[0] = dist0;
for (; maxgen; --maxgen) {
int LK0 = LK;
for (; LK0; --LK0) {
dist0 = calTotalDist(path0, vecPoint);
genNewPath();
double distNew = calTotalDist(pathNew, vecPoint);
if (distNew < dist0) {
/*若终止条件设置为连续若干步的函数值不变,取消下面的注释即可,其他条件亦可自己增加*/
//if (i < CNT) {
// tempDist[i] = distNew;
//}
//else {
// memcpy(tempDist, tempDist + 1, sizeof(double) * (CNT - 1));
// tempDist[CNT - 1] = distNew;
// for (int j = 0; j < CNT - 1; ++j) {
// if (tempDist[j + 1] != tempDist[0]) {
// isSame = false;
// }
// }
//}
//记录接受的解到文件,也可以不记录
for (int i = 0; i < CITY_NUM; ++i) {
outfile << pathNew[i] << " ";
}
outfile << distNew << endl;
//最短路径和最优解的更新
if (minDist > distNew) {
minDist = distNew;
memcpy(minPath, pathNew, sizeof(int) * CITY_NUM);
}
//将当前路径更新,以便下一次循环
memcpy(path0, pathNew, sizeof(int) * CITY_NUM);
//若最后cnt个结果都相同,则找到最优解
//if (isSame && i >= CNT) {
// cout << "break!" << endl;
// break;
//}
//++i;
}
else {
//metropolis准则:可以看到接受新解的概率与这个解和上一个解的函数值(能量差)和当前温度有关,这也体现了退火的过程
float p = exp(-(distNew - dist0) / T);
//产生0-1之间的随机数uf(e),若接受新解的概率p大于uf(e)则接受,否则不接受
if (p > uf(e)) {
memcpy(path0, pathNew, sizeof(int) * CITY_NUM);
}
}
}
//if (isSame && i >= CNT) {
// break;
//}
T *= alpha;//降温
}
//最后CNT个结果
//for (int i = 0; i < CNT; ++i) {
// cout << tempDist[i] << " ";
//}
//将最短路径拷贝到path0
memcpy(path0, minPath, sizeof(int) * CITY_NUM);
outfile.close();
}
data.txt的格式:
1 1150.0 1760.0
2 630.0 1660.0
3 40.0 2090.0
4 750.0 1100.0
5 750.0 2030.0
6 1030.0 2070.0
7 1650.0 650.0
8 1490.0 1630.0
9 790.0 2260.0
10 710.0 1310.0
11 840.0 550.0
12 1170.0 2300.0
13 970.0 1340.0
14 510.0 700.0
15 750.0 900.0
16 1280.0 1200.0
17 230.0 590.0
18 460.0 860.0
19 1040.0 950.0
20 590.0 1390.0
21 830.0 1770.0
22 490.0 500.0
23 1840.0 1240.0
24 1260.0 1500.0
25 1280.0 790.0
26 490.0 2130.0
27 1460.0 1420.0
28 1260.0 1910.0
29 360.0 1980.0
运行结果: