遗传算法 旅行商问题为例

#include <iostream>
#include <ctime>
#include <math.h>
#include <algorithm>
#include <assert.h>
#include "City.h"

using namespace std;


vector<City>  vecCityList;  //城市信息清单

const int UNIT_NUMBER = 5*2; //确保数目为双数 方便计算遗传交叉

#define HOLD_UNIT_NUMBER 1*2

void InitListCity(){
    vecCityList.push_back(City("1",1.0,2.0));
    vecCityList.push_back(City("2",3.0,2.0));
    vecCityList.push_back(City("3",5.0,2.0));
    vecCityList.push_back(City("4",1.0,3.0));
    vecCityList.push_back(City("5",2.0,3.0));
    vecCityList.push_back(City("6",4.0,3.0));
    vecCityList.push_back(City("7",5.0,3.0));
    vecCityList.push_back(City("8",1.0,4.0));
    vecCityList.push_back(City("9",3.0,4.0));
    vecCityList.push_back(City("0",5.0,4.0));
}

CITY_PATH CreateRandomCitypath(){
    vector<int> vecCityIndex;
    for(int i = 0;i < vecCityList.size();++i){
        vecCityIndex.push_back(i);
    }
    CITY_PATH cityPath;
    for(int i = 0;i < vecCityList.size();++i){
        int index = rand()%(vecCityIndex.size());
        cityPath.push_back(vecCityIndex[index]);
        vector<int>::iterator it = vecCityIndex.begin()+index;
        vecCityIndex.erase(it);
    }

    return cityPath;
}

inline double RandFloat()           {return (rand())/(RAND_MAX+1.0);}

void CalMethodPrice(vector<CityWalkMethod>& vecCityWalkMethod){
    double maxMehtodDistance = 0.0;
    for(int j = 0 ; j < vecCityWalkMethod.size();++j){
        vecCityWalkMethod[j].distance_ = 0.0;
        CITY_PATH& cwkm = vecCityWalkMethod[j].cityPath_;
        for(int i = 0;i < cwkm.size()-1;++i){
            int cityA = cwkm[i];
            int cityB = cwkm[i+1];

            double distanceXPow = pow(vecCityList[cityA].x_ - vecCityList[cityB].x_,2);
            double distanceYPow = pow(vecCityList[cityA].y_ - vecCityList[cityB].y_,2);
            vecCityWalkMethod[j].distance_ +=  sqrt((distanceXPow + distanceYPow));
        }
        if(maxMehtodDistance <  vecCityWalkMethod[j].distance_ ){
            maxMehtodDistance =  vecCityWalkMethod[j].distance_;
        }
    }

    for(int i = 0 ; i < vecCityWalkMethod.size();++i){
        vecCityWalkMethod[i].pathPrice_ =
              ( maxMehtodDistance - vecCityWalkMethod[i].distance_)*10;

    }
}

CityWalkMethod SelectMethodUnit(vector<CityWalkMethod>& vecCityWalkMethod){
    double totalPrice = 0.0;
    for(int i = 0 ;i < vecCityWalkMethod.size();++i){
        totalPrice += vecCityWalkMethod[i].pathPrice_;
    }
    double slice = RandFloat()*totalPrice;
    totalPrice = 0.0;


    int index = 0;
    for(int i = 0;i < vecCityWalkMethod.size();++i){
        totalPrice += vecCityWalkMethod[i].pathPrice_;
        if(totalPrice > slice){
            index = i;
            break;
        }
    }

    return vecCityWalkMethod[index];
}


void CrossOver(const CityWalkMethod& methodA,
               const CityWalkMethod& methodB,
               CityWalkMethod& baby1,
               CityWalkMethod& baby2){
    int beg = rand()% (methodA.cityPath_.size());
    int end = rand()% (methodA.cityPath_.size());
    while(beg == end){
        end =  rand()% (methodA.cityPath_.size());
    }
    if(end < beg){
        swap(end,beg);
    }

    baby1 = methodA;
    baby2 = methodB;
    for(int pos = beg;pos<=end;++pos){
        int indexA = methodA.cityPath_[pos];
        int indexB = methodB.cityPath_[pos];

        if(indexA != indexB){
           vector<int>::iterator it1 = find(baby1.cityPath_.begin(),baby1.cityPath_.end(),indexA);
           vector<int>::iterator it2 = find(baby1.cityPath_.begin(),baby1.cityPath_.end(),indexB);
           swap(*it1,*it2);

           it1 = find(baby2.cityPath_.begin(),baby2.cityPath_.end(),indexA);
           it2 = find(baby2.cityPath_.begin(),baby2.cityPath_.end(),indexB);
           swap(*it1,*it2);
        }
    }

}


void DebugPrintVecCitymethod(const vector<CityWalkMethod>& vecCityWalkMethod){
    //=======================
    cout << endl;
    for(int i = 0 ; i < vecCityWalkMethod.size();++i){
        for(int j =0 ;j < vecCityWalkMethod[i].cityPath_.size();++j){
            cout << vecCityWalkMethod[i].cityPath_[j] << " ";
        }
        cout << "distance = " << vecCityWalkMethod[i].distance_ << "	";
        cout << "price = " << vecCityWalkMethod[i].pathPrice_ << endl;
    }
    //===========================
}

bool CompareMethodPrice(CityWalkMethod a,CityWalkMethod b){
    return a.pathPrice_ > b.pathPrice_;
}


void Mutation( CityWalkMethod&  cityWalkMethod){
    if( (rand()%100) > 75){
        swap(cityWalkMethod.cityPath_[0],cityWalkMethod.cityPath_[1]);
    }
}

void SelectAndCrossOver(vector<CityWalkMethod>& vecCityWalkMethod){
    vector<CityWalkMethod> tmp;
    assert(vecCityWalkMethod.size() != 0);
    assert(vecCityWalkMethod.size() == UNIT_NUMBER);
    sort(vecCityWalkMethod.begin(),vecCityWalkMethod.end(),CompareMethodPrice);

    for(int i =0;i < HOLD_UNIT_NUMBER;i++){
        //上一代最优走法 优先保留到下一代
       tmp.push_back(vecCityWalkMethod[i]);
    }


    for(int i = 0 ; i < (UNIT_NUMBER-HOLD_UNIT_NUMBER)/2;i++){
        //选择两种方法进行交叉
        CityWalkMethod methodA = SelectMethodUnit(vecCityWalkMethod);
        CityWalkMethod methodB = SelectMethodUnit(vecCityWalkMethod);
//        while(methodA == methodB){
//             methodB = SelectMethodUnit(vecCityWalkMethod);
//        }
        CityWalkMethod baby1,baby2;
        CrossOver(methodA,methodB,baby1,baby2);
        Mutation(baby1);
        Mutation(baby2);
        tmp.push_back(baby1);
        tmp.push_back(baby2);
    }
    vecCityWalkMethod = tmp;
    assert(vecCityWalkMethod.size() == UNIT_NUMBER);
    CalMethodPrice(vecCityWalkMethod);
}


int main(int argc, char *argv[])
{

        srand( (unsigned)time( NULL ) );
        InitListCity();

        //初始化第一代城市走法
        vector<CityWalkMethod> vecCityWalkMethod;
        for(int i =0;i < UNIT_NUMBER;++i){
            CityWalkMethod citywalkmethod;
            CITY_PATH cp = CreateRandomCitypath();
            citywalkmethod.cityPath_ = cp;
            vecCityWalkMethod.push_back(citywalkmethod);
        }
        CalMethodPrice(vecCityWalkMethod);

        int loop = 0;
        while(loop < 1000)
        {
            SelectAndCrossOver(vecCityWalkMethod);
            DebugPrintVecCitymethod(vecCityWalkMethod);
            loop++;
        }

    cout << "Hello World!" << endl;
    return 0;
}
 1 #ifndef CITY_H
 2 #define CITY_H
 3 
 4 #include <vector>
 5 #include <string>
 6 
 7 using namespace std;
 8 
 9 
10 
11 struct City{
12     float x_;
13     float y_;
14     string cityName_;
15     City(){}
16     City(string name,float x,float y):
17         x_(x),y_(y),cityName_(name)  {}
18 };
19 
20 #define CITY_PATH vector<int>
21 
22 struct CityWalkMethod{
23     CITY_PATH cityPath_;
24     double distance_;
25     double pathPrice_;
26     CityWalkMethod():distance_(0),pathPrice_(0){}
27     bool operator ==(const CityWalkMethod& r){
28         return cityPath_ == r.cityPath_;
29     }
30 };
31 
32 #endif // CITY_H

代码中 我们使用

void InitListCity(){
    vecCityList.push_back(City("1",1.0,2.0));
    vecCityList.push_back(City("2",3.0,2.0));
    vecCityList.push_back(City("3",5.0,2.0));
    vecCityList.push_back(City("4",1.0,3.0));
    vecCityList.push_back(City("5",2.0,3.0));
    vecCityList.push_back(City("6",4.0,3.0));
    vecCityList.push_back(City("7",5.0,3.0));
    vecCityList.push_back(City("8",1.0,4.0));
    vecCityList.push_back(City("9",3.0,4.0));
    vecCityList.push_back(City("0",5.0,4.0));
}

 来将城市信息输入

城市信息结构为

float x y 城市坐标

string name 城市名称

而城市的走法 我们使用的是是整数数组  记录依次走过城市的走法

比如 城市走法数组为 2 1 0 3 则代表我们走过城市的走法为

先走过vecCityList中第三个城市(vecCityList[2])

再走到vecCityList中第二个城市(vecCityList[1])

再走到vecCityList中第一个城市(vecCityList[0])

再走到vecCityList中第四个城市(vecCityList[3])

每种城市走法的评分标准是 将每种走法中最长的距离得出 依次将走法的距离同最长距离相减 就是该走法的评价得分

每种城市走法的距离计算方式是  第一个城市X Y 同第二个城市的 X Y的平方差开根号  (distance)² = (x₁-x₂)² -  (y₁-y₂)²

各个城市之间的距离综合就是城市走法的距离

有了走法 有了评分标准 然后就是遗传算法的三板斧

选择 交叉 变异

选择办法为 首先选择得分最高的几条 直接放入下一代中保证优良基因的延续

代码

// 按照走法评分排序 选择得分最高 直接放入下一代中保证优良基因的延续
sort(vecCityWalkMethod.begin(),vecCityWalkMethod.end(),CompareMethodPrice); for(int i =0;i < HOLD_UNIT_NUMBER;i++){ //上一代最优走法 优先保留到下一代 tmp.push_back(vecCityWalkMethod[i]); }
然后在总分区间总 生成一个随机数 随机数在那种方法的区间里 就选择它
CityWalkMethod SelectMethodUnit(vector<CityWalkMethod>& vecCityWalkMethod){
    double totalPrice = 0.0;
    for(int i = 0 ;i < vecCityWalkMethod.size();++i){
        totalPrice += vecCityWalkMethod[i].pathPrice_;
    }
    double slice = RandFloat()*totalPrice;
    totalPrice = 0.0;


    int index = 0;
    for(int i = 0;i < vecCityWalkMethod.size();++i){
        totalPrice += vecCityWalkMethod[i].pathPrice_;
        if(totalPrice > slice){
            index = i;
            break;
        }
    }

    return vecCityWalkMethod[index];
}

 

交叉

代码



void CrossOver(const CityWalkMethod& methodA,
               const CityWalkMethod& methodB,
               CityWalkMethod& baby1,
               CityWalkMethod& baby2){
    int beg = rand()% (methodA.cityPath_.size());
    int end = rand()% (methodA.cityPath_.size());
    while(beg == end){
        end =  rand()% (methodA.cityPath_.size());
    }
    if(end < beg){
        swap(end,beg);
    }

    baby1 = methodA;
    baby2 = methodB;
    for(int pos = beg;pos<=end;++pos){
        int indexA = methodA.cityPath_[pos];
        int indexB = methodB.cityPath_[pos];

        if(indexA != indexB){
           vector<int>::iterator it1 = find(baby1.cityPath_.begin(),baby1.cityPath_.end(),indexA);
           vector<int>::iterator it2 = find(baby1.cityPath_.begin(),baby1.cityPath_.end(),indexB);
           swap(*it1,*it2);

           it1 = find(baby2.cityPath_.begin(),baby2.cityPath_.end(),indexA);
           it2 = find(baby2.cityPath_.begin(),baby2.cityPath_.end(),indexB);
           swap(*it1,*it2);
        }
    }

}

 

变异

变异则简单很多 随机的变异概率  随机的选择行走方法中的索引 然后交换

比如行走方式 21304  

随机一个数字 如果大于变异概率  比如 随机范围100 大于90则变异  则进行变异

在行走方式中 随机选择两个数字 比如选择了 1 和0

那么行走方式 21304 变异为 20314

反复多次 得到极其接近最优解的答案

以 代码为例

第一次随机生成的 行走路线

距离在27到19 之间

经过几代遗传后 结果如图

路线最短距离已经变成17了

 经过100次遗传后 路线最短距离已经变成15了

 
作 者: itdef
欢迎转帖 请保持文本完整并注明出处
技术博客 http://www.cnblogs.com/itdef/
B站算法视频题解
https://space.bilibili.com/18508846
qq 151435887
gitee https://gitee.com/def/
欢迎c c++ 算法爱好者 windows驱动爱好者 服务器程序员沟通交流
如果觉得不错,欢迎点赞,你的鼓励就是我的动力
阿里打赏 微信打赏
原文地址:https://www.cnblogs.com/itdef/p/6831811.html