这次的文章是以一份报告的形式贴上来,代码只是简单实现,难免有漏洞,比如循环输入的控制条件,说是要求输入1,只要输入非0就行。希望会帮到以后的同学(*^-^*)

一、问题描述

旅行商问题(Traveling-Salesman Problem,TSP)。设有n个互相可直达的城市,某推销商准备从其中的A城出发,周游各城市一遍,最后又回到A城。要求为该旅行商规划一条最短的旅行路线。

二、目的

为了解决旅行商问题,用了遗传算法,模拟染色体的遗传过程,进行求解。

为了直观的更有比较性的观察到程序的运行效果,我这里程序里给定了10个城市的坐标,并计算出其任意两个的欧氏距离,10个点的位置排布见图1。程序的理想最优距离为20.485281,即绕三角形一圈,而且路程起点不固定,因为只要满足点围着三角形一圈即为最短距离,最优解。所以问题转换为,求图中10 个点的不重复点的闭环序列的距离最小值。

图 1

三、原理

1、内部变量介绍

程序总体围绕了遗传算法的三个主要步骤:选择--复制,交叉,变异。给定了10个种群,即10条染色体,每条染色体都是除首位外不重复的点组成,首尾相同保证路线是闭合的,所以一条染色体包含11个点。

种群由一个结构体group表示,内含城市的序列int city[11]、种群的适应度double fit、该种群适应度占总群体适应度的比例double p,和为了应用赌轮选择机制的积累概率 double jlleigailv。

程序还包括一个始终记录所有种群中的最优解的城市序列数组groupbest[11],记录最优解的适应度,即最大适应度的变量 double groupbestfit。

种群的最大繁衍代数设置为1000,用户能够输入繁衍代数,但必须在1000以内。10个点的不同排列序列有10!种,即3628800中排列可能,其中各代之间可能产生重复,不同种群间也会出现重复,学生觉得1000左右应该能验证程序的性能了,就定为1000。

2、运行思想介绍

(a)采用整数编码的方式,标记0到9号城市。

(b)选择--复制:

利用赌轮选择机制,分10次从10个种群中挑选出10个染色体进行复制。

每次随机生成一个0到1之内的小数,因为适应度越高的染色体的积累概率区间越大,所以适应度越高的染色体被选择的次数会越多,满足了优胜劣汰的原则。

选择--复制完后,要重新计算每个种群的适应度等信息,与已经保存的最优染色体进行比较,如果比已经存在的适应度还要高就进行最优染色体的更新。如果最优染色体没有更新,则说明新成的最大适应度种群不如以前的好,则在新生成的种群中找到适应度最低的,用最优染色体提换掉。

(c)交叉:

每一代的繁衍都让10个种群中相邻的两个种群进行染色体交叉,交叉率为1,即0号种群和1号种群交叉,2号种群和3号种群交叉,以此类推。交叉段是由2个随机数决定,采用部分映射交叉,直接交换由随机数产生的染色体片段。

交叉完后因为要满足点的不重复,所以要进行消除重复的操作。原理是用一个数组保留交叉过来的染色体片段,删除染色体上已经交换的片段,在剩下的点中消除与新交换片段中重复的点,然后将原染色体剩下的点都向前移集中在头部,再将保存在数组中的新交换过来的染色体插入到头部之后,在以上过程中用一个数组记录已经存在的点。接下来将没有用的点顺序插到染色体尾部,到此已经生成了新的染色体。

交叉完后要重新计算每个种群的适应度等信息,与已经保存的最优染色体进行比较,如果比已经存在的适应度还要高就进行最优染色体的更新。如果最优染色体没有更新,则说明新成的最大适应度种群不如以前的好,则在新生成的种群中找到适应度最低的,用最优染色体提换掉。

(d)变异:

因为变异在自然界并不是每次都会发生,所有每次要尽行变异前都生成一个0到9内的随机整数,如果大于3就进行变异,否则不变异,总体变异率为0.6。因为这个染色体和自然中的每一段都对应一个功能的染色体不一样,染色体越是按数的相邻大小排列,距离会越短,所以变异就起了很大作用,起到调整点的顺序的作用,所以变异率要大一点。

如果要变异,则变异3次。每次生成2个随机数,决定要在哪个种群变异哪一个位置。比如挑选了第二条染色体,变异第3个位置,则将染色体的3号位和6号位互换,即互换位置和为9。

(e)输出:

输出用户程序得到的最优解的城市序列、路程距离、适应度和在第几代得到的最优解。

程序可以循环多次运行,只要用户按照提示输入。

四、结果

程序的总体结果又很大随机性,但特别坏的结果毕竟只占少数,多数都是一般结果,多随机运行几次,还有控制繁衍的代数就能够得到比较好的结果。令人振奋的是,经过多次的尝试,又一次输出了理想最优解,正好是围三角形绕一圈,路程距离是20.485281,在第756代生成。一下是程序运行的比较好的结果、比较差的结果的举例,和最优结果的贴图,图2为最优结果。

图2

图3

图4

五、讨论

这次程序--遗传算法解决旅行商问题,的总体思想结合了课本和网上有关内容,但令人振奋的是,上图1给的实例,还有具体的实现代码和里面的具体思想都是出自学生仔细思考的结果,没有抄袭,程序的每一步正确运行都凝聚了学生认真对待的心血。

写这个程序总体上分成了2个步骤,思想理解与构造,具体代码实现。

大约用了1个白天天左右的时间去了解:遗传算法怎么去解决这个问题,怎样进行选择复制,交叉的方法有哪些,变异的方法又有哪些。决定了每一步用的思想后便进行具体敲代码。

敲代码又分成了两段。第一段时间,在开始编码的那天晚上我完成了程序的基本内容,跑一下也没有问题,只是效果有很大随机性,结果不是很好,第二天想了很久,又看了很多资料发现,原因在于交叉选择的方法不好。一开始那种方法是,交换后的染色体直接放到相邻染色体的相应位置,在其他地方进行消除重复的操作,染色体上会出现很多空洞,然后用没有用过的点顺序插在空洞里,构成新的染色体。后来我想了很久,吃饭时走在路上一想,不对啊,这样有可能完全破坏了原来的优秀队列,因为这个优秀染色体就是由排列顺序决定的。回来后就修改了交叉的算法,形成了现在的算法,这样能够最大程度的保留原来的顺序,又能很大可能获得新的优秀队列。

程序有400多行,交叉算法用的变量多而且又复杂,其中用来找bug的时间比较多,敲代码的时候有很多细节问题脑子一开始想好了的,没有敲上去。而且一边敲,一边也在对原由思想进行改进。

总之,遗传算法的可移植性很大,可以用来逼近很多问题的最优解,实在是很厉害。做了这么多,我真的感觉收获良多,一些细节处的bug好烦人,希望以后自己能够更加细心。

六、代码

里面的思想在前面已经讲了,但里面的变量我设的时候标注的不是很清楚,思想懂了,代码完全可以自己写出来的(*^-^*)

 #include<stdio.h>
#include<string.h>
#include<time.h>
#include<math.h> double distance[][];//城市之间的距离
int dai,die;
int cities[][];//记录城市坐标
int citynum=;
int groupbest[];//最优解染色体
double groupbestp;//最优解的p
double groupbestfit;//最优解的fit
int changebest;//要不要用最优解替代新种群 struct group
{
int city[];//一维记录城市序号,二三维记录坐标
double p;//占总群的概率
double fit;//适应度
double jileigailv;
}group[]; /*用来计算种群的p、fit*/
void jisuan()
{
int i,j,k;
double ss,s;
s=0.0;
ss=0.0;
for(k=;k<;k++)
{
for(i=;i<citynum;i++)
{
s+=distance[group[k].city[i]][group[k].city[i+]];
}
group[k].fit=1.0/s;
ss+=group[k].fit;
}
s=0.0;
for(i=;i<;i++)
{
group[i].p=group[i].fit/ss;
s+=group[i].p;
group[i].jileigailv=s;
}
}
/*保存最优解*/
void savebest()
{
int i,j,flag=;
double fit=groupbestfit;
j=;
for(i=;i<;i++)
{
if(group[i].fit>fit)
{
j=i;
fit=group[i].fit;
flag=;//标记已经有更好的
}
}
if(flag)
{
dai=die;
for(i=;i<citynum+;i++)//保存最优解
{
groupbest[i]=group[j].city[i];
}
groupbestp=group[j].p;
changebest=;
groupbestfit=group[j].fit;
}
else
changebest=;//说明新生成的解还不如原来的好,要进行替换
}
/*用最优解替代新种群中的最差的染色体*/
void changebestgroup()
{
int j,i;
double fit=group[].fit;
j=;
if(changebest)
{
for(i=;i<;i++)
{
if(group[i].fit<fit)
{
fit=group[i].fit;
j=i;
}
}
for(i=;i<citynum+;i++)
{
group[j].city[i]=groupbest[i];
}
jisuan();
}
}
/*初始种群和城市坐标,计算距离*/
void chushigroup()
{
int i,j,t,flag,k;
double ss;
cities[][]=;//初始化坐标
cities[][]=;
cities[][]=;
cities[][]=;
cities[][]=;
cities[][]=;
cities[][]=;
cities[][]=;
cities[][]=;
cities[][]=;
cities[][]=;
cities[][]=;
cities[][]=;
cities[][]=;
cities[][]=;
cities[][]=;
cities[][]=;
cities[][]=;
cities[][]=;
cities[][]=;
memset(groupbest,-,sizeof(groupbest));
groupbestp=0.0;
groupbestfit=0.0;
changebest=;
for(i=;i<citynum;i++)
for(j=;j<=i;j++)
{
if(j==i)
distance[i][j]=0.0;
else
{
distance[i][j]=sqrt(pow(cities[][i]-cities[][j],2.0)+pow(cities[][i]-cities[][j],2.0));//欧氏距离
distance[j][i]=distance[i][j];
}
}
printf("最优解的距离是:%f\n",distance[][]+distance[][]+distance[][]+distance[][]+distance[][]+distance[][]+distance[][]+distance[][]+distance[][]+distance[][]);
srand((unsigned)time(NULL));
ss=;
for(k=;k<;k++)
{//一个数量为10的种群,和10 个城市环
for(i=;i<citynum;i++)
{
flag=;
while(flag)
{
t=rand()%citynum;
for(j=;j<i;j++)
{
if(group[k].city[j]==t)
{
break;
}
}
if(j==i)
{
group[k].city[i]=t;
flag=;
}
}
}
group[k].city[]= group[k].city[];
}
//以上产生了10 个种群,分别有不重复的染色体 jisuan();
savebest();
printf("初始种群为:\n");
for(i=;i<;i++)
{
for(j=;j<citynum+;j++)
printf("%d ",group[i].city[j]);
printf("||适应度:%f,占总群的概率:%f\n",group[i].fit,group[i].p);
}
}
/*选择--复制*/
void xuanze()
{
int i,j,temp[][],k;
double t;
srand((unsigned)time(NULL));
for(i=;i<;i++) //选10条染色体出来复制,赌轮
{
t=rand()%*1.0/;
for(j=;j<;j++)
{ if(t<=group[j].jileigailv)
{
for(k=;k<citynum+;k++)
{
temp[i][k]=group[j].city[k];
}
break;
}
}
}
//拷贝新种群
for(i=;i<;i++)
for(j=;j<citynum+;j++)
{
group[i].city[j]=temp[i][j];
}
jisuan();
savebest();
changebestgroup();
}
/*交叉*/
void jiaocha()
{
int point1,point2,temp,i,j,k,temp3[][],temp2[][],num,write;
srand((unsigned)time(NULL));
point1=rand()%;
point2=rand()%;
if(point1>point2)
{
temp=point1;
point1=point2;
point2=temp;
}
//交换,每2条交换
if(point1!=point2)
{
for(j=;j<;j=j+)
{
memset(temp3,-,sizeof(temp3));
memset(temp2,-,sizeof(temp2));
k=;
for(i=point1;i<=point2;i++)
{
temp2[][k]=group[j].city[i];
temp2[][k]=group[j-].city[i];
temp3[][temp2[][k]]=;//标记数字已经存在了
temp3[][temp2[][k]]=;
k++;
group[j].city[i]=-;
group[j-].city[i]=-;
}
num=point2-point1+;//交换的位数
//消重
for(k=;k<point1;k++)
{
if(temp3[][group[j-].city[k]]==)
{
group[j-].city[k]=-;
}
else
temp3[][group[j-].city[k]]=;
}
for(k=point2+;k<citynum;k++)
{
if(temp3[][group[j-].city[k]]==)
{
group[j-].city[k]=-;
}
else
temp3[][group[j-].city[k]]=;
}
for(k=;k<point1;k++)
{
if(temp3[][group[j].city[k]]==)
{
group[j].city[k]=-;
}
else
temp3[][group[j].city[k]]=;
}
for(k=point2+;k<citynum;k++)
{
if(temp3[][group[j].city[k]]==)
{
group[j].city[k]=-;
}
else
temp3[][group[j].city[k]]=;
}
write=;
for(i=;i<;i++)
{
while(write<&&group[j-].city[write]==-)
{
write++;
}
if(write<)
{
temp=group[j-].city[i];
group[j-].city[i]=group[j-].city[write];
group[j-].city[write]=temp;
write++;
}
else
{
write=;
for(k=i;k<;k++)
{
group[j-].city[k]=temp2[][write++];
if(write==num)
break;
}
break;
}
}
write=;
for(i=;i<;i++)
{
while(write<&&group[j].city[write]==-)
{
write++;
}
if(write<)
{
temp=group[j].city[i];
group[j].city[i]=group[j].city[write];
group[j].city[write]=temp;
write++;
}
else
{
write=;
for(k=i;k<;k++)
{
group[j].city[k]=temp2[][write++];
if(write==num)
break;
}
break;
}
}
k=;
for(i=;i<citynum;i++)
{
if(group[j-].city[i]==-)
{
while(temp3[][k]==&&k<)
{
k++;
}
group[j-].city[i]=k++;
}
}
k=;
for(i=;i<citynum;i++)
{
if(group[j].city[i]==-)
{
while(temp3[][k]==&&k<)
{
k++;
}
group[j].city[i]=k++;
}
}
group[j].city[]=group[j].city[];
group[j-].city[]=group[j-].city[];
}//end of j
jisuan();
savebest();
changebestgroup();
} //end of if(!)
}
/*变异*/
void bianyi()
{
int t1,t2,temp,t,s=;
srand((unsigned)time(NULL));
t=rand()%;
if(t>)//变异率为3/5
{
//挑1个不同的变异,只交换一位
t1=rand()%;//种群
t2=rand()%;//变换位
temp=group[t1].city[t2];
group[t1].city[t2]=group[t1].city[-t2];
group[t1].city[-t2]=temp;
group[t1].city[]=group[t1].city[];
}
jisuan();
savebest();
changebestgroup();
}
/*主函数*/
int main()//最优解20.485281
{
int j,flag,tuichu=;
double distancenum;
while(tuichu)
{
distancenum=0.0;
flag=;
chushigroup();
while(flag)
{
printf("请输入种群繁衍代数(1000以内):");
scanf("%d",&die);
if(die<=)
{
flag=;
}
}
while(die--)
{
xuanze();
jiaocha();
bianyi();
}
printf("最优种群是:\n");
for(j=;j<citynum+;j++)
{
printf("%d ",groupbest[j]);
if(j<citynum)
{
distancenum+=distance[groupbest[j]][groupbest[j+]];
}
}
printf("距离为:%f,适应度为:%f,代数:%d\n\n",distancenum,groupbestfit,dai);
printf("继续产生新种群请按输入1,退出请输入0:");
scanf("%d",&tuichu);
printf("\n");
}
return ;
}

遗传算法解决旅行商问题(TSP)的更多相关文章

  1. 【智能算法】用模拟退火(SA, Simulated Annealing)算法解决旅行商问题 (TSP, Traveling Salesman Problem)

    喜欢的话可以扫码关注我们的公众号哦,更多精彩尽在微信公众号[程序猿声] 文章声明 此文章部分资料和代码整合自网上,来源太多已经无法查明出处,如侵犯您的权利,请联系我删除. 01 什么是旅行商问题(TS ...

  2. tsp问题——遗传算法解决

    TSP问题最简单的求解方法是枚举法. 它的解是多维的.多局部极值的.趋于无穷大的复杂解的空间.搜索空间是n个点的全部排列的集合.大小为(n-1)! .能够形象地把解空间看成是一个无穷大的丘陵地带,各山 ...

  3. 【高级算法】遗传算法解决3SAT问题(C++实现)

    转载请注明出处:http://blog.csdn.net/zhoubin1992/article/details/46910079 1 SAT问题描写叙述 命题逻辑中合取范式 (CNF) 的可满足性问 ...

  4. 遗传算法解决寻路问题——Python描述

    概要 我的上一篇写遗传算法解决排序问题,当中思想借鉴了遗传算法解决TSP问题,本质上可以认为这是一类问题,就是这样认为:寻找到一个序列X,使F(X)最大. 详解介绍 排序问题:寻找一个序列,使得这个序 ...

  5. 遗传算法解决TSP问题实现以及与最小生成树的对比

    摘要: 本实验采用遗传算法实现了旅行商问题的模拟求解,并在同等规模问题上用最小生成树算法做了一定的对比工作.遗传算法在计算时间和占用内存上,都远远优于最小生成树算法. 程序采用Microsoft vi ...

  6. 遗传算法解决TSP问题

    1实验环境 实验环境:CPU i5-2450M@2.50GHz,内存6G,windows7 64位操作系统 实现语言:java (JDK1.8) 实验数据:TSPLIB,TSP采样实例库中的att48 ...

  7. 转:遗传算法解决TSP问题

    1.编码 这篇文章中遗传算法对TSP问题的解空间编码是十进制编码.如果有十个城市,编码可以如下: 0 1 2 3 4 5 6 7 8 9 这条编码代表着一条路径,先经过0,再经过1,依次下去. 2.选 ...

  8. 用遗传算法解决TSP问题

    浅谈遗传算法:https://www.cnblogs.com/AKMer/p/9479890.html Description \(小m\)在踏上寻找\(小o\)的路程之后不小心碰到了大魔王\(fat ...

  9. 禁忌搜索算法TSA 旅行商问题TSP python

    import math import random import numpy as np import matplotlib.pyplot as plt from mpl_toolkits.mplot ...

随机推荐

  1. [ACM] POJ 3259 Wormholes (bellman-ford最短路径,推断是否存在负权回路)

    Wormholes Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 29971   Accepted: 10844 Descr ...

  2. Android onTouch、OnLongClick、onClick和ScrollView滑动事件冲突

    为了实现近期录制的长按,松开手指,结束录制功能.在项目,难道你去走一走会头晕,书写demo为了下一个梳理. 顺便研究android事件调用机制. 先上效果界面: 布局: <RelativeLay ...

  3. Unable to start MySQL service. Another MySQL daemon is already running with the same UNIX socket

    Unable to start MySQL service. Another MySQL daemon is already running with the same UNIX socket 特征 ...

  4. 黄聪:Microsoft Enterprise Library 5.0 系列教程(六) Security Application Block

    原文:黄聪:Microsoft Enterprise Library 5.0 系列教程(六) Security Application Block 开发人员经常编写需要安全功能的应用程序.这些应用程序 ...

  5. hdu2175汉诺塔IX

    汉诺塔IX Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Subm ...

  6. Android 关于资源适配

    一. 关于图片资源 图片宽高 不要固定大小,在小屏幕和大屏幕,不同分频率上 ,採用不同的图片,这个要美工切图的. 不同的分辨率,界面的长宽比不一致,须要不同规格的图片 在drawable-hdpi,d ...

  7. 将 Android* x86 NDK 供 Eclipse* 而移植 NDK 演示示例应用程序

    目标 面向 Eclipse (ADT) 的 Android 插件如今支持基于 NDK 的应用开发. 其可自己主动生成项目和构件文件以及代码存根.并可集成到整个 Android 应用开发中(构建原生库. ...

  8. c++堆栈实现

    A Stack is a data-structure that You can only add an element to the top of the Stack, andYou can onl ...

  9. oracle 优化or 更换in、exists、union all几个字眼,测试没有问题!

    oracle 优化or 更换in.exists.union几个字眼.测试没有问题! 根据实际情况选择相应的语句是.假设指数,or全表扫描,in 和not in 应慎用.否则会导致全表扫描.  sele ...

  10. JAVA技术交流群

    推荐:组[八方扑灭]:http://jq.qq.com/?_wv=1027&k=RFLXu0. QQ: 292352612  集团专注于技术.软件project.JAVA.c\c++.WEB. ...