蚁群算法代码实现

旅行商问题大都是用遗传算法求解,不过蚁群算法比它高效得多,在百度的蚁群算法吧里有人发了个注释清晰的代码,有兴趣的可以去研究一下蚁群算法和模拟退火算法,这两者都可以解决旅行商问题。而关于遗传算法和模拟退火算法,博客园里的某位牛人很清楚地介绍了,发个链接吧

遗传算法入门:http://www.cnblogs.com/heaad/archive/2010/12/23/1914725.html

模拟退火算法入门:http://www.cnblogs.com/heaad/archive/2010/12/20/1911614.html

这里发个贴吧里面的蚁群算法代码。

// AO.cpp : 定义控制台应用程序的入口点。
#pragma once

#include
#include
#include

const double ALPHA=1.0; //启发因子,信息素的重要程度
const double BETA=2.0;  //期望因子,城市间距离的重要程度
const double ROU=0.5; //信息素残留参数

const int N_ANT_COUNT=34; //蚂蚁数量
const int N_IT_COUNT=1000; //迭代次数
const int N_CITY_COUNT=51; //城市数量

const double DBQ=100.0; //总的信息素
const double DB_MAX=10e9; //一个标志数,10的9次方

double g_Trial[N_CITY_COUNT][N_CITY_COUNT];//两两城市间信息素,就是环境信息素
double g_Distance[N_CITY_COUNT][N_CITY_COUNT]; //两两城市间距离

//eil51.tsp城市坐标数据
double x_Ary[N_CITY_COUNT]=
{
   37,49,52,20,40,21,17,31,52,51,
   42,31,5,12,36,52,27,17,13,57,
   62,42,16,8,7,27,30,43,58,58,
   37,38,46,61,62,63,32,45,59,5,
   10,21,5,30,39,32,25,25,48,56,
    30
};

double y_Ary[N_CITY_COUNT]=
{
   52,49,64,26,30,47,63,62,33,21,
   41,32,25,42,16,41,23,33,13,58,
   42,57,57,52,38,68,48,67,48,27,
   69,46,10,33,63,69,22,35,15,6,
   17,10,64,15,10,39,32,55,28,37,
    40
};

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

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

//返回浮点数四舍五入取整后的浮点数
double ROUND(double dbA)
{
    return(double)((int)(dbA+0.5));
}

//定义蚂蚁类
class CAnt
{
public:
   CAnt(void);
   ~CAnt(void);

public:

    intm_nPath[N_CITY_COUNT]; //蚂蚁走的路径
    doublem_dbPathLength; //蚂蚁走过的路径长度

    intm_nAllowedCity[N_CITY_COUNT]; //没去过的城市
    intm_nCurCityNo; //当前所在城市编号
    intm_nMovedCityCount; //已经去过的城市数量

public:

    intChooseNextCity(); //选择下一个城市
    void Init();//初始化
    void Move();//蚂蚁在城市间移动
    voidSearch(); //搜索路径
    voidCalPathLength(); //计算蚂蚁走过的路径长度

};

//构造函数
CAnt::CAnt(void)
{
}

//析构函数
CAnt::~CAnt(void)
{
}

//初始化函数,蚂蚁搜索前调用
void CAnt::Init()
{

    for (inti=0;i     {
       m_nAllowedCity[i]=1; //设置全部城市为没有去过
       m_nPath[i]=0; //蚂蚁走的路径全部设置为0
    }

   //蚂蚁走过的路径长度设置为0
   m_dbPathLength=0.0;

   //随机选择一个出发城市
   m_nCurCityNo=rnd(0,N_CITY_COUNT);

   //把出发城市保存入路径数组中
   m_nPath[0]=m_nCurCityNo;

   //标识出发城市为已经去过了
   m_nAllowedCity[m_nCurCityNo]=0;

   //已经去过的城市数量设置为1
   m_nMovedCityCount=1;

}

//选择下一个城市
//返回值 为城市编号
int CAnt::ChooseNextCity()
{

    intnSelectedCity=-1; //返回结果,先暂时把其设置为-1

   //==============================================================================
   //计算当前城市和没去过的城市之间的信息素总和
   
    doubledbTotal=0.0;   
    doubleprob[N_CITY_COUNT]; //保存各个城市被选中的概率

    for (inti=0;i     {
       if (m_nAllowedCity[i] == 1) //城市没去过
       {
           prob[i]=pow(g_Trial[m_nCurCityNo][i],ALPHA)*pow(1.0/g_Distance[m_nCurCityNo][i],BETA);//该城市和当前城市间的信息素
           dbTotal=dbTotal+prob[i]; //累加信息素,得到总和
       }
       else //如果城市去过了,则其被选中的概率值为0
       {
           prob[i]=0.0;
       }
    }

   //==============================================================================
   //进行轮盘选择
    doubledbTemp=0.0;
    if (dbTotal> 0.0) //总的信息素值大于0
    {
       dbTemp=rnd(0.0,dbTotal); //取一个随机数

       for (int i=0;i        {
           if (m_nAllowedCity[i] == 1) //城市没去过
           {
               dbTemp=dbTemp-prob[i]; //这个操作相当于转动轮盘,如果对轮盘选择不熟悉,仔细考虑一下
               if (dbTemp < 0.0) //轮盘停止转动,记下城市编号,直接跳出循环

 

 {
                   nSelectedCity=i;
                   break;
               }
           }
       }
    }

   //==============================================================================
   //如果城市间的信息素非常小 ( 小到比double能够表示的最小的数字还要小 )
   //那么由于浮点运算的误差原因,上面计算的概率总和可能为0
   //会出现经过上述操作,没有城市被选择出来
   //出现这种情况,就把第一个没去过的城市作为返回结果
   
   //题外话:刚开始看的时候,下面这段代码困惑了我很长时间,想不通为何要有这段代码,后来才搞清楚。
    if(nSelectedCity == -1)
    {
       for (int i=0;i        {
           if (m_nAllowedCity[i] == 1) //城市没去过
           {
               nSelectedCity=i;
               break;
           }
       }
    }

   //==============================================================================
   //返回结果,就是城市的编号
    returnnSelectedCity;
}


//蚂蚁在城市间移动
void CAnt::Move()
{
    intnCityNo=ChooseNextCity(); //选择下一个城市

   m_nPath[m_nMovedCityCount]=nCityNo; //保存蚂蚁走的路径
   m_nAllowedCity[nCityNo]=0;//把这个城市设置成已经去过了
   m_nCurCityNo=nCityNo; //改变当前所在城市为选择的城市
   m_nMovedCityCount++; //已经去过的城市数量加1
}

//蚂蚁进行搜索一次
void CAnt::Search()
{
    Init();//蚂蚁搜索前,先初始化

   //如果蚂蚁去过的城市数量小于城市数量,就继续移动
    while(m_nMovedCityCount < N_CITY_COUNT)
    {
       Move();
    }

   //完成搜索后计算走过的路径长度
   CalPathLength();
}


//计算蚂蚁走过的路径长度
void CAnt::CalPathLength()
{

   m_dbPathLength=0.0; //先把路径长度置0
    intm=0;
    int n=0;

    for (inti=1;i     {
       m=m_nPath[i];
       n=m_nPath[i-1];
       m_dbPathLength=m_dbPathLength+g_Distance[m][n];
    }

   //加上从最后城市返回出发城市的距离
   n=m_nPath[0];
   m_dbPathLength=m_dbPathLength+g_Distance[m][n];   

}


//tsp类
class CTsp
{
public:
   CTsp(void);
   ~CTsp(void);

public:
    CAntm_cAntAry[N_ANT_COUNT]; //蚂蚁数组
    CAntm_cBestAnt; //定义一个蚂蚁变量,用来保存搜索过程中的最优结果
                                       //该蚂蚁不参与搜索,只是用来保存最优结果

public:

   //初始化数据
    voidInitData();

   //开始搜索
    voidSearch();

   //更新环境信息素
    voidUpdateTrial();


};


//构造函数
CTsp::CTsp(void)
{
}

CTsp::~CTsp(void)
{
}


//初始化数据
void CTsp::InitData()
{

   //先把最优蚂蚁的路径长度设置成一个很大的值
   m_cBestAnt.m_dbPathLength=DB_MAX;

   //计算两两城市间距离
    doubledbTemp=0.0;
    for (inti=0;i     {
       for (int j=0;j        {
           dbTemp=(x_Ary[i]-x_Ary[j])*(x_Ary[i]-x_Ary[j])+(y_Ary[i]-y_Ary[j])*(y_Ary[i]-y_Ary[j]);
           dbTemp=pow(dbTemp,0.5);
           g_Distance[i][j]=ROUND(dbTemp);
       }
    }

   //初始化环境信息素,先把城市间的信息素设置成一样
   //这里设置成1.0,设置成多少对结果影响不是太大,对算法收敛速度有些影响
    for (inti=0;i     {
       for (int j=0;j        {
           g_Trial[i][j]=1.0;
       }
    }

}

 


//更新环境信息素
void CTsp::UpdateTrial()
{
   //临时数组,保存各只蚂蚁在两两城市间新留下的信息素
    doubledbTempAry[N_CITY_COUNT][N_CITY_COUNT];
   memset(dbTempAry,0,sizeof(dbTempAry)); //先全部设置为0

   //计算新增加的信息素,保存到临时数组里
    intm=0;
    intn=0;
    for (inti=0;i     {
           for (int j=1;j            {
               m=m_cAntAry[i].m_nPath[j];
               n=m_cAntAry[i].m_nPath[j-1];
               dbTempAry[n][m]=dbTempAry[n][m]+DBQ/m_cAntAry[i].m_dbPathLength;
               dbTempAry[m][n]=dbTempAry[n][m];
           }

           //最后城市和开始城市之间的信息素
           n=m_cAntAry[i].m_nPath[0];
           dbTempAry[n][m]=dbTempAry[n][m]+DBQ/m_cAntAry[i].m_dbPathLength;
           dbTempAry[m][n]=dbTempAry[n][m];

    }

   //==================================================================
   //更新环境信息素
    for (inti=0;i     {
       for (int j=0;j        {
           g_Trial[i][j]=g_Trial[i][j]*ROU+dbTempAry[i][j]; //最新的环境信息素 =留存的信息素 + 新留下的信息素
       }
    }

}


void CTsp::Search()
{

    charcBuf[256]; //打印信息用

   //在迭代次数内进行循环
    for (inti=0;i     {
       //每只蚂蚁搜索一遍
       for (int j=0;j        {
           m_cAntAry[j].Search();
       }

       //保存最佳结果
       for (int j=0;j        {
           if (m_cAntAry[j].m_dbPathLength            {
               m_cBestAnt=m_cAntAry[j];
           }
       }

       //更新环境信息素
       UpdateTrial();

       //输出目前为止找到的最优路径的长度
       sprintf(cBuf,"\n[%d] %.0f",i+1,m_cBestAnt.m_dbPathLength);
       printf(cBuf);
    }

}

 

int main()
{
   //用当前时间点初始化随机种子,防止每次运行的结果都相同
    time_ttm;
   time(&tm);
    unsigned intnSeed=(unsigned int)tm;
   srand(nSeed);

   //开始搜索
    CTsptsp;

   tsp.InitData(); //初始化
   tsp.Search(); //开始搜索

   //输出结果
   printf("\nThe best tour is :\n");

    charcBuf[128];
    for (inti=0;i     {
       sprintf(cBuf,"d ",tsp.m_cBestAnt.m_nPath[i]+1);
       if (i % 20 == 0)
       {
           printf("\n");
       }
       printf(cBuf);
    }

   printf("\n\nPress any key to exit!");
   getchar();

    return0;
}


三、改进的蚁群算法[3]

蚁群算法具有如下一些优点:①通用性较强,能够解决很多可以转换为连通图结构的路径优化问题;②同时具有正负反馈的特点,通过正反馈特点利用局部解构造全局解,通过负反馈特点也就是信息素的挥发来避免算法陷入局部最优;③有间接通讯和自组织的特点,蚂蚁之间并没有直接联系,而是通过路径上的信息素来进行间接的信息传递,自组织性使得群体的力量能够解决问题。

但是,基本蚁群算法也存在一些缺点:①从蚁群算法的复杂度来看,该算法与其他算法相比,所需要的搜索时间较长;②该算法在搜索进行到一定程度以后,容易出现所有蚂蚁所发现的解完全一致这种“停滞现象”,使得搜索空间受到限制

3.1基于遗传学的改进蚁群算法研究

该文献[2]提出的算法弥补了基本蚁群算法中“容易陷入停滞状态”和“盲目随机搜索”的不足。文献中提出的解决办法是在每一次迭代搜索后,都把当前解和最优解进行交叉变异,这样既能搜索更大的解空间,又能使系统陷入局部最优后跳出停滞状态。

这种基于遗传学的蚁群算法(G-蚁群算法)的基本思想是在以蚁群算法为主体的基础上引入遗传算法的思想,目的是让蚁群算法经过迭代产生遗传算法所需的初始种群数据,提高种群数据的多样性。然后,遗传算法经过选择、交叉和变异操作,将处理后的数据交由蚁群算法模块进行判断处理,若满足结束条件则退出系统,否则重新进行迭代操作。

该文献中的交叉操作采用了Davis提出的OX交叉算子,即按照交叉概率Pc进行交叉操作,通过一个亲体中挑选出的子序列路径作为后代相对位置的子序列,变异操作以变异概率Pm执行变异操作,在子代序列路径中随机选择两点进行变异操作,在交叉变异操作结束后,判断当前解是否满足收敛条件,若满足收敛条件则更新当前最优解,返回蚁群算法程序,执行下一次的迭代操作。若不满足收敛条件则继续进行遗传算法的交叉变异操作。

3.2蚁群系统(Ant System,AS)

蚁群系统是Gambardella等人于1996年提出的一种修正的蚁群算法。

该算法的基本思想是:

(1)引入一个新的常量 ,其取值范围为 ,在蚂蚁k每次选择路径之前先产生一个随机数 ,且有 ,有了这个随机数之后,蚂蚁k的路径将会按照下面的规则进行。

在公式中,假设蚂蚁k当前所在的节点为i,那么蚂蚁k由节点i向点节j移动遵循的规则用公式PP表示如下:

                            

                  

 (2)对全局信息素更新策略的改进

按照最短路径更新全局信息素,其具体更新方法如下:

  式(3-2)中其中 为信息素挥发参数, ;式(3-3)中, 表示本次循环之后路径(i,j)上的信息素增量; 表示到目前为止找出的全局最优路径。

(3)对局部信息素更新策略也进行了改进

                       

                       

式(3-4)表示蚂蚁从节点i转移到节点j后,其经过的路径(i, j)上的信息素更新的方式。其中, 为常数,表示算法初始化时路径上的信息素浓度; 为可调参数, 。

3.3 精英蚁群系统(Elitist Ant System,EAS)

精英蚁群系统是对AS的第一次改进,是学者们为了解决基本蚁群算法求解大规模问题时收敛速度慢、不容易产生优化解而提出的。

该算法具体的改进策略如 :                              

 式(3-6)表示信息素增加强度定义方法,和以前的相同, 是调整最优解的影响权重的参数,适当的设置该参数的值可以提高算法的性能;式(3-7)表示精英蚂蚁给路径(i,j)上所增加的信息素量。

3.4 最大最小蚁群系统(Max-Min Ant System,MMAS)

“最大最小蚁群系统”是德国学者Thomas Stutzle 等提出的另一种通用性较强的改进的蚁群算法。该算法相比基本蚁群算法作了如下一些改进:

(1)一次循环结束后,并不是所有的蚂蚁都进行信息素更新,而是只对一只蚂蚁的信息素进行更新。这只蚂蚁只能是两类蚂蚁:在当前循环中找到最优解的蚂蚁或者是可能发现已知最优路径的蚂蚁。其信息素更新依照如下规则:            

 式 (3-9)中 根据进行信息素更新的蚂蚁的类别可以是已知的最优解的路径长度或者是本次循环中的最优解的路径长度。

(2)信息素浓度的限制。

为了防止某条路径上的信息素出现大或者过小的极端情况,设定信息素浓度区间为 。通过这种方式使得在某条路径上的信息素浓度增大到超过区间上限或者减小到低于区间下限时,算法采用强制手段对其进行调整,以此提高算法的有效性。

(3)为了在开始吸引更多的蚂蚁进行搜索,信息素浓度初始化的值不再是一个常数,而是设置为区间的上限 ,并且选定一个较小的挥发系数,以此来得到更多的搜索路径。

3.5排序蚁群系统(Rank-Based Ant System,ASrank)

排序蚁群系统引入了遗传算法中排序的观念,其改进的基本原理是:每只蚂蚁释放的信息素挥发程度受到它们各自的等级的影响,按照各自等级的高低来决定挥发程度的高低。当每只蚂蚁都生成一条路径以后,根据其各自的路径长度进行由短到长的排序,路径长度越短的代表等级越高,反之越低。因此,在更新信息素时,并不是考虑所有的蚂蚁,只考虑比较“优秀”的 只蚂蚁( 表示蚂蚁的排名)。

 式(3-11)中, 按照式(3-7)的方式计算;式(3-12)中, 表示排名在第 位的蚂蚁环游的长度。

3.6 几种改进的蚁群算法的比较

(1)对比基本蚁群算法(ACA),AS 算法、EAS 算法、MMAS 算法以及ASrank算法的共同之处就是它们都允许最优解所在的路径上的信息素进行加强,有效地利用了最优解,但是这样产生了一个弊端就是可能会导致搜索中出现停滞现象。

(2)针对避免停滞现象,几种改进算法采取的策略不同。蚁群系统通过增加局部信息素的更新来减少路径上的信息素量,以此让后面的蚂蚁选择这条路径的可能性减小;在 MMAS 算法中,采用的是给定信息素量的上下限,以此使得路径上的信息素量不会小于下限(某一最小值),也不会超过上限(某一最大值),避免所有蚂蚁选择同一条路径,也就是避免了搜索中出现停滞现象。

四、ACO应用进展及发展趋势

4.1应用的进展[7]

自从ACO在一些经典的组合规划问题如TSP和QAP等NP难的组合优化问题上取得成功以来,目前已陆续渗透到许多新的实际的工程领域中。

(1)在各种工程和工业生产中的应用[4,5]。例如采用ACO的思想来求解大规模集成电路综合布线问题。在布线过程中,各个引脚对蚂蚁的引力可根据引力函数来计算。各个线网agent根据启发策略,像蚁群一样在开关盒网格上爬行,所经之处便布上1条金属线,历经1个线网的所有引脚之后,线网便布通了。

(2) ACO在各种实际规划问题中的应用。例如在机器人路径规划中的应用[6]。机器人作为一种智能体,在复杂工作环境下的路径规划问题、多机器人之间的协作策略问题,在很大程度上类似于蚂蚁觅食优选路径以及蚂蚁群体中个体之间通过信息素形成协作。路径规划算法是实现机器人控制和导航的基础之一,试验证明ACO解决该问题有很大的优越性。

 另外,ACO在动态优化组合问题中也有应用,具体是在有向连接的网络路由和无连接网络系统路由中的应用。其他应用还包括蚂蚁人工神经网络、车辆路线问题(Vehicle Routine Prob-lem,VRP)、在图像处理和模式识别领域的应用等等。

4.2 存在的问题[8]

蚁群算法的研究成果令人瞩目,但作为一种较新的理论,它依然存在一些问题。

(1)对于大规模组合优化问题,算法的计算时间而且复杂。由于蚁群算法的时间复杂度是,因此在处理较大规模的组合优化问题时,运算量较大,时间较长。

(2)算法容易在某个或某些局部最优解的邻域附近发生停滞现象,造成早熟收敛,即搜索进行到一定程度后,所有蚂蚁发现的解完全一致,不能继续对解空间进一步搜索,不利于发现全局最优解。

(3)不能较好的解决连续域问题。

(4)由于蚁群算法中蚂蚁个体的运动过程的随机性,当群体规模设置较大时,很难在较短时间内从杂乱无章的路径中找出一条较好的路径。

(5)信息素更新策略,路径搜索策略和最优解保留策略都带有经验性,没有经过严格的理论论证。因此基本蚁群算法的求解效率不高、收敛性较差、求解结果具有较大的分散性。

4.3 发展趋势

随着蚁群算法在工程实践中应用的深入和系统复杂性的增加,需要处理的数据量也越来越大,这些问题的影响日益突出,使得单纯一到两种智能方法往往不能很好的解决问题。由于蚁群算法易与其他进化算法或者局部搜索算法结合。所以如何根据实际情况融合多种智能方法必将成为今后蚁群算法新的研究热点。目前,蚁群算法的研究大多集中在算法、模型的更新,以及软件的开发上,所处理的数据也都是静态的。硬件的运行速度要高于软件,如何利用硬件的优势,利用DSP,FPGA和PLC等硬件实现蚁群算法,并使它能够应用于实时系统将是以后研究的一个方向。

 参考文献

[1] 吴庆洪,张颖,马宗民.蚁群算法综述[A].2011.

[2] 张怀锋,宋顺林.基于遗传学的改进蚁群算法研究[M].2011.

[3] 曾云.基于改进蚁群算法的物流配送路径优化研究[D].北京:北京物资学院,2012.

[4] 庄昌文,范明钰,李春辉,虞厥邦.基于协同工作方式的一种蚁群布线系统[J].半导体学报,1999,20(5):400-406.

[5] COELLO C A C,GUTIERREZ R L Z,GARCIA B M,et al.Automated Design of Combinational Logic Circuits Using the Ant System [J].Engineering Optimization,2002,34(2):109-127

[6]樊晓平,罗 熊,易晟,张航.复杂环境下基于蚁群优化算法的机器人路径规划[J].控制与决策,2004,19(2):166-170.

[7] 叶志伟,周欣,夏彬.蚁群算法研究应用现状与展望[A].2010.

[8]戴宏发,张源原,孙国强,刘成亮.蚁群算法研究现状及发展[A].2011


你可能感兴趣的:(数学,算法)