C++ 用遺傳算法解決TSP問題,旅行商問題

這也是人工智能實驗的一個題目

  • 這是一個很簡陋的遺傳算法版本,只有交叉(交配)
  • 因爲種羣個體只有2個,所以就拋棄了選擇複製
  • 變異暫無
#include<iostream>
#include<fstream>
using namespace std;
float city_dis[4][4];
class individual
{
public:
    int gene[5];    //個體基因,5個數字,0xxx0,從城市0開始出發,到0結束
    float fitness;  //適應度,取20.0/distance (這個20隨便取的,重點是distance的倒數)
    int distance;   //計算當前基因(路線)下的總距離
    individual(int gene0,int gene1,int gene2,int gene3,int gene4)
    {//初始化基因
        gene[0]=gene0;
        gene[1]=gene1;
        gene[2]=gene2;
        gene[3]=gene3;
        gene[4]=gene4;
        //基因傳遞後執行update函數(計算fitness和distance)
        update_info();
    }
    void set_new_gene(int *new_gene)
    {
        for(int i=1;i<4;++i)
        {//設置新基因只會改變中間3個基因,所以循環從1到3
            this->gene[i] = new_gene[i-1];//gene和傳遞進來到new_gene開始計數值不同
        }
        update_info();//更新個體fitness和distance
    }
    void update_info()
    {//更新fitness和distance的函數
        int new_dis = 0;
        for(int i=0;i<4;++i)
        {
            new_dis += city_dis[this->gene[i]][this->gene[i+1]];
        }
        this->distance  = new_dis;
        this->fitness = 20.0/this->distance;
    }
};
class tsp
{
public:
    int ROUTE_NUM;          //城市數量,用於循環
    int generation_counts;  //迭代次數,控制循環結束時間
    int best_route[5];      //存儲目前最好的個體(路線)
    int best_distance;      //最好個體的路線距離
    float best_fitness;     //其適應度
    individual *father1;    //初始個體1(路線1
    individual *father2;    //初始個體2(路線2
    tsp()
    {
        ROUTE_NUM = 5;      //5個城市
        generation_counts = 500;                //迭代次數500次
        memset(best_route,-1,5*sizeof(int));    
        best_distance = 0;                      
        best_fitness = 0;
        load_city_distance();                   //從文件加載城市距離
        father1 = new individual(0,2,1,3,0);    //初始化個體1
            //try rand father sequence //取消下面備註則會生成隨機的基因給初始個體
            // int rand1[3];
            // get_random_nums(rand1,3);
            // father1->set_new_gene(rand1);
        father2 = new individual(0,1,3,2,0);    //初始化個體2
            // int rand2[3];
            // get_random_nums(rand2,3);
            // father2->set_new_gene(rand2);
        
    }
    ~tsp()
    {//析構函數
        delete father1;
        delete father2;
    }
    void start_generate()
    {//迭代總函數
        srand(time(NULL));                      //取隨機數用
        while(--generation_counts >= 0)         //
        {
            //cout<<generation_counts<<endl;
            get_best_fornow();                  //獲取當前種羣最好個體
            switch_part_genes();                //交換兩個體的隨機2個基因,開始和結束的0基因不參加交換
        }
    }
    void get_best_fornow()
    {//判斷當前種羣是否比已有的best個體更優,有則替換
        individual *now_the_best;
        if(father1->fitness > father2->fitness)
        {//找到兩個個體最優,並用指針指向它
            now_the_best = father1;
        }
        else
        {
            now_the_best = father2;
        }
        if(now_the_best->fitness > this->best_fitness)
        {//better one will replace the original best individual
            for(int i=0;i<5;++i)
            {
                this->best_route[i] = now_the_best->gene[i];//替換基因
            }
            this->best_distance = now_the_best->distance;   //替換distance
            this->best_fitness = now_the_best->fitness;     //替換fitness
        }
    }
    void switch_part_genes()
    {//switch 2 random genes in individual
        int gene_place1[2];
        int gene_place2[2];
        get_random_nums(gene_place1,2);//生成1-3中2個不同隨機數給place1
        get_random_nums(gene_place2,2);//生成1-3中2個不同隨機數給place2
        
        //start to switch
        swap_gene(gene_place1,gene_place2);//交換基因
        father1->update_info();//更新個體信息
        father2->update_info();

    }
    void swap_gene(int loc1[2],int loc2[2])
    {   //loc1 is for father1;loc 2 for father 2
        //用loc1中的2個father1基因位置來交換loc2中2個father2基因的位置
        // cout<<loc1[0]<<" "<<loc1[1]<<endl;
        // cout<<loc2[0]<<" "<<loc2[1]<<endl;
        for(int i=0;i<2;++i)
        {   //father1[i] <-> father2[i] 
            int temp_father1_gene = father1->gene[loc1[i]];
            father1->gene[loc1[i]] = father2->gene[loc2[i]];
            father2->gene[loc2[i]] = temp_father1_gene;
        }
        clear_conflict(father1);//交換後可能出現衝突 比如01220這種序列,需要處理衝突
        clear_conflict(father2);
    }
    void clear_conflict(individual *ptr)
    {
        int mapping[4]={0,2,3,1};//mapping映射 0->0 1->2 2->3 3->1 //01220就會映射爲01320
        int has_Conf_loc = has_conflict(ptr);
        while(has_Conf_loc)
        {
            switch(has_Conf_loc)
            {//根據衝突位置用映射消除衝突
                case 1:
                    ptr->gene[1] = mapping[ptr->gene[2]];
                    break;
                case 2:
                    ptr->gene[1] = mapping[ptr->gene[3]];
                    break;
                case 3:
                    ptr->gene[3] = mapping[ptr->gene[3]];
                    break;
            }
            has_Conf_loc = has_conflict(ptr);
        }
        return;
    }
    int has_conflict(individual *one)
    {   //可能衝突位置只有123,0和4固定爲城市0,只要1和2、1和3、2和3都不衝突,即基因不衝突
        //不同返回值用於快速定位衝突位置
        if(one->gene[1] == one->gene[2])
            return 1;
        if(one->gene[1] == one->gene[3])
            return 2;
        if(one->gene[2] == one->gene[3])
            return 3;
        return 0;
    }
    bool get_random_nums(int *nums,int size)
    {//給nums數組生成size個不同隨機數
        if(size <= 0)
        {
            return false;
        }
        if(size == 1)
        {
            nums[0] = rand()%3+1;//range 1-3
            return true;
        }
        //size >= 2;
        for(int i=0;i<size;++i)
        {//i is the target place
            nums[i] = rand()%3+1;
            for(int j=0;j<i;++j)
            {//j seeking the same number
                if(nums[i] == nums[j])
                {   
                    nums[i] = rand()%3+1;
                    j=-1;
                }
            }
        }
        return true;
    }
    void print_best_route()
    {
        cout<<"\nBEST ROUTE:";
        for(int i=0;i<ROUTE_NUM;++i)
        {
            cout<<best_route[i]<<" ";
        }
        cout<<endl;
        cout<<"DISTANCE:"<<this->best_distance<<endl;
        cout<<"FITNESS:"<<this->best_fitness<<endl;
    }
    void load_city_distance()
    {//從文件讀取城市距離信息
        char in_data[10];
        ifstream in_stream;
        in_stream.open("ds.txt",ios::in);
        if(!in_stream.is_open())
            return;
        int j=0;
        int k=0;
        while(!in_stream.eof())
        {
            in_stream.getline(in_data,10);

            for(int i=0;i<10;++i)
            {
                if(in_data[i]>= '0' && in_data[i] <= '9')
                {
                    city_dis[j][k++] = in_data[i]-'0';
                    if(k==4)
                    {
                        k=0;
                        ++j;
                        break;
                    }
                }
            }
        }
        in_stream.close();
    }
};
int main()
{
    tsp tsp_demo;
    tsp_demo.start_generate();
    tsp_demo.print_best_route();
    return 0;
}

附測試截圖
在這裏插入圖片描述
ds.txt 文件內容
0 1 3 4
1 0 2 5
3 2 0 3
4 5 3 0

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章