最近在上计算智能的课,老师刚刚教了遗传算法,布置了用遗传算法解决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; }