旅行商问题大都是用遗传算法求解,不过蚁群算法比它高效得多,在百度的蚁群算法吧里有人发了个注释清晰的代码,有兴趣的可以去研究一下蚁群算法和模拟退火算法,这两者都可以解决旅行商问题。而关于遗传算法和模拟退火算法,博客园里的某位牛人很清楚地介绍了,发个链接吧
遗传算法入门: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 <iostream>
- #include <math.h>
- #include <time.h>
- 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)
- {
- return nLow+(nUpper-nLow)*rand()/(RAND_MAX+1);
- }
- //返回指定范围内的随机浮点数
- double rnd(double dbLow,double dbUpper)
- {
- double dbTemp=rand()/((double)RAND_MAX+1.0);
- return dbLow+dbTemp*(dbUpper-dbLow);
- }
- //返回浮点数四舍五入取整后的浮点数
- double ROUND(double dbA)
- {
- return (double)((int)(dbA+0.5));
- }
- //定义蚂蚁类
- class CAnt
- {
- public:
- CAnt(void);
- ~CAnt(void);
- public:
- int m_nPath[N_CITY_COUNT]; //蚂蚁走的路径
- double m_dbPathLength; //蚂蚁走过的路径长度
- int m_nAllowedCity[N_CITY_COUNT]; //没去过的城市
- int m_nCurCityNo; //当前所在城市编号
- int m_nMovedCityCount; //已经去过的城市数量
- public:
- int ChooseNextCity(); //选择下一个城市
- void Init(); //初始化
- void Move(); //蚂蚁在城市间移动
- void Search(); //搜索路径
- void CalPathLength(); //计算蚂蚁走过的路径长度
- };
- //构造函数
- CAnt::CAnt(void)
- {
- }
- //析构函数
- CAnt::~CAnt(void)
- {
- }
- //初始化函数,蚂蚁搜索前调用
- void CAnt::Init()
- {
- for (int i=0;i<N_CITY_COUNT;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()
- {
- int nSelectedCity=-1; //返回结果,先暂时把其设置为-1
- //==============================================================================
- //计算当前城市和没去过的城市之间的信息素总和
- double dbTotal=0.0;
- double prob[N_CITY_COUNT]; //保存各个城市被选中的概率
- for (int i=0;i<N_CITY_COUNT;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;
- }
- }
- //==============================================================================
- //进行轮盘选择
- double dbTemp=0.0;
- if (dbTotal > 0.0) //总的信息素值大于0
- {
- dbTemp=rnd(0.0,dbTotal); //取一个随机数
- for (int i=0;i<N_CITY_COUNT;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<N_CITY_COUNT;i++)
- {
- if (m_nAllowedCity[i] == 1) //城市没去过
- {
- nSelectedCity=i;
- break;
- }
- }
- }
- //==============================================================================
- //返回结果,就是城市的编号
- return nSelectedCity;
- }
- //蚂蚁在城市间移动
- void CAnt::Move()
- {
- int nCityNo=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
- int m=0;
- int n=0;
- for (int i=1;i<N_CITY_COUNT;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:
- CAnt m_cAntAry[N_ANT_COUNT]; //蚂蚁数组
- CAnt m_cBestAnt; //定义一个蚂蚁变量,用来保存搜索过程中的最优结果
- //该蚂蚁不参与搜索,只是用来保存最优结果
- public:
- //初始化数据
- void InitData();
- //开始搜索
- void Search();
- //更新环境信息素
- void UpdateTrial();
- };
- //构造函数
- CTsp::CTsp(void)
- {
- }
- CTsp::~CTsp(void)
- {
- }
- //初始化数据
- void CTsp::InitData()
- {
- //先把最优蚂蚁的路径长度设置成一个很大的值
- m_cBestAnt.m_dbPathLength=DB_MAX;
- //计算两两城市间距离
- double dbTemp=0.0;
- for (int i=0;i<N_CITY_COUNT;i++)
- {
- for (int j=0;j<N_CITY_COUNT;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 (int i=0;i<N_CITY_COUNT;i++)
- {
- for (int j=0;j<N_CITY_COUNT;j++)
- {
- g_Trial[i][j]=1.0;
- }
- }
- }
- //更新环境信息素
- void CTsp::UpdateTrial()
- {
- //临时数组,保存各只蚂蚁在两两城市间新留下的信息素
- double dbTempAry[N_CITY_COUNT][N_CITY_COUNT];
- memset(dbTempAry,0,sizeof(dbTempAry)); //先全部设置为0
- //计算新增加的信息素,保存到临时数组里
- int m=0;
- int n=0;
- for (int i=0;i<N_ANT_COUNT;i++) //计算每只蚂蚁留下的信息素
- {
- for (int j=1;j<N_CITY_COUNT;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 (int i=0;i<N_CITY_COUNT;i++)
- {
- for (int j=0;j<N_CITY_COUNT;j++)
- {
- g_Trial[i][j]=g_Trial[i][j]*ROU+dbTempAry[i][j]; //最新的环境信息素 = 留存的信息素 + 新留下的信息素
- }
- }
- }
- void CTsp::Search()
- {
- char cBuf[256]; //打印信息用
- //在迭代次数内进行循环
- for (int i=0;i<N_IT_COUNT;i++)
- {
- //每只蚂蚁搜索一遍
- for (int j=0;j<N_ANT_COUNT;j++)
- {
- m_cAntAry[j].Search();
- }
- //保存最佳结果
- for (int j=0;j<N_ANT_COUNT;j++)
- {
- if (m_cAntAry[j].m_dbPathLength < m_cBestAnt.m_dbPathLength)
- {
- m_cBestAnt=m_cAntAry[j];
- }
- }
- //更新环境信息素
- UpdateTrial();
- //输出目前为止找到的最优路径的长度
- sprintf(cBuf,"\n[%d] %.0f",i+1,m_cBestAnt.m_dbPathLength);
- printf(cBuf);
- }
- }
- int main()
- {
- //用当前时间点初始化随机种子,防止每次运行的结果都相同
- time_t tm;
- time(&tm);
- unsigned int nSeed=(unsigned int)tm;
- srand(nSeed);
- //开始搜索
- CTsp tsp;
- tsp.InitData(); //初始化
- tsp.Search(); //开始搜索
- //输出结果
- printf("\nThe best tour is :\n");
- char cBuf[128];
- for (int i=0;i<N_CITY_COUNT;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();
- return 0;
- }
SendMessage(m_hWnd,USERMSG_UPDATE,0,0); //注意:此处不能为PostMessage函数,因为需要每一代更新界面,要等消息处理完成才能返回
自定义用户消息如下:
#define USERMSG_UPDATE WM_USER+2
2. 在窗口消息处理函数WndProc中添加代码如下:
1)对WM_PAINT进行相应的代码如下:
- hdc = BeginPaint(hWnd,&ps);
- // TODO: 在此添加任意绘图代码...
- for(int i=0;i<51;i++)
- {
- RECT rect;
- rect.left = x_Ary[i]-2;
- rect.right = x_Ary[i]+2;
- rect.top = y_Ary[i]-2;
- rect.bottom = y_Ary[i]+2;
- Rectangle(hdc,rect.left,rect.top,rect.right,rect.bottom);
- HGDIOBJ brush = GetStockObject(DC_BRUSH);
- COLORREF col(RGB(124,252,0));
- SetDCBrushColor(hdc,col);
- FillRect(hdc,&rect,(HBRUSH)brush);
- char ch[3];
- memset(ch,0,3);
- sprintf(ch,"%d",i+1);
- TextOutA(hdc,x_Ary[i]+2,y_Ary[i]+2,LPCSTR(ch),strlen(ch));
- }
- EndPaint(hWnd, &ps);
2)鼠标左键消息WM_LBUTTONDOWN响应:
- RECT rect;
- GetClientRect(hWnd,&rect);
- InvalidateRect(hWnd,&rect,true);
- UpdateWindow(hWnd);
- time_t tm;
- time(&tm);
- unsigned int nSeed=(unsigned int)tm;
- srand(nSeed);
- //开始搜索
- CTsp tsp(hWnd);
- tsp.InitData(); //初始化
- tsp.Search(); //开始搜索
3)对用户自定义消息USERMSG_UPDATE的响应:
- RECT rect;
- GetClientRect(hWnd,&rect);
- InvalidateRect(hWnd,&rect,true);
- UpdateWindow(hWnd);
- hdc = GetDC(hWnd);
- HGDIOBJ pen = GetStockObject(DC_PEN);
- COLORREF col(RGB(124,252,0));
- SetDCPenColor(hdc,col);
- for(int i=0;i<50;i++)
- {
- int n = bestPath[i];
- int m = bestPath[i+1];
- MoveToEx(hdc,x_Ary[n],y_Ary[n],NULL);
- LineTo(hdc,x_Ary[m],y_Ary[m]);
- }
- MoveToEx(hdc,x_Ary[bestPath[50]],y_Ary[bestPath[50]],NULL);
- LineTo(hdc,x_Ary[bestPath[0]],y_Ary[bestPath[0]]);
- ReleaseDC(hWnd, hdc);
4)添加全局变量bestPath,用以保存找到的最佳路径,并在TSP类的search函数中每一代进行更新
- for(int k=0;k<N_CITY_COUNT;k++)
- {
- bestPath[k] = m_cBestAnt.m_nPath[k];
- }
最后程序的运行结果如下: