집합 커버 문제 (Set Cover Problem)
전체 집합 U와, 그 부분 집합 Si들을 원소로 갖는 집합 F가 있다고 하자.
F의 원소인 Si들을 합집합하여 전체 집합 U와 같아지는 집합 Si들의 최소 개수를 찾는 문제이다.
F에 있는 집합들의 모든 조합을 하나씩 찾을 경우
- 만약 F의 원소의 개수가 n개이면 나올 수 있는 경우의 수는 (2n - 1)개이고, n이 증가함에 따라 이 방식으로 최적해를 찾는 것은 실질적으로 불가능하다.
따라서 최적해 대신 최적해에 근접한 근사해(Approximation Solution)을 찾아야 한다.
- U에 있는 원소를 가장 많이 커버하는 Si를 반복적으로 찾아서 포함시키는 방식을 사용한다.
알고리즘
입력에 전체 집합 U와 그 부분 집합 Si들을 포함한 집합 F가 주어진다.
Pseudo Code
SetCover(Set u, Set f) { Set cover; while (u.size() != 0) { u의 원소들을 가장 많이 포함하는 집합 s_i를 f에서 선택한다. u에서 s_i를 뺀다. (차집합) s_i를 cover에 추가한다. s_i를 f에서 제거한다. } return cover; }
예제
신도시를 계획하는데 10개의 마을에 학교를 배치해야 한다.
각 점은 마을을 의미하고, 선분은 15분 이내의 거리를 뜻한다.
학교는 마을에 위치해야 하고, 등교 거리는 15분 이내여야 한다.
집합 커버 문제로 변환시키면 다음과 같다.
U = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
F = {S1, S2, S3, S4, S5, S6, S7, S8, S9, S10}
S1 = {1, 2, 3, 8}
S2 = {1, 2, 3, 4, 8}
S3 = {1, 2, 3, 4}
S4 = {2, 3, 4, 5, 7, 8}
S5 = {4, 5, 6, 7}
S6 = {5, 6, 7, 9, 10}
S7 = {4, 5, 6, 7}
S8 = {1, 2, 4, 8}
S9 = {6, 9}
S10 = {6, 10}
위의 알고리즘을 적용하는 순서는 다음과 같다.
첫 번째 루프
집합 U의 원소를 가장 많이 커버하는 집합인 S4를 F에서 선택한다.
U에서 S4를 빼면 U = {1, 6, 9, 10}이 된다.
S4를 cover에 추가하고 F에서 제거한다.
두 번째 루프
집합 U의 원소를 가장 많이 커버하는 집합인 S6를 F에서 선택한다.
U에서 S6를 빼면 U = {1}이 된다.
S6를 cover에 추가하고 F에서 제거한다.
세 번째 루프
집합 U의 원소를 가장 많이 커버하는 집합인 S1를 F에서 선택한다.
U에서 S1를 빼면 U는 공집합이 된다.
S1를 cover에 추가하고 F에서 제거한다.
S1, S4, S6이 담긴 cover를 리턴한다.
시간 복잡도
루프를 반복하는 횟수는 최대 n번이다.
전체 집합 U의 원소를 가장 많이 포함하는 집합 Si를 찾을 때 걸리는 시간 복잡도는 O(n2)이다.
집합 U에서 집합 Si를 뺄 때 O(n)이 걸린다.
Si를 cover에 추가하고 F에서 뺄 때 O(1)이 걸린다.
따라서 전체 시간 복잡도는 O(n3)이다.
- 이는 지수 시간(2n)에 비해 굉장히 짧은 시간이다.
근사 비율 (Approximation Ratio)
근사 알고리즘에서 근사해가 최적해에 얼마나 가까운지를 나타내는 척도이다.
SetCover 알고리즘의 근사 비율은 Klogn이라고 한다.
K는 최적해의 집합의 수이다.
위의 예제에서는 최적해의 개수 K = 2이므로 Klogn = 2log10 < 8이다.
즉 SetCover 알고리즘의 근사해의 집합 수가 8개를 초과하지 않는다는 것이다.
'Algorithm > 알기 쉬운 알고리즘' 카테고리의 다른 글
04. 그리디(Greedy) 알고리즘 (0) | 2021.05.03 |
---|---|
작업 스케줄링(Task Scheduling) (0) | 2021.05.03 |
부분 배낭 문제 (Fractional Kanpsack Problem) (0) | 2021.05.03 |
최단 경로 찾기(Shortest Path) (0) | 2021.05.03 |
최소 신장 트리(Minimum Spanning Tree) (0) | 2021.05.03 |