最近在上计算智能的课,老师刚刚教了遗传算法,布置了用遗传算法解决TSP的问题的作业,于是经过几小时的奋战,终于编写完成。
首先先对TSP问题进行分析。TSP问题,也就是旅行商问题,题目的大题内容是 一位旅行商,要遍游N座城市(城市数量记为NUM_CITY), 已知每两座城市之间的位置,要求每座城市必须去且只去过一次,求遍游该N座城市的最短路程。
利用遗传算法解决该问题,步骤如下:
1.初始化城市之间的距离
2.生成并初始化初始种群,种群内每个个体保存着一条完整的路径(每个城市标号出现且只出现一次)
3.计算目前种群每个个体的适应值(要求求最短路程,路径一定是个正数,故而得到每个个体中保存的路径的总路程后,取倒数,得到的数越大,适应性越高,总路程越短)
4.找出目前最优个体,让其直接复制至下一代(即不变异)
5.对其他个体根据发生交叉互换的概率Pc,得到参与交叉互换的个体集合
6.使参与交叉互换的个体随机发生交叉互换(每个个体只参与一次)交叉互换的片段起始点和终点均随机产生
7.对除最优个体外的每个个体,根据突变概率Pm发生突变,随机产生两个位置点,使这两个位置点之间的片段进行置倒(即2345变成5432)
8.循环执行步骤34567,直到演变代数>GENERATIONS为止(暂定为5000代)
代码如下所示,(注释部分用的printf用于测试查错,可忽视)
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>
#define RAND(X) (rand()%(X))
#define NUM_CITY (30)
#define GENERATIONS (5000)
#define MAX_SIZE (50)
#define LOWEST_ALIVE (1.0/MAX_SIZE)
typedef struct _Group{
int city[NUM_CITY];
int adapt;
float pAlive;
}Group;
int cityDistance[NUM_CITY][NUM_CITY];
Group g_Group[MAX_SIZE];
float Pm = 0.1;
float Pc = 0.8;
int bestOne;
void getFitness(Group &group) // 得到适应值
{
int distance = 0;
int temp;
int x1, x2, y1, y2;
int tdistance;
for (int i = 1; i < NUM_CITY; i++)
{
distance += cityDistance[group.city[i - 1]][group.city[i]];
}
group.adapt = distance;
group.pAlive = 1 / (float)distance;
}
void Swap(int &a, int &b) // 交换ab值
{
int c;
c = a;
a = b;
b = c;
}
void Init() // 初始化
{
srand((unsigned int)time(0));
for (int i = 0; i < NUM_CITY; i++) // 初始化城市距离
{
for (int j = i + 1; j < NUM_CITY; j++)
{
cityDistance[i][j] = RAND(100) + 1;
cityDistance[j][i] = cityDistance[i][j];
}
}
printf("城市距离如下:\n");
for (int i = 0; i < NUM_CITY; i++)
{
for (int j = 0; j < NUM_CITY; j++)
{
if (j < i + 1)
printf(" ");
else
printf("%3d ", cityDistance[i][j]);
}
printf("\n");
}
for (int i = 0; i < MAX_SIZE; i++) // 初始化样本数列
{
for (int j = 0; j < NUM_CITY; j++)
{
g_Group[i].city[j] = j;
}
}
int r;
for (int i = 0; i < MAX_SIZE; i++) // 打乱顺序
{
for (int j = 0; j < NUM_CITY; j++)
{
r = RAND(NUM_CITY);
Swap(g_Group[i].city[j], g_Group[i].city[r]);
}
}
printf("产生初始种群如下:\n");
for (int i = 0; i < MAX_SIZE; i++)
{
printf("第%d个个体:\n", i + 1);
for (int j = 0; j < NUM_CITY; j++)
{
printf("%3d ", g_Group[i].city[j]);
}
printf("\n");
}
}
void GetAliveP() // 存活率
{
float totalAlive = 0;
// 选择最优部分
totalAlive = 0;
for (int i = 0; i < MAX_SIZE; i++) // 计算个体适应值
{
getFitness(g_Group[i]);
totalAlive += g_Group->pAlive;
}
for (int i = 0; i < MAX_SIZE; i++) // 矫正个体存活率 让总和为1
{
g_Group[i].pAlive /= totalAlive;
}
bestOne = 0;
for (int i = 0; i < MAX_SIZE; i++)
{
if (g_Group[i].pAlive > g_Group[bestOne].pAlive)
bestOne = i;
}
printf("目前最佳个体为:%d, 其距离为%d,其轨迹如下:\n", bestOne+1, g_Group[bestOne].adapt);
for (int i = 0; i < NUM_CITY; i++)
printf("%d ", g_Group[bestOne].city[i]);
printf("\n");
}
int isOnIt(int num, int Array[NUM_CITY], int ignorePos, int pos1, int pos2) // num是否在Array[]的pos1到pos2之间 其中跳过ignorePos(该数字的原位置)
{
for (int i = pos1; i <= pos2; i++)
{
if (Array[i] == num)
return i;
}
return -1;
}
void Swap(int sel1,int sel2,int pos1, int pos2) // 交叉互换
{
int temp;
int maxSize = pos2 - pos1 + 1;
//printf("开始初步交叉互换\n");
//printf("%d %d\n", pos1, pos2);
for (int i = pos1; i <= pos2; i++)
{
temp = g_Group[sel1].city[i];
g_Group[sel1].city[i] = g_Group[sel2].city[i];
g_Group[sel2].city[i] = temp;
}
//for (int j = 0; j < NUM_CITY; j++)
// printf("%4d", g_Group[sel1].city[j]);
//printf("\n");
//for (int j = 0; j < NUM_CITY; j++)
// printf("%4d", g_Group[sel2].city[j]);
//printf("\n");
int pos;
//printf("开始矫正重复值\n");
int times = 0;
for (int i = 0; i < NUM_CITY; i++) // 矫正重复值
{
times = 0;
if (i >= pos1 && i <= pos2)
{
i = pos2;
continue;
}
do
{
times++;
pos = isOnIt(g_Group[sel1].city[i], g_Group[sel1].city, i, pos1, pos2);
if (pos != -1)
{/*
for (int j = 0; j < NUM_CITY; j++)
printf("%4d", g_Group[sel1].city[j]);
printf("\n");
for (int j = 0; j < NUM_CITY; j++)
printf("%4d", g_Group[sel2].city[j]);
printf("\n");
printf("%d %d %d %d %d\n",pos1,pos2,pos, g_Group[sel1].city[i], g_Group[sel2].city[pos]);*/
g_Group[sel1].city[i] = g_Group[sel2].city[pos];
//printf("pos:%d,pos1:%d,pos2:%d\n", pos, pos1, pos2);
}
} while (pos != -1);
do
{
pos = isOnIt(g_Group[sel2].city[i], g_Group[sel2].city, i, pos1, pos2);
if (pos != -1)
{
g_Group[sel2].city[i] = g_Group[sel1].city[pos];
//printf("pos:%d,pos1:%d,pos2:%d\n", pos, pos1, pos2);
}
} while (pos != -1);
}
// printf("交叉互换过程完毕\n");
}
void Mutation(int sel, int pos1,int pos2)//个体突变
{
int maxSize = pos2 - pos1 + 1;
for (int i = 0; i < maxSize / 2; i++)
{
Swap(g_Group[sel].city[pos1+i], g_Group[sel].city[pos2-i]);
}
}
void Genetic() // 产生下一代种群
{
int maxNum = 0, minNum = 0;
for (int i = 0; i < MAX_SIZE; i++)
{
if (g_Group[i].pAlive > g_Group[maxNum].pAlive)
maxNum = i;
if (g_Group[i].pAlive < g_Group[maxNum].pAlive)
minNum = i;
}
g_Group[minNum] = g_Group[maxNum]; // 使最大直接替换最小
//printf("开始交叉\n");
// 交叉配对
int selNum;
int maxTimes = 0, nowTimes = 0;
int canSelected[MAX_SIZE]; // 可以用于交叉的个体
bool isCanSelected[MAX_SIZE];
for (int i = 0; i < MAX_SIZE; i++)
{
if (i == maxNum) // 不让最优秀的个体参与配对
continue;
else if ((RAND(100)) / 100.0 < Pc) // 根据概率判断是否参与配对
{
canSelected[maxTimes++] = i;
}
}
for (int i = 0; i < maxTimes; i++)
{
selNum = RAND(maxTimes);
Swap(canSelected[i], canSelected[selNum]);
}
int pos1, pos2;
for (int i = 0; i < maxTimes; i+=2)
{
selNum = i + 1;
if (selNum >= maxTimes)
break;
pos1 = RAND(NUM_CITY); // 选定交叉位置
pos2 = RAND(NUM_CITY - pos1) + pos1;
if (pos1 == pos2)
{
if (pos1 > 0)
pos1--;
else
pos2++;
}/*
printf("%d与%d开始交叉互换\n", canSelected[i], canSelected[selNum]);*/
Swap(canSelected[i], canSelected[selNum], pos1, pos2);
/*
printf("第%d个体与第%d个体进行交叉配对,得到新的两个个体如下:\n", canSelected[i] + 1, canSelected[selNum] + 1);
printf("第%d个体:\n", canSelected[i] + 1);
for (int j = 0; j < NUM_CITY; j++)
printf("%4d", g_Group[canSelected[i]].city[j]);
printf("\n第%d个体:\n", canSelected[selNum] + 1);
for (int j = 0; j < NUM_CITY; j++)
printf("%4d", g_Group[canSelected[selNum]].city[j]);
printf("\nselNum:%d, maxTimes:%d\n",selNum,maxTimes);*/
}
/*
printf("开始突变\n");*/
// 突变
for (int i = 0; i < MAX_SIZE; i++)
{
if (i == maxNum || i == minNum)
{
continue;
}
if (RAND(100) / 100.0 < Pm) // 符合突变概率
{
pos1 = RAND(NUM_CITY); // 选择位置
pos2 = RAND(NUM_CITY - pos1) + pos1;
if (pos1 == pos2)
{
if (pos1 > 0)
pos1--;
else
pos2++;
}
/*printf("第%d个体突变前:\n", i + 1);
for (int j = 0; j < NUM_CITY; j++)
printf("%4d", g_Group[i].city[j]);
printf("\n");*/
Mutation(i, pos1, pos2);
// printf("第%d个体突变:\n", i + 1);/*
// for (int j = 0; j < NUM_CITY; j++)
// printf("%4d", g_Group[i].city[j]);
// printf("\n");
}
}
}
void Train()
{
int nowGenerations = 0;
float totalAlive = 0;
do
{
printf("第%d代种群\n", nowGenerations + 1);
GetAliveP();// 计算存活率
Genetic();// 根据遗传规律得到下一代
nowGenerations++;
} while (nowGenerations < GENERATIONS);
printf("经过%d次繁衍,得到的优秀个体为:\n", nowGenerations);
printf("其距离为%d,其轨迹如下:\n", g_Group[bestOne].adapt);
for (int i = 0; i < NUM_CITY; i++)
printf("%d ", g_Group[bestOne].city[i]);
printf("\n");
printf("其他个体如下:\n");
for (int i = 0; i < MAX_SIZE; i++)
{
printf("其距离为%d,其轨迹如下:\n", g_Group[i].adapt);
for (int j = 0; j < NUM_CITY; j++)
{
printf("%d ", g_Group[i].city[j]);
}
printf("\n");
}
}
int main()
{
Init();
Train();
return 0;
}