蚁群算法原理及c++实现

参考博客
https://blog.csdn.net/Oudasheng/article/details/84994336
https://blog.csdn.net/wayjj/article/details/72809344#commentsedit
https://blog.csdn.net/Oudasheng/article/details/84994336
https://www.cnblogs.com/mahaitao/p/5572095.html
https://www.cnblogs.com/Horizon-asd/p/12723886.html
https://blog.csdn.net/chen10217/article/details/100762552

一、原理

1. 蚂蚁觅食行为

蚁群在寻找食物时,总能找到一条蚁穴到食物的最短路径,并且能随着环境的变化而更新最优路径。究其原因,是因为蚂蚁在运动过程中,会在所经过路径上留下一种称为信息素(pheromone)的物质,其他蚂蚁在运动中可以感知到这种物质,并以此来指导自己的运动方向。
蚁群的这种行为表现出一种信息正反馈现象:某一路径上走过的蚂蚁越多,则后来者选择该路径的概率越大。

2.蚁群算法

又称蚂蚁算法,是一种基于群体智能的算法,用来在图中寻找优化路径的概率型。它由Marco Dorigo于1992年在他的博士论文中提出,其灵感来源于蚂蚁在寻找食物过程中发现路径的行为。在解决实际问题时,人工蚁群相较于自然蚁群有一定的记忆能力,可记住已访问过的节点,其次人工蚁群在选择路径时依照一定规律,并不是盲目的。蚁群算法常用来解决路径规划等离散优化问题,如旅行商问题(TSP)、指派问题、调度问题。

2,1 特点

  1. 正反馈:可较快发现较优解。
  2. 分布式:基于种群的进化算法,本质具有并行性,易于并行实现。
  3. 启发式搜索:反映搜索中的的先验性和确定性因素(如距离)强度。
  4. 鲁棒性强:不易受某个个体影响。

2.2 算法流程(以TSP问题为例)

TSP问题:一商人去n个城市销货,所有城市走一遍再回到起点,使所走路程最短。

  1. 初始化相关参数:蚁群规模、信息素因子、启发函数因子、信息素挥发因子、信息素常数和最大迭代次数等。将城市信息读入程序并进行预处理,即将城市间信息转化为矩阵。
  2. 随机将蚂蚁放入不同出发点,计算每个蚂蚁的下一步要访问的城市,直到有蚂蚁访问完所有城市。
  3. 计算每个蚂蚁经过的路径长度Lk,记录当前迭代次数下的最优解(访问完所有城市且路径长度最短),更新各条路径上的信息素浓度。
  4. 断是否达到最大迭代次数,若否则返回步骤2,若是则顺序执行。
  5. 输出最优路径和相关指标,如运行时间和迭代次数。

2.3 相关公式

在这里插入图片描述
在这里插入图片描述

2.4 流程图

在这里插入图片描述

三、例子

试设计一个并行算法,求下图中一个源点到其他定点的最短路径。(VS2019+Eigen)
在这里插入图片描述

3.1 关键代码

数据结构:

将蚂蚁设置为一个结构体,包含所在位置、禁忌表、所走路径和是否到达终点标志四项内容。为了便于计算,将信息素、启发信息与距离信息分别在8*8的矩阵中存放,这样可以调用Eigen库直接进行矩阵计算,达到更新信息素的目的。
城市也设为一个结构体,包含城市编号和选择概率两项内容。这里将城市设置为结构体,主要是考虑到在选择下一步行进城市时,要先计算选择概率再通过轮盘赌来确定下一步城市,轮盘赌时需要将城市编号与其选择概率一一对应。
具体代码部分如下:

struct ant                 //蚂蚁结构体
{
	int loc;               //位置
	int tabu[cityNum];     //禁忌表
	int antPath[pathNum];  //走过的路
	bool flag;             //是否到达终点7
};
struct ant ants[antNum];   //蚁群

typedef Matrix<double, 8, 8> Matrix8d;
Matrix8d dist;             //距离矩阵
Matrix8d pher;             //信息素矩阵
Matrix8d nextPher;         //下一代信息素矩阵
Matrix8d insp;             //启发信息矩阵

struct city                //城市结构体
{
	int num;               //编号
	double prob;           //选择概率
};
struct city cityProb[cityNum];    //可到达城市组
double lineCityProb[cityNum];     //线性化 可到达城市的选择概率
城市选择方式

当蚂蚁k选择下一步要去的城市时,有以下几个步骤:

  1. 对照蚂蚁k的禁忌表,求出下一步所有可去的城市各自的选择概率(概率计算见公式(1));
  2. 线性化所有可去城市的概率,生成介于0~1之间的随机数(线性化概率的目的是实现轮盘赌);
  3. 使用轮盘赌方法选择下一步要去的城市。
    概率计算与轮盘赌选择对应代码片如下:
//轮盘赌选择下一步行进城市
int citySelect(int k, int f)
{
	int c = 0;//记录蚂蚁可行进的城市个数


	//1、计算可行进的各城市 选择概率
	for (int m = 0; m < cityNum; m++)
	{
		//若城市(i,j)之间有路且j不在蚂蚁k的禁忌表中,则计算概率
		if (dist(ants[k].loc, m) != -1 && !ifCityInTabu(m, k))
		{
			cityProb[c].num = m;
			cityProb[c].prob = citySelProb(k, m);
			c++;
		}
	}

	//2、线性化选择概率
	for (int m = 0; m < c; m++)
	{
		for (int n = m; n >= 0; n--)
		{
			lineCityProb[m] += cityProb[n].prob;
		}
	}

	//3、产生随机数选择城市
	double r = rand() / double(RAND_MAX);
	int j = 0;   //选取的目标城市
	for (int m = 0; m < cityNum; m++)
	{
		if (r <= lineCityProb[m])
		{
			j = cityProb[m].num;
			updateAnt(k, j);
			if (j == f)
				ants[k].flag = 1;  //若蚂蚁k下一步城市为目的地城市,则修改标志
			return j;
		}

	}
}
信息素更新

因为将信息素存入矩阵,所以在计算时较为简单,具体分为如下几步:

  1. 计算信息素增量矩阵:
    for k = 1 to m do (遍历蚁群)
    for j = 1 to n do (遍历蚂蚁k的行走路径)
       计算蚂蚁k在路径(i, j)对应的信息素增量(见公式(3));
    更新路径(i, j)在上一轮的信息素增量;
      end for
    end for
  2. 计算更新后的信息素矩阵:
    信息素挥发系数*信息素矩阵+信息素增量矩阵(见公式(2))。
    对应代码片如下:
 void updatePher()
{
	for (int i = 0; i < antNum; i++)
	{
		if(ants[i].flag == 1)  //只对到达目的点的蚂蚁 所走过路径 更新信息素
			for (int j = 0; j < pathNum; j++)
			{
				if (ants[i].antPath[j] == -1 || ants[i].antPath[j + 1] == -1)
					break;
				else
					nextPher(ants[i].antPath[j], ants[i].antPath[j + 1])
					+= pQ / getAntLen(ants[i]);
			}
		
	}
	nextPher = pVol * pher + nextPher;
}

3.2 运行结果

参数设置

const int cityNum = 8;     //城市数量
const int pathNum = 16;    //路径数量
const int antNum = 12;     //蚂蚁数量(1.5倍城市数量)
const double pVol = 0.3;   //信息素挥发系数 0.2~0.5
const int pQ = 10;         //信息素强度 10~1000
const double pImp = 3;     //信息素相对重要性 1~4
const double qImp = 4;     //启发信息相对重要性 3~4.5
const int gen = 100;       //迭代次数 100~500

运行结果

在这里插入图片描述

问题

  1. 各项参数初始值虽然知道设置范围,但因为不够理解参数如何影响迭代的结果,参数设定主要依靠猜测。
  2. 代码运行后,发现回归太早,不符合蚁群算法回归较慢(200~500)的特点,后经过检查,发现是计算蚂蚁k在路径(i,j)上的信息素增量时,将除数Lk理解成了路径(i,j) 的距离,但实际上应该为蚂蚁k本次迭代中做走过路径距离之和。经修改后,可以符合蚁群算法回归较慢的特点。
  3. 只计算源点到某一个定点时,代码没有问题,循环结算到每个顶点最短路径时,有时运行会报如下错误,有时不会,不太明白。
    在这里插入图片描述

源码

#include<iostream>
#include<Eigen\Dense>
#include<stdlib.h>
#include<time.h>
#include<math.h>

using namespace Eigen;
using namespace std;

/* 
   功能:此代码使用蚁群算法计算源点0 ~ 其他定点的最短路径
   author:yuzewei
   date:2020/12/19
*/

#define CLOCK_PER_SEC ((clock_t)1000)

const int cityNum = 8;     //城市数量
const int pathNum = 16;    //路径数量
const int antNum = 12;     //蚂蚁数量(1.5倍城市数量)
const double pVol = 0.3;   //信息素挥发系数 0.2~0.5
const int pQ = 10;         //信息素强度 10~1000
const double pImp = 3;     //信息素相对重要性 1~4
const double qImp = 4;     //启发信息相对重要性 3~4.5
const int gen = 100;       //迭代次数 100~500

struct ant                 //蚂蚁结构体
{
	int loc;               //位置
	int tabu[cityNum];     //禁忌表
	int antPath[pathNum];  //走过的路
	bool flag;             //是否到达终点7
};
struct ant ants[antNum];   //蚁群

typedef Matrix<double, 8, 8> Matrix8d;
Matrix8d dist;             //距离矩阵
Matrix8d pher;             //信息素矩阵
Matrix8d nextPher;         //下一代信息素矩阵
Matrix8d insp;             //启发信息矩阵

struct city                //城市结构体
{
	int num;               //编号
	double prob;           //选择概率
};
struct city cityProb[cityNum];    //可到达城市组
double lineCityProb[cityNum];     //线性化 可到达城市的选择概率

clock_t start, finish;     
double duration;

void initAnts();                  
void initCityProb();              
void initMarix();   
bool ifCityInTabu(int, int);
int citySelect(int, int);
void updateAnt(int, int);
double citySelProb(int, int);
int getAntLen(ant);
int getBestPath();
void printBestPath(int, int);
void updatePher();
void evolution();

int main()
{
	srand((unsigned)time(NULL));

	evolution();
}

//蚁群初始化
void initAnts()
{
	//初始化禁忌表与行走路线
	for (int i = 0; i < antNum; i++)
	{
		for (int j = 0; j < cityNum; j++)
		{
			ants[i].tabu[j] = -1;
		}
		for (int j = 0; j < pathNum; j++)
		{
			ants[i].antPath[j] = -1;
		}
	}
	//将蚂蚁放入城市
	for (int i = 0; i < antNum; i++)
	{
		//ants[i].loc = rand() % 8;
		ants[i].loc = 0;//出发点都在起点
		ants[i].tabu[0] = ants[i].loc;
		ants[i].antPath[0] = ants[i].loc;
		ants[i].flag = 0;
	}
}

//初始化城市选择概率数组
void initCityProb()
{

	for (int i = 0; i < cityNum; i++)
	{
		cityProb[i].num = -1;
		cityProb[i].prob = 0;
		lineCityProb[i] = 0;
	}
}

//初始化距离、信息素、启发信息矩阵
void initMarix()
{
	dist = Matrix8d::Constant(8, 8, -1);
	dist(0, 2) = 47;
	dist(0, 4) = 70;
	dist(0, 5) = 24;

	dist(1, 3) = 31;
	dist(1, 6) = 74;
	dist(1, 7) = 79;

	dist(2, 1) = 55;
	dist(2, 3) = 88;
	dist(2, 4) = 23;
	dist(2, 6) = 66;

	dist(3, 7) = 29;

	dist(4, 1) = 31;
	dist(4, 6) = 42;

	dist(5, 2) = 25;
	dist(5, 3) = 120;

	dist(6, 7) = 66;

	pher = Matrix8d::Zero();
	nextPher = Matrix8d::Zero();
	insp = Matrix8d::Zero();
	for (int i = 0; i < 8; i++)
	{
		for (int j = 0; j < 8; j++)
		{
			if (dist(i, j) != -1)
			{
				insp(i, j) = 1 / dist(i, j);//启发信息为距离的倒数
				pher(i, j) = 1;             //信息素浓度初始值为1
			}

		}
	}
}

//轮盘赌选择下一步行进城市
int citySelect(int k, int f)
{
	int c = 0;//记录蚂蚁可行进的城市个数


	//1、计算可行进的各城市 选择概率
	for (int m = 0; m < cityNum; m++)
	{
		//若城市(i,j)之间有路且j不在蚂蚁k的禁忌表中,则计算概率
		if (dist(ants[k].loc, m) != -1 && !ifCityInTabu(m, k))
		{
			cityProb[c].num = m;
			cityProb[c].prob = citySelProb(k, m);
			c++;
		}
	}

	//2、线性化选择概率
	for (int m = 0; m < c; m++)
	{
		for (int n = m; n >= 0; n--)
		{
			lineCityProb[m] += cityProb[n].prob;
		}
	}

	//3、产生随机数选择城市
	double r = rand() / double(RAND_MAX);
	int j = 0;   //选取的目标城市
	for (int m = 0; m < cityNum; m++)
	{
		if (r <= lineCityProb[m])
		{
			j = cityProb[m].num;
			updateAnt(k, j);
			if (j == f)
				ants[k].flag = 1;  //若蚂蚁k下一步城市为目的地城市,则修改标志
			return j;
		}

	}
}

//更新蚂蚁信息
void updateAnt(int k, int l)
{
	ants[k].loc = l;
	for (int i = 0; i < cityNum; i++)
		if (ants[k].tabu[i] == -1)
		{
			ants[k].tabu[i] = l;
			break;
		}
	for (int i = 0; i < pathNum; i++)
		if (ants[k].antPath[i] == -1)
		{
			ants[k].antPath[i] = l;
			break;
		}
}

//蚂蚁k从当前城市i选择下一步行进城市为j市的概率
double citySelProb(int k, int j)
{
	double a, b, c, prob;
	a = b = c = prob = 0;
	int i = ants[k].loc;

	a = pow(pher(i, j), pImp) + pow(insp(i, j), qImp);
	for (int m = 0; m < cityNum; m++)
	{
		if (dist(i, m) != -1 && !ifCityInTabu(m, k))
		{
			b = pow(pher(i, m), pImp) + pow(insp(i, m), qImp);
			c += b;
		}
	}

	prob = a / c;
	return prob;
}

//判断城市j是否在蚂蚁k的禁忌表中
bool ifCityInTabu(int j, int k)
{
	for (int i = 0; i < cityNum; i++)
	{
		if (j == ants[k].tabu[i])
		{
			return 1;
			//break;
		}
	}
	return 0;
}

//计算路径长度
int getAntLen(struct ant a)
{
	int len = 0;
	for (int j = 0; j < pathNum; j++)
	{
		if (a.antPath[j] == -1 || a.antPath[j + 1] == -1)
			break;
		else
			len += dist(a.antPath[j], a.antPath[j + 1]);

	}
	return len;
}

//计算最优路径对应的蚂蚁编号
int getBestPath()
{
	int d[antNum];
	int min;
	int k;  //蚂蚁k的路线到达目的地节点最短
	for (int i = 0; i < antNum; i++)
	{
		d[i] = -1;
	}
	for (int i = 0; i < antNum; i++)
	{
		
		d[i] = getAntLen(ants[i]);
	}

	min = d[0];
	k = 0;
	for (int i = 1; i < antNum; i++)
	{
		if (d[i] < min && ants[i].flag == 1)  // 最优路径只从到达目标点的蚂蚁中筛选
		{
			min = d[i];
			k = i;
		}
	}
	return k;
}

//打印最优路径、最短距离
void printBestPath(int k, int f)
{
	cout << "  最短路径为:";
	for (int i = 0; i < pathNum; i++)
	{
		if (ants[k].antPath[i] == -1)
			break;

		cout << ants[k].antPath[i];
		if (ants[k].antPath[i+1] != -1)
			cout << "->";
	}
	cout << endl;
	cout << "  对应距离为:" << getAntLen(ants[k]) << endl;
}

//更新信息素矩阵
void updatePher()
{
	for (int i = 0; i < antNum; i++)
	{
		if(ants[i].flag == 1)  //只对到达目的点的蚂蚁 所走过路径 更新信息素
			for (int j = 0; j < pathNum; j++)
			{
				if (ants[i].antPath[j] == -1 || ants[i].antPath[j + 1] == -1)
					break;
				else
					nextPher(ants[i].antPath[j], ants[i].antPath[j + 1])
					+= pQ / getAntLen(ants[i]);
			}
		
	}
	nextPher = pVol * pher + nextPher;
}


//迭代
void evolution()
{
	for (int f = 1; f < cityNum; f++)
	{
		cout << "【从源点0到定点" << f << "】" << endl;
		cout << "开始迭代........." << endl;

		//初始化参数
		initAnts();
		initMarix();

		int g = 0; //当前代数
		start = clock();

		while (g < gen)
		{
			//1、蚁群内所有蚂蚁都到达目的地
			int p = 0; //蚁群前进步数
			while (p < pathNum)
			{
				for (int i = 0; i < antNum; i++)
				{
					if (ants[i].flag == 1)//到达目的地
						continue;
					citySelect(i, f);
					initCityProb();
				}
				p++;
			}

			if (g == gen - 1)
			{
				cout << "达到最高迭代次数!" << endl;
				printBestPath(getBestPath(), f);
			}
				

			//3、更新信息素矩阵
			updatePher();

			//4、初始化蚁群;更新信息素矩阵
			initAnts();
			pher = nextPher;
			nextPher = Matrix8d::Zero();

			g++;
		}

		finish = clock();
		duration = ((double)finish - start) / CLOCK_PER_SEC;
		cout << "  耗时:" << duration << "秒" << endl;
	}

}
  • 24
    点赞
  • 193
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
蚁群算法是一种基于模拟蚂蚁觅食行为的启发式算法,常用于解决路径规划问题。下面是一个简单的蚁群算法路径规划的C++实现: ```c++ #include <iostream> #include <cmath> #include <cstdlib> #include <ctime> using namespace std; const int MAXN = 100; // 最大城市数 const int MAXM = 10000; // 最大迭代次数 const double ALPHA = 1.0; // 信息素重要程度因子 const double BETA = 2.0; // 启发函数重要程度因子 const double RHO = 0.5; // 信息素挥发因子 const double Q = 100.0; // 常系数 const double MAX_T = 100.0; // 最大初始信息素浓度 const double MIN_T = 0.1; // 最小初始信息素浓度 int n; // 城市数 double dist[MAXN][MAXN]; // 城市间距离 double tau[MAXN][MAXN]; // 信息素浓度 int ant_num; // 蚂蚁数量 int ant_path[MAXM][MAXN]; // 蚂蚁路径 double ant_dist[MAXM]; // 蚂蚁路径长度 double best_dist; // 最短路径长度 int best_path[MAXN]; // 最短路径 double rand_double() { return rand() / (RAND_MAX + 1.0); } void init() { // 初始化距离和信息素浓度 for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { if (i == j) { dist[i][j] = 0.0; tau[i][j] = 0.0; } else { dist[i][j] = rand_double(); tau[i][j] = MAX_T; } } } // 初始化最短路径长度和路径 best_dist = 1e9; for (int i = 0; i < n; i++) { best_path[i] = i; } } void ant_move(int k) { int cur_city = rand() % n; // 随机选择起点城市 ant_path[k][0] = cur_city; for (int i = 1; i < n; i++) { double sum_prob = 0.0; double prob[MAXN]; for (int j = 0; j < n; j++) { if (j == cur_city) { prob[j] = 0.0; } else { prob[j] = pow(tau[cur_city][j], ALPHA) * pow(1.0 / dist[cur_city][j], BETA); sum_prob += prob[j]; } } double r = rand_double() * sum_prob; double tmp_sum_prob = 0.0; int next_city; for (int j = 0; j < n; j++) { if (j == cur_city) { continue; } tmp_sum_prob += prob[j]; if (tmp_sum_prob >= r) { next_city = j; break; } } ant_path[k][i] = next_city; ant_dist[k] += dist[cur_city][next_city]; cur_city = next_city; } ant_dist[k] += dist[ant_path[k][n - 1]][ant_path[k][0]]; } void update_tau() { // 信息素挥发 for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { tau[i][j] *= (1.0 - RHO); } } // 信息素更新 for (int k = 0; k < ant_num; k++) { double delta_tau = Q / ant_dist[k]; for (int i = 0; i < n; i++) { int cur_city = ant_path[k][i]; int next_city = ant_path[k][(i + 1) % n]; tau[cur_city][next_city] += delta_tau; } } } void solve() { for (int i = 0; i < MAXM; i++) { // 蚂蚁移动 for (int k = 0; k < ant_num; k++) { ant_move(k); if (ant_dist[k] < best_dist) { best_dist = ant_dist[k]; for (int j = 0; j < n; j++) { best_path[j] = ant_path[k][j]; } } } // 更新信息素 update_tau(); } } int main() { srand(time(NULL)); n = 10; ant_num = 50; init(); solve(); cout << "最短路径长度:" << best_dist << endl; cout << "最短路径:"; for (int i = 0; i < n; i++) { cout << best_path[i] << " "; } cout << endl; return 0; } ```
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值