컨텐츠 내 위젯


최단경로 알고리즘 알고리즘

1. 데이크스트라

시작 노드에서 특정 노드까지 가는 최소 거리를 재는 알고리즘으로,
마지막으로 방문한 노드와 연결된 것들 중 가장 작은 것을 선택후 거리를 탐색하며, 그리디 알고리즘에 해당한다.

-- 시작 노드 이외의 다른 노드까지의 거리를 무한대로 지정한다.
-- 시작노드를 선택후 방문처리 한다.
-- 시장 노드를 제외한 다른 노드들 만큼 아래를 반복한다.
----- 방문하지 않은 노드들 중 가장 거리가 짧은 노드를 선택 후 방문처리 한다.
----- 선택된 노드와 연결된 노드들(방문 X)을 순회하며, [선택된 노드까지의 거리 + 선택된 노드와 연결된 노드까지의 거리]를 기존의 거리와 비교후 작으면 업데이트 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
INF = 1e9;
 
graph = [
  [],
  [(1,4),(2,2),(5,3)],
  [(2,4),(3,3)],
  [(3,2),(5,6)],
  [(3,3),(1,5)],
  [(1,3),(2,6)],
  []
];
# 방문하지 않은 노드들 중 가장 작은 노드 찾기
def minnode(n, d, visited):
  ret = [-1, INF];
  for i in range(7):
    if ret[1> d[i] and visited[i] == False : ret = [i,d[i]];
  return ret[0];      
 
def dijkstra(s) :
  visited = [False* 7;
  visited[0= True;
  d = [INF] * 7;
  # 시작지점 거리 초기화 및 방문 처리
  d[s] = 0;
  visited[s] = True;
  
  # 시작지점에서의 거리 초기화
  for i in graph[s]:
    d[i[1]] = i[0];
 
  # 노드 갯수만큼 반복
  # 원리가 각 노드를 거쳤을 때 이전 거리보다 작아지면 업데이트 하는거기 때문에..
  for n in range(7):
    # 아직 방문 안한 노드 중 가장 거리가 짧은것 받아오기
    s = minnode(s, d, visited);
    # 방문처리
    visited[s] = True;
    # 위에서 얻은 노드를 거칠 때 거리가 작아지면 업데이트
    for i in graph[s]:
      idx = i[1];
      dist = i[0];
      if visited[idx] == False:
        if d[idx] > d[s] + dist:
          d[idx] = d[s] + dist;
  return d[1:];
  
print(dijkstra(1))
cs

이를 리스트로 처리하는 방법도 있지만 그러면 효율성이 떨어지기에, 일부분을 우선순위 큐로 처리한다.
이렇게 할 경우 "가장 거리가 작은 노드"를 찾는데 드는 시간이 줄어든다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import heapq;
INF = 1e9;
graph = [
  [],
  [(1,4),(2,2),(5,3)],
  [(2,4),(3,3)],
  [(3,2),(5,6)],
  [(3,3),(1,5)],
  [(1,3),(2,6)],
  []
];
 
 
def dijkstra() :
  q = [];
  d = [INF] * 7;
  s = 1;
  d[s] = 0;
  # 큐에 (거리, 노드번호) 추가
  # 거리 순으로 뽑아내야 하므로 우선순위 큐 사용
  heapq.heappush(q,(0,s));
  while q:
    # 큐에서 끄낸 후 현재 자리로 정함
    dist, now = heapq.heappop(q);
    # 이미 계산된 값이 있으며 아래와 같은 경우,
    # 거리는 다른데 노드번호는 중복되어서 힙에 들어가 있을 때가 있음.
    # (5,3), (2,3) 이 큐에 들어갔을 때 (2,3) 먼저 처리된 다음에 (5,3)을 처리할 경우
    if d[now] < dist :
      continue;
    # 현재 자리와 연결된 노드들 순회
    for i in graph[now]:
      # 시작 노드에서 현재 자리까지의 거리 + 현재 자리에서 해당 노드까지의 거리
      cost = dist + i[0];
      idx = i[1];
      # 이미 계산되어 있는 시작노드에서 해당 노드까지의 거리 보다 위 결과가 작다면
      if cost < d[idx]:
        # 업데이트
        d[idx] = cost;
        # 및 큐에 등록하므로써 순회할 수 있도록 처리
        heapq.heappush(q, (cost, idx));
  return d[1:];
print(dijkstra());
cs


2. 플로이드

모든 노드끼리 서로 걸리는 거리를 재는 알고리즘이다.
점화식은 아래와 같으며, 시간복잡도가 V3 이므로 노드의 갯수가 적어야 한다.
 
D[a][b] = min(D[a][b], D[a][k]+D[k][b])


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import heapq;
INF = 1e9;
 
graph = [
  [],
  [(4,2),(6,4)],
  [(3,1),(7,3)],
  [(5,1),(4,4)],
  [(2,3)]
];
 
def floyd():
  d = [[INF] * 5 for _ in range(5)];
  # 그래프에 따라 현재 d[시작노드][끝노드]를 일차적으로 초기화
  for i in range(5):
    d[i][i] = 0;
    for j in graph[i]:
      d[i][j[1]] = j[0];
  # 점화식에 따라 중간에 다른거 거칠 때 기존 계산된 거리보다 작으면 업데이트
  for a in range(5):
    for b in range(5):
      for k in range(5):
        d[a][b] = min(d[a][b], d[a][k] + d[k][b]);
  return d[1:];
  
print(floyd());
cs



3. 벨만포드 알고리즘

그래프의 음수 간선이 존재할 때 사용한다.
단순히 음수 간선이 존재할 때는 데이크스트라도 가능하지만, 음수 간선이 낀 사이클이 존재할 경우는 벨만포드 알고리즘으로 해결해야 한다.

기본적으로는 데이크스트라와 비슷하지만, 데이크스트라는 특정 노드(가장 거리가 짧은)에 연결된 간선들만 확인하지만, 벨만포드는 모든 간선에 대해 확인한다.

모든 간선에 대해 확인하면서 n-1번 확인하는데 이는 특정 노드에서 다른 노드까지 연결된 간선의 추가 최대 n-1개 일 수 있기 때문이다.
[1] --> [2] -->  [3] --> [4] --> [5]

그리고 거리를 업데이트 하는 식은, 
간선 시작점 최소 거리 + 간선 거리 가 지금까지 계산한 거리[간선 도착점 인덱스] 보다 작을 때,
지금까지 계산한 거리[간선 도착점 인덱스]  = 간선 시작점 최소 거리 + 간선 거리 로 진행하면 된다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
INF = 1e9;
 
graph = [
  [],
  [(1,4),(2,2),(5,3)],
  [(2,4),(3,3)],
  [(3,2),(5,6)],
  [(3,3),(1,5)],
  [(1,3),(2,6)],
  []
];
 
 
def bf(s) :
  d = [INF] * 7;
  # 시작지점 거리 초기화 및 방문 처리
  d[s] = 0;
  
  # 시작지점에서의 거리 초기화
  for i in graph[s]:
    d[i[1]] = i[0];
  print(d);
  # 노드 갯수 - 1 만큼 반복
  # 왜 노드 갯수 - 1 까지 반복하냐면.. 정점 A에서 B까지 거치는 간선의 가능한 최대 갯수가 노드 갯수 - 1 이기 때문.
  for n in range(7):
    # 모든 정점을 돌면서 기존 거리보다 새로 계산된 거리가 작아지면 업데이트
    # 입력을 간선정보만 따로 보기좋게 안 받았으므로 그래프를 두번 돌면서 처리
    for i in range(7):
      item = graph[i];
      # i - 간선 시작지점 idx
      for j in item:
        idx = j[1];
        dist = j[0];
        # idx - 간선 끝지점 idx
        # 시작지점까지의 거리 + 현재 간선 거리  가   간선 끝지점의 거리보다 작을때 업데이트.
        if d[idx] > (d[i] + dist):
          print(f'cycle={n}')
          print(f'cycle={idx}')
          print(f'cycle={d[i] + dist}')
          d[idx] = d[i] + dist;
          print(d);
          if n == 6print('음의 사이클 존재');
    # print(d[1:])
  return d[1:];
 
print(bf(4));
 
# 1회전 - [INF,6,2,0,1,3];
# ---> 4에서 바로 갈 수 있는 
# 2 ~ 6 회전 - [5,2,0,1,3];
 
cs



덧글

댓글 입력 영역




(adsbygoogle = window.adsbygoogle || []).push({});