-
프림 알고리즘Algorithm/Theory 2020. 3. 6. 17:13728x90
- MST(Minimum Spanning Tree)를 만드는 방법 중 하나입니다.
- 현재 트리를 기준으로 가장 최소인 점을 찾아나가는 방식입니다.
- 사이클을 확인할 필요가 없습니다. (현재트리를 기준으로 하기 때문에 사이클이 생기지 않습니다.)
프림 알고리즘은 현재까지 생성된 트리를 기준으로 가장 최소 비용의 간선을 선택해서 트리를 확장시켜 나갑니다.
현재 트리를 기준으로 한다는 말 속에는 이미 방문한 점은 방문하지 않는다는 말이 포함되어 있습니다.
때문에 자연스럽게 사이클이 생기지 않습니다 :)과정은 간단합니다.
1. 현재 트리를 기준으로 최소 비용으로 방문 가능한 점을 방문합니다.
2. 새로운 점을 기준으로 최소 간선의 목록을 업데이트 합니다.
직접 문제를 하나 풀어보면 쉽게 이해하실 수 있으실거에요.
같이 예를 살펴보죠 :)저희의 목적은 방문 가능한 노드들 중에서 최소 비용이 드는 길을 선택하는거에요. (방문한 노드는 다시 방문하지 않습니다.)
길을 선택했으면 방문 가능한 노드들이 더 생겨나겠죠? 이걸 기준으로 최소 비용이 드는 길을 업데이트 해줍니다.먼저 시작 노드를 정합니다. 0부터 시작해볼게요.
그럼 1, 2 번 노드가 방문이 가능해지고 각각 5, 4가 비용이 최소 비용이 됩니다.그럼 5, 4중에서 최소 비용인 4를 선택해야겠죠? 2번을 방문합니다.
이미 방문한 0은 제외하고 새롭게 3, 4번 노드를 방문할 수 있게 되었습니다.이제 최소 비용은 2가 되겠네요. 1번 노드를 방문합니다.
3번 노드를 방문할 수 있는 다른 길이 생겼지만 이미 최소 비용인 6보다 크기 때문에 업데이트 하지 않습니다.이제 감 잡으셨죠? 최소 비용인 3번 노드를 방문합니다.
4번 노드로 갈 수 있는 새로운 길이 생겼네요. 최소 비용으로 업데이트 시켜줍니다.이제 마지막 5번 노드만 남았습니다. 새로운 길이 생겼으나 최소비용과 동일합니다. 둘 다 선택이 가능하겠네요.
마지막 길까지 선택해주면 MST가 완성됩니다.
어렵지 않죠??
차근차근 따라오시면 쉽게 이해하셨으리라 생각해요 :)구현
문제는 구현이죠... 워낙 유명한 이론이기 때문에 이미 다른 언어로는 많은 코드가 있으니 참고하시면 될 것 같구요.
저는 Swift로 구현해보았습니다.
최소값을 구하는 부분을 Heap을 사용한다던지, 좀 더 좋은 방법으로 구할 수 있을 것 같은데,
추후에 업데이트 해보겠습니다.
/** * 현재 노드에 연결되어 있는 노드만 확인할 것이기 때문에 * 간단하게 목적지와 비용만 저장하는 Edge 를 정의합니다. */ struct Edge { var destination: Int var cost: Int } func updateDist(_ tree: [Edge], dist: inout [Int?]) -> [Edge] { for edge in tree { if let d = dist[edge.destination] { if d > edge.cost { dist[edge.destination] = edge.cost } } else { dist[edge.destination] = edge.cost } } return tree } func prim(_ n: Int, _ costs: [[Int]]) -> Int { // 최소 비용을 저장할 배열 var dist = [Int?](repeating: nil, count: n) // 방문 체크 배열 var visit = [Bool](repeating: false, count: n) // 방문한 노드의 개수 ( 방문 체크 배열에서 확인해도 되지만.. 그냥 간단하게 변수를 하나 만들었습니다. ) var visitCount = 0 // 현재 방문 노드를 저장합니다. 0에서 시작합니다. var now = 0 // 최소비용을 저장한다. var minimumCost = 0 /** * 배열의 인덱스가 해당 노드가 되고 연결된 * 연결된 선을 저장합니다. */ var tree = [[Edge]](repeating: [], count: n) /** * Setup Tree * 선의 양 끝을 둘다 저장해줘야 합니다. */ for cost in costs { tree[cost[0]].append(Edge(destination: cost[1], cost: cost[2])) tree[cost[1]].append(Edge(destination: cost[0], cost: cost[2])) } visit[now] = true visitCount += 1 // 탐색 while visitCount < n { let tree = updateDist(tree[now], dist: &dist) // 최소값 구하기 var minCost: Int? = nil for i in 0..<n { guard !visit[i], let d = dist[i] else {continue} if let min = minCost { if min > d { minCost = d now = i } } else { minCost = d now = i } } visit[now] = true visitCount += 1 minimumCost += minCost! } return minimumCost } // 입력: ( Int: 노드의 개수, [[Int]]: [[출발 노드, 끝 노드, 비용]] ) // 예상결과: 23 prim(6, [[0, 1, 5], [0, 2, 4], [1, 2, 2], [1, 3, 7], [2, 3, 6], [2, 4, 11], [3, 5, 8], [3, 4, 3], [4, 5, 8]])
감사합니다. :)
'Algorithm > Theory' 카테고리의 다른 글
Binary Search 이진탐색 (0) 2020.06.12 Permutation 순열 (0) 2020.06.11 Recursion 재귀 (0) 2020.06.11 위상정렬 Topological Sort (0) 2020.06.07 합병정렬 Merge sort (0) 2020.05.14