본문 바로가기
  • 초부득3 - 어제보다 나은 내일을 위해
  • 꿈이 현실이 되는 날까지
sw사관학교 정글 2기/알고리즘

다이나믹 프로그래밍

by 금의야행 2021. 8. 26.

동빈나 강의 :https://www.youtube.com/watch?v=5Lu34WIx2Us

https://blog.naver.com/ndb796/221233570962

  • 다이나믹 프로그래밍은 메모리를 적절히 사용하여 수행 시간 효율성을 비약적으로 향상시키는 방법
  • 이미 계산된 결과(작은 문제)는 별도의 메모리 영역에 저장하여 다시 계산하지 않음
  • 다이나믹 프로그래밍의 구현은 일반적으로 두 가지 방식(탑다운, 바텀업)으로 구성

 

일반적인 프로그래밍 분야에서의 동적(Dynamic)의 의미
  • 프로그램이 실행되는 도중

자료구조에서 동적 할당(Dynamic Allocation)은 '프로그램이 실행되는 도중에 실행에 필요한 메모리를 할당하는 기법'

다이나믹 프로그래밍에서 다이나믹은 특별한 의미는 없음.

 

 

다이나믹 프로그맹의 조건

1. 최적 부분 구조 (Optimal Substructure)

  • 큰 문제를 작은 무제로 나눌 수 있으며, 작은 문제의 답을 모아서 큰 문제를 해결 가능

2. 중복되는 부분 문제 (Overlapping Subproblem)

  • 동일한 작은 문제를 반복적으로 해결해야 함

 

 

대표적인 동적 프로그래밍 문제

피보나치 수열

피보나치 수열은 다음과 같은 형태의 수열

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

특정 번째의 수열은 앞 번째에 두 수의 합. 2 = 1+1 / 3 = 1+2

 

 

점화식

인접한 항들 사이의 관계식

 

피보나치 수열의 점화식:

An = An-1 + An-2 , A1 = 1, A2 = 1 

실제 프로그램에서 재귀 함수와 점화식을 사용하면 해결 가능.

 

 

피보나치 수열 : 단순 재귀 소스코드
def fibo(x):
	if x==1 or x==2:
    	return 1
	return fibo(x-1) + fibo(x-2)

print(fibo(4))
>>>3

 

중복되는 부분 문제

 

다이나믹 프로그래밍의 사용조건을 만족하는지 확인

  • 최적 부분 구조 : 큰 문제를 작은 문제로 나눌 수 있다.
  • 중복되는 부분 문제: 동일한 작은 문제를 반복적으로 해결

따라서 피보나치 수열은 다이나믹 프로그래밍의 사용조건 만족

 

 

메모이제이션 ( Memoization )  | 탑다운 방식

한 번 계산한 결과를 메모리 공간에 메모하는 기법

  • 같은 문제를 다시 호출하면 메모했던 결과를 그대로 가져옴
  • 값을 기록해 놓는다는 점에서 캐싱(Caching)이라고도 함

 

탑다운 vs 바텀업

탑다운(메모이제이션) 방식은 하향식, (작은 문제들을 재귀적으로 해결해서 큰문제를 해결)

바텀업은 상향식 (아래 쪽에서부터 작은 문제를 하나씩 해결해나가며 먼저 해결한 문제들의 값을 활용해서 그 다음의 문제까지 차례대로 해결. 반복문)이라고도 함.

 

다이나믹 프로그래밍의 전형적인 형태는 보텀업 방식

  • 결과 저장용 리스트는 DP 테이블이라고 부름

엄밀히 말하면 메모이제이션은 이전에 계산된 결과를 일시적으로 기록해 놓는 넓은 개념을 의미

 

 

 

피보나치 수열 : 탑다운 다이나믹 프로그래밍 소스코드

1. 탑다운

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

# 피보나치 함수를 재귀함수로 구현


def fibo(x):
    # 종료조건 (1 혹은 2일때 1을 반환)
    if x == 1 or x == 2:
        return 1
    # 이미 계산한 적 있는 문제라면 그대로 반환
    if dp[x] != 0:
        return dp[x]
    # 아직 계산하지 않은 문제라면 점화식에 따라서 피보나치 결과 반환
    dp[x] = fibo(x-1) + fibo(x-2)
    return dp[x]


print(fibo(99))

 

2. 바텀업

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

# 피보나치 함수를 재귀함수로 구현

dp2[1] = 1
dp2[2] = 1
n = 99

# 피보나치 수열 반복문으로 구현 (바텀업)
for i in range(3, n+1):
    dp2[i] = dp2[i-1] + dp2[i-2]

print(dp2[n])

 

 

원래는 이와같이 단순재귀에서의 아주 많은 양의 연산을 줄일 수 있음

 

피보나치 수열 함수의 시간 복잡도는 O(N) 

 

 

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

DP와 분할 정복 모두 최적 부분 구조를 가질 때 사용 할 수 있다.

  • 큰 문제를 작은 문제로 나눌 수 있으며, 작은 문제의 답을 모아서 큰 문제를 해결할 수 있는 상황

DP와 분할 정복의 차이점은 부분 문제의 중복

  • DP 문제에서는 각 부분 문제들이 서로 영향을 미치며 부분 문제가 중복됌
  • 분할 정복에서는 동일한 부분 문제가 반복적으로 계산되지 않음

 

DP 문제에 접근하는 방법

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

 

1. 가장 먼저 그리디, 구현, 완전 탐색등의 아이디어로 문제를 해결 할 수 있는지 검토

  • 다른 알고리즘으로 풀이방법이 떠오르지 않거나, 너무 많은 시간복잡도가 소요될 것 같다면 DP를 고려

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

 

일반적인 코딩 테스트 수준에서는 기본 유형의 다이나믹 프로그래밍 문제가 출제되는 경우가 많다.

  • 왜냐하면 DP문제가 출제될 시 그 문제에 맞는 점화식을 떠올리는 과정에서 많은 시간이 소요될 수 있기 때문. 

'sw사관학교 정글 2기 > 알고리즘' 카테고리의 다른 글

알고리즘 공부 전략 + 유용한 링크  (0) 2021.10.19
그리디 알고리즘  (0) 2021.08.30
Topological sort, 위상 정렬  (0) 2021.08.25
그래프 탐색 알고리즘: DFS/BFS  (0) 2021.08.19
정렬  (0) 2021.08.11

댓글