一、 实验题目
有 n 个城市,用 1,2,…,n 表示,城 i,j 之间的距离为 dij,有一个货郎从城 1 出发到其他城市一次且仅一次,最后回到城市 1,怎样选择行走路线使总路程最短?
二、 题目分析
在开始这个实验题目之前,我查找过很多这方面的资料,货郎担问题(TSP 问题)是一个组合优化问题。该问题可以被证明具有 NPC 计算复杂性。因此,单纯的从以往通过数据结构的方法解决这个问题是不现实的,因为货郎担问题具有(n!)/2 的时间复杂度,如果使用穷举法在面临点阵数目庞大的情况是无法在短时间内求出解的,一次需要考虑用启发式搜
索或者是其他方法求解。通过网上人的总结,解决货郎担问题基本有如下一些方法:
分支定界法
改良圈算法
“便宜”算法
贪心算法
遗传算法
群蚁算法
等等,其中有一些精确解法,可以求出精确的值,不过当数据集过于庞大的时候就会出现运算时间太长的问题。也有一些模糊求解的方法,比如我在求解这道题目时使用的遗传算法和群蚁算法,通过大量随机数来逐渐逼近极小值,这种通过近似计算逐渐逼近最终结果的方法虽然说不能精确地求得最优解,但是在面临数据集庞大的情况下,所求的的解相比于精确求解方法要好的多。因此,我对 TSP 问题的主要研究集中在遗传算法上。
三、 算法分析
我所使用的遗传算法是基于贪心算法的一种升级版本,它基于贪心算法和随机算法并且结合自然中的自然选择方法,在求解这一问题具有比较明显的优势。遗传算法(Genetic Algorithm)是模拟达尔文生物进化论的自然选择和遗传学机理的生物进化过程的计算模型,是一种通过模拟自然进化过程搜索最优解的方法。遗传算法的基本运算过程如下:
a)初始化:设置进化代数计数器 t=0,设置最大进化代数 T,随机生成 M 个个体作为初
始群体 P(0)。
b)个体评价:计算群体 P(t)中各个个体的适应度。
c)选择运算:将选择算子作用于群体。选择的目的是把优化的个体直接遗传到下一代或通
过配对交叉产生新的个体再遗传到下一代。选择操作是建立在群体中个体的适应度评估基础
上的。
d)交叉运算:将交叉算子作用于群体。所谓交叉是指把两个父代个体的部分结构加以替
换重组而生成新个体的操作。遗传算法中起核心作用的就是交叉算子。
e)变异运算:将变异算子作用于群体。即是对群体中的个体串的某些基因座上的基因值
作变动。
群体 P(t)经过选择、交叉、变异运算之后得到下一代群体 P(t 1)。
f)终止条件判断:若 t=T,则以进化过程中所得到的具有最大适应度个体作为最优解输出,
终止计算。
通过以上对使用遗传算法求解 TSP 问题的步骤,我们可以简要归纳出程序中会使用到的一些函数。
首先,需要生成随机种群,通过 InitGroup()函数产生预设种群数量的种群,它们的值都是随机产生的。然后使用求解当前种群的适应度函数 Cal_PathLen_FIndex()来对各个种群进行评价。
接下来为了后续实现方便,我做了一个排序函数,将种群根据适应度的高低从高到低进行排序,函数为 SortGroup()。
然后就是我们遗传算法的核心部分(进化函数 Evolution),我分为三个部分:进化,繁殖和变异。进化函数是模仿自然界中的进化,将比较优秀的种群(线路)保留下来并增加其数量,我使用了 CopyGroup(struct Group &G1,struct Group &G2)函数,对种群中一定数目的种群进行替换;关于繁殖一部分也是程序中最复杂的地方,也就是我们通常说的交叉繁殖。
最后附上源码,欢迎指正
/*通过遗传算法解决TSP问题实现近似求解*/
#include <windows.h>
#include <iostream>
#include <iomanip>
#include <fstream>
#include <math.h>
#include <time.h>
#include <stdlib.h>
#include <stdio.h>
using namespace std;
#define CITY 12 //城市数量
#define MAX 100 //进化次数
#define GROUPSIZE 200 //种群数量
#define CGINDEX 0.1 //种群变异概率
#define BNWINDEX 0.9 //种群交配概率
#define EVINDEX 0.6 //进化的概率
ofstream out;
int city_Map[CITY][CITY]; //保存城市地图
int start_Citycode = 0; //定义出发城市
int bestFitGroupID = 0; //保存目前最佳种群
int counter = 0; //进化代数
int tmp_Index1 = GROUPSIZE*BNWINDEX/2;
int tmp_Index2 = GROUPSIZE*(1-BNWINDEX);
int tmp_Index3 = GROUPSIZE*(1-BNWINDEX/2);
int tmp_Index4 = GROUPSIZE*(1-EVINDEX);
int tmp_Index5 = GROUPSIZE*EVINDEX;
struct Group //定义种群
{
int group[CITY]; //保存种群基因
int path_Len; //保存种群路径和
float fit_Index; //种群适应度
} curGroup[GROUPSIZE],bestFitGroup;
void InitMapByHand();
void InitMapTest();
void InitGroup();
void PrintMap();
void PrintAllInfo();
void Cal_PathLen_FIndex();
void Evolution(); //进化
void BirthNewGroup(struct Group& G1,struct Group& G2); //交叉(交配)
void ChangeGroup(); //突变
void PrintResult();
void SortGroup();
void CopyGroup(struct Group &G1,struct Group &G2);
bool CheckAlreadyExist(struct Group* G1 ,int p_Ck);
void gotoxy(int x,int y);
int main()
{
out.open("resultTSP.txt",ios::out|ios::trunc);
InitMapTest();
PrintMap();
InitGroup();
Cal_PathLen_FIndex();
SortGroup();
out<<"["<<counter<<"]";
cout<<"["<<counter<<"]";
//PrintAllInfo();
gotoxy(100,15);
cout<<"CUR BEST SOLUTION:"<<curGroup[0].path_Len<<endl;
while(counter++ < MAX)
{
out<<endl<<endl<<"["<<counter<<"]";
cout<<endl<<endl<<"["<<counter<<"]";
Evolution();
Cal_PathLen_FIndex();
SortGroup();
//PrintAllInfo();
gotoxy(100,15);
cout<<"CUR BEST SOLUTION:"<<curGroup[0].path_Len<<endl;
}
system("cls");
PrintResult();
out.close();
return 0;
}
void gotoxy(int x,int y)//X表示横坐标,Y表示纵坐标。
{
HANDLE app;
COORD pos;
pos.X=x;
pos.Y=y;
app=GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleCursorPosition(app,pos);
}
void InitMapByHand()
{
int disTmp = -1;
for(int i = 0; i < CITY; i++)
for(int j = i+1 ; j < CITY; j++)
{
cout<<"请输入城市"<<i<<"和"<<j<<"之间的距离: ";
cin>>disTmp;
city_Map[i][j] = abs(disTmp);
city_Map[j][i] = abs(disTmp);
}
for(int i = 0; i < CITY; i++)
city_Map[i][i] = 0;
}
void InitMapTest()
{
for(int i = 0; i < CITY; i++)
for(int j = 0 ; j < CITY; j++)
{
city_Map[i][j] = 10;
}
for(int i = 0; i < CITY; i++)
city_Map[i][i] = 0;
for(int i = 0; i < CITY - 1; i++)
{
city_Map[i][i+1] = 1;
city_Map[i+1][i] = 1;
}
city_Map[CITY - 1][0] = 1;
city_Map[0][CITY - 1] = 1;
}
//产生初始化群落
void InitGroup()
{
int flag,tmp_Citycode,flagFirstInput = 0;;
cout<<endl<<"请设置起始城市编号[0-"<<CITY-1<<"]:";
do
{
if(flagFirstInput)
{
cout<<"错误输入,请重试编号[0-"<<CITY-1<<"]:";
}
flagFirstInput = 1;
cin>>start_Citycode;
}
while((start_Citycode > CITY - 1) || (start_Citycode < 0));
for(int i = 0; i < GROUPSIZE; i++)
for(int j = 0; j < CITY; j++)
{
curGroup[i].group[j] = -1;
}
srand((unsigned)time(NULL));
for(int i = 0; i < GROUPSIZE; i++)
{
curGroup[i].group[0] = start_Citycode;
for(int j = 1; j < CITY;)
{
tmp_Citycode = rand()%CITY;
flag = 1;
for(int k = 0; k < j; k++)
{
if(curGroup[i].group[k] == tmp_Citycode)
{
flag = 0;
break;
}
}
if(flag)
{
curGroup[i].group[j] = tmp_Citycode;
j++;
}
}
}
}
void PrintMap()
{
cout<<">>>>>>>>>>>>>>>>城市地图为<<<<<<<<<<<<<<<<"<<endl;
for(int i = 0; i < CITY; i++)
{
for(int j = 0; j < CITY; j++)
cout<<city_Map[i][j]<<"\t";
cout<<endl;
}
cout<<endl;
}
void Cal_PathLen_FIndex()
{
int tmp_PathLen = 0;
float tmp_SumLen = 0;
float tmp_SumFIndex = 0;
int tmp_1,tmp_2;
for(int i = 0; i < GROUPSIZE; i++)
{
for(int j = 0; j < CITY - 1; j++)
{
tmp_1 = curGroup[i].group[j];
tmp_2 = curGroup[i].group[j+1];
tmp_PathLen += city_Map[tmp_1][tmp_2];
}
tmp_PathLen += city_Map[tmp_2][start_Citycode];
curGroup[i].path_Len = tmp_PathLen;
tmp_SumLen += tmp_PathLen;
tmp_PathLen = 0;
}
for(int i = 0; i < GROUPSIZE; i++)
{
curGroup[i].fit_Index = 1 - (double)curGroup[i].path_Len/tmp_SumLen;
tmp_SumFIndex +=curGroup[i].fit_Index;
}
for(int i = 0; i < GROUPSIZE; i++)
{
curGroup[i].fit_Index = curGroup[i].fit_Index/tmp_SumFIndex;
}
}
void PrintAllInfo()
{
out<<">>>>>>>>>>>>>>>>种群信息<<<<<<<<<<<<<<<<"<<endl;
out<<"编号\t路径长度\t适应度\t\t路径"<<endl;
for(int i = 0; i < GROUPSIZE; i++)
{
out<<"["<<i<<"]\t";
cout<<"["<<i<<"]\t";
out<<curGroup[i].path_Len<<"\t"<<fixed<<setprecision(4)<<curGroup[i].fit_Index<<"\t";
cout<<curGroup[i].path_Len<<"\t"<<fixed<<setprecision(4)<<curGroup[i].fit_Index<<"\t";
for(int j = 0; j < CITY; j++)
{
out<<curGroup[i].group[j]<<" ";
cout<<curGroup[i].group[j]<<" ";
}
out<<endl;
cout<<endl;
}
}
void Evolution()
{
for(int i = 0; i < tmp_Index4; i++)
{
CopyGroup(curGroup[i],curGroup[i + tmp_Index5]);
}
for(int i = 0; i < tmp_Index1; i+=2)
{
BirthNewGroup(curGroup[tmp_Index2 + i], curGroup[tmp_Index3 + i]);
}
ChangeGroup();
}
void ChangeGroup()
{
int tmp_cG1,tmp_cG2,tmp_City;
float cGIndex[GROUPSIZE];
bool cGFlag[GROUPSIZE];
for(int i = 0; i < GROUPSIZE; i++)
{
cGFlag[i] = false;
cGIndex[i] = 0;
}
srand((unsigned)time(NULL));
for(int i = 0; i < GROUPSIZE; i++)
{
cGIndex[i] = (rand()%100)/100.0;
}
for(int i = 0; i < GROUPSIZE; i++)
{
if(counter > MAX/2)
{
//后期增大变异概率,否则后期种群趋于一致很难得到最优解
for(int i = 0; i < GROUPSIZE; i++)
{
cGIndex[i] -= 0.5;
}
}
if(cGIndex[i] < CGINDEX)
{
cGFlag[i] = true;
}
}
//尽量避免当前最优解受到变异的干扰。
for(int i = 0; i < GROUPSIZE; i++)
{
if(i == bestFitGroupID)
{
cGFlag[i] = false;
break;
}
}
srand((unsigned)time(NULL));
for(int i = 0; i < GROUPSIZE; i++)
{
if(cGFlag[i])
{
//确保起始点不会受到变异干扰
tmp_cG1 = rand()%(CITY-1) + 1;
tmp_cG2 = rand()%(CITY-1) + 1;
tmp_City = curGroup[i].group[tmp_cG1];
curGroup[i].group[tmp_cG1] = curGroup[i].group[tmp_cG2];
curGroup[i].group[tmp_cG2] = tmp_City;
}
}
}
void BirthNewGroup(struct Group& G1,struct Group& G2)
{
int tmp_Poi;
Group tmp_G1,tmp_G2;
tmp_G1.group[0] = start_Citycode;
tmp_G2.group[0] = start_Citycode;
srand((unsigned)time(NULL));
tmp_Poi = rand()%(CITY - 1) + 1;
for(int i = 1; i < tmp_Poi; i++)
{
tmp_G1.group[i] = G1.group[i];
}
for(int i = tmp_Poi; i < CITY; i++)
{
for(int j = 0; j < CITY; j++)
{
if(CheckAlreadyExist(&tmp_G1,G2.group[j]))
{
tmp_G1.group[i] = G2.group[j];
break;
}
}
}
for(int i = tmp_Poi; i < CITY; i++)
{
tmp_G2.group[i] = G2.group[i];
}
for(int i = 1; i < tmp_Poi; i++)
{
for(int j = 0; j < CITY; j++)
{
if(CheckAlreadyExist(&tmp_G2,G1.group[j]))
{
tmp_G2.group[i] = G1.group[j];
break;
}
}
}
G1 = tmp_G1;
G2 = tmp_G2;
}
void PrintResult()
{
cout<<endl<<endl<<">>>>>>>>>>>>>>>>>>>>>结果打印<<<<<<<<<<<<<<<<<<<<"<<endl<<endl;
cout<<">>经过算法计算最短路径为:"<<curGroup[0].path_Len<<endl;
cout<<">>最佳路径为:";
out<<endl<<endl<<">>>>>>>>>>>>>>>>>>>>>结果打印<<<<<<<<<<<<<<<<<<<<"<<endl<<endl;
out<<">>经过算法计算最短路径为:"<<curGroup[0].path_Len<<endl;
out<<">>最佳路径为:";
for(int i = 0; i < CITY; i++)
{
cout<<curGroup[0].group[i]<<"->";
out<<curGroup[0].group[i]<<"->";
}
cout<<start_Citycode;
out<<start_Citycode;
cout<<endl<<endl<<">>>>>>>>>>>>>>>>>>>>>2016-11<<<<<<<<<<<<<<<<<<<<";
out<<endl<<endl<<">>>>>>>>>>>>>>>>>>>>>2016-11<<<<<<<<<<<<<<<<<<<<";
}
void SortGroup()
{
Group tmp_Group;
for(int i = 0; i < GROUPSIZE; i++)
{
for(int j = i + 1; j < GROUPSIZE; j++)
{
if(curGroup[i].fit_Index < curGroup[j].fit_Index)
{
tmp_Group = curGroup[i];
curGroup[i] = curGroup[j];
curGroup[j] = tmp_Group;
}
}
}
}
void CopyGroup(struct Group &G1,struct Group &G2)
{
G2 = G1;
}
bool CheckAlreadyExist(struct Group* G1, int p_Ck)
{
for(int i = 0; i < CITY; i++)
{
if(G1->group[i] == p_Ck)
{
return false;
}
}
return true;
}