1. Euclidean Distance (유클리드 거리)
두 점 (x1, y1)와 (x2, y2) 사이의 거리를 계산하는 공식이다.
2차원 뿐만 아니라, n차원에서도 같은 원리를 사용한다.
유클리드 거리 공식은 2차원뿐만 아니라, 3차원, 4차원, ..., n차원에서도 동일한 원리로 적용할 수 있다.
- 3차원으로 확장하면?
- n차원에서는?
즉, 차원이 늘어나도 각 좌표의 차이를 제곱한 후 더하고, 루트를 씌우는 원리는 변함없다.
# 2차원에서 유클리드 거리 계산 코드
import math
def euclidean_distance(p1, p2):
return math.sqrt((p2[0] - p1[0]) ** 2 + (p2[1] - p1[1]) ** 2)
p1 = (1, 2)
p2 = (4, 6)
print(euclidean_distance(p1, p2)) # 5.0
# 4차원에서 유클리드 거리 계산 코드
import math
def euclidean_distance(p1, p2):
return math.sqrt(sum((a - b) ** 2 for a, b in zip(p1, p2)))
p1 = (1, 2, 3, 4) # 4차원 좌표
p2 = (4, 6, 7, 8)
print(euclidean_distance(p1, p2))
2. MST(Minimum Spanning Tree, 최소 신장 트리)
- 그래프에서 모든 정점을 포함하면서, 간선 가중치의 합이 최소가 되는 트리이다.
- 사이클이 없고, 모든 노드를 연결하는 최소 비용 네트워크이다.
대표적인 알고리즘으로 2개가 있다.
크루스칼 알고리즘(Kruskal's Algorithm)
- 간선을 정렬 후, 유니온 파인드 사용
🌟원리
- 모든 간선을 가중치 기준으로 정렬
- 가장 가중치가 작은 간선부터 추가 (사이클이 생기지 않도록)
- 모든 노드가 연결될 때까지 반복
class DisjointSet:
"""유니온 파인드(Disjoint Set) 자료구조 구현"""
def __init__(self, n):
self.parent = list(range(n)) # 각 노드의 부모를 자기 자신으로 초기화
def find(self, x):
"""루트 노드를 찾는 함수 (경로 압축 적용)"""
if self.parent[x] != x:
self.parent[x] = self.find(self.parent[x]) # 경로 압축
return self.parent[x]
def union(self, a, b):
"""두 노드를 같은 집합으로 합치는 함수"""
root_a = self.find(a)
root_b = self.find(b)
if root_a != root_b:
self.parent[root_b] = root_a # 한 쪽을 다른 쪽의 부모로 설정
def kruskal(n, edges):
"""크루스칼 알고리즘으로 MST 구하기"""
edges.sort(key=lambda x: x[2]) # 간선 리스트를 가중치 기준으로 정렬
ds = DisjointSet(n) # 유니온 파인드 자료구조 생성
mst = [] # MST 간선 리스트
for u, v, weight in edges:
if ds.find(u) != ds.find(v): # 사이클이 생기지 않도록 확인
ds.union(u, v) # 두 정점을 합침
mst.append((u, v, weight)) # MST 추가
return mst # 최소 신장 트리 반환
edges = [(0, 1, 1), (1, 2, 3), (0, 2, 2)] # 그래프 간선(u, v, weight)
print(kruskal(3, edges)) # [(0, 1, 1), (0, 2, 2)]
MST(Minimum Spanning Tree, 최소 신장 트리): 크루스칼 알고리즘
프림 알고리즘 (Prim's Algorithm)
- 우선순위 큐(힙) 사용
🌟원리
- 임의의 정점에서 시작
- 현재 선택한 정점에서 가장 가중치가 작은 간선 추가
- 방문하지 않은 정점 중, 가중치가 가장 낮은 간선을 반복적으로 추가 (우선순위 큐 사용 -> 가장 작은 값부터 자동으로 선택됨)
import heapq
def prim(n, graph):
"""프림 알고리즘을 사용해 최소 신장 트리(MST) 찾기"""
visited = [False] * n # 방문한 정점을 저장하는 배열
min_heap = [(0, 0)] # (가중치, 정점) 형태로 저장하는 우선순위 큐
mst_cost = 0 # MST 총 비용
mst_edges = [] # MST 간선 리스트
while min_heap:
weight, u = heapq.heappop(min_heap) # 가장 작은 가중치를 가진 간선 선택
if visited[u]: # 이미 방문한 정점이면 스킵
continue
visited[u] = True # 방문 처리
mst_cost += weight # MST 비용 누적
for v, w in graph[u]: # 현재 정점과 연결된 모든 간선 탐색
if not visited[v]: # 방문하지 않은 정점만 추가
heapq.heappush(min_heap, (w, v))
return mst_cost # MST 총 비용 반환
graph = {
0: [(1, 1), (2, 2)],
1: [(0, 1), (2, 3)],
2: [(0, 2), (1, 3)]
}
print(prim(3, graph)) # MST 총 비용 출력
3. TSP(Traveling Salesman Problem, 외판원 문제)
한 번만 방문해서 모든 도시를 돌고 최소 비용으로 다시 출발점으로 돌아오는 문제이다.
완전 탐색(브루트포스) 풀이
from itertools import permutations
def tsp(graph):
"""모든 순열을 시도하는 완전 탐색 풀이 (O(n!))"""
n = len(graph)
min_cost = float('inf')
for path in permutations(range(n)): # 모든 경로 순열 생성
cost = sum(graph[path[i]][path[i + 1]] for i in range(n - 1)) + graph[path[-1]][path[0]]
min_cost = min(min_cost, cost) # 최소 비용 갱신
return min_cost # 최적 비용 반환
graph = [
[0, 10, 15, 20],
[10, 0, 35, 25],
[15, 35, 0, 30],
[20, 25, 30, 0]
]
print(tsp(graph)) # 최소 비용 출력
DP + 비트마스킹 풀이
INF = float('inf')
def tsp_dp(graph):
"""DP + 비트마스킹을 이용한 TSP 풀이"""
n = len(graph)
dp = [[None] * (1 << n) for _ in range(n)] # DP 테이블
def dfs(curr, visited):
"""현재 도시 curr에서, 방문 상태 visited일 때 최소 비용"""
if visited == (1 << n) - 1: # 모든 도시 방문 완료
return graph[curr][0] or INF # 다시 시작점(0)으로 돌아가는 비용
if dp[curr][visited] is not None: # 이미 계산한 값이면 반환
return dp[curr][visited]
min_cost = INF
for next_city in range(n): # 모든 도시 탐색
if visited & (1 << next_city) == 0 and graph[curr][next_city] > 0:
# 아직 방문 안 한 도시 & 경로가 존재하면 이동
cost = graph[curr][next_city] + dfs(next_city, visited | (1 << next_city))
min_cost = min(min_cost, cost) # 최소 비용 갱신
dp[curr][visited] = min_cost # DP 테이블에 저장
return min_cost
return dfs(0, 1) # 0번 도시에서 시작, 0번 도시 방문 상태(0001)
graph = [
[0, 10, 15, 20],
[10, 0, 35, 25],
[15, 35, 0, 30],
[20, 25, 30, 0]
]
print(tsp_dp(graph))
각 개념에 대해 정리
Euclidean Distance | 두 점 사이의 거리 계산 | math.sqrt((x2-x1)**2 + (y2-y1)**2) |
MST | 최소 비용으로 모든 노드 연결 | 크루스칼 알고리즘 프림 알고리즘 |
TSP | 모든 도시 방문 후 최소 비용 경로 찾기 | 완전 탐색 DP + 비트마스킹 |
각 개념의 연관 관계
1. Euclidean Distance -> MST & TSP
- 유클리드 거리는 그래프의 간선 가중치(거리) 계산에 자주 사용된다.
- MST(최소 신장 트리)나 TSP(외판원 문제)에서 도시 간 거리 계산에 활용이 가능하다.
2. MST vs TSP (그래프 문제)
- MST(최소 신장 트리) : 그래프에서 최소 비용으로 모든 노드를 연결하는 문제
- TSP(외판원 문제) : 모든 노드를 방문하고 최소 비용으로 다시 출발점으로 돌아오는 문제
- 두 문제 모두 그래프를 기반으로 하지만, MST는 사이클 없이 연결, TSP는 모든 노드를 한 번씩 방문 후 돌아옴(사이클 존재)
'코딩 테스트' 카테고리의 다른 글
브루트포스와 프루닝 (0) | 2025.02.11 |
---|---|
MST(Minimum Spanning Tree, 최소 신장 트리): 크루스칼 알고리즘 (1) | 2025.02.07 |
[python에서 priority queue 자료구조 사용하기 / heapq] (0) | 2025.02.03 |
스택과 큐 (0) | 2023.11.19 |
구간 합 (1) | 2023.11.18 |