Algorithm/알고리즘

[최단 경로] 플로이드 워셜 알고리즘 (Floyd-Warshall Algorithm)

이숨인 2024. 6. 18. 12:21

플로이드 워셜 알고리즘 개요

플로이드 워셜 알고리즘은

  • 모든 노드에서 다른 모든 노드까지의 최단 경로를 모두 계산한다
  • 다익스트라 알고리즘과 마찬가지로, 단계별로 거쳐가는 노드를 기준으로 알고리즘을 수행한다. 그러나, 아래 두 가지에서 다익스트라와 차이가 있다. 
    • 다익스트라 알고리즘은 한 시작점에서 다른 정점까지의 최단 거리를 구하는데 반해 플로이드 알고리즘은 모든 정점 쌍에 대해서 둘 사이의 최단 거리를 구할 수 있다.
    •  음의 가중치를 가지는 그래프에서도 쓸 수 있다
  • 플로이드 알고리즘은 2차원 테이블에 최단 거리 정보를 저장한다.

 

플로이드 워셜 알고리즘

  • 각 단계마다 특정 한 노드 k를 거쳐가는 경우를 확인한다.
    • a에서 b로 가는 최단거리보다, a에서 k를 거쳐 b로 가는 거리가 더 짧은지 검사한다.
    • 점화식은 다음과 같다.

 

 

 

 

동작 과정 살펴보기

[초기상태] 그래프를 준비하고, 최단거리 테이블을 초기화

  • 처음에는 각 노드를 기준으로, 현재 노드와 인접한 노드들을 확인하여 테이블 갱신
    • 행 : 출발노드
    • 열 : 도착노드 의미
  • 이후, 1번노드~4번노드 각각에 대해, 각 노드를 거쳐가는 경우를 고려해서, 테이블 갱신

 

[Step1 ] 1번 노드를 거쳐가는 경우를 고려해서, 테이블 갱신

  • 하늘색은 step1을 통해 갱신되는 부분을 의미함
    • 1번 노드를 거쳐가는 경우를 확인하고 있기 때문, 시작지점이 1인 경우(1행)와, 도착지점이 1인 경우(1열)은 색칠되어있지 않다
    • 또한, 자기자신-자기자신으로 가는 경우는 갱신되지 않는다고 가정 → 대각선 색칠 x
    • ex) 2번 노드→4번 노드의 경우, 원래는 ‘무한’이었지만, 1번 노드를 거쳐가는 순간 거리가 9로 갱신된 것을 확인할 수 있다.

 

[Step2 ] 2번 노드를 거쳐가는 경우를 고려해서, 테이블 갱신

 

  • 하늘색은 step2을 통해 갱신되는 부분을 의미함
    • 2번 노드를 거쳐가는 경우를 확인하고 있기 때문, 시작지점이 2인 경우(2행)와, 도착지점이 2인 경우(2열)은 색칠되어있지 않다
    • 또한, 자기자신-자기자신으로 가는 경우는 갱신되지 않는다고 가정 → 대각선 색칠 x

 

[Step3 ] 3번 노드를 거쳐가는 경우를 고려해서, 테이블 갱신

  • ex) 4번 노드→1번 노드의 경우, 원래는 ‘무한’이었지만, 3번 노드를 거쳐가는 순간 거리가 2+5 = 7로 갱신된 것을 확인할 수 있다.

 

[Step4 ] 4번 노드를 거쳐가는 경우를 고려해서, 테이블 갱신

 

 

 

코드

INF = int(1e9) #무한을 의미하는 값으로 10억을 설정

n = int(input) #노드의 개수 입력받기
m = int(input) #간선의 개수 입력받기



#2차원 리스트를 만들고, 무한으로 초기화

graph = [[INF]*(n+1) for _ in range(n+1)]


#자기 자신에서 자기 자신으로 가는 비용은 0으로 초기화
for a in range(1,n+1):
  for b in range(1,n+1):
    if a==b:
      graph[a][b] = 0


#각 간선에 대한 정보를 입력받아, 그 값으로 초기화
for _ in range(m):
  # a에서 b로 가는 비용은 c라고 설정
  a,b,c = map(int,input().split())
  graph[a][b] = c


#점화식에 따라 플로이드-워셜 알고리즘 수행(3중 for문)
for k in range(1,n+1): # k는 거쳐가는 노드
  for a in range(1,n+1): # a는 출발 노드
    for b in range(1,n+1): # b는 도착 노드
      graph[a][b] = min(graph[a][b], graph[a][k] + graph[k][b])
  

#수행된 결과 출력
for a in range(1,n+1):
  for b in range(1,n+1):
    # 도달할 수 없는 경우, 무한(INFINITY) 이라고 출력
    if graph[a][b] == INF:
      print("INFINITY", end=" ")
    else:
      print(graph[a][b], end = " ")
  print()

 

 

플로이드-워셜 알고리즘 성능 분석

  • 노드의 개수가 N개일 때, 알고리즘상으로 N번의 단계(각 노드를 거쳐가는 단계)를 수행한다
    • 각 단계마다 O(N^2)의 연산을 통해, 현재 노드를 거쳐 가는 모든 경로를 고려(3중 반복문으로 구현)
  • 따라서, 플로이드 워셜 알고리즘의 총 시간 복잡도는 O(N^3)
    • 플로이드 워셜이 사용되어야 하는 경우 → 노드의 갯수가 500개 이하로, 작은 경우가 많다 (시간초과 판정 안받으려면)
  • 따라서, 노드의 갯수가 충분히 크다면, 최단거리를 구하는 문제에서 보통 다익스트라를 사용한다

 

다익스트라 vs 플로이드 워셜

  • 다익스트라 알고리즘
    • 한 지점에서 다른 특정 지점까지의 최단 경로를 구하는 알고리즘
  • 플로이드-워셜 알고리즘
    • 모든 지점에서 다른 모든 지점까지의 최단 경로를 모두 구해야하는 경우

다익스트라플로이드-워셜

 

다익스트라 플로이드
한 지점에서 다른 특정 지점까지의 최단 경로 모든 지점에서 다른 모든 지점까지의 최단 경로
- 단계마다 '거쳐 가는 노드'를 기준으로 알고리즘을 수행

- 단계마다 최단 거리를 가지는 노드를 하나씩 반복적으로 선택

- 이후 해당 노드를 거쳐가는 경로를 확인하며 최단 거리 테이블을 갱신
- 단계마다 '거쳐 가는 노드'를 기준으로 알고리즘을 수행

- 매 단계마다 방문하지 않은 노드 중에서 최단 거리를 갖는 노드를 찾을 필요가 없다(다익스트라는 필요했)
1차원 리스트에 최단거리 저 2차원 테이블에 최단 거리 정보 저장

 

 

 

Reference

https://www.youtube.com/watch?v=hw-SvAR3Zqg