Dynamic Programming

Created

Recap:

  • Greed: process the input in some order, myopically making irrevocable decisions.
  • Divide-and-conquer: break up probelms into independent subproblems; solve them; and combine individual solutions to form solution to original problem
  • Dyanmic programming (caching intermediate results in a table for later reuse): break up a problem into a series of overlapping subproblems; combine solutions to smaller subproblems to form solutions to a large subproblem

Bellman pioneered the systematic study of DP in 1950s. Etymology

  • Dynamic programming = programming over time
  • Named this to avoid fear of mathematical research

Weighted Interval Scheduling

Recall the interval scheduling problem. We modify it so that jobs have weights.

  • A job jj starts at sjs_j, finishes at fjf_j, and has weight wj>0w_j>0.
  • Two jobs are compatible if they don't overlap.
  • Goal: find the max-weight subset of mutually compatible jobs.

The greedy algorithm is correct if all weights are 1, but fails spectaculary for the weighted version.

  • Recall: it considers jobs in ascending order of finish time, and adds the job to the subset if its compatible with the previously chosen jobs.

Dynamic programming: binary choice

Definition: OPT(j)OPT(j) = max weight of any subset of mutually compatible jobs for subproblem consisting only of jobs 1,2,…,j1,2, \dots, j. Goal: OPT(n)OPT(n) = max weight of any subset of mutually compatible jobs Case 1: OPT(j)OPT(j) does not select job jj.

  • There must be an optimal solution to problem consisting of remaining jobs 1,2,…,j−11,2,\dots,j-1. Case 2: OPT(j)OPT(j) selects job jj.
  • Collect profit wjw_j
  • Can't use incompatible jobs {p(j)+1,p(j)+2,…,j−1}\{p(j)+1, p(j)+2, \dots, j-1\}
  • Must include optimal solution to problem consisting of remaining
    compatible jobs 1,2,…,p(j)1,2,\dots, p(j).

Bellman equation:

{0,if j=0max{OPT(j−1),wj+OPT(p(j))},if j>0 \left\{ \begin{array}{lr} 0, & \text{if } j = 0\\ max\{OPT(j-1), w_j + OPT(p(j))\}, & \text{if } j>0 \end{array} \right.

Brute force:

Brute-force(n,s1,…,sn,f1,…,fn,w1,…,wnn,s_1,\dots,s_n,f_1,\dots,f_n, w_1,\dots,w_n):

  • sort jobs by finish time so that f1≤f2≤⋯≤fnf_1 \leq f_2 \leq \dots \leq f_n
  • Compute p[1],p[2],…,p[n]p[1],p[2], \dots, p[n] via binary search
  • Return Compute-Opt(n)

Compute-Opt(j):

  • If (j=0)
    • Return 0
  • Else
    • Return max {Compute−Opt(j−1),wj+Compute−Opt(p[j])}\{Compute-Opt(j-1), w_j + Compute-Opt(p[j])\}

Observation: recurisve algorithm is slow because of the overlapping subproblems ⇒\Rightarrow exponential-time algorithm.

  • E.g., the numbe of recursive calls for family of "layered" instances grows like the fibonacci sequence.

Memoization

Top-down dp (memoization).

  • Cache result of subproblem jj in M[j]M[j].
  • Use M[j]M[j] to avoid solving subproblem jj more than once.

Top-Down(n,s1,…,sn,f1,…,fn,w1,…,wnn,s_1,\dots,s_n,f_1,\dots,f_n, w_1,\dots,w_n)

  • sort jobs by finish time so that f1≤f2≤⋯≤fnf_1 \leq f_2 \leq \dots \leq f_n
  • Compute p[1],p[2],…,p[n]p[1],p[2], \dots, p[n] via binary search
  • M[0]←0M[0] \gets 0
  • Return M-Compute-Opt(n)

M-Compute-Opt(j):

  • If (M[j]M[j] is uninitialized)
    • M[j]←{M−Compute−Opt(j−1),wj+M−Compute−Opt(p[j])}M[j] \gets \{M-Compute-Opt(j-1), w_j + M-Compute-Opt(p[j])\}
  • Return M[j]M[j]

Running time analysis The memoized version of the algorithm takes O(nlog⁡n)O(n \log n) time. Proof:

  • sort by finish time with mergesort: O(nlog⁡n)O(n \log n).

  • compute p[j]p[j] for each jj: O(nlog⁡n)O(n \log n) via binary search

  • M−Compute−Opt(j−1)M-Compute-Opt(j-1): each invocation takes O(1)O(1) time and either

      1. returns an initialized value M[j]M[j]
      1. initializes M[j]M[j] and makes two recursive calls.
  • Progress measures Φ=#\Phi=\# initialized entires among M[1..n]M[1..n].

    • Initially Φ=0\Phi=0; throughout Φ≤n\Phi \leq n
      1. decreases Φ\Phi by 1⇐≤2n1 \Leftarrow \leq 2n recursive calls.
  • Overall running time of M−Compute−Opt(n)M-Compute-Opt(n) is O(n)O(n).

Those who cannot remember the past are codemned to repeat it.

So now that we have a DP algorithm that computes the optimal value, how do you find the optimal solution?

Find-Solution(jj)

  • If j=0j=0
    • return ∅\varnothing
  • Else if (wj+M[p[j]]>M[j−1])w_j + M[p[j]] > M[j-1])
    • Return {j}∪Find−Solution(p[j]))\{j\} \cup Find-Solution(p[j]))
  • Else
    • Return Find−Solution(j−1)Find-Solution(j-1).

M[j]=max{M[j−1],wj+M[p[j]]}M[j] = max \{M[j-1], w_j + M[p[j]]\}

Botton-up DP

Unwind recursion Buttom-Up(n,s1,…,sn,f1,…,fn,w1,…,wnn,s_1,\dots,s_n,f_1,\dots,f_n, w_1,\dots,w_n)

  • sort jobs by finish time so that f1≤f2≤⋯≤fnf_1 \leq f_2 \leq \dots \leq f_n
  • Compute p[1],p[2],…,p[n]p[1],p[2], \dots, p[n] via binary search
  • M[0]←0M[0] \gets 0
  • For j=1j=1 to nn:
    • M[j]←max{M[j−1],wj+M[p[j]]}M[j] \gets max \{M[j-1], w_j + M[p[j]]\}

Those are previously computed value.

The running time of the button-up version takes O(nlog⁡n)O(n \log n) time.

Maximum subarray problem

Given an array xx of nn integer (pos/neg), find a contiguous subarray whose sum is a maximum.

  • Let OPT(i) be teh maximum possible sum for any subarray ending at ii.
  • max{vi,vi+OPT(i−1)}max\{ v_i, v_i + OPT(i-1)\}.

The brute-force algorithm involves computing the sum for each i to j of a[i]+a[i+1]+⋯+a[j]a[i]+a[i+1] + \dots + a[j].

  • This takes Θ(n3)\Theta(n^3) time.

Apply the "cumulative sum" trick

  • precompute cumulative sums: S[i]=a[0]+⋯+a[i]S[i] = a[0] + \dots + a[i].
  • Now a[i]+a[i+1]+⋯+a[j]=S[j]−S[i−1]a[i] + a[i+1] + \dots + a[j] = S[j] - S[i-1]
  • Improves running time to Θ(n2)\Theta(n^2).

Kadane's algorithm: OPT(i)=OPT(i) = max sum of any subarray of xx whose rightmost index is ii

  • The goal: max OPT(i)OPT(i).

Bellman equation:

{xi,if i=0max{xi,xi+OPT(i−1)},if i>0 \left\{ \begin{array}{lr} x_i, & \text{if } i = 0\\ max\{x_i, x_i + OPT(i-1)\}, & \text{if } i>0 \end{array} \right.
  • in the max statement, we want to either take only element ii or take the element ii together with the best subarray ending at index i−1i-1.

This makes the running time O(n)O(n).

Segmented Least Squares

This is a multi-choice problem in the sense that you're mkaing the best choice out of many options.

Least square is a foundational problem in statistics.

  • Given nn points in the plane; (x1,y1),(x2,y2),…,(xn,yn)(x_1, y_1), (x_2, y_2), \dots, (x_n, y_n).
  • Find a line y=ax+by=ax+b that minimizes the sum of the squared error. SSE=∑i=1n(yi−axi−b)2SSE = \sum^n_{i=1} (y_i - ax_i - b)^2

Solution: calculus ⇐\Leftarrow min error is acheived when...

Segmented least squares: points lie roughly on a squence of several line segments. Given nn points in the plane: (x1,y1),(x2,y2),…,(xn,yn)(x_1, y_1), (x_2, y_2), \dots, (x_n, y_n), find a sequence of lines that mimimizes f(x)f(x). What is the reasonable choice for f(x)f(x) to balance accuracy (goodness of fit) and parsimony (number of lines)?

  • The goal is to mimimize f(x)=E+cLf(x) = E + cL for some constant c>0c > 0, where
    • EE = sum of the sums of the squared errors in each segment
    • LL = the number of lines.

Dynamic programming: multiway choice (as compared to binary choice until now). Notation:

  • OPT(j)OPT(j) = minimum cost for points p1,p2,…,pjp_1, p_2, \dots, p_j.
  • eije_{ij} = SSE (sum of squared errors) for the points pi,pi+1,…,pjp_i, p_{i+1}, \dots, p_j for some i≤ji \leq j.

To compute OPT(j)OPT(j):

  • last segment uses points pi,pi+1,…,pjp_i, p_{i+1}, \dots, p_j for some i≤ji \leq j.
  • cost = eij+c+OPT(i−1)e_{ij} + c + OPT(i-1).

Bellman equation:

OPT(j)={0if j=0min1≤i≤j{eij+c+OPT(i−1)}if j>0\begin{equation} OPT(j) = \begin{cases} 0 & \text{if $j=0$}\\ min_{1 \leq i \leq j}\{e_{ij} + c + OPT(i-1)\} & \text{if $j>0$}\\ \end{cases} \end{equation}
  • ii ranges from 11 to jj, making it multiway.

Algorithm

segmented-least-squares(n,p1,…,pn,cn,p_1, \dots, p_n, c)

  • For j=1j=1 to nn:

    • For i=1i=1 to jj,
      • compute the SSE eije_{ij} for the points p1,pi+1,…,pjp_1, p_{i+1}, \dots, p_j
  • M[0]←0M[0] \gets 0

  • For j=1j=1 to nn

    • M[j]←mini≤i≤j{eij+c+M[i−1]}M[j] \gets min_{i\leq i\leq j} \{e_{ij} + c + M[i-1]\} where M[i−1]M[i-1] is the previous previously computed value.
  • return M[n]M[n].

Analysis

Theorem: DP algorithm solves the segmented least squares problem in O(n3)O(n^3) time and O(n2)O(n^2) space.

Proof:

  • the bottleneck comes from computing SSE eije_{ij} for each ii and jj.
  • It takes O(n)O(n) to compute eije_{ij}.

It can be improved to O(n2)O(n^2) time

  • for each ii: pre compute the comulative sums, which can be used to compute eije_{ij} in O(1)O(1) time.

Knapsack Problem

Goal: pack knapsack so as to maximize total value of items taken

  • there are nn items: item ii provides value vi>0v_i > 0 and weighs wi>0w_i > 0.
  • value of a subset of items = sum of values of individual items.
  • knapsack has weight limit of WW

For example, the subset {1,2,5}\{1,2,5\} has value 3535 and weight 1010.

Assume that all values and weights are integral.

Define OPT(i,w)OPT(i,w) = optimal value of knapsack problem with items 1,…,i1, \dots, i, subject to weight limit ww. We want to find OPT(n,W)OPT(n,W) meaning that we use all nn items and are subject to weight limit WW.

In case 1: OPT(i,w)OPT(i,w) does not select item ii. (possibily because wi>ww_i>w).

  • Then OPT(i,w)OPT(i,w) selects the best of {1,2, dots,i−1}\{ 1, 2,\ dots, i-1\} subject to weight limit ww.

In case 2: OPT(i,w)OPT(i,w) selects item ii.

  • collect the new value viv_i.
  • new weight limit = w−wiw-w_i.
  • OPT(i,w)OPT(i,w) selects bets of {1,2,…,i−1}\{1,2,\dots, i-1\} subject to the new weight limit.

Bellman equation:

OPT(i−1)={0if i=0OPT(i−1,w)if wi>wmax{OPT(i−1,w),vi+OPT(i−1,w−wi)}otherwise\begin{equation} OPT(i-1) = \begin{cases} 0 & \text{if $i=0$}\\ OPT(i-1, w) & \text{if $w_i > w$}\\ max\{OPT(i-1, w), v_i + OPT(i-1, w-w_i) \} & \text{otherwise} \end{cases} \end{equation}

Algorithm

Knapsack(n,W,w1,…,wn,v1,…,vnn, W, w_1, \dots,w_n, v_1, \dots, v_n)

  • For w=0w=0 to WW
    • M[0,w]←0M[0,w] \gets 0.
  • For i=1i=1 to nn
    • For w=0w=0 to WW,
      • If (wi>ww_i>w) M[i,w]←M[i−1,w]M[i,w] \gets M[i-1, w]
      • Else M[i,w]←max{M[i−1,w],vi+M[i−1,w−wi]}M[i,w] \gets max\{M[i-1,w], v_i + M[i-1, w-w_i]\}
  • Return M[n,W]M[n,W].

Analysis

Theorem: the DP algorithm solves the knapsack problem with nn items and max weight WW in Θ(nW)\Theta(nW) time and Θ(nW)\Theta(nW) space. Where weights are integers between 11 and WW.

Proof:

  • Take O(1)O(1) time per table entry
  • There are Θ(nW)\Theta(nW) table entries
  • After computing optimal values, can trace back to find solutions: OPT(i,w)OPT(i,w) takes item ii iff M[i,w]>M[i−1,w]M[i,w] > M[i-1, w].

Remarks:

  • algorithm depends critically on assumptions that weights are integral
  • assumption that values are integral was not used

Bellman Ford Algorithm

Djikstra's algorithm does not work with edges with negative weight. This is beacuse greedy algorithms do not do backtracking and there could be a shorter path but would not be the smallest initially, which is how greedy algorithms work.

In graphs with negative cycles, the shortest path can be infinitely small, which is why the path must not have negative cycels.

Bellman Ford Algorithm finds the shortest path when there are negative values.

Theorem: If GG has no negative cycles, then there is a sortest path from SS to tt that is simple, i.e., it has nor epeated nodes, and hence at most n−1n-1 edges.

We define the bellman equation as OPT(n-1, s) which represents the shortest path from s to t.

Define OPT(i,v)OPT(i, v) as the minimum cost of a path from vv to tt using at most ii edges.

If i>0i>0 then OPT(i,v)=min{OPT(i−1,v),minw∈V(cvw+OPT(i−1,w))} OPT(i,v) = min \{OPT(i-1, v), min_{w\in V}(c_vw + OPT(i-1,w))\}

Algorithm

Shortest-Path(G,s,tG,s,t)

  • n←n \gets number of nodes in GG

  • Array M[0…n−1,V]M[0\dots n-1, V]

  • Define M[0,t]=0M[0,t]=0 and M[0,v]=∞M[0,v]=\infty for all other v∈Vv \in V.

  • For i=1,…,n−1i=1, \dots, n-1

    • For v∈Vv \in V in any order
      • Compute M[i,v]M[i,v] using recurrence
    • Endfor
  • Endfor

  • Return M[n−1,s]M[n-1,s].t