用模拟退火算法解决旅行商问题C++

源码分三个文件;

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为城市数

算法结束条件:无变化控制法(相邻两个温度下结果相等)

b96f7940a6904e38b653dbf7f7570688.png

aabff5ab6e90423b9d12c8285cb56d35.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值