始めての遺伝的アルゴリズム

ayumegu(プログラマー)
よろしくお願いします。

こんにちは、あゆめぐです。
マッチ箱の脳という人工知能の本をしっていますか?(10年以上前のものですが・・・。)

イラストが入っていてとてもわかりやすい人工知能の本です!
amazonのkindleだと600円で売っているのでプライム会員は0円らしい・・・。
なので気になった方はどうぞ〜
作者の方は私の好きなゲームであるがんばれ森川君2号やアストロノーカをつくったかたです。

というわけで、本日はマッチ箱の脳の最初にのっていたやつをUnityでちょっとためしてみました。
問題としては各項目3択の全10問の問題があって、それを全問正解するまでがんばらせるというものです。
詳しいことは本を・・・うん・・・。

簡単に私が覚えているルールは

  • 10体のあゆめぐちゃん(本ではマッチ箱)がテストを受けます。
  • 成績の悪い2匹には出て行ってもらいます。
  • 優秀な二匹の子供を二匹作ります
    • ランダムな位置で二匹の遺伝子(解答)を入れ替えます。
    • ランダムな確率で突然変異がおこります。
      • 私の場合は1~11のランダムで11の場合は突然変異なし
      • それ以外の場合はその番号の解答を+1しました。3になったら0にする

あとはこれを全問正解するまで繰り返させます。
問題はかわることはありません。
またそれぞれの個体が解答を途中でかえるなどもありません。 早いときで9世代、遅いと50世代くらいで全問正解しました。

とりあえず処理のソースを

##あゆめぐクラス

これは1匹のあゆめぐちゃんのクラスです。
dnaにテストの解答、scoreにテストの点数が入ります

using UnityEngine;
using System.Collections;
public class Ayumegu {
public int[] dna;
public int score;
public Ayumegu()
{
dna = new int[ExampleTest.QUESTION_COUNT];
for(int i = 0; i < ExampleTest.QUESTION_COUNT; i++)
{
dna[i] = Random.Range(0, 3);
}
}
public string GetDNA()
{
string str = "";
for(int i = 0; i < ExampleTest.QUESTION_COUNT; i++)
{
str += dna[i].ToString();
}
return str;
}
public void SetDNA(string str)
{
dna = new int[ExampleTest.QUESTION_COUNT];
for(int i = 0; i < ExampleTest.QUESTION_COUNT; i++)
{
dna[i] = int.Parse(str.Substring(i, 1));
}
}
}

##テストクラス

こちらはテストのクラスです。
問題数や解答の変数があり、採点するメソッドGetScoreがあります。

using UnityEngine;
using System.Collections;
public class ExampleTest{
public const int QUESTION_COUNT = 10; // 問題の数
public int[] result; // 解答
public ExampleTest()
{
// テストの解答 ランダムでもなんでも 0〜2までの数字を入れる
result = new int[]{1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
}
public int GetScore(int[] answerList)
{
int score = 0;
for(int i = 0; i < QUESTION_COUNT; i++)
{
if(result[i] == answerList[i])
{
score += 1;
}
}
return score;
}
}

##動かすクラス

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class GATest : MonoBehaviour {
public const int AYUMEGU_COUNT = 10;
public List<Ayumegu> ayumeguList;
public ExampleTest test;
public int count = 0;
void Start () {
test = new ExampleTest();
ayumeguList = new List<Ayumegu>();
for(int i = 0; i < AYUMEGU_COUNT; i++)
{
ayumeguList.Add (new Ayumegu());
}
Test();
}
// テストを受けます
public void Test()
{
for(int i = 0; i < AYUMEGU_COUNT; i++)
{
ayumeguList[i].score = test.GetScore(ayumeguList[i].dna);
}
Next();
}
// 結果をもとに点数の悪い子には出て行ってもらいます
public void Next()
{
ayumeguList.Sort(delegate(Ayumegu dna1, Ayumegu dna2){ return dna1.score - dna2.score; });
// 全問正解していたら終了
if(ayumeguList[ayumeguList.Count - 1].score == ExampleTest.QUESTION_COUNT)
{
Debug.Log (count + "End" + ayumeguList[ayumeguList.Count - 1].GetDNA());
}
else
{
count++;
for(int i = 0; i < 2; i++)
{
ayumeguList.RemoveAt(i);
}
CreateChild(ayumeguList[ayumeguList.Count - 1], ayumeguList[ayumeguList.Count - 2]);
}
}
// 優秀な二匹の遺伝子を引き継いだ子を2匹追加します。
public void CreateChild(Ayumegu parent1, Ayumegu parent2)
{
int rand = Random.Range(1, ExampleTest.QUESTION_COUNT - 1);
string parent1Dna = parent1.GetDNA();
string parent2Dna = parent2.GetDNA();
string child1Dna = parent1Dna.Substring(0, rand) + parent2Dna.Substring(rand, ExampleTest.QUESTION_COUNT - rand);
string child2Dna = parent2Dna.Substring(0, rand) + parent1Dna.Substring(rand, ExampleTest.QUESTION_COUNT - rand);
child1Dna = ChangeDna(child1Dna);
child2Dna = ChangeDna(child2Dna);
Ayumegu child1 = new Ayumegu();
child1.SetDNA(child1Dna);
ayumeguList.Add (child1);
Ayumegu child2 = new Ayumegu();
child2.SetDNA(child2Dna);
ayumeguList.Add (child2);
Test();
}
// 遺伝子の一部を突然変異させます
private string ChangeDna(string dna)
{
int changePoint = Random.Range(1, 12);
if(changePoint < 11)
{
int no = int.Parse(dna.Substring(changePoint - 1, 1));
no++;
if(no > 2) no = 0;
dna = dna.Substring(0, changePoint - 1) + no.ToString() + dna.Substring(changePoint, ExampleTest.QUESTION_COUNT - changePoint);
}
return dna;
}
}

えーっとえーっとコンソールはってもつまらないと思うので次の記事でUIでもつけてみようかな・・・。
うん、これだけだとシュールだし、なにしてるかわからないよね・・・。
では今回はここまでで・・・w