zoukankan      html  css  js  c++  java
  • 遗传算法解决旅行商问题(TSP)

    这次的文章是以一份报告的形式贴上来,代码只是简单实现,难免有漏洞,比如循环输入的控制条件,说是要求输入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好烦人,希望以后自己能够更加细心。

    六、代码

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

      1 #include<stdio.h>
      2 #include<string.h>
      3 #include<time.h>
      4 #include<math.h>
      5 
      6 double distance[10][11];//城市之间的距离 
      7 int dai,die;
      8 int cities[2][10];//记录城市坐标
      9 int citynum=10;
     10 int groupbest[11];//最优解染色体 
     11 double groupbestp;//最优解的p 
     12 double groupbestfit;//最优解的fit 
     13 int changebest;//要不要用最优解替代新种群 
     14 
     15 struct group
     16 {
     17   int city[11];//一维记录城市序号,二三维记录坐标
     18   double p;//占总群的概率 
     19   double fit;//适应度 
     20   double jileigailv;
     21 }group[10];
     22 
     23 /*用来计算种群的p、fit*/
     24 void jisuan() 
     25 {
     26   int i,j,k;
     27   double ss,s;
     28   s=0.0;
     29   ss=0.0;
     30   for(k=0;k<10;k++)
     31   {
     32     for(i=0;i<citynum;i++)
     33         {
     34           s+=distance[group[k].city[i]][group[k].city[i+1]];
     35         }
     36     group[k].fit=1.0/s;
     37     ss+=group[k].fit;
     38   }
     39   s=0.0;
     40   for(i=0;i<10;i++)
     41   {
     42   group[i].p=group[i].fit/ss;
     43   s+=group[i].p;
     44   group[i].jileigailv=s;
     45   } 
     46 }
     47 /*保存最优解*/ 
     48 void savebest()
     49 {
     50   int i,j,flag=0;
     51   double fit=groupbestfit;
     52   j=0;
     53   for(i=0;i<10;i++)
     54   {
     55      if(group[i].fit>fit)
     56      {
     57        j=i;
     58        fit=group[i].fit;
     59        flag=1;//标记已经有更好的 
     60      }
     61   }
     62      if(flag)
     63      {
     64          dai=die;
     65        for(i=0;i<citynum+1;i++)//保存最优解
     66        {
     67           groupbest[i]=group[j].city[i];
     68        }
     69        groupbestp=group[j].p;
     70        changebest=0; 
     71        groupbestfit=group[j].fit;
     72      }
     73      else
     74       changebest=1;//说明新生成的解还不如原来的好,要进行替换
     75 }
     76 /*用最优解替代新种群中的最差的染色体*/
     77 void changebestgroup()
     78 {
     79   int j,i;
     80   double fit=group[0].fit;
     81   j=0;
     82   if(changebest)
     83   {
     84     for(i=1;i<10;i++)
     85     {
     86       if(group[i].fit<fit)
     87       {
     88         fit=group[i].fit;
     89         j=i;
     90       }
     91     }
     92     for(i=0;i<citynum+1;i++)
     93     {
     94       group[j].city[i]=groupbest[i];
     95     }
     96     jisuan();
     97   }
     98 }
     99 /*初始种群和城市坐标,计算距离*/ 
    100 void chushigroup()
    101 {
    102   int i,j,t,flag,k;
    103   double ss;
    104   cities[0][0]=0;//初始化坐标
    105   cities[1][0]=0;
    106   cities[0][1]=2;
    107   cities[1][1]=0;
    108   cities[0][2]=4;
    109   cities[1][2]=0;
    110   cities[0][3]=6;
    111   cities[1][3]=0;
    112   cities[0][4]=6;
    113   cities[1][4]=2;
    114   cities[0][5]=6;
    115   cities[1][5]=4;
    116   cities[0][6]=6;
    117   cities[1][6]=6;
    118   cities[0][7]=5;
    119   cities[1][7]=5;
    120   cities[0][8]=4;
    121   cities[1][8]=4;
    122   cities[0][9]=2;
    123   cities[1][9]=2;
    124 memset(groupbest,-1,sizeof(groupbest));
    125 groupbestp=0.0;
    126 groupbestfit=0.0;
    127 changebest=0;
    128   for(i=0;i<citynum;i++)
    129     for(j=0;j<=i;j++)
    130     {
    131       if(j==i)
    132           distance[i][j]=0.0;
    133       else
    134       {
    135           distance[i][j]=sqrt(pow(cities[0][i]-cities[0][j],2.0)+pow(cities[1][i]-cities[1][j],2.0));//欧氏距离
    136           distance[j][i]=distance[i][j];
    137       }
    138     }
    139   printf("最优解的距离是:%f
    ",distance[0][1]+distance[1][2]+distance[2][3]+distance[3][4]+distance[4][5]+distance[5][6]+distance[6][7]+distance[7][8]+distance[8][9]+distance[9][0]);
    140   srand((unsigned)time(NULL));
    141   ss=0;
    142   for(k=0;k<10;k++)
    143   {//一个数量为10的种群,和10 个城市环 
    144     for(i=0;i<citynum;i++)
    145     {
    146         flag=1;
    147         while(flag)
    148         {
    149           t=rand()%citynum;
    150            for(j=0;j<i;j++)
    151           {
    152             if(group[k].city[j]==t)
    153             {
    154               break;
    155             }
    156           }
    157             if(j==i)
    158             {
    159               group[k].city[i]=t;
    160               flag=0;
    161             }
    162          }
    163      }
    164    group[k].city[10]= group[k].city[0];
    165   }
    166   //以上产生了10 个种群,分别有不重复的染色体
    167 
    168 jisuan();
    169 savebest();
    170 printf("初始种群为:
    ");
    171 for(i=0;i<10;i++)
    172 {
    173   for(j=0;j<citynum+1;j++)
    174       printf("%d ",group[i].city[j]);
    175   printf("||适应度:%f,占总群的概率:%f
    ",group[i].fit,group[i].p);
    176 }
    177 }
    178 /*选择--复制*/ 
    179 void xuanze()
    180 {
    181   int i,j,temp[10][11],k;
    182   double t;
    183   srand((unsigned)time(NULL));
    184   for(i=0;i<10;i++) //选10条染色体出来复制,赌轮
    185   {
    186     t=rand()%10000*1.0/10000;
    187     for(j=0;j<10;j++)
    188     {
    189         
    190       if(t<=group[j].jileigailv)
    191       {
    192           for(k=0;k<citynum+1;k++)
    193           {
    194             temp[i][k]=group[j].city[k];
    195           }
    196           break; 
    197       } 
    198     }
    199   }
    200   //拷贝新种群
    201   for(i=0;i<10;i++)
    202     for(j=0;j<citynum+1;j++)
    203     {
    204       group[i].city[j]=temp[i][j];
    205     }
    206     jisuan();
    207     savebest();
    208     changebestgroup();
    209 }
    210 /*交叉*/ 
    211 void jiaocha()
    212 {
    213   int point1,point2,temp,i,j,k,temp3[2][10],temp2[2][10],num,write;
    214   srand((unsigned)time(NULL));
    215   point1=rand()%10;
    216   point2=rand()%10;
    217   if(point1>point2)
    218   {
    219     temp=point1;
    220     point1=point2;
    221     point2=temp;
    222   }
    223   //交换,每2条交换
    224  if(point1!=point2)
    225  {
    226   for(j=1;j<10;j=j+2)
    227   {
    228        memset(temp3,-1,sizeof(temp3));
    229        memset(temp2,-1,sizeof(temp2));
    230        k=0;
    231     for(i=point1;i<=point2;i++)
    232     {
    233      temp2[0][k]=group[j].city[i];
    234      temp2[1][k]=group[j-1].city[i];
    235      temp3[0][temp2[0][k]]=1;//标记数字已经存在了
    236      temp3[1][temp2[1][k]]=1;
    237      k++;
    238      group[j].city[i]=-1;
    239      group[j-1].city[i]=-1;
    240     }
    241     num=point2-point1+1;//交换的位数 
    242      //消重
    243     for(k=0;k<point1;k++)
    244       {
    245         if(temp3[0][group[j-1].city[k]]==1)
    246         {
    247            group[j-1].city[k]=-1;
    248         }
    249         else
    250            temp3[0][group[j-1].city[k]]=1;
    251       } 
    252       for(k=point2+1;k<citynum;k++)
    253       {
    254         if(temp3[0][group[j-1].city[k]]==1)
    255         {
    256            group[j-1].city[k]=-1;
    257         }
    258         else
    259            temp3[0][group[j-1].city[k]]=1;
    260       }
    261       for(k=0;k<point1;k++)
    262       {
    263         if(temp3[1][group[j].city[k]]==1)
    264         {
    265            group[j].city[k]=-1;
    266         }
    267         else
    268            temp3[1][group[j].city[k]]=1;
    269       } 
    270       for(k=point2+1;k<citynum;k++)
    271       {
    272         if(temp3[1][group[j].city[k]]==1)
    273         {
    274            group[j].city[k]=-1;
    275         }
    276         else
    277            temp3[1][group[j].city[k]]=1;
    278       }
    279       write=0; 
    280       for(i=0;i<10;i++)
    281       {
    282       while(write<10&&group[j-1].city[write]==-1)
    283       {
    284         write++;
    285       }
    286       if(write<10)
    287       {
    288         temp=group[j-1].city[i];
    289         group[j-1].city[i]=group[j-1].city[write];
    290         group[j-1].city[write]=temp;
    291         write++;
    292       }
    293       else
    294       {
    295           write=0;
    296       for(k=i;k<10;k++)
    297       {
    298           group[j-1].city[k]=temp2[0][write++];
    299           if(write==num)
    300           break;
    301        }
    302       break;
    303       }
    304     } 
    305      write=0; 
    306      for(i=0;i<10;i++)
    307      {
    308       while(write<10&&group[j].city[write]==-1)
    309       {
    310         write++;
    311       }
    312       if(write<10)
    313       {
    314         temp=group[j].city[i];
    315         group[j].city[i]=group[j].city[write];
    316         group[j].city[write]=temp;
    317         write++;
    318       }
    319       else
    320       {
    321           write=0;
    322         for(k=i;k<10;k++)
    323         {
    324           group[j].city[k]=temp2[1][write++];
    325           if(write==num)
    326               break;
    327          }
    328         break;
    329       }
    330     } 
    331     k=0;
    332     for(i=0;i<citynum;i++)
    333     {
    334        if(group[j-1].city[i]==-1)
    335        {
    336          while(temp3[0][k]==1&&k<10)
    337          {
    338            k++;
    339          }
    340          group[j-1].city[i]=k++;
    341        }
    342     }
    343     k=0;
    344     for(i=0;i<citynum;i++)
    345     {
    346        if(group[j].city[i]==-1)
    347        {
    348          while(temp3[1][k]==1&&k<10)
    349          {
    350            k++;
    351          }
    352          group[j].city[i]=k++;
    353        }
    354     }
    355    group[j].city[10]=group[j].city[0];
    356    group[j-1].city[10]=group[j-1].city[0];
    357   }//end of j
    358  jisuan();
    359  savebest();
    360  changebestgroup();
    361  } //end of if(!)
    362 }
    363 /*变异*/ 
    364 void bianyi()
    365 {
    366    int t1,t2,temp,t,s=3;
    367    srand((unsigned)time(NULL));
    368    t=rand()%10;
    369    if(t>3)//变异率为3/5 
    370    {
    371        //挑1个不同的变异,只交换一位
    372     t1=rand()%10;//种群 
    373     t2=rand()%10;//变换位
    374     temp=group[t1].city[t2];
    375     group[t1].city[t2]=group[t1].city[9-t2];
    376     group[t1].city[9-t2]=temp;
    377     group[t1].city[10]=group[t1].city[0];   
    378    }
    379    jisuan();
    380    savebest();
    381    changebestgroup();
    382 }
    383 /*主函数*/
    384 int  main()//最优解20.485281
    385 {
    386   int j,flag,tuichu=1;
    387   double distancenum;
    388   while(tuichu)
    389   {
    390     distancenum=0.0;
    391     flag=1;
    392     chushigroup();
    393     while(flag)
    394     {
    395     printf("请输入种群繁衍代数(1000以内):");
    396     scanf("%d",&die);
    397     if(die<=1000)
    398     {
    399     flag=0;
    400     }
    401   }
    402   while(die--)
    403   {
    404      xuanze();
    405      jiaocha();
    406      bianyi();
    407   }
    408  printf("最优种群是:
    ");
    409       for(j=0;j<citynum+1;j++)
    410       {
    411         printf("%d ",groupbest[j]);
    412         if(j<citynum)
    413         {
    414         distancenum+=distance[groupbest[j]][groupbest[j+1]];
    415         }
    416       }
    417       printf("距离为:%f,适应度为:%f,代数:%d
    
    ",distancenum,groupbestfit,dai);
    418       printf("继续产生新种群请按输入1,退出请输入0:");
    419       scanf("%d",&tuichu); 
    420       printf("
    ");
    421     }
    422       return 0;
    423 }
  • 相关阅读:
    嵌入式(C)笔试题
    经典C语言编程注意点
    深拷贝和浅拷贝(一)
    拷贝构造函数
    树与二叉树的概念
    线性数据结构
    排序算法_冒泡排序
    排序算法_选择排序
    机器学习_支持向量机
    数据库_11_1~10总结回顾+奇怪的NULL
  • 原文地址:https://www.cnblogs.com/studylyn/p/5097238.html
Copyright © 2011-2022 走看看