728x90
다이나믹 프로그래밍이란
- dynamic programming은 최적의 답을 찾기 위한 수학적인 개념으로 복잡한 문제를 간단한 문제의 sequence로 변형해 풀이하는 divide and conquer 최적화 방법 중 하나이다.
- divide and conquer 방법의 단점은 동일한 계산을 반복해서 수행해야하는 단점이 있는데 중간 계산 결과를 저장해 한 번만 계산하도록 하는 memoization으로 이 문제를 극복합니다.
- dynamic programming을 적용할 수 있는 문제는 '최적성 원리'가 성립하는 문제다. 최적성의 원리는 문제 전체의 최적해가 작은 부분의 최적해로 구성될 때 성립한다. 이 경우 문제를 점화식 형태로 구성해 풀이한다.
피보나치 수열
- 피보나치 수열은 첫 두 항이 1로 시작하고, 연속된 두 항의 합이 다음 항의 값이 되는 수열이다.
1항 | 2항 | 3항 | 4항 | 5항 | 6항 | 7항 | 8항 |
---|---|---|---|---|---|---|---|
1 | 1 | 2 | 3 | 5 | 8 | 13 | 21 |
- 피보나치 수열은 명시적인 일반항을 정의할 수 없고 항 간의 관계로 점화식을 정의해 표현할 수 있다. 연속된 두 항의 합이 그 다음 항의 값이 된다는 말을 점화식으로 표현하면 다음과 같이 표현할 수 있다.
- 반복적인 계산으로 피보나치 수열의 임의의 항의 값을 계산할 수 있다. 재귀 함수를 통해 피보나치 수열의 임의의 항을 계산한다고 하면 명시적으로 값을 정의한 제 1항과 제 2항이 나올 때 까지 점화식을 활용해 치환해 나가야한다.
- 예를 들어
의 값을 구하고자 할 때 점화식을 사용하면f[5] 와f[4] 의 값을 계산해야한다.f[3] 은f[3] 로 바로 2로 계산할 수 있지만,f[2]+f[1] 는f[4] 로 전개되어f[3]+f[2] 을 다시 한번 계산해야한다.f[3] 를 재귀적으로 계산하는 과정은 아래 그림과 같다.f[5]

를 구할 때 까지f[5] 은 중복되어 계산된다. 만약 구하고자 하는 항이 커질 수록 중복되는 계산의 수가 급격히 늘어나 비효율적이다.f[3] - 이때 피보나치 수열의 중간 계산 값을 저장하고 이후에 그 값을 활용하면 비용을 줄일 수 있다. 정의가 되어있는 1항과 2항을 제외하고 3항부터 계산한 값을 다른 공간에 저장을 하고 다음번 해당 값이 필요할 때 그 값을 다시 계산하는 것이 아니라 저장된 값을 읽어오는 방식으로 계산 비용을 줄일 수 있다.

- 아래 코드에서 line 22의 n값을 바꾸며 확인해보면 실행시간의 차이를 확인할 수 있다. n이 커짐에 따라 소요 시간의 차이가 커지고 있다.

This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import time | |
F = [0]*50 | |
def fibon: | |
if n == 1 or n == 2: | |
return 1 | |
return fibon−1 + fibon−2 | |
def fibo_memoizationn: | |
if n == 1 or n==2: | |
F[n] = 1 | |
return F[n] | |
if F[n]: # n항이 계산되어있다면 다시 계산하지 않고 저장한 값을 반환 | |
return F[n] | |
else: # 계산된 적이 없다면 새로 계산 | |
F[n] = fibo_memoizationn−1 + fibo_memoizationn−2 | |
return F[n] | |
n = 50 | |
start = time.time | |
f50 = fibon | |
fibo_time = time.time - start | |
start = time.time | |
f_m50 = fibo_memoizationn | |
fibo_m_time = time.time - start | |
printf"Recursive(value:f50,time:fibotime" | |
printf"Memoization ( value : {f_m50}, time : {fibo_m_time}" |
Dynamic programming을 구성하는 요소
Stages
- DP를 적용하기 위해서는 문제를 multipple stage로 구성하는 것이 필요하다.
- 하나의 stage는 일반적인 최적화 방법으로 해결할 수 있어야 하고, 이 결과는 다음 stage의 최적화에 도움을 준다.
- 시간에 따라 정의되는 stage는 순서가 명확하지만 종종 시간의 의미를 포함하지 않는 경우 Stage의 순서를 정의하는 것이 어려울 수 있다.
- 위의 피보나치 수열의 예시에서는 각각의 수열의 항이 stage가 된다.
State
- State는 각 stage를 설명하는 변수로 해당 stage에 도달하는 과정에 대한 정보를 모두 포함한다.
- 다음 stage의 state를 구할 때는 현재 state값만 넘겨주면 그 stage까지 과정을 생략하고 다음 state 값을 구할 수 있다.
- 위의 피보나치 수열의 예시에서는 수열의 항의 값이 state가 된다.
Decision
- 한 state에서 다음 state로 전환할 때 선택할 수 있는 state가 다수라면 그 중 하나를 선택해야 한다.
- 위의 피보나치 수열의 예시는 state가 전환할 때 선택지가 하나 뿐이기 때문에 decision은 고려하지 않는다.
점화식 표현
- dynamic programming을 구성요소로 표현하면 다음과 같이 표현할 수 있다.
는 n번째 stage의 state,s[n] n번째 stage의 decision이라고 하면 dynamic programming은 다음과 같이 정의할 수 있다.d[n] s[n]=f(s[n−i],d[n−j]) - dynamic programming 방식으로 문제를 해결하고자 할 때 stage, state, decision을 정의하는 것이 창의적인 아이디어가 필요한 부분이다.
Examples
- 이 문제는 dynamic programming의 구성 요소를 모두 포함하면서 어렵지 않게 풀 수 있어 dynamic programming의 개념을 익히기에 적합한 문제라고 생각한다.
- n년의 최대 수익은 1)A투자로 n-1년 최대 수익에 5%, 2)B투자로 n-3년 최대 수익에 20%, 3)C투자로 n-5년 최대 수익에 35%을 더한 값 중 가장 큰 값이 된다. 이로부터 최적성의 원리 조건을 만족하는 것을 확인하고 dynamic programming 방식으로 풀이하면 된다.
- stage는 시간(
)으로 정의할 수 있고, state(t )는 해당 년도에 얻을 수 있는 최대 수익, decision은 투자의 종류를 결정하는 것으로 정의한다. 이를 점화식 형태로 나타내면 다음과 같다.DP
- dynamic programming으로 문제를 정의한 후 코드로 구현하는 것은 어렵지 않게 구현할 수 있다.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
import sys def solveh,n: if n == 0: return h dp = [0] * n+1 dp[0] = h for i in range1,n+1: a,b,c = 0,0,0 if i >4: c = intdp[i−5]∗1.35 if i > 2: b = intdp[i−3]∗1.2 if i >0: a = intdp[i−1]∗1.05 dp[i] = maxa,b,c return dp[n] if __name__ == '__main__': H, N = listmap(int,sys.stdin.readline(.split)) sol = solveH,N printsol
같이 풀어보면 좋은 문제들
'알고리즘 공부 > 개념 정리' 카테고리의 다른 글
[알고리즘] 그래프 탐색 알고리즘, BFS와 DFS 2 | 2022.09.21 |
---|---|
[자료구조] Queue과 Stack 0 | 2022.08.21 |