다이나믹 프로그래밍
리얼 버라이어티 다이너믹 프로그램 무한도전 생각나고 마음에 드는 이름이다
프로그래밍을 어떻게 하길래 다이나믹 한지 알아보자
어디 뭐 백덤블링이라도 조지거나 다이나믹 듀오 부르기라도 해야 할지 모르겠다
다이나믹 프로그래밍
위에는 사전적 정의다
봐도 별 도움 안되니 새로 알아보자
다이나믹 듀오와는 관계 없다
문제를 푸는 패러다임이다
구체적인 방식이라기 보단 개념이라 볼 수 있다
기본적으로 작은 문제 풀고 해를 저장 후 큰 문제 풀 때 재사용한다
주목 할 점은 작은 문제를 풀고 그 값을 저장 한다는 거다
대표적으로 두 가지 방법이 있는데 상향식과 하향식이다
유형 | 설명 | 코드 구조 |
---|---|---|
상향식 (Bottom-up) | 작은 문제부터 차근차근 쌓아 올림 | for 반복문 |
하향식 (Top-down) | 큰 문제를 작은 문제로 쪼개며 해결 | 재귀 + 메모이제이션 |
이 두가지 방식은 예제와 함께 알아보자
2가지 조건을 만족해야 사용 가능하다
이 두 조건을 만족시 DP 적용가능하다
-피보나치로 알아보자
def fib(n):
if n <= 1:
return n
return fib(n-1) + fib(n-2)
간단해서 좋지만 재귀를 잔뜩 부르기에 효율은 개나 줘버렸다
지랄마라 컴퓨터가 고작 그걸로 힘들어하겠냐?
라고 생각하던 때가 제게도 있었습니다
재귀의 경우
고작 5번째 수 구하려면 이지랄 해야한다
fib(5)
├─ fib(4)
│ ├─ fib(3)
│ │ ├─ fib(2)
│ │ │ ├─ fib(1)
│ │ │ └─ fib(0)
│ │ └─ fib(1)
│ └─ fib(2)
│ ├─ fib(1)
│ └─ fib(0)
└─ fib(3)
├─ fib(2)
│ ├─ fib(1)
│ └─ fib(0)
└─ fib(1)
아직 안 와닿는다면 표로 보여주겠다
입력값 | 시간복잡도 | 결과 |
---|---|---|
fib(30) |
약 $2^{30}$ ≈ 10억 회 | 수 초 ~ 수 분 |
fib(40) |
약 $2^{40}$ ≈ 1조 회 | 수십 분~몇 시간 |
fib(100) |
약 $2^{100}$ ≈ 우주의 원자 개수보다 많음 | 사실상 불가능 |
fib(1000) |
??? | AI반란나면 1순위 척결대상됨 |
그러니 미래의 지배자에게 잘보이려면 단순 재귀 같은 걸로 컴퓨터 괴롭히지 말자
memo = {}
def fib(n):
if n in memo:
return memo[n]
if n <= 1:
memo[n] = n
else:
memo[n] = fib(n-1) + fib(n-2)
return memo[n]
큰 문제에서 쪼개가며 계산한 정보는 저장하고 필요할때 써먹는다…
훌륭하게 DP의 조건을 만족하고 있다 볼 수 있다
def fib(n):
dp = [0] * (n+1)
dp[1] = 1
for i in range(2, n+1):
dp[i] = dp[i-1] + dp[i-2]
return dp[n]
작은 문제부터 시작해 값을 저장해나가며 이를 활용하고 있다
이도 훌륭한 방식이다
이럴때 쓴다
상황 | 추천 방식 |
---|---|
재귀적으로 정의된 문제 | Top-Down |
메모리 효율 중요, 재귀 깊이 위험 | Bottom-Up |
반복문이 더 빠른 환경 | Bottom-Up |
구현이 직관적인 게 중요 | Top-Down |
Top-Down: “필요할 때 계산해서 저장”
Bottom-Up: “처음부터 전부 계산해서 쌓아올림