๋ถ๋ฅ:์๊ณ ๋ฆฌ์ฆ
์ด ๋ฌธ์์ ์๋ณธ์ ์ธ๋ถ ์ํค์์ ๊ฐ์ ธ์์ต๋๋ค.
TSP์์ ๋์ด์ด
1. ๊ฐ์[ํธ์ง]
์ธํ์ ์ํ ๋ฌธ์ (Traveling Salesperson Problem)๋ ์กฐํฉ ์ต์ ํ ๋ฌธ์ ์ ์ผ์ข
์ผ๋ก, NP-๋ํด ์งํฉ์ ์ํ๊ธฐ ๋๋ฌธ์ ๊ณ์ฐ ์ด๋ก ์์ ํด๋ฅผ ๊ตฌํ๊ธฐ ์ด๋ ค์ด ๋ฌธ์ ์ ๋ํ์ ์ธ ์ฌ๋ก๋ก ๋ง์ด ๋ค๋ฃฌ๋ค.
์ธํ์ ๋ฌธ์ ๋ ๋ค์๊ณผ ๊ฐ์ด ์ค๋ช ํ ์ ์๋ค. ์ด๋ค ์ธํ์์ด n๊ฐ์ ๋์๋ฅผ ๋ฐฉ๋ฌธํ ๊ณํ์ ์๋ฆฝํ๊ณ ์๋ค๊ณ ๊ฐ์ ํ์. ๊ฐ ๋์๋ ๋ค๋ฅธ ๋ชจ๋ ๋์์ ๋๋ก๋ก ์ฐ๊ฒฐ๋์ด ์๋ค. ์ถ์ฅ ๋น์ฉ์ ์ต์๋ก ์ค์ด๊ธฐ ์ํ์ฌ ์ธํ์์ด ๊ฑฐ์ฃผํ๊ณ ์๋ ๋์์์ ๊ฐ ๋์๋ฅผ ํ ๋ฒ์ฉ๋ง ๋ฐฉ๋ฌธํ๊ณ ๋ค์ ์ถ๋ฐํ ๋์๋ก ๋์์ค๋ ๊ฐ์ฅ ์ต์ ๋น์ฉ์ ์ผ์ฃผ์ฌํ ๊ฒฝ๋ก๋ฅผ ์ฐพ๊ณ ์ ํ๋ค.
<ํ์ด์ฌ ์๊ณ ๋ฆฌ์ฆ ์ธํฐ๋ทฐ> p.320, ์ฑ ๋ง, 2020
๊ทธ๋ฆผ๊ณผ ๊ฐ์ด ๊ฐ ๋์์ ์์น๊ฐ ํ์๋ ๋ฏธ๊ตญ ์ง๋๊ฐ ์๋ค. ๊ฐ ๋์๋ฅผ ํ ๋ฒ์ฉ ๋ฐฉ๋ฌธํ๋ค๊ณ ํ์ ๋, ์ด๋ค ์์๋ก ๋ฐฉ๋ฌธํด์ผ ๊ฐ์ฅ ์งง์ ๊ฑฐ๋ฆฌ๊ฐ ๋ ๊น?๋ง์ฝ ๋์๊ฐ 20๊ฐ๋ผ๊ณ ํ ๋ ์ด ๋ฌธ์ ์ ์ ๋ต์ ์ฐพ๊ธฐ ์ํด ๋ค๋ ์ผ ํ๋ ์ด ๊ฒฝ๋ก์ ์๋ ๋ค. ์ด ๊ฐ์ ์ผ๋ง๋ ๋ ๊น? ์ด๋ค. ๊ทธ๋ฌ๋๊น ์ฝ 240๊ฒฝ ๋ฒ์ ๊ฒฝ๋ก๋ฅผ ๋ค๋ ๋ด์ผ ๊ฐ์ฅ ์งง์ ๊ฒฝ๋ก๋ฅผ ์ฐพ์ ์ ์๋ค. ๋ฌธ์ ๋ ๋จ์ํ์ง๋ง ์ ๋ต์ ์ค๋ก ์์ฒญ๋๋ค.
์ธํ์ ๋ฌธ์ ๋ ๋ค์๊ณผ ๊ฐ์ด ์ค๋ช ํ ์ ์๋ค. ์ด๋ค ์ธํ์์ด n๊ฐ์ ๋์๋ฅผ ๋ฐฉ๋ฌธํ ๊ณํ์ ์๋ฆฝํ๊ณ ์๋ค๊ณ ๊ฐ์ ํ์. ๊ฐ ๋์๋ ๋ค๋ฅธ ๋ชจ๋ ๋์์ ๋๋ก๋ก ์ฐ๊ฒฐ๋์ด ์๋ค. ์ถ์ฅ ๋น์ฉ์ ์ต์๋ก ์ค์ด๊ธฐ ์ํ์ฌ ์ธํ์์ด ๊ฑฐ์ฃผํ๊ณ ์๋ ๋์์์ ๊ฐ ๋์๋ฅผ ํ ๋ฒ์ฉ๋ง ๋ฐฉ๋ฌธํ๊ณ ๋ค์ ์ถ๋ฐํ ๋์๋ก ๋์์ค๋ ๊ฐ์ฅ ์ต์ ๋น์ฉ์ ์ผ์ฃผ์ฌํ ๊ฒฝ๋ก๋ฅผ ์ฐพ๊ณ ์ ํ๋ค.
<ํ์ด์ฌ ์๊ณ ๋ฆฌ์ฆ ์ธํฐ๋ทฐ> p.320, ์ฑ ๋ง, 2020
๊ทธ๋ฆผ๊ณผ ๊ฐ์ด ๊ฐ ๋์์ ์์น๊ฐ ํ์๋ ๋ฏธ๊ตญ ์ง๋๊ฐ ์๋ค. ๊ฐ ๋์๋ฅผ ํ ๋ฒ์ฉ ๋ฐฉ๋ฌธํ๋ค๊ณ ํ์ ๋, ์ด๋ค ์์๋ก ๋ฐฉ๋ฌธํด์ผ ๊ฐ์ฅ ์งง์ ๊ฑฐ๋ฆฌ๊ฐ ๋ ๊น?๋ง์ฝ ๋์๊ฐ 20๊ฐ๋ผ๊ณ ํ ๋ ์ด ๋ฌธ์ ์ ์ ๋ต์ ์ฐพ๊ธฐ ์ํด ๋ค๋ ์ผ ํ๋ ์ด ๊ฒฝ๋ก์ ์๋ ๋ค. ์ด ๊ฐ์ ์ผ๋ง๋ ๋ ๊น? ์ด๋ค. ๊ทธ๋ฌ๋๊น ์ฝ 240๊ฒฝ ๋ฒ์ ๊ฒฝ๋ก๋ฅผ ๋ค๋ ๋ด์ผ ๊ฐ์ฅ ์งง์ ๊ฒฝ๋ก๋ฅผ ์ฐพ์ ์ ์๋ค. ๋ฌธ์ ๋ ๋จ์ํ์ง๋ง ์ ๋ต์ ์ค๋ก ์์ฒญ๋๋ค.
2. ๋ฌธ์ ์ ์ ์[ํธ์ง]
์ธํ์ ์ํ ๋ฌธ์ ๋ ๋ค์๊ณผ ๊ฐ์ด ๊ทธ๋ํ ์ด๋ก ์ ์ฉ์ด๋ก ์๋ฐํ๊ฒ ์ ์ํ ์ ์๋ค.
์ฃผ์ด์ง ์์ ๊ทธ๋ํ G=(V, E)๊ฐ, ์ฐ๊ฒฐ๋์ด ์๊ณ (connected) ๊ฐ์ค์น๊ฐ ์๋(weighted) ์์ ํ(complete) ๊ทธ๋ํ๋ผ๊ณ ๊ฐ์ ํ์. ์ด ๊ทธ๋ํ์์ ์ถ๋ฐ ์ ์ ์์ ๋ค๋ฅธ ๋ชจ๋ ์ ์ ๋ค์ ๋ฐฉ๋ฌธํ๊ณ ์๋์ ์ถ๋ฐ ์ ์ ์ผ๋ก ๋๋์ ์ค๋ ์ํ ๊ฒฝ๋ก๋ค ์ค์์ ๊ฐ์ค์น์ ํฉ์ด ์ต์๊ฐ ๋๋ ์ํ ๊ฒฝ๋ก๋ฅผ ์ฐพ์๋ผ.
์ฃผ์ด์ง ์์ ๊ทธ๋ํ G=(V, E)๊ฐ, ์ฐ๊ฒฐ๋์ด ์๊ณ (connected) ๊ฐ์ค์น๊ฐ ์๋(weighted) ์์ ํ(complete) ๊ทธ๋ํ๋ผ๊ณ ๊ฐ์ ํ์. ์ด ๊ทธ๋ํ์์ ์ถ๋ฐ ์ ์ ์์ ๋ค๋ฅธ ๋ชจ๋ ์ ์ ๋ค์ ๋ฐฉ๋ฌธํ๊ณ ์๋์ ์ถ๋ฐ ์ ์ ์ผ๋ก ๋๋์ ์ค๋ ์ํ ๊ฒฝ๋ก๋ค ์ค์์ ๊ฐ์ค์น์ ํฉ์ด ์ต์๊ฐ ๋๋ ์ํ ๊ฒฝ๋ก๋ฅผ ์ฐพ์๋ผ.
3. ๋ฌธ์ ์ ํด๊ฒฐ[ํธ์ง]
์ธํ์ ์ํ ๋ฌธ์ ๋ NP-๋ํด ์งํฉ์ ์ํ๋ ๋ฌธ์ ๋ผ๋ ๊ฒ์ด ์ฆ๋ช
๋์๋ค. ๋คํญ ์๊ฐ์ ์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ์ ์๋ ํด๊ฒฐ์ฑ
์ ์๋ฌด๋ ์ฐพ์ง ๋ชปํ์ผ๋ฉฐ, ๋คํญ ์๊ฐ์ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ์ง ๋ชปํ๋ค๋ ๊ฒ๋ ์๋ฌด๋ ์ฆ๋ช
ํ์ง ๋ชปํ๋ค. ๊ทธ๋์ ๋ํดํ๋ค๊ณ ํ๋ ๊ฒ์ด๋ค.
3.1. ๋์ ๊ณํ์ผ๋ก ํ๊ธฐ[ํธ์ง]
์ธํ์ ์ํ ๋ฌธ์ ์ ์ต์ ํด๋ ๋์ ๊ณํ(Dynamic Programming)์ผ๋ก ์ง์ ์๊ฐ์ ํ ์ ์๋ค. ๋ค์์ ๋์ ๊ณํ์ผ๋ก ์ธํ์ ์ํ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ํ์ด์ฌ ์์ค ์ฝ๋์ด๋ค.
def travel (W):
n = len(W) - 1
size = 2 ** (n - 1)
D = [[0] * size for _ in range(n + 1)]
P = [[0] * size for _ in range(n + 1)]
for i in range(2, n + 1):
D[i][0] = W[i][1]
for k in range(1, n - 1):
for A in range(1, size):
if (count(A, n) == k):
for i in range(2, n + 1):
if (not isIn(i, A)):
D[i][A], P[i][A] = minimum(W, D, i, A)
A = size - 1
D[1][A], P[1][A] = minimum(W, D, 1, A)
return D, P
def minimum (W, D, i, A):
minValue = INF
minJ = 1
n = len(W) - 1
for j in range(2, n + 1):
if (isIn(j, A)):
m = W[i][j] + D[j][diff(A, j)]
if (minValue > m):
minValue = m
minJ = j
return minValue, minJ์ ํ์ด๋ฒ์ ๋ํ ์์ธํ ํด์ค๊ณผ ์์ค ์ฝ๋์ ๋ํ ์ค๋ช
์ ์ด ์์์ ์ฐธ๊ณ ํ๋ฉด ๋๋ค. [1]3.2. ๋ถ๊ธฐ ํ์ ์ผ๋ก ํ๊ธฐ[ํธ์ง]
์ธํ์ ์ํ ๋ฌธ์ ์ ์ต์ ํด๋ ๋ถ๊ธฐ ํ์ (Branch-and-Bound)์ผ๋ก ์ง์ ์๊ฐ์ ํ ์ ์๋ค. ๋ค์์ ๋์ ๊ณํ์ผ๋ก ์ธํ์ ์ํ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ํ์ด์ฌ ์์ค ์ฝ๋์ด๋ค.
def travel2 (W):
global minlength, opttour
n = len(W) - 1
PQ = PriorityQueue()
v = SSTNode(0)
v.path = [1]
v.bound = bound(v, W)
minlength = INF
PQ.put((v.bound, v))
while (not PQ.empty()):
v = PQ.get()[1]
if (v.bound < minlength):
for i in range(2, n + 1):
if (v.contains(i)):
continue
u = SSTNode(v.level + 1)
u.path = v.path[:]
u.path.append(i)
if (u.level == n - 2):
for k in range(2, n + 1):
if (not u.contains(k)):
u.path.append(k)
u.path.append(1)
if (length(u.path, W) < minlength):
minlength = length(u.path, W)
opttour = u.path[:]
else:
u.bound = bound(u, W)
if (u.bound < minlength):
PQ.put((u.bound, u))
def bound(v, W):
n = len(W) - 1
total = length(v.path, W)
for i in range(1, n + 1):
if (hasOutgoing(i, v.path)):
continue
min = INF
for j in range(1, n + 1):
if (i == j): continue
if (hasIncoming(j, v.path)): continue
if (j == 1 and i == v.path[len(v.path) - 1]): continue
if (min > W[i][j]): min = W[i][j]
total += min
return total์ ํ์ด๋ฒ์ ๋ํ ์์ธํ ํด์ค๊ณผ ์์ค ์ฝ๋์ ๋ํ ์ค๋ช
์ ์ด ์์์ ์ฐธ๊ณ ํ๋ฉด ๋๋ค. [2]