イタチプログラムを改善する


以前の投稿でイタチプログラムについて投稿した.

今回はそれを少し改善してアニメーションで過程が示せるようにしてみた.

# 必要なライブラリのインストールとインポート
!pip install Pillow
!apt-get install -y fonts-ipafont-gothic

import random
from PIL import Image, ImageDraw, ImageFont
import os
from IPython.display import Image as IPythonImage
from IPython.display import display

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 create_image(text, generation, fitness, image_folder):
    """テキストを画像として保存する関数。

    Args:
    - text (str): 表示する文字列。
    - generation (int): 世代数。
    - fitness (int): 適合度。
    - image_folder (str): 画像を保存するフォルダのパス。
    """
    img_width = 800
    img_height = 200
    background_color = (255, 255, 255)
    text_color = (0, 0, 0)

    # フォントの設定(Colabで利用可能な日本語フォントを指定)
    font = ImageFont.truetype("/usr/share/fonts/opentype/ipafont-gothic/ipag.ttf", 32)

    img = Image.new('RGB', (img_width, img_height), color=background_color)
    draw = ImageDraw.Draw(img)
    message = f"世代 {generation}: {text} (適合度: {fitness})"

    # テキストの位置を中央に調整
    # 変更点: textsize -> textbbox を使用
    text_bbox = draw.textbbox((0, 0), message, font=font)
    text_width = text_bbox[2] - text_bbox[0]
    text_height = text_bbox[3] - text_bbox[1]
    position = ((img_width - text_width) / 2, (img_height - text_height) / 2)

    draw.text(position, message, fill=text_color, font=font)
    img.save(f"{image_folder}/generation_{generation}.png")

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

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

    Returns:
    - str: 目標に到達した文字列。
    """
    # 画像を保存するフォルダを作成
    image_folder = "weasel_images"
    if not os.path.exists(image_folder):
        os.makedirs(image_folder)

    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

    # 初期世代の画像を保存
    create_image(best_match, generation, best_fitness, image_folder)

    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)

        # 画像を保存
        create_image(best_match, generation, best_fitness, image_folder)

        print(f"世代 {generation}: {best_match} (適合度: {best_fitness})")

        if best_fitness == len(target):
            break

    # GIFを生成
    create_gif(image_folder)

    # 生成されたGIFを表示
    display(IPythonImage(open('weasel_evolution.gif','rb').read()))

    return best_match

def create_gif(image_folder):
    """保存された画像からGIFを生成する関数。

    Args:
    - image_folder (str): 画像が保存されているフォルダのパス。
    """
    images = []
    image_files = sorted([f for f in os.listdir(image_folder) if f.endswith('.png')], key=lambda x: int(x.split('_')[1].split('.')[0]))
    for filename in image_files:
        img = Image.open(f"{image_folder}/{filename}")
        images.append(img)

    # GIFを保存
    images[0].save('weasel_evolution.gif', save_all=True, append_images=images[1:], duration=200, loop=0)

# 関数の呼び出し
weasel_program()

コメントを残す

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