遗传算法 解决TSP问题 C++实现(一)

  最近学习人工智能刚好学到了遗传算法,虽然我们的老师就让我们down一个就ok了,不过出于对于研究算法的爱好,就花了一天的功夫整了一下,感觉效率还可以,就记录一下coding的思路,和大家分享一下。

效果图

遗传算法介绍

  遗传算法(Genetic Algorithm,简称GA)是一种启发式搜索算法,它模拟的是达尔文生物进化论的自然选择和遗传学机理的生物进化过程的计算模型,通过不断的进化和遗传来搜索最优解。

简单流程如下:

  1. 随机初始化一定数量的种群(Population)个体(Individual),对个体进行基因(DNA)编码
  2. 计算种群中个体的适应度(fittness)
  3. 根据每一个个体的适应度,通过一定规则进行选择(selection)
  4. 对选中的个体按一定概率进行交叉操作(copulation)
  5. 对选中的个体按一定概率进行变异操作(heteromorphosis)
  6. 生成新的种群Population' ,满足迭代上限则返回,否则进入2

流程图如下:遗传算法流程图

解决TSP问题

旅行商问题,即TSP问题(Traveling Salesman Problem)是数学领域中著名问题之一。假设有一个旅行商人要拜访N个城市,他必须选择所要走的路径,路径的限制是每个城市只能拜访一次,而且最后要回到原来出发的城市。路径的选择目标是要求得的路径路程为所有路径之中的最小值。

数据结构

本篇先介绍一下基本的数据结构以及一些重要参数的定义。具体的各个算法及分析将在后续篇章跟上。

参数常量定义

#define  MAX_CITY_COUNT 20                          //最大城市数量
#define  MAX_GENRATION_COUNT 20                 //最大迭代次数
#define  MAX_INIT_POPULATION_COUT 10                //初始种群数量
#define  MAX_DNA_SIZE 20                            //DNA的长度(相当于城市数量)
#define  MAX_COPULATION_SIZE 3                      //交叉个体DNA时允许的最大交叉长度
#define  MAX_GENERATION_NUM_WHEN_BEST_NOT_CHANGE 100    //当最优个体没有变化时允许的最大迭代周期
#define  MIN_ACCEPT_FITTNESS                        //可以接受的最小适应度,当种群中的最优个体达到最小满足适应度后,停止进化 

城市的存储结构

struct City{
    int _posX; 
    int _posY; 
};

City cities[MAX_CITY_COUNT]={{60,200},{180,200},{80,180},{140,180},{20,160},{100,160},{200,160},{140,140},{40,120},{100,120},{180,100},{60,80},{120,80},{180,60},{20,40},
{100,40},{200,40},{20,20},{60,20},{160,20}};

为了方便计算各个基因的适应度,也就是TSP的路径长度,还要引入一个新的距离矩阵方便计算。

距离矩阵(distanceMatrix)

定义如下:

int**    _distanceOfCities; //存放所有城市的距离

初始化的时候只要计算每两两城市的距离即可。
p.s.注意相同城市距离为0

基因的存储结构
typedef vector<int>  DNA; //基因

基因就是一个int型向量,其长度固定为城市数量,且互不重复。

个体的数据结构
typedef struct {
    double _fittness; //个体的适应度
    DNA _DNA;         //个体的DNA序列
}Individual;

个体的适应度在TSP问题中为所有路径长度的倒数。所以适应度越大越好。

种群的数据结构
typedef vector<Individual> Population;

Genome类定义

Genome类是作者用来解决TSP问题的一个封装类,所有的selection,copulation,heteromorphosis等算法都封装在内,保证外界用户在调用遗传算法时候的透明。

下面来介绍一下Genome类中的成员函数以及成员变量。
p.s.在作者搞遗传算法的时候为了方便测试还把很多的测试函数也写在了里面,读者可以选择性忽略。为了保证思路的完整,在这里我还是都罗列了出来。


成员函数

class GenomeTSP{
public:
    GenomeTSP();
    ~GenomeTSP();


    /*
    *   选择算法
    */
    pair<Individual,Individual> selection();         
    /*
    * 变异算法 
    */
    void heteromorphosis(Individual &individual);

    /*
    *  交配算法
    */
    void copulation(Individual &individual1, Individual &individual2);     


    /*
    *消除DNA片段的冲突
    */
    void eliminateDNAConfilct(Individual &individual,int confilctDNASeg, int heteroPos); 


    /*
    * 随机生成变异区间
    * 第一个参数为区间下限,第二个参数为区间上限。 
    * 区间上下限距离不超过MAX_COPULATION_SIZE
    */
    pair<int,int> getRandomCopuSec();    

    /*
    *  将DNASegVec中每一个元素对应individual的DNA中的每一个元素的位置返回
    *  将按照DNASegVec中的顺序返回
    */
    DNA mapDNAToIndiPos(Individual &individual, DNA &DNASegVec);

    /*
     * 适应度函数(计算某一个个体的适应度,目前使用index代替引用)
     */
    double  fitness(Individual &indexOfIndividual); 


    /*------------------辅助函数------------------*/
    void initDisArr();                                                      //初始化距离矩阵
    void showDisArr();                                                      //显示距离矩阵
    void initPopulation();                                                  //初始化种群
    void showCurrPopulation();                                              //显示初始种群
    int  getCurrPopulationSize() const ;                                    //种群大小
    void setCurrPopulation(Population popu);                                //设置当前种群

    int  getCurrGeneration() const ;                                        //得到当前迭代数
    void setCurrGeneration(int generation);                                 //设置当前世代

    int  getBestGeneration() ;                                              //获取最优个体的连续周期
    void setBestGeneration(int bestGeneration);                             //设置最优个体的连续周期

    DNA  removeDepuInDNA(DNA firstDNA, DNA secDNA);//去除firstDNA中与secDNA相同的元素,并返回uniqueDNA
    bool isSameIndividual(Individual &individual1, Individual &individual2);//比较两个个体是否相同。 相同则返回true.
    void showIndividual(Individual &individual);                            //显示个体的信息
    Individual findBestIndividual();                                        //返回当前世代的最优个体

    Individual getCurrBestIndividual() const;                               //获得当前最优个体
    void       setCurrBestIndividual(Individual indi);                      //设置当前最优个体

    Individual getLastBestIndividual() const;                               //获得上一时代最优个体    
    void       setLastBestIndividual(Individual indi);                      //设置上一代的最有个体
    /*
    * 从srcDNAVec的区间[minPos,maxPos]复制元素到destDNAVec
    * 复制到destDNAVec时,复制到当前destDNAVec的末尾
    */
    void copyDNASeg(DNA srcDNAVec, int minPos, int maxPos, DNA &destDNAVec); 

    /*
    *   用src的区间元素[srcBegin,srcEnd]代替dest的区间元素[destBegin,destEnd]
    *   暂时没有考虑区间的合法性,调用时需要输入合法数据
    */
    void replaceDNASeg(DNA &replaceDNA, int srcBegin, int srcEnd, DNA &needToBeReplacedDNA, int destBegin, int destEnd);

    /*
    *   根据replacePosVec来替换,needToBeReplaceVec中的对应位置
    *   replaceVec 和 replacePosVec中元素个数相同,依次代表了替换元素的位置
    */
    void replaceDNASegByVec(DNA &replaceVec, DNA &needToBeReplaceVec, DNA replacePosVec);


    //------------------测试函数-----------------//
    void test_EliminateDNAConfilct();/*测试 消除DNA冲突函数*/
    void test_copyDNASeg();          /*测试 复制DNA片段函数*/
    void test_mapDNAPos();           /*测试 映射DNA位置函数*/
    void test_replaceDNASeg();       /*测试 代替DNA片段函数*/
    void test_replaceDNASegByVec();  /*s测试 根据vector替换DNA片段函数*/

成员变量

int**           _distanceOfCities;  //存放所有城市的距离
vector<Individual>    _populationVec;       //存放初始种群 
Individual            _currBestIndividual;  //当前种群的最优个体,当连续n代最优个体没有变化的时候,输出最优个体
Individual            _lastBestIndividual;   //上一时代的最优个体 
int                   _bestGeneration;      //最优个体的连续周期
int                   _generation;          //迭代数
float                 _totalFittness;       //总适应度

第一篇的文章就到这里了,我们介绍了遗传算法,以及TSP问题,以及解决TSP问题的数据结构,接下来我们要实打实的coding去实现每一个步骤来解决TSP问题了。下一篇马上出炉。

  • 5
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值