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

来源:互联网 发布:apple mac mini 编辑:程序博客网 时间:2024/04/30 08:12

#include<cmath>#include<cstdlib>#include<algorithm>#include<fstream>#include<string>#include<iostream>#include<ctime>#include <limits>#include<cassert>#include<vector>using namespace std;class GA{private:struct City{              //城市(名、坐标)结构string name;          //名称(编号)int x, y;             //二维坐标City(){}};struct Edge{              //城市间距离int pos;              //下标int length;           //距离大小,表示第pos个城市和第pos+1个城市间的距离};string datafile;          //测试数据文件名string resultfile;        //测试结果文件名int cityn;                //城市数目int popsize;              //种群规模double pc;                //交叉概率double pm;                //变异概率int maxgen;               //最大进化代数int bestgen;              //最忧解最早出现代数int bestlength;           //最短路径长度(最优解)int *bestpath;            //最优路径City *city;               //记录城市信息的数组int **distance;           //cityn X cityn 距离矩阵,记录城市之间距离double *fitness;          //种群适应度,记录种群中各个体的适应度double *p;                //种群总个个体适应度累积概率int **father;             //父代int **child;              //子代int gen;                  //当前代数clock_t start, end;      //用于计算程序运行时间的参数public:GA(string df, int n, int p, double p1, double p2, int g,string rf){/**df     ----        测试数据文件*n      ----        城市数目*p      ----        种群规模*p1     ----        交叉概率*p2     ----        变异概率*g      ----        进化代数*rf     ----        测试结果文件*/datafile = df;cityn = n;popsize = p;pc = p1;pm = p2;maxgen = g;resultfile = rf;}~GA(){                            //析构函数,释放数组空间delete[]bestpath;delete[]city;for (int i = 0; i < cityn; ++i){delete[]distance;}delete[]distance;for (int i = 0; i < popsize; ++i){delete[]father;delete[]child;}delete[]father;delete[]child;}void initdata(){                      //初始化数据//读取测试数据fstream file(datafile, ios::in);if (file.bad()){cout << "Fail to open the txt file !\n";exit(-1);}city = new City[cityn];for (int i = 0; i < cityn; ++i){file >> city[i].name >> city[i].x >> city[i].y;}file.close();//初始化距离矩阵distance = new int*[cityn];for (int i = 0; i < cityn; ++i){distance[i] = new int[cityn];}for (int i = 0; i < cityn; i++){distance[i][i] = 0;for (int j = i + 1; j < cityn; ++j){int xd = city[i].x - city[j].x;int yd = city[i].y - city[j].y;double rij = sqrt((xd*xd + yd*yd) / 10.0);int tij = (int)round(rij + 0.5);if (tij < rij){distance[i][j] = distance[j][i] = tij + 1;}else{distance[i][j] = distance[j][i] = tij;}}}}void initpop(){                           //初始化种群father = new int*[popsize];child = new int*[popsize];for (int i = 0; i < popsize; ++i){father[i] = new int[cityn];child[i] = new int[cityn];}/**贪心算法思想优化初始种群*for(a==0...size-1){*      以城市a为起点*      在剩下的cityn-1个城市中找出距离a最近的城市b*      在剩下的cityn-2个城市中找出距离b最近的城市c*      ...*      以此类推直到所有城市都遍历完*      }*其中0<=a<size,所以共产生size个具有贪心性质的初始染色体*///assert(popsize >= cityn);                   //算法默认要求种群的规模大于城市数目int size = popsize*0.2;int best, minlength;int *vis = new int[cityn];                 //辅助数组,0表示该城市未访问,1表示已访问for (int i = 0; i < size; ++i){            //初始化cityn个具有贪婪性质的初始染色体memset(vis, 0, cityn*sizeof(vis));vis[i] = 1;father[i][0] = i;for (int j = 1; j < cityn; ++j){       //贪心算法找出余下cityn-1个城市的序列minlength = numeric_limits<int>::max();for (int k = 0; k < cityn; ++k){if (vis[k] == 0 && minlength > distance[i][k]){   //father[i][j-1]minlength = distance[i][k];best = k;}}father[i][j] = best;vis[best] = 1;}}delete[]vis;/*for (int i = 0; i < cityn; ++i){cout << countlength(father[i]) << endl;}*/vector<int> randomseq;                 //利用vector里的random_shuffle()函数随机生成剩余popsizefor (int i = 0; i < cityn; ++i){randomseq.push_back(i);}for (int i = size; i < popsize; ++i){random_shuffle(randomseq.begin(), randomseq.end());for (int j = 0; j < cityn; ++j){father[i][j] = randomseq[j];}}//初始化最佳长度、最优解出现代数、当前代数bestlength = numeric_limits<int>::max();bestgen = gen = 0;bestpath = new int[cityn];//计算初始种群的最短路径int length;for (int i = 0; i < popsize; ++i){length = countlength(father[i]);if (bestlength > length){bestlength = length;for (int j = 0; j < cityn; ++j){bestpath[j] = father[i][j];}}}}void evolution(){srand(unsigned(time(NULL)));start = clock();                          //算法开始initdata();initpop();while (gen++ < maxgen){                  //算法实现的主体部分(以进化代数为算法结束判断条件)countfitness();                      //计算适应度               select();                            //选择crossover();                         //交叉mutation();                          //变异copy();                              //子代替代父代成为新的父代并继续繁衍下去}end = clock();                           //算法结束showresult();}void countfitness(){                             //计算适应度和适应度累积概率(轮盘赌算法需要)int sum = 0;int *d = new int[popsize];for (int i = 0; i < popsize; ++i){d[i] = countlength(father[i]);sum += d[i];}fitness = new double[popsize];p = new double[popsize];fitness[0] = 1.0*d[0] / sum;p[0] = (1 - fitness[0]) / (cityn - 1);for (int i = 1; i < popsize; ++i){fitness[i] = 1.0*d[i] / sum;p[i] = p[i - 1] + (1 - fitness[i]) / (cityn - 1);}}void select(){    //轮盘赌选择算法剩下popsize-1个染色体int k;for (int i = 0; i < popsize; ++i){k = 0;double r = static_cast<double>(rand()) / RAND_MAX;while (r > p[k]){k++;}for (int j = 0; j < cityn; ++j){        //把父代第k个个体复制到子代child[i][j] = father[k][j];}}//最优保存策略,用父代中的最优个体代替子代中的最劣个体int best = 0;int minlength = countlength(father[0]);for (int i = 1; i < popsize; ++i){int length = countlength(father[i]);if (minlength < length){minlength = length;best = i;}}int worst = 0;int maxlength = countlength(child[0]);for (int i = 1; i < popsize; ++i){int length = countlength(child[i]);if (maxlength > length){maxlength = length;worst = i;}}for (int i = 0; i < cityn; ++i){child[worst][i] = father[best][i];}delete[]fitness;delete[]p;}void crossover(){                            //交叉double r;                                //随机数int n = 0;int *flag = new int[popsize];            //是否已进行过交叉的标记memset(flag, 0, sizeof(flag));while (n < popsize){r = static_cast<double>(rand()) / RAND_MAX;if (r < pm){/**交叉操作*(1)随机选取两个染色体n1和n2,同时随机产生两个交叉点left和right(left<right)*(2)将两个交叉点之间的基因段交换*(3)将互换的基因段以外的部分中与互换后基因段有冲突的元素用另一父代相同位置上的元素代替*/int n1, n2;int left, right;do{       //保证选取出来交叉的两个染色体不同且没进行过交叉操作n1 = rand() % popsize;n2 = rand() % popsize;} while (n1 == n2 || flag[n1] == 1 || flag[n2] == 1);flag[n1] = flag[n2] = 1;do{       //保证交叉点不同left = rand() % cityn;right = rand() % cityn;} while (left == right);if (left > right){                       //使得left<rightint temp = left;left = right;right = temp;}//保存两个交叉点之间的元素映射int *map1 = new int[cityn];int *map2 = new int[cityn];memset(map1, -1, sizeof(map1));memset(map2, -1, sizeof(map2));for (int i = left; i <= right; ++i){map1[child[n1][i]] = child[n2][i];map2[child[n2][i]] = child[n1][i];}//将两个交叉点之间的基因段交换for (int i = left; i <= right; ++i){    int temp = child[n1][i];child[n1][i] = child[n2][i];child[n2][i] = temp;}//处理交换后染色体两边基因的冲突int index;int c;for (int i = 0; i < left; ++i){          c = child[n1][i];index = left;while (index <= right){if (c == child[n1][index]){c = map2[child[n1][index]];index = left - 1;}index++;}child[n1][i] = c;}for (int i = 0; i < left; ++i){c = child[n2][i];index = left;while (index <= right){if (c == child[n2][index]){c = map1[child[n2][index]];index = left - 1;}index++;}child[n2][i] = c;}for (int i = right + 1; i < cityn; ++i){c = child[n1][i];index = left;while (index <= right){if (c == child[n1][index]){c = map2[child[n1][index]];index = left - 1;}index++;}child[n1][i] = c;}for (int i = right + 1; i < cityn; ++i){c = child[n2][i];index = left;while (index <= right){if (c == child[n2][index]){c = map1[child[n2][index]];index = left - 1;}index++;}child[n2][i] = c;}delete[]map1;delete[]map2;//计算交叉操作是否获得比当前最优解更好的解int length;length = countlength(child[n1]);if (bestlength > length){bestlength = length;bestgen = gen;for (int i = 0; i < cityn; ++i){bestpath[i] = child[n1][i];}}length = countlength(child[n2]);if (bestlength > length){bestlength = length;bestgen = gen;for (int i = 0; i < cityn; ++i){bestpath[i] = child[n2][i];}}}n += 2;}delete[]flag;}void mutation(){                             //变异double r;                                //随机数int n = 0; int mut;                                 //变异方式while (n < popsize){r = static_cast<double>(rand()) / RAND_MAX;if (r < pm){/**变异操作(共有两种变异方式)*(一)*(1)将城市序列转换成城市间距离的数组并按照距离从大到小进行排序*(2)选择最长距离和次长的两段城市间距离*(3)根据两段城市间距离在染色体中的相对位置进行消除*(二)*(1)随机选取染色体上的两个城市left和right*(2)以left为起点,将left与right之间的城市重新排列,排列方式如下:*          【i】假设left位置上为城市a,在余下的right-left-1个城市中找出距离a最近的城市,设为b*          【ii】接着在剩下的right-left-2个城市中找出距离b最近的城市,设为c*          【iii】以此类推,直到所有城市都已重排列*/mut = rand() % 2;          //0为第一种变异方式,1为第二种变异方式int left, right;if (mut == 0){Edge *edge = new Edge[cityn];for (int i = 0; i < cityn - 1; ++i){edge[i].pos = i;edge[i].length = distance[child[n][i]][child[n][i + 1]];}edge[cityn - 1].pos = cityn - 1;edge[cityn - 1].length = distance[child[n][0]][child[n][cityn - 1]];qsort(edge, cityn, sizeof(edge[0]), cmp);                      //按距离从大到小排序left = edge[0].pos;                                            //最长距离right = edge[1].pos;                                           //次长距离if (left > right){int temp = left;left = right;right = temp;}if (left == right - 1 || left == right - cityn + 1){           //最长的两个距离相邻int r, temp;r = rand() % cityn;temp = child[n][right];child[n][right] = child[n][r];child[n][r] = temp;}else{for (int i = left + 1; i <= (right + left) / 2; ++i){int temp = child[n][i];child[n][i] = child[n][right + left + 1 - i];child[n][right + left + 1 - i] = temp;}}}else{int m = (cityn / 4 - 2) * (gen / (maxgen / 4));left = rand() % (cityn - m);right = left + m;int best, minlength;int *vis = new int[cityn];                            //辅助数组,0表示该城市未访问,1表示已访问memset(vis, 0, cityn*sizeof(vis));//vis[father[n1][left]] = 1;for (int i = left + 1; i <= right; ++i){               //找出余下城市的序列minlength = numeric_limits<int>::max();for (int j = left + 1; j <= right; ++j){if (vis[child[n][j]] == 0 && minlength > distance[child[n][i - 1]][child[n][j]]){minlength = distance[child[n][i - 1]][child[n][j]];best = j;}}vis[child[n][best]] = 1;int temp = child[n][i];child[n][i] = child[n][best];child[n][best] = temp;}delete[]vis;}//计算变异操作是否获得比当前最优解更好的解int length = countlength(child[n]);if (bestlength > length){bestlength = length;bestgen = gen;for (int i = 0; i < cityn; ++i){bestpath[i] = child[n][i];}}}++n;}}void copy(){                            //子代复制为父代,继续繁衍double sum = 0;for (int i = 0; i < popsize; ++i){sum += countlength(child[i]);for (int j = 0; j < cityn; ++j){father[i][j] = child[i][j];}}//cout << sum/cityn <<" "<<bestlength<<" "<<bestgen<< endl;//system("pause");}void showresult(){                 //输出结果fstream result(resultfile, ios::out);if (result.bad()){cout << "Fail to open the txt file !\n";exit(-1);}result << "最短路径长度:" << bestlength << '\n';result << "最短路径出现代数:" << bestgen << '\n';result << "算法运行时间(ms):" << (end - start) << "\n";result << "最优路径:" << endl;for (int i = 0; i < cityn; ++i){result << "    ";result << bestpath[i] << '\t';if ((i + 1) % 12 == 0){result << '\n';}}result.close();cout << "最优解为:" << bestlength << ",已生成" << resultfile << "记录实验结果。\n";}int countlength(int *seq){                         //计算城市序列seq的路径长度int length = 0;for (int i = 0; i < cityn - 1; ++i){length += distance[seq[i]][seq[i + 1]];}length += distance[seq[0]][seq[cityn - 1]];return length;}static int cmp(const void *a,const void *b){ return ((Edge*)b)->length < ((Edge*)a)->length; }};int main(){/**实验测试样例*测试数据:att48.txt,其实际最优解为10628*城市数目:48*种群规模:100*交叉概率:0.5*变异概率:0.1*进化代数:1000*/(new GA("att48.txt", 48, 100, 0.5, 0.1, 1000,"result.txt"))->evolution();}


附件:att48.txt

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 苹果8视频不清晰怎么办 乳疮腐烂还臭怎么办 冰箱肉腐烂很臭怎么办 指环扣松了怎么办视频 奇迹mu端游杀人了怎么办 奇迹最强者号找不着了怎么办 v领地退不了押金怎么办 全民奇迹sf钻石变负数怎么办 电脑上的新建没有了怎么办 火车上行李箱砸人怎么办 违建拆除后怎么办房产证 外地车遇到限号怎么办 双号限行 违了怎么办 下高速当地限行怎么办 下高速发现限号怎么办 下高速正好限号怎么办 限号不让下高速怎么办 我的歌没有编曲怎么办 奇迹暖暖以前的账号怎么办 孕妇误吃桃胶了怎么办? 孕4个月吃了桃胶怎么办 刚怀孕吃了桃胶怎么办 额头被打了个包怎么办 裤子被84掉颜色怎么办 高中生晚上偷家里电脑上网怎么办 住高层睡不好觉怎么办 水瓶座如果恨我们了该怎么办 不锈钢保温瓶不保温了怎么办 壁纸颜色选深了怎么办 客厅壁纸太暗了怎么办 别人说你衣服丑怎么办 高楼热水器风大熄火怎么办 1楼独立下水2楼怎么办 宜家家具不会装怎么办 服务行业遇到低素质客户怎么办 服务类没有进项票怎么办 教师对学生缺乏耐心怎么办 买了竹料烂尾楼怎么办 刚毕业想换工作怎么办 客厅灯变不了光怎么办 塑料镀铝浸底漆咬底怎么办