相信學過演算法的童鞋都聽說過一個很經典的問題:TSP問題,這個問題是NP問題,無法在多項式時間內進行求解。當問題規模較小時,還可以用窮舉的方法進行求解,但是當城市一旦變多,窮舉的時間將會指數級增加。就算採用啟發學習法搜尋,估計也很難求解。
但是這個問題是可以嘗試解決的,人工智慧給我們提供了強大的武器,也許儘管無法求得全域最優解,但我們也能得到一個很不錯的解。最主要的是,我們可以在可以忍耐的時間內得到一個解。下面給出人工智慧中對TSP問題求解的幾種方法,這也是劉峽壁老師在課堂上反覆強調的。
1、使用霍普菲爾德網路求解TSP問題
首先我們要把TSP的圖轉換為二維矩陣,就像下面這樣,這裡是一個以城市為元素的矩陣,同時每個元素都對應一個神經網路的神經元:
限制條件是:
- 每行僅有一個神經元處於活躍狀態
- 每列僅有一個神經元處於活躍狀態
- 對n個城市的問題,需要有n個神經元處於活躍狀態
例如針對五個城市,列代表到達某個城市的時間,行代表城市,則可以如下表示:接下來我們就可以按照霍普菲爾德網路求解的一般做法進行處理,我們可以如下定義能量函數:然後可以把上面的式子轉換為符合二維霍普菲爾德網路的形式:最後我們只需要隨機給定初始值,對神經元進行擾動,當網路能量達到最小時會穩定下來,我們就會得到需要的解,下面是別人針對4個城市的一個求解過程:2、使用遺傳演算法求解TSP問題遺傳演算法主要有三個操作,選擇,交叉和變異。個人覺得在求解TSP問題時,遺傳演算法的優勢就是能在全域和局部直接達到平衡,它不像窮舉演算法那樣耗費很多時間進行全域搜尋,而是能較快確定解的範圍,當確定範圍以後,它又能對局部進行比較細緻的搜尋,找到一個比較好的解。
全域探索(Exploration):交叉重組與突變是演算法全域探測能力的主要構成要素。
局部探測(Exploitation):對種群個體的選擇是演算法局部探測能力的主要構成要素。
平衡方法主要通過調節各要素的隨機變化的參數實現,較大突變機率具有較強的全域探測能力,較大的選擇機率意味著較強局部搜尋能力。
主要資料結構:
基因表示方法:城市遍曆順序,如G =[1,3,2],表示遍曆順序為1->3->2->1
class Map{private:int **distance;//用於儲存城市之間的距離public:int Distance(int city_1,city_2);//返回兩個城市的距離}class Population{private:int Max_number;int RanParNum;//每次隨機播放的父體個數double pro;//變異機率int **GenList;//用於儲存所有群體基因int **RandomParent;//用於儲存隨機播放的父母群體int **BestTwoParentFromRP;//用於儲存從RandomParent中選取的最好的兩個個體int *NewChild;//用於儲存即將進行變異和更新群體的孩子個體int *Solution;public:static int GenLen;Population(int m=100,int RPN=5,double p=0.8,int i=0,int **GL=NULL,int **RP=NULL,int **BTPFRP=NULL,int *NC=NULL,int *S=NULL):Max_number(m),RanParNum(RPN),pro(p),icount(i),GenList(GL),RandomParent(RP),BestTwoParentFromRP(BTPFRP),NewChild(NC),Solution(S){}//構造參數void randominit();//初始化群體及相關參數bool Evlote(int MaxTime);void GetParents();//選取少量個體void SelectBestParent();//從少量個體中選取兩個作為父母void ReConbination();//基因重組,並保證子個體是合法的void Swap();//交換變異int Replace();//更新群體void Display();//輸出狀態int fit(int* person);//計算適應度,這裡為對應方案的路程的倒數};//主要進化演算法:for(i=0;i<MaxTime;i++)//進化過程{GetParents();SelectBestParent();int *AnotherChild = new int[GenLen+1];//用於暫時儲存重組後的一個孩子個體,另一個儲存在Population的NewChild中,用於即將進行的個體變異ReConbination(&AnotherChild);//重組Swap();//變異Replace();//更新群體for(int i=0;i<=GenLen;i++)//將AnotherChild賦值給NewChild進行個體變異{NewChild[i]=AnotherChild[i];}delete AnotherChild;Swap();//變異Replace();//更新群體}
更多內容,請參看這裡:https://www.ads.tuwien.ac.at/raidl/tspga/TSPGA.html3、使用蟻群演算法求解TSP問題對於蟻群演算法不想過多說明,具體看這裡,Ant colony optimization algorithms 總之你可以小看一隻螞蟻,但不能小看一群螞蟻,《哥德爾、艾舍爾、巴哈集異璧之大成》這本奇書中也提到了這一點。用蟻群求解TSP問題主要是利用蟻周模型,即螞蟻每完成一次城市的遍曆後才更新所有路徑上的資訊素,求解過程如下:下面是一個四個城市的例子:這裡共有兩隻螞蟻,一隻沿著四條邊走,另一隻沿著對角線走,它們經過的距離肯定是不一樣的,由於蟻周模型中資訊素的變化是依據下面這個式子:所以前一隻螞蟻通過後在每條邊留下的資訊素為1/4=0.25 ;後一隻螞蟻為1/4.8≈0.21經過疊加以後,上下兩條邊的資訊素得到的加強,相比之下對角線的資訊素值最小。隨著時間推移和資訊素的揮發,資訊素值越大的路線上走過的螞蟻越多,而螞蟻的增多也促進了資訊素的增加,相反資訊素值越小將會越來越沒有螞蟻走,最終廢棄。針對這個例子,最終的結果就是走正方形的四條邊。
//主要資料結構:public class ACO { private Ant[] ants; //螞蟻private int antNum; //螞蟻數量private int cityNum; //城市數量private int MAX_GEN; //運行代數private float[][] pheromone; //資訊素矩陣private int[][] distance; //距離矩陣private int bestLength; //最佳長度private int[] bestTour; //最佳路徑//三個參數private float alpha; private float beta;private float rho; public void init(String filename);//從檔案初始化資料}//主要演算法:for (int g = 0; g < MAX_GEN; g++) {//螞蟻依據資訊素尋找解for (int i = 0; i < antNum; i++) {for (int j = 1; j < cityNum; j++) {ants[i].selectNextCity(pheromone);//選擇下一個城市,並從禁忌表去除該城市}ants[i].getTabu().add(ants[i].getFirstCity());//更新最好Tourif (ants[i].getTourLength() < bestLength) {bestLength = ants[i].getTourLength();for (int k = 0; k < cityNum + 1; k++) {bestTour[k] = ants[i].getTabu().get(k).intValue();}}//計算此次釋放的資訊素for (int j = 0; j < cityNum; j++) {ants[i].getDelta()[ants[i].getTabu().get(j).intValue()][ants[i].getTabu().get(j+1).intValue()] = (float) (1./ants[i].getTourLength());ants[i].getDelta()[ants[i].getTabu().get(j+1).intValue()][ants[i].getTabu().get(j).intValue()] = (float) (1./ants[i].getTourLength());}}//更新資訊素 //資訊素揮發 for(int i=0;i<cityNum;i++) for(int j=0;j<cityNum;j++) pheromone[i][j]=pheromone[i][j]*(1-rho); //資訊素更新 for(int i=0;i<cityNum;i++){ for(int j=0;j<cityNum;j++){ for (int k = 0; k < antNum; k++) {pheromone[i][j] += ants[k].getDelta()[i][j];} } } //重新初始化螞蟻for(int i=0;i<antNum;i++){ ants[i].init(distance, alpha, beta); } }
同樣這裡也存在全域和局部搜尋問題
全域探索(Exploration):揮發係數(rho)越大;資訊素的更新策略越均勻,收斂越慢,全域探索能力越強。
局部探測(Exploitation):與全域探索對應,揮發係數(rho)越小;資訊素的更新策略越不均勻,收斂越快,局部探測能力越強。
可以使用一些有效參數和更新策略選取演算法,對多組參數和更新策略進行比較、驗證,以獲得效果理想的參數和更新策略。
更多參看 http://www.cnblogs.com/biaoyu/archive/2012/09/26/2704456.html
小結:以上的三種方法分別對應於人工智慧的三個子領域(神經網路,進化計算,群智能),在這裡我們能看到使用多種方法求解某一類問題的妙處,這三種方法中究竟哪種方法最好並沒有一個權威的論斷,不過顯然既然是從不同角度出發肯定都有各自的優缺點,難分伯仲。還是要說,人工智慧是很有意思的一個領域,這個領域很寬泛,面極其廣闊,包含了太多的東西,包括認知科學,機器學習,自然語言處理,機器人學,電腦博弈,自動定理證明,模式識別,電腦視覺等等等等,希望更多的人能湧入這個領域,AI前景無限美好。