About dynamic programming

예시) Fibonacci number

Matrix chain multiplication 보충 설명

https://www.youtube.com/watch?v=n_3E2-UhLeU

행렬A가 p x q 사이즈고, 행렬 B가 q x r 사이즈면, 결과 행렬 C는 p x r 이됨

void product(int A[][], int B[][], int C[][]){
	for (int i = 0; i < p; i++){
		for (int j = 0; j < r; j++){
			C[i][j] = 0;
			for (int k = 0; k < q; k++)
				C[i][j] += A[i][k] * B[k][j];
		}
	}
}

이 때 곱셈연산의 개수는 i,j,k loop를 다 돌아야 되니까, pqr임

다시 말해서, p x q이랑 q x r 행렬 내적하는데 필요한건 pqr이라는 것

[예시]

자 그럼 이 때, 행렬 A가 10x100, 행렬 B는 100x5, 행렬 C는 5x50 이라고 해보자.

ABC는 결합법칙으로 인해, 두 가지 방법으로 계산할 수 있다. ①(AB)C or ②A(BC)

①의 첫번째 연산

②의 첫번째 연산

n개의 행렬의 곱 A1A2A3…An을 계산하는 최적의 순서는? (이 때, A_i = p_k-1 x p_k 라고 가정 → 인접한 행렬은 Multiplication 가능)

int matrixChain(int n)
{
	// i=j인 case, table의 대각선 방향은 다 0으로 채운다
	for (int i = 1; i <= n; i++){
			m[i][i] = 0;  
	}
	// 좌하단 방향 대각선으로 해결하는 형태
	for (int r = 1; r <= n-1; r++) // 대각선 한 줄 채웠으니, for문은 n-1회 하면됨. 테이블에서 맨 윗줄 8까지지만, 7번만 더 채운다고
	{
			for (int i = 1; i <= n - r; i++) // r이 늘어날 수록, 채워야되는 칸은 적어지는 것 반영한 
			{
					int j = i + r; // m[i,j] format에서, 행 인덱스를 i라고 할 때, 열 index는 i+r이다. 대각선 방향으로 진행되는 걸 고려하면
					m[i][j] = m[i+1][j] + p[i-1]*p[i]*p[j];
					for (int k = i + 1; k <= j-1; k++)
					{
							if (m[i][j] > m[i][k] + m[k+1][j] + p[i-1]*p[k]*p[j])
								m[i][j] = m[i][k] + m[k+1][j] + p[i-1]*p[k]*p[j];
					}
			}
	}
	return m[1][n]; // 우리의 최종 goal
}

예시 LCS: Longest Common Sequence

LCS 보충설명

L[i][j] 점화식

첫번째 문자열의 1~i번째까지 문자 까지,

두번째 문자열의 1~j번째까지 문자 비교할 때, 공통 부분 문자열의 최대 길이를 L이라고 할 때 점화식은 아래와 같다.

image.png

점화식만 보면 복잡한데, Table로 그려서 찾을 수 있다.

x 문자열 = A B C B D A B라고 하고, y문자열 = B D C A B A 라고 할 때 L을 구해보자.

  1. 아래와 같이 테이블을 그린다. 첫 행과 첫 열은 별도로 추가하고 0으로 padding한다.

    image.png

  2. 이제 행 단위로 오른쪽으로 순차로 값을 채워가는데, 채우는 기준은 아래와 같다.

    1. 다른 문자를 만난 일반 경우 : 왼쪽 칸과 위에 칸 중에서 큰 수를 취한다.
    2. 같은 문자를 만난 경우 : 좌상단 대각선 값에 ++을 취해준다.

    image.png

    여기서 최우측최하단 값이 Longest length이다.

  3. Sequence를 찾으려면 역추적을 해야되는데 아래와 같은 조건으로 따라간다

    1. 다른 문자를 만난 일반 경우 : 좌측 칸과 위쪽 칸 중에 큰 쪽으로 간다.
      • 만약 같은 값이 있다면 취사선택하면 되는데, 그럼 Sequence 경우의 수가 늘어난다는 뜻이다.
    2. 같은 문자를 만난 경우 : 좌측 상단 대각선 방향으로 이동한다.
  4. 첫 열에 다다르면 끝나는데, 그 때 대각선으로 떨어지는 문자들을 모아본다.

    image.png

    image.png

    image.png

// pseudo code
function LCS(x[1,.m], y[1,.n]):
	// 2차원 배열 L[m+1][n+1] 초기화
	declare int L[0..m][0..n]
	
	// Step 1: LCS Table 채우기
	for i from 0 to m:
		for j from 0 to n:
			if i == 0 or j == 0: // 첫번째 행,열 0 padding
				L[i][j] = 0
			else if x[i-1] == y[j-1]: // 문자열이 같으면 이전 대각선 값에 ++
				L[i][j] = L[i-1][j-1] + 1;
			else: // 다르면 좌측이나 상단 중 같은 값 선택
				L[i][j] = max(L[i-1][j], L[i][j-1])
	
	// Step 3: LCS 역추적
	...

아마 이미 Libarary에서 제공하는 것도 많을거고.

메모리에 저장한 값을 재사용함으로써 직접적인 연산량은 줄어들었는데 cost는 Memory 사이즈

추가