수정입니다

Dynamic Programming 본문

전공/알고리즘

Dynamic Programming

nongdamgom 2024. 4. 18. 01:02

 

DP : Binomial Design - Part 1

이항계수 ( n개 중 k개를 뽑는 조합 )


(n,k) = (n-1, k-1) + (n-1, k)   0<k<n
       = 1           (if n == k or k==0)

B[n,k] = B[n-1, k-1] + B[n-1, k]   if 0<k<n
B[n,k] = 1

 


Q. solve B[5,3] => solve == algorithm 을 제시하고 design과 analysis를 제시해라 라는 뜻
==> DP.. local neighbor 계산 활용

neighbor clever enumeration != brute-force enumeration


DP == Save and Reuse.

 

 

Divide & Conquer 방식으로 Binomial design을 해보자.

D&C_Bin(n,k)
{
	if(n==k or k==0) return 1;  // termination
	else 
    		step 1  ==> 문제에 이미 Devide가 되어 있어서 필요 없음
    		step 2  ==> D&C_Bin_left(n-1, k-1) D&C_Bin_right(n-1, k) 
    		step 3  ==> return left + right 
}

 

D&C 방법
B[5,3] = B[4,2] +B[4,3]
B[4,2] = B[3,1] + B[3,2]
B[4,3] = B[3,2] + B[3,3]


--> linear recurrence가 발생함 ( 세부 문제로 쪼개는데 크게크게 쪼개지지 않음)
--> multi-dimensional recurrence.
--> observe 'subproblem overlapping' => 왼/오 가 독립이 안됨(문제 사이즈가 크게 줄어들지 않는다)
--> Fibonacci recurrence.
--> D&C recurrence X => inefficient => New approach.

 

=> 이러한 문제를 D&C 방식으로 푸는 것은 비효율적이므로, 새로운 방식을 도입.


Algorithm Design Approaches


1. Brute-Force => Full/Exhaustive Enumeration
2. Divide and Conquer
3. Dynamic Programming

 


Problem : subproblem recurrence =>  D&C 처럼 보인다.
-> D&C recurrence X -> subproblem overlap -> inefficient
-> TC grows Exponential!!! 
==> DP를 활용할 수 있다
--> Note: Principle of optimality, optimal substructure.

 

 


Binomial Design - Part 2

 

DP <Design>
Step 1:
 - Establish a recursive property from the problem
 - Identify(Find out. Derive) a recurrence equation from P.
 - 큰 문제 = 작은 문제 +/* 작은문제 +...+ 작은문제


Step 2:
 - Solve in a bottom-up fashion programming
  cf) D&C : top-down fashion(recursion) programming => 중첩되는 현상을 미리 알 수 없음
 - Solve smaller problems first
 - Save them(smaller problems) in arrays(tables, dict...)
 - Use them later. (from look-up tables)

 

Q. solve B[5,3]

Step 1. 

B[n,k] = B[n-1, k-1] + B[n-1, k]   if 0<k<n
B[n,k] = 1

수식은 이미 세워진 상태이다.

 

Step 2.

1) 가장 작은 문제 먼저 해결.

  0 1 2 3 4 5
0 1        N / A               N / A               N / A               N / A               N / A       
1 1 1        N / A               N / A               N / A               N / A       
2 1   1        N / A               N / A               N / A       
3 1     1        N / A               N / A       
4 1       1        N / A       
5 1     ans.   1

 

  • n개에서 k개를 뽑는 것이므로 n < k 인 경우는 구할 필요가 없음
  • n개에서 n개를 뽑는 경우, n개에서 1개를 뽑는 경우의 수는 1가지

2) local neighbor 계산을 이용해 그 다음으로 쉬운 문제 점점 풀어나가기

  0 1 2 3 4 5
0 1        N / A               N / A               N / A               N / A               N / A       
1 1 1        N / A               N / A               N / A               N / A       
2 1 2 1        N / A               N / A               N / A       
3 1 3 3 1        N / A               N / A       
4 1   here here 1        N / A       
5 1     ans.   1

 

  • 그 다음 쉬운 문제 => B[2,1] = B[1,0] + B[1,1] = 1 + 1 = 2
  • 그 다음 => B[3,1] = B[2,0] + B[2,1] = 1 + 2 = 3
  • 그다음 => B[3,2] = B[2,1] + B[2,2] = 2 + 1 = 3
  • ...
  • ans (5,3) = (4,2)+(4,3) ==> 표의 바로 위와 왼쪽 위를 활용하여 채울 수 있음.
  • => neighbor clever enumeration 

neighbor clever enumeration != brute-force enumeration

=> burte force는 매순간 필요한 계산을 매번 함, 하지만 이 방식은 한번씩만 계산 해 놓고, 그걸 활용해서 계산에 이용.


DP == Save and Reuse.

 

  0 1 2 3 4 5
0 1        N / A               N / A               N / A               N / A               N / A       
1 1 1        N / A               N / A               N / A               N / A       
2 1 2 1        N / A               N / A               N / A       
3 1 3 3 1        N / A               N / A       
4 1   6 4 1        N / A       
5 1     ans. = 10   1

 

 


DP : Binomial Design - Analysis


Step 1(resurrent eqn)

Step 2(bottom-up save and reuse)

Design = steps + 기본적 골격. + example
           = detailed algorithmic code X

DP_Bin(n,k) : iterative(for-loop, while-loop)
----------------------------------
for i = 0 -> n //step 2
    for j = 0 -> min(i,k) // k는 n의 개수를 넘을 수 없으므로 
        if(j==0 or j == i) B[i,j] = 1
        else B[i,j] = B[i-1,j-1] + B[i-1. j]  // step 1.
return B[n,k]



Analysis? Time complexity : T(n, k) = O(?)

 

D&C analysis TC = divide + conquer + combine


DP basic operations = array cell computations.
= table build-up time

 

 

B[5,3]의 계산량을 파악해보자.

  0 1 2 3 4 5
0        1                 
1        2               3               
2        4               5               6             
3        7               8               9               10           
4        11               12               13               14        필요없음  
5        15               16               17               18           

 

  • 1
  • 2
  • 3 (k)
  • 4
  • 4
  • 4

 

B[5,4]의 계산량을 파악해보자.

  0 1 2 3 4 5
0        1                 
1        2               3               
2        4               5               6             
3        7               8               9               10           
4        11               12               13               14               15         
5        16               17               18               19               20         
  • 1
  • 2
  • 3
  • 4 (k)
  • 5
  • 5

 

==> 

 

T(n,k) = 1+2+3+...+ k + (k+1)*(n-k+1) 
= k(k+1)/2 + (k+1)*(n-k+1) = O(nk)
= (2n-k+2)(k+1)/2 = O(nk)  ==> 테이블 크기 만큼


===> 쉽다. 그러므로 step 1을 유도하는 과정에 힘을 쏟아야 함.

 

 

 

DP : CMM Design - Part 1


DP for Chained Matrix Multiplication(CMM)

Def. Given a given ordered sequence of matrices,
Find an optimal order(= min # multiplications) for CMM.

 

Multiplying A = [pxq], B = [qxr] => AB = [pxr]
A = [3x3], B = [3x2] => AB = [3x2]  ==> matrix 곱 자체가 cpu 낭비가 심한 operation

Multiplying Chained Matrices (a sequence of matrices)
     A   x   B   x    C    x   D
[20x2] [2x30] [30x12] [12x8]  ==> 가장 짧게 끝나는 행렬 곱은?

--> Design : B&F = O(2^n), D&C(???), DP

 

 

 

Step 0. examples (guessing)
Step 1. recursive property = recurrence (math. expression)
Step 2. save/reuse

 

 


Step 0. examples (guessing)
: start with easier subproblems (hand computation)
: insight

  A1 x  A2 x  A3 x  A4 x  A5 x A6
[5x2] [2x3] [3x4] [4x6] [6x7] [7x8]
=> 가장 쉬운 문제? A1 x A1 = 0
=> 쉬운 문제? A2 x A3, A4xA5 ... A5xA6  (matrix 두개 곱하기)


A4 x A5 = 4x6x7 -> array 2D array M[4,5] save
*이걸 이차원 배열에 저장한다는 생각부터 하기 쉽지 않다...
A5 x A6 = 6x7x8 -> M[5,6] save
==> A3 x A2(X)   :  왜? 애초부터 matrix 순서는 1-2-3-4-5-6 으로 정해져서 주어짐. 반대 => 아예 다른거

 

M 1 2 3 4 5 6
1 0 5x2x3        
2        N / A        0 2x3x4      
3        N / A               N / A        0 3x4x6    
4        N / A               N / A               N / A        0 4x6x7  
5        N / A               N / A               N / A               N / A        0 6x7x8
6        N / A               N / A               N / A               N / A               N / A        0

 

 

 



==> 한단계 더 나아간다
==> 그 다음 쉬운 문제는?
A4 x A5 x A6 = min(P,Q)
1) (A4xA5)xA6 = 4x6x7 + 4x7x8 = M[4,5] + 4x7x8 = P
2) A4x(A5xA6) = 6x7x8 + 4x6x8 = M[5,6] + 4x6x8 = Q

 

M 1 2 3 4 5 6
1 0 5x2x3        
2        N / A        0 2x3x4      
3        N / A               N / A        0 3x4x6    
4        N / A               N / A               N / A        0 4x6x7 min(P,Q)
5        N / A               N / A               N / A               N / A        0 6x7x8
6        N / A               N / A               N / A               N / A               N / A        0

 




====> next ?
A1xA2xA3xA4 = M[1,4]
= min(M[1,3] + alpha, M[2,4]+beta, M[1,2]+M[3,4]+gamma)
(A1~A3)xA4
A1x(A2~A4)
(A1xA2)x(A3xA4)

 

M 1 2 3 4 5 6
1 0 5x2x3 M[1,3] M[1,4]    
2        N / A        0 2x3x4 M2,4]    
3        N / A               N / A        0 3x4x6    
4        N / A               N / A               N / A        0 4x6x7  
5        N / A               N / A               N / A               N / A        0 6x7x8
6        N / A               N / A               N / A               N / A               N / A        0

 

 



====> Final : M[1,6] = min
1. A1x(A2~A6) =      M[1,1] + M[2,6] + 5x2x8
2. (A1A2)x(A3~A6) =  M[1,2] + M[3,6] + 5x3x8
3. (A1~A3)x(A4~A6) = M[1,3] + M[4,6] + 5x4x8
4. (A1~A4)x(A5A6) =  M[1,4] + M[5,6] + 5x6x8
5. (A1~A5)xA6 =      M[1,5] + M[6,6] + 5x7x9

 

 A1 x  A2 x  A3 x  A4 x  A5 x A6
[5x2] [2x3] [3x4] [4x6] [6x7] [7x8]

M 1 2 3 4 5 6
1 0 5x2x3 = 30 30 + 5x3x4
= 90
24 + 5x2x4
= 64
64 + 5x4x6
= 184
72 + 5x2x6
= 132
30+72+5x3x6
= 192
132 + 5x6x7
= 342
156 + 5x2x7
= 226
64+168+5x4x7
30+198+5x3x7
226 + 5x7x8
= 506
268 + 5x2x8
= 348
132+336+5x6x8
= 708
64+392+5x4x8
= 616
30+276+5x3x8
= 426
2        N / A        0 2x3x4 = 24 24 + 2x4x6
= 72
72 + 2x3x6
= 108
72 + 2x6x7
= 156
198 + 2x3x7
= 240
24+168+2x4x7
= 248
156 + 2x7x8
= 268
276 + 2x3x8
= 324
72+336+2x6x8
24+392+2x4x7
3        N / A               N / A        0 3x4x6 = 72 72 + 3x6x7
= 198
168 + 3x4x7
= 252
198 + 3x7x8
= 366
392 + 3x4x8
= 488
72+336+3x6x8
= 552
4        N / A               N / A               N / A        0 4x6x7 = 168 168 + 4x7x8
= 392
336 + 4x6x8
= 528
5        N / A               N / A               N / A               N / A        0 6x7x8 = 336
6        N / A               N / A               N / A               N / A               N / A        0

 



Recurrence eqn.  

M[1,6] = min(M[1,k] + M[k+1,6] + C(0)xC(k)xX(6))
         for 1<=k<=5  (==> k 값이 변함 ?  k에 대한 범위가 필요함)

M[i,j] = min{  M[i,k] + M[k+1, j] + C(i-1)C(k)C(j)  }   
            for i <= k <= j-1
M[i,i] = 0

 

** C(i-1) 인 이유

=> result matrix의 크기는 i번째 matrix의 행 x   ?    x  j번째 matrix 의 열

=> 여기서의 C는 column을 나타내므로, i번째 matrix의 row를 나타내려면, i-1번째 matrix가 있다고 가정 후

Ai-1[?, C(i-1)]  Ai[C(i-1), C(i)] ....... Aj [?, C(j)]  이렇게 나타내는 것. (순서가 있는 행렬곱이라 이어진다)

 

 

 

DP: CMM - Analysis


DP(CMM) = linear # combinations

DP_CMM(input: matrices_seq, dimensions_row_col)
{
    for i -> n
        for j -> n
            M[i,j] = recurrence : function of subproblems
            ==> for(k= i -> j-1)

            즉
            for-loop(k)
            ...
            M[i,j] <- min()

}

 

==> 낭비 심함  ==> code optimization!! in DP.

 

 

Analysis: Time complexity A1xA2x...xAn

T(n) = #subproblems * time/subprob = O(n^3)
T(n) = O(n^2) * n -> O(n^3)


교과서 보면 보다 자세하게 구현되어 있다고 한다.
==> 아래쪽을 계산할 필요가 없으므로 인덱싱을 잘해서 코드 최적화를 이룰 수 있다.

=> code optimization is required for running-time for A.

 

 

'전공 > 알고리즘' 카테고리의 다른 글

Greedy - Kruskal Algorithm : MST  (0) 2024.04.19
Greedy - Prim Algorithm : MST  (1) 2024.04.19
DP - Floyd Algorithm : APSP  (1) 2024.04.19
Divide and Conquer  (0) 2024.04.13
Algorithms: Efficiency, Analysis, and Order  (0) 2024.04.13