基于遗传算法的TSP问题求解(C)

  TSP问题:

  TSP(Travelling salesman problem): 译作“旅行商问题”, 一个商人由于业务的需要,要到n个城市,每个城市之间都有一条路径和其他所有的城市相连。现在要求从一个城市出发,穿越所有其他所有的城市,再回到出发的城市。 出于成本的考虑,要求商人走的路径的长短最短。问能否找到这样的一条路径?

  这是个经典的NP-complete问题。 时间复杂度为θ(n!)。 随着城市的数量规模增大,在有限的时间内得不到问题的最优解。 我们只能退而求其次,在有限的时间内(或可接受的时间内)找到一个近似最优解,而且这个近似最优解和最优解的误差我们也是可以接受的。 出于这样的考虑,为求解这类问题的启发式,元启发式算法,演化算法营运而生。

  随着研究的深入,TSP问题已经演化成好多版本。本文的C程序对于对称和非对称的版本都适用。

  遗传算法:

  遗传算法(Genetic Algorithm),也称进化算法,是依据生物进化的过程而提出的一种启发式算法,由美国的J.Holland于1975年首次提出。其主要特点是依据生物进化中的“优胜劣汰”或者“竞争”法作为问题的解的选择依据。 直接对结构对象进行操作,不存在求导和函数连续性的限定;具有内在的隐并行性和更好的全局寻优能力;采用概率化的寻优方法,能自动获取和指导优化的搜索空间,自适应地调整搜索方向,不需要确定的规则。遗传算法的这些性质,已被人们广泛地应用于组合优化、机器学习、信号处理、自适应控制和人工生命等领域。它是现代有关智能计算中的关键技术之一

  遗传算法的基本原理:

  首先根据问题产生多个初始可行解(),然后从初始解中选择诺干优异的个体(问题的解)进行各种操作(交叉,变异)用以产生新的后代。再从新的后代中选择优异的个体进行相应的操作产生它们的后代,如此不断循环,直到迭代的次数达到了预先设定的值或者多次迭代以后产生的最优异的个体(最优解)的质量并没有明显的提高,就可以停止迭代,这时的最优个体(最优解)最为问题的解。

  从上面的原理我们可以知晓该算法主要涉及的步骤如图 1所示:

  • 编码  

  在解实现初始化之前,如何表示一个解即编码是一个很重要的问题。 一般的编码方式有:

  1. 基于二进制编码: 作为遗传算法传统的编码方式。对于TSP问题,经过交叉后可能得不到可行解,需要修补操作。
  2. 基于矩阵的编码: 需要更多的额外空间,而且随着种群规模的增加,存储空间剧增和前期处理工作任务很庞大。后续操作也比较复杂。
  3. 基于邻近的编码: 采用链表的方式存储,但是变异,交叉操作复杂
  4. 基于格雷码方法:传统二进制编码的一种改进,容易实现交叉,变异操作,但是对于该问题不是最优的
  5. 基于符号编码:对于TSP问题,我们直接使用路径来表示一个染色体。即使用一个整数数组,数组长度为TSP城市的数量,数组中存储各个城市编号,从下标为0开始逐个遍历数组元素形成的一个序列即为路径(对于要回到原点的要求,为了方便表示,在这里不作考虑,只是在计算路径长度的时候会添加相应的长度)。
  • 形成初始解

  采用随机的方式产生诺干个(种群规模)的序列,即产生符合城市编号的随机数存储在数组中,数组中的元素包含所有的城市编号,而且没有重复的编码。数组的个数为种群规模。

  • 选择

  在形成一定数量的可行解(染色体)后,需要对这些父代的染色体进行筛选。根据生物遗传的基因的优胜劣汰原则,在筛选染色体的我们也会秉承精英保留原则,使得优异的基因有更多的机会得到遗传。

  适应度函数: 这里我们选择路径长度的倒数来作为解的适应度

  在这个问题中,我们选择“轮盘赌”算法来筛选染色体。

  基本原理:计算每个染色体(路径)的长度的倒数,再得到所有路径倒数之和,用每条路径的倒数除以所有所有路径倒数之和即为这条路径被选中的概率(路径越短,概率越高)。

  • 交叉

  这里我们选择交替位置交叉法(Alternating Position Crossover,APX)来对一对染色体进行交叉操作,其基本原理如下图所示

  左边为父代的两个染色体,右边为子代染色体。 将左上的数组第一个元素放入右上数组的第一位置中,再转移到左下数组第一个元素,查看右上数组是否已经包含了该元素,如果未包含将其插入右上数组中,否则插入右下数组中。接着从左上数组的第二个元素开始,到左下第二个元素,和前次同意的判断操作。如此类推直到右边两个数组都被填满了为止。

  交叉概率:交叉概率对于解的收敛速度有着重要的影响。一般选择0.6-1。

  • 变异

  生物的进化,除了遗传父母的基因,还有自身基因有一定的概率突变。基于这个原理,变异操作在一定的概率上是作用于染色体自身的。

  变异概率:一定的概率师兄自身基因的改变

  在这个问题中,我们选择位置倒换法,即染色体上随机的产生两个位置上数值互换。

  • 终止条件

  一般有两种方式停止交叉,变异的操作。一,预先设定迭代次数。二,多次跌代后,解的质量得不到一定要求的提高,或者解达到要求的质量,这时都可以停止迭代。这个问题上我们选择第一种。

  

  基于TSP问题的遗传算法代码如下:

  

  1. #include <stdio.h>
  2. #include <tchar.h>
  3. #include <math.h>
  4. #include <stdlib.h>
  5. #include <time.h>
  6.  
  7. int scale; /* 种群规模 */
  8. int cityNum; /* 城市数量 */
  9. int *pathlength; /* 存储种群每个个体路径长度 */
  10. double *cumPropa; /* 存储个体累积概率 */
  11. int bestlength; /* 最佳路径长度 */
  12. int *bestpath; /* 最佳路径 */
  13. float pc; /* 交叉概率 */
  14. float pm; /* 变异概率 */
  15. int count; /* 变异次数 */
  16. int MAX_Gene; /* 迭代次数 */
  17.  
  18. /* 函数声明 */
  19. void APCrossover(int **,int ,int ,int );
  20. void copy(int *,int **,int ,int );
  21. void cumDistance(int **,int **, int *,int ,int );
  22. void cumulatePro(int **,double *,int *,int );
  23. void copytoBestPath(int *,int **,int ,int );
  24. void copy(int *,int **,int ,int );
  25. int *creatArray1(int );
  26. double *creatArraydoub(int );
  27. int **creatArray2(int , int );
  28. void CrossAndMutation(int **,int );
  29. void cumDistance(int **,int **, int *,int ,int);
  30. void mutation(int **,int);
  31. void Initialize(int **, int , int );
  32. void readfile(int **, int , int );
  33. void rouletteAlgo(int **,double *, int **,int );
  34. void NcopyO(int **,int **);
  35.  
  36. //创建一个整形的二维整数数组
  37. int **creatArray2(int scale, int cityNum)
  38. {
  39. int i;
  40.  
  41. int **ptemp;
  42.  
  43. ptemp=(int **)malloc(sizeof(int *)*scale);
  44. for(i=;i<scale;i++)
  45. ptemp[i]=(int *)malloc(sizeof(int)*cityNum);
  46.  
  47. return ptemp;
  48. }
  49. //创建一个整形的一维数组
  50. int *creatArray1(int scale)
  51. {
  52. int *tempcreate;
  53. tempcreate=(int *)malloc(sizeof(int)*scale);
  54. return tempcreate;
  55. }
  56. //创建一个双精度型的一维数组
  57. double *creatArraydoub(int scale)
  58. {
  59. double *tempcreate;
  60. tempcreate=(double *)malloc(sizeof(double)*scale);
  61. return tempcreate;
  62. }
  63.  
  64. // 二维数组拷贝
  65. void NcopyO(int **newgeneration,int **oldgeneratoin)
  66. {
  67. int i,j;
  68. for(i=;i<scale;i++)
  69. for(j=;j<cityNum;j++)
  70. oldgeneratoin[i][j]=newgeneration[i][j];
  71.  
  72. }
  73.  
  74. //计算一次迭代各可行解的路径长度
  75. void cumDistance(int **oldgeneration,int **cityDistance, int *pathlength,int cityNum,int scale)
  76. {
  77. int i,j,k;
  78. int length=;
  79. int min=;
  80. int minp;
  81.  
  82. for(i=;i<scale;i++)
  83. {
  84. length=;
  85. for(k=;k<cityNum-;k++)
  86. {
  87. j=k+;
  88. length+=cityDistance[oldgeneration[i][k]][oldgeneration[i][j]];
  89. }
  90. //题目要求回到原点,所以加上回到原点的距离
  91. pathlength[i]=length+cityDistance[oldgeneration[i][cityNum-]][oldgeneration[i][]];
  92.  
  93. if(pathlength[i]<min)
  94. {
  95. min=pathlength[i];
  96. minp=i;
  97. }
  98. }
  99. if(min<bestlength)
  100. {
  101. bestlength=min;
  102. //将每代最优解直接加入下一代中,即“精英保留”原则
  103. copytoBestPath(bestpath,oldgeneration,minp,cityNum);
  104. }
  105.  
  106. }
  107.  
  108. void copytoBestPath(int *bestpath,int **oldGeneration,int position,int cityNum)
  109. {
  110. int i;
  111. for(i=;i<cityNum;i++)
  112. bestpath[i]=oldGeneration[position][i];
  113. }
  114.  
  115. //计算一次迭代中各个可行解作为交叉操作的累积概率
  116.  
  117. void cumulatePro(int **generation,double *cumPropa,int *pathlength,int scale)
  118. {
  119. int i;
  120. double sumlength=;
  121.  
  122. for(i=;i<scale;i++)
  123. sumlength+=/(double)pathlength[i];
  124.  
  125. cumPropa[]=(/(double)pathlength[])/sumlength;
  126.  
  127. for(i=;i<scale;i++)
  128. {
  129. cumPropa[i]=(/(double)pathlength[i])/sumlength+cumPropa[i-];
  130. }
  131. }
  132.  
  133. //初始化话生产相应规模的可行解
  134. void Initialize(int **geneSolution, int scale, int cityNum)
  135. {
  136. int i,j,k;
  137. int randnum;
  138. bool exist;
  139.  
  140. srand(time(NULL));
  141.  
  142. for(i=;i<scale;i++)
  143. for(j=;j<cityNum;j++)
  144. {
  145. exist=true;
  146. while(exist==true){
  147. //注意:rand()/Rand_MAX 结果只能是0, 应该先进行类型转换
  148. randnum=(int)(((double)rand()/RAND_MAX)*cityNum);
  149. for(k=;k<j;k++)
  150. if(geneSolution[i][k]==randnum)
  151. break;
  152. if(k==j)
  153. exist=false;
  154. }
  155. geneSolution[i][j]=randnum;
  156. }
  157.  
  158. }
  159.  
  160. //读取文件中的各相邻点的距离信息
  161. void readfile(int **cityDistance, int scale, int cityNum)
  162. {
  163. int i,j;
  164. FILE *fp;
  165. errno_t err;
  166.  
  167. err=fopen_s(&fp,"data.txt","r");
  168.  
  169. if(err!=)
  170. {
  171. printf("The file can not be found!\n");
  172. }
  173. else
  174. {
  175.  
  176. for(i=;i<scale;i++)
  177. {
  178. for(j=;j<cityNum;j++)
  179. {
  180. fscanf_s(fp,"%d ",&cityDistance[i][j]);
  181. }
  182. }
  183.  
  184. fclose(fp);
  185. }
  186.  
  187. }
  188.  
  189. //拷贝一条路径
  190. void copy(int *oldGeneration,int **newGeneration,int position,int cityNum)
  191. {
  192. int i;
  193. for(i=;i<cityNum;i++)
  194. newGeneration[position][i]=oldGeneration[i];
  195. }
  196.  
  197. //使用轮盘赌算法选择交叉的对象
  198. void rouletteAlgo(int **oldGeneration,double *cumPropa, int **newGeneration,int scale)
  199. {
  200. int i,j;
  201. double randNum;
  202.  
  203. for(i=;i<scale-;i++)
  204. {
  205. randNum=(double)rand()/RAND_MAX;
  206. if(cumPropa[]>=randNum)
  207. copy(oldGeneration[],newGeneration,i,scale);
  208. else
  209. {
  210. for(j=;j<scale;j++)
  211. if(randNum>cumPropa[j] && randNum<=cumPropa[j])
  212. break;
  213. copy(oldGeneration[i],newGeneration,i,scale);
  214. }
  215. }
  216.  
  217. copy(bestpath,newGeneration,scale-,scale);
  218.  
  219. }
  220.  
  221. //交叉操作:交替位置交叉法(Alternating Position Crossover,APX)
  222. void APCrossover(int **newgeneration,int p1,int p2,int cityNum)
  223. {
  224. int i;
  225. int s1=;
  226. int s2=;
  227.  
  228. int *tempArray1;
  229. int *tempArray2;
  230.  
  231. tempArray1=creatArray1(cityNum);
  232. tempArray2=creatArray1(cityNum);
  233.  
  234. for(i=;i<cityNum;i++)
  235. {
  236. tempArray1[i]=newgeneration[p1][i];
  237. tempArray2[i]=newgeneration[p2][i];
  238. }
  239.  
  240. int m,n;
  241. m=;
  242. n=;
  243.  
  244. while(s1< || s2<)
  245. {
  246. for(i=;i<s1;i++)
  247. if(tempArray1[m]==newgeneration[p1][i])
  248. break;
  249. if(i==s1)
  250. {
  251. newgeneration[p1][s1]=tempArray1[m];
  252. m++;
  253. s1++;
  254. }
  255. else{
  256. newgeneration[p2][s2]=tempArray1[m];
  257. m++;
  258. s2++;
  259. }
  260.  
  261. for(i=;i<s1;i++)
  262. if(tempArray2[n]==newgeneration[p2][i])
  263. break;
  264. if(i==s1)
  265. {
  266. newgeneration[p1][s1]=tempArray2[n];
  267. n++;
  268. s1++;
  269. }
  270. else{
  271. newgeneration[p2][s2]=tempArray2[n];
  272. n++;
  273. s2++;
  274. }
  275. }
  276.  
  277. }
  278.  
  279. //变异操作
  280. void mutation(int **newgeneration,int p1)
  281. {
  282. int rand1,rand2;
  283. int temp;
  284. int i;
  285.  
  286. srand(time(NULL));
  287.  
  288. for(i=;i<count;i++)
  289. {
  290.  
  291. rand1=(int)(((double)rand()/RAND_MAX)*cityNum);
  292. rand2=(int)(((double)rand()/RAND_MAX)*cityNum);
  293. while(rand1==rand2)
  294. {
  295. rand2=(int)(((double)rand()/RAND_MAX)*cityNum);
  296. }
  297.  
  298. temp=newgeneration[p1][rand1];
  299. newgeneration[p1][rand1]=newgeneration[p1][rand2];
  300. newgeneration[p1][rand2]=temp;
  301. }
  302.  
  303. }
  304.  
  305. //对一代群体进行交叉变异操作
  306. void CrossAndMutation(int **newgeneration,int cityNum)
  307. {
  308. float rand1,rand2;
  309. int k;
  310. for(k=;k<cityNum;k=k+)
  311. {
  312. srand(time(NULL));
  313. rand1=(float)rand()/RAND_MAX;
  314.  
  315. if(rand1>pc)
  316. {
  317. APCrossover(newgeneration,k,k+,cityNum);
  318. }
  319. else
  320. {
  321. rand2=(float)rand()/RAND_MAX;
  322. if(rand2>pm)
  323. {
  324. mutation(newgeneration,k);
  325. }
  326. rand2=(float)rand()/RAND_MAX;
  327. if(rand2>pm)
  328. {
  329. mutation(newgeneration,k+);
  330. }
  331.  
  332. }
  333. }
  334.  
  335. }
  336.  
  337. int main()
  338. {
  339. int **a;
  340. int **oldGeneration;
  341. int **newGeneration;
  342. int i,j;
  343.  
  344. MAX_Gene=;
  345. cityNum=;
  346. scale=;
  347. bestlength=;
  348. pc=0.6;
  349. pm=0.5;
  350. count=;
  351. int m;
  352.  
  353. a=creatArray2(scale,cityNum);
  354. pathlength=creatArray1(scale);
  355. cumPropa=creatArraydoub(scale);
  356. bestpath=creatArray1(cityNum);
  357.  
  358. readfile(a,scale,cityNum);
  359.  
  360. oldGeneration=creatArray2(scale,cityNum);
  361. newGeneration=creatArray2(scale,cityNum);
  362.  
  363. Initialize(newGeneration,scale,cityNum);
  364.  
  365. for(m=;m<MAX_Gene;m++)
  366. {
  367. NcopyO(newGeneration,oldGeneration);
  368. cumDistance(oldGeneration,a,pathlength,scale,cityNum);
  369. cumulatePro(oldGeneration,cumPropa,pathlength,scale);
  370. rouletteAlgo(oldGeneration,cumPropa,newGeneration,scale);
  371. CrossAndMutation(newGeneration,cityNum);
  372.  
  373. }
  374.  
  375. printf("The best path is :\n");
  376.  
  377. for(i=;i<cityNum;i++)
  378. {
  379. printf("%d ",bestpath[i]);
  380. }
  381. printf("\n");
  382.  
  383. printf("The minmum length is %d\n",bestlength);
  384.  
  385. return ;
  386. }

  遗传算法只能得到问题的近似最优解,而且对不同的问题,该算法的性能也不一样。一般要求结合问题的一些特点和属性或者与其他的演化算法相结合,例如蚁群算法,粒子群算法,模拟退火法等。

  

  

  

  

  

  

基于遗传算法(Genetic Algorithm)的TSP问题求解(C)的更多相关文章

  1. 遗传算法Genetic Algorithm

    遗传算法Genetic Algorithm 好家伙,回回都是这个点,再这样下去人估计没了,换个bgm<夜泊秦淮>,要是经典咏流传能投票选诗词,投票选歌,俺一定选这个 开始瞎叨叨 遗传算法的 ...

  2. 【智能算法】超详细的遗传算法(Genetic Algorithm)解析和TSP求解代码详解

    喜欢的话可以扫码关注我们的公众号哦,更多精彩尽在微信公众号[程序猿声] 文章声明 此文章部分资料和代码整合自网上,来源太多已经无法查明出处,如侵犯您的权利,请联系我删除. 00 目录 遗传算法定义 生 ...

  3. 遗传算法(Genetic Algorithm)——基于Java实现

    一.遗传算法原理介绍 遗传算法(Genetic Algorithm)是模拟达尔文生物进化论的自然选择和遗传学机理的生物进化过程的计算模型,是一种通过模拟自然进化过程搜索最优解的方法.遗传算法是从代表问 ...

  4. 超详细的遗传算法(Genetic Algorithm)解析

    https://blog.csdn.net/u010451580/article/details/51178225 https://www.jianshu.com/p/c82f09adee8f 00 ...

  5. 遗传算法 Genetic Algorithm

    2017-12-17 19:12:10 一.Evolutionary Algorithm 进化算法,也被成为是演化算法(evolutionary algorithms,简称EAs),它不是一个具体的算 ...

  6. MIP启发式算法:遗传算法 (Genetic algorithm)

    *本文主要记录和分享学习到的知识,算不上原创 *参考文献见链接 本文主要讲述启发式算法中的遗传算法.遗传算法也是以local search为核心框架,但在表现形式上和hill climbing, ta ...

  7. 遗传算法(Genetic Algorithm, GA)及MATLAB实现

    遗传算法概述: • 遗传算法(Genetic Algorithm,GA)是一种进化算法,其基本原理是仿效生物界中的“物竞天择.适者生存”的演化法则,它最初由美国Michigan大学的J. Hollan ...

  8. Evolutionary Computing: 3. Genetic Algorithm(2)

    承接上一章,接着写Genetic Algorithm. 本章主要写排列表达(permutation representations) 开始先引一个具体的例子来进行表述 Outline 问题描述 排列表 ...

  9. Evolutionary Computing: 2. Genetic Algorithm(1)

    本篇博文讲述基因算法(Genetic Algorithm),基因算法是最著名的进化算法. 内容依然来自博主的听课记录和教授的PPT. Outline 简单基因算法 个体表达 变异 重组 选择重组还是变 ...

随机推荐

  1. 实例测试java的Integer转String的效率问题1.8

    原文链接:https://blog.csdn.net/chicaohun7473/article/details/100851373 查看String源码时,读到源码的toString方法时,打算探究 ...

  2. Neo4j删除节点和关系、彻底删除节点标签名

    https://www.jianshu.com/p/59bd829de0de 总结提前: [1]先删关系,再删节点 [2]当记不得关系名时,type(r)可以查到关系名 [3]彻底删除节点标签名,需要 ...

  3. kafka offset manage

    kafka low api:fetch数据从topic partition offset buffsize长度. 提交一般两个维度:时间维度,满多少条提交(0.8X之前是没这参数) 在0.8.2.2版 ...

  4. Nmap扫描原理(上)

    转自:https://blog.csdn.net/qq_34398519/article/details/89055991 Nmap是一款开源免费的网络发现(Network Discovery)和安全 ...

  5. BZOJ4383/LuoGuP3588 Pustynia/PUS 线段树建图优化

    我会告诉你我看了很久很久才把题目看懂吗???怀疑智商了 原来他给的l,r还有k个数字都是下标... 比如给了一个样例 l, r, k, x1,x2,x3...xk,代表的是一个数组num[l]~num ...

  6. 初识css3 3d动画效果

    (先看我博客右上角的3d盒子动画效果,目前没做兼容处理,最好最新的chrome看)无意间看到网上css3写的3d动画效果,实在炫酷,以前理解为需要js去计算去写,没想到css直接可以实现.于是开始研究 ...

  7. 工控安全入门(三)—— 再解S7comm

    之前的文章我们都是在ctf的基础上学习工控协议知识的,显然这样对于S7comm的认识还不够深刻,这次就做一个实战补全,看看S7comm还有哪些值得我们深挖的地方. 本篇是对S7comm的补全和实战,阅 ...

  8. PAT甲级——A1064 Complete Binary Search Tree

    A Binary Search Tree (BST) is recursively defined as a binary tree which has the following propertie ...

  9. VS2010中Cocos2d-x中文乱码问题

    不罗嗦,直接进入主题,VS2010的默认编码是"GB2312",那么以创建一个label为例,当我们使用 CCLabelTTF::create(const char *label, ...

  10. Jemter测压工具的安装与使用

    注:在安装Jmeter之前,请先检查下电脑有没有装JDK:开始->运行->然后输入cmd->进入命令行界面,输入java -version , 出现以下信息就是此电脑已安装了JDK ...