다이나믹 프로그래밍은 메모리 공간을 약간 더 사용하여 연산 속도를 비약적으로 증가시키는 방법이다. 다만 항상 사용할 수 있는 것은 아니며, 다음 조건을 만족할 때 사용할 수 있다.
1. 큰 문제를 작은 문제로 나눌 수 있다.
2. 작은 문제에서 구한 정답은 그것을 포함하는 큰 문제에서도 동일하다.
메모이제이션(Memoization)은 다이나믹 프로그래밍을 구현하는 방법 중 한 종류로, 한 번 구한 결과를 메모리 공간에 메모해두고 같은 식을 다시 호출하면 메모한 결과를 그대로 가져오는 기법을 말한다. 메모이제이션은 값을 저장하는 방법이기에 캐싱(Caching)이라고도 한다.
구현 방법은 한 번 구한 정보를 리스트에 저장하는 것이다. 다이나믹 프로그래밍을 재귀적으로 수행하다가 같은 정보가 필요할 때는 예전에 구한 정답을 그대로 리스트에서 가져오면 되는 것이다.
피보나치 수열의 경우 다이나믹 프로그래밍을 사용하면 복잡한 연산량을 해결할 수 있다.
기존에 우리가 아는 피보나치 수열의 점화식을 토대로 코드를 구현하면 아래와 같다.
# 피보나치 함수
def fibo(x):
if x == 1 or x == 2:
return 1
return fibo(x-1) + fibo(x-2)
print(fibo(4))
위 코드의 문제점은 fibo(n)에서 n의 값이 커질수록 수행시간과 연산량이 기하급수적으로 증가한다. 피보나치 수열의 경우 시간 복잡도가 O(2^N)으로 N= 30, 즉 fibo(30)을 구하려 하면 약 10억 가량의 연산을 수행해야 한다. 이를 메모이제이션 기법을 사용하면 연산량이 보다 줄어든다.
# 한 번 계산한 결과를 메모이제이션하기 위한 리스트 초기화
d = [0] * 100
# 피보나치 함수를 재귀로 구현(탑다운 다이나믹 프로그래밍)
def fibo(x):
# 종료 조건
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))
위와 같이 다이나믹 프로그래밍을 적용했을 때의 피보나치 수열 알고리즘의 시간 복잡도는 O(N)이다. 한 번 구한 결과는 다시 구해지지 않기 때문이다. 이처럼 재귀 함수를 이용하여 다이나믹 프로그래밍 소스코드를 작성하는 방법을, 큰 문제를 해결하기 위해 작은 문제를 호출한다고 하여 탑다운(Top-Down) 방식이라고 한다.
재귀함수 대신 반복문을 사용하는 경우가 있는데 반복문을 사용하여 소스코드를 작성하는 경우 작은 문제부터 차근차근 답을 도출하여 바텀업(Bottom-Up) 방식이라고 말한다.
피보나치 수열을 Bottom-Up 방식으로 구현하는 방법은 아래와 같다.
# 피보나치 수열 (바텀업 방식)
# DP 테이블 초기화
d = [0] * 100
# 첫 번째 피보나치 수와 두 번째 피보나치 수는 1
d[1] = 1
d[2] = 1
n = 99
# 피보나치 함수를 반복문으로 구현(바텀업 방식)
for i in range(3, n-1):
d[i] = d[i-1] + d[i-2]
print(d[n])
메모이제이션은 때에 따라 다른 자료형을 이용할 수도 있다. 사전 자료형을 예시로 들 수 있는데 사전 자료형은 수열처럼 연속적이지 않은 경우에 유용하다. 코딩테스트에서 다이나믹 프로그래밍 문제는 대체로 간단한 형태로 출제된다. 특정 문제를 완전 탐색으로 접근했을 때 시간이 오래 걸리면 다이나믹 프로그래밍을 적용할 수 있는지 해결하고자 하는 부분 문제들의 중복 여부를 확인해봐야 한다.
또한 재귀함수로 비효율적인 프로그램을 작성하고 나중에 탑다운 방식처럼 작은 문제에서 구한 답이 큰 문제에서도 사용될 수 있으면 재귀 함수를 메모이제이션을 이용하여 코드 수정을 하는 것도 좋은 방법이다.
재귀함수를 이용하는 탑다운 방식보다는 바텀업 방식으로 구현하는 것을 권장한다. 시스템 상 재귀함수의 스택 크기가 한정되어 있을 수 있기 때문이다.
'프로그래밍 > 알고리즘' 카테고리의 다른 글
[알고리즘] 서로소 집합(union-find 연산) 정리 (0) | 2021.03.23 |
---|---|
[알고리즘] 최단경로 알고리즘 개념정리(다익스트라/플로이드 워셜) (0) | 2021.03.22 |
[알고리즘] 이진탐색 개념 정리 (0) | 2021.03.15 |
[알고리즘] 정렬 알고리즘 개념 총정리(선택 정렬/삽입 정렬/퀵 정렬/계수 정렬) (0) | 2021.03.15 |
[알고리즘] DFS/BFS 개념 정리 (0) | 2021.03.07 |