ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [python] 🚨 다이나믹 프로그래밍
    파이썬/알고리즘 공부 2021. 10. 26. 21:38
    728x90

    동적계획법(dynamic Programming)

    • 하나의 큰 문제를 여러 개의 공통되는 작은 문제로 나누어서 작은 문제의 정답들을 결합하여 알고리즘을 푸는 과정(규칙을 찾아가는 과정)

    😂이번 동적계획법에서는 저번에 다뤘던 동적계획법으로부터 좀 더 나아가서 좀 더 깊게 들어가보자~ 라는 마음으로 시작했는데... 너무 어렵다.... 오늘도 역시 기본으로 끝내지는 거같다....

    ➰피보나치 수열: 단순 재귀

    def fibo(x):
        if x == 1 or x == 2:
            return 1
        return fibo(x-1) + fibo(x-2)
    
    print(fibo(s))
    >>> 3

    ➰탑다운(Top down) vs 보텀업(Bottom up)

    • ✅탑다운(메모이제이션) - 하향식이라고도 하며 재귀함수를 이용해 큰 문제를 해결하기 위해서 작은 문제들을 재귀적으로 호출하여 작은문제가 해결돼었을 떄 실제로 큰 문제 답까지 얻을 수 있고, 한 번 계산된 결과를 작성하는 메모이제이션 기법 사용
    • ✅코드
    # 한 번 계산된 결과를 메모이제이션하기 위한 리스트 초기화
    d = [0] * 100
    
    #피보나치 함수를 재귀함수로 구현(탑다운 다이나기 프로그래밍)
    def fibo(x):
    
        #종료조건(1혹은 2일 1을 반환)
        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))
    >>218922995834555169026
    • ✅보텀업- 상향식라고도 하며 아래쪽에서 작은 문제를 하나씩 해결, 먼저 계산했던 문제들을 활용해서 그 다음에 문제를 차례로 해결(반복문을 활용)
    • ✅코드
    d = [0] * 100
    
    #첫 번째 피보나치 수와 두 번째 피보나치 수는 1
    d[1] = 1
    d[2] = 2
    n = 99
    
    # 피보나치 함수 반복으로 구현(보텀업)
    for i in range(3, n+1):
        d[i] = d[i-1] + d[i-2]
    
    print(d[n])
    >>218922995834555169026

    ➰메모이제이션 동작 분석

    • 이미 계산된 결과를 메모리에 저장하면 다음과 같이 색칠괸 노드만 처리할 것을 기대할 수 있다.

    ✅메모이제이션 사용 안 할 경우

    ✅메모이제이션 사용 할 경우

    함수가 호출될때 마다 프린트를 사용해 사진과 같이 함수가 호출되는지를 살펴보면 얼마나 더 효율적인지 느낄 수 있을 걱 같다

    d = [0] * 100
    
    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]
    
    fibo(6)
    >>> f(6)f(5)f(4)f(3)f(2)f(1)f(2)f(3)f(4)

    함수 호출을 최소화하여 메모리 공간과 시간을 모두 절약할 수 있다. 이경우 수열 함수의 시간 복잡도는 O(N)이다.

    ➰다이나믹vs분할 정복

    지금 학습하고 있는 다이나믹 프로그래밍과 저번에 학습했던 퀵 정렬과 같은 분할 정복은 어떤 차이와 비슷한 점이 있는지 파악해보자

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

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

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

    • 다이나믹 프로그래밍 문제에서는 각 부분 문제들이 서로 영향을 미치며 부분 문제가 중복된다.
    • 분할 정복 문제에서는 동리한 부분 문제가 반복적으로 계산되지 않는다.

    ➰다이나믹 프로그래밍 문제에 접근하는 방법

    • 가장 먼저 그리디, 구현, 완전 탐색 등의 아이디어로 문제를 해결할 수 있는지 검토. (떠오르지 않는다면 다이나믹 프로그래밍을 고려)
    • 일단 재귀 함수로 비효율적인 완전 탐색 프로그램을 작성한뒤에(탑다운) 작은 문제에서 구한 답이 큰 문제에서 그대로 사용될 수 있으면 코드를 개선.

    ❓문제풀이: 개미전사

    1 3 1 5

    4개가 다음과 같이 존재한다고 가정

    • 두 번째 식량창고와 네번 식량창고를 선택했을대 최댓값인 총 8개의 식량을 빼앗을 수 있다.
    • 식량창고 N 개에 대한 정보가 주어졌을대 얻을 수 있는 식량의 최댓값을 구하는 프로그램을 작성해보자

    문제 조건

    ✅문제 접근법
    창고 0, 1, 2, 3으로 가정했을때 최적의 해를 계산해보자

    a0 = 1
    a1 = 3
    a2 = 3 max(창고1, 창고2 + 창고0)
    a3 = 8

    사진으로 파악해보면 이해가 더 쉬울 거 같다.

    500

    ✅코드

    n = int(input())
    array = list(map(int,input().split()))
    
    d = [0] * 100
    
    d[0] = array[0]
    d[1] = max(array[0], array[1])
    
    for i in range(2, n):
        d[i] = max(d[i-1],d[i-2] + array[i])
    print(d[n-1])

    출처: https://www.youtube.com/watch?v=5Lu34WIx2Us&list=PLRx0vPvlEmdAghTr5mXQxGpHjWqSz0dgC&index=6

    댓글

Designed by Tistory.