蚁群算法解决TSP问题(C++编写,附全代码)

1.Tsp问题描述

有N个城市,C={0,1,2,3…,N-1},任意两城市距离为dij,求一条经过每个城市的路径,使得距离最小。以30个城市为例分析,另代码中50和75个城市同理。
蚁群算法解决TSP问题(C++编写,附全代码)_第1张图片

2. 参数设置

N:城市个数30
M:蚂蚁个数30
a:表征信息素重要程度的参数,设为2
b:表征能见度重要程度的参数,设为3
p:局部信息素挥发参数,设为0.1
r: 全局信息素挥发参数,设为0.1
NC_MAX: 最大迭代次数,设为500次

3. 算法过程

(1) 蚂蚁均匀分布在各个城市(每个城市放一只)
(2) 随机在30个城市中选一个城市节点,再由这个节点出发由贪心策略到扫描完剩余所有节点得到一条最短路径之和,记为Lnn。
初始化信息素浓度:Info[i][j]=Info[j][i]=1/(N*Lnn)
初始化能见度:visible[i][j]=visible[j][i]=1/dij
(3)对每只蚂蚁从开始出发搜索一条路径:就是从当前城市去往下一城市直到禁忌表中所有节点访问完为止,得到当前路径长度tourlength。最关键就是如何选择下一城市(轮盘赌)。
先生成一个随机数q
P(j)=[(info[start][j])a+(visible[start][j])b]/sum

由概率q按轮盘赌规则选择下一城市为j。
并更新局部信息素浓度:info[i][j]=info[j][i]=(1-p)* info[i][j]+p/(N*Lnn)
(4)进行局部比较:如果当前路径长度<局部最好路径长度,更新局部最好路径为当前路径
(5) 进行全局比较:如果局部路径长度<全局最好路径长度,更新全局最好路径为局部路径
更新全局信息素浓度:info[i][j]=info[j][i]=(1-r)*info[i][j]+r/globalBestLength

4. 实验结果

蚁群算法解决TSP问题(C++编写,附全代码)_第2张图片
蚁群算法解决TSP问题(C++编写,附全代码)_第3张图片
可看到在第96代就得到了最优路径。

5.代码


#include   
#include   
#include   
using namespace std;

//该程序是以蚁群系统为模型写的蚁群算法程序(强调:非蚂蚁周模型),以三个著名的TSP问题为测试对象   
//通过微调参数,都可以获得较好的解   


//----------(1)问题一:Oliver 30 城市 TSP 问题 best_length = 423.7406; ------------------------
//该程序最好的结果是423.741,可运行多次获得
//城市节点数目
#define N 30
//城市坐标
double C[N][2]={
{2,99},{4,50},{7,64},{13,40},{18,54},{18,40},{22,60},{24,42},{25,62},{25,38},
{37,84},{41,94},{41,26},{44,35},{45,21},{54,67},{54,62},{58,35},{58,69},{62,32},
{64,60},{68,58},{71,44},{71,71},{74,78},{82,7},{83,46},{83,69},{87,76},{91,38}
};
//----------上面参数是固定的,下面的参数是可变的-----------
//蚂蚁数量
#define M 30
//最大循环次数NcMax
int NcMax = 500;
//信息启发因子,期望启发式因子,全局信息素挥发参数,局部信息素挥发参数, 状态转移公式中的q0
double alpha = 2, beta = 3, rou = 0.1, alpha1 = 0.1,  qzero = 0.01;
//-----------问题一结束------------------------------------------------------------------------


/*
//----------(2)问题二:Elion50 城市 TSP 问题 best_length = 427.96; ----------------------------
//该程序最好的结果是428.468,可运行多次获得
//城市节点数目
#define N 50
//城市坐标
double C[N][2]={
{5,64}, {5,25}, {5,6}, {7,38}, {8,52}, {10,17},
{12,42}, {13,13}, {16,57}, {17,33}, {17,63},
{20,26}, {21,47}, {21,10}, {25,32}, {25,55},
{27,68}, {27,23}, {30,48}, {30,15}, {31,62},
{31,32}, {32,22}, {32,39}, {36,16}, {37,69},
{37,52}, {38,46}, {39,10}, {40,30}, {42,57},
{42,41}, {43,67}, {45,35}, {46,10}, {48,28},
{49,49}, {51,21}, {52,33}, {52,41}, {52,64},
{56,37}, {57,58}, {58,27}, {58,48}, {59,15},
{61,33}, {62,42}, {62,63}, {63,69}
};
//----------上面参数是固定的,下面的参数是可变的-----------
//蚂蚁数量
#define M 50
//最大循环次数NcMax
int NcMax = 1000;
//信息启发因子,期望启发式因子,全局信息素挥发参数,局部信息素挥发参数, 状态转移公式中的q0
double alpha = 2, beta = 4, rou = 0.1, alpha1 = 0.1,  qzero = 0.01;
//-----------问题二结束------------------------------------------------------------------------
*/

//----------(3)问题三:Elion75 城市 TSP 问题 best_length = 542.31;   
//该程序最好的结果是542.309,可运行多次获得    
//城市节点数目   
/*#define N 75   
//城市坐标   
double C[N][2] = {
	{ 6, 25 }, { 7, 43 }, { 9, 56 }, { 10, 70 }, { 11, 28 },
	{ 12, 17 }, { 12, 38 }, { 15, 5 }, { 15, 14 }, { 15, 56 },
	{ 16, 19 }, { 17, 64 }, { 20, 30 }, { 21, 48 }, { 21, 45 },
	{ 21, 36 }, { 22, 53 }, { 22, 22 }, { 26, 29 }, { 26, 13 },
	{ 26, 59 }, { 27, 24 }, { 29, 39 }, { 30, 50 }, { 30, 20 },
	{ 30, 60 }, { 31, 76 }, { 33, 34 }, { 33, 44 }, { 35, 51 },
	{ 35, 16 }, { 35, 60 }, { 36, 6 }, { 36, 26 }, { 38, 33 },
	{ 40, 37 }, { 40, 66 }, { 40, 60 }, { 40, 20 }, { 41, 46 },
	{ 43, 26 }, { 44, 13 }, { 45, 42 }, { 45, 35 }, { 47, 66 },
	{ 48, 21 }, { 50, 30 }, { 50, 40 }, { 50, 50 }, { 50, 70 },
	{ 50, 4 }, { 50, 15 }, { 51, 42 }, { 52, 26 }, { 54, 38 },
	{ 54, 10 }, { 55, 34 }, { 55, 45 }, { 55, 50 }, { 55, 65 },
	{ 55, 57 }, { 55, 20 }, { 57, 72 }, { 59, 5 }, { 60, 15 },
	{ 62, 57 }, { 62, 48 }, { 62, 35 }, { 62, 24 }, { 64, 4 },
	{ 65, 27 }, { 66, 14 }, { 66, 8 }, { 67, 41 }, { 70, 64 }
};
//----------上面参数是固定的,下面的参数是可变的-----------   
//蚂蚁数量   
#define M 75   
//最大循环次数NcMax   
int NcMax = 1000;
//信息启发因子,期望启发式因子,全局信息素挥发参数,局部信息素挥发参数, 状态转移公式中的q0   
double alpha = 2, beta = 5, rou = 0.1, alpha1 = 0.1, qzero = 0.1;
//-----------问题三结束------------------------------------------------------------------------   
*/

//===========================================================================================================   
//局部更新时候使用的的常量,它是由最近邻方法得到的一个长度   
//什么是最近邻方法?:)就是从源节点出发,每次选择一个距离最短的点来遍历所有的节点得到的路径   
//每个节点都可能作为源节点来遍历   
double Lnn;
//矩阵表示两两城市之间的距离   
double allDistance[N][N];

//计算两个城市之间的距离   
double calculateDistance(int i, int j)
{
	return sqrt(pow((C[i][0] - C[j][0]), 2.0) + pow((C[i][1] - C[j][1]), 2.0));
}

//由矩阵表示两两城市之间的距离   
void calculateAllDistance()
{
	for (int i = 0; i < N; i++)
	{
		for (int j = 0; j < N; j++)
		{
			if (i != j)
			{
				allDistance[i][j] = calculateDistance(i, j);
				allDistance[j][i] = allDistance[i][j];
			}
		}
	}
}

//获得经过n个城市的路径长度   
double calculateSumOfDistance(int* tour)
{
	double sum = 0;
	for (int i = 0; i< N; i++)
	{
		int row = *(tour + 2 * i);
		int col = *(tour + 2 * i + 1);
		sum += allDistance[row][col];
	}
	return sum;
}

class ACSAnt;

class AntColonySystem
{
private:
	double info[N][N], visible[N][N];//节点之间的信息素强度,节点之间的能见度   
public:
	AntColonySystem()
	{
	}
	//计算当前节点到下一节点转移的概率   
	double Transition(int i, int j);
	//局部更新规则   
	void UpdateLocalPathRule(int i, int j);
	//初始化   
	void InitParameter(double value);
	//全局信息素更新   
	void UpdateGlobalPathRule(int* bestTour, int globalBestLength);
};

//计算当前节点到下一节点转移的概率   
double AntColonySystem::Transition(int i, int j)
{
	if (i != j)
	{
		return (pow(info[i][j], alpha) * pow(visible[i][j], beta));
	}
	else
	{
		return 0.0;
	}
}
//局部更新规则   
void AntColonySystem::UpdateLocalPathRule(int i, int j)
{
	info[i][j] = (1.0 - alpha1) * info[i][j] + alpha1 * (1.0 / (N*Lnn));
	info[j][i] = info[i][j];
}
//初始化   
void AntColonySystem::InitParameter(double value)
{
	//初始化路径上的信息素强度tao0   
	for (int i = 0; i < N; i++)
	{
		for (int j = 0; j < N; j++)
		{
			info[i][j] = value;
			info[j][i] = value;
			if (i != j)
			{
				visible[i][j] = 1.0 / allDistance[i][j];
				visible[j][i] = visible[i][j];
			}
		}
	}
}

//全局信息素更新   
void AntColonySystem::UpdateGlobalPathRule(int* bestTour, int globalBestLength)
{
	for (int i = 0; i < N; i++)
	{
		int row = *(bestTour + 2 * i);
		int col = *(bestTour + 2 * i + 1);
		info[row][col] = (1.0 - rou) * info[row][col] + rou * (1.0 / globalBestLength);
		info[col][row] = info[row][col];
	}
}

class ACSAnt
{
private:
	AntColonySystem* antColony;
protected:
	int startCity, cururentCity;//初始城市编号,当前城市编号   
	int allowed[N];//禁忌表       
	int Tour[N][2];//当前路径   
	int currentTourIndex;//当前路径索引,从0开始,存储蚂蚁经过城市的编号   
public:
	ACSAnt(AntColonySystem* acs, int start)
	{
		antColony = acs;
		startCity = start;
	}
	//开始搜索   
	int* Search();
	//选择下一节点   
	int Choose();
	//移动到下一节点   
	void MoveToNextCity(int nextCity);

};

//开始搜索   
int* ACSAnt::Search()
{
	cururentCity = startCity;
	int toCity;
	currentTourIndex = 0;
	for (int i = 0; i < N; i++)
	{
		allowed[i] = 1;
	}
	allowed[cururentCity] = 0;
	int endCity;
	int count = 0;
	do
	{
		count++;
		endCity = cururentCity;
		toCity = Choose();
		if (toCity >= 0)
		{
			MoveToNextCity(toCity);
			antColony->UpdateLocalPathRule(endCity, toCity);
			cururentCity = toCity;
		}
	} while (toCity >= 0);
	MoveToNextCity(startCity);
	antColony->UpdateLocalPathRule(endCity, startCity);

	return *Tour;
}

//选择下一节点   
int ACSAnt::Choose()
{
	int nextCity = -1;
	double q = rand() / (double)RAND_MAX;
	//如果 q <= q0,按先验知识,否则则按概率转移,   
	if (q <= qzero)
	{
		double probability = -1.0;//转移到下一节点的概率   
		for (int i = 0; i < N; i++)
		{
			//去掉禁忌表中已走过的节点,从剩下节点中选择最大概率的可行节点   
			if (1 == allowed[i])
			{
				double prob = antColony->Transition(cururentCity, i);
				if (prob  > probability)
				{
					nextCity = i;
					probability = prob;
				}
			}
		}
	}
	else
	{
		//按概率转移            
		double p = rand() / (double)RAND_MAX;//生成一个随机数,用来判断落在哪个区间段   
		double sum = 0.0;
		double probability = 0.0;//概率的区间点,p 落在哪个区间段,则该点是转移的方向   
		//计算概率公式的分母的值   
		for (int i = 0; i < N; i++)
		{
			if (1 == allowed[i])
			{
				sum += antColony->Transition(cururentCity, i);
			}
		}
		for (int j = 0; j < N; j++)
		{
			if (1 == allowed[j] && sum > 0)
			{
				probability += antColony->Transition(cururentCity, j) / sum;
				if (probability >= p || (p > 0.9999 && probability > 0.9999))
				{
					nextCity = j;
					break;
				}
			}
		}
	}
	return nextCity;
}

//移动到下一节点   
void ACSAnt::MoveToNextCity(int nextCity)
{
	allowed[nextCity] = 0;
	Tour[currentTourIndex][0] = cururentCity;
	Tour[currentTourIndex][1] = nextCity;
	currentTourIndex++;
	cururentCity = nextCity;
}

//------------------------------------------   
//选择下一个节点,配合下面的函数来计算的长度   
int ChooseNextNode(int currentNode, int visitedNode[])
{
	int nextNode = -1;
	double shortDistance = 0.0;
	for (int i = 0; i < N; i++)
	{
		//去掉已走过的节点,从剩下节点中选择距离最近的节点   
		if (1 == visitedNode[i])
		{
			if (shortDistance == 0.0)
			{
				shortDistance = allDistance[currentNode][i];
				nextNode = i;
			}
			if (shortDistance < allDistance[currentNode][i])
			{
				nextNode = i;
			}
		}
	}
	return nextNode;
}

//给一个节点由最近邻距离方法计算长度   
double CalAdjacentDistance(int node)
{
	double sum = 0.0;
	int visitedNode[N];
	for (int j = 0; j < N; j++)
	{
		visitedNode[j] = 1;
	}
	visitedNode[node] = 0;
	int currentNode = node;
	int nextNode;
	do
	{
		nextNode = ChooseNextNode(currentNode, visitedNode);
		if (nextNode >= 0)
		{
			sum += allDistance[currentNode][nextNode];
			currentNode = nextNode;
			visitedNode[currentNode] = 0;
		}
	} while (nextNode >= 0);
	sum += allDistance[currentNode][node];
	return sum;
}

//---------------------------------结束---------------------------------------------   

//--------------------------主函数--------------------------------------------------   
int main()
{
	srand(time(0));
	time_t timer = clock();

	//由矩阵表示两两城市之间的距离   
	calculateAllDistance();
	//蚁群系统对象   
	AntColonySystem* acs = new AntColonySystem();
	ACSAnt* ants[M];
	//蚂蚁均匀分布在城市上   
	for (int k = 0; k < M; k++)
	{
		ants[k] = new ACSAnt(acs, (int)(k%N));
	}
	calculateAllDistance();
	//随机选择一个节点计算由最近邻方法得到的一个长度   
	int node = rand() % N;
	Lnn = CalAdjacentDistance(node);

	//各条路径上初始化的信息素强度   
	double initInfo = 1 / ( N*Lnn);
	acs->InitParameter(initInfo);

	//全局最优路径   
	int globalTour[N][2];
	//全局最优长度   
	double globalBestLength = 0.0;
	for (int i = 0; i < NcMax; i++)
	{
		//局部最优路径   
		int localTour[N][2];
		//局部最优长度   
		double localBestLength = 0.0;
		//当前路径长度   
		double tourLength;
		for (int j = 0; j < M; j++)
		{
			int* tourPath = ants[j]->Search();
			tourLength = calculateSumOfDistance(tourPath);
			//局部比较,并记录路径和长度   
			if (tourLength < localBestLength || abs(localBestLength - 0.0) < 0.000001)
			{
				for (int m = 0; m< N; m++)
				{
					int row = *(tourPath + 2 * m);
					int col = *(tourPath + 2 * m + 1);
					localTour[m][0] = row;
					localTour[m][1] = col;
				}
				localBestLength = tourLength;
			}
		}
		//全局比较,并记录路径和长度   
		if (localBestLength < globalBestLength || abs(globalBestLength - 0.0) < 0.000001)
		{
			for (int m = 0; m< N; m++)
			{
				globalTour[m][0] = localTour[m][0];
				globalTour[m][1] = localTour[m][1];
			}
			globalBestLength = localBestLength;
		}
		acs->UpdateGlobalPathRule(*globalTour, globalBestLength);
		//输出所有蚂蚁循环一次后的迭代最优路径   
		cout << "第 " << i + 1 << " 迭代最优路径:" << localBestLength << "." << endl;
		for (int m = 0; m< N; m++)
		{
			cout << localTour[m][0] << ".";
		}
		cout << endl;
	}

	//输出全局最优路径   
	cout << "全局最优路径长度:" << globalBestLength << endl;
	cout << "全局最优路径:";
	for (int m = 0; m< N; m++)
	{
		cout << globalTour[m][0] << ".";
	}
	cout << endl;
	time_t timer1 = clock();
	double t = (timer1 - timer);
	printf("花费时间:%f\n", t);
	system("pause");
	return 0;
}



你可能感兴趣的:(学习笔记)