최적의 해를 구하기에 시간이 너무 많이 걸리거나 메모리 공간이 많이 필요한 문제가 있다. 이는 컴퓨터의 연산 속도, 메모리 공간에 대한 제약이 걸려 효율적인 알고리즘이 필요하다.
다만, 이런 문제들 중에서도 메모리 공간을 약간 더 사용하여, 속도를 비약적으로 높이는 방법이 있는데 이 중 대표적인 방법이 다이나믹 프로그래밍(Dynamic Programming)기법(동적 계획법)이다.
한번 해결된 부분 문제의 정답을 메모리에 기록하여, 한번 계산한 답은 다시 계산하지 않도록 하는 문제 해결법 이다.
다이나믹 프로그래밍은 *점화식을 그대로 코드로 옮겨 구현할 수 있다.
(점화식: 인접한 항들 사이의 관계식)
대표적인 예시 문제가 피보나치 수열 문제이다.
피보나치 함수 코드
# 피보나치 함수(Fibonacci Function)을 재귀함수로 구현
def fibo(x):
if x == 1 or x == 2:
return 1
return fibo(x - 1) + fibo(x - 2)
print(fibo(7))

위 사진만 보더라도 n이 커질수록 수행 시간이 기하급수적으로 늘어난다.
빅오 표기법으로는 의 지수 시간이 소요된다고 한다.
이를 해결하기 위해 보면 같은 함수를 계속 불러내는 것을 확인할 수 있다.
사진의 경우 f(4)를 총 3번 부르고 있다. 즉, f(n)에서 n이 커질수록 반복해서 호출하는 수가 많아진다.
그러므로 이런 문제는 반복 계산을 줄여주는 다이나믹 프로그래밍을 사용하면 효율적이다.
다이나믹 프로그래밍은 다음 조건을 만족해야 사용 가능하다.
1. 큰 문제를 작은 문제로 나눌 수 있다.
2. 작은 문제에서 구한 정답은 그것을 포함하는 큰 문제에서도 동일하다.
피보나치 수열은 위 2개의 조건을 만족하므로, 이 문제를 *메모이제이션(Memorizations)기법을 사용해 해결해보자.
(*메모이제이션: DP를 구현하는 방법 중 한 종류.
이전에 계산된 결과를 일시적으로 기록해 값을 저장하는 방법, 캐싱(caching)이라고도 한다. 한번 구한 정보를 리스트에 저장하는 것.)
보통 재귀함수의 오버헤드를 줄이기 위해 보텀업 방식으로 구현하는 것을 권장한다.
재귀 함수를 이용하여 큰 문제를 해결하기 위해 작은 문제를 호출하는 방식.
# 한 번 계산된 결과를 메모이제이션(Memoization)하기 위한 리스트 초기화
d = [0] * 100
# 피보나치 함수(Fibonacci Function)를 재귀함수로 구현 (탑다운 다이나믹 프로그래밍)
def fibo(x):
# 종료 조건(1 혹은 2일 때 1을 반환)
if x == 1 or x == 2:
return 1
# 이미 계산한 적 있는 문제라면 그대로 반환
if d[x] != 0:
return d[x]
# 아직 계산하지 않은 문제라면 점화식에 따라서 피보나치 결과 반환
d[x] = fibo(x - 1) + fibo(x - 2)
return d[x]
print(fibo(99))
단순히 반복문을 이용하여 작은 문제를 먼저 해결하고, 해결된 작은 문제를 모아 큰 문제를 해결하는 방식.
# 앞서 계산된 결과를 저장하기 위한 DP 테이블 초기화
d = [0] * 100
# 첫 번째 피보나치 수와 두 번째 피보나치 수는 1
d[1] = 1
d[2] = 1
n = 99
# 피보나치 함수(Fibonacci Function) 반복문으로 구현(보텀업 다이나믹 프로그래밍)
for i in range(3, n + 1):
d[i] = d[i - 1] + d[i - 2]
print(d[n])