C++下基于模拟退火算法解决TSP问题

一、原理

首先明确TSP问题的目标是什么,假设当前有3个城市,需要你从第一个城市开始,遍历所有城市,然后回到初始位置,要求总路径最短。这个时候就需要计算每个城市之间的两两距离,然后按顺序确定一条最短路径。

最稳妥的方案是,使用暴力枚举,列举出来所有的路线为1->2->3->1、1->3->2->1,然后计算一个最短路径即可。

但是当城市数量很多的时候,暴力枚举的方法计算量十分大,此时就需要考虑有没有简单一些的方法,虽然不是十分准确,但是能猜出一个大致的结果,即使不是最优解,只要接近最优结果也能接受。其他大部分智能优化算法都是这种思路,只是使用的具体思路有区别。

模拟退火算法,实际就是猜答案,然后从猜的结果中找个最优的结果输出,当猜的数量够大,结果就最接近实际结果。它会设置一个初始温度比较高例如1000,一个结束温度20,以一定函数从1000降低到20,然后设置一个迭代次数,每次迭代都会随机打乱路线,如果这个路线比之前的路线好,则直接把它作为最优路线,如果没有之前的好,则以一个很小的概率接受,这个概率也是通过随机数确定的,这里为什么要以一个很小的概率接受一个不好的结果呢?其实是为了防止结果陷入局部最优解,但是对于模拟退火这种猜答案的方式,个人感觉其实用不着以很小的概率接受一共不好的路线。

所以这个算法名字听起来不知所云,但是主要就是猜答案,也没有什么特殊的。

二、代码

//模拟退火算法(SA)求解TSP问题
/*类的体会2:
1.类外访问类中private方式:
①通过设置类中的public,get()函数,来获取一些变量值;
②对于数组的获取,一方面可以通过get(下标)函数来完成一对一的获取;
另一方面可以通过在类中public处声明类外的友元函数friend(对象),友元函数通过对象可以访问到private
2.类中函数要访问类外数据:
①全局变量可以直接访问;
②在类外创建该类的对象,通过对象调用函数,并传递类外数据参数;
③访问其他类还可以用友元函数
*/#include<iostream>
#include<stdlib.h>
#include<time.h>
#include<math.h>
#include<vector>
#include<algorithm>using namespace std;#define T0 1000				//初始温度
#define T_end 20			//退出温度(环境温度)
#define q 0.9				//退火系数
#define L 10				//每个温度时的迭代次数,即链长
#define C 10				//城市数量//10个城市的坐标:
double citys_position[C][2] =
{{1304,2312},{3639,1315},{4177,2244},{3712,1399},{3488,1535},{3326,1556},{3238,1229},{4196,1004},{4312,7900},{4386,570}
};class Simulated_Annealing
{
private:int i, j;int Current_Solution[C];		//存放一个解int Current_copy[C];			//存放解的副本,用于计算差异double T = T0;					//温度变量,并初始化double f1, f2, df;				//f1是初始解目标值,f2是新解目标值,df是新旧解目标值差int count = 0;					//记录降温次数(因为按系数退火,迭代次数不确定)vector<vector<double>> All_soluitons;	//存放每代中最后的解(也可以是全部解,加上链表迭代的) vector<double> All_fitness;		//存放每代中最后的解值public:/*《函数SA声明》:模拟退火主体功能*/void SA();/*《函数citys_generate声明》:初始化解*/void citys_generate();/*《函数Swap声明》:初始化解*/void Swap(int* input_solution);/*《Fitness函数声明:对传入解计算适应度值》*/double Fitness(int* input_solution);/*《distance函数声明:计算两两节点的距离,传入两个城市各自的坐标信息》*/double distance(double* city1, double* city2);/*《getCount函数:给外部调用count值》*/int getCount() { return count; }/*《getF1函数:给外部调用f1值》*/int getF1() { return f1; }/*《getCurrent_Solution函数:给外部调用该数组》*/int getCurrent_Solution(int k){return Current_Solution[k];}/*《get_All_solutions函数申明:定义为类中的友元函数,可以通过对象去访问类中的private。注意:友元函数是可以访问类中的私有成员,但是得告诉他,他是哪个类的朋友。是通过对象的传参来作过渡,而不能实现直接的访问,他找不到!》*/friend void get_All_solutions(Simulated_Annealing &SA);};void main()
{srand((unsigned)time(NULL));time_t start, finish;			//计时变量start = clock();				//程序运行开始计时/*《调用Simulated_Annealing类》*/Simulated_Annealing SA1;SA1.SA();finish = clock();				//程序结束停止计时//计算运行时间double run_time = ((double)(finish - start)) / CLOCKS_PER_SEC;//输出结果printf("模拟退火算法解TSP问题:\n");cout << "初始温度T0=" << T0 << ",降温系数q=" << q << endl;printf("每个温度迭代%d次,共降温%d次。\n", L, SA1.getCount());/*《调用get_All_solutions函数:输出每次退火的方法和值,友元函数,可以通过对象去访问类中的private》*/get_All_solutions(SA1);printf("最终得TSP问题最优路径为:\n");for (int j = 0; j < C - 1; j++){printf("%d—>", SA1.getCurrent_Solution(j));}printf("%d—>", SA1.getCurrent_Solution(C - 1));printf("%d\n", SA1.getCurrent_Solution(0));cout << "最优路径长度为:" << SA1.getF1() << endl;printf("程序运行耗时%1f秒.\n", run_time);}/*《函数SA定义》:模拟退火主体功能*/
void Simulated_Annealing::SA()
{/*①《调用函数citys_generate》:初始化解*/citys_generate();/*《调用Fitness函数:对传入解计算适应度值》*/f1 = Fitness(Current_Solution);//随机求解的一个代价值while (T > T_end)//当温度低于结束温度时,退火结束{All_soluitons.push_back({});	//每代开始添加一个空行,存放本代结果值All_fitness.push_back({});		//每代开始添加一个空行,存放本代代价值for (i = 0; i < L; i++)//迭代次数5{//②复制初始解for (j = 0; j < C; j++){Current_copy[j] = Current_Solution[j];}/*③《调用Swap函数:对传入解做邻域搜索,两点交换》*/Swap(Current_copy);//随机生成一个新的路线/*④《调用Fitness函数:对传入解计算适应度值》*/f2 = Fitness(Current_copy);//计算当前代价df = f1 - f2;//当前代价和前一个代价差/*退火原理:Metropolis准则,df<0*///⑤若新解>旧解值,df<0,则以概率 exp(df / T) 接受新解  df越大,指数越大。含义为新解效果越差,接受概率越小if (df < 0){//随机数(0,1),用于决定是否接受新解double r;r = (double)rand() / RAND_MAX; //分母32767if (r < exp(df / T))//以很小的概率接受这个不好的解{//接受新解for (j = 0; j < C; j++){Current_Solution[j] = Current_copy[j];}f1 = f2;}}//⑥若新解<=旧解值,df>=0,则直接接受新解else//如果新解效果比较好,直接用这个新解{for (j = 0; j < C; j++){Current_Solution[j] = Current_copy[j];}f1 = f2;}}//⑦存放本次迭代的最后方案和适应值for (int j = 0; j < C; j++){All_soluitons[count].push_back(Current_Solution[j]);//每次迭代的路径存入vector}All_fitness[count] = f1;//⑧以一定的速率降温T *= q;//以1/2的速率减小count++;}}
/*《函数citys_generate定义》:初始化解*/
void Simulated_Annealing::citys_generate()
{//①一个城市序列vector<int> temp_city;for (int i = 0; i < C; i++)//城市数量{temp_city.push_back(i + 1);}//②打乱random_shuffle(temp_city.begin(), temp_city.end());//均匀分布来随机移动元素//③赋值给Current_Solutionfor (int j = 0; j < temp_city.size(); j++){Current_Solution[j] = temp_city[j];//初始为随机解}
}
/*《函数Swap定义》:初始化解*/
void Simulated_Annealing::Swap(int* input_solution)
{//①生成交换节点下标int point1 = rand() % C;//交换点1int point2 = rand() % C;//交换点2while (point1 == point2){point2 = rand() % C;//保证交换点不同}//②交换swap(input_solution[point1], input_solution[point2]);//随机交换最终路径的航点
}/*《Fitness函数定义:对传入解计算适应度值》*/
double Simulated_Annealing::Fitness(int* input_solution)//代价函数
{//初始化路径长度double cost = 0;//从第一个城市遍历到最后一个城市for (int j = 0; j < C - 1; j++){/*《调用distance函数:计算两两节点的距离,传入两个城市各自的坐标信息》*/int k = input_solution[j];//城市序号-1=城市位置下标   第二个参数为第二个城市的位置cost += distance(citys_position[input_solution[j] - 1], citys_position[input_solution[j + 1] - 1]);//初始位置为第一个城市}//从最后一个城市回到第一个城市cost += distance(citys_position[input_solution[C - 1] - 1], citys_position[input_solution[0] - 1]);return cost;
}
/*《distance函数声明:计算两两节点的距离,传入两个城市各自的坐标信息》*/
double Simulated_Annealing::distance(double* city1, double* city2)
{/*计算相邻两城市间距离*/double x1 = city1[0];			//城市1横坐标double y1 = city1[1];double x2 = city2[0];double y2 = city2[1];			//城市2纵坐标double dist = pow((pow((x1 - x2), 2) + pow((y1 - y2), 2)), 0.5);return dist;					//返回距离值
}
/*《get_All_solutions函数申明:定义为类中的友元函数,通过对象可以访问类中的向量》*/
void get_All_solutions(Simulated_Annealing &SA)
{cout << "每次退火内部迭代的最终解:" << endl;for (int i = 0; i < SA.All_soluitons.size(); i++){for (int j = 0; j < SA.All_soluitons[i].size(); j++){cout << SA.All_soluitons[i][j] << "—>";}cout << SA.All_soluitons[i][0] << endl;cout << "路线代价为:" << SA.All_fitness[i] << std::endl << std::endl;}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/107474.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2023年数维杯数学建模C题宫内节育器的生产求解全过程文档及程序

2023年数维杯数学建模 C题 宫内节育器的生产 原题再现&#xff1a; 宫内节育器&#xff08;IUD&#xff09;是一种相对安全、有效、经济、可逆、简便&#xff0c;广大妇女易接受的节育器具&#xff0c;目前已成为我国育龄妇女的主要避孕措施。据悉&#xff0c;我国约70%妇女选…

SpringMvc第五战-【SpringMvcJSR303和拦截器】

前言&#xff1a; 小编阐述了springmvc 中的文件下载&#xff0c;以及jrebel的使用和文件下载以及多文件下载! 在本次小编将会介绍JSR303的概念&#xff0c;应用场景和在具体实例的使用&#xff1b;和拦截器的应用 一.JSR303的介绍 1.什么是JSR303&#xff1f; JSR是Java S…

在 Arweave 中轻松管理文件:借助 4EVERLAND 完成 Web3 前端Path Manifests的终极指南

为什么使用Path Manifests&#xff1f; 当在 IPFS 上发布 NFT 时&#xff0c;图片和元数据会被上传到 IPFS 网络以获得一个根 CID&#xff0c;其形式如下&#xff1a; ipfs://bafybeic36ik6cngu37xbzmpytuvyo7z3lyeen44clkkxq5d263zj4nkzr4 通过使用这个根 CID&#xff0c;每…

java程序处理三张表要进行怎么样的操作

首先第一步梳理思路&#xff0c;id没有存在说明是新添加的&#xff0c;如果id存在那就是对现有文章的修改。 /*** 发布文章或保存草稿** param dto* return*/Overridepublic ResponseResult submitNews(WmNewsDto dto) {//0.条件判断if(dto null||dto.getContent() null){ret…

Multi Query Attention Group Query Attention

Multi Query Attention(MQA)在2019年就被提出来了&#xff0c;用于推理加速&#xff0c;但在当时并没有受到很多关注&#xff0c;毕竟一张2080就能跑Bert-base了。随着LLM的大火&#xff0c;MQA所带来的收益得以放大。 思路 Multi Query Attention(MQA)跟Multi Head Attention…

2.zigbee开发,zigbee点亮灯,如何正确使用别人提供的模板文件

一。成为点灯大师的其中一步&#xff08;普通点灯&#xff0c;操作系统点灯&#xff0c;网络点灯&#xff0c;wifi点灯&#xff0c;蓝牙点灯&#xff0c;zigbee点灯&#xff0c;LoRa点灯&#xff0c;NB点灯&#xff09;*&#xff08;寄存器点灯&#xff0c;HAL库点灯&#xff0…

2023年数维杯数学建模B题节能列车运行控制优化策略求解全过程文档及程序

2023年数维杯数学建模 B题 节能列车运行控制优化策略 原题再现&#xff1a; 在城市交通电气化进程快速推进的同时&#xff0c;与之相应的能耗增长和负面效应也在迅速增加。城市轨道交通中的快速增长的能耗给城轨交通的可持续性发展带来负担。2018 年&#xff0c;北京、上海、…

西工大 ASLP 实验室在 WeNet 中开源基于 CPPN 的神经网络热词增强语音识别方案

语境偏置&#xff08;Contextual biasing&#xff09;旨在将语境知识集成到语音识别&#xff08;ASR&#xff09;系统中&#xff0c;以提高在相关领域词汇&#xff08;俗称“热词”&#xff09;上的识别准确率。在许多ASR场景中&#xff0c;待识别语音中可能会包含训练数据中数…

windows10系统下Python3.11中安装Numpy库教程

Python3.11中安装Numpy库目录 项目场景&#xff1a;问题描述解决方案&#xff1a;①下载Numpy文件②把NumPy文件放到Python安装的Scripts文件夹里。③安装numpy④安装验证 项目场景&#xff1a; numpy是开源的数值计算扩展&#xff0c;用于数据分析、机器学习、科学计算的重要…

pgzrun 拼图游戏制作过程详解(3)

3. 绘制完整的拼图 建立Gird列表存储小拼图的基本信息 Gird[] for i in range(6):for j in range(4):SquareActor("girl_06")Square.leftSquare_size*jSquare.topSquare_size*iGird.append(Square) 修改draw()绘制函数 建立循环绘制Gird列表中的所有小拼图 def d…

开源大模型ChatGLM2-6B 1. 租一台GPU服务器测试下

0. 环境 租用了1台GPU服务器&#xff0c;系统 ubuntu20&#xff0c;GeForce RTX 3090 24G。过程略。本人测试了ai-galaxy的&#xff0c;今天发现网友也有推荐autodl的。 &#xff08;GPU服务器已经关闭&#xff0c;因此这些信息已经失效&#xff09; SSH地址&#xff1a;* 端…

虚拟机作为master远程控制台式机中的机器人在仿真环境中进行slam地图构建与自主导航

文章目录 前言一、思路流程二、具体步骤1.虚拟机网络配置2.台式机网络配置3.网络测试 三、远程操控SLAM建立地图三、远程操控SLAM导航 前言 虚拟机作为master远程控制台式机中的机器人在仿真环境中进行slam地图构建与自主导航 最近有时间一直在搞Ubuntu虚拟机与台式机的通讯&…