手机版
你好,游客 登录 注册
背景:
阅读新闻

C++版遗传算法求解TSP

[日期:2019-01-06 15:00:54] 来源:linux公社   作者:linux公社 [字体: ]

之前在采用Matlab以及Java实现GA to solve TSP 时,考虑到程序的运行效率,通常会选用 array 来放置各种数据,众所周知,array的特点就是固定分配的连续物理地址进行数据的存储,然而对于不定长度的数据进行存储时,一般的方式是采用 多次少量 ,即预先分配一定内存空间,等不够用了再分配一定的内存空间(多说一句,Matlab 中还可以采用以下两种方式:用 array=[]; 以及用cell实现存储不定长度数组,但效率不高)。而在C++ STL 中有一种神奇的数据类型vector容器,它既有着 array 的连续内存分配方式,又能不用指定数据存储长度,对于一组不同规模的数据集进行测试时,再也不用担心使用array时提醒必须预分配确定的存储空间了~~

以下为HeuristicOperator.h头文件:

#pragma once#include iostream #include vector #include algorithm // std::shuffle#include random // std::default_random_engine#include chrono using namespace std;

class HeuristicOperator {public: vector vector double getCoord(void); //函数1:获取坐标函数 vector vector double getDM(vector vector double Coord); //函数2:获取距离矩阵函数 vector int getInitS(int n); //函数3:获取初始解函数 double Eval(vector int S, vector vector double DM, int n); //函数4:评价函数

vector double bestS(vector double Eval, int Length); //函数5:搜索范围内最优评价值及其相应的位置函数

vector int RandPosition(int n); //函数6:产生Sharking操作位置函数 vector int Swap(vector int S, vector int RP); //函数7:交换算子 vector int Flip(vector int S, vector int RP); //函数8:翻转算子 vector int Insert(vector int S, vector int RP); //函数9:插入算子};

对应的HeuristicOperator.cpp类文件比较容易实现,在此不再赘述。本文所用算例为31城市的TSP问题,与Java版遗传算法求解TSP求解算例一致,具体数据如下:

1304 23123639 13154177 22443712 13993488 15353326 15563238 12294196 10044312 7904386 5703007 19702562 17562788 14912381 16761332 6953715 16783918 21794061 23703780 22123676 25784029 28384263 29313429 19083507 23673394 26433439 32012935 32403140 35502545 23572778 28262370 2975

以下为遗传算法的主函数:

/* 文件名:CppGATSP 作者:Alex Xu 地址:Dalian Maritime University 描述:利用遗传算法求解TSP问题 创建时间:2018年12月10日11点27分*/

#include iostream #include vector #include numeric //accumulate#include chrono //time#include "HeuristicOperator.h"using namespace std;using namespace chrono;

//设置算法参数# define POP_SIZE 2# define MAX_GEN 4000

int main() { //计时开始 auto start = system_clock::now();

//生成距离矩阵 HeuristicOperator ga_dm; vector vector double GA_DM; GA_DM = ga_dm.getDM(ga_dm.getCoord());

int n = int(GA_DM[0].size()); //城市规模

//初始化算法 vector vector int initPop(POP_SIZE, vector int (n)); //初始种群 vector vector int Pop(POP_SIZE, vector int (n)); //当前种群 vector vector int newPop(POP_SIZE, vector int (n)); //新种群 vector double popFit(POP_SIZE); //记录种群适应度值 vector int bestIndival(n); //最优个体 vector double gs(MAX_GEN + 1); //记录全局最优解 gs[0] = 1e9; unsigned int seed = (unsigned)std::chrono::system_clock::now().time_since_epoch().count();

//生成初始种群 HeuristicOperator s0; for (int i = 0; i POP_SIZE; i++) { initPop[i] = s0.getInitS(n); } Pop = initPop;

//开始进化 for (int gen = 1; gen = MAX_GEN; gen++) {

HeuristicOperator eval; //计算种群的适应度值(这里直接用路径长度表示) for (int i = 0; i POP_SIZE; i++) { popFit[i] = eval.Eval(Pop[i], GA_DM, n); }

HeuristicOperator bestEI; //找出种群中个体的最优适应度值并记录相应的个体编号 vector double bestEvalIndex(2); bestEvalIndex = bestEI.bestS(popFit, POP_SIZE); double bestEval = bestEvalIndex[0]; //最优适应度值 int bestIndex = int(bestEvalIndex[1]); //最优适应度值对应的个体编号

//最优解的更新 if (bestEval gs[gen - 1]) { //比上一代优秀则更新 gs[gen] = bestEval; bestIndival = Pop[bestIndex]; } else { //不比上一代优秀则不更新 gs[gen] = gs[gen - 1]; } if (gen % 100 == 0) { cout "第" gen "次迭代时全局最优评价值为" gs[gen] endl; }

//扰动操作(产生新种群) for (int p = 0; p POP_SIZE; p++) { HeuristicOperator shk; vector int randPosition = shk.RandPosition(n); vector int tmpS(n); double randShk = rand() / double(RAND_MAX); if (randShk 0.33) { tmpS = shk.Swap(Pop[p], randPosition); //交换操作 } else if (randShk = 0.67) { tmpS = shk.Flip(Pop[p], randPosition); //翻转操作 } else { tmpS = shk.Insert(Pop[p], randPosition); //插入操作 }

HeuristicOperator evl; if (evl.Eval(tmpS, GA_DM, n) evl.Eval(Pop[p], GA_DM, n)) { newPop[p] = Pop[p]; } else { newPop[p] = tmpS; } } Pop = newPop;

//选择操作(轮盘赌) vector double Cusum(POP_SIZE + 1, 0); //适用于轮盘赌的累加器Cusum(补充了cus[0]=0; for (int i = 0; i POP_SIZE; i++) { Cusum[i + 1] = Cusum[i] + popFit[i]; }

double Sum = accumulate(popFit.begin(), popFit.end(), 0.0); //计算各个体被选择的概率(归一化) vector double cusFit(POP_SIZE + 1); //放置种群中个个体被选择的概率值 for (int i = 0; i POP_SIZE + 1; i++) { cusFit[i] = Cusum[i] / Sum; }

for (int p = 0; p POP_SIZE; p++) { //轮盘赌产生新种群 double r = rand() / double(RAND_MAX); for (int q = 0; q POP_SIZE; q++) { if (r cusFit[q] r = cusFit[q + 1]) { newPop[p] = Pop[q]; } } } Pop = newPop; }

//计时结束 auto end = system_clock::now(); auto duration = duration_cast microseconds (end - start); cout "花费了" double(duration.count()) * microseconds::period::num / microseconds::period::den "秒" endl;

//输出结果 double gs0 = 15377.711; cout "最优解为" gs[MAX_GEN] endl; double e = (gs[MAX_GEN] - gs0) / gs0; cout "误差为" e * 100.0 '%' endl; cout "最优路径为" endl; for (int i = 0; i i++) { cout bestIndival[i] + 1 '\t'; } while (1) {}}

以上即为C++语言所编写的遗传算法求解TSP示例,运行环境为:Windows10 64位操作系统;CPU:i7-8750H; 内存:8G;Microsoft Visual Studio Community 2017 。求解结果如下:

与已知最优解的误差为1.48%,所用时间约为3.6s. 还可以接受。但值得注意的是:本文实验参数最大迭代次数4000代,而种群规模仅为2,这与一般的遗传算法思想上是没问题的,只是实际参数可能不太符合。当然,对于算法参数这些细节都是可以调节的,不必太过于纠结。

啊哈,这次的利用C++编程遗传算法求解TSP就这些了~~~

Linux公社的RSS地址:https://www.linuxidc.com/rssFeed.aspx

本文永久更新链接地址:https://www.linuxidc.com/Linux/2019-01/156196.htm

linux C 语言中的结构体和共用体(联合体) 多目录工程的CmakeLists.txt编写(自动添加多目录下的文件)

Linux公社的RSS地址http://www.it56.cn/rss.xml

本文永久更新链接地址www.it56.cn/RedLinux/8521.html

linux