Dynamic Programming - Rod Cutting

Rod Cutting Problem

• Maximal subarray is a relatively simple DP program
• At any step, finding the optimal solution requires an optimal solution to only one subproblem

• A more interesting problem is Rod Cutting
• Finding an optimal solution requires solutions to multiple subproblems

Rod Cutting Problem

• Problem: Find best way to cut a rod of length $n$
• Given: rod of length $n$

• Assume that each length rod has a price $p_i$

• Find best set of cuts to get maximum price
• Each cut is integer length
• Can use any number of cuts, from 0 to $n-1$
• No cost for a cut

Rod Cutting Prices

• Example rod lengths and values:
•  length $i$ 1 2 3 4 5 6 7 8 price $p_i$ 1 5 8 9 10 17 17 20

Cutting the Rod

• Can cut rod in $2^{n-1}$ ways since each inch can have a cut or no cut

• Example - rod of length 4 (assuming values for 1-4, above):
•  Cuts At Value 4 (ie no cuts) 9 1, 3 1 + 8 = 9 2, 2 5 + 5 = 10 3, 1 8 + 1 = 9 1, 1, 2 1 + 1 + 5 = 7 1, 2, 1 1 + 5 + 1 = 7 2, 1, 1 5 + 1 + 1 = 7 1, 1, 1, 1 1 + 1 + 1 + 1 = 4

• Best: two 2-inch pieces = revenue of $p_2 + p_2 = 5 + 5 = 10$

Calculating Maximum Revenue

• We can compute the maximum revenue ($r_i$) for rods of length $i$

• Repeat the value/price table for easy reference:  length $i$ 1 2 3 4 5 6 7 8 price $p_i$ 1 5 8 9 10 17 17 20

•  $i$ $r_i$ optimal solution 1 1 1 (no cuts) 2 5 2 (no cuts) 3 8 3 (no cuts) 4 10 2 + 2 5 13 2 + 3 6 17 6 (no cuts) 7 18 1 + 6 or 2 + 2 + 3 8 22 2 + 6
• Let's compute these values from the top of the table, down
• 1: Obvious
• 2: Compare: 2, 1+1

• 3: Compare: 3, 1+2, 2+1, 1+1+1

• 4: 4, 1+3, 2+2, 3+1, 1+1+2, 1+2+1, 2+1+1, 1+1+1+1
• ...

• Do we need to examine all of these?
• What is the relation between 1+3, 1+2+1, 1+1+2, and 1+1+1+1?
• All we need to compare is sums of $r_i$ pairs

• Simplistic comparison: $r_k = \max(p_k, r_1+r_{k-1}, r_2+r_{k-2}, \dots, r_{k-1}+r_1)$
• But, rather than adding two $r$ values (eg $r_2$ and $r_{k-2}$) we can add a $p$ value and an $r$ value (eg $p_2$ and $r_{k-2}$)
• This approach determines which first cut (ie $p_i$) gives the best revenue.
• This approach gives the same results and is easier to calculate

Caclulating Maximum Revenue (Continued)

• Better comparison: $r_k = \max(p_i + r_{k-i})$ over all $1≤ i ≤k$

• Here's a table showing what each $r_i$ depends on
• $r_4$  $i$ $r_i$ Maximum of 1 $r_1$ $p_1+r_0$ 2 $r_2$ $p_1+r_1, p_2+r_0$ 3 $r_3$ $p_1+r_2, p_2+r_1, p_3+r_0$ 4 10 $p_1+r_3, p_2+r_2, p_3+r_1,p_4+r_0$ ... ... ...

• How do we fill in table entry $r_k$:
• For each possible first cut (ie $p_1 .. p_k$),
• Calculate the sum of the value of that cut (ie $p_i$) and the best that could be done with the rest of the rod (ie $r_{k-i}$).
• Choose the largest sum $(p_i + r_{k-i})$.
• Notice that each value of $r_i$ depends only on values higher in the table

• Repeat the value/price table for easy reference:  length $i$ 1 2 3 4 5 6 7 8 price $p_i$ 1 5 8 9 10 17 17 20

•  $i$ $r_i$ optimal solution 1 1 1 (no cuts) 2 5 2 (no cuts) 3 8 3 (no cuts) 4 10 2 + 2 5 13 2 + 3 6 17 6 (no cuts) 7 18 1 + 6 or 2 + 2 + 3 8 22 2 + 6

• Examples:
\begin{aligned} r_4 & = \max(p_1+r_3, p_2+r_2, p_3+r_1, p_4+r_0) \\ & = \max(1+8, 5+5, 8+1, 9+0) \\ & = \max(9, 10, 9, 9) \\ & = 10 \end{aligned} \quad \begin{aligned} r_5 & = \max(p_1+r_4, p_2+r_3, p_3+r_2, p_4+r_1, p_5+r_0) \\ & = \max(1+10, 5+8, 8+5, 9+1, 10+0) \\ & = \max(11, 13, 13, 10, 10) \\ & = 13 \end{aligned}
• Assume $r_0 = 0$
• We will discuss finding the solution (ie 2,3) later

Rod Cutting: Recursive Solution

• This recursive algorithm uses the formula above and is slow
• Code
•     -- price array p, length n
Cut-Rod(p, n)
if n = 0 then
return 0
end if
q := MinInt
for i in 1 .. n loop
q = max(q, p(i) + Cut-Rod(p, n-i)
end loop
return q

• Recursion tree (shows subproblems): 4/[3,2,1,0]//[2,1,0],[1,0],0//[1,0],0,0//0
• Performance: Let T(n) = number of calls to Cut-Rod(x, n), for any x
• $T(0) = 0$
• $\displaystyle T(n) = 1 + \sum_{i=1}^n T(n-i) = 1 + \sum_{j=0}^{n-1} T(j)$
• Solution: $T(n) = 2^n$

Rod Cutting: Dynamic Programming Solutions

• Problem with recursive solution: subproblems solved multiple times

• Must figure out a way to solve each subproblem just once

• Two possible solutions: solve a subproblem and remember its solution
• Top Down: Memoize recursive algorithm
• Bottom Up: Figure out optimum order to fill the solution array

Rod Cutting: Top Down Memoized Solution

• This memoized recursive solution is faster than the one above

• Store solution to subproblem of length i in array element r(i)
•     MemoizedCutRod(p, n)
r: array(0..n) := (0 => 0, others =>MinInt)
return MemoizedCutRodAux(p, n, r)

MemoizedCutRodAux(p, n, r)
if r(n) = 0 and then n /= 0 then  -- check if need to calculate a new solution
q: int := MinInt
for i in 1 .. n loop
q := max(q, p(i) + MemoizedCutRodAux(p, n-i, r))
end loop
end if
r(n) := q
end if
return r(n)

Rod Cutting: Bottom Up Solution

• Store solution to subproblem of length i in array element r(i)
•     BottomUpCutRod(p, n)
r: array(0..n)    -- optimal value for rods of length 0..n
r(0) := 0
for j in 1 .. n loop
q := MinInt
for i in 1 .. j loop  -- Find the max cut position for length j
q := max(q, p(i) + r(j-i)
end loop
r(j) := q
end loop
return r(n)

Rod Cutting: Running Times

• Both top down and bottom up requre Θ(n^2) time

• Bottom up: Nested loops, 1+2+3+4+ ...n

• Top down: MemoizedCutRod
• MemoizedCutRod solves each subproblem only once
• it solves subproblems for sizes 0, 1, 2, ...., n
• To solve subproblem of size n, the for loop iterates n times
• Over all recursive calls, the total number of iterations = 1 + 2 + ...

Rod Cutting: Finding the Solution

• MemoizedCutRod simply gave the optimum value, not optimum cuts

• Let's use the bottom up approach and remember cuts
•     ExtendedBottomUpCutRod(p, n)
r: array(0..n)    -- optimal value for rods of length 0..n
s: array(0..n)    -- optimal first cut for rods of length 0..n
r(0) := 0
for j in 1 .. n loop
q := MinInt
for i in 1 .. j loop  -- Find the max cut position for length j
if q < p(i) + r(j-i) then
q := p(i) + r(j-i)
s(j) := i   -- Remember the value of best so far value of i
end if
end loop
r(j) := q
end loop
return r and s

PrintCutRodSolution(p, n)
(r, s) := ExtendedBottomUpCutRod(p, n)
while n > 0 loop
print s(n)
n := n - s(n)
end loop

• Return values from ExtendedBottomUpCutRod(p, n)
•  i 0 1 2 3 4 5 6 7 8 $p_i$ 0 1 5 8 9 10 17 17 20 ri 0 1 5 8 10 13 17 18 22 si 0 1 2 3 2 2 6 1 2

 length $i$ 1 2 3 4 5 6 7 8 price $p_i$ 1 5 8 9 10 17 17 20

• Notice: values of subproblem solutions gives enough information to solve the whole problem

Back to Dynamic Programming