Backtracking

January 27, 2021

Assignment Comments, Sample Solutions

General comments

Format code in code blocks.

Messy Recurrences

  • \(J(n) = J(n/2) + J(n/3) + J(n/6)+ n\)
    • Second row: \(\frac{n}{2}+\frac{n}{3}+\frac{n}{6}=n\).
    • All row sums equal \(n\): internal nodes dominate: \(J(n) \in O(n \log n)\)
      • The unevenness of the tree only changes the base of the log, which only changes the answer up to a constant factor (i.e., not at all.)
  • \(K(n) = K(n/2) + 2K(n/3) + 3K(n/4)+ n^2\)
    • Second row: \(\left(\frac{n}{2}\right)^2+\left(\frac{n}{3}\right)^2+\left(\frac{n}{3}\right)^2+\left(\frac{n}{4}\right)^2+\left(\frac{n}{4}\right)^2+\left(\frac{n}{4}\right)^2=\frac{95}{144}n^2\)
    • Subsequent rows get multiplied by \(\frac{95}{144}\): root dominates: \(K(n) = O(n^2)\)
      • The unevenness of the tree means there are even fewer internal nodes and leaves for the root to dominate. If we added nodes to make a full tree, the root would still dominate.

Writing Recursive Functions

  • Keep in mind any preconditions, and make sure:
    • Your algorithm uses them, if necessary, and
    • Your recursive calls obey them.
  • Write your function assuming the Recursion Fairy (inductive hypothesis) works, for any subproblem.

Find the Rotation

Preconditions: “Suppose you are given a sorted array of \(n\) distinct numbers that has been rotated \(k\) steps, for some unknown integer \(k\) between \(1\) and \(n − 1\).

  • Use the preconditions when describing your algorithm.
    • e.g., there’s only one base case
  • Make sure your recursive calls obey the preconditions.
rotationCheck(A[1..n])
if (n == 2) 
  return(1)  << only one possible rotation when n = 2 >>
else
  m = ceiling(n/2)
  if(A[m]>A[n])   << it's in second half >>
    k = (m-1)+rotationCheck(A[m..n])
  else  << it's in first half >>
    k = rotationCheck(A[1..m])
return(k)

Another rotation finder

RotationFinder(A[1...n]):
  if n=2 return 1
  else:
       middle <- floor(n/2)
       if A[middle]>A[middle+1]:  << we found it (lucky) >>
           return middle
       if A[middle]>A[1]:   << it's in second half >>
            return middle + rotationFinder(A[(middle+1)...n])
       else:   << it's in first half >>
            return rotationFinder(A[1...middle])

Recurrence: \(C(n) = C(n/2) + O(1)\). Solution: \(C(n) \in O(\log n)\).

Backtracking

General Idea

  • Problem has several options, and recursive substructure.
  • Try one of the options, and solve the subproblem recursively.
    • If that doesn’t work, try another option.

Backtracking algorithms tend to be slow.

Example: \(n\) Queens

Can you place \(n\) queens on an \(n\times n\) chessboard so that none are threatening another?

Schwer ist es übrigens nicht, durch ein methodisches Tatonniren sich diese Gewissheit zu verschaffen, wenn man 1 oder ein paar Stunden daran wenden will.

Gauss says it’s no big deal.

Backtracking algorithm: idea

Start with \(r=1\) and \(j = 1\).

  • Place a queen on the \(r\)th row in the \(j\)th square (avoiding conflicts with rows \(1, \ldots, r-1\)).
    • If you find a legal move, recursively solve the remaining rows.
    • If you can’t find a legal move, try the \((j+1)\)st square.

Try \(n=4\).

Backtracking algorithm: description

Q[1..n] is going to contain the locations of the queens. If Q[i] == j, that means there is a queen on the \(j\)th square of row \(i\).

Preconditions: r stands for the row we are trying to place a queen on. So we’ve managed to place a queen on rows \(1, \ldots, r-1\) legally when this algorithm is called.

PlaceQueens(Q[1 .. n], r):
    if r = n + 1
        print Q[1 .. n]  << Problem solved! >>
    else
        for j <- 1 to n
            legal <- TRUE
            for i <- 1 to r − 1
                if (Q[i] = j) or (Q[i] = j + r − i) or (Q[i] = j − r + i)
                    legal <- False    << a previous queen is attacking this square >>
            if legal
                Q[r] <- j
                PlaceQueens(Q[1 .. n], r + 1)

Backtracking

PlaceQueens(Q[1 .. n], r):
    if r = n + 1
        print Q[1 .. n]  << Problem solved! >>
    else
        for j <- 1 to n
            legal <- TRUE
            for i <- 1 to r − 1
                if (Q[i] = j) or (Q[i] = j + r − i) or (Q[i] = j − r + i)
                    legal <- False    << a previous queen is attacking this square >>
            if legal
                Q[r] <- j
                PlaceQueens(Q[1 .. n], r + 1)
  • The recursive call is “hopeful.”
    • It may fail. If it does, you try the next option. backtracking
    • Equivalent to a depth-first search.

See Figure 2.3.

Churros

Churro Cutting

  • The price of a churro depends on its length, according to the following table.
       [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
length    1    2    3    4    5    6    7    8    9    10
price     1    5    8    9   10   17   17   20   24    30
  • Out of the fryer, churros are \(n\) inches long, but they can be cut into smaller pieces before they are sold.
  • What is the optimal way to cut an \(n\)-inch churro to maximize the total price of all the pieces?

Table Groups

Table #1 Table #2 Table #3 Table #4 Table #5 Table #6 Table #7
Andrew Jordan Levi Ethan Josiah John Nathan
Kevin Blake Logan Grace Bri Jack Trevor
Kristen James Drake Isaac Talia Claire Graham

Optimal Churro Cutting

       [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
length    1    2    3    4    5    6    7    8    9    10
price     1    5    8    9   10   17   17   20   24    30
  1. A five-inch churro sells for 10 cents. Is there a way to cut it into pieces so that the total price of all the pieces will be more? What is the best (largest) total price you can find? What is the worst total price?

  2. Suppose (as inductive hypothesis) that CutChurro(k) returns the maximum price among all the ways to cut up a \(k\)-inch churro, for \(k\leq n\). Let’s say you cut a 3-inch piece off of an \(n\)-inch churro, leaving yourself with pieces of size \(3\) inches and \(n-3\) inches. You plan to sell the \(3\)-inch piece, but you are open to making more cuts in the \(n-3\)-inch piece. Give an expression for the maximum price you can get for the pieces, among all the ways you can continue to cut the \(n-3\)-inch piece up.

Optimal Churro Cutting, continued

  1. Suppose (as inductive hypothesis) that CutChurro(k) returns the maximum price among all the ways to cut up a \(k\)-inch churro, for \(k\leq n\). Write a for-loop to determine the maximum price among all the ways to cut up an \(n\)-inch churro.