言語はrubyを使って巡回セールスマン問題を解くプログラムを作成しました。遺伝的アルゴリズムを用い、遺伝情報などアルゴリズムで用いる固有の変数や配列をクラスを用いてひとまとめにしてあります。
アルゴリズムとしては基本的なスタイルは踏襲し、また遺伝情報の交叉もテストにて正しく交叉されていることは確認できました(交叉の点数は4点交叉です)。また適合度の高い順にソートも出来ています。次の世代にはその中から上位50%を残し、ルーレット式で次世代の遺伝情報を選択しています。
一通り作成し、実際に動作させてみました。個体数256、世代数30世代にわたって回し、結果を出力させました。30世代までだいたい距離45から40の間を行ったり来たりしており、参考書に書かれているようにきれいに収束する様子が見られません。(解答は 35.626)
選択の方法に問題があるのかと思っています。また突然変異の確率も調整が必要かもしれません。
ソースに関してはもう少しシンプルに書きたいと思っています。なんとなくC言語 like なソースになっています。500行は長すぎな気がしています。
ソースを添付しますので、ご回答をよろしくお願いいたします。ソースは最初に作成した genetic クラスのみ添付します。
ruby
1#------------------------------------------------------------------------------------# 2#Geneticsクラス(genesの生成と、交叉) 3class Genetics 4 5#インスタンス変数 genes, value, evaluation は参照と変更可能 6attr_accessor :genes 7attr_accessor :value 8attr_accessor :evaluation 9attr_accessor :reciprocal 10 11#コンストラクタ(インスタンス変数 @genes, @value, @evaluation, @reciprocal生成) 12def initialize(number_of_genes, max_value, overwrap) 13 @genes = Array.new(number_of_genes, 0) #genes配列(遺伝情報を格納する配列) 14 @value = Array.new(number_of_genes, 0.0) #value配列(遺伝情報に基づく値(表現型の情報)) 15 @evaluation = 1.0 #value配列に基づき、各個体の評価関数の値(適合度)を格納 16 @reciprocal = 0.0 #評価関数の値(evaluation)の逆数を格納(評価関数が少ないほど良い場合に用いる) 17 18 #重複を認める場合、ランダムに遺伝情報を生成 19 if overwrap == 0 20 for i in 0..(number_of_genes-1) do 21 @genes[i] = Random.rand(max_value * 10) % max_value 22 end 23 #重複を認めない場合、遺伝情報の分だけ値をシャッフルして生成 24 else 25 for i in 0..(number_of_genes-1) do 26 @genes[i] = i 27 end 28 29 @genes.shuffle! 30 end 31 32end 33 34#crossメソッド(2つの親の遺伝情報を交叉、1点交叉) 35def cross(other, child1, child2, number_of_genes, max_value, overwrap, cross) 36 37 index = 0 38 division = number_of_genes / (cross+1) 39 40 #重複を認める場合 41 if overwrap == 0 42 stack = 0 43 44 #cross(交叉点数により交叉する、しないの区別) 45 for i in 0..(cross) do 46 47 #i が偶数(0を含む)の遺伝情報は親をそのまま引き継ぐ 48 if (i != cross) && ( ( i / 2 ) == 0 ) 49 for j in (division * i)..( (division * (i+1)) -1 ) do 50 child1.genes[j] = self.genes[j] 51 child2.genes[j] = other.genes[j] 52 end 53 54 #i が奇数の場合は遺伝情報を交換 55 elsif (i != cross) && ( ( i / 2 ) != 0) 56 for j in (division * i)..( (division * (i+1)) -1 ) do 57 child1.genes[j]= other.genes[j] 58 child2.genes[j] = self.genes[j] 59 end 60 61 #iが偶数(0を含む)でかつラスト(ラストまで交換なし) 62 elsif (i == cross) && ( ( i / 2 ) == 0) 63 for j in (division * i)..( number_of_genes -1 ) do 64 child1.genes[j] = self.genes[j] 65 child2.genes[j] = other.genes[j] 66 end 67 68 #i が奇数でかつラスト(ラストまで交換) 69 elsif (i == cross) && ( ( i / 2 ) != 0) 70 for j in (division * i)..(number_of_genes-1) do 71 child1.genes[j]= other.genes[j] 72 child2.genes[j] = self.genes[j] 73 end 74 75 end #if文終わり 76 77 #突然変異(各個体について、1つの遺伝子を書き換え) 78 index = Random.rand(number_of_genes * 10) % number_of_genes 79 child1.genes[index] = Random.rand(max_value) 80 child2.genes[index] = Random.rand(max_value) 81 #puts "index = " + index.to_s 82 end 83 84 #重複を認めない場合 85 else 86 stack = 0 87 88 one_to_two = 0 89 two_to_one = 0 90 91 #親から子へそのままコピー 92 for i in 0..(number_of_genes-1) do 93 child1.genes[i] = self.genes[i] 94 child2.genes[i] = other.genes[i] 95 end 96 97 for i in 0..cross do 98 99 #交差点が偶数で crossの値が(ラスト)でない場合はそのまま 100 if (i != cross) && ( (i / 2) == 0 ) 101 next 102 103 #交差点が奇数で crossの値が(ラスト)でない場合は遺伝情報を交換 104 elsif ( i != cross) && ( (i / 2) != 0 ) 105 for j in (division * i)..( (division * (i+1)) -1 ) do 106 one_to_two = child1.genes[j] 107 two_to_one = child2.genes[j] 108 109 if child1.genes[j] == child2.genes[j] 110 child1.genes[j] = two_to_one 111 child2.genes[j] = one_to_two 112 else 113 #2つの個体の遺伝情報が異なる場合、各個体間で入れ替え(重複を避けるため) 114 for k in 0..(number_of_genes-1) do 115 if child1.genes[k] == two_to_one && (k != j) 116 child1.genes[k] = child1.genes[j] 117 child1.genes[j] = two_to_one 118 break 119 end 120 end 121 122 for k in 0..(number_of_genes-1) do 123 if child2.genes[k] == one_to_two && (k != j) 124 child2.genes[k] = child2.genes[j] 125 child2.genes[j] = one_to_two 126 break 127 end 128 end 129 130 end #if文終了(遺伝情報が同じかどうかの判定) 131 end #for文終了(遺伝情報の交換) 132 133 #交差点が偶数で cross(ラスト)の場合はループ終了 134 elsif (i == cross) && ( (i / 2) == 0 ) 135 break 136 137 #交差点が奇数で cross(ラスト)の場合は最後(number_of_genes -1)まで遺伝情報を交換 138 elsif (i == cross) && ( (i / 2) != 0 ) 139 for j in (division * i)..( number_of_genes -1 ) do 140 one_to_two = child1.genes[j] 141 two_to_one = child2.genes[j] 142 143 if child1.genes[j] == child2.genes[j] 144 child1.genes[j] = two_to_one 145 child2.genes[j] = one_to_two 146 else 147 #2つの個体の遺伝情報が異なる場合、各個体間で入れ替え(重複を避けるため) 148 for k in 0..(number_of_genes-1) do 149 if child1.genes[k] == two_to_one && (k != j) 150 child1.genes[k] = child1.genes[j] 151 child1.genes[j] = two_to_one 152 break 153 end 154 end 155 156 for k in 0..(number_of_genes-1) do 157 if child2.genes[k] == one_to_two && (k != j) 158 child2.genes[k] = child2.genes[j] 159 child2.genes[j] = one_to_two 160 break 161 end 162 end 163 164 end #if文終了(遺伝情報が同じかどうか判定) 165 end #for文終了 (遺伝情報の交換) 166 167 end #if文終了 (crossが偶数か奇数か) 168 end #for文終了(cross=交叉点数の分だけ) 169 170 #突然変異(各個体について、1つの遺伝子を書き換え) 171 index = Random.rand(number_of_genes * 10) % number_of_genes 172 stack = child1.genes[index] 173 child1.genes[index] = Random.rand(max_value) 174 175 for j in 0..(number_of_genes-1) do 176 if child1.genes[j] == stack && (j != index) 177 child1.genes[j] = stack 178 break 179 end 180 end 181 182 index = Random.rand(number_of_genes * 10) % number_of_genes 183 stack = child2.genes[index] 184 child2.genes[index] = Random.rand(max_value) 185 186 for j in 0..(number_of_genes-1) do 187 if child2.genes[j] == stack && (j != index) 188 child2.genes[j] = stack 189 break 190 end 191 end 192 193 stack = 0 194 index = 0 195 196 end #overwrapによる場合分け終了 197end #crossメソッド終了 198 199end 200#Geneticクラス定義ここまで 201#---------------------------------------------------------------------------------# 202