Travelling Salesman Problem#
외판원 문제(travelling salesman problem, TSP)에 대한 자세한 내용은 블로그에서 참고해보시기 바랍니다.
이강우 & 김정자. (2012). EXCEL 2010 경영과학. 한경사, 390.
1(Yard) | 2(A) | 3(B) | 4(C) | 5(D) | |
---|---|---|---|---|---|
1(Yard) | - | 4.0 | 4.0 | 4.7 | 5.0 |
2(A) | 0.0 | - | 1.3 | 2.5 | 1.7 |
3(B) | 0.0 | 1.3 | - | 1.0 | 2.0 |
4(C) | 0.0 | 2.5 | 1.0 | - | 2.0 |
5(D) | 0.0 | 1.7 | 1.4 | 2.0 | - |
외판원 문제를 정식화하면 $n$개의 도시가 있을 때 인덱스 $i$와 $j$는 $n$개 만큼있으며 파리미터는 거리 $c_{ij}$, 의사결정변수로 $x_{ij}$와 $u_{i}$가 있으며 Miller-Tucker-Zemlin (MTZ) 공식은 다음과 같습니다.
위의 의사결정변수와 파라미터를 이용하여 총 거리의 합을 최소화하는 정수 계획법은 다음과 같습니다.
import os
import sys
# Add the parent directory for importing custom library
sys.path.append('../')
from pulp import *
from ortools.utils import output
n = 5
costs = [
[100, 4.0, 4.0, 4.7, 5.0],
[0.0, 1000, 1.3, 2.5, 1.7],
[0.0, 1.3, 100, 1.0, 1.4],
[0.0, 2.5, 1.0, 100, 2.0],
[0.0, 1.7, 1.4, 2.0, 100],
]
prob = LpProblem('Traveling Salesman Problem', LpMinimize)
indexs = [(i, j) for i in range(n) for j in range(n)]
x = LpVariable.dicts('x', indexs, lowBound=0, cat='Binary')
prob += lpSum([costs[i][j]*x[i, j] for i, j in indexs])
for j in range(n):
prob += lpSum([x[i, j] for i in range(n)]) == 1
for i in range(n):
prob += lpSum([x[i, j] for j in range(n)]) == 1
prob.solve()
print(value(prob.objective))
for i in prob.variables():
if i.varValue == 1:
print(i.name, '=', i.varValue)
---------------------------------------------------------------------------
ModuleNotFoundError Traceback (most recent call last)
Cell In[2], line 1
----> 1 from pulp import *
2 from ortools.utils import output
4 n = 5
ModuleNotFoundError: No module named 'pulp'
from pulp import *
# Initialize travelling salesman problem
prob = LpProblem('Travelling Salesman', LpMinimize)
n = len(cities)
indexs = [(i, j) for i in range(n) for j in range(n) if i != j]
# Creating decision variables
x = LpVariable.dicts('x', indexs, cat='Binary')
u = LpVariable.dicts('u', list(range(n)), lowBound=0, upBound=n-1, cat='Continuous')
# Objective function
prob += lpSum([cities[i][j] * x[(i,j)] for i, j in indexs])
# Constraints
for i in range(n):
prob += lpSum([x[(i,j)] for j in range(n) if i != j]) == 1
for j in range(n):
prob += lpSum([x[(i,j)] for i in range(n) if i != j]) == 1
for i in range(1, n):
for j in range(1, n):
if i != j:
prob += u[i] - u[j] + n * x[(i,j)] <= n - 1
# Solve problem
prob.solve()
print(value(prob.objective))
for i in prob.variables():
if i.name[0] == 'u':
print(i.name, '=', i.varValue)
elif i.varValue != 0:
print(i.name, '=', i.varValue)
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-12-eb891054b2dc> in <module>
4 prob = LpProblem('Travelling Salesman', LpMinimize)
5
----> 6 n = len(cities)
7 indexs = [(i, j) for i in range(n) for j in range(n) if i != j]
8
NameError: name 'cities' is not defined
위의 의사결정변수와 파라미터를 이용하여 총 거리의 합을 최소화하는 정수 계획법은 다음과 같습니다.
목적함수는 식(1)과 같습니다. 제약식은 다음과 같습니다. 제약식(2)과 (3)은 각 도시 $j$로 들어오는 도시는 1개가 되어야만 하고 각 도시 $i$에서 출발하는 도시는 1개가 되어야 한다는 걸 나타냅니다. 제약식(3)은 부등식 제약조건(inequality constraints)으로 MTZ 공식입니다. $x_{ij}=1$일 때, $(i,j)$에 대한 두 도시 사이는 순서는 $u_{j} \ge u_{i}$를 뜻합니다. 제약식(4)와 (5)는 의사결정변수 $x_{ij}$는 $0$과 $1$ 값만 가질 수 있는 이진변수이고, $u_{i}$는 $0$보다 크거나 같고 $n-1$보다 작거나 같은 값을 가질 수 있는 변수를 나타냅니다.