イタチプログラムを改善する
以前の投稿でイタチプログラムについて投稿した.
今回はそれを少し改善してアニメーションで過程が示せるようにしてみた.
# 必要なライブラリのインストールとインポート
!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()