標籤:sni 尋找 自然界 越來越大 output tsp out win 機率
一、論述
1、演算法來源
蟻群演算法的基本原理來源於自然界螞蟻覓食的最短路徑原理,根據昆蟲學家的觀察,發現自然界的螞蟻雖然視覺不發達,但它可以在沒有任何提示的情況下找到從食物源到巢穴的最短路徑,並且能在環境發生變化(如原有路徑上有了障礙物)後,自適應地搜尋新的最佳路徑。
2、單個螞蟻尋找路徑
正反饋:
單個的螞蟻為了避免自己迷路,它在爬行時,同時也會釋放一種特殊的分泌物——資訊素(Pheromone),而且它也能覺察到一定範圍內的其它螞蟻所分泌的資訊素,並由此影響它自己的行為。當一條路上的資訊素越來越多(當然,隨著時間的推移會逐漸減弱),後來的螞蟻選擇這條路徑的機率也就越來越大,從而進一步增加了該路徑的資訊素濃度,這種選擇過程稱為螞蟻的自催化過程。
多樣性:
同時為了保證螞蟻在覓食的時候不至走進死胡同而無限迴圈,螞蟻在尋找路徑的過程中,需要有一定的隨機性,雖然在覓食的過程中會根據資訊素的濃度去覓食,但是有時候也有判斷不準,環境影響等其他很多種情況,還有最終要的一點就是當前資訊素濃度大的路徑並不一定是最短的路徑,需要不斷的去修正,多樣性保證了系統的創新能力。
正是這兩點小心翼翼的巧妙結合才使得蟻群的智能行為湧現出來。
3、具體實現需要解決的兩個首要問題
(1)如何?單個螞蟻尋路的過程
(2)如何?資訊素濃度的更新
二、具體實現
代碼如下所示:
#include <iostream>#include <algorithm>#include <cstring>#include <windows.h>#include <stdio.h>#include <stdlib.h>#include <math.h>#include <time.h>using namespace std;/*int CityPos[10][2]= {{87,7},{91,38},{83,46},{71,44},{64,60},{68,58},{83,69}, {87,76},{74,78},{71,71}}; //10個城市的座標*/unsigned seed=(unsigned)time(0);//原型:void srand(unsigned seed);//30個城市的座標int CityPos[30][2]={{87,7},{91,38},{83,46},{71,44},{64,60},{68,58},{83,69},{87,76},{74,78},{71,71},{58,69},{54,62},{51,67},{37,84},{41,94},{2,99},{7,64},{22,60},{25,62},{18,54},{4,50},{13,40},{18,40},{24,42},{25,38},{41,26},{45,21},{44,35},{58,35},{62,32}};#define CITY_NUM 30 //城市數量#define ANT_NUM 30 //蟻群數量#define TMAC 1000 //迭代最大次數#define ROU 0.5 //誤差大小#define ALPHA 1 // 資訊素重要程度的參數#define BETA 4 // 啟發學習法因子重要程度的參數#define Q 100 //資訊素殘留參數const int maxn = 100;double dis[maxn][maxn]; //距離double info[maxn][maxn]; //資訊素矩陣double E[CITY_NUM][CITY_NUM]; //啟發因子矩陣int vis[CITY_NUM][CITY_NUM];double Bestlength;double ans[CITY_NUM];const double mmax = 10e9;//返回指定範圍內的隨機整數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));}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; //printf("Init %d \n", cur_cityno); } //選擇下一個城市 //傳回值 為城市編號 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() { //puts("update info"); 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++) { //puts("****");// for (int j = 0; j < CITY_NUM; j++) {// printf("%d ", ants[i].Path[j]);// } //puts(""); for (int j = 1; j < CITY_NUM; j++) { m = ants[i].Path[j]; n = ants[i].Path[j-1]; //printf("%d %d\n", m, n); 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(){ //freopen("output.txt", "w", stdout); srand(seed); TSP tsp; //初始化蟻群 tsp.Init(); //開始尋找 tsp.Search(); puts("The Minimum length route is :\n"); for (int i = 0; i < CITY_NUM; i++) { if (i != 0 && i % 20 == 0) { puts(""); } printf("%d ", tsp.ant_best.Path[i]); } return 0;}
運算結果
(1)選擇老師所給10個城市的資料,結果如下所示,經過50次迭代就能得到最優解,而且演算法相當穩定,多次嘗試都是50次就能得到最優解,相比於遺傳演算法的500次迭代在時間上有了很大的改進。
(2)選擇老師所給的30個城市的資料,經過1000次迭代過程能得到一個非常較優的解,結果穩定在425左右,與最優解424.869292非常接近,並且偶爾會得到最優解,相比於遺傳演算法迭代10000次結果在700左右,最佳結果有了很大的飛躍。
總結:
(1)蟻群演算法相比於遺傳演算法針對TSP問題的求解有了很大的改進,我覺得很大的原因是蟻群在尋找路徑過程中的正反饋調節,正是根據資訊素濃度的調整,才使得路徑長度很容易收斂。同時由於用了輪盤賭演算法,有保證了存在一定的多樣性,受初始值的影響不大。
(2)蟻群演算法也有一定的局限性,雖然相對於遺傳演算法有了很大的改進,但是有時候仍然找不到最優解,只能找到次優解。後續要具體最佳化的話,就要調節相關參數來不斷進行改進。
蟻群演算法解決TSP問題