COMP3010: Algorithm Theory and Design
Daniel Sutantyo, Department of Computing, Macquarie University
4.3 - Developing a DP Algorithm - Recursive Relation
Developing a DP Algorithm
- The four steps:
- Show that there is an optimal substructure
- Show the recursive relation that gives optimal solution
- Find the VALUE of the optimal solution
- (optional) Find the COMBINATION that gives the optimal solution
4.3 - Developing a DP Algorithm - Recursive Relation
Developing a DP Algorithm
- In proving the existence of optimal substructure, we assume that we know what the optimal solution is
- but this means we know which subproblem to use
- how do we pick the subproblem that will give us the optimal answer?
4.3 - Developing a DP Algorithm - Recursive Relation
\(i\)
\(p_i\)
1 2 3 4 5 6 7 8 9 10
1 5 8 9 10 17 17 20 24 30
\(i\)
optimal
1 2 3 4 5 6 7 8 9 10
1 5 8 10 13 17 ?? ?? ?? ??
Developing a DP Algorithm
4.3 - Developing a DP Algorithm - Recursive Relation
\(i\)
\(p_i\)
1 2 3 4 5 6 7 8 9 10
1 5 8 9 10 17 17 20 24 30
\(i\)
optimal
1 2 3 4 5 6 7 8 9 10
1 5 8 10 13 17 ?? ?? ?? ??
$10
$9 + $1
$8 + $5
$5 + $8
$1 + $10
Recursive Relation
4.3 - Developing a DP Algorithm - Recursive Relation
- Let \(p_i\) be the price of a rod of length \(i\), \(1 \le i \le n\)
- \(p_n\) is the price of the whole rod (i.e. don't make any cut)
- Let \(r_i\) be the maximum revenue that we can obtain from a rod of length \(i\), \(1 \le i \le n\)
Recursive Relation
4.3 - Developing a DP Algorithm - Recursive Relation
- \(p_i\) price or rod of length \(i\)
- \(r_i\) revenue from rod of length \(i\)
- The possible revenues for cutting a rod of length \(n\) are:
- \(p_n\)
- \(p_{n-1} + r_1\)
- \(p_{n-2} + r_2 \)
- ...
- \(p_2+r_{n-2}\)
- \(p_1+r_{n-1}\)
- All cases except for the first one creates an additional subproblem
Recursive Relation
4.3 - Developing a DP Algorithm - Recursive Relation
- The recursive relation for the rod cutting problem is
- i.e. go through all the subproblems, and find one that will maximise the revenue
- isn't that how you would code this problem?
\[r_n = \max_{1\le i \le n} (p_i + r_{n-i}) \]
Recursive Relation
4.3 - Developing a DP Algorithm - Recursive Relation
- Finding the recursive relation is generally not hard, because it is as simple as going through all the possible options and choosing the best one
- i.e. what you would write in the code
- In the case of rod cutting, we want to maximise the revenue, so obviously we use the max function here
- The hard thing (maybe) is to write it in a clear mathematical notation
- for example, here is the recursive relation for LCSS problem
\[ \text{LCSS}(X,Y) = \begin{cases} 0 & \text{if \(X\) or \(Y\) is empty}\\1 + \text{LCSS}(X_2,Y_2) & \text{if $x_1 = y_1$}\\\max\left(\text{LCSS}(X_2,Y_1),\text{LCSS}(X_1,Y_2)\right) & \text{if $x_1 \ne y_2$}\end{cases} \]
Recursive Relation
4.3 - Developing a DP Algorithm - Recursive Relation
- We are going to see more example of finding the recursive relation
- every problem is different, there's not really a general way to do it
- in my opinion, this is the hardest part, because once I work this out, it is easy to code
- showing optimal substructure is often MUCH easier, it's just confusing at first, but very routine once you know how it works, plus you are giving pretty much the same argument for most problems
- Now that we know the recursive relation, we can easily code it
Rod Cutting - The Code
4.3 - Developing a DP Algorithm - Recursive Relation
\[r_n = \max_{1\le i \le n} (p_i + r_{n-i}) \]
// assume p[i] gives price of rod of length i
public static int cut(int n) {
if (n == 0)
return 0;
int answer = Integer.MIN_VALUE;
for (int i = 1; i <= n; i++) {
answer = Math.max(answer, p[i] + cut(n-i));
}
return answer;
}
Rod Cutting - The Code
4.3 - Developing a DP Algorithm - Recursive Relation
static int[] p = {0, 1, 5, 8, 9, 10, 17, 17, 20, 24, 30,
33, 36, 40, 42, 45, 50, 52, 58, 58, 60,
62, 65, 66, 72, 80, 82, 83, 85, 87, 88};
public static int cut(int n) {
if (n == 0)
return 0;
int answer = Integer.MIN_VALUE;
for (int i = 1; i <= n; i++) {
answer = Math.max(answer, p[i] + cut(n-i));
}
return answer;
}
n | optimal answer | no. of recursive calls |
---|---|---|
10 | 30 | 1,024 |
15 | 45 | 33,792 |
20 | 63 | 1,082,368 |
25 | 80 | 34,636,800 |
30 | 94 | 1,108,378,624 |
Rod Cutting - Top Down
4.3 - Developing a DP Algorithm - Recursive Relation
static int[] p = {0, 1, 5, 8, 9, 10, 17, 17, 20, 24, 30,
33, 36, 40, 42, 45, 50, 52, 58, 58, 60,
62, 65, 66, 72, 80, 82, 83, 85, 87, 88};
public static int cut(int n) {
if (n == 0)
return 0;
int answer = Integer.MIN_VALUE;
for (int i = 1; i <= n; i++) {
answer = Math.max(answer, p[i] + cut(n-i));
}
return answer;
}
public static int cut(int n) {
if (r[n] != 0) return r[n]; // memoise
if (n == 0)
return 0;
int answer = Integer.MIN_VALUE;
for (int i = 1; i <= n; i++) {
answer = Math.max(answer, p[i] + cut(n-i));
}
return r[n] = answer; // store the result
}
Rod Cutting - Top Down
4.3 - Developing a DP Algorithm - Recursive Relation
n | optimal answer | no. of recursive calls |
---|---|---|
10 | 30 | 56 |
15 | 45 | 122 |
20 | 63 | 213 |
25 | 80 | 329 |
30 | 94 | 470 |
public static int cut(int n) {
if (r[n] != 0) return r[n]; // memoise
if (n == 0)
return 0;
int answer = Integer.MIN_VALUE;
for (int i = 1; i <= n; i++) {
answer = Math.max(answer, p[i] + cut(n-i));
}
return r[n] = answer; // store the result
}
Rod Cutting - Top Down vs Bottom Up
4.3 - Developing a DP Algorithm - Recursive Relation
recursion
top-down
DP
bottom-up
DP
???
Rod Cutting - Bottom-Up
4.3 - Developing a DP Algorithm - Recursive Relation
- Bottom-up DP:
- to compute \(r_n\) we need to know \(r_{n-1}\), \(r_{n-2}\), \(\dots\), \(r_1\)
- so why not just compute all of them (like fibonacci)
public static int cut_bottom_up(int n) {
r[0] = 0;
for (int j = 1; j <= n; j++) {
int answer = Integer.MIN_VALUE;
for (int i = 1; i <= j; i++) {
answer = Math.max(answer, p[i] + r[j-i]);
}
r[j] = answer;
}
return r[n];
}
Rod Cutting - Bottom-Up
4.3 - Developing a DP Algorithm - Recursive Relation
public static int cut_bottom_up(int n) {
r[0] = 0;
for (int j = 1; j <= n; j++) {
int answer = Integer.MIN_VALUE;
for (int i = 1; i <= j; i++) {
answer = Math.max(answer, p[i] + r[j-i]);
}
r[j] = answer;
}
return r[n];
}
- To compute \(r_n\), we need \(r_1\), \(r_2\), \(\dots\), \(r_{n-1}\)
- Compute \(r_j\) for \(j = 1, 2, \dots, n\)
- \(r_1 = p_1\)
- \(r_2 = \max\{p_2,p_1+r_1\}\)
- \(r_3 = \max\{p_3,p_2+r_1,p_1+r_2\}\)
Rod Cutting - Final Step
4.3 - Developing a DP Algorithm - Recursive Relation
- The four steps:
- Show that there is an optimal substructure
- Show the recursive relation that gives optimal solution
- Find the VALUE of the optimal solution
- (optional) Find the COMBINATION that gives the optimal solution
Rod Cutting - Final Step
4.3 - Developing a DP Algorithm - Recursive Relation
public static int cut_bottom_up(int n) {
r[0] = 0;
for (int j = 1; j <= n; j++) {
int answer = Integer.MIN_VALUE;
for (int i = 1; i <= j; i++) {
if (answer < p[i] + r[j-i]) {
answer = p[i] + r[j-i];
s[j] = i;
}
}
r[j] = answer;
}
return r[n];
}
- Create an array \(s\) where \(s[j]\) holds the optimal size of the first piece (i.e. the piece that you do not cut any further) when solving a subproblem of size \(j\)
Rod Cutting - Final Step
4.3 - Developing a DP Algorithm - Recursive Relation
- Basically, for each length, we have to compute:
- \(p_n\)
- \(p_1+r_{n-1}\)
- \(p_2+r_{n-2}\)
- ...
- \(p_{n-2} + r_2 \)
- \(p_{n-1} + r_1\)
- and we choose one of these (the best one)
- so just keep track what the length of the uncut rod is
Rod Cutting - Final Step
4.3 - Developing a DP Algorithm - Recursive Relation
\(i\)
\(p_i\)
1 2 3 4 5 6 7 8 9 10
1 5 8 9 10 17 17 20 24 30
\(s_i\)
1 2 3 2 2 6 1 2 3 10
- For \(i = 10\), the optimal cut is 10 and 0, so we store \(s_{10} = 10\)
- optimal solution is 30, just use 10
- For \(i = 9\), the optimal cut is 3 and 6, so we store \(s_{9} = 3\)
- For \(i = 6\), the optimal cut is 6 and 0, so we store \(s_{6} = 6\)
- optimal solution is 3 and 6
Rod Cutting - Final Step
4.3 - Developing a DP Algorithm - Recursive Relation
\(i\)
\(p_i\)
1 2 3 4 5 6 7 8 9 10
1 5 8 9 10 17 17 20 24 30
\(s_i\)
1 2 3 2 2 6 1 2 3 10
public static String construct_solution(int n) {
String ans = "";
while (n > 0) {
ans = ans + s[n] + " ";
n = n - s[n];
}
return ans;
}
Rod Cutting - Final Step
4.3 - Developing a DP Algorithm - Recursive Relation
static int[] p = {0, 1, 5, 8, 9, 10, 17, 17, 20, 24, 30,
33, 36, 40, 42, 45, 50, 52, 58, 58, 60,
62, 65, 66, 72, 80, 82, 83, 85, 87, 88};
n | optimal answer | optimal cuts |
---|---|---|
10 | 30 | 10 |
15 | 45 | 2 and 13 |
20 | 63 | 2 and 18 |
25 | 80 | 25 |
30 | 94 | 12 and 18 |
Rod Cutting - Final Words
4.3 - Developing a DP Algorithm - Recursive Relation
- In these lectures, you are not learning how to code a dynamic programming solution, you're learning how to recognise the situation where you can apply the method:
- check if there is an optimal substructure
- find the recursive relation
- code it
- Bottom-up dynamic programming can be difficult
COMP3010 - 4.3 - Developing a DP Algorithm - Part 2 - Recursive Relation
By Daniel Sutantyo
COMP3010 - 4.3 - Developing a DP Algorithm - Part 2 - Recursive Relation
Writing the algorithm and code for rod-cutting
- 213