【人工智能I】蚁群算法解决TSP问题(实验)

蚁群算法

蚂蚁如何找到最短路径

在这里插入图片描述

基本思想

在这里插入图片描述

实验原理

实验步骤

在这里插入图片描述
在这里插入图片描述

算法来源

蚁群算法的基本原理来源于自然界蚂蚁觅食的最短路径原理,根据昆虫学家的观察,发现自然界的蚂蚁虽然视觉不发达,但它可以在没有任何提示的情况下找到从食物源到巢穴的最短路径,并且能在环境发生变化(如原有路径上有了障碍物)后,自适应地搜索新的最佳路径。

单个蚂蚁寻找路径

正反馈:

单个的蚂蚁为了避免自己迷路,它在爬行时,同时也会释放一种特殊的分泌物——信息素(Pheromone),而且它也能觉察到一定范围内的其它蚂蚁所分泌的信息素,并由此影响它自己的行为。当一条路上的信息素越来越多(当然,随着时间的推移会逐渐减弱),后来的蚂蚁选择这条路径的概率也就越来越大,从而进一步增加了该路径的信息素浓度,这种选择过程称为蚂蚁的自催化过程。

多样性:

同时为了保证蚂蚁在觅食的时候不至走进死胡同而无限循环,蚂蚁在寻找路径的过程中,需要有一定的随机性,虽然在觅食的过程中会根据信息素的浓度去觅食,但是有时候也有判断不准,环境影响等其他很多种情况,还有最终要的一点就是当前信息素浓度大的路径并不一定是最短的路径,需要不断的去修正,多样性保证了系统的创新能力。
正是这两点小心翼翼的巧妙结合才使得蚁群的智能行为涌现出来。

具体实现需要解决的两个首要问题

  • 如何实现单个蚂蚁寻路的过程;
  • 如何实现信息素浓度的更新。

实验代码

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<time.h>

#define CITY_NUM 20 //城市数量 
#define ANT_NUM 30  //蚁群数量 
#define TMAC 1000   //最大迭代次数 
#define ROU 0.3     //误差大小 
#define ALPHA 1     //信息素重要程度的参数 
#define BETA 4      //启发式因子重要程度的参数 
#define Q 100       //信息素残留参数 

double dis[100][100];   //距离 
double info[100][100];  //信息素矩阵 
const double mmax=1000;

double CityPos[30][2]={
	18200.0000,109550.0000,
	18200.0000,109583.3333,
	18206.3889,109581.9444,
	18207.5000,109477.7778,
	18215.8333,109700.5556,
	18216.6667,109700.0000,
	18220.5556,109510.2778,
	18223.8889,109552.2222,
	18229.7222,109528.3333,
	18233.3333,109533.3333,
	18266.6667,109566.6667,
	18266.6667,109666.6667,
	18271.3889,109705.8333,
	18278.3333,109730.2778,
	18279.4444,109675.2778,
	18281.1111,109480.8333,
	18281.3889,109684.1667,
	18283.3333,109400.0000,
	18283.8889,109569.7222,
	18283.8889,109705.5556,
	18325.8333,109528.3333,
	18327.2222,109256.3889,
	18327.7778,109247.5000,
	18332.5000,109490.2778,
	18333.3333,109450.0000,
	18335.2778,109323.0556,
	18336.1111,109731.3889,
	18344.7222,109452.2222,
	18347.2222,109638.8889,
	18347.7778,109203.3333
};

//返回指定范围内的随机整数 
int rnd(int n,int p){
    return n+(p-n)*rand()/(RAND_MAX+1);
} 

//返回指定范围内的随机浮点数 
double rnd(double d,double b){
    double dbTemp=rand()/((double)RAND_MAX+1.0);
	return d+dbTemp*(b-d);
}

struct Ant{
    int Path[CITY_NUM];//蚂蚁走的路径 
	double length;//路径总长度 
	int vis[CITY_NUM];//走过城市标记 
	int cur_cityno;//当前城市 
	int moved_cnt;//已走的数量  	
	//初始化 
	void Init(){
	    memset(vis,0,sizeof(vis));
		length=0;
		cur_cityno=rnd(0,CITY_NUM);//随机选择一个出发城市 
		Path[0]=cur_cityno;
		vis[cur_cityno]=1;
		moved_cnt=1;
	}
	//选择下一个城市 
	//返回值为城市编号 
	int chooseNextCity(){
	   int nSelectedCity=-1;//返回结果,先暂时把其设置为-1 
	   //计算当前城市和没去过的城市之间的信息素总和 
	   double dbTotal=0.0;
	   double prob[CITY_NUM];//保存各个城市被选中的概率 
	   for(int i=0;i<CITY_NUM;i++){ 
	        if(!vis[i]){
			    prob[i]=pow(info[cur_cityno][i],ALPHA)*pow(1.0/dis[cur_cityno][i],BETA);
				dbTotal+=prob[i];
			}
			else{
			   prob[i]=0;
			}
		}
		//进行轮盘选择 
		double dbTemp=0.0;
		if (dbTotal>0.0){//总的信息素值大于0 
		    dbTemp=rnd(0.0,dbTotal);
		    for (int i=0;i<CITY_NUM;i++){
			   if (!vis[i]){ 
			       dbTemp-=prob[i];
				   if (dbTemp<0.0){
				        nSelectedCity=i;
						break;
					}
				}
			}
		}
		//如果城市间的信息素非常小(小到比double能够表示的最小的数字还要小)      
	    //出现这种情况,就把第一个没去过的城市作为返回结果 
		if (nSelectedCity==-1){
		    for (int i=0;i<CITY_NUM;i++){
			    if(!vis[i]){//城市没去过 
				    nSelectedCity=i;
					break;
				}
		    }
		}
		return nSelectedCity;    
	}     
	
	//蚂蚁在城市间移动    
	void Move(){        
	    int nCityno=chooseNextCity();//选择下一个城市        
		Path[moved_cnt]=nCityno;//保存蚂蚁走的路径
		vis[nCityno]=1;//把这个城市设置成已经去过 
		cur_cityno=nCityno;//更新已走路径长度 
		length+=dis[Path[moved_cnt-1]][Path[moved_cnt]];
		moved_cnt++;
	}
	
	//蚂蚁进行搜索一次    
	void Search(){        
	    Init();        
		//如果蚂蚁去过的城市数量小于城市数量,就继续移动 
		while(moved_cnt<CITY_NUM){            
		    Move();        
		}        
		length+=dis[Path[CITY_NUM-1]][Path[0]];    
	}
};  

struct TSP{    
    Ant ants[ANT_NUM];    
	Ant ant_best;    
	//定义一群蚂蚁
	//保存最好结果的蚂蚁
	void Init(){        
	    //初始化为最大值        
		ant_best.length=mmax;        
		puts("cal dis");        
		//计算两两城市间距离        
		for(int i=0;i<CITY_NUM;i++){            
		    for(int j=0;j<CITY_NUM;j++){                
			    double temp1=CityPos[j][0]-CityPos[i][0];                
				double temp2=CityPos[j][1]-CityPos[i][1];                
				dis[i][j]=sqrt(temp1*temp1+temp2*temp2);            
			}        
		}        
		//初始化环境信息素        
		puts("init info");        
		for(int i=0;i<CITY_NUM;i++){            
		    for(int j=0;j<CITY_NUM;j++){                
			    info[i][j]=1.0;            
			}       
		}   
	}    
	
	//更新信息素,当前每条路上的信息素等于过去保留的信息素    
	//加上每个蚂蚁这次走过去剩下的信息素    
	void Updateinfo(){     
		double tmpinfo[CITY_NUM][CITY_NUM];        
		memset(tmpinfo,0,sizeof(tmpinfo));        
		int m=0;        
		int n=0;       
		//遍历每只蚂蚁        
		for(int i=0;i<ANT_NUM;i++){           
			for(int j=1;j<CITY_NUM;j++){                
			    m=ants[i].Path[j];                
				n=ants[i].Path[j-1];              
				tmpinfo[n][m]=tmpinfo[n][m]+Q/ants[i].length;                
				tmpinfo[m][n]=tmpinfo[n][m];            
			}            
			//最后城市和开始城市之间的信息素            
			n=ants[i].Path[0];            
			tmpinfo[n][m]=tmpinfo[n][m]+Q/ants[i].length;            
			tmpinfo[m][n]=tmpinfo[n][m];        
		}          
		
		//更新环境信息素        
		for(int i=0;i<CITY_NUM;i++){
		    for(int j=0;j<CITY_NUM;j++){
			   //最新的环境信息素 = 留存的信息素 + 新留下的信息素                
			   info[i][j]=info[i][j]*ROU+tmpinfo[i][j];
			}
		}
	}
	
	//寻找路径,迭代TMAC次 
	void Search(){ 
	    for(int i=0;i<TMAC;i++){
		    printf("current iteration times %d\n", i);
			for(int j=0;j<ANT_NUM;j++){                
			    ants[j].Search();
			}
			//保存最佳结果 
			for(int j=0;j<ANT_NUM;j++){
			    if(ant_best.length>ants[j].length){
				    ant_best=ants[j];
				}
			}
			//更新环境信息素 
			Updateinfo();
			printf("current minimum length %lf\n",ant_best.length);
		}
	}
};

int main(){
	TSP tsp;   //初始化蚁群 
	tsp.Init();//开始查找 
	tsp.Search();
	printf("\n利用蚁群求得30个城市最短路径为 : \n");
	for(int i=0;i<CITY_NUM;i++){
		if(i!=CITY_NUM-1){
			printf("%d-->",tsp.ant_best.Path[i]);
		}
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值