일반적인 프로그래밍 분야에서의 동적이란?
반면에 다이나믹 프로그래밍에서는 별다른 의미 없이 사용된 단어
1. 최적 부분 구조(Optimal Substructure)
큰 문제를 작은 문제로 나눌 수 잇으며 작은 문제의 답을 모아서 큰 문제를 해결할 수 있을 때
2. 중복되는 부분 문제(Overlapping Subproblem)
동일한 작은 문제를 반복적으로 해결해야 할 때
피보나치 수열
1,1,2,3,5,8,13,21,34,55,89,...
n번째 수는 n-1, n-2번째 수의 합을 나열한 수열
1+1= 2 , 1+2= 3, 2+3=5
피보나치 수열 문제는 다이나믹 프로그래밍의 사용 이유를 알려주는 대표적인 문제 중 하나입니다.
다이나믹 프로그래밍을 언제 사용하는지 알기 위해서는 위의 구현 조건에 적합한 문제인지 판별해야 합니다.
1. 최적 부분 구조
예를 들어 피보나치 수열의 5번째 수를 알고 싶다면
1+1 = 2(3번째) , 1+2= 3(4번째), 2+3= 5(5번째) 로 정답은 5
5번째의 식을 다시 본다면 2(3번째) + 3(4번째) 라는 것을 알 수 있습니다.
이를 봤을 때 5번째라는 큰 문제를 3번째와 4번째의 두 문제로 나누어서 풀고 합하여도 해결할 수 있기 때문에 최적 부분 구조에 적합하다고 할 수 있습니다.
2. 중복되는 부분 문제
위 처럼 3번째 4번째를 나누어서 풀 때 중복되는 부분이 있는지 확인해 보아야 합니다.
1+2=3(4번째) 풀이에서 2는 사실 3번째 풀이에서 나오는 결과 값입니다.
3번째 풀이를 할 때 만약 이 값을 어떤 메모리에 저장을 하게 된다면 4번째 풀이를 할 때에는 3번째 풀이를 할 필요가 없어질 것입니다. 이 방법이 바로 밑에 설명 된 메모이제이션 이라고 부릅니다.
점화식: 인접한 항들 사이의 관계식
# 재귀 함수로 구현
def fibonacci(n):
if n == 1 or n == 2:
return 1
return fibonacci(n - 1) + fibonacci(n - 2)
위처럼 단순 재귀 함수로 해결하면 지수 시간 복잡도를 가지게 됩니다.(O(2^n))
다음과 같이 f(2)가 여러 번 호출되는 것을 확인할 수 있습니다.(중복되는 부분 문제)
f(30)을 계산하기 위해 약 10억 가량의 연산을 수행해야 합니다.
구현 조건을 만족하는지 확인
1. 최적 부분 구조
2. 중복되는 부분 문제
한 번 계산한 결과를 메모리 공간에 메모하는 기법
하향식이라고도 불립니다.
상향식이라고도 불립니다.
다이나믹 프로그래밍의 전형적인 형태는 보텀업 방식입니다.
d = [0] * 100
def fibo(n):
if n == 1 or n == 2:
return 1
if d[n] != 0:
return d[n]
d[n] = fibo(n - 1) + fibo(n - 2)
return d[n]
n = int(input())
d[1] = 1
d[2] = 1
for i in range(3,n+1):
d[i] = d[i-1] + d[i-2]
print(d[n])
작은 문제들을 먼저 해결해논 다음에 큰 문제들을 해결하는 과정
둘 다 최적 부분 구조를 가질 때 사용할 수 있습니다.
최적 부분 구조
큰 문제를 작은 문제로 나눌 수 있으며 작은 문제의 답을 모아서 큰 문제를 해결할 수 있는 상황
차이점은 부분 문제의 중복!
다이나믹 프로그래밍 문제에서는 각 부분 문제들이 서로 영향을 미치며 부분 문제가 중복됩니다.
분할 정복 문제에서는 동일한 부분 문제가 반복적으로 계산되지 않습니다.