• 遗传算法解决旅行商问题GA_TSP


    心血来潮把GA_TSP问题用C++封装起来搞了一遍,期间真是收益不小。

    主要是用STL中的vector和list,结构体赋值中遇到了一些难点,原谅我自己是一棵白菜。

     

    选择方法:用种群前面最优的20%代替后面的20%进行淘汰(当然这个比例可以自己拟定,修改代码中得pm_即可)。

    变异方法:交换一个路径上随机产生的两个城市。

    交叉方法:三交换启发交叉(THGA)。

     

    genticTsp.h 代码如下:

     1 #ifndef GENTIC_TSP_H_
     2 #define GENTIC_TSP_H_
     3 #include <iostream>
     4 #include <vector>
     5 #include <string>
     6 #define CITIES 99
     7 #define UNITS 50
     8 #define MAX_GEN 15
     9 
    10 
    11 struct city{
    12     int id;
    13     int x;
    14     int y;
    15 };
    16 
    17 struct unit{
    18     double length;
    19     std::vector<int> path;
    20     bool operator<(const struct unit &other) const
    21     {
    22         return length < other.length;
    23     }
    24 };
    25 
    26 
    27 class GenticTSP{
    28     public:
    29         GenticTSP();
    30         void initMatrix(const std::string &filename);//file to matrix
    31         void initGroup();//shuffle a group
    32         double lenOfUnit(unit &p);//
    33         int searchCity(unit &, int id);//search a city's id for cross
    34         void selectGroup();
    35         void crossUnits(unit &p, unit &q, unit &r);
    36         void mutateGroup();
    37         void evoluteGroup();
    38         void printBestUnit();
    39         ~GenticTSP();
    40     private:
    41         unit bestUnit_;
    42         double pm_;// mutate probability
    43         double ps_;//save probability
    44 
    45 };
    46 
    47 #endif  /*GENTIC_TSP_H_*/
    GenticTsp.h

    genticTsp.cpp代码如下:

      1 #include "GenticTSP.h"
      2 #include <iostream>
      3 #include <fstream>
      4 #include <string>
      5 #include <vector>
      6 #include <list>
      7 #include <algorithm>
      8 #include <math.h>
      9 #include <time.h>
     10 #include <string.h>
     11 
     12 using namespace std;
     13 
     14 city cities[CITIES];
     15 unit group[UNITS];
     16 double cityMatrix[CITIES][CITIES];
     17 
     18 GenticTSP::GenticTSP()
     19     :pm_(0.2), ps_(0.8)
     20 {
     21 
     22 }
     23 
     24 GenticTSP::~GenticTSP()
     25 {
     26 
     27 }
     28 
     29 void GenticTSP::initMatrix(const string &filename)
     30 {
     31     int i, j;
     32     ifstream in(filename);
     33     for(i = 0; i < CITIES; ++i)
     34     {
     35 
     36         in >> cities[i].id >> cities[i].x >> cities[i].y;
     37     }
     38 
     39     for(i = 0; i < CITIES; ++i)
     40     {
     41         cityMatrix[i][i] = 0;
     42         for(j = i + 1; j < CITIES; ++j)
     43         {
     44             cityMatrix[i][j] = sqrt((cities[i].x - cities[j].x) * (cities[i].x - cities[j].x) + (cities[i].y - cities[j].y) * (cities[i].y - cities[j].y));
     45             cityMatrix[j][i] = cityMatrix[i][j];
     46         }
     47 
     48     }
     49 }
     50 
     51 
     52 //calculate the path-lenght of each path
     53 double GenticTSP::lenOfUnit(unit &unit)
     54 {
     55     unit.length = 0;
     56     for(int j = 0; j < CITIES - 1; ++j)
     57     {
     58         unit.length += cityMatrix[unit.path[j]][unit.path[j + 1]];
     59     }
     60 
     61     //回到起始城市
     62     unit.length += cityMatrix[unit.path[0] ][unit.path[CITIES - 1] ];
     63 
     64     return unit.length;
     65 }
     66 
     67 
     68 //init group of various paths
     69 void GenticTSP::initGroup()
     70 {
     71     vector<int> tmp;
     72     for(int i = 0; i < CITIES; ++i)
     73         tmp.push_back(i);
     74 
     75     for(int i = 0; i < UNITS; ++i)
     76     {
     77         random_shuffle(tmp.begin(),tmp.end());
     78         group[i].path = tmp;
     79         group[i].length = lenOfUnit(group[i]);
     80     }
     81 }
     82 
     83 //for test
     84 void printlist(list<int> &path)
     85 {
     86     for(list<int>::iterator it = path.begin(); it != path.end(); ++it){
     87         cout << *it << " ";    
     88     }
     89     cout << endl;
     90 }
     91 
     92 void rightRotateList(list<int> &path, list<int>::iterator ptr, list<int>::iterator res)
     93 {
     94     path.insert(ptr, res, path.end());
     95     path.erase(res, path.end());
     96 }
     97 
     98 
     99 void GenticTSP::selectGroup()
    100 {
    101     int selected = UNITS * ps_;
    102     int non_selected = UNITS - selected; //防止取整丢失,所以直接采用减去selected
    103     for(int i = 0; i < UNITS; ++i)
    104     {
    105         group[i].length = lenOfUnit(group[i]);
    106     }
    107     sort(group, group + UNITS);
    108 
    109     /*群体规模保持不变
    110      *被淘汰的个体数目为non_selected
    111      *用被选择的前non_seleted个个体替代之
    112      */
    113     for(int i = 0; i < non_selected; ++i)
    114     {
    115         group[UNITS-1-i].path =  group[i].path;
    116         group[UNITS-1-i].length =  group[i].length;
    117 
    118        /* WARNING:
    119         * error:memcpy(&group[UNITS-1-i], &group[i], sizeof(unit)); 
    120         * 不能用memcpy
    121         * 因为unit是自定义的结构体
    122         * 其中的vector类型也不是原生的数据类型
    123         * 如若使用,需要自定义构造函数
    124         */
    125     }
    126 }
    127 
    128 
    129 void GenticTSP::evoluteGroup()
    130 {
    131     for(int i = 0; i < MAX_GEN; ++i)
    132     {
    133         selectGroup();
    134 
    135         for(int j = 0; j < UNITS-2;)
    136         {
    137             crossUnits(group[j], group[j+1], group[j+2]);
    138             j += 2;
    139         }
    140 
    141         mutateGroup();
    142     }
    143 
    144 }
    145 
    146 
    147 void GenticTSP::mutateGroup()
    148 {
    149     srand(time(NULL));
    150     int num;
    151     int pos1, pos2;
    152     int tmp;
    153     int sum = UNITS * pm_; //变异的总数= 群体数 * 变异概率
    154     while(sum--)
    155     {
    156         //随机选取一个发生变异的unit
    157         num = rand() % UNITS;
    158         //随机选取两个path上变异的城市,采用交换两个城市的方法进行变异
    159         pos1 = rand() % CITIES;
    160         pos2 = rand() % CITIES;
    161 
    162         //如果相同则不能算是变异,这里用while确保变异
    163         while(pos1 == pos2)
    164             pos2 = rand() % CITIES;
    165 
    166         tmp = group[num].path[pos1];
    167         group[num].path[pos1] = group[num].path[pos2];
    168         group[num].path[pos2] = tmp;
    169 
    170         //更新该变异后path的长度
    171         lenOfUnit(group[num]);      
    172     }
    173 }
    174 
    175 
    176 
    177 
    178 void GenticTSP::crossUnits(unit &u1, unit &u2, unit &u3)
    179 {
    180     //将路径存储在list类型的容器中便于后面的插入操作
    181     list<int> path1(u1.path.begin(), u1.path.end());
    182     list<int> path2(u2.path.begin(), u2.path.end());
    183     list<int> path3(u3.path.begin(), u3.path.end());
    184     //随机产生起始城市的id, 并将位置默认为第一个位置
    185     srand((int)time(0));
    186     int cityID = rand() % CITIES;
    187     list<int>::iterator res1, res2, res3;
    188     res1 = find(path1.begin(), path1.end(), cityID);
    189     res2 = find(path2.begin(), path2.end(), cityID);
    190     res3 = find(path3.begin(), path3.end(), cityID);
    191     if(res1 == path1.end() || res2 == path2.end() || res3 == path3.end())
    192     {
    193         cout << "city not found" << endl;
    194     }
    195     
    196     //将以随机选取的城市及其后面的城市右旋到序列首部
    197     rightRotateList(path1, path1.begin(), res1);
    198     rightRotateList(path2, path2.begin(), res2);
    199     rightRotateList(path3, path3.begin(), res3);
    200 
    201     list<int>::iterator ptr1, ptr2, ptr3;
    202     ptr1 = path1.begin();
    203     ptr2 = path2.begin();
    204     ptr3 = path3.begin();
    205 
    206     /*CITIES个城市
    207      *第一个城市在循环体外已经确定
    208      *循环体内只要再执行CITIES - 2 次即可
    209      *因为当执行倒数第二次后,最后一个也就是仅剩的一个也无需再循环了
    210      */
    211     for(int k = 1; k < CITIES-1; ++k)
    212     {
    213       //  cout << "______" << k<< "______" << endl;
    214         int h1 = *ptr1;
    215         double len1 = cityMatrix[h1][*(++ptr1)];
    216         int h2 = *ptr2;
    217         double len2 = cityMatrix[h2][*(++ptr2)];
    218         int h3 = *ptr3;
    219         double len3 = cityMatrix[h3][*(++ptr3)];
    220 
    221         //找出前两个城市距离最小的那个path中得第一个城市的编号
    222         double len = (len1 <= len2) ? len1 : len2;
    223         len = (len < len3) ? len : len3;
    224         if(len == len1){
    225             cityID = *ptr1;
    226         }else if(len == len2){
    227             cityID = *ptr2;
    228         }else{
    229             cityID = *ptr3;
    230         }
    231 
    232         //与当前城市距离最小的下一个城市编号为cityID
    233         res1 = find(ptr1, path1.end(), cityID);
    234         res2 = find(ptr2, path2.end(), cityID);
    235         res3 = find(ptr3, path3.end(), cityID);
    236         if(res1 == path1.end() || res2 == path2.end() || res3 == path3.end())
    237         {
    238             cout << "city not found in loop" << endl;
    239         }
    240         rightRotateList(path1, ptr1, res1);
    241         rightRotateList(path2, ptr2, res2);
    242         rightRotateList(path3, ptr3, res3);
    243 
    244         //指针失效,需要重新定位
    245         ptr1 = find(path1.begin(), path1.end(), cityID);
    246         ptr2 = find(path2.begin(), path2.end(), cityID);
    247         ptr3 = find(path3.begin(), path3.end(), cityID);
    248     }
    249 
    250 
    251     /*为了避免三个路径相同,所以这里在不改变路径的情况下替换起点城市,有利于接下来的交叉*/
    252     cityID = (rand() % CITIES);
    253     res2 = find(path2.begin(), path2.end(), cityID);
    254     rightRotateList(path2, path2.begin(), res2);
    255 
    256     cityID = (rand() % CITIES);
    257     res3 = find(path3.begin(), path3.end(), cityID);
    258     rightRotateList(path3, path3.begin(), res3);
    259 
    260     //交叉完之后再赋值给vector类型
    261     u1.path.assign(path1.begin(), path1.end());
    262     u2.path.assign(path2.begin(), path2.end());
    263     u3.path.assign(path3.begin(), path3.end());
    264 }
    265     
    266     
    267 //print top5 paths with length
    268 void GenticTSP::printBestUnit()
    269 {
    270     for(int i = 0; i < 5; ++i)
    271     {
    272         cout << "Path" << i << ": ";
    273         for(vector<int>::iterator it = group[i].path.begin(); it != group[i].path.end(); ++it){
    274             cout << *it << " ";    
    275         }
    276         cout << endl << "length: " << group[i].length << endl; 
    277     }
    278 }
    GenticTsp.cpp

    maintest.cpp

     1 #include <iostream>
     2 #include <string>
     3 #include <vector>
     4 #include <fstream>
     5 #include "GenticTSP.h"
     6 using namespace std;
     7 
     8 
     9 int main(int argc, const char *argv[])
    10 {
    11     GenticTSP genticTsp;
    12     genticTsp.initMatrix("city.txt");
    13     genticTsp.initGroup();
    14     genticTsp.evoluteGroup();
    15     genticTsp.selectGroup(); //进化后需要再一次选择,从而排序出最优序列
    16     genticTsp.printBestUnit ();
    17 
    18     return 0;
    19 }
    maintest.cpp

    city.txt

    1  6  4 
      2 15 15 
      3 24 18 
      4 33 12 
      5 48 12 
      6 57 14 
      7 67 10 
      8 77 10 
      9 86 15 
     10  6 21 
     11 17 26 
     12 23 25 
     13 32 35 
     14 43 23 
     15 55 35 
     16 65 36 
     17 78 39 
     18 87 35 
     19  3 53 
     20 12 44 
     21 28 53 
     22 33 49 
     23 47 46 
     24 55 52 
     25 64 50 
     26 71 57 
     27 87 57 
     28  4 72 
     29 15 78 
     30 22 70 
     31 34 71 
     32 42 79 
     33 54 77 
     34 66 79 
     35 78 67 
     36 87 73 
     37  7 81 
     38 17 95 
     39 26 98 
     40 32 97 
     41 43 88 
     42 57 89 
     43 64 85 
     44 78 83 
     45 83 98 
     46  5 109 
     47 13 111 
     48 25 102 
     49 38 119 
     50 46 107 
     51 58 110 
     52 67 110 
     53 74 113 
     54 88 110 
     55  2 124 
     56 17 134 
     57 23 129 
     58 36 131 
     59 42 137 
     60 53 123 
     61 63 135 
     62 72 134 
     63 87 129 
     64  2 146 
     65 16 147 
     66 25 153 
     67 38 155 
     68 42 158 
     69 57 154 
     70 66 151 
     71 73 151 
     72 86 149 
     73  5 177 
     74 13 162 
     75 25 169 
     76 35 177 
     77 46 172 
     78 54 166 
     79 65 174 
     80 73 161 
     81 86 162 
     82  2 195 
     83 14 196 
     84 28 189 
     85 38 187 
     86 46 195 
     87 57 194 
     88 63 188 
     89 77 193 
     90 85 194 
     91  8 211 
     92 12 217 
     93 22 210 
     94 34 216 
     95 47 203 
     96 58 213 
     97 66 206 
     98 78 210 
     99 85 204 
    city.txt

    用的city.txt这里的数据,据说最优路径代价是1100+,可是我用我的选择,变异,交叉方法目前来看,当规模为50时,最小代价只能达到1500+;当我把种群的规模调整到500时,代价能达到1300+;5000时,仍然是1300+。

  • 相关阅读:
    [Android学习笔记]Activity
    [Android学习笔记]Activity,View,Windows简介
    [Android]Eclipse的使用
    [Cocos2d-x]解决Android平台ndk-build时不自动删除外部库
    [Cocos2d-x]Android的android.mk文件通用版本
    [C++][STL]string记录
    nyoj-103-A+B Problem II
    nyoj-95-众数问题
    nyoj-187-快速查找素数
    nyoj-8-一种排序
  • 原文地址:https://www.cnblogs.com/beatrice7/p/4142568.html
Copyright © 2020-2023  润新知