GAの二点交叉で交叉点を選ぶ具体的な実装方法について
なんかぐぐっても具体的な実装の情報が出てこなかったので。あったら教えて。
以下は二点交叉の例(1と4で交叉)。2箇所適当に選んで遺伝子を入れ替える。
0 1 2 3 4 5 |a|a|a|a|a| |a|b|b|b|a| x -> |b|b|b|b|b| |b|a|a|a|b|
問題となった点は以下の2つ。
- 二点交叉時に選ぶ交叉点に、一番外側(0と5)は含まれるか
- 二点交叉で二点を偏り無く選ぶ方法
1.はとりあえず含まないものとした(これも合ってるのか分からないけど)。
今回は2.について。以下に考えられる実装方法を挙げる。
- 交叉点を1つ適当に選んだ後、それより右側から1つ適当に選ぶ
- こういう間違い結構あるんじゃないかな。ちゃんと検証してないけど、この方法だと多分右側に交差点が偏る。
- 考えられる交差点をリストに入れてシャッフルし、先頭から2つ取り出す
- 時間無かったときに1回この方法で実装した。当然遅い。
- 交叉点を1つ適当に選び、2つ目は重複しなくなるまで取り出すのを繰り返す
- 気にならない程度だろうけど多少無駄がありそう。あとこれは乱数にも影響が出そうな気がする。
- 今回実装した方法
- 以下にJavaソースと説明を書く(この記事用に書いてるのでミスがあるかも)
/** * 指定された長さの遺伝子型に対して交叉点を2つ選び、昇順にして返す。 * * @param genotypeLength 遺伝子型の長さ * @param random 乱数生成器 * @return {左側の交叉点, 右側の交叉点}となっているint型配列 */ public int[] getCrossoverPoints(int genotypeLength, Random random) { int less = random.nextInt(genotypeLength-1) + 1; // (1) int more = random.nextInt(genotypeLength-2) + 1; // (2) if (more < less) { // (3) int tmp = less; less = more; more = tmp; } else { // (4) ++more; } return new int[]{less, more}; }
交叉点の候補は、冒頭の例では1〜4となる。この4つの数値から偏り無く2つを選ぶ。
(1)について
RandomのnextInt(int)メソッドは「 0 <= x < 指定した数値」となるint値xを返すため、
遺伝子長より1つ少ない数値を引数として渡す。
冒頭の例で言う0〜3のint値が返るので、1加えて1〜4に変換してless(暫定的な左側の交叉点)へ代入。
(2)について
(1)よりも候補が1つ減るので、今度は1〜3に変換してmore(暫定的な右側の交叉点)へ代入。
(3)について
(1)、(2)の結果、moreの方がlessよりも小さかった場合、2つを入れ替える。
(4)について
(2)で得られた値と実際に欲しい値との対応付けを行っている。
(1)で得られた値と(2)で得られた値には以下2通りの場合が考えられる。
- (1)の値未満の場合→そのまま
- (1)の値以上の場合→1つ右側へずらす
例えば冒頭の例について、(1)で2が得られた場合、残りの候補1,3,4に対して、
(2)で得られる可能性がある値1,2,3はそれぞれ以下のように対応付けられる。
1 (2) 3 4 1 2 3
つまり、「(1)で得られた値<=(2)で得られた値」の場合に限り、(2)で得られた値をインクリメントすれば良い。
これは(3)とちょうど逆の条件になっているため、else文の中に入れられる。
終わり。