遗传算法(GA)求解中国旅行商问题
1 void QSort(float *R, int **P, int start, int end) 2 { 3 if (start >= end) return; 4 float tmp; 5 int *ptr; 6 if (start + 1 == end)//只有两个元素的时候直接排序 7 { 8 if (R[start] > R[end]) 9 { 10 tmp = R[start]; 11 R[start] = R[end]; 12 R[end] = tmp; 13 14 ptr = P[start]; 15 P[start] = P[end]; 16 P[end] = ptr; 17 } 18 } 19 else 20 { 21 int i, j; 22 i = start + 1; j = end; 23 while (i < j) 24 { 25 while (R[i] <= R[start]&&i<end) i++; 26 while (R[j] > R[start]&&j>start) j--; 27 if (i < j) 28 { 29 tmp = R[i]; 30 R[i] = R[j]; 31 R[j] = tmp; 32 33 ptr = P[i]; 34 P[i] = P[j]; 35 P[j] = ptr; 36 } 37 } 38 tmp = R[start]; 39 R[start] = R[j]; 40 R[j] = tmp; 41 42 ptr = P[start]; 43 P[start] = P[j]; 44 P[j] = ptr; 45 46 QSort(R,P, start, j - 1); 47 QSort(R, P,j + 1, end); 48 } 49 }
void QSort(float *R, int **P, int start, int end);
1 /** 2 *遗传算法求解中国旅行商问题 3 *遗传算法使用的三个算子: 4 *(1)选择算子:根据适应度函数,选择适应度该高的个体进行下一步的杂交, 5 反应进化过程中优胜劣汰的自然规律 6 *(2)交叉算子:交叉过程随机选择两个个体进行排队,根据预先设定的交叉概 7 率Pc(0.5~0.8),决定是否交叉,交叉方法有单点交叉,两点交叉,和均 8 匀交叉,这个类似于生物学中染色体交换基因片段 9 *(3)变异算子:变异算子将新个体的基因链中的某一位或者某一片段按变异概率 10 Pm(0.001~0.1)进行变异 11 **/ 12 13 #include<stdio.h> 14 #include<stdlib.h> 15 #include<time.h> 16 #include<math.h> 17 #include"qsort.h" 18 #define INF 9999999 //指定为正无穷 19 20 //定义坐标点结构体 21 struct Point 22 { 23 int x; 24 int y; 25 }; 26 /********************************************** 27 *以下过程包括: 28 *1. 读入31个城市的坐标位置 29 *2. 求任意两个城市间的距离 30 *3. 求一条完整路径的总长度 31 ************************************************/ 32 //从文件读取坐标点 33 Point* getPoints(int len, char* filename) 34 { 35 Point* ps = (Point*)malloc(sizeof(Point)*len); 36 FILE *infile; 37 fopen_s(&infile, filename, "r"); 38 if (infile == NULL) 39 { 40 printf("文件打开失败\n"); 41 } 42 for (int i = 0; i < len; i++) 43 { 44 fscanf_s(infile, "%d%d", &ps[i].x, &ps[i].y); 45 } 46 fclose(infile); 47 return ps; 48 } 49 50 //求两点间的距离 51 float getDistance(Point p1, Point p2) 52 { 53 return powf(powf(p1.x - p2.x, 2) + powf(p1.y - p2.y, 2), 0.5); 54 } 55 56 //计算任意两座城市间的距离,并保存在二维数组里 57 float** getAnyDistance(Point* ps, int len)//求任意两座城市之间的距离 58 { 59 float** cost = (float**)malloc(sizeof(float*)*len); 60 for (int i = 0; i < len; i++) 61 { 62 cost[i] = (float*)malloc(sizeof(float)*len); 63 } 64 for (int i = 0; i < len; i++) 65 { 66 for (int j = i + 1; j < len; j++) 67 { 68 cost[i][j] = getDistance(ps[i], ps[j]); 69 cost[j][i] = cost[i][j]; 70 } 71 cost[i][i] = 0; 72 } 73 return cost; 74 } 75 76 //计算一条环路的总长度,本算法里面的适应度就是以环路的长度为标准,越小适应度越高 77 float getTotalDistance(int *path, float **cost, int len) 78 { 79 float total = 0; 80 for (int i = 0; i < len - 1; i++) 81 { 82 total += cost[path[i]][path[i + 1]]; 83 } 84 total += cost[path[len - 1]][path[0]]; 85 return total; 86 } 87 88 void copyPath(int *path1, int *path2, int len) 89 { 90 while (len>0) 91 { 92 path1[len - 1] = path2[len - 1]; 93 len--; 94 } 95 } 96 /****************************************************/ 97 //判断值在不在数组的start位到end位之间,在返回标号,不在-1; 98 int isInArray(int value, int *s, int start, int end) 99 { 100 //printf("vslue=%d start=%d,end=%d\n", value,start,end); 101 for (int i = start; i <= end; i++) 102 { 103 //printf("s[%d] = %d\n", i,s[i]); 104 if (s[i] == value) 105 { 106 //printf("%d返回值:%d\n", i, s[i]); 107 return i; 108 } 109 } 110 return -1; 111 } 112 113 //两个个体交叉,产生两个新的个体的过程 114 void Cross(int *f1, int *f2, int *s1, int *s2, int start, int end, int len) 115 { 116 117 if (start >= end || end >= len) 118 { 119 return; 120 } 121 int j,k; 122 for (int i = start; i <= end; i++)//交换start到end间的基因片段 123 { 124 s1[i] = f2[i]; 125 s2[i] = f1[i]; 126 } 127 /** 128 *理论上讲交叉过程应该保持其他部分不变化,但受制于本讨论问题中每一个城市 129 *是唯一的,并且要求出现并且只能出现在回路中一次,(出发节点两次),因此交换 130 *基因片段后必将影响其他部分,这里的工作就是在尽量保证不变的前提下,解决冲突 131 *本算法参考博客名为 xiaoning6p14 关于巡回旅行商问题的遗传算法路径交叉的思路 132 *进行编写,使用部分匹配交叉(PMX),详细算法地址为: 133 *http://blog.sina.com.cn/s/blog_5d7883db0100bl04.html 134 **/ 135 for (int i = (end+1)%len; i != start; i++,i%=len) 136 { 137 j = isInArray(f1[i], s1, start, end); 138 if (j!=-1) 139 { 140 k = isInArray(f1[j], s1, start, end); 141 while (k != -1) 142 { 143 j = k; 144 k = isInArray(f1[j], s1, start, end); 145 } 146 s1[i] = f1[j]; 147 } 148 else 149 { 150 s1[i] = f1[i]; 151 } 152 153 j = isInArray(f2[i], s2, start, end); 154 if (j != -1) 155 { 156 k = isInArray(f2[j], s2, start, end); 157 while (k != -1) 158 { 159 j = k; 160 k = isInArray(f2[j], s2, start, end); 161 } 162 s2[i] = f2[j]; 163 } 164 else 165 { 166 s2[i] = f2[i]; 167 } 168 } 169 } 170 171 //随机产生一条环路 172 void getOneRandSolution(int *fR,int len) 173 { 174 //srand(time(NULL)); 175 int x, y,tmp; 176 for (int i = 0; i < len; i++) 177 { 178 fR[i] = i; 179 } 180 for (int i = 0; i < len / 2; i++) 181 { 182 x = rand() % len; 183 y = rand() % len; 184 tmp = fR[x]; 185 fR[x] = fR[y]; 186 fR[y] = tmp; 187 } 188 } 189 190 //产生一个初始种族,Num表示种群数量,len表示维度(城市个数) 191 //每一个可行路径相当于一个个体 192 void initPopulation(int **P, int Num,int len) 193 { 194 for (int i = 0; i < Num; i++) 195 { 196 getOneRandSolution(P[i], len); 197 } 198 } 199 200 201 //选择过程,选择过程按照环路的总长度进行排序,选择前面的一般个体进行繁衍 202 //其他个体将会被淘汰,这里使用快速排序算法进行个体排列 203 void Select(int **P, int Num, int len, float *dis, float **cost) 204 { 205 for (int i = 0; i < Num; i++) 206 { 207 dis[i] = getTotalDistance(P[i], cost, len); 208 } 209 //采用快速排序方法选择将种群个体按照优胜劣汰方式排列 210 QSort(dis, P, 0, Num - 1); 211 } 212 213 //交叉过程,产生下一代群体,start之前为已经选择的先进个体, 214 //start后面开始存放交叉个体,及覆盖掉原先的被淘汰个体 215 void Crossover(int **P, int start, int Num, int len, float Pc) 216 { 217 int index = start; 218 //srand(time(NULL)); 219 int x, y,begin,end; 220 float r; 221 while (index < Num) 222 { 223 x = rand() % start; 224 y = rand() % start; 225 r = (float)(rand() % 1000) / 1000; 226 if (Pc>r) 227 { 228 begin = rand() % len; 229 end = rand() % len; 230 if (begin > end) 231 { 232 int t = begin; 233 begin = end; 234 end = t; 235 } 236 Cross(P[x], P[y], P[index], P[index + 1], begin, end, len); 237 index += 2; 238 } 239 } 240 } 241 242 //基因的变异过程 243 void Mutation(int **P, int Num, int len,float Pm) 244 { 245 //srand(time(NULL)); 246 float r; 247 int x, y, tmp; 248 for (int i = 0; i < Num; i++) 249 { 250 r = (float)(rand() % 1000) / 1000; 251 if (Pm>r) 252 { 253 x = rand() % len; 254 y = rand() % len; 255 tmp = P[i][x]; 256 P[i][x] = P[i][y]; 257 P[i][y] = tmp; 258 } 259 } 260 261 } 262 263 int *GA_TSP(char *fileName,int Num,int len,int round,int Iterators,float Pc,float Pm) 264 { 265 /***************初始化各种数据*******************/ 266 Point *ps = getPoints(len, fileName); 267 float **cost = getAnyDistance(ps, len); 268 int Nc = 0;//迭代次数 269 int **P = (int**)malloc(sizeof(int*)*Num); 270 int *result = (int*)malloc(sizeof(int)*len); 271 float minDis = INF; 272 float *dis = (float*)malloc(sizeof(float)*Num); 273 for (int i = 0; i < Num; i++) 274 { 275 P[i] = (int*)malloc(sizeof(int)*len); 276 } 277 /*********************************************/ 278 while (round>0)//每一轮相当于旧的种族灭绝,再产生新的种群,这样做是为了尽可能避免局部极值 279 { 280 Nc = 0; 281 initPopulation(P, Num, len); 282 while (Nc < Iterators)//繁衍的代数 283 { 284 Select(P, Num, len, dis, cost);//选择 285 Crossover(P, Num / 2, Num, len, Pc);//交叉 286 Mutation(P, Num, len, Pm);//变异 287 Nc++; 288 } 289 Select(P, Num, len, dis, cost); 290 if (dis[0] < minDis)//保存最优解 291 { 292 copyPath(result, P[0], len); 293 minDis = dis[0]; 294 } 295 round--; 296 } 297 /*******内存释放********/ 298 for (int i = 0; i < Num; i++) 299 { 300 free(P[i]); 301 } 302 free(P); 303 free(dis); 304 free(ps); 305 for (int i = 0; i < len; i++) 306 { 307 free(cost[i]); 308 } 309 free(cost); 310 return result; 311 } 312 313 314 int main() 315 { 316 srand(time(NULL)); 317 /** 318 *种群规模400,城市个数31,轮数10,每个种群繁衍500代,交叉概率0.8,变异概率0.05 319 **/ 320 int *solution = GA_TSP("points.txt", 200, 31, 100,500, 0.8, 0.05); 321 Point *ps = getPoints(31, "points.txt"); 322 float **cost = getAnyDistance(ps, 31); 323 printf("最短路径长度是<%fkm>,路径如下:\n", getTotalDistance(solution, cost, 31)); 324 for (int i = 0; i < 31; i++) 325 { 326 printf("%d ", solution[i]); 327 } 328 printf("\n"); 329 330 FILE *outfile; 331 fopen_s(&outfile, "result.txt", "w"); 332 if (outfile == NULL) 333 { 334 printf("文件无法打开!\n"); 335 } 336 else 337 { 338 for (int i = 0; i < 31; i++) 339 { 340 fprintf(outfile, "%d %d\n", ps[solution[i]].x, ps[solution[i]].y); 341 } 342 fclose(outfile); 343 } 344 345 free(solution); 346 free(ps); 347 for (int i = 0; i < 31; i++) 348 { 349 free(cost[i]); 350 } 351 free(cost); 352 return 0; 353 }
城市坐标数据:
1304 2312
3639 1315
4177 2244
3712 1399
3488 1535
3326 1556
3238 1229
4196 1004
4312 790
4386 570
3007 1970
2562 1756
2788 1491
2381 1676
1332 695
3715 1678
3918 2179
4061 2370
3780 2212
3676 2578
4029 2838
4263 2931
3429 1908
3507 2367
3394 2643
3439 3201
2935 3240
3140 3550
2545 2357
2778 2826
2370 2975