C语言编写遗传算法解决TSP旅行商问题

最近在上计算智能的课,老师刚刚教了遗传算法,布置了用遗传算法解决TSP的问题的作业,于是经过几小时的奋战,终于编写完成。

首先先对TSP问题进行分析。TSP问题,也就是旅行商问题,题目的大题内容是 一位旅行商,要遍游N座城市(城市数量记为NUM_CITY), 已知每两座城市之间的位置,要求每座城市必须去且只去过一次,求遍游该N座城市的最短路程。

利用遗传算法解决该问题,步骤如下:

1.初始化城市之间的距离

2.生成并初始化初始种群,种群内每个个体保存着一条完整的路径(每个城市标号出现且只出现一次)

3.计算目前种群每个个体的适应值(要求求最短路程,路径一定是个正数,故而得到每个个体中保存的路径的总路程后,取倒数,得到的数越大,适应性越高,总路程越短)

4.找出目前最优个体,让其直接复制至下一代(即不变异)

5.对其他个体根据发生交叉互换的概率Pc,得到参与交叉互换的个体集合

6.使参与交叉互换的个体随机发生交叉互换(每个个体只参与一次)交叉互换的片段起始点和终点均随机产生

7.对除最优个体外的每个个体,根据突变概率Pm发生突变,随机产生两个位置点,使这两个位置点之间的片段进行置倒(即2345变成5432)

8.循环执行步骤34567,直到演变代数>GENERATIONS为止(暂定为5000代)


代码如下所示,(注释部分用的printf用于测试查错,可忽视)

#include 
#include 
#include 
#include 
#define RAND(X) (rand()%(X))
#define NUM_CITY	(30)
#define GENERATIONS	(5000)
#define MAX_SIZE	(50)
#define LOWEST_ALIVE	(1.0/MAX_SIZE)
typedef struct _Group{
	int city[NUM_CITY];
	int adapt;
	float pAlive;
}Group;

int cityDistance[NUM_CITY][NUM_CITY];
Group g_Group[MAX_SIZE];
float	Pm = 0.1;
float	Pc = 0.8;
int bestOne;
void getFitness(Group &group) // 得到适应值
{
	int distance = 0;
	int temp;
	int x1, x2, y1, y2;
	int tdistance;
	for (int i = 1; i < NUM_CITY; i++)
	{
		distance += cityDistance[group.city[i - 1]][group.city[i]];
	}
	group.adapt = distance;
	group.pAlive = 1 / (float)distance;
}
void Swap(int &a, int &b) // 交换ab值
{
	int c;
	c = a;
	a = b;
	b = c;
}
void Init() // 初始化
{
	srand((unsigned int)time(0));
	for (int i = 0; i < NUM_CITY; i++) // 初始化城市距离
	{
		for (int j = i + 1; j < NUM_CITY; j++)
		{
			cityDistance[i][j] = RAND(100) + 1;
			cityDistance[j][i] = cityDistance[i][j];
		}
	}
	printf("城市距离如下:\n");
	for (int i = 0; i < NUM_CITY; i++)
	{
		for (int j = 0; j < NUM_CITY; j++)
		{
			if (j < i + 1)
				printf("    ");
			else
				printf("%3d ", cityDistance[i][j]);
		}
		printf("\n");
	}
	for (int i = 0; i < MAX_SIZE; i++) // 初始化样本数列
	{
		for (int j = 0; j < NUM_CITY; j++)
		{
			g_Group[i].city[j] = j;
		}
	}
	int r;
	for (int i = 0; i < MAX_SIZE; i++) // 打乱顺序
	{
		for (int j = 0; j < NUM_CITY; j++)
		{
			r = RAND(NUM_CITY);
			Swap(g_Group[i].city[j], g_Group[i].city[r]);
		}
	}
	printf("产生初始种群如下:\n");
	for (int i = 0; i < MAX_SIZE; i++)
	{
		printf("第%d个个体:\n", i + 1);
		for (int j = 0; j < NUM_CITY; j++)
		{
			printf("%3d ", g_Group[i].city[j]);
		}
		printf("\n");
	}

}
void GetAliveP() // 存活率
{
	float totalAlive = 0;
	//		选择最优部分
	totalAlive = 0;
	for (int i = 0; i < MAX_SIZE; i++) // 计算个体适应值
	{
		getFitness(g_Group[i]);
		totalAlive += g_Group->pAlive;
	}
	for (int i = 0; i < MAX_SIZE; i++) // 矫正个体存活率 让总和为1
	{
		g_Group[i].pAlive /= totalAlive;
	}
	bestOne = 0;
	for (int i = 0; i < MAX_SIZE; i++)
	{
		if (g_Group[i].pAlive > g_Group[bestOne].pAlive)
			bestOne = i;
	}
	printf("目前最佳个体为:%d, 其距离为%d,其轨迹如下:\n", bestOne+1, g_Group[bestOne].adapt);
	for (int i = 0; i < NUM_CITY; i++)
		printf("%d ", g_Group[bestOne].city[i]);
	printf("\n");
}
int isOnIt(int num, int Array[NUM_CITY], int ignorePos, int pos1, int pos2) // num是否在Array[]的pos1到pos2之间 其中跳过ignorePos(该数字的原位置)
{
	for (int i = pos1; i <= pos2; i++)
	{
		if (Array[i] == num)
			return i;
	}
	return -1;
}

void Swap(int sel1,int sel2,int pos1, int pos2) // 交叉互换
{
	int temp;
	int maxSize = pos2 - pos1 + 1;
	//printf("开始初步交叉互换\n");
	//printf("%d %d\n", pos1, pos2);
	for (int i = pos1; i <= pos2; i++)
	{
		temp = g_Group[sel1].city[i];
		g_Group[sel1].city[i] = g_Group[sel2].city[i];
		g_Group[sel2].city[i] = temp;
	}
	//for (int j = 0; j < NUM_CITY; j++)
	//	printf("%4d", g_Group[sel1].city[j]);
	//printf("\n");
	//for (int j = 0; j < NUM_CITY; j++)
	//	printf("%4d", g_Group[sel2].city[j]);
	//printf("\n");
	int pos;
	//printf("开始矫正重复值\n");
	int times = 0;
	for (int i = 0; i < NUM_CITY; i++) // 矫正重复值
	{
		times = 0;
		if (i >= pos1 && i <= pos2)
		{
			i = pos2;
			continue;
		}
		do 
		{
			times++;

			pos = isOnIt(g_Group[sel1].city[i], g_Group[sel1].city, i, pos1, pos2);
			if (pos != -1)
			{/*
				for (int j = 0; j < NUM_CITY; j++)
					printf("%4d", g_Group[sel1].city[j]);
				printf("\n");
				for (int j = 0; j < NUM_CITY; j++)
					printf("%4d", g_Group[sel2].city[j]);
				printf("\n");
				printf("%d %d %d %d %d\n",pos1,pos2,pos, g_Group[sel1].city[i], g_Group[sel2].city[pos]);*/
				g_Group[sel1].city[i] = g_Group[sel2].city[pos];
				//printf("pos:%d,pos1:%d,pos2:%d\n", pos, pos1, pos2);
			}
		} while (pos != -1);
		do
		{
			pos = isOnIt(g_Group[sel2].city[i], g_Group[sel2].city, i, pos1, pos2);
			if (pos != -1)
			{
				g_Group[sel2].city[i] = g_Group[sel1].city[pos];
				//printf("pos:%d,pos1:%d,pos2:%d\n", pos, pos1, pos2);
			}
		} while (pos != -1);
	}
//	printf("交叉互换过程完毕\n");
}
void Mutation(int sel, int pos1,int pos2)//个体突变
{
	int maxSize = pos2 - pos1 + 1;
	for (int i = 0; i < maxSize / 2; i++)
	{
		Swap(g_Group[sel].city[pos1+i], g_Group[sel].city[pos2-i]);
	}
}
void Genetic() // 产生下一代种群
{
	int maxNum = 0, minNum = 0;	
	for (int i = 0; i < MAX_SIZE; i++)
	{
		if (g_Group[i].pAlive > g_Group[maxNum].pAlive)
			maxNum = i;
		if (g_Group[i].pAlive < g_Group[maxNum].pAlive)
			minNum = i;
	}
	g_Group[minNum] = g_Group[maxNum]; // 使最大直接替换最小 
	//printf("开始交叉\n");
	// 交叉配对
	int selNum;
	int maxTimes = 0, nowTimes = 0;
	int canSelected[MAX_SIZE]; // 可以用于交叉的个体
	bool isCanSelected[MAX_SIZE];
	for (int i = 0; i < MAX_SIZE; i++)
	{
		if (i == maxNum) // 不让最优秀的个体参与配对
			continue;
		else if ((RAND(100)) / 100.0 < Pc) // 根据概率判断是否参与配对
		{
			canSelected[maxTimes++] = i;
		}
	}
	for (int i = 0; i < maxTimes; i++)
	{
		selNum = RAND(maxTimes);

		Swap(canSelected[i], canSelected[selNum]);
	}
	int pos1, pos2;
	for (int i = 0; i < maxTimes; i+=2)
	{
		selNum = i + 1;
		if (selNum >= maxTimes)
			break;
		pos1 = RAND(NUM_CITY); // 选定交叉位置
		pos2 = RAND(NUM_CITY - pos1) + pos1;
		if (pos1 == pos2)
		{
			if (pos1 > 0)
				pos1--;
			else
				pos2++;
		}/*
		printf("%d与%d开始交叉互换\n", canSelected[i], canSelected[selNum]);*/
		Swap(canSelected[i], canSelected[selNum], pos1, pos2);
		/*
				printf("第%d个体与第%d个体进行交叉配对,得到新的两个个体如下:\n", canSelected[i] + 1, canSelected[selNum] + 1);
				printf("第%d个体:\n", canSelected[i] + 1);
				for (int j = 0; j < NUM_CITY; j++)
				printf("%4d", g_Group[canSelected[i]].city[j]);
				printf("\n第%d个体:\n", canSelected[selNum] + 1);
				for (int j = 0; j < NUM_CITY; j++)
				printf("%4d", g_Group[canSelected[selNum]].city[j]);
				printf("\nselNum:%d, maxTimes:%d\n",selNum,maxTimes);*/
	}
/*
	printf("开始突变\n");*/
	// 突变
	for (int i = 0; i < MAX_SIZE; i++)
	{
		if (i == maxNum || i == minNum)
		{
			continue;
		}
		if (RAND(100) / 100.0 < Pm) // 符合突变概率
		{
			pos1 = RAND(NUM_CITY); // 选择位置
			pos2 = RAND(NUM_CITY - pos1) + pos1;
			if (pos1 == pos2)
			{
				if (pos1 > 0)
					pos1--;
				else
					pos2++;
			}
			/*printf("第%d个体突变前:\n", i + 1);
			for (int j = 0; j < NUM_CITY; j++)
				printf("%4d", g_Group[i].city[j]);
			printf("\n");*/
			Mutation(i, pos1, pos2);
		//	printf("第%d个体突变:\n", i + 1);/*
		//	for (int j = 0; j < NUM_CITY; j++)
		//		printf("%4d", g_Group[i].city[j]);
		//	printf("\n");
		}
	}
}
void Train()
{
	int nowGenerations = 0;
	float totalAlive = 0;
	do 
	{
		printf("第%d代种群\n", nowGenerations + 1);
		GetAliveP();// 计算存活率
		Genetic();// 根据遗传规律得到下一代
		nowGenerations++;
	} while (nowGenerations < GENERATIONS);
	printf("经过%d次繁衍,得到的优秀个体为:\n", nowGenerations);
	printf("其距离为%d,其轨迹如下:\n", g_Group[bestOne].adapt);
	for (int i = 0; i < NUM_CITY; i++)
		printf("%d ", g_Group[bestOne].city[i]);
	printf("\n");
	printf("其他个体如下:\n");
	for (int i = 0; i < MAX_SIZE; i++)
	{
		printf("其距离为%d,其轨迹如下:\n", g_Group[i].adapt);
		for (int j = 0; j < NUM_CITY; j++)
		{
			printf("%d ", g_Group[i].city[j]);
		}
		printf("\n");
	}
}
int main()
{
	Init();
	Train();
	return 0;
}


你可能感兴趣的:(C)