Files
2nd/10_Wiki/Topics/Computer_Science_and_Theory/Dynamic-Programming.md
T
2026-05-10 22:08:15 +09:00

6.2 KiB

id, title, category, status, canonical_id, aliases, duplicate_of, source_trust_level, confidence_score, verification_status, tags, raw_sources, last_reinforced, github_commit, tech_stack
id title category status canonical_id aliases duplicate_of source_trust_level confidence_score verification_status tags raw_sources last_reinforced github_commit tech_stack
wiki-2026-0508-dynamic-programming Dynamic Programming 10_Wiki/Topics verified self
DP
동적 계획법
동적 프로그래밍
none A 0.95 applied
algorithms
dp
optimization
memoization
tabulation
2026-05-10 pending
language framework
python none

Dynamic Programming

매 한 줄

"매 overlapping subproblem 의 cache 의 의 의 exponential → polynomial". 매 1953 Bellman 의 명명 ("dynamic" 의 RAND 의 selling reason). 매 modern algo 의 universal tool — 매 Bioinformatics, ML training, RL value iteration 의 base.

매 핵심

매 두 조건 (DP applicability)

  1. Optimal substructure: 매 optimal solution 의 sub-optimal solution 의 의 build.
  2. Overlapping subproblems: 매 same subproblem 의 repeatedly 의 solve.

매 두 style

  • Top-down (memoization): recursion + cache. 매 declarative, lazy.
  • Bottom-up (tabulation): iterative table fill. 매 stack-safe, faster constant.

매 state design

  • state: 매 minimal info 의 의 의 subproblem 의 identify.
  • transition: 매 state → state 의 recurrence.
  • base case: 매 smallest 의 known answer.
  • answer: 매 final state 의 retrieve.

매 응용

  1. Bioinformatics: sequence alignment (Needleman-Wunsch).
  2. NLP: edit distance, CKY parsing, Viterbi.
  3. RL: Bellman value iteration.
  4. Graph: Floyd-Warshall, Bellman-Ford.
  5. Compiler: register allocation, instruction scheduling.

💻 패턴

Fibonacci (intro example)

from functools import lru_cache

# Top-down
@lru_cache(maxsize=None)
def fib(n):
    if n < 2: return n
    return fib(n-1) + fib(n-2)

# Bottom-up O(1) space
def fib_bu(n):
    if n < 2: return n
    a, b = 0, 1
    for _ in range(n - 1):
        a, b = b, a + b
    return b

0/1 Knapsack

def knapsack(weights: list[int], values: list[int], capacity: int) -> int:
    n = len(weights)
    dp = [[0] * (capacity + 1) for _ in range(n + 1)]
    for i in range(1, n + 1):
        for w in range(capacity + 1):
            if weights[i-1] <= w:
                dp[i][w] = max(dp[i-1][w], dp[i-1][w - weights[i-1]] + values[i-1])
            else:
                dp[i][w] = dp[i-1][w]
    return dp[n][capacity]
# Time O(n·W), Space O(n·W) — can compress to O(W) with reverse iteration

Longest Common Subsequence (LCS)

def lcs(a: str, b: str) -> int:
    n, m = len(a), len(b)
    dp = [[0] * (m + 1) for _ in range(n + 1)]
    for i in range(1, n + 1):
        for j in range(1, m + 1):
            if a[i-1] == b[j-1]:
                dp[i][j] = dp[i-1][j-1] + 1
            else:
                dp[i][j] = max(dp[i-1][j], dp[i][j-1])
    return dp[n][m]

Edit Distance (Levenshtein)

def edit_distance(a: str, b: str) -> int:
    n, m = len(a), len(b)
    dp = [[0] * (m + 1) for _ in range(n + 1)]
    for i in range(n + 1): dp[i][0] = i
    for j in range(m + 1): dp[0][j] = j
    for i in range(1, n + 1):
        for j in range(1, m + 1):
            if a[i-1] == b[j-1]:
                dp[i][j] = dp[i-1][j-1]
            else:
                dp[i][j] = 1 + min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1])
    return dp[n][m]

Coin Change (min coins)

def coin_change(coins: list[int], amount: int) -> int:
    dp = [float('inf')] * (amount + 1)
    dp[0] = 0
    for x in range(1, amount + 1):
        for c in coins:
            if c <= x:
                dp[x] = min(dp[x], dp[x - c] + 1)
    return dp[amount] if dp[amount] != float('inf') else -1

Bitmask DP (TSP)

def tsp(dist: list[list[int]]) -> int:
    n = len(dist)
    INF = float('inf')
    dp = [[INF] * n for _ in range(1 << n)]
    dp[1][0] = 0  # start at 0 with mask {0}
    for mask in range(1, 1 << n):
        for u in range(n):
            if not (mask & (1 << u)) or dp[mask][u] == INF: continue
            for v in range(n):
                if mask & (1 << v): continue
                new_mask = mask | (1 << v)
                dp[new_mask][v] = min(dp[new_mask][v], dp[mask][u] + dist[u][v])
    return min(dp[(1 << n) - 1][u] + dist[u][0] for u in range(1, n))
# O(n²·2ⁿ) — feasible for n ≤ 20

매 결정 기준

상황 Approach
매 overlapping subproblem 의 detect DP
매 unique subproblem (no overlap) divide & conquer
매 greedy 의 optimal proof 가능 greedy (faster, less memory)
매 state 의 huge approximate DP / RL
매 small recursion depth top-down (cleaner)
매 deep recursion 의 우려 bottom-up
매 memory 의 tight rolling array (O(prev) 의 의)

기본값: 매 first attempt 의 top-down + memo. 매 deep / huge 의 의 의 bottom-up.

🔗 Graph

🤖 LLM 활용

언제: 매 optimization problem 의 overlapping subproblem 의 detect, 매 string/sequence problem, 매 counting problem (combinatorial), 매 RL value function 의 의. 언제 X: 매 unique subproblem (D&C 의 의), 매 greedy 의 proven optimal, 매 huge state space 의 approximation 의 의 (NN, MCTS).

안티패턴

  • state 의 over-include: 매 unnecessary dim 의 의 의 의 exponential blow-up.
  • base case X: 매 infinite recursion / wrong result.
  • mutable default args (Python): def f(x, memo={}) 의 cross-call leak.
  • bottom-up 의 wrong order: 매 state 의 dependency 의 의 의 의 의 fill 의 X.
  • @lru_cache with mutable args: list / dict 의 의 의 hash error — tuple 의 의.

🧪 검증 / 중복

  • Verified (Bellman 1957 "Dynamic Programming"; CLRS Ch 15; Kleinberg-Tardos Ch 6).
  • 신뢰도 A.

🕓 Changelog

날짜 변경
2026-05-08 Phase 1
2026-05-10 Manual cleanup — DP foundations with knapsack, LCS, edit distance, bitmask TSP