基本原理
自然界现象:自然界蚂蚁群体在寻找食物的过程中,通过一种被称为信息素(Pheromone)的物质实现相互的间接通信,从而能够合作发现从蚁穴到食物源的最短路径。
算法抽象:通过对这种群体智能行为的抽象建模,研究者提出了蚁群优化算法(Ant Colony Optimization, ACO),为最优化问题、尤其是组合优化问题的求解提供了一强有力的手段。
蚂蚁在寻找食物的过程中往往是随机选择路径的,但它们能感知当前地面上的信息素浓度,并倾向于往信息素浓度高的方向行进。信息素由蚂蚁自身释放,是实现蚁群内间接通信的物质。由于较短路径上蚂蚁的往返时间比较短,单位时间内经过该路径的蚂蚁多,所以信息素的积累速度比较长路径快。因此,当后续蚂蚁在路口时,就能感知先前蚂蚁留下的信息,并倾向于选择一条较短的路径前行。这种正反馈机制使得越来越多的蚂蚁在巢穴与食物之间的最短路径上行进。由于其他路径上的信息素会随着时间蒸发,最终所有的蚂蚁都在最优路径上行进。
算法流程
路径构建:伪随机比例选择规则
α和β是两个预先设置的参数,用来控制启发式信息与信息素浓度作用的权重关系。
当α=0时,算法演变成传统的随机贪心算法,最邻近城市被选中的概率最大(只与路径距离有关,最短的路径被选中)。
当β=0时,蚂蚁完全只根据信息素浓度确定路径,算法将快速收敛,这样构建出的最优路径往往与实际目标有着较大的差异,算法的性能比较糟糕(与路径距离无关)。
信息素更新
算法迭代每一轮,问题空间中的所有路径上的信息素都会发生蒸发,我们为所有边上的信息素乘上一个小于1的常数。信息素蒸发是自然界本身固有的特征,在算法中能够帮助避免信息素的无限积累,使得算法可以快速丢弃之前构建过的较差的路径。
m是蚂蚁个数;ρ是信息素的蒸发率(挥发因子),规定0<ρ≤1。
tau是第k只蚂蚁在它经过的边上释放的信息素量,它等于蚂蚁k本轮构建路径长度的倒数。Ck表示路径长度,它是Rk中所有边的长度和.
Java 实现
import java.util.Arrays;
import java.util.HashSet;
/**
* Main
*/
public class Main {
public static int citySize = 10;
/* public static int[][] cities = {
{ 99, 3, 1, 2 },
{ 3, 99, 5, 4 },
{ 1, 5, 99, 2 },
{ 2, 4, 2, 99 },
}; */
public static int[][] cities = {
//0, 1, 2, 3, 4, 5, 6, 7, 8, 9
{ 0, 23, 93, 18, 40, 34, 13, 75, 50, 35 },//0
{ 23, 0, 75, 4, 72, 74, 36, 57, 36, 22 },//1
{ 93, 75, 0, 64, 21, 73, 51, 25, 74, 89 },//2
{ 18, 4, 64, 0, 55, 52, 8, 10, 67, 1 }, //3
{ 40, 72, 21, 55, 0, 43, 64, 6, 99, 74 }, //4
{ 34, 74, 73, 52, 43, 0, 43, 66, 52, 39 },//5
{ 13, 36, 51, 8, 64, 43, 0, 16, 57, 94 },//6
{ 75, 57, 25, 10, 6, 66, 16, 0, 23, 11 }, //7
{ 50, 36, 74, 67, 99, 52, 57, 23, 0, 42 },//8
{ 35, 22, 89, 1, 74, 39, 94, 11, 42, 0 }//9
};
public static int antsSize = 10;
public static int a = 1;
public static int b = 2;
public static double rou = 0.5;
public static double[][] pheromone = new double[citySize][citySize];
public static int bestLength = 0;
public static int[] bestPath = new int[citySize];
public static int batch = 50;
public static class Ant {
public int id;
public int[] path = new int[citySize];
public HashSet<Integer> Tabu = new HashSet<>();
public int length;
public String pathStr;
public Ant(int id) {
this.id = id;
for (int i : bestPath) {
this.Tabu.add(i);
}
}
}
public static Ant[] ants = new Ant[antsSize];
// 步骤一:初始化,贪心算法初始化信息素浓度
public static void init() {
HashSet<Integer> set = new HashSet<>();
bestPath[0] = 0;
set.add(0);
for (int i = 1; i < citySize; i++) {
int former = bestPath[i - 1];
int[] distance = cities[former].clone();
Arrays.sort(distance);
for (int j = 0; j < distance.length; j++) {
if (set.add(j)) {
bestPath[i] = j;
break;
}
}
bestLength += cities[former][bestPath[i]];
}
double initPheromone = antsSize / (double) bestLength;
for (int i = 0; i < citySize; i++) {
for (int j = 0; j < citySize; j++) {
pheromone[i][j] = initPheromone;
}
}
}
// 步骤2.1
public static void initCity() {
for (int i = 0; i < ants.length; i++) {
ants[i] = new Ant(i);
ants[i].path[0] = (int) Math.abs(Math.random() * 4);
ants[i].Tabu.remove(ants[i].path[0]);
}
}
// 步骤2.2
public static void chooseCity() {
for (int i = 1; i < citySize; i++) {
for (Ant ant : ants) {
double[] properority = new double[citySize];
for (int city : ant.Tabu) {
double beta = 1.0 / cities[ant.path[i]][city];
properority[city] = pheromone[ant.path[i]][city] * Math.pow(beta, b);
}
double sum = 0;
for (int j = 0; j < properority.length; j++) {
sum += properority[j];
}
properority[0] /= sum;
// 进行轮盘赌选择下一个城市
for (int j = 1; j < properority.length; j++) {
properority[j] = properority[j - 1] + properority[j] / sum;
}
double p = Math.random();
for (int j = 0; j < properority.length; j++) {
if (properority[j] >= p) {
ant.path[i] = j;
break;
}
}
//更新未走过的城市列表
ant.length += cities[ant.path[i - 1]][ant.path[i]];
ant.Tabu.remove(ant.path[i]);
}
}
// 最后还要回到原来的出发点
for (Ant ant : ants) {
ant.length += cities[ant.path[0]][ant.path[citySize - 1]];
ant.pathStr = "";
for (int i = 0; i < ant.path.length; i++) {
ant.pathStr += ant.path[i];
}
ant.pathStr += ant.path[0];
}
}
// 步骤3 更新信息素浓度
public static void updatePheromone() {
for (int i = 0; i < citySize; i++) {
for (int j = 0; j < citySize; j++) {
double tao = pheromone[i][j];
tao = tao * (1 - rou);
double taoK = 0.0;
String subString = i + j + "";
for (Ant ant : ants) {
if (ant.pathStr.contains(subString)) {
taoK = taoK + 1 / (double) ant.length;
}
}
tao += taoK;
pheromone[i][j] = tao;
}
}
}
public static void main(String[] args) {
init();
for (int i = 0; i < batch; i++) {
initCity();
chooseCity();
int lengthAvg = 0;
for (Ant ant : ants) {
lengthAvg+=ant.length;
if (bestLength >= ant.length) {
bestLength = ant.length;
bestPath = ant.path;
System.out.println("最佳路线为:" + ant.pathStr);
}
}
System.out.println(i+":平均路径长度为"+lengthAvg/antsSize+" 最短路径长度为:"+bestLength);
updatePheromone();
}
System.out.println("最短路径长度为:"+bestLength);
System.out.println("最佳路线为:" + Arrays.toString(bestPath));
}
}
改进算法
精华(精英)蚂蚁系统
精华(精英)蚂蚁系统(Elitist Ant System,EAS)是对基础AS的第一次改进,它在原AS信息素更新原则的基础上增加了一个对至今最优路径的强化手段。
基于排列的蚂蚁系统
基于排列的蚂蚁系统(rank-based Ant System,ASrank)在AS的基础上给蚂蚁要释放的信息素大小加上一个权值,进一步加大各边信息素量的差异,以指导搜索。在每一轮所有蚂蚁构建完路径后,它们将按照所得路径的长短进行排名,只有生成了至今最优路径的蚂蚁和排名在前(w-1)的蚂蚁才被允许释放信息素,蚂蚁在边(i, j)上释放的信息素 的权值由蚂蚁的排名决定。