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

一、实验原理

  1. 巡回旅行商问题
    1.1给定一组n个城市和俩俩之间的直达距离,寻找一条闭合的旅程,使得每个城市刚好经过一次且总的旅行距离最短。
    1.2 TSP问题也称为货郎担问题,是一个古老的问题。最早可以追溯到1759年Euler提出的骑士旅行的问题。1948年,由美国兰德公司推动,TSP成为近代组合优化领域的典型难题。
    1.3 TSP是一个具有广泛的应用背景和重要理论价值的组合优化问题。 近年来,有很多解决该问题的较为有效的算法不断被推出,例如Hopfield神经网络方法,模拟退火方法以及遗传算法方法等。
  2. 用遗传算法求解TSP问题
    TSP搜索空间随着城市数n的增加而增大,所有的旅程路线组合数为(n-1)!/2。在如此庞大的搜索空间中寻求最优解,对于常规方法和现有的计算工具而言,存在着诸多计算困难。借助遗传算法的搜索能力解决TSP问题,是很自然的想法。
    2.1基本遗传算法可定义为一个8元组:
    (SGA)=(C,E,P0,M,Φ,Г,Ψ,Τ)
    C ——个体的编码方法,SGA使用固定长度二进制符号串编码方法;
    E ——个体的适应度评价函数;
    P0——初始群体;
    M ——群体大小,一般取20—100;
    Ф——选择算子,SGA使用比例算子;
    Г——交叉算子,SGA使用单点交叉算子;
    Ψ——变异算子,SGA使用基本位变异算子;
    Т——算法终止条件,一般终止进化代数为100—500;
    2.2问题的表示
    对于一个实际的待优化问题,首先需要将其表示为适合于遗传算法操作的形式。用遗传算法解决TSP,一个旅程很自然的表示为n个城市的排列,但基于二进制编码的交叉和变异操作不能适用。
    路径表示是表示旅程对应的基因编码的最自然,最简单的表示方法。它在编码,解码,存储过程中相对容易理解和实现。例如:旅程(5-1-7-8-9-4-6-2-3)可以直接表示为(5 1 7 8 9 4 6 2 3)
    2.3产生初始种群
    一是完全随机产生,它适合于对问题的解无任何先验知识的情况。随机性较强,因而也较公正
    二是某些先验知识可转变为必须满足的一组要求,然后在满足这些要求的解中在随机地选取样本。这样选择初始种群可使遗传算法更快的达到最优解。种群有一定的目标性和代表性,但取例不如完全随机的广泛,而且先验知识是否可靠也是一个问题.
    2.4适应度函数
    遗传算法在进化搜索中基本不利用外部信息,仅以适应度函数为依据,利用种群中每个个体的适应度值来进行搜索。TSP的目标是路径总长度为最短,路径总长度的倒数就可以为TSP的适应度函数:
    2.5选择
    一般地说,选择将使适应度较大(优良)个体有较大的存在机会,而适应度较小(低劣)的个体继续存在的机会也较小。简单遗传算法采用赌轮选择机制,令Σfi表示群体的适应度值之总和,fi表示种群中第i个染色体的适应度值,它产生后代的能力正好为其适应度值所占份额fi/Σfi。
    2.6交叉
    基于路径表示的编码方法,要求一个个体的染色体编码中不允许有重复的基因码,也就是说要满足任意一个城市必须而且只能访问一次的约束。基本遗传算法的交叉操作生成的个体一般不能满足这一约束条件。
    部分匹配交叉操作要求随机选取两个交叉点,以便确定一个匹配段,根据两个父个体中两个交叉点之间的中间段给出的映射关系生成两个子个体。
    2.7变异
    遗传算法解决TSP 问题基于二进值编码的变异操作不能适用,不能够由简单的变量的翻转来实现
    在TSP问题中个体的编码是一批城市的序列,随机的在这个序列抽取两个城市,然后交换他们的位置。这样就实现了个体编码的变异,算法如下:
    产生两个0到1之间的随机实数;
    将这两个随机实数转化为0到n(城市数)-1之间的随机整数;
    将这两个随机整数指代的城市进行交换;

二、实验内容

1.N>=8。
2.随即生成N个城市间的连接矩阵。
3.指定起始城市。
4.给出每一代的最优路线和总路线长度。
5.以代数T作为结束条件,T>=50。

三、实验源码

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;

const int city_num = 8;   //城市数量
const int unit_num = 100; //群体规模
double ps = 0.01;        //变异概率
const int genmax = 50;   //最大迭代数
//城市间距离矩阵
int length_table[city_num][city_num] = {
     
    {
     0, 2, 3, 4, 5, 7, 3, 1},
    {
     2, 0, 2, 6, 3, 5, 4, 2},
    {
     3, 2, 0, 1, 9, 8, 7, 6},
    {
     4, 6, 1, 0, 5, 4, 3, 2},
    {
     5, 3, 9, 5, 0, 2, 1, 6},
    {
     7, 5, 8, 4, 2 ,0, 3, 6},
    {
     3, 4, 7, 3, 1, 3, 0, 5},
    {
     1, 2, 6, 2, 6, 6, 5, 0}
};

class Unit {
     
    public:
        int path[city_num+1]; //个体路径信息
        int length;         //个体代价
};
bool cmp(Unit u1, Unit u2) {
      return u1.length < u2.length; }
bool IsEqual(Unit u1, Unit u2) {
     
    for (int j = 0; j < city_num; j++)
        if (u1.path[j] != u2.path[j])
            return false;
    return true;
}
bool IsExist(vector<Unit> temp, Unit u) {
     
    for (int i = 0; i < temp.size(); i++)
        if(IsEqual(temp[i],u))
            return true;
    return false;
}
void PrintPath(Unit u) {
     
    for (int j = 0; j <= city_num; j++)
        printf("%d ", u.path[j]);
}
class Group {
     
    public:
        Group();
        Unit group[unit_num];
        Unit best;
        double alllength;
        int best_gen;
        void assess();//对每个个体进行评估
        void choose_copy();
        int sort_group();
        void cross(Unit &father, Unit &mother);//交叉
        void mutation(Unit &t);//突变  
        void print();//输出信息
        void work();//种群进化
    private:

};
Group::Group() {
     //随机产生N个染色体组成每个种群
    best.length = 0x3f3f3f3f;
    best_gen = 0;
    int start_city;
    cout << "Please input the start city, and it should be in a range of 0~" << city_num - 1<<" : ";
    cin >> start_city;
    for (int i = 0; i < unit_num; i++) {
     
        group[i].path[0]=start_city;
        bool flag[city_num] = {
     };
        flag[start_city]=true;
        for (int j = 1; j < city_num; j++) {
     
            int t_city = rand() % city_num;
            while (flag[t_city])
                t_city = rand() % city_num;
            flag[t_city] = true;
            group[i].path[j] = t_city;
        }
        group[i].path[city_num] = group[i].path[0];
    }
}
void Group::assess() {
     //计算每个个体的适应度的倒数
    alllength = 0;
    for (int k = 0; k < unit_num; k++) {
     
        group[k].length = 0;
        for (int i = 0; i < city_num; i++)
            group[k].length += length_table[group[k].path[i]][group[k].path[i+1]];
        alllength += 1.0/group[k].length;
    }
    //cout << "alllength: " << alllength << endl;
}
int Group::sort_group(){
     
    Unit *u = group;
    sort(u, u + unit_num, cmp); //根据评估结果排序
    return u->length;
}
void Group::choose_copy(){
     //选择-复制
    for (int i = 0; i < unit_num;i++){
     
        double m = 0;
        double p;
        double r = (double)rand()/RAND_MAX; //r为0至1的随机数
        for (int j = 0; j < unit_num; j++) {
     
            p = 1.0 / (group[j].length * alllength);
            m = m + p;
            if (r <= m){
     
                for (int t = 0; t <= city_num;t++)
                    group[i].path[t] = group[j].path[t];
                group[i].length = group[j].length;
                //printf("第%d个个体选择的是%d\n", i+1, j+1);
                break;
            }
        }
    }
}
void Group::cross(Unit &father, Unit &mother) __attribute__ ((__MINGW_ATTRIB_NO_OPTIMIZE)) {
     //交叉   
    bool flag1[city_num] = {
     };
    bool flag2[city_num] = {
     };
    int l = 1 + (rand() % (city_num - 1));
    int r = 1 + (rand() % (city_num - 1));
    if (l > r)
        swap(l, r);
    Unit son1, son2;
    for (int i = l; i <= r; i++){
     
        son1.path[i] = father.path[i];
        flag1[father.path[i]]=true;
        son2.path[i] = mother.path[i];
        flag2[mother.path[i]]=true;
    }
    for (int i = 0; i < l; i++) {
     //找到l~r中映射的城市,并在子种群中确定城市
        if(!flag1[mother.path[i]])
            son1.path[i] = mother.path[i];
        else {
     
            int temp = mother.path[i];
            int j;
            while (flag1[temp]) {
     
                for (j = l; j <= r; j++)
                    if (father.path[j] == temp)
                        break;
                temp=mother.path[j];
            }
            son1.path[i]=temp;
        }
        if(!flag2[father.path[i]])
            son2.path[i] = father.path[i];
        else {
     
            int temp = father.path[i];
            int j;
            while (flag2[temp]) {
     
                for (j = l; j <= r; j++)
                    if (mother.path[j] == temp)
                        break;
                temp=father.path[j];
            }
            son2.path[i]=temp;
        }
    }
    for (int i = r + 1; i <= city_num; i++){
     //找到l~r中映射的城市,并在子种群中确定城市
        if(!flag1[mother.path[i]])
            son1.path[i] = mother.path[i];
        else {
     
            int temp = mother.path[i];
            int j;
            while (flag1[temp]) {
     
                for (j = l; j <= r; j++)
                    if (father.path[j] == temp)
                        break;
                temp=mother.path[j];
            }
            son1.path[i]=temp;
        }
        if(!flag2[father.path[i]])
            son2.path[i] = father.path[i];
        else {
     
            int temp = father.path[i];
            int j;
            while (flag2[temp]) {
     
                for (j = l; j <= r; j++)
                    if (mother.path[j] == temp)
                        break;
                temp=father.path[j];
            }
            son2.path[i]=temp;
        }
    }
    for (int i = 0; i <= city_num;i++){
     
        mother.path[i] = son1.path[i];
        father.path[i] = son2.path[i];
    }
}
void Group::mutation(Unit &t) {
     //变异
    int one = 1 + (rand() % (city_num - 1));
    int two = 1 + (rand() % (city_num - 1));
    while (two == one)
        two = rand() % city_num;
    swap(t.path[one], t.path[two]); 
}
void Group::print(){
     
    for (int i = 0; i < unit_num; i++){
     
        printf("第%d个个体,路径信息为", i+1);
        PrintPath(group[i]);
        printf(";总权值:%d;\n", group[i].length);
    }
    printf("最优个体,路径信息为:\n");
    vector<Unit> temp;
    int length=sort_group();
    for (int i = 0; i < unit_num; i++) {
     
        if(group[i].length==length &&(!IsExist(temp, group[i]))){
     
            PrintPath(group[i]);
            cout << endl;
            temp.push_back(group[i]);
        }
    }
    printf("总权值:%d;\n", length);
    temp.clear();
}
void Group::work() {
     //遗传算法
    for (int i = 0; i < genmax; i++) {
     
        assess(); //评估
        if(i+1<=2||(i+1)%10==0){
     
            printf("第%d代:\n", i + 1);
            print();
        }
        if (best.length > group[0].length) {
     //
            memcpy(&best, &group[0], sizeof(group[0]));
            best_gen = i+1;
        }
        choose_copy();//选择复制
        for (int j = 0; j + 1 < unit_num; j += 2)//交叉
          cross(group[j], group[j + 1]);
        for (int j = 0; j < unit_num; j++){
     //变异
            int temp = rand() % 100;
            if(temp==j)
                mutation(group[j]);
        }
        
    }
}

Unit group[unit_num]; //种群变量
Unit bestone;         //记录最短路径
int generation_num;   //记录当前达到了第几代

int main() {
     
    srand((int)time(0));
    Group g;
    g.work();
    printf("求解结果路径信息为:");
    PrintPath(g.best);
    printf("; 总权值:%d; 第%d代;\n", g.best.length, g.best_gen);
    return 0;
}

四、实验结果

第一代

遗传算法解决TSP问题(C++实现)_第1张图片

第二代

遗传算法解决TSP问题(C++实现)_第2张图片

第50代

遗传算法解决TSP问题(C++实现)_第3张图片

你可能感兴趣的:(笔记)