from multiprocessing import freeze_supportimport randomfrom typing import Listfrom numpy import arangeimport matplotlib.pyplot as pltfrom pylab import meshgrid, cmfrom ch7.toolbox import ( crossover_blend, mutation_random_deviation, constraints, selection_rank_with_elite, crossover_operation, mutation_operation)# 최적화 대상 함수def func(x, y): return -10 * pow(x * y, 2) * x / (3 * pow(x * x * x / 4 + 1, 2) + pow(y, 4) + 1)# 개체 클래스 정의class Individual: def __init__(self, gene_list: List[float]) -> None: self.gene_list = [constraints(g) for g in gene_list] self.fitness = func(self.gene_list[0], self.gene_list[1]) def __str__(self): return f'x: {self.gene_list[0]}, y: {self.gene_list[1]}, fitness: {self.fitness}'# 교차 함수def crossover(parent1, parent2): child1_genes, child2_genes = crossover_blend(parent1.gene_list, parent2.gene_list, 0.5) return Individual(child1_genes), Individual(child2_genes)# 돌연변이 함수def mutate(ind): mutated_gene = mutation_random_deviation(ind.gene_list, 0, 1, 0.5) return Individual(mutated_gene)# 선택 함수def select(population): return selection_rank_with_elite(population, elite_size=2)# 랜덤 개체 생성def create_random(): return Individual([ round(random.uniform(-10, 10), 2), round(random.uniform(-10, 10), 2) ])# 메인 실행 함수def main(): POPULATION_SIZE_LIST = [6, 10, 20, 50] # 집단의 개체 수 CROSSOVER_PROBABILITY = 0.8 # 교차 확률 MUTATION_PROBABILITY = 0.2 # 돌연변이 확률 MAX_GENERATIONS = 10 # 최대 세대 수 X_range = arange(-10, 10, 0.2) # x축 범위 생성 Y_range = arange(-10, 10, 0.2) # y축 범위 생성 X, Y = meshgrid(X_range, Y_range) # 2차원 격자 좌표 생성 Z = func(X, Y) # 목적 함수의 함수값 계산 (히트맵용) # 각 개체 수에 대해 실험 반복 for POPULATION_SIZE in POPULATION_SIZE_LIST: # 초기 개체 집단 생성 first_population = [create_random() for _ in range(POPULATION_SIZE)] # 초기 최적 개체 선택 best_ind = random.choice(first_population) # 세대 수 초기화 generation_number = 0 # 현재 집단 복사 population = first_population.copy() # 최대 세대 수만큼 반복 while generation_number < MAX_GENERATIONS: # 세대 수 증가 generation_number += 1 # 선택 연산으로 자식 개체 생성 offspring = select(population) # 교차 확률에 따라 교차 연산 수행 crossed_offspring = crossover_operation(offspring, crossover, CROSSOVER_PROBABILITY) # 돌연변이이 확률에 따라 돌연변이 연산 수행 mutated_offspring = mutation_operation(crossed_offspring, mutate, MUTATION_PROBABILITY) # 새로운 세대 집단으로 교체 population = mutated_offspring.copy() # 이번 세대의 최고 개체 찾기 best_of_generation = max(population, key=lambda ind: ind.fitness) # 현재까지의 최고 개체보다 나으면 업데이트 if best_ind.fitness < best_of_generation.fitness: best_ind = best_of_generation # 히트맵 시각화 im = plt.imshow(Z, cmap=cm.bwr, extent=[-10, 10, -10, 10]) # 색상 바 추가 plt.colorbar(im) # 축 눈금 제거 plt.xticks([]) plt.yticks([]) # 그래프 제목 설정 plt.title(f"Population size: {POPULATION_SIZE}, Generation: {generation_number}\n" f"Best Individual: {round(best_ind.fitness, 2)}") # 개체들의 현재 위치 시각화 (검정색 점) plt.scatter([ind.gene_list[0] for ind in population], [ind.gene_list[1] for ind in population], color='black') # 그래프 출력 plt.show() # 콘솔에 최적 개체 정보 출력 print(f'Best Individual : {best_ind} for population size: {POPULATION_SIZE}')# ✅ 진입점 보호 (Windows 환경에서 반드시 필요)if __name__ == '__main__': freeze_support() main()