蚁群算法
一、背景
在蚂蚁觅食的过程中,单个蚂蚁的行为比较简单,但蚁群整体却可以体现一些智能的行为。
这是因为蚁群内的蚂蚁可以通过某种信息机制实现信息的传递。这种信息机制就是“信息素”。
蚂蚁会在其经过的路径上释放一种称之为“信息素”的物质,蚁群内的蚂蚁对“信息素”具有感知能力,它们会沿着“信息素”浓度较高的路径行走,而每只路过的蚂蚁都会在路上留下“信息素”,这就形成了一种类似正反馈的机制,这样经过一段时间后,整个蚁群就会沿着最短路径到达食物源了。
二、思想
将蚁群算法应用于解决优化问题的基本思路为:
- 解的表示:用蚂蚁的行走路径表示待优化问题的可行解,整个蚂蚁群体的所有路径构成待优化问题的解空间。
- 寻找最优解的过程:路径较短的蚂蚁释放的信息素量较多,随着时间的推进,较短的路径上累积的信息素浓度逐渐增高,选择该路径的蚂蚁个数也愈来愈多。(怎么表示解的偏向性?)
- 最优解:最终,整个蚂蚁会在正反馈的作用下集中到最佳的路径上,此时对应的便是待优化问题的最优解。
三、实现
蚂蚁找到最短路径要归功于信息素和环境,假设有两条路可从蚁窝通向食物,开始时两条路上的蚂蚁数量差不多:当蚂蚁到达终点之后会立即返回,距离短的路上的蚂蚁往返一次时间短,重复频率快,在单位时间里往返蚂蚁的数目就多,留下的信息素也多,会吸引更多蚂蚁过来,会留下更多信息素。而距离长的路正相反,因此越来越多的蚂蚁聚集到最短路径上来。
蚂蚁具有的智能行为得益于其简单行为规则,该规则让其具有多样性和正反馈。在觅食时,多样性使蚂蚁不会走进死胡同而无限循环,是一种创新能力;正反馈使优良信息保存下来,是一种学习强化能力。两者的巧妙结合使智能行为涌现,如果多样性过剩,系统过于活跃,会导致过多的随机运动,陷入混沌状态;如果多样性不够,正反馈过强,会导致僵化,当环境变化时蚁群不能相应调整。
四、规则
1. 感知范围
蚂蚁观察到的范围是一个方格世界,相关参数为速度半径,一般为3,可观察和移动的范围为3x3方格。
2. 环境信息
蚂蚁所在环境中有障碍物、其他蚂蚁、信息素,其中信息素包括食物信息素(找到食物的蚂蚁留下的)、窝信息素(找到窝的蚂蚁留下的),信息素以一定速率消失。
3. 觅食规则
蚂蚁在感知范围内寻找食物,如果感知到就会过去;否则朝信息素多的地方走,每只蚂蚁会以小概率犯错误,并非都往信息素最多的方向移动。蚂蚁找窝的规则类似,仅对窝信息素有反应。
4. 移动规则
蚂蚁朝信息素最多的方向移动,当周围没有信息素指引时,会按照原来运动方向惯性移动。而且会记住最近走过的点,防止原地转圈。
5. 避障规则
当蚂蚁待移动方向有障碍物时,将随机选择其他方向;当有信息素指引时,将按照觅食规则移动。
6. 散发信息素规则
在刚找到食物或者窝时,蚂蚁散发的信息素最多;当随着走远时,散发的信息素将逐渐减少。
五、特点
与其他优化算法相比,蚁群算法具有以下几个特点:
(1)采用正反馈机制,使得搜索过程不断收敛,最终逼近最优解。
(2)每个个体可以通过释放信息素来改变周围的环境,且每个个体能够感知周围环境的实时变化,个体间通过环境进行间接地通讯。
(3)搜索过程采用分布式计算方式,多个个体同时进行并行计算,大大提高了算法的计算能力和运行效率。
(4)启发式的概率搜索方式不容易陷入局部最优,易于寻找到全局最优解。
六、应用
6.1 TSP(旅行商问题)
6.1.1 分析
- 解空间:不同起点的闭合路径
- 寻找最优解的过程:尝试不同的路径
- 最优解:总长度最小的路径
运用人工蚁群算法求解TSP问题时的基本原理是:
- 将m个蚂蚁随机地放在多个城市
- 让这些蚂蚁从所在城市出发,n步(一个蚂蚁从一个城市到另外一个城市为1步)之后返回到出发城市。
-
如果m个蚂蚁所走出的m条路径中的最短者不是TSP问题的最短路程,则重复这一过程,直至寻找到满意的TSP问题的最短路径为止,流程图如下:
6.1.2 建立数学模型
(一)算法相关参数
:城市i和城市j之间边上信息素的残留强度;
:一次循环后边上信息素的增量
:一次循环之后蚂蚁k对边上信息素的贡献量
:城市,之间的能见度,反映了由城市转移到城市的启发程度
:城市到城市之间的距离
:蚂蚁从当前所在的城市到下一个城市去的概率
:禁忌表,用于存放第只蚂蚁已经走过的城市
:信息素总量,信息素总信息量为蚂蚁循环一周后向经过路径释放信息素的总量
:信息素残留系数,由于蚂蚁释放的信息量回随着时间的转移而逐渐挥发,以至于路径上的信息素不能无限递增,该系数太小时会降低算法的全局搜素能力,过大时容易使算法陷入局部最优,影响全局搜素能力。
:蚂蚁总数,在TSP问题中,每次循环当中,每只蚂蚁所走出的每条路径为TSP问题的候选解,m只蚂蚁一次循环所走出来的m条路经为TSP问题的一个解子集,所以这个解子集越大则算法的全局搜索能力越强,但是过大会使算法的收敛速度降低。如果m太小的话,算法也很容易就陷入局部最优,过早的出现停滞现象,(ps:王老师讲过曾经见到一个学生在论文中让一个蚂蚁去跑路径,老师开玩笑说,估计把这只蚂蚁就给累死了)所以选择合理的蚂蚁总数是非常重要的。
:信息启发因子,反应了蚂蚁在进行城市选择时,信息素会起多大的作用,即蚁群在路径搜素中随即性因素作用的强度。(反映了蚂蚁在从城市向城市移动时,这两个城市之间道路上所累积的信息素在指导蚂蚁选择城市的程度。)
:期望值启发式因子,反映了蚂蚁在从城市向城市转移时候期望值在指导蚁群搜素中的相对重要程度。其大小反映了蚁群在道路搜素中的先验性、确定性等因素的强弱,、的大小也会影响算法的收敛性。
参数影响分析:
常用参数取值:
(二)算法过程中的关键步骤
1、蚂蚁在选择下一个要转移的城市时候是基于概率选择的,这个概率并不是随机概率,而是其中的一些参数(由两个城市之间的信息素(信息素残留+信息启发因子)和城市能见度(城市能见度+能见度启发因子)共同决定)来决定。
假定在时刻,蚂蚁从目前所在的城市要选择转移去的下一个j城市,概率大小为:
其中表示允许蚂蚁下一步可容许去的城市的集合(也就是不在禁忌表中的城市),为边上的信息素因数,为城市,间能见度因数。
2、对任意两个城市,之间道路对应的边信息素增量按照下式进行:
其中,为蚂蚁对边上所贡献的信息素增量, 是经过边的所有蚂蚁对边的信息素量贡献,为信息素残留系数。对于的调整方式不同,可以将蚁群算法分为三种模型:蚁密模型、蚁量模型和蚁周模型。
2.1 蚁密模型(Ant-Density模型)
在蚁密模型当中,每一只蚂蚁在经过城市,时,对该边所贡献的信息素增量为常量,每个单位长度是:
2.2 蚁量模型(Ant-Quantity模型)
在蚁量模型中,一只蚂蚁k在经过城市,之间边时,对该边所贡献的信息素增量为变量,为,它与城市,之间的路径长度有关,具体为:
2.3 蚁周模型(Ant-Cycle模型)
上述两种模型,对两城市之间边上信息素贡献的增量在蚂蚁经过边的同时完成,而蚁周模型对边信息素的增量是在本次循环结束时才进行更新调整。一只蚂蚁在经过城市,时,对边上贡献的增量为每单位长度,为蚂蚁在本次循环走出路径的长度。
区别:
1.蚁周模型利用的是全局信息,即蚂蚁完成一个循环后更新所有路径上的信息素;
2.蚁量和蚁密模型利用的是局部信息,即蚂蚁完成一步后更新路径上的信息素。
(三)Java编程实现
编程分析:
-
初始化
:二维数组,初始化值为0。 城市i和城市j之间边上信息素的残留强度;
:随机数初始化。 城市,之间的能见度,反映了由城市转移到城市的启发程度
:初始化城市坐标,然后再初始化距离。 城市到城市之间的距离
:二维数组,蚂蚁数*城市数,初始化为空。 禁忌表,用于存放第只蚂蚁已经走过的城市:信息素总量,信息素总信息量为蚂蚁循环一周后向经过路径释放信息素的总量
:信息素残留系数。
:蚂蚁总数。
:信息启发因子。
:期望值启发式因子。
代码部分
终于把错误改正了,觉得主要有一下两点需要注意:使用的是“蚁周模型”(全局更新),而不是“蚁量模型”(局部更新),目前还没有找到两者主要的区别
一定要注意在蚂蚁没走过的路径上的信息素的挥发,否则结果很难收敛(但是目前还没分析出原因)
结果不收敛,找不到原因,望指教!!
import java.util.*;
public class AntColonyAlgorithm2 {
public static final int n=50;//城市数
public static final int m=50;//蚂蚁数
public static final double alpha=1.0,beta=2.0;//信息启发因子+期望值启发因子
public static final double rho=0.5,Q=100;//信息素挥发因子+信息素增量
public static void main(String[] args) {
Random random=new Random();
int[] distance_x = {
178,272,176,171,650,499,267,703,408,437,491,74,532,
416,626,42,271,359,163,508,229,576,147,560,35,714,
757,517,64,314,675,690,391,628,87,240,705,699,258,
428,614,36,360,482,666,597,209,201,492,294};
int[] distance_y = {
170,395,198,151,242,556,57,401,305,421,267,105,525,
381,244,330,395,169,141,380,153,442,528,329,232,48,
498,265,343,120,165,50,433,63,491,275,348,222,288,
490,213,524,244,114,104,552,70,425,227,331};
//随机初始化城市的位置坐标
City[] city=new City[AntColonyAlgorithm2.n];
for(int i=0;i<AntColonyAlgorithm2.n;i++){
city[i]=new City(distance_x[i],distance_y[i]);
// city[i]=new City(random.nextInt(800),random.nextInt(600));
}
//初始化城市间的距离、信息素残留、能见度
for(int i=0;i<AntColonyAlgorithm2.n;i++){
for(int j=0;j<i;j++){
//距离
// city[i].d[j]=Math.sqrt(Math.pow((city[i].x-city[j].x),2)+Math.pow((city[i].y-city[j].y),2));
city[i].d[j]=(double)(int)(Math.sqrt(Math.pow((city[i].x-city[j].x),2)+Math.pow((city[i].y-city[j].y),2))+0.5);
city[j].d[i]=city[i].d[j];
//信息素残留
city[i].tau[j]=1.0;
city[j].tau[i]=1.0;
//能见度
// city[i].eta[j]=1;
// city[j].eta[i]=city[i].eta[j];
}
city[i].d[i]=0;
// city[i].eta[i]=1;
city[i].tau[i]=1;
// System.out.println();
}
double min=1000000000;
int NC_max=100;//定义最大循环次数
for(int i=0;i<NC_max;i++){
Ant[] ants=new Ant[AntColonyAlgorithm2.m];
//每只蚂蚁都要走一条回路,然后进行比较
for(int k=0;k<AntColonyAlgorithm2.m;k++){
ants[k]=new Ant(random.nextInt(AntColonyAlgorithm2.n));
ants[k].searchPath(city);
if(ants[k].length<min){
min=ants[k].length;
}
}
//更新路径信息素含量,这种跟新方法,那些没有蚂蚁经过的路径的信息素不会挥发
// for(int j=0;j<AntColonyAlgorithm2.m;j++){
// double incre=AntColonyAlgorithm2.Q/ants[j].length;
// for(int k=0;k<AntColonyAlgorithm2.n;k++){
// city[ants[j].path[k]].tau[ants[j].path[k+1]]=city[ants[j].path[k]].tau[ants[j].path[k+1]]*AntColonyAlgorithm2.rho+incre;
// city[ants[j].path[k+1]].tau[ants[j].path[k]]=city[ants[j].path[k]].tau[ants[j].path[k+1]];
// }
// }
double[][] incre=new double[AntColonyAlgorithm2.n][AntColonyAlgorithm2.n];
for(int j=0;j< AntColonyAlgorithm2.n;j++){
for (int k=0;k<AntColonyAlgorithm2.n;k++){
incre[j][k]=0.0;
}
}
for(int j=0;j<AntColonyAlgorithm2.m;j++){
for(int k=0;k<AntColonyAlgorithm2.n;k++){
incre[ants[j].path[k]][ants[j].path[k+1]]+=AntColonyAlgorithm2.Q/ants[j].length;
incre[ants[j].path[k+1]][ants[j].path[k]]=incre[ants[j].path[k]][ants[j].path[k+1]];
}
}
for(int j=0;j< AntColonyAlgorithm2.n;j++){
for (int k=0;k<AntColonyAlgorithm2.n;k++){
city[j].tau[k]=city[j].tau[k]*AntColonyAlgorithm2.rho+incre[j][k];
}
}
//选出本轮的最佳路径
// for(int a=0;a<AntColonyAlgorithm2.m;a++){
//// System.out.print(String.format("%1$.3f", ants[a].start));
//// System.out.print(ants[a].start);
//// System.out.print(' ');
// if(ants[a].length<min){
// min=ants[a].length;
// }
// }
System.out.println();
System.out.println("第"+i+"轮,最短距离为:"+min);
}
}
}
class City{
double x,y;
//该城市到达其他城市的距离
double[] d=new double[AntColonyAlgorithm2.n];
//该城市与其他城市的边之间的信息素残留
double[] tau=new double[AntColonyAlgorithm2.n];
//能见度
double[] eta=new double[AntColonyAlgorithm2.n];
public City(){
this.x=0;
this.y=0;
}
public City(double x,double y){
this.x=x;
this.y=y;
}
}
class AntCity{
boolean unvisited;//该城市是否被访问
double p;//该城市被访问的概率
public AntCity(){
this.unvisited=true;
}
public AntCity(boolean unvisited){
this.unvisited=unvisited;
}
}
class Ant{
int start=0;//出发城市
int cindex;//蚂蚁当前位置,城市标号
int count;//记录该蚂蚁走过的城市数
double length=0;//记录蚂蚁到目前为止走过的总长度
int[] path=new int[AntColonyAlgorithm2.n+1];
//未访问过的城市列表
AntCity[] antCities=new AntCity[AntColonyAlgorithm2.n];
//初始化
public Ant(int cindex){
this.start=cindex;
this.cindex=cindex;
for(int i=0;i<AntColonyAlgorithm2.n;i++){
this.antCities[i]=new AntCity();
}
this.antCities[cindex].unvisited=false;
this.count=0;
this.path[this.count++]=cindex;
}
//选择要移动的城市
public int chooseNextCity (City[] cities) {
int to = -1;
double sum = 0;
//计算概率
for (int i = 0; i < AntColonyAlgorithm2.n; i++) {
//目前经过的城市到下一个所有城市的概率大小
if (this.antCities[i].unvisited) {
try {
this.antCities[i].p = Math.pow(( cities[cindex].tau[i]), AntColonyAlgorithm2.alpha) * Math.pow(1.0 / cities[cindex].d[i], AntColonyAlgorithm2.beta);
sum += this.antCities[i].p;
} catch (Exception e) {
System.out.println("current city: " + this.cindex + "target city:" + to);
System.exit(-1);
}
} else {
this.antCities[i].p = 0.0;
}
}
//轮盘选择城市
Random random = new Random();
double rand = random.nextDouble() * sum;
// System.out.println(rand+" "+sum);
if (sum > 0.0) {
for (int i = 0; i < AntColonyAlgorithm2.n; i++) {
if (antCities[i].unvisited) {
rand -= this.antCities[i].p;
if (rand < 0.0) {
to = i;
break;
}
}
}
}
if (to == -1) {
to = random.nextInt(AntColonyAlgorithm2.n);
while (this.antCities[to].unvisited == false) {
to = random.nextInt(AntColonyAlgorithm2.n);
}
}
return to;
}
public void move(City[] cities,int to){
//蚂蚁本次移动的距离
double lengtht=cities[this.cindex].d[to];
//蚂蚁做出移动
if(this.cindex!=to) {
//做出移动
this.cindex = to;
//更新禁忌表
this.antCities[cindex].unvisited = false;
this.antCities[cindex].p = 0;
//更新路程总长度
this.length += lengtht;
this.path[this.count++]=this.cindex;
}
}
public void searchPath(City[] cities){
while (this.count<AntColonyAlgorithm2.n){
int to=this.chooseNextCity(cities);
this.move(cities,to);
}
//计算路径总长度
this.length+=cities[this.path[this.count-1]].d[this.path[0]];
this.path[this.count]=this.start;
}
}
参考资料:
[1] https://baike.baidu.com/item/%E8%9A%81%E7%BE%A4%E7%AE%97%E6%B3%95
[2] https://www.cnblogs.com/yhgao96/articles/10887569.html
[3] https://blog.csdn.net/peachhhh/article/details/109501256