본문 바로가기
CS/Algorithm

[알고리즘 개념 정리] 다이나믹 프로그래밍 (Python)

by 랄라J 2024. 3. 27.

 

[이것이 코딩 테스트다 with Python] 28강 다이나믹 프로그래밍 개요를 듣고 정리한 내용입니다.

 

다이나믹 프로그래밍 (= 동적 계획법)

- 메모리를 적절히 사용하여 수행 시간 효율성을 비약적으로 향상시키는 방법이다.

- 이미 계산된 결과는 별도의 메모리 영역에 저장하여 다시 계산하지 않도록 한다.

    메모이제이션 : 한번 계산한 결과를 메모리 공간에 메모하는 기법이다. 즉, 같은 문제를 다시 호출하면 메모했던 결과를 그대로 가져온다. 값을 기록해 놓는다는 점에서 캐싱이라고도 한다.

- 다이나믹 프로그래밍의 구현은 일반적으로 2가지 방식(top-down과 bottom-up)으로 구성된다.

    top-down(메모이제이션) 방식 (=하향식): 재귀함수를 이용함

    bottom-up 방식 (=상향식): 반복문을 이용함, DP의 전형적인 형태

(+. 자료구조에서 동적 할당은 프로그램이 실행되는 도중에 실행에 필요한 메모리를 할당하는 기법을 의미한다.)

 

다이나믹 프로그래밍의 조건

다음의 조건을 만족할 때 사용할 수 있다.

1. 최적 부분 구조 (Optimal Substructure) : 큰 문제를 작은 문제로 나눌 수 있으며 작은 문제의 답을 모아서 큰 문제를 해결할 수 있다.

2. 중복되는 문제 (Overlapping Subproblem): 동일한 작은 문제를 반복적으로 해결해야 한다.

 

다이나믹 프로그래밍의 대표적인 예시인 피보나치 수열을 통해 더 알아보자

피보나치 수열

1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89 ...

 

점화식이란 인접한 항들 사이의 관계식을 의미한다.

피보나치 수열 점화식은 아래와 같다.

 

프로그래밍에서는 이러한 수열을 배열이나 리스트를 이용해 표현한다.

def fibo(x):
    if x == 1 or x == 2:
        return 1
    return fibo(x - 1) + fibo(x - 2)

print(fibo(4))

 

단순 재귀 함수로 피보나치 수열을 해결하면 지수 시간 복잡도(O(2ⁿ))를 가지게 된다.

다음과 같이 f(2)가 여러 번 호출되는 것을 볼 수 있다. 즉, 중복되는 부분 문제가 발생한다.

 

위 문제의 효율적인 해법으로 다이나믹 프로그래밍을 사용해 해결할 수 있다.

메모이제이션 기법을 활용하면 아래와 같이 이미 계산된 결과를 메모리에 저장해두었기 때문에 색칠된 노드만 처리할 것을 기대할 수 있다.

 

실제로 호출되는 함수를 확인해보면 아래 그림과 같다!

메모이제이션을 이용하는 경우 피보나치 수열 함수의 시간 복잡도는 O(N)이다.

 

Top-down 방식의 DP

# 한 번 계산된 결과를 메모이제이션 하기 위한 리스트 초기화
d = [0] * 100

# 피보나치 함수를 재귀함수로 구현(top-down DP)
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))

 

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(fibo(n))

 

다이나믹 프로그래밍과 분할정복

공통점 : 다이나믹 프로그래밍과 분할 정복은 모두 최적 부분 구조를 가질 때 사용할 수 있다.

차이점 : 다이나믹 프로그래밍과 분할 정복의 차이점은 부분 문제의 중복이다.

    다이나믹 프로그래밍 문제에서는 각 부분 문제들이 서로 영향을 미치며 부분 문제가 중복된다.

    분할 정복 문제에서는 동일한 부분 문제가 반복적으로 계산되지 않는다.

 


다이나믹 프로그래밍 문제 접근법

- 주어진 문제가 DP 유형임을 파악하는 것이 중요하다.

- 가장 먼저 그리디, 구현, 완전 탐색 등의 아이디어로 문제를 해결할 수 있는지 검토할 수 있다. 다른 알고리즘으로 풀이방법이 떠오르지 않으면 다이나믹 프로그래밍을 고려해보자.

- 일단 재귀 함수로 비효율적인 완전 탐색 프로그램을 작성 한 뒤에 작은 문제에서 구한 답이 큰 문제에서 그대로 사용될 수 있으면 코드를 개선하는 방법을 사용할 수 이다.

- 일반적인 코딩 테스트 수준에서는 기본 유형의 DP 문제가 출제되는 경우가 많다.

반응형

댓글