개요


유전 알고리즘을 이용하여 초기 랜덤으로 배정된 문자열 집단에서 목표 문자열로 진화를 시켜보자.

해설

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017

소스코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
import numpy as np
from numpy.random import default_rng # 랜덤 뽑기, 중복 없이 뽑기
import random # 확률적으로 랜덤 뽑기
import sys

answer = sys.argv[1]
low = 65
high = 123

dawkins = np.array(
[ord(x) for x in answer if x != ' ']
)


space_position = [x[0] for x in enumerate(answer) if x[1] == ' ']


def chromosomes():
return np.random.randint(
low=low, high=high, size=len(dawkins)
)


def init_population(r: int):
return np.array([
chromosomes() for _ in range(r)
])


def diff(x: np.ndarray):
return np.subtract(dawkins, x)


def std(dif: np.ndarray, r=4):
return round(np.std(dif), r)


def fitness(population: np.ndarray):
return np.array([std(diff(x)) for x in population])


def description(label: str, min_loss: float, master_string: str):
print('--------------------------------------------')
print(label, 'min loss = ', min_loss)
print(label, 'master = ', master_string)
print('--------------------------------------------')


def ranking_selection(population: np.ndarray, fargs: np.ndarray, n: int):
return population[fargs][:n]


def sex(s1: np.ndarray, s2: np.ndarray, mw: int = 99):
p = np.zeros(len(s1), int)
d1 = diff(s1)
d2 = diff(s2)
for i in range(len(s1)):
[m] = random.choices(range(0, 2), weights=[mw, 1])
if m == 0: # 변이가 발생하지 않음
p[i] = s1[i] if abs(d1[i]) < abs(d2[i]) else s2[i]
else: # 변이 발생
p[i] = np.random.randint(low, high)
return p


def crossover(selection: np.ndarray, r: int, mw: int = 99):
high = len(selection)
offspring = np.empty((0, len(selection[0])), int)
while r > 0:
rng = default_rng()
x, y = rng.choice(high, size=2, replace=False)
child = sex(selection[x], selection[y], mw)
offspring = np.vstack((offspring, child))
r -= 1
return offspring


def ascii_to_str(gene: np.ndarray):
a = list(map(chr, gene))
for s in space_position:
a.insert(s, ' ')
return ''.join(a)


gen = 1 # 세대
n = 1000 # 인구수
m = 100 # 선택 수
mw = 99 # 변이 가중치 (mw:1)
population = init_population(n) # 인구 초기화
while True:
fit = fitness(population) # 적합도 계산
fargs = np.argsort(fit) # 순위 매기기
master_index = fargs[0] # 대표 유전자
master_string = ascii_to_str(population[master_index]) # 대표 문자열
description(f'Generation {gen}', fit[fargs[0]], master_string)
if np.array_equal(population[master_index], dawkins): # 정답을 찾았는가?
print(f'Generation {gen} found the answer: {answer}')
break
else:
selection = ranking_selection(population, fargs, m) # 순위 선택
population = crossover(selection, n, mw) # 교차
gen += 1

결과

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
python evolving_string.py "Richard Dawkins"

--------------------------------------------
Generation 1 min loss = 9.4169
Generation 1 master = QVTOQqS BerUgY`
--------------------------------------------
--------------------------------------------
Generation 2 min loss = 4.4193
Generation 2 master = Obcchwj Edqkcms
--------------------------------------------
--------------------------------------------
Generation 3 min loss = 2.2868
Generation 3 master = Ogai_q` Cdqkdms
--------------------------------------------
--------------------------------------------
Generation 4 min loss = 1.1628
Generation 4 master = Oicg^qd C_vkfns
--------------------------------------------
--------------------------------------------
Generation 5 min loss = 0.6227
Generation 5 master = Qichard D_vkhms
--------------------------------------------
--------------------------------------------
Generation 6 min loss = 0.4574
Generation 6 master = Qichard Dbwkhns
--------------------------------------------
--------------------------------------------
Generation 7 min loss = 0.4574
Generation 7 master = Qichard Dbwkjns
--------------------------------------------
--------------------------------------------
Generation 8 min loss = 0.4574
Generation 8 master = Qichard Dbwkhns
--------------------------------------------
--------------------------------------------
Generation 9 min loss = 0.4574
Generation 9 master = Qichard Dbwkhns
--------------------------------------------
--------------------------------------------
Generation 10 min loss = 0.4103
Generation 10 master = Qichard D`wkhns
--------------------------------------------
--------------------------------------------
Generation 11 min loss = 0.378
Generation 11 master = Qichard Dbwkins
--------------------------------------------
--------------------------------------------
Generation 12 min loss = 0.3499
Generation 12 master = Qichard Dawkhns
--------------------------------------------
--------------------------------------------
Generation 13 min loss = 0.2575
Generation 13 master = Richard Dbwkins
--------------------------------------------
--------------------------------------------
Generation 14 min loss = 0.0
Generation 14 master = Richard Dawkins
--------------------------------------------
Generation 14 found the answer: Richard Dawkins