MIT 6.006 Introduction to Algorithms 18강을 보고 정리한 내용입니다.
링크: https://www.youtube.com/watch?v=f9cVS_URPc0&list=PLUl4u3cNGP63EdVPNLG3ToM6LaEUuStEY&index=18
SSSP(Single Source Shortest Paths) 문제
를 unweighted 그래프에 대해 DFS
, BFS
를 통해 시간에 풀었다. 그 후, 그래프를 일반화하여 cycle이 존재하는 weighted 그래프에 대해 SSSP 문제를 풀고자 하였으나, 먼저 cycle이 존재하지 않는 weighted 그래프인 DAG 그래프에 대해 DAG Relaxation
을 통해 시간에 풀었다.드디어 가장 일반적인 그래프를 다루게 된다. 여기서 말하는 가장 일반적인 그래프
는 simple graph 중 cycle이 존재할 수 있으며, weight 값으로 양수, 음수, 0 모두 가능한 그래프를 의미한다.
Simple Graph
- edges are distinct
: 하나의 노드에서 다른 노드로 가는 edge가 하나뿐이라는 뜻으로, 아래 그림에서 1번 노드에서 2번 노드로 가는 길이 3개인 경우에는 simple하지 않다.- edges are pairs of distinct vertices
: 하나의 vertice에서 자기 자신으로 돌아오는 edge가 없다는 뜻으로, 아래 그림과 같이 2번 노드에서 2번 노드로 돌아오는 edge가 있는 그래프는 simple하지 않다.
오늘 풀 문제는 SSSP in cyclic graph with negative weights
이다. Cyclic 그래프에 음의 가중치가 존재한다면 negative weight cycles가 존재할 수 있다. 그 이유는 원천 노드로부터 reachable할 때 negative weight cycle을 돌 수록 거리가 감소하기 때문이다. 이 말은 즉, 가장 짧은 거리를 구하기 위해 지나가야 하는 edge의 수가 bounded되어 있지 않다는 뜻이기도 하다. 따라서, 해당 거리를 음의 무한대로 설정하고 사이클 자체를 반환한다. 그 외 나머지 reachable한 거리에 대해서는 가장 짧은 유한한 거리를 반환한다. 쉽게 정리하면 다음과 같다.
가정: 모든 vertex에서의 shortest path distance, weights를 구할 수 있다.
- #1. Reachability
- not reachable:
- reachable through a negative weight cycle:
- others: 유한한 weight
- #2. negative weight cycle이 있다면, 사이클 자체를 반환
Q: graph G가 주어졌을 때, G의 negative-weight cycle 포함 유무를 반환한다.
Undirected 그래프의 경우 negative edge가 존재한다면 왔다갔다 하는 것만으로도 negative weight cycle이 생성된다. 따라서 S를 포함하는 연결 요소가 있으면 negative weight cycle으로부터 접근 가능한 모든 거리가 음의 무한대가 된다. 따라서, 음수의 weight edge가 있다면 그래프에 한정해서 생각한다.
SSSP를 해결하는 알고리즘 A의 실행시간이 걸릴 때, 이를 활용해서 SSSP를 시간에 풀 수 있음을 보여주시오.
직관적으로 생각해보면, 와 가 다를려면 vertices의 수가 edges의 수보다 압도적으로 많아야 한다. 하지만 simple path는 vertices를 반복 방문할 수 없으므로 원천 노드 S를 포함하는 연결된 요소들의 최대 E+1개의 vertices를 갖고 있다. 따라서, 이다.
이번 강의에서 배울 내용을 미리 단계별로 살펴보면 다음과 같다.
BFS
혹은 DFS
를 통해 원천 노드 s로부터 나머지 요소를 신경쓰지 않고 reachable한 vertices만 찾는다면 시간이 걸린다.A 알고리즘
을 수행한다.Claim을 증명하기 전, 두 가지 명제를 먼저 염두해두자.
proof: 반대로 cycle이 있는 상황을 가장해본다.
simple shortest path가 없다고 가정을 한다. 이때, 를 vertices가 가장 적은 shortest path라고 한다.
가 simple하지 않기 때문에 에 cycle C가 존재한다. 이때 cycle C의 가중치는 0 혹은 양수이다. Cycle C의 가중치가 음수라면 이 되기 때문이다.
만약 cycle C의 가중치가 0 혹은 양수라면, cycle C를 제거할 수 있다. Cycle C가 제거되기 전의 경로를 , 제거한 후의 경로를 라고 할 때, 이다. Cycle을 계속해서 제거하다보면 simple path를 생성할 수 있다.
Claim 1을 요약하면, 그래프에 cycle이 없다면
, shortest path는 simple하다. 만약 그래프에 cycle이 있다면
, shortest path가 simple하지 않겠지만, 가 유한한 값이라면, shortest path가 simple일 때까지 cycle을 제거할 수 있다.
Claim 1과 Claim 1'의 흐름을 다시 요약해보면, shortest path는 원래 무한한 갯수의 edges를 가질 수 있다. 하지만 shortest path 거리가 유한하다는 가정 하에는, edges의 개수가 최대 개이다.
여기서 shortest path 거리 유한 -> edges 수 에서 화살표의 방향을 바꾸어서 접근해보면 어떨지 상상해보자. 지나갈 수 있는 edges의 수에 상한선을 두고 이를 바탕으로 shortest path 거리를 구하는 것이다. 3. Negative Cycle Witness에서 이러한 접근 방법을 살펴본다.
지나갈 수 있는 edges의 상한선을 k로 두는 거리를 k-edge distance 라고 한다.
k-edge distance
: s로부터 v까지 가는 path 중 edges를 사용하는 가장 짧은 path
개념을 이용해서 다음과 같이 응용해볼 수 있다. 개의 vertices를 통과하는 가장 짧은 거리 보다 개의 vertices를 통과하는 가장 짧은 거리 가 더 짧다면, negative cycle이 존재하며 v를 witness라고 한다.
Simple path의 최대 edge 수가 이다. 따라서 이보다 더 많은 갯수의 vertex를 지나가면 거리가 늘어나야 한다. 하지만, 반대로 거리가 준다면, 그래프에서 simple path와 negative cycle이 동시에 존재하는 것이다. Negative cycle이 존재한다는 증명으로 v가 witness가 되는 것이다.
앞서, Claim 1에선 가 유한하다면, s로부터 v까지 가는 simple한 가장 짧은 경로가 존재한다고 했다. 이제 가 음의 무한대인 경우를 살펴보자.
Claim 2: 이라면, 는 witness로부터 reachable하다.
proof: s로부터 reachable한 모든 음수의 weight cycle은 witness를 포함한다. 를 증명해본다.
우선 s로부터 reachable한 negative weight cycle C를 고려한다.
, 에 대하여 을 의 predecessor이라고 하자. 이때, 으로 가정한다.
※ 가 가장 짧은 거리이므로 등호로 나타냄
--- (1)
※ 모든 vertices에 대한 합
--- (2)
※ 이므로 좌변의 값이 우변의 값보다 작음
※ (1), (2)번 식 결합
--- (3)
※ 하나의 vertex에 대한 식
만약 C에 witness가 없다면, --- (4)
(3)과 (4)는 모순이므로 C에 witness가 존재해야 한다.
이번 강의에선 더 원활한 분석을 위해 변형된 벨만 포드 알고리즘을 살펴본다. 변형된 벨만 포드 알고리즘은 11. Weighted Shortest Paths 에서 배운 DAG relaxation을 적용하기 위해 그래프를 DAG 그래프를 만든다. 구체적으로, graph duplication을 이용해서 vertex를 복사해 개의 레벨을 만든다. 여기서 레벨에 있는 는 s로부터 v까지 edge를 이용해서 도달한다는 뜻이다. 만약 edge를 더 높은 레벨에만 연결한다면, 새로 만든 이 그래프는 DAG이다. 따라서, 벨만 포드는 그래프를 V배로 복제한 후 SSSP 알고리즘을 DAG 그래프에 대해 수행하므로 총 실행시간이 이다.
Negative weight cycle을 포함하는 directed 그래프를 예를 들어보면 다음과 같다. 그래프로부터 그래프를 만들 때, vertices의 수
는 이고 edges의 수
는 이다. Vertices의 경우 V개의 노드를 V번 이동할 수 있도록 원래의 노드에서 추가적으로 복제하기 때문에 을 곱해주고, edges의 경우, vertex 자기 자신으로 가는 경로 개와 edge 수 를 더한 수에 를 곱한다.
Bellman-ford 알고리즘의 과정은 다음과 같다.
#1. 그래프로부터 DAG 그래프 을 생성한다.
의 vertices:
의 edges:
#2. 그래프에 대핸 를 원천 노드로 하는 SSSP를 DAG relaxation로 수행. 이때 인 모든 에 대하여 를 계산한다.
vertex
에 대해 로 설정한다. witness
에 대해 인 경우: reachable
한 모든 vertex 에 대해:Claim 3: 모든 , 에 대하여
※ 거리가 유한한 모든 정점에 대해 최단 경로 거리를 올바르게 설정한다.
proof: 에 대한 induction을 수행한다.
Base case: 일때 모든 에 대하여 참이다.
예를 들어, 으로부터 reachable한 원소 는 , 즉 자기자신이다.
Inductive step: 에 대해 참이라고 가정한 후, 에 대해 증명
※ 식 해설
(1) 까지의 최단 거리 는 까지의 최단 거리 에서 하나의 edge의 가중치를 더한 값과 같다.
(2) 에 속하는 대신 에 속하는 모든 정점 순회한다. 즉, 더 일반화하였다. 뒤에 합집합이 붙은 이유는 vertex까지 가는데에 최대 k개의 edge를 통과할 수 있는데 k-1개의 edge만 통과했을 때 거리가 더 짧을 가능성이 있기 때문이다.
(3) 에 대해 참이라고 가정했기 때문에 이다.
(4) 정의에 의해 (3)식은 최대 edges를 통과하는 최단거리를 구했다.
Claim 4: 벨만 포드 알고리즘을 수행하면 , 즉 하나의 원천 노드로부터 모든 vertices의 거리가 최단거리이다.
proof: 모든 인 에 대해 Claim 3에 의해 구한다.
변형된 벨만 포드 알고리즘은 graph duplication을 통해 DAG 그래프를 생성하여 DAG relaxation으로 SSSP 문제를 시간에 풀었다. 또한 로부터 로 가는 경로에서 reachable한 negative weight cycle을 으로 반환한다. 결국 시작은 일반적인 그래프였지만, DAG 그래프로 변형하여 DAG relaxation을 수행하였다. 이 강의가 기발하다고 생각하는 점이 여기에 있다. 하나의 기준틀을 제공해주고 개념을 확장시켜나가면서 기준틀을 바탕으로 개념을 엮는게 너무 좋다. 많은 강의들이 개념들을 단절된 형태로 ~가 있다. 방식으로 설명하는데에 갈증을 느꼈던 나로선 이런 깊이가 있는 사골같은 강의를 무료로 들을 수 있는 시대에 태어난 것에 감사하다.
오리지널 벨만포드는 변형된 벨만포드와 비교해 장단점이 존재한다. 장점은 오리지널 벨만 포드는 과 , 만을 저장하여 $O(|V|)$ 공간
만을 필요로 한다. 이는 단점이 될 수 있는데, 과 만을 저장하기 때문에 negative-weight cycle 포함 유무만을 판단
할 뿐, 자체를 반환하지 못한다.
def bellman_ford(Adj, w, s):
# initialization
infinity = float('inf')
d = [infinity for _ in Adj]
parent = [None for _ in Adj]
d[s], parent[s] = 0, s
# construct shortest paths in rounds
V = len(Adj)
for k in range(V - 1):
for u in range(V):
for v in Adj[u]:
try_to_relax(Adj, w, d, parent, u, v)
# check for negative weight cycles accessible for s
for u in range(V):
for v in Adj[u]:
if d[v] > d[u] + w(u,v):
raise Exception('There is a negative cycle!')
return d, parent
def try_to_relax(Adj, w, d, parent, u, v):
if d[v] > d[u] + w(u,v):
d[v] = d[u] + w(u,v)
parent[v] = u
한마디로 요약하면, 벨만 포드 알고리즘은 relaxation 알고리즘과 같다. 다만, edge를 처리할 수 있는 순서를 제한하여 번 안에 모든 edge relaxation을 마친다.