ëì íë¡ê·¸ëë° - íêµê¸°ì êµì¡ëíêµ
ëì íë¡ê·¸ëë° - íêµê¸°ì êµì¡ëíêµ
ëì íë¡ê·¸ëë° - íêµê¸°ì êµì¡ëíêµ
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