1. 介绍
蚁群算法是一种群体智能算法,模拟了蚂蚁寻找食物时的行为,通过蚂蚁之间的信息交流和合作,最终实现全局最优解的寻找【是否找得到和迭代次数有关】。
蚁群算法的基本思想是将搜索空间看作一个由节点组成的图,每个节点代表一种可能的解决方案,而边则代表两个节点之间的关联关系。在这个图中,一只蚂蚁在搜索过程中会沿着路径移动,每经过一个节点,它会留下信息素,这些信息素会吸引其他蚂蚁跟随它的路径。同时,蚂蚁在移动的过程中也会依据信息素的浓度,更有可能选择路径上信息素浓度较高的节点。
蚂蚁群算法的核心是信息素的更新和挥发。信息素的更新是指当一只蚂蚁找到一个更优的解决方案时,它会在路径上留下更多的信息素,这样其他蚂蚁就更有可能跟随这个路径,从而加速搜索过程。信息素的挥发则是指在一定的时间间隔内,信息素会自然挥发,这样可以避免信息素积累过多导致搜索过早收敛。
蚂蚁群算法是一种高效的全局优化算法,广泛应用于组合优化、信号处理、机器学习、图形图像处理等领域。
2. 主体公式
蚁群算法中,蚂蚁到达每个点的概率是根据该点的信息素浓度和距离来计算的。具体地说,设当前蚂蚁所在的城市为
i
i
i ,已经访问过的城市集合为
J
J
J ,未访问过的城市集合为
K
K
K ,则蚂蚁由
i
i
i 到达城市
j
j
j 的概率可以用如下公式计算:
p
i
,
j
=
[
τ
i
,
j
]
α
[
η
i
,
j
]
β
∑
k
∈
K
[
τ
i
,
k
]
α
[
η
i
,
k
]
β
p_{i,j}=\frac{[\tau_{i,j}]^{\alpha}[\eta_{i,j}]^{\beta}}{\sum\limits_{k\in K} [\tau_{i,k}]^{\alpha}[\eta_{i,k}]^{\beta}}
pi,j=k∈K∑[τi,k]α[ηi,k]β[τi,j]α[ηi,j]β
其中, τ i , j \tau_{i,j} τi,j 表示从城市 i i i 到城市 j j j 的信息素浓度, η i , j \eta_{i,j} ηi,j 表示从城市 i i i 到城市 j j j 的距离的倒数, α \alpha α 和 β \beta β 分别表示信息素重要程度因子和距离重要程度因子,通常 α \alpha α 和 β \beta β 的值都为正数。在选择下一个要访问的城市时,蚂蚁会根据这个概率进行随机选择,不难发现,信息素浓度越大,城市间的距离越短,对应的概率就会越大。
对于一只蚂蚁走过的路径上的每条边 $ (i,j)$ ,它在该边上留下的信息素增量为:
Δ
τ
i
j
=
q
L
k
\Delta \tau_{ij} = \dfrac{q}{L_k}
Δτij=Lkq
其中,
q
q
q 是信息素增加强度系数,
L
k
L_k
Lk 表示第
k
k
k 只蚂蚁完成路径的长度。
参数代码形式如下👇:
// 距离矩阵
private double[][] distance;
// 信息素矩阵
private double[][] pheromone;
// 城市数量
private int cityNum;
// 蚂蚁数量
private int antNum;
// 最大迭代次数
private int maxGen;
// 信息素因子
private double alpha;
// 距离重要程度因子
private double beta;
// 信息素挥发因子
private double rho;
// 信息素增加强度系数
private double q;
// 最优路径
private int[] bestTour;
// 最优路径长度
private double bestLength;
3. 算法实现步骤
-
初始化信息素矩阵,初始时各路径上信息素浓度相同
// 初始化信息素矩阵 private void initPheromone() { // 信息素初始值 double initPheromone = 1.0 / (cityNum * distance[0][1]); for (int i = 0; i < cityNum; i++) { for (int j = 0; j < cityNum; j++) { // 各条路径的信息素浓度 pheromone[i][j] = initPheromone; } } }
-
第一只蚂蚁从随机一个城市出发,综合信息素浓度以及到达各城市的距离,计算得到公式 p i j p_{ij} pij 的分子,存储在数组 p p p 中,同时将分母用累加和 s u m sum sum 存储
一个圆形转盘区域标记为 [ 0 , 1 ) [0,1) [0,1) , ∑ p i j = 1 \sum p_{ij} = 1 ∑pij=1
-
选择下一个城市采用轮盘赌的方式,即产生一个在 [ 0 , 1 ) [0,1) [0,1) 随机数 r a n d rand rand 作为圆形转盘的指针指向的位置,利用 t m p tmp tmp 【 p i j p_{ij} pij 累加和】锁定轮盘上的各个区域,一旦 t m p ≥ r a n d tmp \ge rand tmp≥rand ,说明当前城市就是下一个目的地,将此城市在 v i s i t e d visited visited 中标记,同时将其加入访问顺序 t o u r tour tour 中。
用此方法访问所有节点后,返回范围顺序 t o u r tour tour
// 蚂蚁移动 // pheromone : 信息素矩阵 private int[] antMove(double[][] pheromone) { // 存储依次到达的城市序号 int[] tour = new int[cityNum]; boolean[] visited = new boolean[cityNum]; Random random = new Random(); // 生成一个不大于 cityNum 的伪随机整数 int start = random.nextInt(cityNum); // 从 start 城市出发,此城市标记为已访问 visited[start] = true; // 第一个访问 start 城市 tour[0] = start; for (int i = 1; i < cityNum; i++) { // 当前蚂蚁所在的城市 int curCity = tour[i - 1]; // p 存放概率公式的分子部分 double[] p = new double[cityNum]; // sum 存放概率公式的分母部分 double sum = 0.0; for (int j = 0; j < cityNum; j++) { // 城市 j 没有被访问过 if (!visited[j]) { p[j] = Math.pow(pheromone[curCity][j], alpha) * Math.pow(1.0 / distance[curCity][j], beta); sum += p[j]; } } // 用 [0,1] 范围的伪随机数代替轮盘针转到的位置 double rand = random.nextDouble() ; double tmp = 0.0; int nextCity = 0; for (int j = 0; j < cityNum; j++) { if (!visited[j]) { // 每个概率 p[j]/sum 代表轮盘上的一块区域 // tmp 累积值代表指针到的位置 tmp += p[j]/sum; // 超过 rand ,说明来到了这个区间,即当前城市就是目标城市 if (tmp >= rand) { nextCity = j; break; } } } // 标记城市为已访问 visited[nextCity] = true; // 记录蚂蚁的下个城市目的地 tour[i] = nextCity; } return tour; }
-
由于知道城市访问顺序 t o u r tour tour ,因此不难得到访问总距离 t o u r L e n g t h tourLength tourLength ,如果这个总距离小于 b e s t T o u r bestTour bestTour ,说明这是更优的解法,因此更新 b e s t T o u r bestTour bestTour 和 b e s t L e n g t h bestLength bestLength
-
蚂蚁访问完各个城市后开始更新各路径上的信息素增量 Δ τ \Delta\tau Δτ , Δ τ \Delta\tau Δτ 由是信息素增加强度系数 Q Q Q,和 L L L 蚂蚁完成路径的长度决定
-
当所有蚂蚁都将城市访问完毕后,将信息素增量 Δ τ \Delta\tau Δτ 更新至原路径上的信息素中,同时还需要考虑信息素的挥发,最终得到第一次迭代后的信息素矩阵情况
// 更新信息素矩阵 private void updatePheromone() { // 蚂蚁在经过路径上留下的信息增量 double[][] deltaPheromone = new double[cityNum][cityNum]; for (int i = 0; i < antNum; i++) { // 单只蚂蚁根据当前信息素矩阵而得到的访问顺序 int[] tour = antMove(pheromone); // 当前蚂蚁访问的长度 double tourLength = getTourLength(tour); // 说明可以优化 if (tourLength < bestLength) { // 更新最短路径 bestLength = tourLength; // 由于有了最短路径,因此存储的访问路径也需要更新 System.arraycopy(tour, 0, bestTour, 0, cityNum); } for (int j = 0; j < cityNum - 1; j++) { int city1 = tour[j]; int city2 = tour[j + 1]; // 信息素增量Δτ,只要有蚂蚁经过就会变多 deltaPheromone[city1][city2] += q / tourLength; deltaPheromone[city2][city1] = deltaPheromone[city1][city2]; } deltaPheromone[tour[cityNum - 1]][tour[0]] += q / tourLength; deltaPheromone[tour[0]][tour[cityNum - 1]] = deltaPheromone[tour[cityNum - 1]][tour[0]]; } for (int i = 0; i < cityNum; i++) { for (int j = 0; j < cityNum; j++) { // 原来的信息素挥发了部分,同时新的信息素又增加了一些 pheromone[i][j] = pheromone[i][j] * (1.0 - rho) + deltaPheromone[i][j]; } } }
-
多次迭代后可以得到最优路径,存放在 b e s t T o u r bestTour bestTour 中,最短路径长度存放于 b e s t L e n g t h bestLength bestLength
// 运行蚁群算法 public void run() { initPheromone(); // 迭代次数 for (int i = 0; i < maxGen; i++) { updatePheromone(); } System.out.println("迭代"+maxGen+"次后,最优的路径长度:" + bestLength); System.out.println("最优路径为:"); for (int i = 0; i < cityNum; i++) { System.out.print(bestTour[i] + " "); } }
-
样例测试
public class AntColonyOptimizationTest { public static void main(String[] args) { // 城市数目 int cityNum = 4; // 蚂蚁数目 int antNum = 20; // 迭代次数 int maxGen = 100; // 信息素重要程度因子 double alpha = 1.0; // 距离的重要程度因子 double beta = 5.0; // 信息素挥发因子 double rho = 0.5; // 信息素增加强度系数 double q = 1; // 初始化距离矩阵【无向图】 double[][] distance= {{0,58,87,64},{58,0,53,49},{87,53,0,88},{64,49,88,0}}; System.out.println("距离矩阵distance为:"); for (double[] ds : distance) { System.out.println(Arrays.toString(ds)); } AntColonyOptimization aco=new AntColonyOptimization(distance, antNum, maxGen, alpha, beta, rho, q); aco.run(); } }
4. 完整代码
4.1 AntColonyOptimization类
package aca;
import java.util.Random;
public class AntColonyOptimization {
// 距离矩阵
private double[][] distance;
// 信息素矩阵
private double[][] pheromone;
// 城市数量
private int cityNum;
// 蚂蚁数量
private int antNum;
// 最大迭代次数
private int maxGen;
// 信息素因子
private double alpha;
// 距离重要程度因子
private double beta;
// 信息素挥发因子
private double rho;
// 信息素增加强度系数
private double q;
// 最优路径
private int[] bestTour;
// 最优路径长度
private double bestLength;
public AntColonyOptimization(double[][] distance, int antNum, int maxGen, double alpha, double beta, double rho,
double q) {
this.distance = distance;
this.cityNum = distance.length;
this.antNum = antNum;
this.maxGen = maxGen;
this.alpha = alpha;
this.beta = beta;
this.rho = rho;
this.q = q;
this.pheromone = new double[cityNum][cityNum];
this.bestTour = new int[cityNum];
this.bestLength = Double.MAX_VALUE;
}
// 初始化信息素矩阵
private void initPheromone() {
// 信息素初始值
double initPheromone = 1.0 / (cityNum * distance[0][1]);
for (int i = 0; i < cityNum; i++) {
for (int j = 0; j < cityNum; j++) {
// 各条路径的信息素浓度
pheromone[i][j] = initPheromone;
}
}
}
/**
*
* @param pheromone 信息素矩阵
* @return 蚂蚁依次访问城市的顺序
*/
// 蚂蚁移动
private int[] antMove(double[][] pheromone) {
// 存储依次到达的城市序号
int[] tour = new int[cityNum];
boolean[] visited = new boolean[cityNum];
Random random = new Random();
// 生成一个不大于 cityNum 的伪随机整数
int start = random.nextInt(cityNum);
// 从 start 城市出发,此城市标记为已访问
visited[start] = true;
// 第一个访问 start 城市
tour[0] = start;
for (int i = 1; i < cityNum; i++) {
// 当前蚂蚁所在的城市
int curCity = tour[i - 1];
// p 存放概率公式的分子部分
double[] p = new double[cityNum];
// sum 存放概率公式的分母部分
double sum = 0.0;
for (int j = 0; j < cityNum; j++) {
// 城市 j 没有被访问过
if (!visited[j]) {
p[j] = Math.pow(pheromone[curCity][j], alpha) * Math.pow(1.0 / distance[curCity][j], beta);
sum += p[j];
}
}
// 用 [0,1] 范围的伪随机数代替轮盘针转到的位置
double rand = random.nextDouble() ;
double tmp = 0.0;
int nextCity = 0;
for (int j = 0; j < cityNum; j++) {
if (!visited[j]) {
// 每个概率 p[j]/sum 代表轮盘上的一块区域
// tmp 累积值代表指针到的位置
tmp += p[j]/sum;
// 超过 rand ,说明来到了这个区间,即当前城市就是目标城市
if (tmp >= rand) {
nextCity = j;
break;
}
}
}
// 标记城市为已访问
visited[nextCity] = true;
// 记录蚂蚁的下个城市目的地
tour[i] = nextCity;
}
return tour;
}
// 更新信息素矩阵
private void updatePheromone() {
// 蚂蚁在经过路径上留下的信息增量
double[][] deltaPheromone = new double[cityNum][cityNum];
for (int i = 0; i < antNum; i++) {
// 单只蚂蚁根据当前信息素矩阵而得到的访问顺序
int[] tour = antMove(pheromone);
// 当前蚂蚁访问的长度
double tourLength = getTourLength(tour);
// 说明可以优化
if (tourLength < bestLength) {
// 更新最短路径
bestLength = tourLength;
// 由于有了最短路径,因此存储的访问路径也需要更新
System.arraycopy(tour, 0, bestTour, 0, cityNum);
}
for (int j = 0; j < cityNum - 1; j++) {
int city1 = tour[j];
int city2 = tour[j + 1];
// 信息素增量Δτ,只要有蚂蚁经过就会变多
deltaPheromone[city1][city2] += q / tourLength;
deltaPheromone[city2][city1] = deltaPheromone[city1][city2];
}
deltaPheromone[tour[cityNum - 1]][tour[0]] += q / tourLength;
deltaPheromone[tour[0]][tour[cityNum - 1]] = deltaPheromone[tour[cityNum - 1]][tour[0]];
}
for (int i = 0; i < cityNum; i++) {
for (int j = 0; j < cityNum; j++) {
// 原来的信息素挥发了部分,同时新的信息素又增加了一些
pheromone[i][j] = pheromone[i][j] * (1.0 - rho) + deltaPheromone[i][j];
}
}
}
/**
*
* @param tour 蚂蚁访问顺序
* @return 当前访问顺序对应的路径长度
*/
// 获取路径长度
private double getTourLength(int[] tour) {
double tourLength = 0.0;
for (int i = 0; i < cityNum - 1; i++) {
// 将路径中城市间的距离相加
tourLength += distance[tour[i]][tour[i + 1]];
}
// 最后还要返回起点
tourLength += distance[tour[cityNum - 1]][tour[0]];
return tourLength;
}
// 运行蚁群算法
public void run() {
initPheromone();
// 迭代次数
for (int i = 0; i < maxGen; i++) {
updatePheromone();
}
System.out.println("迭代"+maxGen+"次后,最优的路径长度:" + bestLength);
System.out.print("最优路径为:");
for (int i = 0; i < cityNum; i++) {
System.out.print(bestTour[i] + " ");
}
}
}
4.2 AntColonyOptimizationTest类
package aca;
import java.util.Arrays;
import aca.AntColonyOptimization;
public class AntColonyOptimizationTest {
public static void main(String[] args) {
// 城市数目
int cityNum = 4;
// 蚂蚁数目
int antNum = 20;
// 迭代次数
int maxGen = 100;
// 信息素重要程度因子
double alpha = 1.0;
// 距离的重要程度因子
double beta = 5.0;
// 信息素挥发因子
double rho = 0.5;
// 信息素增加强度系数
double q = 1;
// 初始化距离矩阵【无向图】
double[][] distance= {{0,58,87,64},{58,0,53,49},{87,53,0,88},{64,49,88,0}};
// double[][] distance = new double[cityNum][cityNum];
// for (int i = 0; i < cityNum; i++) {
// for (int j = i; j < cityNum; j++) {
// if (i == j) {
// distance[i][j] = 0.0;
// } else {
// double d = Math.random() * 100;
// // 城市 i 到城市 j 的距离
// distance[i][j] = d;
// distance[j][i] = d;
// }
// }
// }
System.out.println("距离矩阵distance为:");
for (double[] ds : distance) {
System.out.println(Arrays.toString(ds));
}
AntColonyOptimization aco=new AntColonyOptimization(distance, antNum, maxGen, alpha, beta, rho, q);
aco.run();
}
}