예꾸
개발자국
예꾸
전체 방문자
오늘
어제
  • 분류 전체보기 (111)
    • CS (6)
      • 데이터베이스 (5)
      • 운영체제 (0)
      • Computer Architecture (1)
    • 끄적끄적 (4)
    • 이론 (29)
      • 알고리즘 (18)
      • 자료구조 (4)
      • WEB (2)
      • JS (2)
      • Git (2)
      • Python (1)
    • 면접준비 (3)
      • Vue (1)
      • Design Pattern (1)
      • Frontend (1)
    • 개발기술 (20)
      • Git PUSH 자동화 (3)
      • VUE (1)
      • Linux (2)
      • MERN Stack (2)
      • React기반 Gatsby로 블로그 개발하기 (6)
      • Typescript (0)
      • 감정일기장(React) (3)
      • CI CD (3)
    • 코드트리 (6)
      • 블로그 챌린지 (3)
      • 모의시험 (3)
    • 취업준비 (3)
      • 코딩테스트 후기 (3)
    • 프로그래머스 (8)
      • SQL (7)
      • 알고리즘 (1)
    • 백준 (31)
      • 그리디(탐욕법) (6)
      • 구현 (5)
      • 그래프탐색(dfs, bfs) (5)
      • 완전탐색 (5)
      • 문자열 (5)
      • 누적합 (2)
      • DP(다이나믹 프로그래밍) (1)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • 알고리즘
  • 백준 그래프탐색
  • javascript
  • 나만의공부노트
  • 백준 구현
  • 프로그래머스 SQL
  • 코딩테스트실력진단
  • 운영체제
  • 프로그래머스
  • 코딩테스트
  • 백준 그리디
  • gatsby
  • 코드트리 추천
  • React
  • 컴퓨터 시스템의 구조
  • 자료구조
  • JS
  • 백준 완전탐색
  • 백준 문자열
  • 코드트리

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
예꾸

개발자국

[다이나믹프로그래밍] DP - JS, Python
이론/알고리즘

[다이나믹프로그래밍] DP - JS, Python

2021. 4. 26. 18:58

다이나믹 프로그래밍 

큰 문제를 작게 나누고 같은 문제라면 한 번씩만 풀어 문제를 효율적으로 해결하려는 알고리즘 기법

다이나믹 프로그래밍을 사용할 수 있는 경우

1. 최적 부분 구조(큰 문제를 작은 문제로 나눌 수 있다)

2. 중복되는 부분 문제(동일한 작은 문제를 반복적으로 해결 (최적화))

 

위의 두 조건이 모두 충족할 경우 다미나믹 프로그래밍을 사용할 수 있다 (점화식 사용)

 

다이나믹 프로그래밍 최적화의 핵심

  • 각 작은 문제는 한 번만 풀어야 한다
  • Optimal Substructure를 만족하기 때문에 같은 문제는 구현 때마다 정답이 같다
  • 정답을 한 번 구했으면 어딘가에 메모해 놓는다
  • 메모하는 것을 코드에서는 배열로 구현할 수 있다
  • 메모한다고 해서 memoization 용어를 쓴다

 

다이나믹 프로그래밍의 특징

1. 다이나믹 프로그래밍은 보텀업 방식과 탑타운 방식이 있다. (보텀업 방식이 일반적)

  • 보텀업 방식 : 반복문을 이용하여 소스코드를 작성해 작은 문제부터 차근차근 답을 도출(DP테이블 사용)
    •  
  • 탑다운 방식 : 큰 문제를 해결하기 위해 작은 문제를 호출(재귀 이용(메모이제이션 방법))
    • 메모이제이션 : 다이나믹 프로그래밍을 구현하는 방법 중 한 종류로, 한 번 구한 결과를 메모리 공간에 메모해두고 같은 식을 다시 호출하면 메모한 결과를 그대로 가져오는 기법 => 값을 저장하는 방법이므로 캐싱이라고도 한다.
    • 재귀함수를 이용하는 방법, 한번 푼 문제는 그 결과를 저장해 놓았다가 나중에 동일한 문제를 풀어야 할 때 이미 저장한 값을 반환한다.

2. 큰 문제를 작은 문제로 나누는 방법이라는 점에서 분할 정복과 같아보이지만 차이점이 있다. 

  • 분할정복 : 퀵 정렬에서 사용됐는데, 리스트를 분할하면 전체적으로 정렬될 수 있도록 하는 기법
    • 차이점은 다이나믹 프로그래밍은 문제들이 서로 영향을 끼친다는 것, 한 번 얘기했던 문제를 다시금 해결해야한다
    • 분할정복을 퀵정렬을 예로 들면 한 번 기준 원소가 자리를 변경해서 자리를 잡게 되면 그 기준 원소의 위치는 더 이상 바뀌지 않고 그 피벗값을 다시 처리하는 부분 문제는 존재하지 않음

3. 재귀함수만을 사용했을 때 시간복잡도는 O(2^n), DP를 사용하는 경우 시간 복잡도 O(N)

좌 : 피보나치 재귀 호출,  우 : DP방식(보텀업, 탑다운)  

  • 재귀 사용 시 동일한 함수가 반복적으로 호출됨. 이미 계산했지만 호출할 때마다 계속 계산됨
  • 탑 다운 방식에서는 한 번 돌았던 함수는 리스트에 저장. 그 함수를 돌 때마다 저장된 값을 불러옴 
    하지만 재귀를 사용하면 컴퓨터 시스템에서는 함수를 다시 호출했을 때 메모리 상에 적재되는 일련의 과정을 따라야 하므로 오버헤드 발생할 수 있음 => 재귀대신 반복문으로 사용하여 오버헤드 줄일 수 있음(보텀업)
  • 보텀업 방식을 사용하는 것을 추천. 보텀업 방식 사용시 반복문을 돌면서 작은 문제부터 차근차근 답을 도출하여 DP테이블에 저장한다. DP테이블에서 계산한 값을 불러와 큰 문제의 답을 도출해낸다

피보나치 수열 구현

기본 구현

피보나치 점화식 An = An-1 + An-2, A1=1, A2=2

Python

# 재귀 => O(2^n)으로 매우 비효율적
# f(6) f(5) f(4) f(3) f(2) f(1) f(2) f(3) f(2) f(1) f(4) f(3) f(2) f(1) f(2) 8
def fibo(x):
    print('f(' + str(x) + ')', end=' ')
    if x == 1 or x == 2:
        return 1
    return fibo(x - 1) + fibo(x - 2)


print(fibo(6))

JS

const fibo = function(x) {
    console.log(`f(${x}) `)
    if (x === 1 || x === 2) {
        return 1
    }
    return fibo(x - 1) + fibo(x - 2)
}

console.log(fibo(6))

 

탑다운(메모이제이션)

1. 메모할 리스트를 만든다(d)

2. 원하는 위치의 값(x)을 인덱스로 사용하여, d[x]이 0인지 아닌지 판별(원하는 위치x의 값을 구했는지 아닌지)

3. d[x]값이 0인 경우 x-1과 x-2의 값을 호출 

4. d[x]값이 0이 아닌 경우 결과값 return 

Python

# 피보나치 수열을 재귀함수로 구현(탑다운 다이나믹 프로그래밍)
# f(6) f(5) f(4) f(3) f(2) f(1) f(2) f(3) f(4) 8
# 시간복잡도 O(2^N) => O(N)
def fibo(x):
    print('f(' + str(x) + ')', end=' ')
    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(6))

JS

let d = Array(100).fill(0)

const fibo = function(x) {
    if (x === 1 || x === 2) {
        return 1
    }
    if (d[x] !== 0) {
        return d[x]
    }
    d[x] = fibo(x-1) + fibo(x-2)
    return d[x]

}   

const result = fibo(99)
console.log(result)

 

보텀업(DP테이블)

1. DP테이블을 생성한다(d)

2. 고정값 정의 (A1=1, A2=1)

3. 3에서 n+1만큼 돌면서 An = An-1 + An-2 반복

Python

# 앞서 계산된 결과를 저장하기 위한 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])

JS

let d = Array(100).fill(0)
// console.log(d)

d[1] = 1
d[2] = 1
const n = 99

for (let i = 3; i < n+1; i++) {
    d[i] = d[i-1] + d[i-2]
}

console.log(d[n]) // 218922995834555200000

 

    '이론/알고리즘' 카테고리의 다른 글
    • [JavaScript] 알고리즘에 쓰이는 문법(형변환)
    • [JavaScript] 알고리즘에 쓰이는 문법 (자르기)
    • [이진탐색] binary Search - JS, Python
    • [정렬] Counting Sort - JS, Python
    예꾸
    예꾸
    비전공자 옒의 개발이야기💻

    티스토리툴바