4.1. 닫힘

집합 A에 관한 관계 R이 있고, R이 가질 수 있는 성질을 P라고 할 때,

집합 A에 관한 관계 R_1이 관계 R을 포함하면서 성질 P를 갖는다면,

관계 R_1을 관계 R에 대한 “P닫힘(또는 폐포)”이라고 표현합니다.

이때, P는 닫힘 또는 “폐포”로도 표현하고, 반사, 대칭, 추이 관계 등이 존재합니다.

R_1은 관계 R을 포함하면서 우리가 원하는 성질을 만족시키는 가장 작은 관계가 될 것입니다.


다음은 폐포의 종류 세 가지에 대해 나열 한 것입니다.

집합 A에 대한 관계 R이 정의 될 때,

1). “반사 폐포”(Reflexive Closure) : R \cup {(a, a) | a \in A} 인 가장 작은 관계 R_1을 “반사 폐포”라고 정의합니다.

2). “대칭 폐포”(Symmetric Closure) : R \cup {(b, a) \in A \times A | (a, b) \in R} 인 가장 작은 관계 R_1을 “대칭 폐포”라고 정의합니다.
( 쉽게 R \cup R^{-1} 라고도 표현 가능 합니다. )

3). “추이 폐포”(Transitive Closure) : R^{\inf}(연결관계)가 되는 가장 작은 관계 R_1을 “추이 폐포”라고 정의합니다.
(연결관계에 대한 내용은 3.3절에 자세하게 설명되어 있습니다. )


4.2. 와샬 알고리즘

추이 폐포를 구하기 위해서는 연결 관계를 구하면 된다는 것을 배웠습니다.

그런데 연결 관계의 정의는 모든 연결의 존재를 알기 위해서 부울곱과 \vee 연산을 반복하여 값을 구해내는데

이론적으로 무한 번 진행하면, 실제로는 더 이상 값의 변화가 없는 지점까지 진행하여 추이 폐포를 구할 수 있습니다.

물론 이렇게 값을 구하는 것은 효율적이지 않기에 실제로는 사용되는 방식은 아닙니다.

위의 방식을 코딩한다면, 시간복잡도를 언급 할 필요 없이 상당한 시간이 걸릴 것입니다.


“와샬 알고리즘”(Warshall Algorithm)은 해당 문제를 시간복잡도 O(n^3)에 풀리게 하는 알고리즘입니다.

와샬 알고리즘의 단계는 다음과 같습니다.

1). 와샬 알고리즘에서 사용하는 관계행렬을 W_k로 둔다면, W_1 = M_R (M_R은 관계 R의 관계 행렬) 이고,

M_R의 행(또는 열)의 크기를 n이라고 정의한다면, (즉, M_R은 n \times n 행렬) W_n까지 값을 구하고,

결과를 W_n으로 정합니다.

( 루프가 없다면, 모든 정점을 한 번씩 모두 지나는 루트가 가장 길 것이므로

매 최소 경로만 파악한다는 가정이 있으면, n회 만에 모든 경로를 정할 수 있게 됩니다. )

2). W_{k-1}값은 W_k에 그대로 전파되고, W_k의 i행 j열의 값을 \sum_{x = 1}^{n} W_k[i][x] \wedge W_k[x][j]으로 다음 추이 관계들을 구합니다.
( 잘 보면 부울곱의 식과 같다는 것을 볼 수 있습니다. 이유는 생각해보면 추이 관계를 구하는 과정이기에 그렇습니다. )

3). 해당 과정을 반복하여 W_n까지 구한다면, W_n은 M_{R^{\inf}}이 됩니다.


코드로 표현하면, 다음과 같습니다.

C++
#include<iostream>

using namespace std;

int** warshall(int** graph, int n) {
    int** result = new int*[n];
    for (int i = 0; i < n; i++) {
        result[i] = new int[n];
        for (int j = 0; j < n; j++) {
            result[i][j] = graph[i][j];
        }
    }

    // Warshall's algorithm , O(n^3)
    for (int k = 0; k < n; k++) {
        for (int i = 0; i < n; i++) { 
            for (int j = 0; j < n; j++) {
                result[i][j] = result[i][j] || (result[i][k] && result[k][j]);
            }
        }
    }

    return result;
}

int main() {
    // Adjacency matrix of a directed graph
    int** graph = new int*[4];
    graph[0] = new int[4]{ 0, 1, 0, 0 };
    graph[1] = new int[4]{ 0, 0, 0, 1 };
    graph[2] = new int[4]{ 1, 0, 0, 0 };
    graph[3] = new int[4]{ 0, 0, 1, 0 };

    int** result = warshall((int**)graph, 4);

    // print W_n matrix
    for (int i = 0; i < 4; i++) {
        for (int j = 0; j < 4;j++) {
            cout << result[i][j] << " ";
        }
        cout << endl;
    }

    return 0;
}

출력 결과는 다음과 같습니다.

Bash
1 1 1 1 
1 1 1 1 
1 1 1 1 
1 1 1 1

집합 A에 대한 동치 관계 R, S가 정의 될 때, R과 S가 둘 다 포함되는 가장 작은 동치 관계는 (R \cup S)^{\inf} 가 됩니다.

이 때, M_{R \cup S} = M_R \vee M_S 가 되고, 연결 관계는 동일하게 와샬 알고리즘으로 풀 수 있습니다.

W_0을 M_{R \cup S}로 둔다면, W_n : (n은 M_{R \cup S}의 행 (또는 열) 의 개수) 가 M_{(R \cup S)^{\inf}}

즉, 결과 값을 도출 할 수 있게 됩니다.


Leave a Reply

Your email address will not be published. Required fields are marked *