源码分三个文件;
1.main.cpp:里面把openCV相关的部分注释了,如果配置了openCV,可以把注释取消,把算出的结果用可视化显示出来
2.anneal.h:这个文件是Anneal类的头文件,里面定义了Point结构体
3.anneal.cpp:这个文件是Anneal类的实现,里面的findPath()函数是模拟退火算法的具体实现
1.main.cpp:
#include<iostream>
#include<vector>
#include "anneal.h"
//#include "opencv2/imgproc/imgproc.hpp"
//#include "opencv2/opencv.hpp"
//void display(std::vector<Point> path1, std::vector<Point> path2, double n);
void main()
{
std::vector<Point> cities =
{
//Point(1,1), Point(1,5), Point(5,6),Point(7,5),Point(7,2),Point(5,3),Point(3,2),Point(2,1)
/*Point(0.4000,0.4439),Point(0.2439,0.1463),Point(0.1707,0.2293),Point(0.2293,0.7610),
Point(0.5171,0.9414),Point(0.8732,0.6536),Point(0.6878,0.5219),Point(0.8488,0.3609),
Point(0.6683,0.2536),Point(0.6195,0.2634)*/
Point(5.294,1.558), Point(4.286,3.622), Point(4.719,2.774), Point(4.185,2.230),
Point(0.915,3.821), Point(4.771,6.041), Point(1.524,2.871), Point(3.447,2.111),
Point(3.718,3.665), Point(2.649,2.556), Point(4.399,1.194), Point(4.660,2.949),
Point(1.232,6.440), Point(5.036,0.244), Point(2.710,3.140), Point(1.072,3.454),
Point(5.855,6.203), Point(0.194,1.862), Point(1.762,2.693), Point(2.682,6.097)
};
typedef long clock_t;
clock_t start, end;
Anneal trip(cities,100); //模拟退火算法,初始化
start = clock();
std::vector<Point> result = trip.findPath(); //模拟退火算法,执行
end = clock();
trip.printPath(result); //输出路径顺序
std::cout << "从“开始”到“求出结果”所花时间:" << double(end - start) / CLOCKS_PER_SEC << "s" << std::endl;
//display(cities, result,100); //可视化输出
}
/*
//可视化输出路径,参数n为放大倍数,大于1为放大,小于1为缩小
void display(std::vector<Point> path1, std::vector<Point> path2, double n)
{
cv::Mat image = cv::Mat::zeros(700, 1400, CV_8UC3);//高800,宽1500,纯黑图像
image.setTo(cv::Scalar(255, 255, 255)); //设置纯白背景
//Point_<int>、Point2i、Point定义坐标为int型的点
//Point_<float>、Point2f定义坐标为浮点型的点
for (std::vector<Point>::iterator it = path1.begin(); it != path1.end(); it++) {
cv::Point2f p(it->x, it->y);
if(it == path1.begin())
cv::circle(image, p * n, 10, cv::Scalar(0, 0, 255), -1); // 画半径为10的圆,起点画红色
else
cv::circle(image, p * n, 10, cv::Scalar(0, 255, 0), -1); // 画半径为10的圆,其他点画蓝色
}
//原始路径
for (std::vector<Point>::iterator it = path1.begin(); it != path1.end(); it++) {
cv::Point2f p1(it->x,it->y);
cv::Point2f p2;
if (it == path1.end() - 1)
p2 = cv::Point2f(path1.begin()->x, path1.begin()->y);
else
p2 = cv::Point2f((it + 1)->x, (it + 1)->y);
cv::Scalar scal(100, 100, 100);//三原色
cv::line(image, n * p1, n * p2, scal, 4);//背景图,起点,终点,
}
for (std::vector<Point>::iterator it = path2.begin(); it != path2.end(); it++) {
cv::Point2f p1(it->x, it->y);
cv::Point2f p2;
if (it == path2.end() - 1)
p2 = cv::Point2f(path2.begin()->x, path2.begin()->y);
else
p2 = cv::Point2f((it + 1)->x, (it + 1)->y);
cv::Scalar scal(200, 0, 0);//三原色
cv::line(image, n * p1, n * p2, scal, 5);//背景图,起点,终点,
}
cv::imshow("旅行商路径图(20城市)", image);
cv::waitKey(0); //按任意键关闭
}
*/
2.anneal.h:
#pragma once
#include<vector>
#include<map>
#include <math.h>
struct Point
{
double x;
double y;
double lenth = sqrt(x * x + y * y);
Point(double x, double y)
{
this->x = x;
this->y = y;
}
double getLenth() { //到原点的距离
return sqrt(x * x + y * y);
}
double getLenth(Point p) { //该点到p点的距离
return sqrt((x - p.x) * (x - p.x) + (y - p.y) * (y - p.y));
}
Point getNormVec() { //获取单位向量
Point(this->x / this->lenth, this->x / this->lenth);
}
double cross(Point vec) { //叉乘,此向量叉乘传入向量
return this->x * vec.y - this->y * vec.x;
}
Point operator*(double k) {
return { x * k , y * k };
}
Point operator/(double k) {
return { x / k , y / k };
}
Point operator+(Point a) {
return { x + a.x , y + a.y };
}
Point operator-(Point a) {
return { x - a.x , y - a.y };
}
double operator*(Point a) {
return x * a.x + y * a.y;
}
Point operator/(Point a) {
return { x / a.x , y / a.y };
}
bool operator==(Point p) const {
return (fabs(x - p.x) < 0.0001 && fabs(y - p.y) < 0.0001);
}
bool operator!=(Point p) const {
return (x != p.x || y != p.y);
}
};
class Anneal
{
public:
std::vector<Point> pt; //储存城市
double T;
double result_lenth;
Anneal(std::vector<Point> &pt, double T);
~Anneal();
std::vector<Point> findPath();
void printPath(std::vector<Point> path);
private:
double damp; //衰减因子
std::vector<Point> newPlan(std::vector<Point> result);
double getAllLenth(std::vector<Point>& temp);
};
3.anneal.cpp:
#include<iostream>
#include "anneal.h"
Anneal::Anneal(std::vector<Point> &pt, double T)
{
this->pt = pt;
this->T = T;
this->damp = 0.95;
std::srand(std::time(0));
}
Anneal::~Anneal()
{
;// delete& (this->pt);
}
std::vector<Point> Anneal::findPath()
{
std::vector<Point> result = this->pt;
result_lenth = 1;
std::vector<Point> choosen_result; // = this->pt;
double choosen_lenth = 0;
//std::cout << "初始顺序,总长度为: " << getAllLenth(result) << std::endl;
while (choosen_lenth != result_lenth) //(T>0.0001)
{
//std::cout << "当前总长度: " << result_lenth << std::endl;
result_lenth = getAllLenth(result);
std::vector<Point> temp;
double temp_lenth;
for (int i = 0; i < result.size() * 100; i++)
{
temp = newPlan(result);
temp_lenth = getAllLenth(temp);
//std::cout << "产生的新方案为长度: " << temp_lenth << std::endl;
if (temp_lenth < result_lenth)
{
//std::cout << "新方案 " << temp_lenth << " 小于原始方案" << result_lenth << " ,直接接受新方案: " << std::endl;
choosen_result = temp;
choosen_lenth = temp_lenth;
}
else //新方案的路径更长
if ((rand() % 1001) / double(1000) < exp(-T)) //高温,小概率不更新,低温大概率不更新
;
else //高温,大概率更新,低温小概率更新
{
//std::cout << "新方案"<< temp_lenth << " 大于原始方案" << result_lenth <<" ,以概率:\t" << 1-exp(-T) <<"\t接受了新方案,哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈" << std::endl << std::endl;
choosen_result = temp;
choosen_lenth = temp_lenth;
}
//std::cout <<"本次探索邻域内一个解,温度:"<< T << " 当前总长度: " << choosen_lenth << std::endl;
}
//std::cout << "当前温度:" << T << " 探索100n个领域之后,总长度: " << choosen_lenth << " 降温" << std::endl;
T *= damp;
result = choosen_result; //初始解更新,初始解长度暂不更新,因为要作为while循环的判断条件
}
std::cout <<"result总长度:" << result_lenth << std::endl;
return result;
}
std::vector<Point> Anneal::newPlan(std::vector<Point> result)
{
/*
//方案1,交换n1与其后面一个作为邻域,如果n1为最后一个,则认为其后面一个为第二个(第一个固定不动)
int n1 = rand() % (result.size() - 1) + 1;
int n2;
if (n1 == result.size() - 1)
n2 = 1;
else
n2 = n1 + 1;
Point temp = result[n1];
result[n1] = result[n2];
result[n2] = temp;
*/
/*
//方案2:交换n1与n2作为邻域
int n1 = rand() % (result.size() - 1) + 1;
int n2 = n1;
while(n1 == n2)
{
n2 = rand() % (result.size() - 1) + 1;
}
Point temp = result[n1];
result[n1] = result[n2];
result[n2] = temp;
*/
//方案3:n1到n2之间的翻转作为邻域
int n1 = rand() % (result.size() - 1) + 1;
int n2 = n1;
while(n1 == n2)
{
n2 = rand() % (result.size() - 1) + 1;
}
int n_temp = n1;
n_temp = n1 < n2 ? n1 : n2;
n2 = n1 > n2 ? n1 : n2;
n1 = n_temp;
while(n1 < n2)
{
Point temp = result[n1];
result[n1] = result[n2];
result[n2] = temp;
n1++;
n2--;
}
return result;
}
double Anneal::getAllLenth(std::vector<Point>& temp)
{
double lenth = 0;
std::vector<Point>::iterator it = temp.begin();
for (std::vector<Point>::iterator it = temp.begin(); it != temp.end(); it++)
if (it == temp.end()-1)
lenth += (*it).getLenth(temp.front());
else
lenth += (*it).getLenth(*(it + 1));
return lenth;
}
void Anneal::printPath(std::vector<Point> path)
{
char ch[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
for (int i = 0; i < path.size(); i++)
for (int j = 0; j < pt.size(); j++)
{
//std::cout << "001 cities:" << cities[j].x << "\t" << cities[j].y << " result:" << result[i].x << "\t" << result[i].y << std::endl;
if (path[i] == pt[j])
std::cout << ch[j];
}
std::cout << std::endl << std::endl;
}
算法运行结果展示
把openCV相关的部分注释了,如果配置了openCV,可以把注释取消,把算出的结果用可视化显示出来。
图中红色点为起点A,绿色点为其他城市B-T,第一个为初始顺序,后面为算法跑出来的结果。
参数
初始温度: t=200
温度衰减系数: damp=0.95,T = T * damp
每个温度下的迭代次数:100n,n为城市数
算法结束条件:无变化控制法(相邻两个温度下结果相等)