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

原创 2015年10月22日 13:22:18

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

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

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

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

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

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

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

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

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

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

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


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

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>
#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;
}


版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

遗传算法解决TSP问题

遗传算法(Genetic Algorithm)是模拟达尔文生物进化论的自然选择和遗传学机理的生物进化过程的计算模型,是一种通过模拟自然进化过程搜索最优解的方法 遗传算法的基本运算过程如下: a)初...

Delphi7高级应用开发随书源码

  • 2003年04月30日 00:00
  • 676KB
  • 下载

(C语言)分支界限法求解旅行商(TSP)问题

1.代码: #include #include #define NoEdge 1000 struct MinHeapNode { int lcost; //子树费用...

Delphi7高级应用开发随书源码

  • 2003年04月30日 00:00
  • 676KB
  • 下载

利用动态规划法求解旅行商问题(TSP)的C语言实现(一)

某推销员要从城市v1 出发,访问其它城市v2,v3,…,v6各一次且仅一次,最后返回v1。D为各城市间的距离矩阵。 问:该推销员应如何选择路线,才能使总的行程最短? D=   0 10 20 30...

贪心算法:旅行商问题(TSP)

TSP问题(Traveling Salesman Problem,旅行商问题),由威廉哈密顿爵士和英国数学家克克曼T.P.Kirkman于19世纪初提出。问题描述如下: 有若干个城市,任何两个城...
  • larry233
  • larry233
  • 2016年03月10日 15:44
  • 11748

Delphi7高级应用开发随书源码

  • 2003年04月30日 00:00
  • 676KB
  • 下载

旅行商问题的n种解法

问题描述: 旅行商问题(Traveling Salesman Problem,TSP)是旅行商要到若干个城市旅行,各城市之间的费用是已知的,为了节省费用,旅行商决定从所在城市出发,到每个城市旅行一次...

Delphi7高级应用开发随书源码

  • 2003年04月30日 00:00
  • 676KB
  • 下载

Delphi7高级应用开发随书源码

  • 2003年04月30日 00:00
  • 676KB
  • 下载
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:C语言编写遗传算法解决TSP旅行商问题
举报原因:
原因补充:

(最多只允许输入30个字)