You are on page 1of 42

Algorithm

CHAPTER

05
분할 정복

5.1 최댓값과 최솟값 찾기


5.2 합병 정렬
5.3 빠른 정렬
5.4 선택
5.5 분할 정복이 부적절한 경우
Algorithm

지난 주 강의 요약

 알고리즘의 효율성은 주로 시간복잡도로 나타낸다 .

 시간복잡도 : 알고리즘의 기본 연산 횟수를 입력 크기에 대한 함수로


표현

 알고리즘의 복잡도 분류 : 최악 / 최선 / 평균 경우 복잡도

 점근적 표기 : 입력 크기가 무한대로 커질 때의 복잡도 표기


– O- 표기 : 점근적 상한

– Ω- 표기 : 점근적 하한

– θ- 표기 : 점근적 상한이고 점근적 하한

알고리즘 Chapter 05 분할 정복 2
Algorithm

분할 정복 설계 전략

1. 분할 (Divide) 단계
- 문제를 같은 유형의 여러 개의 더 작은 부분 문제들로 나눈다 .
- 부분 문제는 풀기 쉬울 때까지 계속 나눈다 .

2. 정복 (Conquer) 단계
- 부분 문제들을 보통 재귀적으로 해결하여 해를 구한다 .

3. 합병 (Merge) 단계
- 문제에 대한 해를 구하기 위해 부분 문제들의 해를 합친다 .

알고리즘 Chapter 05 분할 정복 3
Algorithm

분할 정복 전략 예 : 전쟁

알고리즘 Chapter 05 분할 정복 4
Algorithm

분할 정복 전략

알고리즘 Chapter 05 분할 정복 5
Algorithm

최댓값과 최솟값 찾기

문제 : 크기가 n 인 배열내의 요소들 중 최댓값과 최솟값을


찾는다 .

쉬운 전략
1. 최댓값을 찾는다 . -- 비교 횟수 : n – 1
2. 남은 배열 요소들의 최솟값을 찾는다 . -- 비교 횟수 : n – 2

총 비교 횟수 = (n – 1) + (n – 2) = 2n - 3

알고리즘 Chapter 05 분할 정복 6
Algorithm

분할 정복 전략

1. 배열을 반으로 나눈다 .

2. 양쪽 절반들의 최댓값과 최솟값을 찾는다 .

3. 2 에서 찾은 두 개의 최댓값들과 두 개의
최솟값들을 비교하여 전체 배열의 최댓값과
최솟값을 구한다 .

알고리즘 Chapter 05 분할 정복 7
Algorithm

분할 정복 알고리즘의 진행 과정

알고리즘 Chapter 05 분할 정복 8
Algorithm

최댓값과 최솟값 찾기 알고리즘

findMaxMin(A[ ], i, j, min, max) 5


// 의 최댓값과 최솟값을 찾는다 6
// 입력 : 배열
// 출력 : min( 최솟값 ), max( 최댓값 )
1 9
2 10
11
12
4 }
}

최초 호출 : findMaxMin(A, 0, n - 1, min, max)

알고리즘 Chapter 05 분할 정복 9
실행 트리
Algorithm

비교 횟수 :

알고리즘 Chapter 05 분할 정복 10
Algorithm

합병 정렬

 쉬운 전략
1. 최솟값을 찾는다 .
2. 남은 요소들을 같은 방법을 사용하여 재귀적으로 정렬한다 .
 분석
- 비교 횟수 : (n - 1) + (n - 2) + + 2 + 1 =

 ‘ 균형 취하기 발견법’ 사용 안함
- 크기 k, 의 배열을 크기 1 의 부분 배열 ( 최솟값 요소 ) 과 크기 (k
- 1) 의 부분 배열 ( 최솟값 요소를 제외한 남은 요소들 ) 로 나눈다 .

알고리즘 Chapter 05 분할 정복 11
Algorithm

분할 정복 전략

1. 배열을 반으로 나눈다 .

2. 왼쪽 반과 오른쪽 반을 각각 정렬한다 .

3. 정렬된 왼쪽 반과 오른쪽 반을 합병한다 .

알고리즘 Chapter 05 분할 정복 12
합병 정렬의 진행 과정
Algorithm

알고리즘 Chapter 05 분할 정복 13
Algorithm

합병 정렬 알고리즘

알고리즘 mergeSort(A[ ], low, high)


// 배열 A[low .. high] 를 합병 정렬을 이용하여 정렬한다 .
1 if (low < high) {
2 mid =
3 mergeSort(A, low, mid)
4 mergeSort(A, mid+1, high)
5 merge(A, low, mid, high)
}
최초 호출 : mergeSort(A, 0, n - 1)

알고리즘 Chapter 05 분할 정복 14
Algorithm

합병 알고리즘

Merge(x, y, z)

 임시 저장을 위해 다른 배열을 사용

 크기가 m 인 부분과 크기가 n 인 부분의 합병 : m + n – 1 번의


비교 ( 최악의 경우 )

알고리즘 Chapter 05 분할 정복 15
Algorithm

Merge 메소드
// 정렬된 부분 배열 A[low . . mid] 와 A[mid+1 . . high] 를 합병한다 .
MERGE(A[ ], low, mid, high) {
1 크기가 (high + 1) 인 배열 B 를 만든다
2 h = low; i = low; j = mid +1
3 while (i mid && j high) {
4 if (A[i] A[ j] { B[h] = A[i]; i = i + 1 }
5 else { B[h] = A[ j]; j = j + 1 }
6 h = h+1
}
7 if (i > mid)
8 for (k = j; k high; k++) { B[h] = A[k]; h = h + 1 }
9 else
10 for (k = i; k mid; k++) { B[h] = A[k]; h = h + 1 }
11 for (k = low; k high; k++) A[k] = B[k];
}

알고리즘 Chapter 05 분할 정복 16
Algorithm

시간복잡도 분석

T(n): n 개의 배열 요소들을 정렬하기 위해 필요한 비교 횟수


T(n) = ) + n – 1, n
T(1) = 0

가정 : n = , k>1
T(n)

알고리즘 Chapter 05 분할 정복 17
Algorithm

합병 정렬 알고리즘 분석
관찰
– 자주 분할정복 프로그램에서 재귀를 제거하면 더 효율적인 프로그램을
얻을 수 있다 .

비 재귀적인 Mergesort
크기 =1 크기 =2 크기 =4 크기 =8

아이디어 : Mergesort 에서 배열 부분들을 합병하는 방법을 이용한다 .


알고리즘 Chapter 05 분할 정복 18
Algorithm

비재귀 합병 정렬의 진행 과정

알고리즘 Chapter 05 분할 정복 19
Algorithm

비재귀 합병 정렬 알고리즘
mergeSort2(A[ ], n)
// 배열 A[0 . . n-1] 를 비재귀 합병 정렬을 이용하여 정렬
1 size = 1
2 while (size < n) {
3 for (i = 0; i < n; i = i + 2 * size)
4 Merge(A, i, i + size – 1, i + 2 * size – 1)
5 size = size * 2
}
분석 : 단계
각 단계는 (n) 비교가 필요하다 .
따라서 비교 횟수는 이다 .

알고리즘 Chapter 05 분할 정복 20
Algorithm

합병 정렬의 단점

 합병 정렬의 공간 복잡도 : O(n)

 입력을 위한 메모리 공간 ( 입력 배열 ) 외에
추가로 입력과 같은 크기의 공간 ( 임시 배열 ) 이
별도로 필요
 2 개의 정렬된 부분을 하나로 합병하기 위해
합병된 결과를 저장할 공간이 필요하기 때문

알고리즘 Chapter 05 분할 정복 21
Algorithm

합병 정렬의 응용

 외부정렬의 기본이 되는 정렬 알고리즘

 연결 목록에 있는 데이터를 정렬할 때에도 빠른


정렬이나 힙 정렬 보다 훨씬 효율적
 멀티코어 CPU 와 다수의 프로세서로 구성된
그래픽 처리 장치의 등장으로 정렬 알고리즘을
병렬화하기 위해 합병 정렬 알고리즘이 활용됨

알고리즘 Chapter 05 분할 정복 22
Algorithm

빠른 정렬 (Quicksort)

 절대적으로 가장 빠른 정렬 알고리즘은
아니지만 평균적으로 매우 효율적임
 평균 시간복잡도 :

최악 시간복잡도 : )

알고리즘 Chapter 05 분할 정복 23
기본 아이디어
Algorithm

 배열을 두 부분으로 분할한다 .


- 배열내의 한 기준 요소보다 작거나 같은 요소들은 앞부분에 놓는다 .
- 기준 요소보다 큰 요소들은 모두 뒷부분에 놓는다 .

 각 분할된 부분을 재귀적으로 정렬한다 .

알고리즘 Chapter 05 분할 정복 24
Algorithm

빠른 정렬의 진행 과정

알고리즘 Chapter 05 분할 정복 25
Algorithm

빠른 정렬 알고리즘

quickSort(A[ ], low, high)


// 배열 A[low . . high] 를 빠른 정렬을 이용하여 정렬한다 .
// 입력 : 정렬할 수 있는 요소들의 배열 A[low . . high]
// 출력 : 오름차순으로 정렬된 요소들의 배열 A[low . . high]
1 if (low < high) {
2 s = partition(A, low, high) // 기준 요소 A[low] 를 기준으로 분할
3 quickSort(A, low, s - 1) // 왼쪽 부분을 빠른 정렬
4 quickSort(A, s + 1, high) // 오른쪽 부분을 빠른 정렬
}
// 최초 호출 : quickSort(A, 0, n - 1)

알고리즘 Chapter 05 분할 정복 26
Algorithm

기준 요소에 의한 분할 후 빠른 정렬

기준 요소 p = A[low]

알고리즘 Chapter 05 분할 정복 27
Algorithm

분할 알고리즘
2 단계 알고리즘
1. 배열을 다음과 같이 재배열한다 .

S[low] S[low] > S[low]


2. S[low] 를 “ 구역”의 마지막 요소와 교환한다 .

알고리즘 Chapter 05 분할 정복 28
Algorithm

분할 알고리즘
주 아이디어

small ??? large small large

경우 1: S[i] S[low] 이면 i = i + 1
S[ j] S[low] 이면 j = j - 1

경우 2: S[i] S[low] 이고 S[ j] S[low] 이면


S[i] 와 S[ j] 를 교환하고 i = i + 1, j = j - 1

알고리즘 Chapter 05 분할 정복 29
Algorithm

분할 알고리즘의 진행 과정
p = A[low]

알고리즘 Chapter 05 분할 정복 30
Algorithm

분할 알고리즘의 분할 중 상황

알고리즘 Chapter 05 분할 정복 31
Algorithm

분할 알고리즘
partition(A[ ], low, high)
// 입력 : 배열 A[0 . . n - 1] 의 부분 배열 A[low . . high], low < high
// 출력 : A[low . . high] 의 분할 후 기준 요소의 최종 위치 ( 지수 ) 를 반환
1 i = low + 1
2 j = high
3 while (i j) {
4 if (A[i] A[low]) i = i + 1
5 else if (A[ j] > A[low]) j = j – 1
6 else { A[i] A[ j]
7 i=i+1
8 j=j–1
}
}
9 A[low] A[ j]
10 return j

알고리즘 Chapter 05 분할 정복 32
Algorithm

예 : 빠른 정렬

알고리즘 Chapter 05 분할 정복 33
Algorithm

최악 시간복잡도 분석

주어진 집합이 정렬된 경우

알고리즘 Chapter 05 분할 정복 34
Algorithm

빠른 정렬의 응용

 실제적으로 효율적인 정렬 알고리즘

 평균적으로 최선의 경우보다 약 39% 만큼 더


요소들간의 비교를 함

알고리즘 Chapter 05 분할 정복 35
Algorithm

분할 정복이 부적절한 경우

문제가 분할될 때마다 분할된 부분 문제들의 입력


크기의 합이 분할되기 전의 입력 크기보다 매우
커지는 경우
경우 1: 크기 n 인 문제가 거의 n 에 가까운 크기의 2 개
이상의 부분 문제들로 분할

경우 2: 크기 n 인 문제가 크기의 거의 n 개의 부분 문제
들로 분할 . c 는 상수

알고리즘 Chapter 05 분할 정복 36
Algorithm

토끼 문제

한 쌍의 토끼들은 한 달이 될 때에 한 쌍의
토끼를 낳고 두 달이 될 때에 또 다른 한 쌍의
토끼를 낳는다 . 내가 방금 한 쌍의 새로 태어난
토끼들을 샀다면 지금부터 n 번째 달에 몇 쌍의
토끼들이 태어날까 ?

주 : n 이 주어지면 새로 태어난 토끼 쌍들의


수를 구해야 한다 . n 은 매개변수이다 .

알고리즘 Chapter 05 분할 정복 37
Algorithm

토끼 문제의 설계와 분석

를 지금부터 i 번째 달에 태어난 토끼 쌍들의 수라고 하자 .


i --> 0 1 2 3 4 5 6 …
--> 1 1 2 3 5 8 13 …
는 다음 점화식을 만족시킨다 :
= +,i 2
=1
=1
위 수열은 피보나찌 (Fibonacci) 수열이라고 부른다 .

알고리즘 Chapter 05 분할 정복 38
Algorithm

재귀 알고리즘
F(n)
// n 번째 피보나찌 수 을 분할 정복 기법을 이용하여 //
계산한다
// 입력 : 양의 정수 n
// 출력 : n 번째 피보나찌 수
1

주 . 시간복잡도 : => 지수 시간 알고리즘 !!!!

알고리즘 Chapter 05 분할 정복 39
Algorithm

5 번째 피보나찌 수를 구하는 실행 트리

문제가 분할될 때마다 분할된 부분문제의


입력 크기의 합이 분할되기 전의 입력 크기보다
매우 커진다 .

알고리즘 Chapter 05 분할 정복 40
동적 계획 알고리즘
Algorithm

// n 번째 피보나찌 수 을 동적 계획을 이용하여 계산한다


// 입력 : 양의 정수 n, 크기 n 의 배열 F[0 .. n – 1]
// 출력 : n 번째 피보나찌 수
1 F[0] = 1
2 F[1] = 1
3 for (i = 2; i n; i++)
4 F[i] = F[i – 1] + F[i – 2]
// F[n] 이 구하는 답이다 .

주 . 시간복잡도 :

알고리즘 Chapter 05 분할 정복 41
Algorithm

요약

 균형 취하기 발견법을 적용한 분할정복은 부분 문제들이


독립적이라면 ( 중복 계산이 없다면 ) 유용한 기법이다 .

예 : 최댓값과 최솟값 찾기 , 합병 정렬 , 빠른 정렬

 피보나찌 문제는 부분 문제들이 독립적이지 않은 예이다 .

 대개 분할 또는 합병 단계 중 하나가 쉽다 .

 문제는 보통 2 개의 부분 문제들로 나뉘어지나 항상 그렇지는


않다 .

예 : 이진 탐색은 1 개의 부분 문제로 분할

알고리즘 Chapter 05 분할 정복 42

You might also like