遗传算法(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

posted @ 2014-04-16 16:27  dongchuanniu  阅读(445)  评论(0编辑  收藏  举报