다익스트라 알고리즘(Dijkstra Algorithm)
다익스트라 알고리즘은 그래프에서 정점 사이의 최단 경로를 찾는 알고리즘 입니다. 프림 알고리즘과 동작방식이 비슷하지만 다익스트라 알고리즘은 시작 정점
에서 정점 V
까지의 경로의 길이
를 기준으로 최단 경로를 연결합니다. (단 가중치가 음수인 경우 사용할 수 없습니다.)
다익스트라 알고리즘은 다음과 같은 원리로 동작합니다.
- 시작 정점으로부터 각 정점까지의 경로의 길이를 저장할 배열을 만들고 모든 정점을 ∞(무한대)으로 초기화
- 시작 정점의 경로의 길이를 0으로 초기화하고 시작 정점을 최단 경로에 추가
- 최단 경로에 추가된 정점의 인접한 정점을 확인해서 경로의 길이를 갱신하고 최단 경로에 추가. (단 추가하려는 정점이 이미 최단 경로 안에 존재할 경우 경로의 길이가 갱신되기 이전의 경로보다 작다면 기존 경로를 폐기하고 현재 정점을 경유하도록 수정)
- 모든 정점이 최단 경로에 추가될 때까지 3번 반복
이해를 돕기 위해 예를 들어 보겠습니다.
위 그래프에서 정점 1
을 시작 정점으로 하겠습니다. 정점 1
로부터 각 정점까지의 경로 길이를 저장할 배열을 선언하고 경로 길이를 ∞(무한대)
으로 초기화 합니다. 시작 정점의 경로 길이는 0
으로 초기화 합니다. 정점 1
과 인접한 정점 0
, 2
, 6
의 가중치를 확인해서 배열에 있는 경로 길이보다 작다면 경로 길이를 갱신하고 최단 경로에 추가합니다. 간선 1-0
의 가중치는 8
입니다. 가중치 8
은 배열에 있는 경로 길이 ∞
보다 작으므로 0
의 경로 길이를 8
로 갱신합니다. 2
, 6
도 같은 방법으로 경로 길이를 갱신합니다.
0
과 인접한 정점은 5
가 유일하므로 1-0-5
까지의 경로 길이를 23(8+15)
으로 갱신하고 최단 경로에 추가합니다.
2
도 인접한 정점은 3
이 유일하므로 같은 방법으로 경로의 길이를 44(17+27)
으로 갱신하고 최단 경로에 추가합니다.
6
과 인접한 정점 4
, 3
에서 1-6-4
까지의 경로 길이는 33(10+23)
으로 갱신하고 최단 경로에 추가합니다.
여기서 1-6-3
까지의 경로 길이 35(10+25)
는 기존 경로 1-2-3
까지의 44(17+27)
보다 작으므로 경로 길이를 갱신 및 기존 경로를 폐기하고 최단 경로에 추가합니다.
5
와 인접한 정점은 4
가 유일하고 1-0-5-4
까지의 경로 길이 44(8+15+21)
는 기존 경로 33
보다 크므로 경로 길이를 갱신하지 않습니다.
4
와 인접한 정점 5
, 3
은 모두 기존 경로 길이보다 커지므로 경로 길이를 갱신하지 않습니다.
3
과 인접한 정점 4
, 6
도 마찬가지이므로 경로 길이를 갱신하지 않습니다.
이제 더 이상 탐색할 정점이 없으므로 최단 경로 탐색을 종료합니다.
다익스트라 알고리즘 구현
다익스트라 알고리즘의 구현은 프림 알고리즘과 비슷합니다. 정점과 인접한 간선을 (가중치, 정점)
으로 힙에 삽입하고, 힙에서 꺼낸 정점까지 경로 길이가 배열에 있는 기존 경로보다 작다면 경로 길이를 갱신하고 최단경로에 추가합니다. 이 과정을 힙이 비어 있을 때까지 반복하면 됩니다.
예제 코드에서는 최단 경로 그래프를 반환하도록 하였습니다. 다음은 다익스트라 알고리즘을 구현한 예제 코드입니다.
import sys
import heapq
import math
def dijkstra(start, graphs, weights):
shortest_path = [ [ 0 for _ in range(len(graphs)) ] for _ in range(len(graphs)) ] # 최단 경로 그래프
shortest_vertices = [-1 for _ in range(len(graphs))] # 최단 경로 연결 정보 (-1로 초기화)
shortest_weights = [ math.inf for _ in range(len(graphs)) ] # 최단 경로 가중치 정보 (무한대 값으로 초기화)
visited = set() # 방문 정보
heap = [] # 최소 힙
heapq.heappush(heap, (0, start)) # (가중치, 정점) 쌍으로 힙에 저장
shortest_weights[start] = 0 # 시작 정점 경로 길이 0으로 초기화
while len(heap) > 0:
pop = heapq.heappop(heap)
weight = pop[0]
vertex = pop[1]
visited.add(vertex)
for target, value in enumerate(graphs[vertex]): # 인접한 간선 순회
if target in visited:
continue
if value == 1 and shortest_weights[vertex] + weights[vertex][target] < shortest_weights[target]:
heapq.heappush(heap, (weights[vertex][target], target))
shortest_weights[target] = shortest_weights[vertex] + weights[vertex][target]
shortest_vertices[target] = vertex
# 최단 경로 그래프 생성
for a, b in enumerate(shortest_vertices):
if b >= 0:
shortest_path[a][b] = 1
shortest_path[b][a] = 1
# 출력용
for row in shortest_path:
print(row)
return shortest_path
if __name__ == "__main__":
graphs = [
[0, 1, 0, 0, 0, 1, 0],
[1, 0, 1, 0, 0, 0, 1],
[0, 1, 0, 1, 0, 0, 0],
[0, 0, 1, 0, 1, 0, 1],
[0, 0, 0, 1, 0, 1, 1],
[1, 0, 0, 0, 1, 0, 0],
[0, 1, 0, 1, 1, 0, 0],
]
weights = [
[0, 8, 0, 0, 0, 15, 0],
[8, 0, 17, 0, 0, 0, 10],
[0, 17, 0, 27, 0, 0, 0],
[0, 0, 27, 0, 29, 0, 25],
[0, 0, 0, 29, 0, 21, 23],
[15, 0, 0, 0, 21, 0, 0],
[0, 10, 0, 25, 23, 0, 0],
]
shortest_path = dijkstra(1, graphs, weights)
[0, 1, 0, 0, 0, 1, 0]
[1, 0, 1, 0, 0, 0, 1]
[0, 1, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 1]
[0, 0, 0, 0, 0, 0, 1]
[1, 0, 0, 0, 0, 0, 0]
[0, 1, 0, 1, 1, 0, 0]
댓글