16.01.2015 Views

동적 프로그래밍 - 한국기술교육대학교

동적 프로그래밍 - 한국기술교육대학교

동적 프로그래밍 - 한국기술교육대학교

SHOW MORE
SHOW LESS

You also want an ePaper? Increase the reach of your titles

YUMPU automatically turns print PDFs into web optimized ePapers that Google loves.

©copyright 2005<br />

알고리즘(INA280) NOTE 04<br />

동적 프로그래밍<br />

한국기술교육대학교 인터넷미디어공학부 김상진<br />

교육목표<br />

동적 프로그래밍(dynamic programming) 개요<br />

이항계수 계산 알고리즘<br />

Floyd의 최단 경로 알고리즘<br />

최적의 원칙<br />

연쇄 행렬곱셈 알고리즘<br />

최적의 이진 검색 트리<br />

2/43


동적 프로그래밍<br />

분할정복법은 하향식 해결법으로서, 나누어진 부분들 사이에 서로<br />

상관관계가 없는 문제를 해결하는데 적합하다.<br />

분할정복법으로 피보나찌 수를 계산하면 중복된 계산을 많이<br />

하게 되므로 적합하지 않다.<br />

피보나찌 수를 계산하는 알고리즘 경우에는 나누어진 부분들<br />

사이에 서로 연관되어 있다.<br />

F 5 를 계산하기 위해서는 F 4 와 F 3 을 계산해야 되며, 이둘은<br />

모두 F 2 의 계산이 필요하다.<br />

동적 프로그래밍(dynamic programming)은 분할정복법과 정반대되는<br />

접근 방법으로서, 상향식(bottom-up) 해결법이다.<br />

즉, 분할정복법과 마찬가지로 문제를 작은 여러 개의 사례로 나누어<br />

이들 사례를 먼저 해결한다.<br />

하지만 해결한 값을 보관하여, 나중에 이 값이 또 필요하면 다시<br />

계산하지 않는다.<br />

3/43<br />

이항계수<br />

이항계수(binomial coefficient)는 다음과 같이 정의된다.<br />

⎛n⎞ n!<br />

⎜<br />

, for 0 n k<br />

k<br />

⎟ = ≤ ≤<br />

⎝ ⎠ k!( n−<br />

k)!<br />

n과 k가 작지 않으면 이항계수를 직접 계산하는 것은 어렵다.<br />

이항계수는 다음과 같의 재귀적으로 정의할 수 있다.<br />

⎧⎛n−1⎞ ⎛n−1⎞<br />

⎛n⎞ ⎪⎜ 0 k n<br />

k 1<br />

⎟+ ⎜<br />

k<br />

⎟ < <<br />

⎜<br />

k<br />

⎟= ⎨⎝ − ⎠ ⎝ ⎠<br />

⎝ ⎠ ⎪<br />

⎩1<br />

k = 0, k = n<br />

이 정의로부터 분할정복 방식의 알고리즘을 구현할 수 있다.<br />

이 알고리즘은 n!이나 k!을 계산하지 않고 이항계수를 계산할 수 있다.<br />

4/43


분할정복법을 이용한 이항계수 알고리즘<br />

문제: 이항계수를 계산하기<br />

입력: 음이 아닌 정수 n과 k, 여기서 k≤n<br />

출력: 이항계수 n C k<br />

int bin(int n, int k){<br />

if(k == 0 || n == k) return 1;<br />

else return bin(n-1, k-1) + bin(n-1, k);<br />

}<br />

이 알고리즘은 피보나찌 수를 계산하는 알고리즘과 마찬가지로 같은<br />

계산을 반복적으로 수행한다.<br />

실제 2 n C k -1만큼의 항을 계산해야 n C k 를계산할수있다.<br />

5/43<br />

분할정복법을 이용한 이항계수 알고리즘<br />

2 n C k -1만큼의 항을 계산해야 n C k 를계산할수있다.<br />

(증명) 귀납법으로 증명<br />

귀납출발: n = 1일때, 2 1 C k – 1 = 2 – 1 = 1이다.<br />

이유: n = 1 일때 k는 0 또는 1이다.<br />

귀납가정: n>k일때성립<br />

귀납단계: n = k일때, 알고리즘에 의해 n C k = n-1 C k-1 + n-1 C k 이다.<br />

따라서 n C k 를 계산하기 위한 비용은 n-1 C k-1 과 n-1 C k 을 계산하기<br />

위한 비용을 합한 것에 1을더한것이된다.<br />

그런데 귀납가정에 의해 n-1 C k-1 과 n-1 C k 을 계산하기 위한 비용은<br />

2 n-1 C k-1 -1과 2 n-1 C k -1이다.<br />

따라서 n C k 를 계산하기 위한 비용은 다음과 같다.<br />

= 2 n-1 C k-1 – 1 + 2 n-1 C k - 1 +1 = 2( n-1 C k-1 + n-1 C k ) -1 = 2 n C k -1<br />

6/43


동적 프로그래밍을 이용한 이항계수 알고리즘<br />

알고리즘 절차<br />

단계 1. 재귀 관계식을 정립한다.<br />

⎧Bi [ − 1][ j− 1] + Bi [ − 1][ j] 0< j<<br />

i<br />

Bi [][ j]<br />

= ⎨<br />

⎩1 j = 0, j = i<br />

단계 2. 문제의 사례를 상향식 방식으로 해결한다.<br />

B[0][0] = 1<br />

B[1][0] = 1<br />

B[1][1] = 1<br />

B[2][0] = 1<br />

B[2][1] = B[1][0] + B[1][1] = 1 + 1 = 2<br />

B[2][2] = 1<br />

B[3][0] = 1<br />

B[3][1] = B[2][0] + B[2][1] = 1 + 2 = 3<br />

…<br />

7/43<br />

동적 프로그래밍을 이용한 이항계수 알고리즘<br />

문제: 이항계수를 계산하기<br />

입력: 음이 아닌 정수 n과 k, 여기서 k≤n<br />

출력: 이항계수 n C k<br />

int bin2(int n, int k){<br />

index i, j;<br />

int B[0..n][0..k];<br />

for(i = 0; i≤n; i++)<br />

for(j = 0; j≤min(i,k); j++)<br />

if(j == 0 || j == i) B[i][j] = 1;<br />

else B[i][j] = B[i-1][j-1] + B[i-1][j];<br />

return B[n][k];<br />

}<br />

8/43


동적 프로그래밍을 이용한 이항계수 알고리즘<br />

주어진 n과 k에 대해for 루프가 반복하는 횟수는 다음과 같다.<br />

i<br />

0<br />

1<br />

2<br />

3<br />

…<br />

k<br />

k+1<br />

…<br />

n<br />

반복횟수<br />

1<br />

2<br />

3<br />

4<br />

…<br />

k+1<br />

k+1<br />

…<br />

k+1<br />

전체 반복 횟수는 다음과 같다.<br />

1+ 2+ 3+ 4 + + k+ ( k+ 1) + ( k+ 1) + + ( k+<br />

1)<br />

<br />

n− k+<br />

1번<br />

위 표현식은 다음과 같다.<br />

kk ( + 1) ( k+ 1)(2n− k+<br />

2)<br />

+ ( n− k+ 1)( k+ 1) = ∈Θ( nk)<br />

2 2<br />

9/43<br />

동적 프로그래밍을 이용한 이항계수 알고리즘<br />

개선방법<br />

2차원 배열을 사용하고 있지만 한 행의 계산이 끝나면 더 이상 이전<br />

행의 값이 필요없다.<br />

0부터 k까지의 일차원 배열을 이용하여 구현 가능<br />

다음 특성을 이용하여 계산량을 줄일 수 있다.<br />

⎛n⎞ ⎛ n ⎞<br />

⎜<br />

k<br />

⎟=<br />

⎜<br />

n−<br />

k<br />

⎟<br />

⎝ ⎠ ⎝ ⎠<br />

k>n-k이면 이와 같이<br />

바꾸어 계산하는 것이<br />

더 효율적이다.<br />

10/43


그래프 기초<br />

다음은 가중치 방향 그래프이다.<br />

정점(vertex)<br />

간선(edge)<br />

가중치(weight)<br />

방향 그래프(directed graph, digraph): 간선에 방향이 있는 그래프<br />

가중치 그래프(weighted graph): 간선과 연관된 값이 있는 그래프<br />

경로(path): 한 정점에서 다른 정점까지 도달하기 위해<br />

지나는 일련의 정점(정점 사이에는 간선이 있어야 함)을 말한다.<br />

단순 경로(simple path): 같은 정점을 두 번 지나지 않는 경로<br />

11/43<br />

그래프 기초 -계속<br />

순환(cycle): 한 정점에서 다시 그 정점으로 돌아오는 경로<br />

순환 그래프(cyclic graph): 순환이 존재하는 그래프<br />

비고. 비순환 그래프(acyclic graph)<br />

경로의 길이(length): 가중치 그래프에서는 경로를 구성하는 간선들의<br />

가중치의 합을 말하며, 비가중치 그래프에서는 경로를 구성하는<br />

간선의 수를 말한다.<br />

인접 정점(adjacent vertex): 정점 v i 와 v j 를잇는간선이있으면v i 와 v j 는<br />

인접 정점이라 한다.<br />

12/43


최단 경로 찾기 문제<br />

최단 경로 찾기 문제는 최적화 문제(optimization problem) 중 하나이다.<br />

최적화 문제란 후보 답이 여러 개 존재할 수 있는 문제를 말한다.<br />

이 문제의 해답은 후보 답 중에 가장 최적의 값을 가지는 답을 하나<br />

찾는 문제를 말한다.<br />

최적값이란 보통 최대 또는 최소값을 말한다.<br />

한 정점에서 다른 정점으로 하나 이상의 최단 경로가 존재할 수<br />

있으므로 이 중 하나만 찾으면 된다.<br />

방법 1. 모든 경로를 찾은 후에 그 중에서 최단 경로를 찾는다.<br />

완전 연결 그래프이면 한 정점에서 다른 정점으로 모든 정점을<br />

지나는 경로만 총 (n-2)!개의 존재한다.<br />

이것은 지수시간 보다 더 나쁘다.<br />

방법 2. 동적 프로그래밍을 이용하면 차수가 n 3 인 알고리즘을<br />

만들수있다.<br />

13/43<br />

Floyd의 최단 경로 알고리즘 I<br />

가중치 그래프를 다음과 같이 2차원 배열로 나타낼 수 있다.<br />

⎧edge weight vi에서<br />

vj를 잇는 간선이 있는 경우<br />

⎪<br />

Wi [][ j]<br />

= ⎨∞<br />

vi에서<br />

vj를 잇는 간선이 없는 경우<br />

⎪<br />

⎩0<br />

i = j<br />

W[i][j]<br />

14/43


Floyd의 최단 경로 알고리즘 I -계속<br />

예4.1)<br />

v 2 에서 v 5 까지의 최단 경로를 구하는 방법<br />

D (k) [i][j]: 집합 {v 1 , …, v k }에 속한 정점만을 중간 정점으로 사용하는<br />

v i 에서 v j 로가는최단경로<br />

D (0) [2][5] = ∞<br />

D (1) [2][5]<br />

= min(len[v 2 , v 5 ], len[v 2 , v 1 , v 5 ]) = 14<br />

D (2) [2][5] = D (1) [2][5] = 14<br />

D (3) [2][5] = D (2) [2][5] = 14<br />

D (4) [2][5]<br />

= min(len[v 2 , v 1 , v 5 ], len[v 2 , v 4 , v 5 ],<br />

len[v 2 , v 1 , v 4 , v 5 ], len[v 2 , v 3 , v 4 , v 5 ])<br />

= min(14, 5, 13, 10) = 5<br />

D (4) [2][5] = D (5) [2][5] = 5<br />

W[i][j]<br />

15/43<br />

Floyd의 최단 경로 알고리즘 I -계속<br />

앞 슬라이드 예를 통해 다음이 성립한다는 것을 알 수 있다.<br />

D (0) = W, D (n) = D(최단 경로)<br />

동적 프로그래밍을 이용한 최단 경로 알고리즘의 설계 절차<br />

단계 1. D (k-1) 로부터 D (k) 를 계산할 수 있도록 재귀 관계식을 설립<br />

단계 2. 상향식 방법으로 D (0) 부터 D (n) 까지 차례로 구한다.<br />

재귀 관계식의 설정<br />

다음 두 가지 경우를 고려해야 함<br />

경우 1. v i 에서 v j 로의 하나 이상의 최단 경로가 v k 를 사용하지<br />

않으면 다음이 성립한다.<br />

D (k) [i][j] = D (k-1) [i][j]<br />

경우 2. v i 에서 v j 로의 모든 최단 경로가 v k 를 사용하는 경우<br />

D (k) [i][k] = D (k-1) [i][k] + D (k-1) [k][j]<br />

16/43


Floyd의 최단 경로 알고리즘 I -계속<br />

경우 2.<br />

v i 에서 v j 로의 최단 경로가 v k 를 중간 노드로 사용하는 경우이다.<br />

따라서 이 경로는 두 구간으로 나눌 수 있다.<br />

v i 에서 v k 로의 최단 경로(v k 는포함될수없음): D (k-1) [i][k]<br />

v k 에서 v j 로의 최단 경로(v k 는포함될수없음): D (k-1) [k][j]<br />

그러므로 다음이 성립한다.<br />

D (k) [i][k] = D (k-1) [i][k] + D (k-1) [k][j]<br />

17/43<br />

Floyd의 최단 경로 알고리즘 I -계속<br />

경우 1과경우2를 모두 고려하면 다음과 같다.<br />

D[5][4]<br />

( k) ( k−1) ( k−1) ( k−1)<br />

D i j = D i j D i k + D k j<br />

[][ ] min( [][ ], [][ ] [ ][ ])<br />

<br />

경우 1 경우 2<br />

D (0) [5][4] = ∞<br />

D (1) [5][4] = min(D (0) [5][4], D (0) [5][1]+D (0) [1][4])<br />

= min(∞, 3+1) = 4<br />

D (2) [5][4] = min(D (1) [5][4], D (1) [5][2]+D (1) [2][4])<br />

= min(4, 4+2) = 4<br />

18/43


Floyd의 최단 경로 알고리즘 I -계속<br />

1<br />

2<br />

3<br />

4<br />

5<br />

1<br />

2<br />

3<br />

4<br />

5<br />

D (2)<br />

1<br />

2<br />

3<br />

4<br />

5<br />

D (0) 0<br />

D (1) 0<br />

1<br />

0<br />

1<br />

∞<br />

1<br />

5<br />

1<br />

0<br />

1<br />

∞<br />

1<br />

5<br />

1<br />

0<br />

1<br />

4<br />

1<br />

5<br />

2<br />

9<br />

0<br />

3<br />

2<br />

∞<br />

2<br />

9<br />

0<br />

3<br />

2<br />

14<br />

2<br />

9<br />

0<br />

3<br />

2<br />

14<br />

3<br />

∞<br />

∞<br />

0<br />

4<br />

∞<br />

3<br />

∞<br />

∞<br />

0<br />

4<br />

∞<br />

3<br />

∞<br />

∞<br />

0<br />

4<br />

∞<br />

4<br />

∞<br />

∞<br />

2<br />

0<br />

3<br />

4<br />

∞<br />

∞<br />

2<br />

0<br />

3<br />

4<br />

∞<br />

∞<br />

2<br />

0<br />

3<br />

5<br />

3<br />

∞<br />

∞<br />

∞<br />

0<br />

5<br />

3<br />

4<br />

∞<br />

4<br />

5<br />

3<br />

4<br />

7<br />

4<br />

1<br />

2<br />

3<br />

4<br />

5<br />

1<br />

2<br />

3<br />

4<br />

5<br />

1<br />

2<br />

3<br />

4<br />

5<br />

D (3) 0<br />

D (4) 0<br />

D (5) 20<br />

1<br />

0<br />

1<br />

4<br />

1<br />

5<br />

1<br />

0<br />

1<br />

3<br />

1<br />

4<br />

1<br />

0<br />

1<br />

3<br />

1<br />

4<br />

2<br />

9<br />

0<br />

3<br />

2<br />

14<br />

2<br />

9<br />

0<br />

3<br />

2<br />

14<br />

2<br />

8<br />

0<br />

3<br />

2<br />

5<br />

3<br />

∞<br />

∞<br />

0<br />

4<br />

∞<br />

3<br />

∞<br />

∞<br />

0<br />

4<br />

7<br />

3<br />

10<br />

11<br />

0<br />

4<br />

7<br />

4<br />

∞<br />

∞<br />

2<br />

0<br />

3<br />

4<br />

∞<br />

∞<br />

2<br />

0<br />

3<br />

4<br />

6<br />

7<br />

2<br />

0<br />

3<br />

5<br />

3<br />

4<br />

7<br />

4<br />

0<br />

5<br />

3<br />

4<br />

6<br />

4<br />

5<br />

3<br />

4<br />

6<br />

4<br />

19/43<br />

Floyd의 최단 경로 알고리즘 I – 계속<br />

문제: 가중치 방향 그래프에서 최단 경로 찾기(가중치는 양수)<br />

입력: 가중치 방향 그래프 W, W에있는정점의수n<br />

출력: 2차원 배열, 이 배열의 각 요소는 한 정점에서 다른 정점으로<br />

최단 경로의 길이를 나타낸다.<br />

void floyd(int n, number W[][], number D[][]){<br />

index i, j, k;<br />

D = W;<br />

for(k=1; k≤n; k++)<br />

T(n) = n 3 ∈Θ(n 3 )<br />

for(i=1; i≤n; i++)<br />

for(j=1; j≤n; j++)<br />

D[i][j] = min(D[i][j], D[i][k] + D[k][j])<br />

}<br />

20/43


Floyd의 최단 경로 알고리즘 II – 계속<br />

문제, 입력: Floyd 알고리즘 I과동일<br />

출력: Floyd 알고리즘 I의출력, 다음과 같은 2차원 배열 P<br />

P[i][j]: i에서 j로 최단경로 상에 중간노드가<br />

없는 경우는 0,<br />

있으면 중간노드 중에서 색인 값이 가장 큰 노드,<br />

void floyd(int n, number W[][], number D[][], number P[][]){<br />

index i, j, k;<br />

for(i=1; i


최적화 문제와 동적 프로그래밍<br />

동적 프로그래밍을 이용한 최적화 문제를 해결하는 알고리즘 개발<br />

과정<br />

단계 1. 문제의 사례에 대한 최적해를 주는 재귀 관계식 정립<br />

단계 2. 상향식 방법으로 최적해의 값을 계산한다.<br />

예5.1)<br />

최단 경로의 길이를 구한다.<br />

단계 3. 상향식 방법으로 최적해를 구성한다.<br />

예5.2)<br />

최단 경로를 구한다.<br />

모든 최적화 문제는 동적 프로그래밍으로 해결할 수 있는 것은 아니다.<br />

최적 원칙(principle of optimality): 어떤 문제의 사례에 대한 최적해가<br />

그 사례의 모든 부분사례의 최적해를 항상 포함하고 있으면 이 원칙이<br />

적용된다고 한다.<br />

이 원칙이 적용되어야 동적 프로그래밍으로 최적화 문제를 해결할<br />

수있다.<br />

23/43<br />

최적의 원칙<br />

적용되는 경우<br />

최단 경로 문제<br />

v i 에서 v j 까지 최단 경로가<br />

v k 를 포함하면 이 경로의<br />

부분 경로인 v i 에서 v k 까지의<br />

경로는두정점간의최단<br />

경로이다.<br />

1 v 1<br />

3 1<br />

v 2<br />

v 3<br />

2<br />

4 1<br />

v 4<br />

적용되지 않는 경우<br />

최장 경로 문제<br />

문제의 대한 해를<br />

단순 경로로 제한<br />

v 1 에서 v 4 까지의 최장 경로<br />

(v 1 , v 3 , v 2 , v 4 ) = 7<br />

v 1 에서 v 3 까지의 최장 경로<br />

(v 1 , v 2 , v 3 ) = 4<br />

v 1 에서 v 4 까지의 최장 경로<br />

의 부분 경로는 항상 최장<br />

경로가 아니다.<br />

24/43


연쇄 행렬곱셈<br />

i×j 행렬과 j×k 행렬을 곱하기 위해서는 기본적으로 i×j×k 곱셈이<br />

필요하다.<br />

예5.3)<br />

2×3 행렬과 3×4 행렬을 곱하기<br />

⎡7 8 9 1⎤<br />

⎡1 2 3⎤⎢ 29 35 41 38<br />

2 3 4 5<br />

⎥ ⎡ ⎤<br />

⎢<br />

4 5 6<br />

⎥ = ⎢<br />

74 89 104 83<br />

⎥<br />

⎣ ⎦<br />

⎢ ⎥<br />

⎢<br />

⎣ ⎦<br />

⎣6 7 8 9⎥⎦<br />

29 = 1×7 + 2×2 + 3×6, 이와 같은 계산을 결과 행렬 i×k번<br />

수행해야 한다.<br />

다음과 같은 일련의 행렬을 곱하는 경우를 생각하여 보자.<br />

A × B × C × D<br />

20× 2 2× 30 30×<br />

12 12×<br />

8<br />

중요한 사실. 곱하는 순서는 결과에 영향을 주지 않는다.<br />

예5.4)<br />

A(B(CD)) = (AB)(CD)<br />

25/43<br />

연쇄 행렬곱셈 -계속<br />

일련의 행렬을 곱하는 경우에는 곱하는 순서에 따라 필요한 기본<br />

연산의 수가 크게 달라진다.<br />

예5.5)<br />

A(B(CD) 30×12×8+2×30×8+20×2×8 = 3680<br />

(AB)(CD) 20×2×30+30×12×8+20×30×8 = 8880<br />

A((BC)D) 2×30×12+2×12×8+20×2×8 = 1232<br />

((AB)C)D 20×2×30+20×30×12+20×12×8 = 10320<br />

목표: 가장 최적의 곱하는 순서를 구하는 것<br />

26/43


연쇄 행렬곱셈의 무작정 알고리즘<br />

알고리즘: 가능한 모든 순서를 구한 다음에 그 중에 가장 최적인 순서를<br />

선택한다.<br />

시간복잡도: 지수시간<br />

Why<br />

t n : n개의 행렬(A 1 , A 2 , …, A n )을 곱할 수 있는 가능한 모든 순서의 수<br />

t n 의 부분집합으로서 A 1 을 맨 마지막으로 곱하는 경우를 생각해<br />

볼수있다. 이것의 가능한 경우의 수는 t n-1 이다.<br />

t n 의 부분집합으로서 A n 을 맨 마지막으로 곱하는 경우도 생각해<br />

볼수있다. 이것의 가능한 경우의 수는 t n-1 이다.<br />

따라서 다음이 성립한다.<br />

t n ≥ t n-1 +t n-1 = 2t n-1<br />

t 2 = 1이고, t n ≥ 2t n-1 ≥ 2 2 t n-2 ≥ … ≥ 2 n-2 t 2 ∈Θ(2 n )이다.<br />

27/43<br />

동적 프로그래밍을 이용한 연쇄 행렬곱셈<br />

이 문제는 다음과 같은 이유에 의해 최적의 원칙에 적용된다는 것을<br />

알수있다.<br />

예5.6)<br />

6개의 행렬을 곱하는 최적의 순서가 다음과 같다고 하자.<br />

A 1 ((((A 2 A 3 )A 4 )A 5 )A 6 )<br />

(A 2 A 3 )A 4 는 A 2 에서 A 4 까지의 행렬을 곱하는 최적의 순서이다.<br />

일련의 행렬을 곱하는 경우에 다음이 성립한다.<br />

A k-1 의열수와A k 의 행 수는 같아야 한다.<br />

d k 를 A k 의열의수라하면A k 의행의수는d k-1 이다.<br />

d 0 : A 1 의행수<br />

재귀 관계식의 구축<br />

M[i][j]: i


동적 프로그래밍을 이용한 연쇄 행렬곱셈<br />

6개의 행렬을 곱하는 최적의 순서는 다음 중 하나에 포함된다.<br />

A 1 (A 2 A 3 A 4 A 5 A 6 ) 사실 1. 최적 원칙이 적용되는 문제이다.<br />

(A 1 A 2 )(A 3 A 4 A 5 A 6 ) 사실 2. 옆에 있는 다섯 가지는 크게<br />

(A 1 A 2 A 3 )(A 4 A 5 A 6 ) 두 부분으로 나누어진다.<br />

(A 1 A 2 A 3 A 4 )(A 5 A 6 ) 중요 관찰. 나누어진 두 부분은 사실 1에<br />

의해 최적이다.<br />

(A 1 A 2 A 3 A 4 A 5 )(A 6 )<br />

따라서 최적의 순서는 다음과 같이 정의된다.<br />

M[1][6] = min( M[1][ k] + M[ k+ 1][6] + d d d )<br />

위 결과를 일반화하면 다음과 같다.<br />

0 k 6<br />

1≤k≤5<br />

M[][ i j] = min( M[][ i k] + M[ k+ 1][ j] + d d d )<br />

Mi [][] i = 0<br />

i−1<br />

k j<br />

i≤k≤j−1<br />

29/43<br />

동적 프로그래밍을 이용한 연쇄 행렬곱셈<br />

예5.7) 5.7) A<br />

1× A<br />

2× A<br />

3× A<br />

4× A<br />

5×<br />

A<br />

6<br />

5×<br />

2<br />

2× 3 3× 4 4×<br />

6 6× 7 7×<br />

8<br />

1<br />

2<br />

3<br />

4<br />

5<br />

6<br />

1<br />

0<br />

2<br />

30<br />

0<br />

3<br />

64<br />

24<br />

0<br />

4<br />

132<br />

72<br />

72<br />

0<br />

5<br />

226<br />

156<br />

198<br />

168<br />

0<br />

6<br />

348<br />

268<br />

366<br />

392<br />

336<br />

0<br />

M[i][i] = 0<br />

M[1][2] = M[1][1] + M[2][2] + 5×2×3 = 30<br />

M[2][3] = M[2][2] + M[3][3] + 2×3×4 = 24<br />

…<br />

M[5][6] = M[5][5] + M[6][6] + 6×7×8 = 336<br />

M[1][3] = min(M[1][1] + M[2][3] + 5×2×4,<br />

M[1][2] + M[3][3] + 5×3×4)<br />

= min(24+40,30+60) = 64<br />

…<br />

M[1][4] = min(M[1][1] + M[2][4] + 5×2×6,<br />

M[1][2] + M[3][4] + 5×3×6,<br />

M[1][3] + M[4][4] + 5×4×6)<br />

= min(72+60,30+72+90,64+120) = 132<br />

…<br />

30/43


최적의 순서 구축<br />

최적의 순서를 얻기 위해서는<br />

M[i][j]를 계산할 때 최소값을<br />

주는 k를 P[i][j]에 보관한다.<br />

예5.8) 5.8)<br />

P[2][5] = 4<br />

이것은 A 2 에서 A 5 까지<br />

곱하는 순서는 다음과 같다는<br />

것을 의미한다.<br />

1<br />

2<br />

3<br />

1<br />

2<br />

1<br />

3<br />

1<br />

2<br />

4<br />

1<br />

3<br />

3<br />

5<br />

1<br />

4<br />

4<br />

6<br />

1<br />

5<br />

5<br />

(A 2 A 3 A 4 )A 5<br />

최적의 순서는…<br />

P[1][6] = 1 <br />

A 1 (A 2 A 3 A 4 A 5 A 6 )<br />

4<br />

4<br />

5<br />

P[2][6] = 5 (A 2 A 3 A 4 A 5 )A 6<br />

5<br />

5<br />

P[2][5] = 4 (A 2 A 3 A 4 )A 5<br />

P[2][4] = 3 (A 2 A 3 )A 4<br />

P[2][3] = 2 A 2 (A 3 )<br />

31/43<br />

동적 프로그래밍을 이용한 연쇄 행렬곱셈<br />

int minmult(int n, int[] d, index[][] P){<br />

index i, j, k, diagonal;<br />

int[1..n][1..n] M;<br />

for(i=1;i≤n;i++) M[i][i] = 0;<br />

for(diagonal=1;diagonal≤n-1;diagonal++){<br />

for(i=1; i≤n-diagonal; i++){<br />

j = i+diagonal;<br />

M[i][j] = min(M[i][k]+M[k+1][j]+d[i-1]*d[k]*d[j]);<br />

// where i≤k≤j-1<br />

P[i][j] = a value of k that gave the min<br />

}<br />

}<br />

return M[1][n];<br />

}<br />

diagonal = 1<br />

i = 1, j = 2, k = 1<br />

i = 2, j = 3, k = 2<br />

i = 3, j = 4, k = 3<br />

i = 4, j = 5, k = 4<br />

i = 5, j = 6, k = 5<br />

diagonal = 2<br />

i = 1, j = 3, k = 1, 2<br />

i = 2, j = 4, k = 2, 3<br />

i = 3, j = 5, k = 3, 4<br />

i = 4, j = 6, k = 4, 5<br />

32/43


동적 프로그래밍을 이용한 연쇄 행렬곱셈<br />

모든 경우 분석<br />

k 루프를 수행하는 횟수: min 계산<br />

(j-1)-i+1 = i+diagonal-1-i+1 = diagonal<br />

for-i 루프를 수행하는 횟수 = n-diagonal<br />

전체는 다음과 같다.<br />

n−1<br />

nn ( − 1)( n+<br />

1) 3<br />

∑ [( n- diagonal) × diagonal] = ∈Θ( n )<br />

6<br />

diagonal=<br />

1<br />

33/43<br />

트리 구조의 기본<br />

트리(tree)<br />

루트(root)라고 하는 유일한 시작 노드를 가지며,<br />

각 노드는 여러 개의 자식 노드를 가질 수 있는 구조로서,<br />

루트에서 각 노드까지의 경로가 유일한 구조를 말한다.<br />

트리는 재귀 구조(recursive structure)라한다.<br />

각 노드를 기준으로 그 노드가 루트가 되는 부분트리(subtree)를<br />

만들수있다.<br />

이진 트리(binary tree): 각 노드가 최대 두 개의 자식 노드만을 가질 수<br />

있는 트리<br />

노드의 깊이(depth): 루트 노드부터 그 노드까지 간선의 수<br />

다른 말로 트리에서 노드의 레벨(level)이라 한다.<br />

34/43


트리 구조의 기본 -계속<br />

균형 이진 트리(balanced binary tree): 트리의 모든 노드의 왼쪽<br />

부분트리와 오른쪽 부분트리의 깊이가 최대 하나 차이가 나는 트리<br />

이진 검색 트리의 정의: 순서가 정의되어 있는 항들로 구성한<br />

이진 트리로서 다음과 같은 특성을 가지고 있다.<br />

각 노드는 키를 가지고 있다.<br />

한 노드의 키는 그 노드의 왼쪽 부분트리에 있는 노드들의 모든<br />

키보다 크다.<br />

한 노드의 키는 그 노드의 오른쪽 부분트리에 있는 노드들의 모든<br />

키보다 크다.<br />

최적의 이진 검색 트리: 키를 찾기 위한 평균 시간이 최소화된 이진<br />

검색 트리<br />

35/43<br />

이진 검색 트리의 예<br />

트리 1.<br />

트리 2.<br />

David<br />

Carmen Peter<br />

Isabelle Tom<br />

Ralph Wally<br />

Peter<br />

David<br />

Tom<br />

Carmen Isabelle Ralph Wally<br />

트리 2에서 모든 키를 검색할<br />

확률이 같으면 트리 2는 최적의<br />

이진 검색 트리이다.<br />

검색시간(search time): 어떤 키<br />

를 찾기 위해 필요한 비교 횟수<br />

이 시간은 depth(key)+1이다.<br />

예) 트리 1에서 Peter: 2<br />

트리에 key 1 , key 2 , …, key n 이<br />

있고, p i 는 key i 를 검색할 확률<br />

이며, c i 는 key i 를 검색하기 위해<br />

필요한 검색시간이라 하자.<br />

그러면 이 트리에서 평균<br />

n<br />

검색 시간은<br />

∑ 이다.<br />

i =<br />

cp<br />

1 i i<br />

이 값을 최소화하는 것이<br />

목표<br />

36/43


최적의 이진 검색 트리의 예<br />

예5.9)<br />

p 1 = 0.7, p 2 = 0.2, p 3 = 0.1<br />

트리 a. 3(0.7)+2(0.2)+1(0.1) = 2.6<br />

트리 b. 2(0.7)+3(0.2)+1(0.1) = 2.1<br />

트리 c. 2(0.7)+1(0.2)+2(0.1) = 1.8<br />

트리 d. 1(0.7)+3(0.2)+2(0.1) = 1.5<br />

트리 e. 1(0.7)+2(0.2)+3(0.1) = 1.4<br />

key 3<br />

key 3<br />

key 1<br />

key 1<br />

key 2<br />

트리 b.<br />

key 2<br />

key 1<br />

key 3<br />

key 2<br />

key 1<br />

key 3<br />

트리 a.<br />

트리 d.<br />

key 1<br />

key 2<br />

트리 c. key 2<br />

key 3<br />

트리 e.<br />

37/43<br />

동적 프로그래밍을 이용한 최적의 이진 검색 트리<br />

최적의 이진 검색 트리를 찾기 위해 가능한 모든 이진 검색 트리를<br />

고려하는 것은 최소한 지수시간이 필요하다.<br />

j<br />

c p m=<br />

i m m이 최소화되는 key i 에서 key j 로 구성된 트리가 있다고 하자.<br />

이런 트리를 최적이라 하며, 최적값을 A[i][j]로 표기한다.<br />

A[i][i] = p i<br />

예5.10)<br />

p 1 = 0.7, p 2 = 0.2, p 3 = 0.1일때A[2][3]<br />

1(p 2 )+2(p 3 ) = 1(0.2) + 2(0.1) = 0.4<br />

∑<br />

2(p 2 )+1(p 3 ) = 2(0.2) + 1(0.1) = 0.5<br />

참고. 위 해답은 3개의 값을 모두 고려하였을 때의 해답의 부분<br />

해답이다.<br />

최적의 원칙이 이 문제에도 적용된다.<br />

38/43


동적 프로그래밍을 이용한 최적의 이진 검색 트리<br />

tree k : key k 가 루트에 있어야 한다는 제약이 있을 경우에 최적의 이진<br />

검색 트리<br />

그러면 평균 검색 시간은 다음과 같다.<br />

A[1][ k− 1] + p1 + + pk− 1<br />

+ p [ 1][ ]<br />

k + A k+ n + pk+<br />

1+ +<br />

p<br />

<br />

<br />

n<br />

<br />

<br />

왼쪽 부분트리의<br />

평균 검색 시간<br />

루트 때문에<br />

추가적으로 필요한 시간<br />

루트의<br />

평균 검색 시간<br />

오른쪽 부분트리의<br />

평균 검색 시간<br />

루트 때문에<br />

추가적으로 필요한 시간<br />

n<br />

A[1][ k− 1] + A[ k+ 1][ n]<br />

+∑ p<br />

m=<br />

1<br />

m<br />

39/43<br />

동적 프로그래밍을 이용한 최적의 이진 검색 트리<br />

A[1][n]은 다음과 같다.<br />

n<br />

A[1][ n] = min( A[1][ k− 1] + A[ k+ 1][ n])<br />

+∑ p<br />

m=<br />

1<br />

1≤k≤n<br />

여기서 A[1][0] = 0이고, A[n+1][n]=0이다.<br />

이것을 일반화하면 다음과 같다.<br />

j<br />

Ai [][ j] = min( Ai [][ k− 1] + Ak [ + 1][ j])<br />

+ p<br />

1≤k≤n<br />

Ai [][] i = p, Ai [][ i− 1] = 0, A[ j+ 1][ j] = 0<br />

i<br />

∑<br />

m=<br />

i<br />

m<br />

m<br />

40/43


동적 프로그래밍을 이용한 최적의 이진 검색 트리<br />

예5.10) 5.10) David(3/8), Isabelle(3/8), Ralph(1/8), Wally(1/8)<br />

1<br />

2<br />

3<br />

4<br />

5<br />

0<br />

0<br />

1<br />

3/8<br />

0<br />

2<br />

9/8<br />

3/8<br />

0<br />

3<br />

11/8<br />

5/8<br />

1/8<br />

0<br />

4<br />

7/4<br />

1<br />

3/8<br />

1/8<br />

0<br />

A[i][0] = 0<br />

A[1][1] = 3/8, A[2][2] = 3/8,<br />

A[3][3] = 1/8, A[4][4] = 1/8<br />

A[1][2] = min(A[1][0]+A[2][2],A[1][1]+A[3][2])<br />

+6/8<br />

= min(3/8,3/8)+6/8 = 9/8<br />

A[2][3] = min(A[2][1]+A[3][3],A[2][2]+A[4][3])<br />

+4/8<br />

= min(1/8,3/8)+4/8 = 5/8<br />

A[3][4] = min(A[3][2]+A[4][4],A[3][3]+A[5][4])<br />

+2/8<br />

= min(1/8,1/8)+2/8 = 3/8<br />

…<br />

41/43<br />

동적 프로그래밍을 이용한 최적의 이진 검색 트리<br />

float optsearchtree(int n, float[] p, index[][] R){<br />

index i, j, k, diagonal;<br />

int[1..n+1][0..n] A;<br />

for(i=1;i


동적 프로그래밍을 이용한 최적의 이진 검색 트리<br />

모든 경우 분석<br />

k 루프를 수행하는 횟수: min 계산<br />

j-i+1 = i+diagonal-i+1 = diagonal+1<br />

for-i 루프를 수행하는 횟수 = n-diagonal<br />

전체는 다음과 같다.<br />

n−1<br />

nn ( − 1)( n+<br />

4) 3<br />

∑ [( n- diagonal) × (diagonal+1)] = ∈Θ( n )<br />

6<br />

diagonal=<br />

1<br />

43/43

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!