在以前的文章(簡單遺傳算法MATLAB實現)中已經介紹過,遺傳算法是一種基於達爾文生物進化論的啟發式算法,它的核心思想就是優勝劣汰,適應性好的個體將在生存競爭中獲得更大的生存機會,而適應差的將更有可能在競爭中失敗,從而遭到淘汰。
1. 生物進化
圖1用了一個非常形象的實例,來表現進化機制對生物繁衍的作用。
圖1 眼睛的進化(摘自http://blog.csdn.net/zzwu/article/details/561588)
可以設想,曾有一個時期動物就根本沒有眼睛。那時,動物在它們的環境中航行完全是靠嗅覺和觸覺來躲避掠食它們的動物。他們也相當擅長於這樣做,因為他們靠這樣已經歷了成千上萬個世代。在那個時候,鼻子大和手腳長的男性是受女孩子們歡迎的。然而,突然有一天,當兩個動物配對時,一個基因突變發生在為皮膚細胞提供的藍圖上。這一突變使其后代在他們的頭上發育出了一個具有相當光敏效應的細胞,使其后代能足夠識別周圍環境是亮的還是暗的。這樣就給他帶來了一個微小的優點,因為,如果一種食肉動物,比如一只鷹,來到了某個范圍以內,則它將阻擋了光線,這時,該動物就會感覺得到,就可迅速跑到隱蔽的地方去躲藏起來。另外,這種皮膚細胞還能指示現在是晚上或白天,或告訴他現在是在地面之上或地面之下,這些信息在捕食和吸取營養時都能為它提供方便。你能看到這一新型皮膚細胞將使這一動物與群體中其余的動物相比,具備了稍多的優點,從而更容易獲得異性的青睞,因此也就有更多的生存和繁殖的機會。過了一段時間,由於進化機制的作用,許多動物的染色體中都會出現具有光敏皮膚細胞的基因。現在,如果你再作一些外推,想象這一光敏細胞基因得到了進一步的有利突變,則你能看到,經過許多許多世代后,光敏細胞經過分化形成為一個區域;這個區域不斷變大,產生出一些更為確定的特征,例如形成一個晶體,或產生能區別顏色的視覺細胞;還可以想象,一個突變使某個動物由一個光敏區域改變為兩個光敏區域,由此就使那個動物有了立體視覺。立體視覺對一個生物體來說是一個巨大的進步,因為這能精確告訴他目標離開他有多遠。當然,你也可以把會對眼睛產生不利影響的突變裝入同樣那些基因。但這里重要的一點是,這樣生長出來的后代將不會和已具備改進型眼睛的堂表親戚們那樣取得成功,它們最終將會滅絕。只有成功的基因才會得到繼承。觀察自然界中存在的任何特征就能發現,它們的進化都是利用無數微小的突變發展而來的,且它們都是對擁有者有利。
人類的發展就是進化的一個極為成功的案例。人類從猿猴發展而來(雖然有些生物學家對此並不認同),經過了不能直立行走->可以直立行走->制造工具->創做語言->協同工作->組建社會->……->,這些都是進化的作用。
2. 遺傳算法
遺傳算法的概念最早是由Bagley J.D 於1967年提出的。后來Michigan大學的J.H.Holland教授於1975年開始對遺傳算法(Genetic Algorithm, GA)的機理進行系統化的研究。遺傳算法是對達爾文生物進化理論的簡單模擬,其遵循“適者生存”、“優勝略汰”的原理。遺傳算法模擬一個人工種群的進化過程,並且通過選擇、雜交以及變異等機制,種群經過若干代以后,總是達到最優(或近最優)的狀態。
自從遺傳算法被提出以來,其得到了廣泛的應用,特別是在函數優化、生產調度、模式識別、神經網絡、自適應控制等領域,遺傳算法更是發揮了重大的作用,大大提高了問題求解的效率。遺傳算法也是當前“軟計算”領域的重要研究課題。
模式(Schema)理論是對遺傳算法數學機理的深入解釋。所謂一個模式指的是包含了符號*的字符串,例如一個二進制字符串1**000000*1就可以表達一個模式,*可以是0或者1,所以該模式可以匹配23 = 6個不同的二進制字符串實例。
模式理論可以用公式1解釋。
模式定理:
在遺傳算子選擇、交叉以及變異的作用下,具有階數低、長度短、平均適應度高於種群平均適應度的模式在子代中將以指數級增長。
還有兩個定義:
例如:
0*******1**的階為2,定義距為7.
在基本遺傳算法(或者簡單遺傳算法)中,有三個基本的遺傳操作(operator),即選擇操作(Selection)、交叉操作(Crossover)以及變異操作(Mutation)。關於這些操作詳細的過程請參見簡單遺傳算法MATLAB實現。
3. TSP問題遺傳算法實現
關於TSP問題的定義,在上一篇(蟻群算法Java實現以及TSP問題蟻群算法求解)中已有詳細的說明和定義,在此不予重述。
用遺傳算法求解TSP問題,最困難的是要解決三個方面的問題,即編碼,交叉操作以及變異操作。從文獻中發現這些問題主要有一下幾種解決方法,見表1
表1 主要的編碼方式,交叉操作以及變異操作
3.1 編碼
TSP問題編碼一般有五種種不同的方式:
- 基於二進制的編碼
- 基於矩陣的編碼
- 基於鄰接的編碼
- 基於索引(Ordinary)的編碼
- 基於路徑的編碼
基於二進制的編碼是一種傳統的編碼方式,但是這種方式的編碼,在經過遺傳操作以后很難保證后代還是一個可行解,還需要另外的修正操作;基於矩陣的編碼、鄰接的編碼以及索引的編碼均比較復雜,並且需要占用大量的內存,應用的不是很廣泛;目前最普遍用的編碼方式就是基於路徑的編碼方式,因為它最直觀最容易理解,操作起來也比較方便。以下將主要介紹基於路徑的編碼方式以及相關的遺傳操作。
例如:
一個TSP問題,有六個城市{1,2,3,4,5,6},那么用基於路徑的編碼(1 3 5 4 6 2)就可以表示為一個個體。
3.2 交叉操作
下面主要介紹幾種常見的交叉操作的方式。
3.2.1 部分匹配法(Partially Matching Crossover, PMX)
以兩個父代個體為例:(1 2 3 4 5 6 7 8)和(2 4 6 8 7 5 3 1),隨機選擇兩個交叉的點,假如第一個點為位置4,第二個交叉點為位置6,那么在兩個點之間的位置將進行交叉,其它的位置進行復制或者用相匹配的數進行替換。在此實例中,第一個父代個體中4 5 6被選中,第二個父代個體中,8 7 5被選中。那么4 與8,5與7,6與5相匹配。匹配過程和如圖2所示。
圖2 PMX交叉操作
首先將4 5 6與8 7 5分別加入到子代2和子代1中相應的位置,然后將其他位置上的數字直接復制到相應的后代中,如果該數字已經在該子代中已經存在,則用相應的匹配法則進行替換,例如子代1中將7復制進去的時候,發現7已經存在在子代中,通過查找相應的匹配法則,發現,7與5匹配,然后復制5,又發現5也已經存在在該子代中,在此查找匹配法則,發現5與6匹配,將6復制,6不存在在該子代中,所以可以將6復制進去,如此反復,知道子代中城市的數目達到定義的長度,該子代創建完成。
3.2.2 循環交叉法(Cycle Crossover, CX)
依舊以兩個父代個體為例:(1 2 3 4 5 6 7 8)和(2 4 6 8 7 5 3 1),在構造子代的過程中,首先從父代1中選取第一個元素,然后查找父代2中對應位置的元素在在父代1中的位置,將這個位置對應的元素假如到子代1中,如此過程反復,直到找到一個元素對應的父代2元素等於起始元素,次循環結束,然后將剩余未填充的位置用父代2相應的位置的元素進行填充,這樣就可以完成子代1的創建。子代2創建方法類似。CX操作過程如圖2所示。
圖3 CX操作
首先選擇父代1中的第一個元素1,將它加入到子代1中,然后檢查父代2中對應位置,該位置元素為2,在父代1中查找該元素,該元素在父代1中的位置為2, 將2加入到子代1的第二個位置,再次檢查父代2中第二個位置的元素,它為4,然后查找它在父代1中的位置為4,將4加入到子代1的第四個位置,然后將其加入到子代1中對應的位置4,在檢查父代2中該位置的元素,它為8,查找到它在父代1中的位置為8,然后將其加入到子代1中位置8,再次查找父代2中位置8的元素,它為1,等於起始選擇的元素,循環結束,然后將子代1中3,5,6,7元素為空的位置,用父代2對應位置的元素進行填充,這些元素為6,7,5,3,所以得到的子代1為(1 2 5 4 7 5 3 8)。同樣的方法,得到子代2為(2 4 3 8 5 6 7 1)。
3.2.3 次序交叉法1(Order Crossover, OX1)
還以兩個相同的父代個體為例:(1 2 3 4 5 6 7 8)和(2 4 6 8 7 5 3 1),隨機選擇兩個交叉的點,假如第一個點為位置3,第二個交叉點為位置5,那么在兩個點之間的位置將進行交叉。然后從第二個交叉點開始,將原來相應的父代按照順序進行填充,如果選擇的元素已經存在在該子代中,跳過該元素,選擇下一個元素,這種過程反復進行,知道所有的城市都被選擇一次。在此實例中,第一個父代個體中3 4 5被選中,第二個父代個體中,6 8 7被選中。匹配過程和如圖4所示。
圖4 OX1操作
首先,將選擇的位串進行替換,即3 4 5換到子代2中,6 8 7換到子代1中。現在從第二個交叉點開始,將父代1中的基因插入到子代1中, 所以,1 插入到第六個位置,2 插入到第七個位置,3插入到第八個位置,第二個交叉點后面已經填充滿,將剩下的插入到第一個插入點前面的位置,所以4插入到第一個位置,5插入到第二個位置。這樣,子代1構建完成。同樣地方法,可以構建子代2.當遇到子代中已經存在的基因,就跳到下一個。
3.2.4 次序交叉法2(Order Crossover, OX2)
還以兩個相同的父代個體為例:(1 2 3 4 5 6 7 8)和(2 4 6 8 7 5 3 1),隨機選擇幾個交叉的點,假如第一個點為位置2,第二個交叉點為位置是3,第三個為位置6。首先找到父代2中相應位置的基因在父代1中位置,然后將其用父代2中選擇的位置的基因進行替換插入到子代1中相應的位置中。然后將其它的位置用OX1相似的方法進行填充。這樣就可以得到子代1.同樣,替換角色,可以得到子代2。具體的操作過程如圖5所示。
圖5 OX2操作
首先找到父代2中第,2,3以及第六個位置的基因,它們為4,6和5,這三個基因在父代1中的位置為4,5和6,將它們替換成4,6,5,加入到子代1中相應的位置,然后將父代1中的基因按照順序插入到子代1中,如果該基因已經存在在位串中,則跳過該基因,繼續下一個。這樣就可以構建完子代1。子代2也可以以相同的方法構造完成。
3.2.5 基於位置的交叉法(Position Based Crossover, POS)
還以兩個相同的父代個體為例:(1 2 3 4 5 6 7 8)和(2 4 6 8 7 5 3 1),隨機選擇幾個交叉的點,假如第一個點為位置2,第二個交叉點為位置是3,第三個為位置6。將兩個父代中這些選擇基因按照順序交換,並且插入到子代1和2中。然后對其它位置的基因,按照順序插入。具體操作過程如圖6所示。
圖6 POS操作
首先將2 3 6和4 6 8交換,分別插入到子代2和子代1相應的位置2,4,6中,然后將將父代1和父代2中的基因按照順序插入到子代1和2中,如果該基因已經存在在位串中,則跳過該基因,繼續下一個,知道位串的長度等於定義的長度。
3.2.6 交替位置交叉法(Alternating Position Crossover,APX)
以兩個父代個體為例:(1 2 3 4 5 6 7 8)和(3 7 5 1 6 8 2 4),APX是一種簡單的交叉操作方法,它是輪流選擇父代1和2中的基因,直到子代的長度達到定義的長度為止。具體操作如圖7所示。
圖7 APX操作
首先選擇父代1中的第一個元素1,加入到子代1中,然后選擇父代2中的第一個元素3,它不等於1所以也加入到子代1中,然后再選擇父代1中的第二個元素2,它也不包含在當前的位串中,所以,也加入進去,然后再選擇父代2中的第二個元素,……,直到子代1的長度達到8為止。同樣的方法,可以得到子代2.
3.3 變異操作
同樣地,下面介紹幾種常見的變異操作方法。
3.3.1 替換變異(Displacement Mutation, DM)
DM先從父代個體中選擇一個子位串,然后再隨機在剩下的位串中選擇一個為止,並插入該子位串,如圖8所示。
圖8 DM操作
3.3.2 交換變異(Exchange Mutation, EM)
EM是從父代個體中選擇兩個基因位置,然后互換這兩個位置的基因。如圖9所示。
圖9 EM操作
3.3.3 插入變異(Insertion Mutation, IM)
IM和DM類似,唯一的區別在於它是從父代個體中只選擇一個基因,然后隨機的插入到剩下的基因串中。如圖10所示。
圖10 IM操作
3.3.4 簡單倒位變異(Simple Inversion Mutation, SIM)
SIM先從父代個體中選擇一個基因串,然后將這個基因串中所有的基因倒位放置,如圖11所示。
圖11 SIM操作
3.3.5 倒位變異(Inversion Mutation, IVM)
IVM在SIM的基礎上,隨機選擇一個插入位置,將這個到位后的基因串在插入到其中。如圖12所示。
圖12 IVM操作
3.3.6 爭奪變異(Scramble Mutation, SM)
SM非常簡單,先隨機從父代個體中選擇一個基因串,然后將除了第一個基因外的基因均向前移動一位,將第一個基因移到最后一位。具體操作過程如圖13所示。
圖13 SM操作
4. TSP問題遺傳算法求解以及Java實現
該程序分成兩個java類,Chromosome類和GA類。
Chromosome類的實現:
1: import java.util.Random;
2: import java.util.Vector;
3:
4: public class Chromosome implements Cloneable {
5:
6:
7: private int[] tour;
8: private int[][] distance;
9: private int cityNum;
10: private double fitness;
11:
12: public Chromosome(){
13: cityNum = 30;
14: tour = new int[cityNum];
15: distance = new int[cityNum][cityNum];
16: }
17:
18: public Chromosome(int num, int[][] distance){
19: this.cityNum = num;
20: tour = new int[cityNum];
21: this.distance = distance;
22:
23: }
24:
25: public void randomGeneration(){
26: Vector<Integer> allowedCities = new Vector<Integer>();
27: for (int i = 0; i < cityNum; i++) {
28: allowedCities.add(Integer.valueOf(i));
29: }
30:
31: Random r = new Random(System.currentTimeMillis());
32: for (int i = 0; i < cityNum; i++) {
33:
34: int index = r.nextInt(allowedCities.size());
35: int selectedCity = allowedCities.get(index).intValue();
36: tour[i] = selectedCity;
37: allowedCities.remove(index);
38: }
39:
40: }
41:
42: public void print(){
43: for (int i = 0; i < cityNum; i++) {
44: System.out.print(tour[i] + ",");
45: }
46: System.out.println();
47: System.out.println("Its fitness measure is: "+ getFitness());
48: }
49:
50: private double calculatefitness(){
51: /*for (int i = 0; i < cityNum; i++) {
52: for (int j = 0; j < cityNum; j++) {
53: System.out.print(distance[i][j]+"\t");
54: }
55: System.out.println();
56: }*/
57: double fitness = 0.0;
58: int len = 0;
59: for (int i = 0; i < cityNum - 1; i++) {
60: len += distance[this.tour[i]][this.tour[i+1]];
61: }
62: len += distance[0][tour[cityNum-1]];
63: fitness = 1.0/len;
64: return fitness;
65: }
66:
67: public int[] getTour() {
68: return tour;
69: }
70:
71: public void setTour(int[] tour) {
72: this.tour = tour;
73: }
74:
75: public int[][] getDistance() {
76: return distance;
77: }
78:
79: public void setDistance(int[][] distance) {
80: this.distance = distance;
81: }
82:
83: public int getCityNum() {
84: return cityNum;
85: }
86:
87: public void setCityNum(int cityNum) {
88: this.cityNum = cityNum;
89: }
90:
91: public double getFitness() {
92: this.fitness = calculatefitness();
93: return fitness;
94: }
95:
96: public void setFitness(double fitness) {
97: this.fitness = fitness;
98: }
99:
100: @Override
101: protected Object clone() throws CloneNotSupportedException {
102: Chromosome chromosome = (Chromosome) super.clone();
103: chromosome.cityNum = this.cityNum;
104: chromosome.distance = this.distance.clone();
105: chromosome.tour = this.tour.clone();
106: chromosome.fitness = this.fitness;
107: return chromosome;
108: }
109:
110:
111:
112: }
GA類的實現:
1: import java.io.BufferedReader;
2:
3: import java.io.FileInputStream;
4: import java.io.FileNotFoundException;
5: import java.io.FileOutputStream;
6:
7: import java.io.IOException;
8: import java.io.InputStreamReader;
9: import java.util.ArrayList;
10: import java.util.List;
11: import java.util.Random;
12:
13:
14: public class GA {
15:
16: private Chromosome[] chromosomes;
17: private Chromosome[] nextGeneration;
18: private int N;
19: private int cityNum;
20: private double p_c_t;
21: private double p_m_t;
22: private int MAX_GEN;
23: private int bestLength;
24: private int[] bestTour;
25: private double bestFitness;
26: private double[] averageFitness;
27: private int[][] distance;
28: private String filename;
29:
30: public GA(){
31: N = 100;
32: cityNum = 30;
33: p_c_t = 0.9;
34: p_m_t = 0.1;
35: MAX_GEN = 1000;
36: bestLength = 0;
37: bestTour = new int [cityNum];
38: bestFitness = 0.0;
39: averageFitness = new double[MAX_GEN];
40: chromosomes = new Chromosome[N];
41: distance = new int[cityNum][cityNum];
42:
43: }
44:
45: /**
46: * Constructor of GA class
47: * @param n 種群規模
48: * @param num 城市規模
49: * @param g 運行代數
50: * @param p_c 交叉率
51: * @param p_m 變異率
52: * @param filename 數據文件名
53: */
54: public GA(int n, int num, int g, double p_c, double p_m, String filename){
55: this.N = n;
56: this.cityNum = num;
57: this.MAX_GEN = g;
58: this.p_c_t = p_c;
59: this.p_m_t = p_m;
60: bestTour = new int [cityNum];
61: averageFitness = new double[MAX_GEN];
62: bestFitness = 0.0;
63: chromosomes = new Chromosome[N];
64: nextGeneration = new Chromosome[N];
65: distance = new int[cityNum][cityNum];
66: this.filename = filename;
67: }
68:
69: public void solve() throws IOException{
70: System.out.println("---------------------Start initilization---------------------");
71: init();
72: System.out.println("---------------------End initilization---------------------");
73: System.out.println("---------------------Start evolution---------------------");
74: for (int i = 0; i < MAX_GEN; i++) {
75: System.out.println("-----------Start generation "+ i+"----------");
76: evolve(i);
77: System.out.println("-----------End generation "+ i+"----------");
78: }
79: System.out.println("---------------------End evolution---------------------");
80: printOptimal();
81: outputResults();
82:
83: }
84: /**
85: * 初始化GA
86: * @throws IOException
87: */
88: @SuppressWarnings("resource")
89: private void init() throws IOException{
90: //讀取數據文件
91: int[] x;
92: int[] y;
93: String strbuff;
94: BufferedReader data = new BufferedReader(new InputStreamReader(new FileInputStream(filename)));
95:
96: distance = new int[cityNum][cityNum];
97: x = new int[cityNum];
98: y = new int[cityNum];
99: for (int i = 0; i < cityNum; i++) {
100: strbuff = data.readLine();
101: String[] strcol = strbuff.split("");
102: x[i] = Double.valueOf(strcol[1]).intValue();
103: y[i] = Double.valueOf(strcol[2]).intValue();
104: }
105: //計算距離矩陣 ,針對具體問題,距離計算方法也不一樣,此處用的是att48作為案例,它有48個城市,距離計算方法為偽歐氏距離,最優值為10628
106: for (int i = 0; i < cityNum - 1; i++) {
107: distance[i][i] = 0; //對角線為0
108: for (int j = i + 1; j < cityNum; j++) {
109: double rij = Math.sqrt((x[i] - x[j]) * (x[i] - x[j])+ (y[i] - y[j]) * (y[i] - y[j]));
110: int tij = (int) Math.round(rij);
111: //if (tij < rij) {
112: distance[i][j] = tij;
113: distance[j][i] = distance[i][j];
114: /*}else {
115: distance[i][j] = tij;
116: distance[j][i] = distance[i][j];
117: }*/
118: }
119: }
120: distance[cityNum - 1][cityNum - 1] = 0;
121:
122:
123:
124: for (int i = 0; i < N; i++) {
125: Chromosome chromosome = new Chromosome(cityNum, distance);
126: chromosome.randomGeneration();
127: chromosomes[i] = chromosome;
128: chromosome.print();
129: }
130: }
131:
132: private void evolve(int g){
133: double[] selectionP = new double[N];//選擇概率
134: double sum = 0.0;
135: double tmp = 0.0;
136:
137: for (int i = 0; i < N; i++) {
138: sum += chromosomes[i].getFitness();
139: if (chromosomes[i].getFitness() > bestFitness) {
140: bestFitness = chromosomes[i].getFitness();
141: bestLength = (int) (1.0/bestFitness);
142: for (int j = 0; j < cityNum; j++) {
143: bestTour[j] = chromosomes[i].getTour()[j];
144: }
145:
146: }
147: }
148: averageFitness[g] = sum/N;
149:
150: System.out.println("The average fitness in "+g+ " generation is: "+averageFitness[g]+ ", and the best fitness is: "+bestFitness);
151: for (int i = 0; i < N; i++) {
152: tmp += chromosomes[i].getFitness()/sum;
153: selectionP[i] = tmp;
154: }
155: Random random = new Random(System.currentTimeMillis());
156: for (int i = 0; i < N; i = i+2) {
157:
158: Chromosome[] children = new Chromosome[2];
159: //輪盤賭選擇兩個染色體
160: //System.out.println("---------start selection-----------");
161: //System.out.println();
162: for (int j = 0; j < 2; j++) {
163:
164: int selectedCity=0;
165: for (int k = 0; k < N - 1; k++) {
166: double p = random.nextDouble();
167: if (p > selectionP[k] && p <= selectionP[k+1]) {
168: selectedCity = k;
169: }
170: if (k==0 && random.nextDouble() <= selectionP[k]) {
171: selectedCity = 0;
172: }
173: }
174: try {
175: children[j] = (Chromosome) chromosomes[selectedCity].clone();
176:
177: //children[j].print();
178: //System.out.println();
179: } catch (CloneNotSupportedException e) {
180: // TODO Auto-generated catch block
181: e.printStackTrace();
182: }
183: }
184:
185: //交叉操作(OX1)
186:
187: //System.out.println("----------Start crossover----------");
188: //System.out.println();
189: //Random random = new Random(System.currentTimeMillis());
190: if (random.nextDouble() < p_c_t) {
191: //System.out.println("crossover");
192: //random = new Random(System.currentTimeMillis());
193: //定義兩個cut點
194: int cutPoint1 = -1;
195: int cutPoint2 = -1;
196: int r1 = random.nextInt(cityNum);
197: if (r1 > 0 && r1 < cityNum -1) {
198: cutPoint1 = r1;
199: //random = new Random(System.currentTimeMillis());
200: int r2 = random.nextInt(cityNum - r1);
201: if (r2 == 0) {
202: cutPoint2 = r1 + 1;
203: }else if(r2 > 0){
204: cutPoint2 = r1 + r2;
205: }
206:
207: }
208: if (cutPoint1 > 0 && cutPoint2 > 0) {
209: //System.out.println("Cut point1 is: "+cutPoint1 +", and cut point2 is: "+cutPoint2);
210: int [] tour1 = new int[cityNum];
211: int [] tour2 = new int[cityNum];
212: if (cutPoint2 == cityNum - 1) {
213: for (int j = 0; j < cityNum; j++) {
214: tour1[j] = children[0].getTour()[j];
215: tour2[j] = children[1].getTour()[j];
216: }
217: }else {
218:
219: //int n = 1;
220: for (int j = 0; j < cityNum; j++) {
221: if (j < cutPoint1) {
222: tour1[j] = children[0].getTour()[j];
223: tour2[j] = children[1].getTour()[j];
224: }else if (j >= cutPoint1 && j < cutPoint1+cityNum-cutPoint2-1) {
225: tour1[j] = children[0].getTour()[j+cutPoint2-cutPoint1+1];
226: tour2[j] = children[1].getTour()[j+cutPoint2-cutPoint1+1];
227: }else {
228: tour1[j] = children[0].getTour()[j-cityNum+cutPoint2+1];
229: tour2[j] = children[1].getTour()[j-cityNum+cutPoint2+1];
230: }
231:
232: }
233: }
234: /*System.out.println("The two tours are: ");
235: for (int j = 0; j < cityNum; j++) {
236: System.out.print(tour1[j] +"\t");
237: }
238: System.out.println();
239: for (int j = 0; j < cityNum; j++) {
240: System.out.print(tour2[j] +"\t");
241: }
242: System.out.println();*/
243:
244: for (int j = 0; j < cityNum; j++) {
245: if (j < cutPoint1 || j > cutPoint2) {
246:
247: children[0].getTour()[j] = -1;
248: children[1].getTour()[j] = -1;
249: }else {
250: int tmp1 = children[0].getTour()[j];
251: children[0].getTour()[j] = children[1].getTour()[j];
252: children[1].getTour()[j] = tmp1;
253: }
254: }
255: /*for (int j = 0; j < cityNum; j++) {
256: System.out.print(children[0].getTour()[j]+"\t");
257: }
258: System.out.println();
259: for (int j = 0; j < cityNum; j++) {
260: System.out.print(children[1].getTour()[j]+"\t");
261: }
262: System.out.println();*/
263: if (cutPoint2 == cityNum - 1) {
264: int position = 0;
265: for (int j = 0; j < cutPoint1; j++) {
266: for (int m = position; m < cityNum; m++) {
267: boolean flag = true;
268: for (int n = 0; n < cityNum; n++) {
269: if (tour1[m] == children[0].getTour()[n]) {
270: flag = false;
271: break;
272: }
273: }
274: if (flag) {
275:
276: children[0].getTour()[j] = tour1[m];
277: position = m + 1;
278: break;
279: }
280: }
281: }
282: position = 0;
283: for (int j = 0; j < cutPoint1; j++) {
284: for (int m = position; m < cityNum; m++) {
285: boolean flag = true;
286: for (int n = 0; n < cityNum; n++) {
287: if (tour2[m] == children[1].getTour()[n]) {
288: flag = false;
289: break;
290: }
291: }
292: if (flag) {
293: children[1].getTour()[j] = tour2[m];
294: position = m + 1;
295: break;
296: }
297: }
298: }
299:
300: }else {
301:
302: int position = 0;
303: for (int j = cutPoint2 + 1; j < cityNum; j++) {
304: for (int m = position; m < cityNum; m++) {
305: boolean flag = true;
306: for (int n = 0; n < cityNum; n++) {
307: if (tour1[m] == children[0].getTour()[n]) {
308: flag = false;
309: break;
310: }
311: }
312: if (flag) {
313: children[0].getTour()[j] = tour1[m];
314: position = m+1;
315: break;
316: }
317: }
318: }
319: for (int j = 0; j < cutPoint1; j++) {
320: for (int m = position; m < cityNum; m++) {
321: boolean flag = true;
322: for (int n = 0; n < cityNum; n++) {
323: if (tour1[m] == children[0].getTour()[n]) {
324: flag = false;
325: break;
326: }
327: }
328: if (flag) {
329: children[0].getTour()[j] = tour1[m];
330: position = m+1;
331: break;
332: }
333: }
334: }
335:
336:
337: position = 0;
338: for (int j = cutPoint2 + 1; j < cityNum; j++) {
339: for (int m = position; m < cityNum; m++) {
340: boolean flag = true;
341: for (int n = 0; n < cityNum; n++) {
342: if (tour2[m] == children[1].getTour()[n]) {
343: flag = false;
344: break;
345: }
346: }
347: if (flag) {
348: children[1].getTour()[j] = tour2[m];
349: position = m+1;
350: break;
351: }
352: }
353: }
354: for (int j = 0; j < cutPoint1; j++) {
355: for (int m = position; m < cityNum; m++) {
356: boolean flag = true;
357: for (int n = 0; n < cityNum; n++) {
358: if (tour2[m] == children[1].getTour()[n]) {
359: flag = false;
360: break;
361: }
362: }
363: if (flag) {
364: children[1].getTour()[j] = tour2[m];
365: position = m+1;
366: break;
367: }
368: }
369: }
370: }
371:
372:
373:
374: }
375: }
376: //children[0].print();
377: //children[1].print();
378:
379:
380: //變異操作(DM)
381:
382: //System.out.println("---------Start mutation------");
383: //System.out.println();
384: //random = new Random(System.currentTimeMillis());
385: if (random.nextDouble() < p_m_t) {
386: //System.out.println("mutation");
387: for (int j = 0; j < 2; j++) {
388: //random = new Random(System.currentTimeMillis());
389: //定義兩個cut點
390: int cutPoint1 = -1;
391: int cutPoint2 = -1;
392: int r1 = random.nextInt(cityNum);
393: if (r1 > 0 && r1 < cityNum -1) {
394: cutPoint1 = r1;
395: //random = new Random(System.currentTimeMillis());
396: int r2 = random.nextInt(cityNum - r1);
397: if (r2 == 0) {
398: cutPoint2 = r1 + 1;
399: }else if(r2 > 0){
400: cutPoint2 = r1 + r2;
401: }
402:
403: }
404:
405:
406: if (cutPoint1 > 0 && cutPoint2 > 0) {
407: List<Integer> tour = new ArrayList<Integer>();
408: //System.out.println("Cut point1 is "+cutPoint1+", and cut point2 is "+cutPoint2);
409: if (cutPoint2 == cityNum - 1) {
410: for (int k = 0; k < cutPoint1; k++) {
411: tour.add(Integer.valueOf(children[j].getTour()[k]));
412: }
413: }else {
414: for (int k = 0; k < cityNum; k++) {
415: if (k < cutPoint1 || k > cutPoint2) {
416: tour.add(Integer.valueOf(children[j].getTour()[k]));
417: }
418: }
419: }
420: //random = new Random(System.currentTimeMillis());
421: int position = random.nextInt(tour.size());
422:
423: if (position == 0) {
424:
425: for (int k = cutPoint2; k >= cutPoint1; k--) {
426: tour.add(0, Integer.valueOf(children[j].getTour()[k]));
427: }
428:
429: }else if (position == tour.size()-1) {
430:
431: for (int k = cutPoint1; k <= cutPoint2; k++) {
432: tour.add(Integer.valueOf(children[j].getTour()[k]));
433: }
434:
435: } else {
436:
437: for (int k = cutPoint1; k <= cutPoint2; k++) {
438: tour.add(position, Integer.valueOf(children[j].getTour()[k]));
439: }
440:
441: }
442:
443:
444: for (int k = 0; k < cityNum; k++) {
445: children[j].getTour()[k] = tour.get(k).intValue();
446:
447: }
448: //System.out.println();
449: }
450:
451:
452: }
453: }
454:
455:
456: //children[0].print();
457: //children[1].print();
458:
459:
460: nextGeneration[i] = children[0];
461: nextGeneration[i+1] = children[1];
462:
463: }
464:
465: for (int k = 0; k < N; k++) {
466: try {
467: chromosomes[k] = (Chromosome) nextGeneration[k].clone();
468:
469: } catch (CloneNotSupportedException e) {
470: // TODO Auto-generated catch block
471: e.printStackTrace();
472: }
473: }
474: /*System.out.println("Next generation is:");
475: for (int k = 0; k < N; k++) {
476: chromosomes[k].print();
477: }*/
478: }
479:
480: private void printOptimal(){
481: System.out.println("The best fitness is: " + bestFitness);
482: System.out.println("The best tour length is: " + bestLength);
483: System.out.println("The best tour is: ");
484: for (int i = 0; i < cityNum; i++) {
485: System.out.print(bestTour[i] + ",");
486: }
487: System.out.println();
488: }
489:
490: private void outputResults(){
491: String filename = "C://result.txt";
492: /*File file = new File(filename);
493: if (!file.exists()) {
494: try {
495: file.createNewFile();
496: } catch (IOException e) {
497: // TODO Auto-generated catch block
498: e.printStackTrace();
499: }
500: }
501: */
502: try {
503: @SuppressWarnings("resource")
504: FileOutputStream outputStream = new FileOutputStream(filename);
505: for (int i = 0; i < averageFitness.length; i++) {
506: String line = String.valueOf(averageFitness[i]) + "\r\n";
507:
508: outputStream.write(line.getBytes());
509:
510: }
511:
512: } catch (FileNotFoundException e) {
513: // TODO Auto-generated catch block
514: e.printStackTrace();
515: }catch (IOException e) {
516: // TODO Auto-generated catch block
517: e.printStackTrace();
518: }
519:
520:
521: }
522: public Chromosome[] getChromosomes() {
523: return chromosomes;
524: }
525: public void setChromosomes(Chromosome[] chromosomes) {
526: this.chromosomes = chromosomes;
527: }
528: public int getCityNum() {
529: return cityNum;
530: }
531: public void setCityNum(int cityNum) {
532: this.cityNum = cityNum;
533: }
534: public double getP_c_t() {
535: return p_c_t;
536: }
537: public void setP_c_t(double p_c_t) {
538: this.p_c_t = p_c_t;
539: }
540: public double getP_m_t() {
541: return p_m_t;
542: }
543: public void setP_m_t(double p_m_t) {
544: this.p_m_t = p_m_t;
545: }
546: public int getMAX_GEN() {
547: return MAX_GEN;
548: }
549: public void setMAX_GEN(int mAX_GEN) {
550: MAX_GEN = mAX_GEN;
551: }
552: public int getBestLength() {
553: return bestLength;
554: }
555: public void setBestLength(int bestLength) {
556: this.bestLength = bestLength;
557: }
558: public int[] getBestTour() {
559: return bestTour;
560: }
561: public void setBestTour(int[] bestTour) {
562: this.bestTour = bestTour;
563: }
564: public double[] getAverageFitness() {
565: return averageFitness;
566: }
567: public void setAverageFitness(double[] averageFitness) {
568: this.averageFitness = averageFitness;
569: }
570: public int getN() {
571: return N;
572: }
573:
574:
575: public void setN(int n) {
576: N = n;
577: }
578:
579:
580: public int[][] getDistance() {
581: return distance;
582: }
583:
584: public void setDistance(int[][] distance) {
585: this.distance = distance;
586: }
587:
588: /**
589: * @param args
590: * @throws IOException
591: */
592: public static void main(String[] args) throws IOException {
593: GA ga = new GA(2, 52, 1, 0.95, 0.75, "c://data.txt");
594: ga.solve();
595: }
596:
597:
598: }
599:
5. 總結
TSP問題是一類NP hard問題,用傳統的方法非常難以獲得最優解,特別是當問題規模很大時,最優解基本上不可能能得到。這時候,基於啟發式的算法就可以發揮其作用,雖然它並不能保證可以獲得最優解,但是可以以較快的速度得對近最優解。此文和前一篇類似,都是用啟發式算法(蟻群算法和遺傳算法)求解TSP問題,並且可以獲得比較好的結果。但是程序的參數以及效率需要進一步的測試和研究,才能得到最佳的參數設置。