다이나믹 프로그래밍 이해하기

동적 프로그래밍 알고리즘을 구성하고 코딩하는 방법

코딩 인터뷰 준비에 대해 들어 보셨을 것입니다. 알고리즘 과정에서 어려움을 겪었을 수도 있습니다. 직접 코딩하는 방법을 배우려고 할 때 동적 프로그래밍을 이해하는 것이 중요하다는 어딘가에 들었습니다. DP (동적 프로그래밍)를 사용하여 알고리즘을 작성하는 것은 두려워하는 것처럼 필수적입니다.

그리고 그것을 축소 한 사람들을 누가 비난 할 수 있습니까? 다이나믹 한 프로그래밍은 잘못 이해되기 때문에 위협적인 것 같습니다. 많은 튜토리얼은 프로세스 대신 알고리즘 설명-알고리즘 찾기 결과에 중점을 둡니다. 이것은 이해가 아닌 암기를 권장합니다.

올해 알고리즘 수업 중에는 동적 프로그래밍이 필요한 문제를 해결하기 위해 자체 프로세스를 구성했습니다. 그것의 일부는 내 알고리즘 교수 (많은 신용이 ​​필요합니다!)와 내 자신의 동적 프로그래밍 알고리즘 해부에서 나온 것입니다.

그러나 프로세스를 공유하기 전에 기본 사항부터 시작하겠습니다. 어쨌든 동적 프로그래밍이란 무엇입니까?

동적 프로그래밍 정의

동적 프로그래밍은 최적화 문제를 더 간단한 하위 문제로 세분화하고 각 하위 문제에 솔루션을 저장하여 각 하위 문제가 한 번만 해결되도록합니다.

솔직히 말해서,이 정의는 하위 문제의 예를 볼 때까지 완전히 이해되지 않을 수 있습니다. 괜찮습니다. 다음 섹션에서 올 것입니다.

제가 전하고자하는 것은 DP는 가능한 모든 하위 문제를 검토하고 어떤 하위 문제로도 솔루션을 다시 계산하지 않기 때문에 특정 제약 조건에서 최대 또는 최소 솔루션을 찾는 문제인 최적화 문제에 유용한 기술이라는 것입니다. 이는 정확성과 효율성을 보장하며 알고리즘을 해결하거나 근사화하는 데 사용되는 대부분의 기술에 대해서는 말할 수 없습니다. 이것만으로 DP를 특별하게 만듭니다.

다음 두 섹션에서는 하위 문제가 무엇인지 설명하고 메모리라고하는 기술인 솔루션 저장이 동적 프로그래밍에 중요한 이유에 대해 설명합니다.

하위 문제에 대한 하위 문제 하위 문제에 대한 하위 문제

하위 문제는 원래 문제의 작은 버전입니다. 실제로, 하위 문제는 종종 원래 문제의 단어가 수정 된 것처럼 보입니다. 올바르게 공식화되면 하위 문제는 원래 문제에 대한 해결책을 얻기 위해 서로 구축됩니다.

이것이 어떻게 작동하는지 더 잘 이해하기 위해 동적 프로그래밍 문제의 예에서 하위 문제를 찾아 봅시다.

당신이 1950 년대에 IBM-650 컴퓨터에서 일하고 있다고 가정하십시오. 이것이 무엇을 의미하는지 알고 있습니다 – 펀치 카드! 귀하의 직업은 하루 동안 남성 또는 여성 IBM-650에게 있습니다. 실행할 수있는 자연수 n 펀치 카드가 제공됩니다. 각각의 펀치 카드 i는 미리 결정된 시작 시간 s_i에서 실행되고 미리 결정된 종료 시간 f_i에서 실행을 중지해야합니다. 한 번에 하나의 펀치 카드 만 IBM-650에서 실행할 수 있습니다. 각 펀치 카드는 회사에 얼마나 중요한지에 따라 관련 값 v_i를 갖습니다.

문제점 : IBM-650을 담당하는 담당자는 모든 펀치 카드 실행의 총 가치를 최대화하는 최적의 펀치 카드 스케줄을 결정해야합니다.

이 기사 전체에서이 예제를 자세히 살펴볼 것이기 때문에 지금은 하위 문제 만 다루겠습니다.

하위 문제 : 펀치 카드가 시작 시간별로 정렬되도록 펀치 카드 i-n의 최대 값 일정.

하위 문제가 어떻게 원래 문제를 솔루션을 구성하는 구성 요소로 나눕니다. 하위 문제를 사용하면 펀치 카드 n-1부터 n까지, 그리고 펀치 카드 n-2부터 n까지의 최대 값 일정을 찾을 수 있습니다. 모든 단일 하위 문제에 대한 솔루션을 찾아서 원래 문제 자체를 해결할 수 있습니다. 펀치 카드 1에서 n까지의 최대 값 일정. 하위 문제는 원래 문제처럼 보이므로 하위 문제를 사용하여 원래 문제를 해결할 수 있습니다.

동적 프로그래밍에서 각 하위 문제를 해결 한 후에는 메모하거나 저장해야합니다. 다음 섹션에서 이유를 알아 보겠습니다.

피보나치 수로 동기 부여

주어진 숫자에 대한 피보나치 값을 계산하는 알고리즘을 구현하라는 지시를 받으면 어떻게해야합니까? 내가 아는 대부분의 사람들은 파이썬에서 다음과 같은 재귀 알고리즘을 선택합니다.

데프 피보나치 발 (n) :
  n == 0 인 경우 :
    0을 반환
  elif n == 1 :
    1을 반환
  그밖에:
    피보나치 발 (n-1) + 피보나치 발 (n-2)

이 알고리즘은 그 목적을 달성하지만 엄청난 비용이 듭니다. 예를 들어, n = 5 (F (5)로 약칭)를 해결하기 위해이 알고리즘이 계산해야하는 내용을 살펴 보겠습니다.

에프 (5)
                    / \
                   / \
                  / \
               에프 (4) 에프 (3)
            / \ / \
          에프 (3) 에프 (2) 에프 (2) 에프 (1)
         / \ / \ / \
       F (2) F (1) F (1) F (0) F (1) F (0)
       / \
     에프 (1) 에프 (0)

위의 트리는 n = 5의 피보나치 값을 찾기 위해 수행해야하는 각 계산을 나타냅니다. n = 2의 하위 문제가 3 번 어떻게 해결되는지 확인하십시오. 상대적으로 작은 예 (n = 5)의 경우 계산이 많이 반복되고 낭비됩니다!

n = 2에 대한 피보나치 값을 세 번 계산하는 대신, 한 번 계산하고 그 값을 저장하고 이후의 모든 n = 2 발생에 대해 저장된 피보나치 값에 액세스하는 알고리즘을 만들면 어떨까요? 바로 메모가하는 일입니다.

이를 염두에두고 피보나치 가치 문제에 대한 동적 프로그래밍 솔루션을 작성했습니다.

데프 피보나치 발 (n) :
  메모 = [0] * (n + 1)
  메모 [0], 메모 [1] = 0, 1
  범위 (2, n + 1)의 i의 경우 :
    메모 [i] = 메모 [i-1] + 메모 [i-2]
  메모 반환 [n]

for 루프에 의해 반복적으로 채워지는 memoization array memo []에서 리턴 값의 솔루션이 어떻게 나오는지 주목하십시오. "반복적으로"는 메모 [2]가 메모 [3], 메모 [4],… 및 메모 [n]보다 먼저 계산되어 저장됨을 의미합니다. 메모 []가이 순서로 채워지기 때문에 각 하위 문제 (n = 3)에 대한 솔루션은 이전 하위 문제 (n = 2 및 n = 1)에 대한 솔루션으로 해결할 수 있습니다. 이전에 메모 [].

메모 화는 다시 계산할 필요가 없으므로보다 효율적인 알고리즘을 만듭니다. 따라서 메모는 동적 프로그래밍의 효율성을 보장하지만 동적 프로그램이 최상의 가능성을 찾기 위해 모든 가능성을 통과하도록 보장하는 올바른 하위 문제를 선택합니다.

이제 메모 및 하위 문제를 해결 했으므로 이제 동적 프로그래밍 프로세스를 배울 차례입니다. 버클

내 동적 프로그래밍 프로세스

1 단계 : 하위 문제를 단어로 식별합니다.

너무 자주 프로그래머는 문제에 대해 비판적으로 생각하기 전에 코드 작성을 시작할 것입니다. 안좋다. 키보드를 만지기 전에 두뇌를 발사하는 한 가지 전략은 영어 또는 다른 단어를 사용하여 원래 문제에서 식별 한 하위 문제를 설명하는 것입니다.

동적 프로그래밍이 필요한 문제를 해결하려면 종이를 들고이 문제를 해결하는 데 필요한 정보에 대해 생각해보십시오. 이를 염두에두고 하위 문제를 작성하십시오.

예를 들어, 펀치 카드 문제에서 하위 문제를 "펀치 카드 i부터 n까지의 최대 값 일정으로 작성하여 펀치 카드를 시작 시간별로 정렬 할 수 있습니다."라고 알았습니다. 펀치 카드가 시작 시간별로 정렬되도록 펀치 카드 1에서 n까지의 최대 값 일정을 결정하려면 다음 하위 문제에 대한 답을 찾아야합니다.

  • 펀치 카드가 시작 시간별로 정렬되도록 펀치 카드 n-1에서 n까지의 최대 값 스케줄
  • 펀치 카드가 시작 시간별로 정렬되도록 펀치 카드 n-2에서 n까지의 최대 값 스케줄
  • 펀치 카드가 시작 시간별로 정렬되도록 펀치 카드 n-3에서 n까지의 최대 값 스케줄
  • (등등)
  • 펀치 카드가 시작 시간별로 정렬되도록 펀치 카드 2-n의 최대 값 스케줄

문제를 해결하기 위해 이전의 하위 문제를 바탕으로하는 하위 문제를 식별 할 수 있다면 올바른 길을 가고있는 것입니다.

2 단계 : 하위 문제를 반복적 인 수학적 결정으로 작성합니다.

하위 문제를 단어로 식별했으면 수학적으로 작성해야합니다. 왜? 글쎄, 당신이 찾은 수학적 재귀 또는 반복 결정은 결국 코드에 넣는 것입니다. 또한 하위 문제를 작성하면 단계 1의 단어로 하위 문제를 수학적으로 검사합니다. 수학에서 1 단계의 하위 문제를 인코딩하기 어려운 경우 잘못된 하위 문제 일 수 있습니다!

재발을 찾을 때마다 나 자신에게 묻는 두 가지 질문이 있습니다.

  • 모든 단계에서 어떤 결정을 내립니까?
  • 알고리즘이 i 단계에있는 경우 i + 1 단계에서 수행 할 작업을 결정하려면 어떤 정보가 필요합니까? (그리고 때로는 : 알고리즘이 i 단계에있는 경우 i-1 단계에서 수행 할 작업을 결정하려면 어떤 정보가 필요합니까?)

펀치 카드 문제로 돌아가서 이러한 질문을하겠습니다.

모든 단계에서 어떤 결정을 내립니까? 펀치 카드가 앞에서 언급 한대로 시작 시간별로 정렬되어 있다고 가정합니다. 지금까지 일정과 호환되는 각 펀치 카드 (시작 시간은 현재 실행중인 펀치 카드의 종료 시간 이후 임)에 대해 알고리즘은 두 가지 옵션 (펀치 카드 실행 또는 실행 안 함) 중에서 선택해야합니다.

이 역동적 인 프로그램은 친한 친구 햄릿처럼 각 단계마다 두 가지 옵션 중에서 선택합니다!

알고리즘이 i 단계에있는 경우 i + 1 단계에서 수행 할 작업을 결정하려면 어떤 정보가 필요합니까? 두 옵션 중 하나를 결정하려면 알고리즘이 다음 호환 펀치 카드를 순서대로 알아야합니다. 주어진 펀치 카드 p에 대한 다음 호환 펀치 카드는 f_p (펀치 카드 p의 사전 결정된 종료 시간) 이후에 s_q (펀치 카드 q의 사전 결정된 시작 시간)가 발생하고 s_q와 f_p 사이의 차이가 최소화되도록 펀치 카드 q입니다. 수학자의 말을 버리고, 다음으로 호환되는 펀치 카드는 현재 펀치 카드 실행이 완료된 후 가장 빠른 시작 시간을 가진 펀치 카드입니다.

알고리즘이 i 단계에있는 경우 i-1 단계에서 수행 할 작업을 결정하려면 어떤 정보가 필요합니까? 알고리즘은 향후 결정에 대해 알아야합니다. 펀치 카드 i-1을 실행하거나 실행하지 않기로 결정하기 위해 펀치 카드 i부터 n까지의 결정.

이 질문에 대한 답을 얻었으므로 아마도 당신은 당신의 마음에 되풀이되는 수학적 결정을하기 시작했을 것입니다. 그렇지 않은 경우에도 괜찮습니다.보다 역동적 인 프로그래밍 문제에 노출되면 되풀이하기가 더 쉬워집니다.

더 이상 고민하지 않고 다음과 같이 되풀이합니다.

OPT (i) = 최대 (v_i + OPT (next [i]), OPT (i + 1))

이 수학적 되풀이에는 특히 이전에 글을 쓰지 않은 사람들에게 설명이 필요합니다. 펀치 카드가 시작 시간별로 정렬되도록 OPT (i)를 사용하여 펀치 카드 i에서 n까지의 최대 값 일정을 나타냅니다. 익숙한 것 같습니까? OPT (•)는 1 단계의 하위 문제입니다.

OPT (i)의 가치를 결정하기 위해 두 가지 옵션을 고려하고 목표를 달성하기 위해 이러한 모든 옵션을 최대한 활용하려고합니다. 모든 펀치 카드의 최대 가치 일정. i 단계에서 최대 결과를 제공하는 옵션을 선택하면 해당 값을 OPT (i)로 메모합니다.

펀치 카드 i를 실행하거나 실행하지 않는 두 가지 옵션은 다음과 같이 수학적으로 표시됩니다.

v_i + OPT (다음 [i])

이 절은 펀치 카드 i 실행 결정을 나타냅니다. 펀치 카드 i를 실행하여 얻은 값을 OPT (next [i])에 추가합니다. 여기서 next [i]는 펀치 카드 i 다음에 오는 다음 호환 펀치 카드를 나타냅니다. OPT (next [i])는 펀치 카드가 시작 시간별로 정렬되도록 next [i]부터 n까지의 펀치 카드에 대한 최대 값 스케줄을 제공합니다. 이 두 값을 합산하면 펀치 카드 i ~ n에 대한 최대 값 스케줄이 생성되므로 펀치 카드 i가 실행되는 경우 펀치 카드가 시작 시간별로 정렬됩니다.

OPT (i + 1)

반대로이 절은 펀치 카드 i를 실행하지 않기로 한 결정을 나타냅니다. 펀치 카드 i가 실행되지 않으면 해당 값을 얻지 못합니다. OPT (i + 1)은 펀치 카드가 시작 시간별로 정렬되도록 펀치 카드 i + 1에서 n까지의 최대 값 스케줄을 제공합니다. 따라서 OPT (i + 1)은 펀치 카드 i가 실행되지 않은 경우 펀치 카드가 시작 시간별로 정렬되도록 펀치 카드 i에서 n까지의 최대 값 스케줄을 제공합니다.

이러한 방식으로, 펀치 카드 문제의 각 단계에서 이루어진 결정은 단계 1의 하위 문제를 반영하기 위해 수학적으로 인코딩됩니다.

3 단계 : 1 단계와 2 단계를 사용하여 원래 문제를 해결하십시오.

1 단계에서 펀치 카드 문제의 하위 문제를 단어로 적었습니다. 2 단계에서 이러한 하위 문제에 해당하는 반복적 인 수학적 결정을 작성했습니다. 이 정보로 원래 문제를 어떻게 해결할 수 있습니까?

OPT (1)

그렇게 간단합니다. 1 단계에서 찾은 하위 문제는 펀치 카드 i부터 n까지의 최대 값 일정이므로 펀치 카드는 시작 시간별로 정렬되므로 원래 문제에 대한 솔루션을 펀치 카드 1부터 n까지의 최대 값 일정으로 쓸 수 있습니다 펀치 카드는 시작 시간별로 정렬됩니다. 1 단계와 2 단계가 함께 진행되므로 원래 문제점을 OPT (1)로 작성할 수도 있습니다.

4 단계 : 메모 배열의 크기와 채울 방향을 결정합니다.

3 단계가 믿을 수 없을 정도로 간단 했습니까? 그런 식으로 보인다. OPT (2), OPT (next [1]) 등에 의존하는 경우 어떻게 OPT (1)이 동적 프로그램의 솔루션이 될 수 있습니까?

OPT (1)은 OPT (2)의 솔루션에 의존합니다. 이는 2 단계에서 직접 수행됩니다.

OPT (1) = 최대 (v_1 + OPT (다음 [1]), OPT (2))

그러나 이것은 심각한 문제가 아닙니다. 피보나치 메모 예제를 다시 생각해보십시오. n = 5에 대한 피보나치 값을 찾으려면 알고리즘은 n = 4, n = 3, n = 2, n = 1 및 n = 0에 대한 피보나치 값이 이미 메모되어 있다는 사실에 의존합니다. 메모 테이블을 올바른 순서로 채우면 다른 하위 문제에 대한 OPT (1)의 의존도는 중요하지 않습니다.

메모 테이블을 채우는 올바른 방향을 어떻게 식별 할 수 있습니까? 펀치 카드 문제에서 우리는 OPT (1)이 OPT (2) 및 OPT (next [1])에 대한 솔루션에 의존하고 펀치 카드 2와 다음 [1]이 정렬로 인해 펀치 카드 1 이후에 시작 시간을 갖는다는 것을 알고 있기 때문에 메모 테이블을 OPT (n)에서 OPT (1)까지 채워야한다고 추론 할 수 있습니다.

이 메모리 배열의 크기를 어떻게 결정합니까? 요령은 다음과 같습니다. 배열의 크기는 OPT (•)가 사용하는 변수의 수와 크기와 같습니다. 펀치 카드 문제에는 OPT (i)가 있습니다. 즉, OPT (•)는 펀치 카드 번호를 나타내는 변수 i에만 의존합니다. 이것은 우리의 memoization array가 1 차원이고 n 개의 펀치 카드가 있기 때문에 그 크기는 n이 될 것을 제안합니다.

우리가 n = 5라는 것을 안다면, 우리의 메모리 배열은 다음과 같이 보일 것입니다 :

메모 = [OPT (1), OPT (2), OPT (3), OPT (4), OPT (5)]

그러나 많은 프로그래밍 언어가 0에서 배열 인덱싱을 시작하므로 인덱스가 펀치 카드 번호와 정렬되도록이 메모리 배열을 작성하는 것이 더 편리 할 수 ​​있습니다.

메모 = [0, OPT (1), OPT (2), OPT (3), OPT (4), OPT (5)]

5 단계 : 코드 작성

동적 프로그램을 코딩하기 위해 2-4 단계를 구성했습니다. 동적 프로그램을 작성하는 데 필요한 유일한 새로운 정보는 기본 사례이며 알고리즘을 검토 할 때 찾을 수 있습니다.

펀치 카드 문제에 대한 동적 프로그램은 다음과 같습니다.

데프 punchcardSchedule (n, 값, 다음) :
 # 메모 배열 초기화-4 단계
  메모 = [0] * (n + 1)
  
 # 기본 케이스 설정
  메모 [n] = 값 [n]
  
 # n에서 1로 메모 테이블 작성-2 단계
  범위 (n-1, 0, -1)의 i의 경우 :
    메모 [i] = 최대 (v_i + 메모 [다음 [i]], 메모 [i + 1])
 
 # 원래 문제 OPT (1)으로 솔루션 반환-3 단계
  메모를 돌려주십시오 [1]

첫 번째 동적 프로그램 작성을 축하합니다! 발이 젖었으므로 다른 유형의 다이나믹 프로그램을 안내해 드리겠습니다.

역설의 선택 : 다중 옵션 DP 예

DP와는 관련이 없지만 다중 옵션 결정을 내리는 방법에 대한 정확한 묘사.

이전의 동적 프로그래밍 예제에는 펀치 카드를 실행하거나 실행하지 않기위한 두 가지 옵션 결정이 있었지만 일부 문제는 각 단계에서 결정을 내리기 전에 여러 옵션을 고려해야합니다.

새로운 예를위한 시간입니다.

우정 팔찌를 n 명의 고객에게 판매하는 것으로 가정하고 해당 제품의 가치는 단조롭게 증가합니다. 이는 고객 j가 고객 i 다음에 오면 p_i ≤ p_j가되도록 제품 가격이 {p_1,…, p_n}임을 의미합니다. 이 n 개의 고객은 {v_1,…, v_n} 값을 갖습니다. 주어진 고객은 p_i ≤ v_i 인 경우에만 p_i 가격으로 우정 팔찌를 구매합니다. 그렇지 않으면 해당 고객으로부터 얻은 수익은 0입니다. 가격이 자연수라고 가정하십시오.

문제 : 우정 팔찌를 팔아서 최대한의 수입을 올릴 수있는 가격을 찾아야합니다.

1 단계와 2 단계에 대한 솔루션을보기 전에이 문제를 해결하는 방법에 대해 잠시 생각해보십시오.

1 단계 : 하위 문제를 단어로 식별합니다.

하위 문제 : 고객 i-1의 가격이 q로 설정되도록 고객 i에서 n까지의 최대 수익입니다.

1에서 n까지의 고객에 대한 최대 수익을 결정하려면 다음 하위 문제에 대한 답을 찾아야한다는 것을 깨달음으로써이 하위 문제를 발견했습니다.

  • 고객 n-2의 가격이 q로 설정되도록 고객 n-1부터 n까지의 최대 수익입니다.
  • 고객 n-3의 가격이 q로 설정되도록 고객 n-2에서 n까지의 최대 수익입니다.
  • (등등)

하위 문제에 두 번째 변수 q를 도입했습니다. 각 하위 문제를 해결하려면 하위 문제보다 먼저 고객을 위해 설정 한 가격을 알아야하기 때문에이 작업을 수행했습니다. 변수 q는 가격 세트의 단조로운 특성을 보장하고 변수 i는 현재 고객을 추적합니다.

2 단계 : 하위 문제를 반복적 인 수학적 결정으로 작성합니다.

재발을 찾을 때마다 나 자신에게 묻는 두 가지 질문이 있습니다.

  • 모든 단계에서 어떤 결정을 내립니까?
  • 알고리즘이 i 단계에있는 경우 i + 1 단계에서 수행 할 작업을 결정하려면 어떤 정보가 필요합니까? (그리고 때로는 : 알고리즘이 i 단계에있는 경우 i-1 단계에서 수행 할 작업을 결정하려면 어떤 정보가 필요합니까?)

우정 팔찌 문제로 돌아가서 이러한 질문을하겠습니다.

모든 단계에서 어떤 결정을 내립니까? 우정 팔찌를 현재 고객에게 판매 할 가격을 결정합니다. 가격은 자연수 여야하므로 고객 i에 대한 가격을 q (고객 i-1에 대해 설정된 가격)에서 고객이 우정 팔찌를 구입할 최대 가격 인 v_i까지의 범위에서 설정해야한다는 것을 알고 있습니다.

알고리즘이 i 단계에있는 경우 i + 1 단계에서 수행 할 작업을 결정하려면 어떤 정보가 필요합니까? 내 알고리즘은 고객 i + 1의 가격을 설정할 자연 수를 결정하기 위해 고객 i에 설정된 가격과 고객 i + 1의 가치를 알아야합니다.

이 지식을 통해 수학적으로 재발을 작성할 수 있습니다.

OPT (i, q) = max ~ ([수익 (v_i, a) + OPT (i + 1, a)])
max ~가 q ≤ a ≤ v_i 범위의 모든 a에서 최대 값을 찾도록

다시 한번,이 수학적 반복은 설명이 필요합니다. 고객 i-1의 가격이 q이므로 고객 i의 경우 가격 a는 정수 q에 머 무르거나 q + 1과 v_i 사이의 정수로 변경됩니다. 총 수익을 찾기 위해 고객 i의 수익을 고객 i의 가격이 a로 설정되도록 고객 i의 수익을 고객 i + 1에서 n까지의 최대 수익에 더합니다.

즉, 총 수익을 최대화하려면 알고리즘이 q와 v_i 사이의 가능한 모든 가격을 확인하여 고객 i에 대한 최적의 가격을 찾아야합니다. v_i ≤ q 인 경우 가격 a는 q로 유지되어야합니다.

다른 단계는 어떻습니까?

1 단계와 2 단계를 통한 작업은 동적 프로그래밍에서 가장 어려운 부분입니다. 연습으로, 3 단계, 4 단계 및 5 단계를 통해 본인의 이해를 확인하는 것이 좋습니다.

동적 프로그램의 런타임 분석

이제 알고리즘 작성의 재미있는 부분 : 런타임 분석. 이 토론에서 큰 O 표기법을 사용할 것입니다. 아직 big-O에 익숙하지 않다면 여기를 읽어 보시기 바랍니다.

일반적으로 동적 프로그램의 런타임은 다음 기능으로 구성됩니다.

  • 전처리
  • for 루프 실행 횟수
  • 루프 반복을 위해 재귀를 실행하는 데 걸리는 시간
  • 후 처리

전반적으로 런타임은 다음과 같은 형식을 취합니다.

전처리 + 루프 * 반복 + 후 처리

펀치 프로그램 문제의 런타임 분석을 수행하여 동적 프로그램의 big-O에 익숙해 지도록하겠습니다. 다음은 펀치 카드 문제 동적 프로그램입니다.

데프 punchcardSchedule (n, 값, 다음) :
 # 메모 배열 초기화-4 단계
  메모 = [0] * (n + 1)
  
 # 기본 케이스 설정
  메모 [n] = 값 [n]
  
 # n에서 1로 메모 테이블 작성-2 단계
  범위 (n-1, 0, -1)의 i의 경우 :
    메모 [i] = 최대 (v_i + 메모 [다음 [i]], 메모 [i + 1])
 
 # 원래 문제 OPT (1)으로 솔루션 반환-3 단계
  메모를 돌려주십시오 [1]

런타임을 분석해 봅시다 :

  • 전처리 : 여기서는 메모리 배열을 빌드하는 것을 의미합니다. 에).
  • for 루프가 몇 번이나 실행되는지 : O (n).
  • 루프 반복을 위해 하나의 반복에서 반복을 실행하는 데 걸리는 시간 : 반복은 각 반복에서 두 옵션 사이에서 결정을 내리기 때문에 실행하는 데 일정한 시간이 걸립니다. O (1).
  • 후 처리 : 없음! O (1).

펀치 카드 문제 동적 프로그램의 전체 런타임은 O (n) O (n) * O (1) + O (1) 또는 단순화 된 형태로 O (n)입니다.

훌륭해!

다 끝났습니다. 동적 프로그래밍 마법사가 되려면 한 걸음 더 다가 가십시오!

마가렛 해밀턴 : 우리 역사상 많은 프로그래밍 마법사 중 하나!

마지막 지혜 : 동적 프로그래밍을 계속 연습하십시오. 이러한 알고리즘이 아무리 실망스러워 보일지라도 동적 프로그램을 반복해서 작성하면 하위 문제와 반복이 더 자연스럽게 나옵니다. 다음은 시도해 볼 수있는 고전적인 동적 프로그래밍 문제의 크라우드 소싱 목록입니다.

새로운 역동적 인 프로그래밍 지식을 가지고 인터뷰, 수업 및 삶을 즐기십시오!

이 게시물을 교정 해 주신 Steven Bennett, Claire Durand 및 Prithaj Nath에게 감사드립니다. Hartline 교수에게 감사의 말을 전합니다.

읽은 것을 즐기십니까? 이 작품을 좋아하고 공유하여 사랑을 전하십시오. 생각이나 질문이 있습니까? 트위터 나 아래의 의견을 통해 저에게 연락하십시오.