質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

アルゴリズム

アルゴリズムとは、定められた目的を達成するために、プログラムの理論的な動作を定義するものです。

Q&A

1回答

3464閲覧

ruby で遺伝的アルゴリズムを使って巡回セールスマン問題を解くプログラムを作成しました、思ったように値が収束しません。

shocyu

総合スコア17

Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

アルゴリズム

アルゴリズムとは、定められた目的を達成するために、プログラムの理論的な動作を定義するものです。

0グッド

2クリップ

投稿2015/09/24 09:56

編集2015/09/25 06:22

言語は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

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yuba

2015/09/24 13:30

genesという配列に入っているのは、都市の巡回順でしょうか? 当然、重複や欠落はないはずですよね。 で、他の遺伝子と交叉するとその結果は重複や欠落がないことが保証できなくなると思いますが、実際crossメソッドでは重複を許さない場合にどうするかの記述がないようです。どのようにしているのですか?
shocyu

2015/09/25 06:10

yubaさん、ご質問ありがとうございます。 重複を許さない場合ですが、こちらはかなり処理が長くなっております。もちろん genesの情報はここでは都市の巡回順です。ここで遺伝情報の重複を許すために(その方が交叉の処理が少なくて済むので)、genesの並びの規則はこのようにしています。10都市の巡回の場合です。 最初の番号(genes[0])は1とする(出発は固定する) genes[1]は [1, 2, 3, 4, 5, 6, 7, 8, 9]のいずれかが入る。この場合すでに都市1は選ばれているので、このような対応になる。 genes[1] : [1, 2, 3, 4, 5, 6, 7, 8, 9] 実際の都市: [2, 3, 4, 5, 6, 7, 8, 9 , 10] もし、genes[1]に3が入った場合(実際の都市は4)genes[2]の選べる値と実際の都市との関係は genes[2] : [1, 2, 3, 4, 5, 6, 7, 8] 実際の都市 : [2, 4, 5, 6, 7, 8, 9, 10] つまり、残った都市の中の何番目か、という情報が各 genes の値となっています。そこで重複はOKとなっています。 重複をこのようにOKとしたのは、重複が不可の場合、遺伝情報の交叉において、それぞれの遺伝情報の並び替えをするため、交叉の効果がなくなるのでは、と判断したからです。重複不可の場合もうまく収束しませんでした。 最初の質問を編集してソースを追加します。
yuba

2015/09/25 06:19

帰ってからまた考えますが、タグにアルゴリズムがあると目に止まりやすいかと思います。
shocyu

2015/09/25 06:22

yubaさん、さっそくの返信ありがとうございます。タグにアルゴリズムを追加しました。アドバイスありがとうございました。
guest

回答1

0

あまり、これが原因だとは断定しにくい質問ですね。

遺伝的アルゴリズムで巡回セールスマン問題を解く場合、
代表的な遺伝子表現として、パス表現、隣接表現、順序表現があるようですね。

順序表現は、交差による致死個体の生成を防げますが、
親の形質を継承しにくいと思います。
以下のページで、実際の例を挙げて、親の形質を継承できない場合を
説明しています。
順序問題へのGAの適用

交叉点の左側の経路は親のものが保存されているが, 右側の経路では壊されており,親の一部しか継承されない.

別の遺伝子表現を採用した方が、収束しやすいのかも知れません。

このままの遺伝子表現とするとした場合ですが、
交叉をは2点交叉くらいにした方が良いかも知れません。
Wikipediaの遺伝的アルゴリズムを見ても、

一般に、3点以上の交叉点をもつ方法を多点交叉あるいは n 点交叉という。しかしながら一部の問題を除き、多点交叉は二点交叉と下記で述べる一様交叉のどちらかよりも良い値が出ることはほとんどなく、あまり使われていない。

とあり、多点交叉は効果が低いのではと思います。
さらに、順序表現なので、交叉点数が増えると、
より親の形質を継承しない子が生成されてしまうのではないでしょうか?

投稿2015/09/25 09:55

eripong

総合スコア1546

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

eripong

2015/09/25 10:00 編集

コードを見ると、4点交叉ではなく1点交叉ですか?
shocyu

2015/09/25 10:33

eripong さん、回答ありがとうございます。そして貴重な資料のリンクを貼ってくださり感謝します。まずは遺伝子表現を検討して、もっとも親の性質を受け継ぐことの出来る表現と、それに伴う交叉点数を検証してみたいと思います。アルゴリズムの奥の深さを実感できました。
shocyu

2015/09/25 10:36

eripongさん、もう一点ご質問のあった件ですが、実行した際には交叉点数は4点でやってみました。最初は1点交叉にしたのですが、そこで収束しなかったので調整してみました。crossメソッドの最後の引数である crossによって交叉点数を可変出来るようにしてあります。
eripong

2015/09/25 10:49

「順序問題へのGAの適用」を見ると書いてありますが、 遺伝子表現を変えた場合、交叉の方法も変わります。 そのため、交叉点数の調整というより、交叉方法の選択になると思います。 交叉点数の変更については、了解です。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問