#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
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= 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::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 randomseq; //利用vector里的random_shuffle()函数随机生成剩余popsize
for (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::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(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(rand()) / RAND_MAX;
if (r < pm){
/*
*交叉操作
*(1)随机选取两个染色体n1和n2,同时随机产生两个交叉点left和right(left right){ //使得left 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(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::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 <<" "<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