蚁群算法及其应用

蚁群算法(C语言实现)

蚁群算法(ant colony optimization, ACO),又称蚂蚁算法,是一种用来在图中寻找优化路径的机率型算法。它由Marco Dorigo于1992年在他的博士论文中提出,其灵感来源于蚂蚁在寻找食物过程中发现路径的行为。蚁群算法是一种模拟进化算法,初步的研究表明该算法具有许多优良的性质.针对PID控制器参数优化设计问题,将蚁群算法设计的结果与遗传算法设计的结果进行了比较,数值仿真结果表明,蚁群算法具有一种新的模拟进化优化方法的有效性和应用价值。

预期的结果:

  各个蚂蚁在没有事先告诉他们食物在什么地方的前提下开始寻找食物。当一只找到食物以后,它会向环境释放一种信息素,吸引其他的蚂蚁过来,这样越来越多的蚂蚁会找到食物!有些蚂蚁并没有象其它蚂蚁一样总重复同样的路,他们会另辟蹊径,如果令开辟的道路比原来的其他道路更短,那么,渐渐,更多的蚂蚁被吸引到这条较短的路上来。最后,经过一段时间运行,可能会出现一条最短的路径被大多数蚂蚁重复着。

原理:

  为什么小小的蚂蚁能够找到食物?他们具有智能么?设想,如果我们要为蚂蚁设计一个人工智能的程序,那么这个程序要多么复杂呢?首先,你要让蚂蚁能够避开障碍物,就必须根据适当的地形给它编进指令让他们能够巧妙的避开障碍物,其次,要让蚂蚁找到食物,就需要让他们遍历空间上的所有点;再次,如果要让蚂蚁找到最短的路径,那么需要计算所有可能的路径并且比较它们的大小,而且更重要的是,你要小心翼翼的编程,因为程序的错误也许会让你前功尽弃。这是多么不可思议的程序!太复杂了,恐怕没人能够完成这样繁琐冗余的程序。

  然而,事实并没有你想得那么复杂,上面这个程序每个蚂蚁的核心程序编码不过100多行!为什么这么简单的程序会让蚂蚁干这样复杂的事情?答案是:简单规则的涌现。事实上,每只蚂蚁并不是像我们想象的需要知道整个世界的信息,他们其实只关心很小范围内的眼前信息,而且根据这些局部信息利用几条简单的规则进行决策,这样,在蚁群这个集体里,复杂性的行为就会凸现出来。这就是人工生命、复杂性科学解释的规律!那么,这些简单规则是什么呢?

下面详细说明:

1、范围:

  蚂蚁观察到的范围是一个方格世界,蚂蚁有一个参数为速度半径(一般是3),那么它能观察到的范围就是3*3个方格世界,并且能移动的距离也在这个范围之内。

2、环境:

  蚂蚁所在的环境是一个虚拟的世界,其中有障碍物,有别的蚂蚁,还有信息素,信息素有两种,一种是找到食物的蚂蚁洒下的食物信息素,一种是找到窝的蚂蚁洒下的窝的信息素。每个蚂蚁都仅仅能感知它范围内的环境信息。环境以一定的速率让信息素消失。

3、觅食规则:

  在每只蚂蚁能感知的范围内寻找是否有食物,如果有就直接过去。否则看是否有信息素,并且比较在能感知的范围内哪一点的信息素最多,这样,它就朝信息素多的地方走,并且每只蚂蚁都会以小概率犯错误,从而并不是往信息素最多的点移动。蚂蚁找窝的规则和上面一样,只不过它对窝的信息素做出反应,而对食物信息素没反应。

4、移动规则:

  每只蚂蚁都朝向信息素最多的方向移,并且,当周围没有信息素指引的时候,蚂蚁会按照自己原来运动的方向惯性的运动下去,并且,在运动的方向有一个随机的小的扰动。为了防止蚂蚁原地转圈,它会记住最近刚走过了哪些点,如果发现要走的下一点已经在最近走过了,它就会尽量避开。

5、避障规则:

  如果蚂蚁要移动的方向有障碍物挡住,它会随机的选择另一个方向,并且有信息素指引的话,它会按照觅食的规则行为。

6、播撒信息素规则:

  每只蚂蚁在刚找到食物或者窝的时候撒发的信息素最多,并随着它走远的距离,播撒的信息素越来越少。

 

  根据这几条规则,蚂蚁之间并没有直接的关系,但是每只蚂蚁都和环境发生交互,而通过信息素这个纽带,实际上把各个蚂蚁之间关联起来了。比如,当一只蚂蚁找到了食物,它并没有直接告诉其它蚂蚁这儿有食物,而是向环境播撒信息素,当其它的蚂蚁经过它附近的时候,就会感觉到信息素的存在,进而根据信息素的指引找到了食

问题:

  说了这么多,蚂蚁究竟是怎么找到食物的呢?? 在没有蚂蚁找到食物的时候,环境没有有用的信息素,那么蚂蚁为什么会相对有效的找到食物呢?这要归功于蚂蚁的移动规则,尤其是在没有信息素时候的移动规则。首先,它要能尽量保持某种惯性,这样使得蚂蚁尽量向前方移动(开始,这个前方是随机固定的一个方向),而不是原地无谓的打转或者震动;其次,蚂蚁要有一定的随机性,虽然有了固定的方向,但它也不能像粒子一样直线运动下去,而是有一个随机的干扰。这样就使得蚂蚁运动起来具有了一定的目的性,尽量保持原来的方向,但又有新的试探,尤其当碰到障碍物的时候它会立即改变方向,这可以看成一种选择的过程,也就是环境的障碍物让蚂蚁的某个方向正确,而其他方向则不对。这就解释了为什么单个蚂蚁在复杂的诸如迷宫的地图中仍然能找到隐蔽得很好的食物。

  当然,在有一只蚂蚁找到了食物的时候,大部分蚂蚁会沿着信息素很快找到食物的。但不排除会出现这样的情况:在最初的时候,一部分蚂蚁通过随机选择了同一条路径,随着这条路径上蚂蚁释放的信息素越来越多,更多的蚂蚁也选择这条路径,但这条路径并不是最优(即最短)的,所以,导致了迭代次数完成后,蚂蚁找到的不是最优解,而是次优解,这种情况下的结果可能对实际应用的意义就不大了。

  蚂蚁如何找到最短路径的?这一是要归功于信息素,另外要归功于环境,具体说是计算机时钟。信息素多的地方显然经过这里的蚂蚁会多,因而会有更多的蚂蚁聚集过来。假设有两条路从窝通向食物,开始的时候,走这两条路的蚂蚁数量同样多(或者较长的路上蚂蚁多,这也无关紧要)。当蚂蚁沿着一条路到达终点以后会马上返回来,这样,短的路蚂蚁来回一次的时间就短,这也意味着重复的频率就快,因而在单位时间里走过的蚂蚁数目就多,洒下的信息素自然也会多,自然会有更多的蚂蚁被吸引过来,从而洒下更多的信息素……;而长的路正相反,因此,越来越多地蚂蚁聚集到较短的路径上来,最短的路径就近似找到了。也许有人会问局部最短路径和全局最短路的问题,实际上蚂蚁逐渐接近全局最短路的,为什么呢?这源于蚂蚁会犯错误,也就是它会按照一定的概率不往信息素高的地方走而另辟蹊径,这可以理解为一种创新,这种创新如果能缩短路途,那么根据刚才叙述的原理,更多的蚂蚁会被吸引过来。

//蚁群算法关于简单的TSP问题求解//
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<time.h>

#define M 13  //蚂蚁的数量
#define N 144  //城市的数量
#define R 1000 //迭代次数
#define IN 1  //初始化的信息素的量
#define MAX 0x7fffffff //定义最大值

struct coordinate{
char city[15];  //城市名
int x;        //城市相对横坐标
int y;        //城市相对纵坐标
}coords[N];

double graph[N][N];  //储存城市之间的距离的邻接矩阵,自己到自己记作MAX
double phe[N][N];  //每条路径上的信息素的量
double add[N][N];  //代表相应路径上的信息素的增量
double yita[N][N]; //启发函数,yita[i][j]=1/graph[i][j]
int vis[M][N];  //标记已经走过的城市
int map[M][N];  //map[K][N]记录第K只蚂蚁走的路线
double solution[M]; //记录某次循环中每只蚂蚁走的路线的距离
int bestway[N]; //记录最近的那条路线
double bestsolution=MAX;
int NcMax; //代表迭代次数,理论上迭代次数越多所求的解更接近最优解,最具有说服力
double alpha,betra,rou,Q;

void Initialize(); //信息初始化
void Inputcoords(FILE *fp); //将文件中的坐标信息读入
void GreateGraph(); //根据坐标信息建图
double Distance(int *p); //计算蚂蚁所走的路线的总长度
void Result(); //将结果保存到out.txt中

void Initialize()
{
alpha=2; betra=2; rou=0.7; Q=5000;
    NcMax=R;
return ;
}

void Inputcoords(FILE *fp)
{
int i;
int number;
if(fp==NULL)
{
printf("Sorry,the file is not exist\n");
exit(1);
}
else
{
for(i=0; i<N; ++i)
{
fscanf(fp,"%d%s",&number,coords[i].city);
fscanf(fp,"%d,%d",&coords[i].x,&coords[i].y);
}
}
}

void GreateGraph( )
{
int i,j;
double d;
for(i=0; i<N-1; ++i)
{
graph[i][i]=MAX;   //自己到自己标记为无穷大
for(j=i+1; j<N; ++j)
{
d=(double)((coords[i].x-coords[j].x)*(coords[i].x-coords[j].x)+(coords[i].y-coords[j].y)*(coords[i].y-coords[j].y));
graph[j][i]=graph[i][j]=sqrt(d);
}
}
graph[N-1][N-1]=MAX;
return ;
}

double Distance(int *p)
{
double d=0;
int i;
for(i=0; i<N-1; ++i)
{
d+=graph[*(p+i)][*(p+i+1)];
}
d+=graph[*(p+i)][*(p)];
return d;
}

void Result()
{
FILE *fl;
int i;
fl = fopen("out.txt","a"); //将结果保存在out.txt这个文件里面
fprintf(fl,"%s\n","本次算法中的各参数如下:");
fprintf(fl,"alpha=%.3lf, betra=%.3lf, rou=%.3lf,Q=%.3lf\n",alpha,betra,rou,Q);
fprintf(fl,"%s %d\n","本次算法迭代次数为:",NcMax);
fprintf(fl,"%s %.4lf\n","本算法得出的最短路径长度为:",bestsolution);
fprintf(fl,"%s\n","本算法求得的最短路径为:");
for(i=0; i<N; ++i)
fprintf(fl,"%s → ",coords[bestway[i]].city);
fprintf(fl,"%s",coords[bestway[0]].city);
fprintf(fl,"\n\n\n");
fclose(fl);
return ;
}

int main()
{
int NC=0;
int i,j,k;
int s;
double drand,pro,psum;
FILE *fp;
Initialize();
fp = fopen("coords.txt","r+");
Inputcoords(fp);
GreateGraph();
fclose(fp);
for(i=0; i<N; ++i)
{
for(j=0; j<N; ++j)
{
phe[i][j]=IN; //信息素初始化
if(i!=j)
yita[i][j]=100.0/graph[i][j]; //期望值,与距离成反比
}
}
memset(map,-1,sizeof(map)); //把蚂蚁走的路线置空
memset(vis,0,sizeof(vis)); //0表示未访问,1表示已访问
srand(time(NULL));
while(NC++<=NcMax)
{
for(k=0; k<M; ++k)
{
    map[k][0]=(k+NC)%N;//给每只蚂蚁分配一个起点,并且保证起点在N个城市里
    vis[k][map[k][0]]=1;//将起点标记为已经访问
}
s=1;
while(s<N)
{
    for(k=0;k<M; ++k)
{
    psum=0;
   
    for(j=0;j<N; ++j)
{
    if(vis[k][j]==0)
{
   psum+=pow(phe[map[k][s-1]][j],alpha)*pow(yita[map[k][s-1]][j],betra);
}
}
   drand=(double)(rand()P00);
    drand/=5000.0; //生成一个小于1的随机数
pro=0;
    for(j=0;j<N; ++j)
{
    if(vis[k][j]==0)
   pro+=pow(phe[map[k][s-1]][j],alpha)*pow(yita[map[k][s-1]][j],betra)/psum;
   if(pro>drand)
    break;
}
vis[k][j]=1;  //将走过的城市标记起来
    map[k][s]=j; //记录城市的顺序
}
s++;
}
memset(add,0,sizeof(add));
for(k=0; k<M; ++k) //计算本次中的最短路径//
{
solution[k]=Distance(map[k]); //蚂蚁k所走的路线的总长度
if(solution[k]<bestsolution)
{
bestsolution=solution[k];
for(i=0; i<N; ++i)
bestway[i]=map[k][i];
}
}
for(k=0; k<M; ++k)
{
for(j=0; j<N-1; ++j)
{
add[map[k][j]][map[k][j+1]]+=Q/solution[k];
}
add[N-1][0]+=Q/solution[k];
}
for(i=0; i<N; ++i)
{
for(j=0; j<N; ++j)
{
phe[i][j]=phe[i][j]*rou+add[i][j];
if(phe[i][j]<0.0001) //设立一个下界
phe[i][j]=0.0001;
else if(phe[i][j]>20) //设立一个上界,防止启发因子的作用被淹没
phe[i][j]=20;
}
}
memset(vis,0,sizeof(vis));
memset(map,-1,sizeof(map));
}
Result();
printf("Result is saved in out.txt\n");
return 0;
}


    原文作者:蚁群算法
    原文地址: https://blog.csdn.net/qq_26292325/article/details/78314119
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞