程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> c++ 使用蟻群算法解決TSP問題。

c++ 使用蟻群算法解決TSP問題。

編輯:C++入門知識

TSP問題,旅行商問題:假如一個旅行商人要拜訪n個城市,他必須選擇所有要走的路徑,路徑的限制是每個城市只能拜訪一次,而且最後要回到原來出發的城市。選擇全部路徑中的最小值。TSP具有NP計算復雜度(NP是指在非確定性圖靈機上有多項式時間算法的問題)。TSP問題是數圖論中重要的一個問題,即“已給一個n個點的完全圖,每條邊都有一個長度,求總長度最短的經過每個頂點正好一次的封閉回路”。


蟻群算法:蟻群算法是一種基於種群,通過模擬蟻群采集食物的過程的啟發式搜索算法。螞蟻通過釋放信息素來記錄路徑,並趨向於走信息素量較多的路徑來尋找最佳路徑。其中一些限制的參數。

1.信息素的總量:螞蟻擁有的信息素是有一定總量的,則在出發出信息素量較多,在一定距離後信息素會用盡,以防止其越走越遠。

2.觀測范圍:螞蟻有一定的觀測范圍,會選擇信息素較多的路徑。

3.出錯概率:螞蟻有一定的出錯概率,會不往信息素最多的區域走。這使算法不過早的收斂。

4.信息素的消減速度:信息素以一定的速度消減,使一些不合適的路徑上的信息素能消失,以減少對螞蟻的干擾。

5.記憶能力:螞蟻應該記住之前總過的路徑,以防止回頭,但是這個值較大時,會較低效率。


結合TSP問題,應做的一些改進:

螞蟻數量為m,城市數量為n,城市間的距離為D[ i ] [ j ]。螞蟻行走的過程不應該是以一定速度行走,而是以 一個時間段進行行走,在這個時間段內,每個螞蟻都能完整的從一個城市走到另一個城市。則在n個時間段後,螞蟻完成了一次回路,則n個時間段合起來為一次循環,計循環次數為K。每只螞蟻在一個循環中走過的城市編號,保存在 Path [m] [n];中,對於Path[m][0]]默認為出發點,則螞蟻無法走路徑中的點,則最後一個點不是初始點,但之後要回到初始點,要注意一下。

對於這個系統,應該選擇標記的是點還是邊,即螞蟻留下信息素的地方是邊還是點。暫時使用邊,以方便之後的信息素消散的操作。雖然對於一個點去選取其下一個路徑來說,下一個點上的信息素濃度與下一條邊上的濃度效果是相同的,但由於這是完全圖,對於其他點在選取邊時,會被其他點的最佳選取所增加的信息素所干擾。

對於信息素,在蟻群算法中有兩種信息素,一種是找到食物的螞蟻留下的信息素,一種是找到家的螞蟻留下的信息素。而在TSP中,螞蟻肯定會找到家,則只有一種信息素。則每完成一次循環後,進行信息素的更新。信息素的更新公式:

Phe [ i ][ j ] = Dis* Phe[i][j] + DPhe[i][j]

Phe 表示 每條邊上的信息素濃度,Dis為信息素消逝的速率,DPhe為本次循環內的邊ij的信息素增加量。DPhe的值為 每個螞蟻在本次循環留在路徑ij上的信息素量KDPhe的總和,對於KDPhe有三種模型,Ant-Circle System , Ant-Quantity System 和 Ant-Density System.效果最好的為第一種。具體為

Ant-Circle System : KDPhe = Q / LK 或0(當螞蟻未經過此路徑時。Q為一個常量,LK為這個循環中,此螞蟻的總路程)

Ant-Quantity System : KDPhe = Q / D[i][j] 或0

Ant-Density System .: KDPhe = Q 或 0

顯然,第一種的效果更好,當螞蟻的總路程越短,使其留下來的信息素量越高。


代碼如下:

using namespace std;
//使用10個螞蟻,進行10個城市的TSP問題求解。
const int MMax = 9999;//螞蟻數量,螞蟻數量根據城市數量決定。
const int NMax = 500;//城市數量最大數量,超過出錯
int m,n;//螞蟻數量與城市數量
const double Q = 999 ;//常量
const int K = 1000;//循環總次數
int D[NMax][NMax];//路徑長度
double Phe[NMax][NMax];//邊對應的信息素濃度
int LK;//螞蟻此循環中的總路徑長度
int Path[MMax][NMax];//記錄螞蟻的路徑,用於防止走重復的路。記錄的是點
int ant;//螞蟻當前所在點
int i,j,k,p;//循環使用
double Dis = 0.1;//每次信息素 消失的速率
int sameNum,samePhe[NMax];//每次去尋找信息素最多的邊,如初始情況,信息素量都相同時,要
//隨機的去從中選取邊。
int bugNum,bugTry[NMax];//出錯情況下進行的選擇
double bugP = 0.90;//每一次操作的出錯概率
//後來發現,出錯概率要結合螞蟻數與城市數進行判斷,而定值Q為估計距離
int start;//出發點,城市編號從0 - n-1.
double Max;//用來選取最多信息素的邊
bool Passed[NMax];//用來判斷城市是否已經經過,是否可以選取
int main()
{
	//載入數據
	fstream f("data.txt",ios::in);
	f >> n;
	if( n > NMax)//本來想直接賦值,後來又決定從文件中讀入。
		return 0;
	for(i = 0;i>D[i][j];
	for(i = 0;i < n;i++)
		D[i][i] = 0;
	f >> start;
	if(start > n-1)return 0;//沒必要的檢測,如果文件未正確書寫,發生意外的錯誤,概不負責。
	f.close();
	
	for(i = 0;i < n;i++)
		for(j = 0; j < n;j++) 
			Phe[i][j] = 1;//初始化每條邊上的信息素濃度
	for(i = 0;i< m;i++)
		Path[i][0] = start;//每只螞蟻的出發點都固定
	m = 999;//螞蟻數為城市數的2倍,後來發現不夠
	for(k = 0;k < K;k++){
		for(i = 0;i < n;i++)
			for(j = 0; j < n;j++) 
				Phe[i][j] *= Dis ;//每次循環後,進行信息素的消逝
		srand((unsigned)time(NULL)); 
		for(i = 0;i < m;i++){//對於每只螞蟻,進行一次循環	
			ant = start;
			for(j = 0;j < n;j++)
				Passed[j] = false;
			Passed[ant] = true;
			for(j = 1;j < n;j++){//每只螞蟻選n-1次邊
				Max = 0;
				sameNum  = 0 ;
				bugNum = 0;
				for(p = 0;p < n;p++)
					if(!Passed[p])
						Max = Max > Phe[ant][p] ? Max : Phe[ant][p] ;//尋找周圍邊上信息素的最大值
				for(p = 0;p < n;p++)
					if(Max == Phe[ant][p])
						if(!Passed[p])
							samePhe[sameNum++] = p;//記錄信息素取最大值時,對應的city編號和數量
				for(p = 0;p < n;p++)
					if(!Passed[p])
						bugTry[bugNum++] = p;
				if( (double)rand() /32765 < bugP)
					ant = samePhe[ rand() % sameNum ] ;//用隨機數,從中選中一條邊
				else
					ant = bugTry [ rand() % bugNum ] ;//出錯情況下,隨機選一條邊
				Passed[ant] = true;//路徑經過
				Path[i][j] = ant;//保存路徑
			}
		}
		//完成對每一個螞蟻的操作後,進行增加信息素的操作,使用Ant-Circle System 
		for(i = 0; i < m;i++){
			LK  = 0 ;
			for(j = 0; j < n-1;j++)
				LK += D[Path[i][j]][Path[i][j+1]];//計算一次循環中螞蟻的總路程
			LK += D[Path[i][j]][Path[i][0]];//回到初始點
			for(j = 0; j < n-1;j++)
				Phe[Path[i][j]][Path[i][j+1]] += Q/LK ;
			Phe[Path[i][j]][Path[i][0]] += Q/LK ;//初始點
		}
	}
	p = 0xfffff;//雖然已經完成操作,但我們要直觀的從所有現有路徑中找出最短的路徑。
	for(i = 0;i < m;i++){
		LK = 0;
		for(j = 0;j < n-1;j++)
			LK += D[Path[i][j]][Path[i][j+1]];//計算一次循環中螞蟻的總路程
		LK += D[Path[i][j]][Path[i][0]];//回到初始點
		if(LK < p){
			p = LK;
			start = i;
		}
	}
	for(i = 0;i < n; i++)
		cout << Path[start][i]<<"->";
	cout << Path[start][0]<

寫好後對測試用例進行測試,只有10個城市的最佳路徑求解,發現一開始的代碼執行後得到的結果偏差較大而且次次不同。然後對其中的一些常量進行了更改。但是發現這些常量是息息相關的,更改一個則會導致其他的常數效果變差。如當我將螞蟻數量增加時,每次循環後增加的信息素量會變大,而使每一次的新的信息素增加占信息素量的比例變得極大,而是其對之前可能有的較好路徑的敏感度降低。

決定這些常量的值是一件困難的事,測試了半天後,發現太難去尋找一個較好的搭配,而且在我選取的常量的搭配中,最多嘗試了100000次循環依舊沒有求的最佳解。完。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved