ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 프림 알고리즘
    Algorithm/Theory 2020. 3. 6. 17:13
    - 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

    댓글

Designed by Tistory.