问题描述
在旅行商问题中,给定一组城市及每座城市与其他城市之间的旅行成本,目标是找出一条经过所有城市的路径,要求该路径只经过每座城市一次并且旅行总成本最低。从任意一座城市出发,经过每一座城市之后,再回到出发城市结束旅行。
课上给的距离矩阵,可能是疏忽9到3和3到9的距离不一致,但这不妨碍程序设计
代码部分
#include<iostream>
#include<algorithm>
#include<ctime>
#define num_c 10//城市个数
#define Max_t 100//种群数量
#define rate 0.6//选择率
#define generation 1000//迭代次数
#define v_rate 10//变异率百分比
using namespace std;
//城市距离矩阵
int distance_t[10][10] = {
{ 0,118,1272,2567,1653,2097,1425,1177,3947,1574 },
{ 118,0,1253,2511,1633,2077,1369,1157,3961,1518 },
{ 1272,1253,0,1462,380,1490,821,856,3660,385 },
{ 2567,2511,1462,0,922,2335,1562,2165,3995,933 },
{ 1653,1633,380,922,0,1700,1041,1135,3870,456 },
{ 2097,2077,1490,2335,1700,0,2311,920,2170,1920 },
{ 1425,1369,821,1562,1041,2311,0,1420,4290,626 },
{ 1177,1157,856,2165,1135,920,1420,0,2870,1290 },
{ 3947,3961,3660,3995,3870,2170,4290,2870,0,4090 },
{ 1574,1518,385,993,456,1920,626,1290,4090,0 } };
//种群个体
class solution_t
{
public:
int path[num_c];//记录城市路径
int cost;//总路径长度,也就是适应度
//重载小于号,便于排序
bool operator <(const solution_t &a)const
{
return cost < a.cost;
};
//计算当前的cost
void computeCost()
{
int sum = 0;
for (int i = 1; i < num_c; i++)
sum += distance_t[path[i - 1]][path[i]];
sum += distance_t[path[0]][path[num_c - 1]];
cost = sum;
}
//对个体进行变异,也就是随机交换两个位置的元素
void variation()
{
int a, b;
a = rand() % num_c;
b = rand() % num_c;
swap(path[a], path[b]);
}
};
solution_t population[Max_t];//种群
solution_t nextpo[Max_t];//存储子代的临时数组
//初始化种群,先将个体顺序初始化,就是赋值为0123456789
//然后随机扰动5次,随机交换两个位置的元素
void init(solution_t(&population)[Max_t])
{
srand((unsigned)time(0));
for (int i = 0; i < Max_t; i++)
{
for (int j = 0; j < num_c; j++)
{
population[i].path[j] = j;
}
for (int j = 0; j < num_c / 2; j++)
{
int a, b;
a = rand() % num_c;
b = rand() % num_c;
swap(population[i].path[a], population[i].path[b]);
}
}
}
//选择一定比例适应度高的个体,并从保留下来的个体中随机选择个体,填充剩余位置
void select(solution_t(&population)[Max_t])
{
sort(population, population + Max_t);
int s = (int)(Max_t*rate);
for (int i = s + 1; i < Max_t; i++)
{
int num = rand() % s;
population[i] = population[num];
}
}
//交叉的过程,a b是父代 a1 b1是子代
void exchange(solution_t a, solution_t b, solution_t &a1, solution_t &b1)
{
//随机选取一段基因
int left = rand() % num_c;
int right = (rand() % (num_c - left)) + left;
//交叉互换
for (int i = left; i <= right; i++)
swap(a.path[i], b.path[i]);
bool flag;
flag = true;//判断是否有冲突
//以下是消除冲突的过程
while (flag)
{
int num = 0;
for (int i = left; i <= right; i++)
{
for (int j = 0; j < num_c; j++)
{
if (j >= left&&j <= right)
continue;
if (a.path[i] == a.path[j])
{
a.path[j] = b.path[i];
num++;
}
}
}
if (num == 0)
flag = false;
}
flag = true;
while (flag)
{
int num = 0;
for (int i = left; i <= right; i++)
{
for (int j = 0; j < num_c; j++)
{
if (j >= left&&j <= right)
continue;
if (b.path[i] == b.path[j])
{
b.path[j] = a.path[i];
num++;
}
}
}
if (num == 0)
flag = false;
}
//输出a1 b1 不用a b是为了不改变父代
for (int i = 0; i < num_c; i++)
{
a1.path[i] = a.path[i];
b1.path[i] = b.path[i];
}
}
int main()
{
init(population);
srand((unsigned)time(0));
for (int j = 0; j < Max_t; j++)
population[j].computeCost();
for (int i = 0; i < generation; i++)
{
select(population);
//交叉互换
int counter = 0;
for (int j = 0; j < Max_t / 2; j++)
{
int a = rand() % num_c;
int b = rand() % num_c;
exchange(population[a], population[b], nextpo[counter], nextpo[counter + 1]);
counter = counter + 2;
}
//随机变异
for (int j = 0; j < Max_t; j++)
{
int r = rand() % 100;
if (r <= v_rate)
nextpo[j].variation();
}
//计算适应度
for (int j = 0; j < Max_t; j++)
nextpo[j].computeCost();
//覆盖父代
for (int j = 0; j < Max_t; j++)
population[j] = nextpo[j];
//输出
cout << "generation: " << i << endl;
for (int k = 0; k < Max_t; k++)
{
cout << "unit " << k << ": ";
for (int j = 0; j < num_c; j++)
{
cout << population[k].path[j] << " ";
}
cout << " cost:" << population[k].cost << endl;
}
}
return 0;
}
结果输出
遗传算法得到的不一定是最优解,而是近似最优解,所以可以每次收敛到的数值都不一样。