遗传算法解决TSP问题---C++版

1、问题描述
  • 旅行商问题
    旅行商问题(Traveling Salesman Problem, TSP),又译为旅行推销员问题、货担郎问题,简称为TSP问题,是最基本的路线问题。假设有n个可直达的城市,一销售商从其中的某一城市出发,不重复地走完其余n-1个城市并回到原出发点,在所有可能的路径中求出路径长度最短的一条.
  • 下面给出10个城市的位置信息:
    在这里插入图片描述
2、求解流程图

在这里插入图片描述

3、程序清单
const int city_num = 10;//城市的数量,即基因的个数
const int idv_num = 100;//群体规模
double pc =0.8 ;//交叉概率
double pm = 0.2;//变异概率
double dis[city_num][city_num];//城市间的距离矩阵
//城市的编号及位置
struct City {
	int label;//城市编号
	int x;//该城市在坐标轴x上的位置
	int y;//该城市在坐标轴y上的位置
};
vector<vector<City>> group;//种群,即所有个体的集合
//初始化
void init();
//显示当前的最短路径
void show_path(vector<City>&city);
//选择(采用联赛选择及精英保存策略)
void select();
//单点交叉并消除冲突
void crossover();
//变异
void mutation();
4、源程序
#include<iostream>
#include<vector>
#include<fstream>
#include<random>
#include<map>
#include<time.h>
using namespace std;

const int city_num = 10;//城市的数量,即基因的个数
const int idv_num = 100;//群体规模
double pc =0.8 ;//交叉概率
double pm = 0.2;//变异概率
double dis[city_num][city_num];//城市间的距离矩阵
//城市的编号及位置
struct City {
	int label;//城市编号
	int x;//该城市在坐标轴x上的位置
	int y;//该城市在坐标轴y上的位置
};
vector<vector<City>> group;//种群,即所有个体的集合

//计算两个城市之间的距离
double distance(const City& city1, const City& city2) 
{
	return sqrt(pow((city1.x - city2.x), 2) + pow((city1.y - city2.y), 2));//用欧式距离进行计算
}
//返回当前路径的总长度
double get_sum(vector<City>& vec)
{
	double sum = 0;
	int size = vec.size();//城市的数量
	for (int i = 1; i < size; ++i)
		sum += dis[vec[i].label][vec [i- 1].label];
	sum += dis[vec[size - 1].label][vec[0].label];
	return sum;
}
//初始化
void init()
{
	ifstream fin;//读文件对象
	fin.open("city.txt");//打开文件
	vector<City> city;//存放城市的数据
	for (int i = 0; i < city_num; ++i)//从文件中读入各城市的编号及位置坐标
	{
		City temp;
		fin>>temp.label;
		fin >> temp.x;
		fin >> temp.y;
		city.push_back(temp);
	}
	fin.close();
	for (int i = 0; i < city_num; ++i)//计算城市之间的距离
		for (int j = 0; j < city_num; ++j)
			dis[i][j] = dis[j][i] = distance(city[i], city[j]);
	srand((unsigned)time(NULL));
	for (int i = 0; i < idv_num; ++i)//随机产生100个个体
	{
		vector<City> temp=city;
		for (int j = 0; j < 10; ++j)
			swap(temp[j], temp[rand() % city_num]);
		group.push_back(temp);//将该个体加入到种群中
	}
}
//显示当前的最短路径
void show_path(vector<City>&city)
{
	cout << "path:";
	for (int i = 0; i < city_num; ++i)
		cout << city[i].label << ' ';
	cout << city[0].label << endl;
	cout << endl;
}
//选择(采用联赛选择及精英保存策略)
void select()
{
	vector<vector<City>> temp;
	map<double, int> bigmap;//用于排序,找出最短距离的路径(默认升序排序)
	for (int i = 0; i < idv_num; ++i)
		bigmap[get_sum(group[i])]=i;
	int better_fit, worse_fit;
	better_fit = (*bigmap.begin()).second;//当前适应度最好的个体
	worse_fit = (*(--bigmap.end())).second;//当前适应度最差的个体
    double sum;//当前最优路径的距离
	sum = (*bigmap.begin()).first;
	cout << "distance:" << sum << endl;
	show_path(group[better_fit]);//输出该最优路径
	temp.push_back(group[better_fit]);//将最好的个体复制两份到新的种群中
	temp.push_back(group[better_fit]);
	int M = 3;//精英选择策略中随机选择的个体数
	srand((unsigned)time(NULL));
	for (int i = 0; i < idv_num - 2; ++i)//用联赛选择剩余的个体数
	{
		int fit = INT_MAX;//当前所选个体集合中的最高适应度
		int loc;//适应度最好的个体
		for(int j=0;j<M;++j)//从city_num-2个个体中随机选择M个个体
		{
			int pos = rand() % idv_num;
			while (pos == better_fit || pos == worse_fit)//该个体不能是当前种群中适应度最高或最差的个体
				pos = rand() % idv_num;
			if (get_sum(group[pos]) < fit)//判断当前个体是否适应度最优的个体
			{
				fit = get_sum(group[pos]);
				loc = pos;
			}
		}
		temp.push_back(group[loc]);//用联赛选择策略选择了一个个体,将它加入新的种群中
	}
	group = temp;//产生新的种群
}
//单点交叉并消除冲突
void crossover()
{
	vector<City> temp1, temp2;
	int pos = 7;//发生交叉的起始位置(交叉区域:从该位置到末尾)
	srand((unsigned)time(NULL));
	for (int i = 0; i < 48; ++i)
	{
		double rand_rate = rand()/(float)RAND_MAX;
		if (rand_rate < pc)//如果当前的概率小于交叉的概率则进行交叉
		{
			temp1 = group[i+2];//个体1
			temp1[pos] = group[99 - i][pos];//交叉
			temp1[pos+1] = group[99 - i][pos+1];
			temp1[pos+2] = group[99 - i][pos+2];
			int z = 0;//消除冲突
			for (int j = 0; j < pos; ++j)
			{
				while (group[i+2][z].label == temp1[pos].label || group[i+2][z].label == temp1[pos+1].label ||
					group[i+2][z].label == temp1[pos+2].label)
					++z;
				temp1[j] = group[i + 2][z];
				++z;
			}
			temp2 = group[99 - i];//个体2
			temp2[pos] = group[i+2][pos];//交叉
			temp2[pos + 1] = group[i+2][pos + 1];
			temp2[pos + 2] = group[i+2][pos + 2];
			z = 0;//消除冲突
			for (int j = 0; j < pos; ++j)
			{
				while (group[99 - i][z].label == temp2[pos].label || group[99 - i][z].label == temp2[pos+1].label ||
					group[99 - i][z].label == temp2[pos+2].label)
					++z;
				temp2[j] = group[99 - i][z];
				++z;
			}
			group[i+2] = temp1;//完成交叉
			group[99 - i] = temp2;
		}
	}
}
//变异
void mutation()
{
	srand((unsigned)time(NULL));
	for (int i = 2; i < idv_num; ++i)
	{
		double rand_rate = rand() / (float)RAND_MAX;
		if (rand_rate < pm)//如果当前概率小于变异概率则进行变异
		{
			for (int j = 0; j < 5; ++j)//随机选择5个基因进行变异
			{
				int pos1 = rand() % city_num;//随机选择变异的位置
				int pos2 = rand() % city_num;
				swap(group[i][pos1], group[i][pos2]);
			}
		}
	}
}

int main()
{
	const int gen_max = 2000;//最大迭代数
	init();
	for (int i = 0; i <gen_max ; ++i)
	{
		cout << "Generation " << i +1<< endl;
		select();
		crossover();
		mutation();
	}
}
5、实验结果

在这里插入图片描述

6、实验结果讨论

一般情况下,交叉概率以及变异的概率越大,得到最优解时的迭代次数会相对较少。

  • 1
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值