외판원 순회 문제 시뮬레이터 제작

담금질 기법(Simulated Annealing)과 유전 알고리즘(Genetic Algorithm)

분류

개인 프로젝트

사용한 기술

Python

진행 기간

2020.05 ~ 2020.08

관련 링크

Github

TSP-MetaHeuristic

외판원 순회 문제를 해결하는 모습

Problems

외판원 순회 문제(TSP, Traveling Salesman Problem)는 컴퓨터 과학 분야에서 중요한 문제로 취급되어 왔습니다.

완전 탐색으로 이 문제를 해결한다면, 도시의 수가 N개일 때, N!이라는 경우의 수를 모두 탐색해야지 답을 도출해낼 수 있었습니다. 만약 도시의 수가 20개라면 2,432,902,008,176,640,000이라는 매우 큰 경우의 수가 되고, 컴퓨터가 해결할 수 없게 됩니다.

그래서 다항 시간 내에 답을 "근사"할 수 있는 메타 휴리스틱(Meta-Heuristic) 기법을 활용해 이 문제를 해결해보려고 했습니다.

담금질 기법 - Simulated Annealing

전역 최적화 문제에 대한 메타 휴리스틱 기법 중 하나입니다.

광대한 탐색 범위 내에서 전역 최저점(Global Minimum)을 찾는 것을 목표로 동작합니다. 즉, 전역 최적해에 대한 근사치를 구해 냅니다.

담금질 기법의 동작 방법은 금속 공학의 담금질에서 파생되어 왔습니다. 금속재료를 가열한 뒤 조금씩 냉각하며 결함을 줄이는 것처럼, SA 알고리즘은 해를 반복적으로 개선시키며 현재의 해 근방에 있는 해를 임의로 찾습니다.

전역 최저점

지역 최저점과 전역 최저점

유전 알고리즘 - Genetic Algorithm

생명과학의 유전 현상에서 영감을 얻어 개발된 알고리즘입니다. 전역 최적화 문제에 대한 메타 휴리스틱 기법입니다.

다윈의 진화론을 지탱하는 가장 중요한 개념 중 하나인 "자연 선택"을 기반으로 동작합니다. 또한, 생물의 진화를 모방한 진화 연산의 대표적인 기법으로써, 실제 세대의 진화 과정에서 많은 부분을 따라합니다.

전역 최저점

유전 알고리즘

적용 및 결과

담금질 기법과 유전 알고리즘을 외판원 순회 문제에 적용시켜 봤습니다.

(아래 소개해드리는 코드는 모두 Python으로 구현되었습니다.)

담금질 기법

self.cities = TSP.generates_cities(30)  # 초기 상태를 정의

t = 100000.0  # 초기 온도
delta = 0.9999  # 온도 감률
k = 1  # 볼츠만 상수
lim = 0.005  # 임계 온도
n = len(self.cities)

best_e = TSP.tour_length(self.cities)  # 현재 상태를 평가
best_cities = cities = self.cities

while t > lim:
    e1 = TSP.tour_length(cities)

    # 현재 상태를 변이시킴

    e2 = TSP.tour_length(new_cities)  # 인접 상태의 평가 함수 값

    p = np.exp((e2 - e1) / (k * t))  # 인접 상태로 전이할 확률

    if p < random.random():  # 1 - p의 확률로 인접 상태로 전이
        e1 = e2
        cities = new_cities

    if e1 < best_e:  # 결과 값 갱신
        best_e = e1
        best_cities = cities

    t *= delta  # 현재 온도를 조금씩 낮춤

유전 알고리즘

self.cities = TSP.generates_cities(20)  # 초기 상태를 정의

best_cities = self.cities
best_e = TSP.tour_length(best_cities)

population = [self.Gene_Generation() for _ in range(100)]  # 한 세대당 100명

for g in range(100):  # 100세대 동안 진행
    population_sorted = self.Compute_Performance(population)  # 현재 세대를 평가하고 정렬

    if best_e > population_sorted[0][1]:  # 결과 값 갱신
        best_cities = population_sorted[0][0]
        best_e = population_sorted[0][1]

    children = self.Create_Children(population_sorted)  # 현재 세대를 기반으로 새로운 세대 생성
    new_generation = self.Mutate_Population(children)  # 새로운 세대를 일정 확률로 변이
    population = new_generation

더 자세한 코드는 깃허브에서 확인하실 수 있습니다.