数据来源:TSP数据
找到Symmetric traveling salesman problem (TSP),下载相关数据。
本文选取了att48数据集。
遗传算法
遗传算法的基本运算过程如下:
- 初始化:设置进化代数计数器t=0、设置最大进化代数T、交叉概率、变异概率、随机生成M个个体作为初始种群P
- 个体评价:计算种群P中各个个体的适应度
- 选择运算:将选择算子作用于群体。以个体适应度为基础,选择最优个体直接遗传到下一代或通过配对交叉产生新的个体再遗传到下一代
- 交叉运算:在交叉概率的控制下,对群体中的个体两两进行交叉
- 变异运算:在变异概率的控制下,对群体中的个体两两进行变异,即对某一个体的基因进行随机调整
- 经过选择、交叉、变异运算之后得到下一代群体P1。
重复以上1-6,直到遗传代数为T,以进化过程中所得到的具有最大适应度个体作为最优解输出,终止计算。
选择运算:轮盘赌法
选择算子采用轮盘赌选择,以每个个体的适应度为基础,为每个个体计算累积概率。
qa 表示个体a的累积概率,如上图所示个体1、2、3、4的累积概率分别为0.14、0.53、0.69、1。
随机生成一个0到1的浮点数f,若 qa < f <= qb,则个体b被选中。
交叉运算
①部分映射交叉
②顺序交叉
③基于位置的交叉
变异运算
变异算子随机进行多次,每次在个体基因序列中选择两个位置的基因进行交换。
完整代码如下:
#include <iostream>
#include <fstream>
#include <cmath>
#include <string>
#include <stdlib.h>
#include "time.h" //程序运行时间
#include <unistd.h> //哈希函数
#define CITY_NUM 48 //城市个数
#define POPSIZE 300 //种群个体数
#define MAXVALUE 0x7ffffffff //路径最大值上限
#define N 100000 //需要根据实际求得的路径值修正
#define MAX_GEN 50000 //最大进化代数
#define CROSS 0.5 //交叉算子
#define MUT 0.05 //变异算子
using namespace std;
unsigned seed=(unsigned)time(0); //每次产生不同的随机序列
double Hash[CITY_NUM+1];
typedef struct CityPosition{
double x;
double y;
}CityPosition; //城市位置
CityPosition CityPos[38]={};
double CityDistance[CITY_NUM][CITY_NUM]; //城市距离
typedef struct{
int colony[POPSIZE][CITY_NUM+1]; //城市种群,默认出发城市编号为0,则城市编号的最后一个城市还应该为0
double fitness[POPSIZE]; // 每个个体的适应度,即1/Distance[POPSIZE]
double Distance[POPSIZE]; //每个个体的总路径
int BestRooting[CITY_NUM+1]; //最优城市路径序列
double BestFitness; //最优路径适应值
double BestValue; //最优路径长度
int BestNum; //最优路径城市数目
}TSP,*PTSP;
//计算城市距离CityDistance[i][j]
double CalculatDist(CityPosition CityPos[]){
for(int i=0;i<CITY_NUM;i++){
for(int j=0;j<CITY_NUM;j++){ //最后一个城市还应该返回到出发节点
if(i!=j) CityDistance[i][j]=sqrt(pow(CityPos[j].x-CityPos[i].x,2)+pow(CityPos[j].y-CityPos[i].y,2));
else CityDistance[i][i]=0;
}
}
}
//数组复制
void copy(int a[],int b[]){
for(int i=0;i<CITY_NUM+1;i++)
{
a[i]=b[i];
}
}
//用来检查新生成的节点是否在当前群体中,0号节点是默认出发节点和终止节点
bool check(TSP &city,int pop,int num,int k){
for(int i=0;i<=num;i++){
if(k==city.colony[pop][i])
return true; //新生成节点存在于已经生成的路径中
}
return false; //新生成节点没有存在于已经生成的路径中
}
//种群初始化,即为city.colony[i][j]赋值
void InitColony(TSP &city){
int r;
for(int i=0;i<POPSIZE;i++){
city.colony[i][0]=0;
city.colony[i][CITY_NUM]=0;
city.BestValue=MAXVALUE;
city.BestFitness=0; //适应值越大越好
}
for(int i=0;i<POPSIZE;i++)
{
for(int j=1;j<CITY_NUM;j++)
{
r=rand()%(CITY_NUM-1)+1; //产生1~CITY_NUM-1之间的随机数
while(check(city,i,j,r)) //随机产生城市序号,即为city.colony[i][j]赋值
{
r=rand()%(CITY_NUM-1)+1;
}
city.colony[i][j]=r;
}
}
}
//计算适应度,同时选出最优的
void CalFitness(TSP &city){
int start,end;
int Best=0;
for(int i=0;i<POPSIZE;i++){ //求每个个体的总路径,适应度
city.Distance[i]=0;
for(int j=1;j<=CITY_NUM;j++){
start=city.colony[i][j-1];
end=city.colony[i][j];
city.Distance[i]=city.Distance[i]+CityDistance[start][end]; //city.Distance[i]每个个体的总路径
}
city.fitness[i]=N/city.Distance[i];
if(city.fitness[i]>city.fitness[Best]) //选出最大的适应度,即选出所有个体中的最短路径
Best=i;
}
copy(city.BestRooting,city.colony[Best]); //将最优个体拷贝给city.BestRooting
city.BestFitness=city.fitness[Best];
city.BestValue=city.Distance[Best];
city.BestNum=Best;
}
//适应度
double GetFittness(int a[CITY_NUM+1]){
int i,start,end;
double Distance=0;
for(i=0;i<CITY_NUM;i++){
start=a[i];
end=a[i+1];
Distance+=CityDistance[start][end];
}
return N/Distance;
}
//选择算子:轮盘赌法
void Select(TSP &city){
int TempColony[POPSIZE][CITY_NUM+1];
int i,j,t;
double s;
double GaiLv[POPSIZE];
double SelectP[POPSIZE+1];
double avg;
double sum=0;
for(i=0;i<POPSIZE;i++) sum+=city.fitness[i];
for(i=0;i<POPSIZE;i++) GaiLv[i]=city.fitness[i]/sum;
SelectP[0]=0;
for(i=0;i<POPSIZE;i++) SelectP[i+1]=SelectP[i]+GaiLv[i]*RAND_MAX;
memcpy(TempColony[0],city.colony[city.BestNum],sizeof(TempColony[0]));
for(t=1;t<POPSIZE;t++){
double ran = rand() % RAND_MAX + 1;
s= (double) ran / 100.0;
for(i=1;i<POPSIZE;i++) if(SelectP[i]>=s) break;
memcpy(TempColony[t],city.colony[i-1],sizeof(TempColony[t]));
}
for(i=0;i<POPSIZE;i++) memcpy(city.colony[i],TempColony[i],sizeof(TempColony[i]));
}
//交叉:头尾不变,中间打乱顺序交叉
void Cross(TSP &city,double pc)//交叉概率是pc
{
int i,j,t,l;
int a,b,ca,cb;
int Temp1[CITY_NUM+1],Temp2[CITY_NUM+1];
for(i=0;i<POPSIZE;i++){
double s=((double)(rand()%RAND_MAX))/RAND_MAX;
if(s<pc){
cb=rand()%POPSIZE;
ca=cb;
if(ca==city.BestNum||cb==city.BestNum) //如果遇到最优则直接进行下次循环
continue;
l=rand()%(CITY_NUM/2)+1; //1-一半的位置
a=rand()%(CITY_NUM-l)+1; //全部
memset(Hash,0,sizeof(Hash)); //将s中当前位置后面的n个字节 用ch替换并返回s 。
Temp1[0]=Temp1[CITY_NUM]=0;
for(j=1;j<=l;j++) //打乱顺序即随机,选出来的通过Hash标记为1
{
Temp1[j]=city.colony[cb][a+j-1]; //a+l=2~48 25~38
Hash[Temp1[j]]=1;
}
for(t=1;t<CITY_NUM;t++)
{
if(Hash[city.colony[ca][t]]==0)
{
Temp1[j++]=city.colony[ca][t];
Hash[city.colony[ca][t]]=1;
}
}
memcpy(city.colony[ca],Temp1,sizeof(Temp1));
}
}
}
//对换变异
void Mutation(TSP &city,double pm)//变异概率是pm
{
int i,m;
int Temp[CITY_NUM+1];
for(int k=0;k<POPSIZE;k++){
double s=((double)(rand()%RAND_MAX))/RAND_MAX; //随机产生概率0~1间
i=rand()%POPSIZE; //随机产生0~POPSIZE之间的数
if(s<pm&&i!=city.BestNum) //i!=city.BestNum,即保证最优的个体不变异
{
int a,b,t;
a=(rand()%(CITY_NUM-1))+1;
b=(rand()%(CITY_NUM-1))+1;
copy(Temp,city.colony[i]);
if(a>b) //保证让b>=a
{
t=a;
a=b;
b=t;
}
for(m=a;m<(a+b)/2;m++)
{
t=Temp[m];
Temp[m]=Temp[a+b-m];
Temp[a+b-m]=t;
}
if(GetFittness(Temp)<GetFittness(city.colony[i]))
{
a=(rand()%(CITY_NUM-1))+1;
b=(rand()%(CITY_NUM-1))+1;
memcpy(Temp,city.colony[i],sizeof(Temp));
if(a>b)
{
t=a;
a=b;
b=t;
}
for(m=a;m<(a+b)/2;m++)
{
t=Temp[m];
Temp[m]=Temp[a+b-m];
Temp[a+b-m]=t;
}
if(GetFittness(Temp)<GetFittness(city.colony[i]))
{
a=(rand()%(CITY_NUM-1))+1;
b=(rand()%(CITY_NUM-1))+1;
memcpy(Temp,city.colony[i],sizeof(Temp));
if(a>b)
{
t=a;
a=b;
b=t;
}
for(m=a;m<(a+b)/2;m++)
{
t=Temp[m];
Temp[m]=Temp[a+b-m];
Temp[a+b-m]=t;
}
}
}
memcpy(city.colony[i],Temp,sizeof(Temp));
}
}
}
void OutPut(TSP &city)
{
cout<<"最佳路径为:"<<endl;
for(int i=0;i<=CITY_NUM;i++){
cout<<city.BestRooting[i]+1;
if(i!=CITY_NUM) cout<<"→";
}
cout<<endl<<"最佳路径长度为:"<<city.BestValue<<endl;
}
int main(){
TSP city;
srand(seed);
//读取数据
int m=0,n=0;
double d=0;
double num[CITY_NUM]={0},lont[CITY_NUM]={0},lati[CITY_NUM]={0};
ifstream fin;
fin.open("att48.txt");
while (fin>>d){
if(m%3==0) num[n]=d;
else if(m%3==1) lont[n]=d;
else lati[n++]=d;
m++;
}
CityPosition CityPos[CITY_NUM];
for(int i=0;i<CITY_NUM;i++){
CityPos[i].x=lont[i];
CityPos[i].y=lati[i];
}
CalculatDist(CityPos); //计算城市距离
InitColony(city); //生成初始种群
CalFitness(city); //计算适应度,同时选出最优的
for(int i=0;i<MAX_GEN;i++){
Select(city); //选择:轮盘赌法
Cross(city,CROSS); //交叉
Mutation(city,MUT); //变异
CalFitness(city); //计算新的适应值
}
OutPut(city); //输出
return 0;
}
运行结果如下:
与网站提供的最优解比较,发现最佳路径是正确的,但最佳路径长度的结果有问题,还没有发现是哪里出了错误。
参考:
利用遗传算法求解TSP问题