蚁群算法是一种模拟进化算法,初步的研究表明该算法具有许多优良的性质.针对PID控制器参数优化设计问题,将蚁群算法设计的结果与遗传算法设计的结果进行了比较,数值仿真结果表明,蚁群算法具有一种新的模拟进化优化方法的有效性和应用价值.
蚁群算法是一种求解组合最优化问题的新型通用启发式方法,该方法具有正反馈、分布式计算和富于建设性的贪婪启发式搜索的特点。通过建立适当的数学模型,基于故障过电流的配电网故障定位变为一种非线性全局寻优问题。由柳洪平创建。
预期的结果:
各个蚂蚁在没有事先告诉他们食物在什么地方的前提下开始寻找食物。当一只找到食物以后,它会向环境释放一种信息素,吸引其他的蚂蚁过来,这样越来越多的蚂蚁会找到食物!有些蚂蚁并没有象其它蚂蚁一样总重复同样的路,他们会另辟蹊径,如果令开辟的道路比原来的其他道路更短,那么,渐渐,更多的蚂蚁被吸引到这条较短的路上来。最后,经过一段时间运行,可能会出现一条最短的路径被大多数蚂蚁重复着。
原理:
为什么小小的蚂蚁能够找到食物?他们具有智能么?设想,如果我们要为蚂蚁设计一个人工智能的程序,那么这个程序要多么复杂呢?首先,你要让蚂蚁能够避开障碍物,就必须根据适当的地形给它编进指令让他们能够巧妙的避开障碍物,其次,要让蚂蚁找到食物,就需要让他们遍历空间上的所有点;再次,如果要让蚂蚁找到最短的路径,那么需要计算所有可能的路径并且比较它们的大小,而且更重要的是,你要小心翼翼的编程,因为程序的错误也许会让你前功尽弃。这是多么不可思议的程序!太复杂了,恐怕没人能够完成这样繁琐冗余的程序。
然而,事实并没有你想得那么复杂,上面这个程序每个蚂蚁的核心程序编码不过100多行!为什么这么简单的程序会让蚂蚁干这样复杂的事情?答案是:简单规则的涌现。事实上,每只蚂蚁并不是像我们想象的需要知道整个世界的信息,他们其实只关心很小范围内的眼前信息,而且根据这些局部信息利用几条简单的规则进行决策,这样,在蚁群这个集体里,复杂性的行为就会凸现出来。这就是人工生命、复杂性科学解释的规律!那么,这些简单规则是什么呢?下面详细说明:
1、范围:
蚂蚁观察到的范围是一个方格世界,蚂蚁有一个参数为速度半径(一般是3),那么它能观察到的范围就是3*3个方格世界,并且能移动的距离也在这个范围之内。
2、环境:
蚂蚁所在的环境是一个虚拟的世界,其中有障碍物,有别的蚂蚁,还有信息素,信息素有两种,一种是找到食物的蚂蚁洒下的食物信息素,一种是找到窝的蚂蚁洒下的窝的信息素。每个蚂蚁都仅仅能感知它范围内的环境信息。环境以一定的速率让信息素消失。
3、觅食规则:
在每只蚂蚁能感知的范围内寻找是否有食物,如果有就直接过去。否则看是否有信息素,并且比较在能感知的范围内哪一点的信息素最多,这样,它就朝信息素多的地方走,并且每只蚂蚁多会以小概率犯错误,从而并不是往信息素最多的点移动。蚂蚁找窝的规则和上面一样,只不过它对窝的信息素做出反应,而对食物信息素没反应。
4、移动规则:
每只蚂蚁都朝向信息素最多的方向移,并且,当周围没有信息素指引的时候,蚂蚁会按照自己原来运动的方向惯性的运动下去,并且,在运动的方向有一个随机的小的扰动。为了防止蚂蚁原地转圈,它会记住最近刚走过了哪些点,如果发现要走的下一点已经在最近走过了,它就会尽量避开。
5、避障规则:
如果蚂蚁要移动的方向有障碍物挡住,它会随机的选择另一个方向,并且有信息素指引的话,它会按照觅食的规则行为。
7、播撒信息素规则:
每只蚂蚁在刚找到食物或者窝的时候撒发的信息素最多,并随着它走远的距离,播撒的信息素越来越少。
根据这几条规则,蚂蚁之间并没有直接的关系,但是每只蚂蚁都和环境发生交互,而通过信息素这个纽带,实际上把各个蚂蚁之间关联起来了。比如,当一只蚂蚁找到了食物,它并没有直接告诉其它蚂蚁这儿有食物,而是向环境播撒信息素,当其它的蚂蚁经过它附近的时候,就会感觉到信息素的存在,进而根据信息素的指引找到了食物。
问题:
说了这么多,蚂蚁究竟是怎么找到食物的呢?
在没有蚂蚁找到食物的时候,环境没有有用的信息素,那么蚂蚁为什么会相对有效的找到食物呢?这要归功于蚂蚁的移动规则,尤其是在没有信息素时候的移动规则。首先,它要能尽量保持某种惯性,这样使得蚂蚁尽量向前方移动(开始,这个前方是随机固定的一个方向),而不是原地无谓的打转或者震动;其次,蚂蚁要有一定的随机性,虽然有了固定的方向,但它也不能像粒子一样直线运动下去,而是有一个随机的干扰。这样就使得蚂蚁运动起来具有了一定的目的性,尽量保持原来的方向,但又有新的试探,尤其当碰到障碍物的时候它会立即改变方向,这可以看成一种选择的过程,也就是环境的障碍物让蚂蚁的某个方向正确,而其他方向则不对。这就解释了为什么单个蚂蚁在复杂的诸如迷宫的地图中仍然能找到隐蔽得很好的食物。
当然,在有一只蚂蚁找到了食物的时候,其他蚂蚁会沿着信息素很快找到食物的。
蚂蚁如何找到最短路径的?这一是要归功于信息素,另外要归功于环境,具体说是计算机时钟。信息素多的地方显然经过这里的蚂蚁会多,因而会有更多的蚂蚁聚集过来。假设有两条路从窝通向食物,开始的时候,走这两条路的蚂蚁数量同样多(或者较长的路上蚂蚁多,这也无关紧要)。当蚂蚁沿着一条路到达终点以后会马上返回来,这样,短的路蚂蚁来回一次的时间就短,这也意味着重复的频率就快,因而在单位时间里走过的蚂蚁数目就多,洒下的信息素自然也会多,自然会有更多的蚂蚁被吸引过来,从而洒下更多的信息素……;而长的路正相反,因此,越来越多地蚂蚁聚集到较短的路径上来,最短的路径就近似找到了。也许有人会问局部最短路径和全局最短路的问题,实际上蚂蚁逐渐接近全局最短路的,为什么呢?这源于蚂蚁会犯错误,也就是它会按照一定的概率不往信息素高的地方走而另辟蹊径,这可以理解为一种创新,这种创新如果能缩短路途,那么根据刚才叙述的原理,更多的蚂蚁会被吸引过来。
引申
跟着蚂蚁的踪迹,你找到了什么?通过上面的原理叙述和实际操作,我们不难发现蚂蚁之所以具有智能行为,完全归功于它的简单行为规则,而这些规则综合起来具有下面两个方面的特点:
1、多样性
2、正反馈
多样性保证了蚂蚁在觅食的时候不置走进死胡同而无限循环,正反馈机制则保证了相对优良的信息能够被保存下来。我们可以把多样性看成是一种创造能力,而正反馈是一种学习强化能力。正反馈的力量也可以比喻成权威的意见,而多样性是打破权威体现的创造性,正是这两点小心翼翼的巧妙结合才使得智能行为涌现出来了。
引申来讲,大自然的进化,社会的进步、人类的创新实际上都离不开这两样东西,多样性保证了系统的创新能力,正反馈保证了优良特性能够得到强化,两者要恰到好处的结合。如果多样性过剩,也就是系统过于活跃,这相当于蚂蚁会过多的随机运动,它就会陷入混沌状态;而相反,多样性不够,正反馈机制过强,那么系统就好比一潭死水。这在蚁群中来讲就表现为,蚂蚁的行为过于僵硬,当环境变化了,蚂蚁群仍然不能适当的调整。
既然复杂性、智能行为是根据底层规则涌现的,既然底层规则具有多样性和正反馈特点,那么也许你会问这些规则是哪里来的?多样性和正反馈又是哪里来的?我本人的意见:规则来源于大自然的进化。而大自然的进化根据刚才讲的也体现为多样性和正反馈的巧妙结合。而这样的巧妙结合又是为什么呢?为什么在你眼前呈现的世界是如此栩栩如生呢?答案在于环境造就了这一切,之所以你看到栩栩如生的世界,是因为那些不能够适应环境的多样性与正反馈的结合都已经死掉了,被环境淘汰了!
参数说明:
最大信息素:蚂蚁在一开始拥有的信息素总量,越大表示程序在较长一段时间能够存在信息素。信息素消减的速度:随着时间的流逝,已经存在于世界上的信息素会消减,这个数值越大,那么消减的越快。
错误概率表示这个蚂蚁不往信息素最大的区域走的概率,越大则表示这个蚂蚁越有创新性。
速度半径表示蚂蚁一次能走的最大长度,也表示这个蚂蚁的感知范围。
记忆能力表示蚂蚁能记住多少个刚刚走过点的坐标,这个值避免了蚂蚁在本地打转,停滞不前。而这个值越大那么整个系统运行速度就慢,越小则蚂蚁越容易原地转圈。
蚁群算法的实现
下面的程序开始运行之后,蚂蚁们开始从窝里出动了,寻找食物;他们会顺着屏幕爬满整个画面,直到找到食物再返回窝。
其中,‘F’点表示食物,‘H’表示窝,白色块表示障碍物,‘+’就是蚂蚁了。
参数说明:
最大信息素:蚂蚁在一开始拥有的信息素总量,越大表示程序在较长一段时间能够存在信息素。信息素消减的速度:随着时间的流逝,已经存在于世界上的信息素会消减,这个数值越大,那么消减的越快。
错误概率表示这个蚂蚁不往信息素最大的区域走的概率,越大则表示这个蚂蚁越有创新性。
速度半径表示蚂蚁一次能走的最大长度,也表示这个蚂蚁的感知范围。
记忆能力表示蚂蚁能记住多少个刚刚走过点的坐标,这个值避免了蚂蚁在本地打转,停滞不前。而这个值越大那么整个系统运行速度就慢,越小则蚂蚁越容易原地转圈。
/*ant.c*/ #define SPACE 0×20 #define ESC 0×1b #define ANT_CHAR_EMPTY ‘+’ #define ANT_CHAR_FOOD 153 #define HOME_CHAR ‘H’ #define FOOD_CHAR ‘F’ #define FOOD_CHAR2 ‘f’ #define FOOD_HOME_COLOR 12 #define BLOCK_CHAR 177 #define MAX_ANT 50 #define INI_SPEED 3 #define MAXX 80 #define MAXY 23 #define MAX_FOOD 10000 #define TARGET_FOOD 200 #define MAX_SMELL 5000 #define SMELL_DROP_RATE 0.05 #define ANT_ERROR_RATE 0.02 #define ANT_EYESHOT 3 #define SMELL_GONE_SPEED 50 #define SMELL_GONE_RATE 0.05 #define TRACE_REMEMBER 50 #define MAX_BLOCK 100 #define NULL 0 #define UP 1 #define DOWN 2 #define LEFT 3 #define RIGHT 4 #define SMELL_TYPE_FOOD 0 #define SMELL_TYPE_HOME 1 #i nclude “stdio.h” #i nclude “conio.h” #i nclude “dos.h” #i nclude “stdlib.h” #i nclude “dos.h” #i nclude “process.h” #i nclude “ctype.h” #i nclude “math.h” void WorldInitial(void); void BlockInitial(void); void CreatBlock(void); void SaveBlock(void); void LoadBlock(void); void HomeFoodInitial(void); void AntInitial(void); void WorldChange(void); void AntMove(void); void AntOneStep(void); void DealKey(char key); void ClearSmellDisp(void); void DispSmell(int type); int AntNextDir(int xxx,int yyy,int ddir); int GetMaxSmell(int type,int xxx,int yyy,int ddir); int IsTrace(int xxx,int yyy); int MaxLocation(int num1,int num2,int num3); int CanGo(int xxx,int yyy,int ddir); int JudgeCanGo(int xxx,int yyy); int TurnLeft(int ddir); int TurnRight(int ddir); int TurnBack(int ddir); int MainTimer(void); char WaitForKey(int secnum); void DispPlayTime(void); int TimeUse(void); void HideCur(void); void ResetCur(void); /* ————— */ struct HomeStruct { int xxx,yyy; int amount; int TargetFood; }home; struct FoodStruct { int xxx,yyy; int amount; }food; struct AntStruct { int xxx,yyy; int dir; int speed; int SpeedTimer; int food; int SmellAmount[2]; int tracex[TRACE_REMEMBER]; int tracey[TRACE_REMEMBER]; int TracePtr; int IQ; }ant[MAX_ANT]; int AntNow; int timer10ms; struct time starttime,endtime; int Smell[2][MAXX+1][MAXY+1]; int block[MAXX+1][MAXY+1]; int SmellGoneTimer; int SmellDispFlag; int CanFindFood; int HardtoFindPath; /* —– Main ——– */ void main(void) { char KeyPress; int tu; clrscr(); HideCur(); WorldInitial(); do { timer10ms = MainTimer(); if(timer10ms) AntMove(); if(timer10ms) WorldChange(); tu = TimeUse(); if(tu>=60&&!CanFindFood) { gotoxy(1,MAXY+1); printf(“Can not find food, maybe a block world.”); WaitForKey(10); WorldInitial(); } if(tu>=180&&home.amount<100&&!HardtoFindPath) { gotoxy(1,MAXY+1); printf(“God! it is so difficult to find a path.”); if(WaitForKey(10)==0×0d) WorldInitial(); else { HardtoFindPath = 1; gotoxy(1,MAXY+1); printf(” “); } } if(home.amount>=home.TargetFood) { gettime(&endtime); KeyPress = WaitForKey(60); DispPlayTime(); WaitForKey(10); WorldInitial(); } else if(kbhit()) { KeyPress = getch(); DealKey(KeyPress); } else KeyPress = NULL; } while(KeyPress!=ESC); gettime(&endtime); DispPlayTime(); WaitForKey(10); clrscr(); ResetCur(); } /* —— general sub process ———– */ int MainTimer(void) /* output: how much 10ms have pass from last time call this process */ { static int oldhund,oldsec; struct time t; int timeuse; gettime(&t); timeuse = 0; if(t.ti_hund!=oldhund) { if(t.ti_sec!=oldsec) { timeuse+=100; oldsec = t.ti_sec; } timeuse+=t.ti_hund-oldhund; oldhund = t.ti_hund; } else timeuse = 0; return (timeuse); } char WaitForKey(int secnum) /* funtion: if have key in, exit immediately, else wait ’secnum’ senconds then exit input: secnum — wait this senconds, must < 3600 (1 hour) output: key char, if no key in(exit when timeout), return NULL */ { int secin,secnow; int minin,minnow; int hourin,hournow; int secuse; struct time t; gettime(&t); secin = t.ti_sec; minin = t.ti_min; hourin = t.ti_hour; do { if(kbhit()) return(getch()); gettime(&t); secnow = t.ti_sec; minnow = t.ti_min; hournow = t.ti_hour; if(hournow!=hourin) minnow+=60; if(minnow>minin) secuse = (minnow-1-minin) + (secnow+60-secin); else secuse = secnow - secin; /* counting error check */ if(secuse<0) { gotoxy(1,MAXY+1); printf(“Time conuting error, any keyto exit…”); getch(); exit(3); } } while(secuse<=secnum); return (NULL); } void DispPlayTime(void) { int ph,pm,ps; ph = endtime.ti_hour - starttime.ti_hour; pm = endtime.ti_min - starttime.ti_min; ps = endtime.ti_sec - starttime.ti_sec; if(ph<0) ph+=24; if(pm<0) { ph–; pm+=60; } if(ps<0) { pm–; ps+=60; } gotoxy(1,MAXY+1); printf(“Time use: %d hour- %d min- %d sec “,ph,pm,ps); } int TimeUse(void) { int ph,pm,ps; gettime(&endtime); ph = endtime.ti_hour - starttime.ti_hour; pm = endtime.ti_min - starttime.ti_min; ps = endtime.ti_sec - starttime.ti_sec; if(ph<0) ph+=24; if(pm<0) { ph–; pm+=60; } if(ps<0) { pm–; ps+=60; } return(ps+(60*(pm+60*ph))); } void HideCur(void) { union REGS regs0; regs0.h.ah=1; regs0.h.ch=0×30; regs0.h.cl=0×31; int86(0×10,®s0,®s0); } void ResetCur(void) { union REGS regs0; regs0.h.ah=1; regs0.h.ch=0×06; regs0.h.cl=0×07; int86(0×10,®s0,®s0); } /* ———— main ANT programe ————- */ void WorldInitial(void) { int k,i,j; randomize(); clrscr(); HomeFoodInitial(); for(AntNow=0;AntNow<MAX_ANT;AntNow++) { AntInitial(); } /* of for AntNow */; BlockInitial(); for(k=0;k<=1;k++) /* SMELL TYPE FOOD and HOME */ for(i=0;i<=MAXX;i++) for(j=0;j<=MAXY;j++) Smell[k][j] = 0; SmellGoneTimer = 0; gettime(&starttime); SmellDispFlag = 0; CanFindFood = 0; HardtoFindPath = 0; } void BlockInitial(void) { int i,j; int bn; for(i=0;i<=MAXX;i++) for(j=0;j<=MAXY;j++) block[j] = 0; bn = 1+ MAX_BLOCK/2 + random(MAX_BLOCK/2); for(i=0;i<=bn;i++) CreatBlock(); } void CreatBlock(void) { int x1,y1,x2,y2; int dx,dy; int i,j; x1 = random(MAXX)+1; y1 = random(MAXY)+1; dx = random(MAXX/10)+1; dy = random(MAXY/10)+1; x2 = x1+dx; y2 = y1+dy; if(x2>MAXX) x2 = MAXX; if(y2>MAXY) y2 = MAXY; if(food.xxx>=x1&&food.xxx<=x2&&food.yyy>=y1&&food.yyy<=y2) return; if(home.xxx>=x1&&home.xxx<=x2&&home.yyy>=y1&&home.yyy<=y2) return; for(i=x1;i<=x2;i++) for(j=y1;j<=y2;j++) { block[j] = 1; gotoxy(i,j); putch(BLOCK_CHAR); } } void SaveBlock(void) { FILE *fp_block; char FileNameBlock[20]; int i,j; gotoxy(1,MAXY+1); printf(” “); gotoxy(1,MAXY+1); printf(“Save to [font href="http://www.phpcup.cn/tag.php?name=file" onclick="tagshow(event)" class="t_tag"]file[/font]…”,FileNameBlock); gets(FileNameBlock); if(FileNameBlock[0]==0) strcpy(FileNameBlock,“Ant.ant”); else strcat(FileNameBlock,“.ant”); if ((fp_block = fopen(FileNameBlock, “wb”)) == NULL) { gotoxy(1,MAXY+1); printf(“Creat file %s fail…”,FileNameBlock); getch(); exit(2); } gotoxy(1,MAXY+1); printf(” “); fputc(home.xxx,fp_block); fputc(home.yyy,fp_block); fputc(food.xxx,fp_block); fputc(food.yyy,fp_block); for(i=0;i<=MAXX;i++) for(j=0;j<=MAXY;j++) fputc(block[j],fp_block); fclose(fp_block); } void LoadBlock(void) { FILE *fp_block; char FileNameBlock[20]; int i,j,k; gotoxy(1,MAXY+1); printf(” “); gotoxy(1,MAXY+1); printf(“Load file…”,FileNameBlock); gets(FileNameBlock); if(FileNameBlock[0]==0) strcpy(FileNameBlock,“Ant.ant”); else strcat(FileNameBlock,“.ant”); if ((fp_block = fopen(FileNameBlock, “rb”)) == NULL) { gotoxy(1,MAXY+1); printf(“Open file %s fail…”,FileNameBlock); getch(); exit(2); } clrscr(); home.xxx = fgetc(fp_block); home.yyy = fgetc(fp_block); food.xxx = fgetc(fp_block); food.yyy = fgetc(fp_block); gotoxy(home.xxx,home.yyy); putch(HOME_CHAR); gotoxy(food.xxx,food.yyy); putch(FOOD_CHAR); food.amount = random(MAX_FOOD/3)+2*MAX_FOOD/3+1; /* food.amount = MAX_FOOD; */ home.amount = 0; home.TargetFood = (food.amount<TARGET_FOOD)?food.amount:TARGET_FOOD; for(AntNow=0;AntNow<MAX_ANT;AntNow++) { AntInitial(); } /* of for AntNow */; for(i=0;i<=MAXX;i++) for(j=0;j<=MAXY;j++) { block[j] = fgetc(fp_block); if(block[j]) { gotoxy(i,j); putch(BLOCK_CHAR); } } for(k=0;k<=1;k++) /* SMELL TYPE FOOD and HOME */ for(i=0;i<=MAXX;i++) for(j=0;j<=MAXY;j++) Smell[k]<i id="bks_6wp7flgp">[j] = 0; SmellGoneTimer = 0; gettime(&starttime); SmellDispFlag = 0; CanFindFood = 0; HardtoFindPath = 0; fclose(fp_block); } void HomeFoodInitial(void) { int randnum; int homeplace; /* 1 — home at left-up, food at right-down 2 — home at left-down, food at right-up 3 — home at right-up, food at left-down 4 — home at right-down, food at left-up */ randnum = random(100); if(randnum<25) homeplace = 1; else if (randnum>=25&&randnum<50) homeplace = 2; else if (randnum>=50&&randnum<75) homeplace = 3; else homeplace = 4; switch(homeplace) { case 1: home.xxx = random(MAXX/3)+1; home.yyy = random(MAXY/3)+1; food.xxx = random(MAXX/3)+2*MAXX/3+1; food.yyy = random(MAXY/3)+2*MAXY/3+1; break; case 2: home.xxx = random(MAXX/3)+1; home.yyy = random(MAXY/3)+2*MAXY/3+1; food.xxx = random(MAXX/3)+2*MAXX/3+1; food.yyy = random(MAXY/3)+1; break; case 3: home.xxx = random(MAXX/3)+2*MAXX/3+1; home.yyy = random(MAXY/3)+1; food.xxx = random(MAXX/3)+1; food.yyy = random(MAXY/3)+2*MAXY/3+1; break; case 4: home.xxx = random(MAXX/3)+2*MAXX/3+1; home.yyy = random(MAXY/3)+2*MAXY/3+1; food.xxx = random(MAXX/3)+1; food.yyy = random(MAXY/3)+1; break; } food.amount = random(MAX_FOOD/3)+2*MAX_FOOD/3+1; /* food.amount = MAX_FOOD; */ home.amount = 0; home.TargetFood = (food.amount<TARGET_FOOD)?food.amount:TARGET_FOOD; /* data correctness check */ if(home.xxx<=0||home.xxx>MAXX||home.yyy<=0||home.yyy>MAXY|| food.xxx<=0||food.xxx>MAXX||food.yyy<=0||food.yyy>MAXY|| food.amount<=0) { gotoxy(1,MAXY+1); printf(“World initial fail, any key to exit…”); getch(); exit(2); } gotoxy(home.xxx,home.yyy); putch(HOME_CHAR); gotoxy(food.xxx,food.yyy); putch(FOOD_CHAR); } void AntInitial(void) /* initial ant[AntNow] */ { int randnum; int i; ant[AntNow].xxx = home.xxx; ant[AntNow].yyy = home.yyy; randnum = random(100); if(randnum<25) ant[AntNow].dir = UP; else if (randnum>=25&&randnum<50) ant[AntNow].dir = DOWN; else if (randnum>=50&&randnum<75) ant[AntNow].dir = LEFT; else ant[AntNow].dir = RIGHT; ant[AntNow].speed = 2*(random(INI_SPEED/2)+1); ant[AntNow].SpeedTimer = 0; ant[AntNow].food = 0; ant[AntNow].SmellAmount[SMELL_TYPE_FOOD] = 0; ant[AntNow].SmellAmount[SMELL_TYPE_HOME] = MAX_SMELL; ant[AntNow].IQ = 1;