Showing
7 changed files
with
197 additions
and
0 deletions
floyd-warshall/.DS_Store
0 → 100644
No preview for this file type
floyd-warshall/README.md
0 → 100644
1 | +# Floyd Warshall Algorithm (플로이드-워셜) | ||
2 | + | ||
3 | +> 모든 정점 사이의 최단 거리를 찾는 탐색 알고리즘 | ||
4 | + | ||
5 | +## 아이디어 | ||
6 | + | ||
7 | +* 두 정점 사이의 최단 경로는 **다른 한 정점을 거치거나**, **거치지 않는 경로** 중 하나이다. | ||
8 | + | ||
9 | +* 만약 경유지를 거친다면 최단 경로를 이루는 부분 경로 역시 최단 경로이다. 바꿔 말해 A - B의 최단 경로가 A-K-B라면 A-K와 K-B도 각각 최단 경로이다. *(Optimal Substructure)* | ||
10 | + | ||
11 | +## 알고리즘 설계 및 구현 | ||
12 | + | ||
13 | +1. 한 정점에서 다른 정점으로 바로 갈 수 있으면 최소거리를, 갈 수 없으면 INF값을 담고 있는 2차원 행렬 D을 초기화한다. | ||
14 | + | ||
15 | +2. 각 정점이 경유지 K를 지날 때마다 최단 거리를 계산하여 행렬 D를 갱신한다. | ||
16 | + | ||
17 | +3. Dynamic Programming으로 해결하며, 점화식은 `D[i][j] = min(D[i][j], D[i][k] + D[k][j])` 이다. | ||
18 | + | ||
19 | +## 시간 복잡도 | ||
20 | + | ||
21 | +모든 가능한 경유지에 대해서 모든 정점 모든 정점으로 가는 최단 거리를 확인하므로, 시간복잡도는 `O(n^3)` 이다. | ||
22 | + | ||
23 | +## 예시 | ||
24 | + | ||
25 | +<p align="center"> | ||
26 | + <img src = "./images/floyd_1.png" width="40%" alt="floyd_1"> | ||
27 | +</p> | ||
28 | + | ||
29 | +<p align="center"> | ||
30 | + <img src = "./images/floyd_2.png" width="40%" alt="floyd_2"> | ||
31 | +</p> | ||
32 | + | ||
33 | +## 코드 - Python | ||
34 | + | ||
35 | +``` python | ||
36 | +import copy | ||
37 | + | ||
38 | +# Floyd-Warshall Algorithm: 모든 정점에서 모든 정점까지의 최단 경로 알고리즘 | ||
39 | +def floyd_warshall(g: list[list[int]], n: int) -> list[list[int]: | ||
40 | + dist = copy.deepcopy(g) # dist: 최단 경로의 길이를 담은 행렬 | ||
41 | + | ||
42 | + for k in range(0, n): # 거치는 점 | ||
43 | + for i in range(0, n): # 시작점 | ||
44 | + for j in range(0, n): # 끝점 | ||
45 | + dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]) | ||
46 | + return dist | ||
47 | + | ||
48 | +# print 2-D Matrix | ||
49 | +def printMatrix(d: list[list[int]]) -> None: | ||
50 | + n = len(d[0]) | ||
51 | + | ||
52 | + for i in range(0, n): | ||
53 | + for j in range(0, n): | ||
54 | + print(d[i][j], end=' ') | ||
55 | + print() | ||
56 | + | ||
57 | +# Testcase | ||
58 | +INF = 1000 | ||
59 | +g = [ [0, 1, INF, 1, 5], | ||
60 | + [9, 0, 3, 2, INF], | ||
61 | + [INF, INF, 0, 4, INF], | ||
62 | + [INF, INF, 2, 0, 3], | ||
63 | + [3, INF, INF, INF, 0] ] | ||
64 | + | ||
65 | +dist = floyd_warshall(g, 5) | ||
66 | +printMatrix(dist) | ||
67 | +``` | ||
68 | + | ||
69 | +## Floyd's Algorithm을 통한 최단 경로 출력 | ||
70 | + | ||
71 | +Optimal Substructure을 활용하면 경유하는 정점들을 알 수 있다. | ||
72 | + | ||
73 | +P[i][j] = 중간에 정점이 없으면 0, 있으면 그 중 가장 큰 인덱스 | ||
74 | + | ||
75 | +### 예시 | ||
76 | + | ||
77 | +<p align="center"> | ||
78 | + <img src = "./images/floyd_3.png" width="40%" alt="floyd_3"> | ||
79 | +</p> | ||
80 | + | ||
81 | +``` | ||
82 | + path(5, 3) = 4 | ||
83 | + path(5, 4) = 1 | ||
84 | + path(5, 1) = 0 | ||
85 | + v1 | ||
86 | + path(1, 4) = 0 | ||
87 | + v4 | ||
88 | + path(4, 3) = 0 | ||
89 | + | ||
90 | +즉, v5에서 v3까지 최단 경로는 v5 -> v1 -> v4 -> v3 이다. | ||
91 | +``` | ||
92 | + | ||
93 | +### 코드 | ||
94 | + | ||
95 | +```python | ||
96 | +# Floyd-Warshall 알고리즘을 통한 최단 경로를 구성하는 정점 기록 | ||
97 | +def floyd_warshall_2(g: list[list[int]], n: int): | ||
98 | + dist = copy.deepcopy(g) | ||
99 | + p = [[0] * n for _ in range(n)] # p: 중간에 정점이 없으면 0, 있으면 그 중 가장 큰 인덱스를 포함한 행렬 | ||
100 | + | ||
101 | + for k in range(0, n): | ||
102 | + for i in range(0, n): | ||
103 | + for j in range(0, n): | ||
104 | + if dist[i][k] + dist[k][j] < dist[i][j]: | ||
105 | + p[i][j] = k + 1 # 경로 중 가장 큰 인덱스 저장 | ||
106 | + dist[i][j] = dist[i][k] + dist[k][j] | ||
107 | + return dist, p | ||
108 | + | ||
109 | +# print 2-D Matrix | ||
110 | +def printMatrix(d: list[list[int]]) -> None: | ||
111 | + n = len(d[0]) | ||
112 | + | ||
113 | + for i in range(0, n): | ||
114 | + for j in range(0, n): | ||
115 | + print(d[i][j], end=' ') | ||
116 | + print() | ||
117 | + | ||
118 | +# 특정 두 정점 사이의 최단 경로 출력 | ||
119 | +def printPath(p: list[list[int]], q: int, r: int) -> None: | ||
120 | + if p[q-1][r-1] != 0: # 최단 경로가 경유지를 지날 경우 | ||
121 | + printPath(p, q, p[q-1][r-1]) # 시작 정점에서 겅유하는 정점까지의 경로 검사 | ||
122 | + print(f'v{p[q-1][r-1]}', end=' ') | ||
123 | + printPath(p, p[q-1][r-1], r) # 겅유하는 정점에서 끝 정점까지의 경로 검사 | ||
124 | + | ||
125 | +# Testcase | ||
126 | +INF = 1000 | ||
127 | +g = [ [0, 1, INF, 1, 5], | ||
128 | + [9, 0, 3, 2, INF], | ||
129 | + [INF, INF, 0, 4, INF], | ||
130 | + [INF, INF, 2, 0, 3], | ||
131 | + [3, INF, INF, INF, 0] ] | ||
132 | + | ||
133 | +d, p = floyd_warshall_2(g, 5) | ||
134 | +print() | ||
135 | +printMatrix(d) | ||
136 | +print() | ||
137 | +printMatrix(p) | ||
138 | +print() | ||
139 | +printPath(p, 5, 3) | ||
140 | +``` |
floyd-warshall/floyd_warshall.py
0 → 100644
1 | +# Dongyoung Kwon @Chuncheonian (ehddud2468@gmail.com) | ||
2 | +import copy | ||
3 | + | ||
4 | +# Floyd-Warshall Algorithm: 모든 정점에서 모든 정점까지의 최단 경로 | ||
5 | +def floyd_warshall(g: list[list[int]], n: int) -> list[list[int]]: | ||
6 | + dist = copy.deepcopy(g) # dist: 최단 경로의 길이를 담은 행렬 | ||
7 | + | ||
8 | + for k in range(0, n): # 거치는 점 | ||
9 | + for i in range(0, n): # 시작점 | ||
10 | + for j in range(0, n): # 끝점 | ||
11 | + dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]) | ||
12 | + return dist | ||
13 | + | ||
14 | +# Floyd-Warshall 알고리즘을 통한 최단 경로를 구성하는 정점 기록 | ||
15 | +def floyd_warshall_2(g: list[list[int]], n: int): | ||
16 | + dist = copy.deepcopy(g) | ||
17 | + p = [[0] * n for _ in range(n)] # p: 중간에 정점이 없으면 0, 있으면 그 중 가장 큰 인덱스를 포함한 행렬 | ||
18 | + | ||
19 | + for k in range(0, n): | ||
20 | + for i in range(0, n): | ||
21 | + for j in range(0, n): | ||
22 | + if dist[i][k] + dist[k][j] < dist[i][j]: | ||
23 | + p[i][j] = k + 1 # 경로 중 가장 큰 인덱스 저장 | ||
24 | + dist[i][j] = dist[i][k] + dist[k][j] | ||
25 | + return dist, p | ||
26 | + | ||
27 | +# print 2-D Matrix | ||
28 | +def printMatrix(d: list[list[int]]) -> None: | ||
29 | + n = len(d[0]) | ||
30 | + | ||
31 | + for i in range(0, n): | ||
32 | + for j in range(0, n): | ||
33 | + print(d[i][j], end=' ') | ||
34 | + print() | ||
35 | + | ||
36 | +# 특정 두 정점 사이의 최단 경로 출력 | ||
37 | +def printPath(p: list[list[int]], q: int, r: int) -> None: | ||
38 | + if p[q-1][r-1] != 0: # 최단 경로가 경유지를 지날 경우 | ||
39 | + printPath(p, q, p[q-1][r-1]) # 시작 정점에서 겅유하는 정점까지의 경로 검사 | ||
40 | + print(f'v{p[q-1][r-1]}', end=' ') | ||
41 | + printPath(p, p[q-1][r-1], r) # 겅유하는 정점에서 끝 정점까지의 경로 검사 | ||
42 | + | ||
43 | +# Testcase | ||
44 | +INF = 1000 | ||
45 | +g = [ [0, 1, INF, 1, 5], | ||
46 | + [9, 0, 3, 2, INF], | ||
47 | + [INF, INF, 0, 4, INF], | ||
48 | + [INF, INF, 2, 0, 3], | ||
49 | + [3, INF, INF, INF, 0] ] | ||
50 | + | ||
51 | +d, p = floyd_warshall_2(g, 5) | ||
52 | +print() | ||
53 | +printMatrix(d) | ||
54 | +print() | ||
55 | +printMatrix(p) | ||
56 | +print() | ||
57 | +printPath(p, 5, 3) | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
floyd-warshall/images/.DS_Store
0 → 100644
No preview for this file type
floyd-warshall/images/floyd_1.png
0 → 100644
39 KB
floyd-warshall/images/floyd_2.png
0 → 100644
32.9 KB
floyd-warshall/images/floyd_3.png
0 → 100644
565 KB
-
Please register or login to post a comment