チャレンジ一覧に戻る
中級 0件解決済み 正答率0%

遺伝的アルゴリズムの 1 世代

トーナメント選択、一点交叉、突然変異でビット列の次の世代を作ります。

このチャレンジで学べること

  • トーナメントで親を選ぶ
  • 交叉で解を組み合わせる
  • 突然変異で多様性を加える

問題

原文(英語)を表示

遺伝的アルゴリズムは、進化と同じやり方で候補解の集団を改良します。適応度の高い親を選び、組み合わせ、ランダムな突然変異を加えます。ここでは候補はビット列で、適応度は 1 の個数です (OneMax 問題)。

テストできるように、ランダムな選択はすべて与えられます。N 本の文字列 (N は偶数) から、ペアごとに N 個の子を作ります。ペア k = 0, 1, …, N/2 - 1 について:

  1. 選択: parent1 はトーナメント tournaments[2k] の勝者、parent2 は tournaments[2k + 1] の勝者です。トーナメント [i, j] は population[i] と population[j] のうち 1 の多い方が勝ち、同数なら population[i] が勝ちます。
  2. 交叉 (c = crossover_points[k]): 子 2k = parent1[0:c] + parent2[c:]、子 2k + 1 = parent2[0:c] + parent1[c:]。

その後、各突然変異 [child, position] を順に適用し、その子のそのビットを反転します。

ga_generation(population, tournaments, crossover_points, mutations) を実装してください。

入力

population: 長さ L の N 本のビット列。

tournaments: 集団のインデックスの N 個のペア。

crossover_points: N/2 個の切断点。それぞれ 1 以上 L - 1 以下。

mutations: [child, position] のペア (空の場合もあります)。

出力

N 個の子を順に返します。

制約

2 ≤ N ≤ 200 (N は偶数)、2 ≤ L ≤ 64

0 ≤ 突然変異の数 ≤ 1,000

例

例 1
入力:
mutations = [
    [0, 0]
]
population = ["0000", "1111", "1100", "0011"]
tournaments = [
    [0, 1],
    [2, 3],
    [1, 2],
    [0, 3]
]
crossover_points = [2, 1]

期待される出力:
expected = ["0100", "1111", "1011", "0111"]
入力:
std::vector<std::vector<long long>> mutations = {
    {0, 0}
};
std::vector<std::string> population = {"0000", "1111", "1100", "0011"};
std::vector<std::vector<long long>> tournaments = {
    {0, 1},
    {2, 3},
    {1, 2},
    {0, 3}
};
std::vector<long long> crossover_points = {2, 1};

期待される出力:
std::vector<std::string> expected = {"0100", "1111", "1011", "0111"};
入力:
long[][] mutations = new long[][] {
    new long[] {0, 0}
};
string[] population = new string[] {"0000", "1111", "1100", "0011"};
long[][] tournaments = new long[][] {
    new long[] {0, 1},
    new long[] {2, 3},
    new long[] {1, 2},
    new long[] {0, 3}
};
long[] crossover_points = new long[] {2, 1};

期待される出力:
string[] expected = new string[] {"0100", "1111", "1011", "0111"};

解説

ペア 0: "1111" が "0000" に勝ち、"1100" と "0011" は同数なので前者が勝ちます。2 ビット目の後で切ると "1100" と "1111"。ペア 1: 親は "1111" と "0011"、1 ビット目の後で切ると "1011" と "0111"。最後に子 0 のビット 0 を反転して "0100"。

例 2
入力:
mutations = []
population = ["10", "01"]
tournaments = [
    [0, 1],
    [1, 0]
]
crossover_points = [1]

期待される出力:
expected = ["11", "00"]
入力:
std::vector<std::vector<long long>> mutations = {};
std::vector<std::string> population = {"10", "01"};
std::vector<std::vector<long long>> tournaments = {
    {0, 1},
    {1, 0}
};
std::vector<long long> crossover_points = {1};

期待される出力:
std::vector<std::string> expected = {"11", "00"};
入力:
long[][] mutations = new long[][] {};
string[] population = new string[] {"10", "01"};
long[][] tournaments = new long[][] {
    new long[] {0, 1},
    new long[] {1, 0}
};
long[] crossover_points = new long[] {1};

期待される出力:
string[] expected = new string[] {"11", "00"};

解説

両方とも 1 が 1 つなので、各トーナメントは最初のインデックスが勝ちます: parent1 = "10"、parent2 = "01"。1 ビット目の後で切ると "11" と "00"。

解き方のヒント

まずは自分で考えてから、ヒントを 1 つずつ開いてみましょう。

ヒント 1

子はペアで作る。まず tournament ごとに 2 つの親を選び、それから 1 つの cut point で bit string を組み合わせる。

ヒント 2

選択は fitness のみで決まり、同点なら tournament pair では必ず先の contestant を優先する。

ヒント 3

mutation はすべての子を作り終えてから適用し、各 flip が現在の child string を対象にするようにする。

目安の計算量

時間 O(N * L + M)、追加メモリ O(N * L)

参考資料

この課題はCodeUtility用に調整されています。上記の説明と制約に従ってください。

他の人の解答から学ぶ

コミュニティの解答

まだ公開された解答がありません。この課題を解いて最初の解答を共有しましょう。