이것이 취업을 위한 코딩 테스트다
를 참고하여 작성하였습니다.
DP라고도 자주 불리는 동적 계획법은, 하나의 큰 문제를 여러 개의 작은 문제로 나누어서 그 결과를 저장하여 다시 큰 문제를 해결할 때 사용하는 문제 해결 방법이다.
피보나치 수열을 예시로 들어보자.
피보나치 수열은 이전 두 항의 합을 현재의 항으로 설정하는 수열이다.
현재의 항을 이라고 하면, 피보나치 수열의 점화식은
이라고 표현할 수 있다.
이를 파이썬 코드로 구현할 때, 재귀 함수를 사용하면 간단하게 표현할 수 있다.
def fibo(x):
if x==1 or x==2:
return 1
return fibo(x-1) + fibo(x-2)
하지만, 이 방법의 시간 복잡도는 로 n이 커질수록 수행 시간이 기하급수적으로 늘어난다.
( n=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)
1. 큰 문제를 작은 문제로 나눌 수 있다.
DP는 기본적으로 큰 문제를 작은 문제로 나눈 뒤, 그 결과 값을 재활용하여 전체 답을 구한다.
그러므로 동일한 작은 문제가 반복하여 나타나는 경우 사용 가능하다
2. 작은 문제에서 구한 정답은 그것을 포함하는 큰 문제에서도 동일하다
위 그림에서 A에서 B로 가는 최단 거리
를 구한다고 가정해보자.
중간에 X가 있으므로, A에서 X로 가는 최단 거리
와 X에서 B로 가는 최단 거리
의 합이 전체 최단 거리이다.
이처럼 작은 문제에서 구한 결과가 전체 문제에 동일하게 적용되어야 한다.
- DP로 풀 수 있는 문제인지 확인
- 문제의 변수 파악
- 점화식 만들기
- 메모하기
- 기저 상태 파악하기
- 구현하기
1. DP로 풀 수 있는 문제인지 확인
2. 문제의 변수 파악
피보나치 수
- 몇 번째 숫자인지문자열 간의 차이
- 문자열의 길이, 편집 거리배낭 문제
- index, 무게3. 점화식 만들기
4. 메모하기 (Memoization)
5. 기저 상태 파악하기
6. 구현하기
Bottom-Up
- 반복문 사용Top-Down
- 재귀 사용d = [0] * 100
# Bottom-Up
d[0] = 0
d[1] = 1
for i in range(2,100):
d[i]=d[i-1]+d[i-2]
# Top-Down
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)
분할 정복은, 문제를 나눌 수 없을 때까지 나누어서 각각을 풀면서 다시 합병하여 문제의 답을 얻는 알고리즘이다.
문제를 작게 쪼개서 푼다는 점에서 공통점이 있다.
그러나,
- 동적 계획법은 부분 문제가 중복되어, 큰 문제를 풀 때 재활용됨
- 분할 정복은 부분 문제가 서로 중복되지 않음
이라는 점에서 차이점이 있다.