進化のシミュレーション


イギリスの生物学者Clinton Richard Dawkins (以下、ドーキンス)が提唱したと言われる進化過程のシミュレーションとしてイタチプログラム(weasel program)というものがある。 (ネットを漁っていた時にたまたま出会ったので参考文献らしい参考文献がない・・・)

簡単にまとめると、進化による複雑な変異も条件付けを行うことで比較的短期に実現が可能であるという主張がある。それを証明するために用いられたのがイタチプログラムと呼ばれるものだ。ランダムに文字列を発生させて、最終的に”METHINKS IT IS LIKE A WEASEL”というターゲットの文字列を実現するまで変異させる。この時にターゲットの文字列とヒットしたものはそのまま次の変異に渡されるという条件をつけると比較的短期にランダムな文字列がターゲットの文字列に収束するという。

そのプログラムをChatGPTを使って書いてみた。コードは下記。

# イタチプログラムを日本語のコメント付きで書き直します。

import random

def mutate(parent, mutation_rate=0.05):
    """指定された突然変異率で文字列を変更する。
    
    Args:
    - parent (str): 突然変異を適用する親文字列。
    - mutation_rate (float): 各文字が変異する確率。

    Returns:
    - str: 突然変異後の文字列。
    """
    alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ "
    return ''.join(
        (char if random.random() > mutation_rate else random.choice(alphabet))
        for char in parent
    )

def generate_initial_population(size, target):
    """初期集団を生成する関数。

    Args:
    - size (int): 初期集団のサイズ。
    - target (str): 目標文字列。

    Returns:
    - list: 初期文字列のリスト。
    """
    return [''.join(random.choice("ABCDEFGHIJKLMNOPQRSTUVWXYZ ") for _ in range(len(target))) for _ in range(size)]

def calculate_fitness(individual, target):
    """文字列の適合度を計算する関数。

    Args:
    - individual (str): 評価する文字列。
    - target (str): 目標文字列。

    Returns:
    - int: 目標文字列との一致する文字数。
    """
    return sum(1 for i, char in enumerate(individual) if char == target[i])

def weasel_program(target="METHINKS IT IS LIKE A WEASEL", mutation_rate=0.05, population_size=100, max_generations=1000):
    """進化の過程をシミュレートするメイン関数。

    Args:
    - target (str): 達成したい目標文字列。
    - mutation_rate (float): 突然変異率。
    - population_size (int): 一世代あたりの集団のサイズ。
    - max_generations (int): 最大世代数。

    Returns:
    - str: 目標に到達した文字列。
    """
    population = generate_initial_population(population_size, target)
    best_match = max(population, key=lambda x: calculate_fitness(x, target))
    best_fitness = calculate_fitness(best_match, target)
    generation = 0

    while best_fitness < len(target) and generation < max_generations:
        generation += 1
        # 次世代の集団を生成
        population = [mutate(best_match, mutation_rate) for _ in range(population_size)]
        best_match = max(population, key=lambda x: calculate_fitness(x, target))
        best_fitness = calculate_fitness(best_match, target)
        print(f"世代 {generation}: {best_match} (適合度: {best_fitness})")
        
        if best_fitness == len(target):
            break

    return best_match

# 関数の呼び出しはコメントアウトしています。
weasel_program()

結果は下記であった。

世代 1: RZTMEVKDUOI LURLIVEFO BEFNIK (適合度: 8)

世代 2: RZTMEVKDUOI LURLIVEFA BEFNIK (適合度: 9)

世代 3: MVTMEVKDUOI LURLIVEFA BEFNIK (適合度: 10)

世代 4: MVTMEVKDUOI LURLIVEFA WEFNIK (適合度: 11)

世代 5: MVTMEVKDUOI LURLIVECA WEFNIL (適合度: 12)

世代 6: MVTMEVKDUOI LURLIVECA WEFNIL (適合度: 12)

世代 7: METMHVKDUOI LURLIVECA WEFNIL (適合度: 13)

世代 8: METMHVKDUOI LURLIKECA WEFNIL (適合度: 14)

世代 9: METMHVKDUOT LURLIKECA WEFNIL (適合度: 15)

世代 10: METMHVKVUOT LURLIKECA WEFNIL (適合度: 15)

世代 11: METMHVKVVOT LURLIKECA WEFNIL (適合度: 15)

世代 12: METMNVKVVOT LURLIKECA WEFNIL (適合度: 15)

世代 13: METMNVKV OT LURLIKECA WEFNIL (適合度: 16)

世代 14: METMNVKV OT LURLIKECA WEFNIL (適合度: 16)

世代 15: METMNVKV OT LIFLIKE A WEFNIL (適合度: 17)

世代 16: METMNQKV OT LIFLIKE A WEFNIL (適合度: 17)

世代 17: METMNQKV OT LIFLIKE A WEFNIL (適合度: 17)

世代 18: METMCQKV OT LIFLIKE A WEFNIL (適合度: 17)

世代 19: METMCQKV OT LIFLIKE A WEFNIL (適合度: 17)

世代 20: METMIQKV OT KIFLIKE A WEFNIL (適合度: 18)

世代 21: METMIQKV IT KIFLIKE A WEFNIL (適合度: 19)

世代 22: METMIQKV IT KIFLIKE A WEFNIL (適合度: 19)

世代 23: METMINKV IT KIFLIKE A WEFNIL (適合度: 20)

世代 24: METMINKS IT KIFLIKE A WEFDIL (適合度: 21)

世代 25: METMINKS IT PI LIKE A WEFDIL (適合度: 22)

世代 26: METMINKS IT PI LIKE A WEFDEL (適合度: 23)

世代 27: METMINKS IT PI LIKE A WEFDEL (適合度: 23)

世代 28: METMINKS IT PI LIKE A WEFDEL (適合度: 23)

世代 29: METMINKS IT PI LIKE A WEYDEL (適合度: 23)

世代 30: METMINKS IT PI LIKE A WEYMEL (適合度: 23)

世代 31: METMINKS IT PI LIKE A WEYMEL (適合度: 23)

世代 32: METMINKS IT PI LIKE A WEYMEL (適合度: 23)

世代 33: METMINKS IT PI LIKE A WEYMEL (適合度: 23)

世代 34: METMINKS IT PI LIKE A WEXMEL (適合度: 23)

世代 35: METMINKS IT PI LIKE A WEXSEL (適合度: 24)

世代 36: METMINKS IT PI LIKE A WEXSEL (適合度: 24)

世代 37: METMINKS IT PI LIKE A WEXSEL (適合度: 24)

世代 38: METMINKS IT II LIKE A WEXSEL (適合度: 25)

世代 39: METHINKS IT II LIKE A WEXSEL (適合度: 26)

世代 40: METHINKS IT II LIKE A WEXSEL (適合度: 26)

世代 41: METHINKS IT II LIKE A WEXSEL (適合度: 26)

世代 42: METHINKS IT II LIKE A WEXSEL (適合度: 26)

世代 43: METHINKS IT II LIKE A WEXSEL (適合度: 26)

世代 44: METHINKS IT II LIKE A WEXSEL (適合度: 26)

世代 45: METHINKS IT II LIKE A WEXSEL (適合度: 26)

世代 46: METHINKS IT II LIKE A WEXSEL (適合度: 26)

世代 47: METHINKS IT II LIKE A WEXSEL (適合度: 26)

世代 48: METHINKS IT II LIKE A WEXSEL (適合度: 26)

世代 49: METHINKS IT II LIKE A WEXSEL (適合度: 26)

世代 50: METHINKS IT II LIKE A WEXSEL (適合度: 26)

世代 51: METHINKS IT II LIKE A WEXSEL (適合度: 26)

世代 52: METHINKS IT II LIKE A WEXSEL (適合度: 26)

世代 53: METHINKS IT II LIKE A WEXSEL (適合度: 26)

世代 54: METHINKS IT II LIKE A WEXSEL (適合度: 26)

世代 55: METHINKS IT II LIKE A WEXSEL (適合度: 26)

世代 56: METHINKS IT II LIKE A WEXSEL (適合度: 26)

世代 57: METHINKS IT II LIKE A WEXSEL (適合度: 26)

世代 58: METHINKS IT II LIKE A WEXSEL (適合度: 26)

世代 59: METHINKS IT II LIKE A WEXSEL (適合度: 26)

世代 60: METHINKS IT II LIKE A WEXSEL (適合度: 26)

世代 61: METHINKS IT II LIKE A WEXSEL (適合度: 26)

世代 62: METHINKS IT II LIKE A WEXSEL (適合度: 26)

世代 63: METHINKS IT II LIKE A WEXSEL (適合度: 26)

世代 64: METHINKS IT II LIKE A WEWSEL (適合度: 26)

世代 65: METHINKS IT II LIKE A WEWSEL (適合度: 26)

世代 66: METHINKS IT II LIKE A WEWSEL (適合度: 26)

世代 67: METHINKS IT II LIKE A WEASEL (適合度: 27)

世代 68: METHINKS IT II LIKE A WEASEL (適合度: 27)

世代 69: METHINKS IT II LIKE A WEASEL (適合度: 27)

世代 70: METHINKS IT IS LIKE A WEASEL (適合度: 28)

METHINKS IT IS LIKE A WEASEL

約70世代で収束していることがわかる。

こういったプログラミングによるシミュレーションは理論を体験できとてもおもしろい。

参考文献

-なし

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です