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:
    1. Show that there is an optimal substructure
    2. Show the recursive relation that gives optimal solution
    3. Find the VALUE of the optimal solution
    4. (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:
    1. Show that there is an optimal substructure
    2. Show the recursive relation that gives optimal solution
    3. Find the VALUE of the optimal solution
    4. (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