✨ 원본 μ½”λ“œ

import random
from typing import List
import numpy as np
import matplotlib.pyplot as plt
 
 
def constraints(g, min_=-10, max_=10):
    if max_ and g > max_:
        g = max_
    if min_ and g < min_:
        g = min_
    return g
 
 
def crossover_blend(g1, g2, alpha):
    shift = (1 + 2 * alpha) * random.random() - alpha
    new_g1 = (1 - shift) * g1 + shift * g2
    new_g2 = shift * g1 + (1 - shift) * g2
 
    return constraints(new_g1), constraints(new_g2)
 
 
def mutate_gaussian(g, mu, sigma):
    mutated_gene = g + random.gauss(mu, sigma)
    return constraints(mutated_gene)
 
 
def select_tournament(population, size):
    new_offspring = []
    for _ in range(len(population)):
        candidates = [random.choice(population) for _ in range(size)]
        new_offspring.append(max(candidates, key=lambda ind: ind.fitness))
    return new_offspring
 
 
def func(x):
    return np.sin(x) - .2 * abs(x)
 
 
def get_best(population):
    best = population[0]
    for ind in population:
        if ind.fitness > best.fitness:
            best = ind
    return best
 
 
def plot_population(population, number_of_population):
    best = get_best(population)
    x = np.linspace(-10, 10)
    plt.plot(x, func(x), '--', color='blue')
    plt.plot(
        [ind.get_gene() for ind in population],
        [ind.fitness for ind in population],
        'o', color='orange'
    )
    plt.plot([best.get_gene()], [best.fitness], 's', color='green')
    plt.title(f"Generation number {number_of_population}")
    plt.show()
    plt.close()
 
 
class Individual:
 
    def __init__(self, gene_list: List[float]) -> None:
        self.gene_list = gene_list
        self.fitness = func(self.gene_list[0])
 
    def get_gene(self):
        return self.gene_list[0]
 
 
def crossover(parent1, parent2):
    child1_gene, child2_gene = crossover_blend(parent1.get_gene(), parent2.get_gene(), 1)
    return Individual([child1_gene]), Individual([child2_gene])
 
 
def mutate(ind):
    mutated_gene = mutate_gaussian(ind.get_gene(), 0, 1)
    return Individual([mutated_gene])
 
 
def select(population):
    return select_tournament(population, size=3)
 
 
def create_random():
    return Individual([random.uniform(-10, 10)])
 
 
random.seed(52)
# random.seed(16)  # local maximum
POPULATION_SIZE = 10
CROSSOVER_PROBABILITY = .8
MUTATION_PROBABILITY = .1
MAX_GENERATIONS = 10
 
first_population = [create_random() for _ in range(POPULATION_SIZE)]
plot_population(first_population, 0)
 
generation_number = 0
 
population = first_population.copy()
 
while generation_number < MAX_GENERATIONS:
 
    generation_number += 1
 
    # SELECTION
    offspring = select(population)
 
    # CROSSOVER
    crossed_offspring = []
    for ind1, ind2 in zip(offspring[::2], offspring[1::2]):
        if random.random() < CROSSOVER_PROBABILITY:
            kid1, kid2 = crossover(ind1, ind2)
            crossed_offspring.append(kid1)
            crossed_offspring.append(kid2)
        else:
            crossed_offspring.append(ind1)
            crossed_offspring.append(ind2)
 
    # MUTATION
    mutated_offspring = []
    for mutant in crossed_offspring:
        if random.random() < MUTATION_PROBABILITY:
            new_mutant = mutate(mutant)
            mutated_offspring.append(new_mutant)
        else:
            mutated_offspring.append(mutant)
 
    population = mutated_offspring.copy()
 
    plot_population(population, generation_number)

✨ λ©”μΈν•¨μˆ˜ 1νšŒλ…

random.seed(52)
# random.seed(16)  # local maximum
 
POPULATION_SIZE = 10
CROSSOVER_PROBABILITY = .8
MUTATION_PROBABILITY = .1
MAX_GENERATIONS = 10
  • 0️⃣ GA Parameters μ •μ˜

first_population = [create_random() for _ in range(POPULATION_SIZE)]
  • 0️⃣ 초기 λͺ¨μ§‘단(개체ꡰ) 생성

  • 1️⃣ 적합도 ν‰κ°€λŠ” μ–΄λ””μžˆμ„κΉŒ? : μš°μ„  pass


plot_population(first_population, 0)
 
generation_number = 0
 
population = first_population.copy()
while generation_number < MAX_GENERATIONS:
    generation_number += 1
  • πŸ” 반볡문
  • μ’…λ£Œμ‘°κ±΄μ€ generation_numberκ°€ MAX_GENERATIONS에 도달할 λ•ŒκΉŒμ§€ (generation_number= MAX_GENERATIONSκ°€ 되면 μ’…λ£Œ)

    # SELECTION
    offspring = select(population)
  • 2️⃣ 선택 μ—°μ‚°

    # CROSSOVER
    crossed_offspring = []
    for ind1, ind2 in zip(offspring[::2], offspring[1::2]):
        if random.random() < CROSSOVER_PROBABILITY:
            kid1, kid2 = crossover(ind1, ind2)
            crossed_offspring.append(kid1)
            crossed_offspring.append(kid2)
        else:
            crossed_offspring.append(ind1)
            crossed_offspring.append(ind2)
  • 3️⃣ ꡐ차 μ—°μ‚°

    # MUTATION
    mutated_offspring = []
    for mutant in crossed_offspring:
        if random.random() < MUTATION_PROBABILITY:
            new_mutant = mutate(mutant)
            mutated_offspring.append(new_mutant)
        else:
            mutated_offspring.append(mutant)
  • 4️⃣ λŒμ—°λ³€μ΄ μ—°μ‚°

    population = mutated_offspring.copy()
  • 5️⃣ μƒˆλ‘œμš΄ λͺ¨μ§‘단 μ—…λ°μ΄νŠΈ

    plot_population(population, generation_number)

✨ λ©”μΈν•¨μˆ˜ 2νšŒλ…

random.seed(52)
# random.seed(16)  # local maximum
 
POPULATION_SIZE = 10
CROSSOVER_PROBABILITY = .8
MUTATION_PROBABILITY = .1
MAX_GENERATIONS = 10
  • 0️⃣ GA Parameters μ •μ˜

    β–Έ POPULATION_SIZE : 개체ꡰ 크기
    β–Έ CROSSOVER_PROBABILITY : ꡐ차 ν™•λ₯ 
    β–Έ MUTATION_PROBABILITY : 변이 ν™•λ₯ 
    β–Έ MAX_GENERATIONS : μ΅œλŒ€ μ„ΈλŒ€ 번호 (μ•Œκ³ λ¦¬μ¦˜ 반볡 횟수이자 μ’…λ£Œ 쑰건)


first_population = [create_random() for _ in range(POPULATION_SIZE)]
  • 0️⃣ 초기 λͺ¨μ§‘단(개체ꡰ) 생성
    1. POPULATION_SIZE 만큼 λ°˜λ³΅λ¬Έμ„ μ‹€ν–‰
    2. 각 λ°˜λ³΅μ—μ„œ create_random() ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•˜μ—¬ μƒˆλ‘œμš΄ 개체(Individual 객체)λ₯Ό 생성
    3. μƒμ„±λœ κ°œμ²΄λ“€μ„ λ¦¬μŠ€νŠΈμ— μ €μž₯ν•˜μ—¬ first_population으둜 λ°˜ν™˜

  • 1️⃣ 적합도 ν‰κ°€λŠ” μ–΄λ””μžˆμ„κΉŒ? : μš°μ„  pass

plot_population(first_population, 0)
  • 초기 λͺ¨μ§‘단 μ‹œκ°ν™”
generation_number = 0
  • ν˜„μž¬ μ„ΈλŒ€ 번호(Generation Number)λ₯Ό μ €μž₯ν•˜λŠ” 역할을 ν•  λ³€μˆ˜ generation_number μ„ μ–Έ 및 μ΄ˆκΈ°ν™”
population = first_population.copy()
  • 초기 λͺ¨μ§‘단을 λ³΅μ‚¬ν•˜μ—¬ population λ³€μˆ˜μ— μ €μž₯

πŸ’‘ μ™œ .copy()λ₯Ό μ‚¬μš©ν• κΉŒ?

  • μœ μ „ μ•Œκ³ λ¦¬μ¦˜μ˜ μ˜¬λ°”λ₯Έ 싀행을 보μž₯ν•˜κ³ , μ‹€ν—˜μ˜ μž¬ν˜„μ„±μ„ μœ μ§€ν•˜κΈ° μœ„ν•΄μ„œ
  • 초기 μ„ΈλŒ€μ™€ 이후 μ„ΈλŒ€ κ°„μ˜ 비ꡐλ₯Ό ν†΅ν•œ μ•Œκ³ λ¦¬μ¦˜μ˜ λ™μž‘ 확인을 μœ„ν•΄μ„œ
  • λ‹¨μˆœνžˆ population = first_population을 ν•˜λ©΄, 두 λ³€μˆ˜κ°€ 같은 리슀트λ₯Ό μ°Έμ‘°ν•˜κΈ° λ•Œλ¬Έμ— population이 λ°”λ€Œλ©΄ first_population도 영ν–₯을 λ°›μŒ
  • .copy()λ₯Ό μ‚¬μš©ν•˜λ©΄ μƒˆλ‘œμš΄ λ¦¬μŠ€νŠΈκ°€ μƒμ„±λ˜μ–΄ first_populationκ³Ό 독립적인 개체ꡰ으둜 μž‘λ™

πŸ’‘ .copy() vs .deepcopy()

  • .copy() : 얕은 볡사 (λ¦¬μŠ€νŠΈλŠ” μƒˆλ‘œ μƒμ„±λ˜μ§€λ§Œ, λ‚΄λΆ€ κ°œμ²΄λ“€μ€ κΈ°μ‘΄ 개체λ₯Ό μ°Έμ‘°)
  • copy.deepcopy() : κΉŠμ€ 볡사 (리슀트뿐만 μ•„λ‹ˆλΌ λ‚΄λΆ€ κ°œμ²΄λ„ μƒˆλ‘œ μƒμ„±λ˜μ–΄ μ™„μ „νžˆ 독립적)
  • μ½”λ“œμ—μ„œ .copy()(얕은 볡사)만 μ‚¬μš©ν•œ μ΄μœ λŠ” 개체ꡰ 리슀트λ₯Ό λ…λ¦½μ μœΌλ‘œ μœ μ§€ν•  ν•„μš”λŠ” μžˆμ§€λ§Œ, 개체(Individual) μžμ²΄λŠ” μƒˆλ‘œ λ§Œλ“€ ν•„μš”κ°€ μ—†κΈ° λ•Œλ¬Έ
  • μœ μ „ μ•Œκ³ λ¦¬μ¦˜μ—μ„œ λ§€ μ„ΈλŒ€λ§ˆλ‹€ κ°œμ²΄κ΅°μ„ μ™„μ „νžˆ μƒˆλ‘œμš΄ κ°œμ²΄λ“€λ‘œ κ΅μ²΄ν•˜λŠ” 것이 μ•„λ‹ˆλΌ, κΈ°μ‘΄ κ°œμ²΄λ“€μ„ λ³€ν˜•(선택, ꡐ차, λŒμ—°λ³€μ΄)ν•˜μ—¬ μ—…λ°μ΄νŠΈν•˜κΈ° λ•Œλ¬Έ

while generation_number < MAX_GENERATIONS:
  • μœ μ „ μ•Œκ³ λ¦¬μ¦˜μ˜ μ„ΈλŒ€ 진행을 μ œμ–΄ν•˜λŠ” πŸ” 반볡문
  • generation_number : ν˜„μž¬ μ§„ν–‰ 쀑인 μ„ΈλŒ€ 번호 (0λΆ€ν„° μ‹œμž‘)
  • MAX_GENERATIONS : μ΅œλŒ€ μ„ΈλŒ€ 수 (10)
  • whileλ¬Έ : ν˜„μž¬ μ„ΈλŒ€ λ²ˆν˜Έκ°€ MAX_GENERATIONS보닀 μž‘μœΌλ©΄ 반볡 μ‹€ν–‰ (= μœ μ „ μ•Œκ³ λ¦¬μ¦˜μ„ μ΅œλŒ€ MAX_GENERATIONS μ„ΈλŒ€κΉŒμ§€ μ‹€ν–‰)
    generation_number += 1
  • 반볡문이 싀행될 λ•Œλ§ˆλ‹€ μ„ΈλŒ€ 번호λ₯Ό 1 μ¦κ°€μ‹œν‚΄

    # SELECTION
    offspring = select(population)
  • 2️⃣ 선택 μ—°μ‚° μˆ˜ν–‰
  • select(population) ν•¨μˆ˜ : ν˜„μž¬ 개체ꡰ(population)μ—μ„œ λ‹€μŒ μ„ΈλŒ€λ‘œ 전달할 κ°œμ²΄λ“€μ„ 선택
  • offspring : μ„ νƒλœ κ°œμ²΄λ“€μ˜ 리슀트 β†’ ꡐ차(Crossover)와 λŒμ—°λ³€μ΄(Mutation)에 μ‚¬μš©λ¨

πŸ’‘ offspring λ³€μˆ˜μ— λŒ€ν•˜μ—¬

  • 제 개인적인 μƒκ°μ΄μ§€λ§Œ 선택 연산은 λΆ€λͺ¨(Parent)λ₯Ό μ„ νƒν•˜λŠ” 과정인데, offspring(μžμ†) μ΄λΌλŠ” 이름을 μ‚¬μš©ν•˜κ³  있기 λ•Œλ¬Έμ— μ–΄μƒ‰ν•˜λ‹€κ³  λŠκΌˆμŠ΅λ‹ˆλ‹€.
  • μ½”λ“œ μž‘μ„±μžλŠ” λ‹€μŒ μ„ΈλŒ€λ‘œ 전달될 개체 λΌλŠ” 의미둜 μ‚¬μš©ν•œ κ²ƒμœΌλ‘œ λ³΄μ΄λ‚˜, μ—„λ°€νžˆ λ§ν•˜λ©΄ select()μ—μ„œ λ°˜ν™˜λœ κ°œμ²΄λ“€μ€ 아직 λΆ€λͺ¨(Parent)이며, μžμ†(offspring)이 μ•„λ‹˜μ— μœ μ˜ν•˜μ…”μ•Ό ν•©λ‹ˆλ‹€.

    # CROSSOVER
    crossed_offspring = []
    for ind1, ind2 in zip(offspring[::2], offspring[1::2]):
        if random.random() < CROSSOVER_PROBABILITY:
            kid1, kid2 = crossover(ind1, ind2)
            crossed_offspring.append(kid1)
            crossed_offspring.append(kid2)
        else:
            crossed_offspring.append(ind1)
            crossed_offspring.append(ind2)
  • 3️⃣ ꡐ차 μ—°μ‚° μˆ˜ν–‰

    1. crossed_offspring = [] : μƒˆλ‘œμš΄ 개체ꡰ(ꡐ차 ν›„ μžμ†λ“€)을 μ €μž₯ν•  빈 리슀트 생성
    2. for ind1, ind2 in zip(offspring[::2], offspring[1::2]):
      • offspring[::2]: 짝수 번째 인덱슀의 개체 β†’ 첫 번째 λΆ€λͺ¨(ind1) κ·Έλ£Ή
      • offspring[1::2]: ν™€μˆ˜ 번째 인덱슀의 개체 β†’ 두 번째 λΆ€λͺ¨(ind2) κ·Έλ£Ή
      • zip()을 μ‚¬μš©ν•˜μ—¬ λΆ€λͺ¨ 쌍 생성
      • for λ£¨ν”„λŠ” λͺ¨λ“  λΆ€λͺ¨ μŒμ— λŒ€ν•΄ 반볡 μ‹€ν–‰
    3. if random.random() < CROSSOVER_PROBABILITY:
      • random.random() β†’ 0~1 μ‚¬μ΄μ˜ λ‚œμˆ˜ 생성
      • CROSSOVER_PROBABILITY β†’ ꡐ차 ν™•λ₯  (예 : 0.8 β†’ 80% ν™•λ₯ λ‘œ ꡐ배 μˆ˜ν–‰)
      • λ‚œμˆ˜ 값이 CROSSOVER_PROBABILITY보닀 μž‘μœΌλ©΄ ꡐ차 μˆ˜ν–‰, 크면 ꡐ차 없이 λΆ€λͺ¨λ₯Ό κ·ΈλŒ€λ‘œ μœ μ§€
    4. kid1, kid2 = crossover(ind1, ind2)
      • crossover() ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•΄ λΆ€λͺ¨ 개체 두 개(ind1, ind2)λ₯Ό κ΅μ°¨ν•˜μ—¬ μƒˆλ‘œμš΄ 두 개의 μžμ†(kid1, kid2) 생성
    5. crossed_offspring.append(kid1) crossed_offspring.append(kid2)
      • μƒμ„±λœ 두 개의 μžμ†(kid1, kid2)을 λ¦¬μŠ€νŠΈμ— μΆ”κ°€
      • μƒˆλ‘œμš΄ 개체ꡰ(crossed_offspring)에 μ €μž₯
    6. else: crossed_offspring.append(ind1) crossed_offspring.append(ind2)
      • ꡐ차가 λ°œμƒν•˜μ§€ μ•Šμ€ 경우 (80% ν™•λ₯ μ„ λ„˜κΈ΄ 경우)

      • λΆ€λͺ¨ 개체(ind1, ind2)λ₯Ό κ·ΈλŒ€λ‘œ λ‹€μŒ μ„ΈλŒ€μ— 전달

    πŸ’‘ 리슀트 μŠ¬λΌμ΄μ‹± : [start:stop:step]

    πŸ’‘ zip()

    • μ—¬λŸ¬ 개의 iterable(리슀트, νŠœν”Œ λ“±)을 λ™μ‹œμ— λ¬Άμ–΄μ£ΌλŠ” ν•¨μˆ˜
    • μ—¬λŸ¬ λ¦¬μŠ€νŠΈμ—μ„œ 각각 같은 μΈλ±μŠ€μ— μžˆλŠ” μš”μ†Œλ“€μ„ νŠœν”Œλ‘œ λ¬Άμ–΄μ„œ λ°˜ν™˜
    list1 = [1, 2, 3]
    list2 = ['a', 'b', 'c']
     
    zipped = zip(list1, list2)
    print(list(zipped))  
    # [(1, 'a'), (2, 'b'), (3, 'c')]

    # MUTATION
    mutated_offspring = []
    for mutant in crossed_offspring:
        if random.random() < MUTATION_PROBABILITY:
            new_mutant = mutate(mutant)
            mutated_offspring.append(new_mutant)
        else:
            mutated_offspring.append(mutant)
  • 4️⃣ λŒμ—°λ³€μ΄ μ—°μ‚° μˆ˜ν–‰
    1. mutated_offspring = [] : λŒμ—°λ³€μ΄κ°€ 적용된 κ°œμ²΄λ“€μ„ μ €μž₯ν•  빈 리슀트 생성
    2. for mutant in crossed_offspring: : ꡐ차(Crossover) 이후 μƒμ„±λœ 개체ꡰ(crossed_offspring)의 λͺ¨λ“  개체λ₯Ό ν•˜λ‚˜μ”© 순회
    3. if random.random() < MUTATION_PROBABILITY:
      • ν™•λ₯ (MUTATION_PROBABILITY)에 따라 λŒμ—°λ³€μ΄ μˆ˜ν–‰ μ—¬λΆ€ κ²°μ •
      • random.random() β†’ 0~1 μ‚¬μ΄μ˜ λ‚œμˆ˜λ₯Ό 생성
      • MUTATION_PROBABILITY보닀 μž‘μœΌλ©΄ λŒμ—°λ³€μ΄ λ°œμƒ
    4. new_mutant = mutate(mutant)
      • mutate() ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•˜μ—¬ μ„ νƒλœ 개체(mutant)에 λŒμ—°λ³€μ΄λ₯Ό 적용
      • λŒμ—°λ³€μ΄κ°€ λ°œμƒν•œ 경우, μƒˆλ‘œμš΄ λ³€μ΄λœ 개체(new_mutant) 생성
    5. mutated_offspring.append(new_mutant)
      • λŒμ—°λ³€μ΄κ°€ 적용된 개체λ₯Ό μƒˆλ‘œμš΄ 리슀트(mutated_offspring)에 μΆ”κ°€
    6. else: mutated_offspring.append(mutant)
      • λŒμ—°λ³€μ΄κ°€ λ°œμƒν•˜μ§€ μ•Šμ€ 경우, κΈ°μ‘΄ 개체(mutant)λ₯Ό κ·ΈλŒ€λ‘œ μœ μ§€ν•˜μ—¬ λ¦¬μŠ€νŠΈμ— μΆ”κ°€

    population = mutated_offspring.copy()
  • 5️⃣ μƒˆλ‘œμš΄ λͺ¨μ§‘단 ꡬ성
  • ꡐ차(Crossover)와 λŒμ—°λ³€μ΄(Mutation)을 거친 개체ꡰ(mutated_offspring)을 μƒˆλ‘œμš΄ 개체ꡰ(population)으둜 μ—…λ°μ΄νŠΈ
    plot_population(population, generation_number)
  • μƒˆλ‘œ μƒμ„±λœ λͺ¨μ§‘단 μ‹œκ°ν™”

  • πŸ” 반볡문으둜 λŒμ•„κ°€ μ’…λ£Œ 쑰건을 λ§Œμ‘±ν•  λ•Œ κΉŒμ§€ μœ„ κ³Όμ • 반볡

✨ ν•¨μˆ˜ μ‚΄νŽ΄λ³΄κΈ°

# Import part
 
import random                    # λ‚œμˆ˜ 생성 라이브러리
from typing import List          # 리슀트 νƒ€μž… 힌트λ₯Ό μ œκ³΅ν•˜λŠ” 라이브러리
import numpy as np               # ν–‰λ ¬ 및 μˆ˜ν•™ 연산을 μ§€μ›ν•˜λŠ” 라이브러리
import matplotlib.pyplot as plt  # 데이터 μ‹œκ°ν™”λ₯Ό μœ„ν•œ 라이브러리

πŸ’‘ typing λͺ¨λ“ˆ

  • νƒ€μž… νžŒνŒ…μ„ μœ„ν•΄ μ‚¬μš©λ˜λŠ” λͺ¨λ“ˆ
  • νƒ€μž… νžŒνŒ…μ΄λž€ 파이썬 μ½”λ“œλ₯Ό μž‘μ„±ν•  λ•Œ νƒ€μž…μ— λŒ€ν•œ 메타 정보λ₯Ό μ œκ³΅ν•˜λŠ” 것
  • Python은 동적 νƒ€μž… μ–Έμ–΄λΌμ„œ λ³€μˆ˜μ˜ νƒ€μž…μ„ λͺ…μ‹œν•˜μ§€ μ•Šμ•„λ„ λ˜μ§€λ§Œ, νƒ€μž… νžŒνŒ…μ„ ν•˜λ©΄ μ—¬λŸ¬ κ°€μ§€ μ‹€μš©μ μΈ μž₯점이 있음
    β–Έ μ½”λ“œ 가독성 ν–₯상
    β–Έ 였λ₯˜ λ°©μ§€ 및 정적 뢄석 κ°€λŠ₯
    β–Έ νŒ€ ν˜‘μ—… & μ½”λ“œ μœ μ§€λ³΄μˆ˜μ— 도움
    β–Έ 개발 도ꡬ 지원 (IDE μžλ™μ™„μ„±, μ½”λ“œ 뢄석 λ“±)
  • Python 3.9λΆ€ν„°λŠ” νƒ€μž… μ–΄λ…Έν…Œμ΄μ…˜μ˜ μ‚¬μš©μ„±μ΄ λŒ€ν­ κ°œμ„ λ˜μ–΄ 이제 typing λͺ¨λ“ˆμ—μ„œ List, Dict, Tuple, Setλ₯Ό λΆˆλŸ¬μ˜€μ§€ μ•Šκ³ , list, dict, tuple, set λ‚΄μž₯ νƒ€μž…μ„ 톡해 λ°”λ‘œ μ›μ†Œμ˜ νƒ€μž…κΉŒμ§€ λͺ…μ‹œν•΄μ€„ 수 μžˆμŠ΅λ‹ˆλ‹€.

def func(x):                          # μž…λ ₯ κ°’ `x`λ₯Ό 기반으둜 ν•¨μˆ˜λ₯Ό κ³„μ‚°ν•˜μ—¬ λ°˜ν™˜
    return np.sin(x) - .2 * abs(x)    # 적합도 ν•¨μˆ˜(Fitness Function)
 

class Individual:
    def __init__(self, gene_list: List[float]) -> None: # μ΄ˆκΈ°ν™” λ©”μ„œλ“œ / λ§€κ°œλ³€μˆ˜λŠ” gene_list / νƒ€μž… νžŒνŒ…μ„ ν†΅ν•œ 인코딩 방법 μ •μ˜
        self.gene_list = gene_list                      # 전달받은 μœ μ „ 정보λ₯Ό self.gene_list에 μ €μž₯
        self.fitness = func(self.gene_list[0])          # 첫 번째 μœ μ „μžλ₯Ό 기반으둜 적합도(fitness)λ₯Ό 평가
 
    def get_gene(self):
        return self.gene_list[0]                        # 개체의 첫 번째 μœ μ „μžλ₯Ό λ°˜ν™˜ν•˜λŠ” λ©”μ„œλ“œ
  • Individual 클래슀의 μ—­ν•  : μœ μ „ μ•Œκ³ λ¦¬μ¦˜μ—μ„œ ν•˜λ‚˜μ˜ 개체(Individual)λ₯Ό ν‘œν˜„ / μœ μ „ 정보(μœ μ „μž)와 적합도(Fitness)λ₯Ό μ €μž₯ν•˜κ³  관리

πŸ’‘νŒŒμ΄μ¬ Class

  • 클래슀 Class : 객체λ₯Ό λ§Œλ“€κΈ° μœ„ν•œ 섀계도
  • μΈμŠ€ν„΄μŠ€ Instance : 클래슀λ₯Ό 기반으둜 μƒμ„±λœ μ‹€μ œ 객체 / 같은 클래슀λ₯Ό μ‚¬μš©ν•˜μ—¬ λ§Œλ“  μ—¬λŸ¬ μΈμŠ€ν„΄μŠ€λ“€μ€ ν΄λž˜μŠ€λŠ” λ™μΌν•˜μ§€λ§Œ, 각 μΈμŠ€ν„΄μŠ€λŠ” λ©”λͺ¨λ¦¬μ—μ„œ λ…λ¦½μ μœΌλ‘œ μ‘΄μž¬ν•¨
  • λ©”μ„œλ“œ Method : 클래슀λ₯Ό λ§Œλ“€λ©΄μ„œ κ·Έ μ•ˆμ— 넣은 ν•¨μˆ˜ / μ‚¬μš©ν•˜λ €λ©΄ 객체.λ©”μ„œλ“œ()와 같은 ν˜•μ‹μœΌλ‘œ 호좜
  • __init__ λ©”μ„œλ“œ : μ΄ˆκΈ°ν™”(Initialize)λ©”μ„œλ“œλ‘œ, μ–΄λ–€ 클래슀의 객체가 λ§Œλ“€μ–΄μ§ˆ λ•Œ μžλ™μœΌλ‘œ ν˜ΈμΆœλ˜μ–΄ κ·Έ 객체가 κ°–κ²Œ 될 μ—¬λŸ¬ μ„±μ§ˆμ„ μ •ν•΄μ€Œ (μƒμ„±μž)
  • self ν‚€μ›Œλ“œ : 클래슀 λ‚΄λΆ€μ—μ„œ 자기 μžμ‹ (μΈμŠ€ν„΄μŠ€)을 가리킴 / selfλ₯Ό μ‚¬μš©ν•˜λ©΄ __init__ λ©”μ„œλ“œλΏλ§Œ μ•„λ‹ˆλΌ 같은 클래슀 λ‚΄ λ‹€λ₯Έ λ©”μ„œλ“œμ—μ„œλ„ 속성을 λ³€κ²½ν•˜κ±°λ‚˜ μ ‘κ·Όν•  수 있음

def get_best(population):                # μ£Όμ–΄μ§„ 개체 집단(population)μ—μ„œ κ°€μž₯ 높은 fitness 값을 κ°€μ§„ 개체λ₯Ό μ°Ύμ•„ λ°˜ν™˜ν•˜λŠ” ν•¨μˆ˜ (μ‹œκ°ν™”μ— ν™œ)
    best = population[0]                 # 첫 번째 개체λ₯Ό 초기 졜적 개체둜 μ„€μ •
    for ind in population:               # λͺ¨λ“  κ°œμ²΄μ— λŒ€ν•΄ 반볡
        if ind.fitness > best.fitness:   # ν˜„μž¬ best보닀 fitness 값이 더 크닀면
            best = ind                   # bestλ₯Ό ν˜„μž¬ 개체둜 μ—…λ°μ΄νŠΈ
    return best                          # μ΅œμ’…μ μœΌλ‘œ κ°€μž₯ fitnessκ°€ 높은 개체 λ°˜ν™˜

def plot_population(population, number_of_population):   # μ‹œκ°ν™” ν•¨μˆ˜
    best = get_best(population)                          # get_best ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•΄ μ£Όμ–΄μ§„ κ°œμ²΄κ΅°μ—μ„œ κ°€μž₯ μš°μˆ˜ν•œ 개체λ₯Ό 선택
    x = np.linspace(-10, 10)                             # -10μ—μ„œ 10κΉŒμ§€μ˜ 값을 일정 κ°„κ²©μœΌλ‘œ λ‚˜λˆˆ 배열을 생성
    plt.plot(x, func(x), '--', color='blue')             # 배열을 적합도 ν•¨μˆ˜μ— λŒ€μž…ν•΄ κ·Έλž˜ν”„ 그리기 / νŒŒλž€μƒ‰ 점
    plt.plot(
        [ind.get_gene() for ind in population],          # XμΆ• : 개체의 μœ μ „μž κ°’(`get_gene` λ©”μ„œλ“œ ν™œμš©)
        [ind.fitness for ind in population],             # YμΆ• : 개체의 적합도
        'o', color='orange'                              # 주황색 μ›μœΌλ‘œ ν‘œμ‹œ
    )
    plt.plot([best.get_gene()], [best.fitness], 's', color='green')   # 졜적 개체(best)의 μœ„μΉ˜ μ‹œκ°ν™”
    plt.title(f"Generation number {number_of_population}")            # κ·Έλž˜ν”„ 제λͺ© μ„€μ •
    plt.show()                                                        # κ·Έλž˜ν”„λ₯Ό 화면에 좜λ ₯
    plt.close()                                                       # κ·Έλž˜ν”„λ₯Ό λ‹«μ•„ λ©”λͺ¨λ¦¬λ₯Ό 확보
  • λ§€κ°œλ³€μˆ˜

    β–Έ population : ν˜„μž¬ μ„ΈλŒ€μ˜ 개체ꡰ(리슀트 ν˜•νƒœ)
    β–Έ number_of_population: ν˜„μž¬ μ„ΈλŒ€μ˜ 번호 (μ„ΈλŒ€ 수)


def create_random():                              # 초기 λͺ¨μ§‘단 생성에 ν™œμš©λ˜λŠ” ν•¨μˆ˜
    return Individual([random.uniform(-10, 10)])         
  • create_random ν•¨μˆ˜μ˜ μ—­ν•  : μƒˆλ‘œμš΄ 개체(Individual)λ₯Ό λ¬΄μž‘μœ„λ‘œ μƒμ„±ν•˜μ—¬ λ°˜ν™˜

  • λ™μž‘

    1. random.uniform(-10, 10) : -10μ—μ„œ 10 μ‚¬μ΄μ˜ λ¬΄μž‘μœ„ μ‹€μˆ˜λ₯Ό 생성
    2. Individual([random_value]) : μœ„μ—μ„œ μƒμ„±λœ 랜덀 κ°’μœΌλ‘œ Individual 객체λ₯Ό 생성 / λ§Œλ“€μ–΄μ§„ κ°œμ²΄λŠ” μœ μ „μžλ₯Ό 리슀트 ν˜•νƒœλ‘œ 가짐
    3. μƒμ„±λœ 개체λ₯Ό λ°˜ν™˜

def select_tournament(population, size):               # ν† λ„ˆλ¨ΌνŠΈ λ°©μ‹μœΌλ‘œ 선택 연산을 μ§„ν–‰ν•˜λŠ” ν•¨μˆ˜
    new_offspring = []                                 # μƒˆλ‘œμš΄ κ°œμ²΄λ“€μ„ μ €μž₯ν•  리슀트 μ΄ˆκΈ°ν™”
    for _ in range(len(population)):                   # κΈ°μ‘΄ λͺ¨μ§‘λ‹¨μ˜ 크기λ₯Ό λ§Œμ‘±ν•  λ•Œ κΉŒμ§€ μƒˆλ‘œμš΄ 개체 생성
        candidates = [random.choice(population) for _ in range(size)] 
        new_offspring.append(max(candidates, key=lambda ind: ind.fitness))
    return new_offspring                               # μ„ νƒλœ κ°œμ²΄λ“€λ‘œ κ΅¬μ„±λœ μƒˆλ‘œμš΄ κ°œμ²΄κ΅°μ„ λ°˜ν™˜
  • select_tournament ν•¨μˆ˜μ˜ μ—­ν•  : ν† λ„ˆλ¨ΌνŠΈ 선택 방식(Tournament Selection)을 μ‚¬μš©ν•˜μ—¬ μƒˆλ‘œμš΄ κ°œμ²΄κ΅°μ„ 선택

  • λ§€κ°œλ³€μˆ˜

    β–Έ population : ν˜„μž¬ 개체ꡰ(리슀트 ν˜•νƒœ)
    β–Έ size : ν† λ„ˆλ¨ΌνŠΈμ— μ°Έκ°€ν•  후보 개체의 수

  • μΆ”κ°€ 주석

    β–Έ candidates = [random.choice(population) for _ in range(size)]
    Β Β Β  ν˜„μž¬ 개체ꡰ populationμ—μ„œ size 개만큼 λ¬΄μž‘μœ„λ‘œ 후보λ₯Ό μ„ νƒν•˜μ—¬ candidates 리슀트λ₯Ό 생성

    β–Έ new_offspring.append(max(candidates, key=lambda ind: ind.fitness))
    Β Β Β  candidates λ¦¬μŠ€νŠΈμ—μ„œ κ°€μž₯ 높은 적합도λ₯Ό κ°€μ§„ 개체λ₯Ό μ„ νƒν•˜μ—¬ new_offspring에 μΆ”κ°€.

    πŸ’‘ max(iterable, key=function)

    • μ£Όμ–΄μ§„ 리슀트(iterable)μ—μ„œ κ°€μž₯ 큰 값을 μ°ΎλŠ” ν•¨μˆ˜
    • key=function을 μ‚¬μš©ν•˜λ©΄, 리슀트의 각 μš”μ†Œμ—μ„œ νŠΉμ • μ†μ„±μ΄λ‚˜ 기쀀을 기반으둜 비ꡐ할 수 있음

    πŸ’‘ Lambda

    • 이름 없이 μ‚¬μš©ν•  수 μžˆλŠ” 읡λͺ… ν•¨μˆ˜(Anonymous Function)
    • 일반적인 def ν‚€μ›Œλ“œλ₯Ό μ‚¬μš©ν•œ ν•¨μˆ˜μ™€ 달리, ν•œ μ€„λ‘œ κ°„κ²°ν•˜κ²Œ ν‘œν˜„
    • lambda λ§€κ°œλ³€μˆ˜: λ°˜ν™˜κ°’ ν˜•νƒœλ‘œ μ„ μ–Έ
    • return ν‚€μ›Œλ“œλŠ” μ‚¬μš©ν•˜μ§€ μ•ŠμœΌλ©°, ν•œ μ€„λ§Œ μž‘μ„±ν•  수 있음

  • 메인 ν•¨μˆ˜μ—μ„œ μ‚¬μš©
def select(population):
    return select_tournament(population, size=3)
  • select ν•¨μˆ˜λ₯Ό μ‹€ν–‰ν•˜λ©΄ β€˜select_tournamentβ€™μ˜ μ‹€ν–‰ κ²°κ³Όλ₯Ό λ°˜ν™˜
  • ν›„λ³΄λŠ” 3개

def constraints(g, min_ = -10, max_ = 10):   # μ œμ•½ν•¨μˆ˜ / constraints = μ œμ•½
    if max_ and g > max_:                    # λ§Œμ•½ max_κ°€ None이 μ•„λ‹ˆκ³  gκ°€ max_보닀 크닀면, gλ₯Ό max_둜 μ„€μ • (μƒν•œ μ œν•œ)
        g = max_                          
    if min_ and g < min_:                    # λ§Œμ•½ min_이 None이 μ•„λ‹ˆκ³  gκ°€ min_보닀 μž‘λ‹€λ©΄, gλ₯Ό min_으둜 μ„€μ • (ν•˜ν•œ μ œν•œ)
        g = min_
     
    return g                                 # μ œν•œμ΄ 적용된 gλ₯Ό λ°˜ν™˜
  • constraints ν•¨μˆ˜μ˜ μ—­ν•  : μ£Όμ–΄μ§„ κ°’ gλ₯Ό νŠΉμ • λ²”μœ„ λ‚΄λ‘œ μ œν•œ

  • λ§€κ°œλ³€μˆ˜

    β–Έ g : μ œν•œμ„ μ μš©ν•  κ°’
    β–Έ min_ : g κ°’μ˜ ν•˜ν•œ (μ΅œμ†Œκ°’), κΈ°λ³Έκ°’ -10
    β–Έ max_ : g κ°’μ˜ μƒν•œ (μ΅œλŒ€κ°’), κΈ°λ³Έκ°’ 10


def crossover_blend(g1, g2, alpha):
    shift = (1 + 2 * alpha) * random.random() - alpha         # [-alpha, 1 + alpha] λ²”μœ„μ—μ„œ λžœλ€ν•œ ν˜Όν•© λΉ„μœ¨ 생성  
 
    new_g1 = (1 - shift) * g1 + shi용
```python
def mutate(ind):
    mutated_gene = mutate_gaussian(ind.get_gene(), 0, 1) # 평균이 0이고 ν‘œμ€€ νŽΈμ°¨κ°€ 1인 μ •κ·œ λΆ„ν¬μ—μ„œ μƒ˜ν”Œμ„ μΆ”μΆœν•˜μ—¬ ind의 μœ μ „μž 값에 더함
    return Individual([mutated_gene])                    # 변이가 적용된 μƒˆλ‘œμš΄ μœ μ „μžλ‘œ μƒˆλ‘œμš΄ Individual 객체λ₯Ό μƒμ„±ν•˜μ—¬ λ°˜ν™˜
  • mutate ν•¨μˆ˜λ₯Ό μ‹€ν–‰ν•˜λ©΄ mutate_gaussian ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•˜μ—¬ λŒμ—°λ³€μ΄λ₯Ό μ μš©ν•˜κ³  변이가 적용된 μƒˆλ‘œμš΄ μœ μ „μžλ₯Ό λ°˜ν™˜

✨ λ©”μΈν•¨μˆ˜ 3νšŒλ…

  • 2νšŒλ…, ν•¨μˆ˜ μ΄ν•΄ν•˜κΈ°λ₯Ό 읽고 메인 μ½”λ“œλ₯Ό λ‹€μ‹œ 읽어보기
  • μ˜ˆμ‹œ (λ―Έμ™„) image