04a: Recursion: Backtracking

ICS211, Spring 2013
Dr. Zach

(switch view)

Status check

A Problem to Solve

Find all combinations of 3 integers between 1 and 4 that sum to 5.

Determine all combinations

 111 112 113 114   121 122 123 124   131 132 133 134   141 142 143 144
 211 212 213 214   221 222 223 224   231 232 233 234   241 242 243 244
 311 312 313 314   321 322 323 324   331 332 333 334   341 342 343 344
 411 412 413 414   421 422 423 424   431 432 433 434   441 442 443 444

Sum all combinations

And see which == 5.

 111 112 113 114   121 122 123 124   131 132 133 134 | 141 142 143 144
 211 212 213 214   221 222 223 224 | 231 232 233 234   241 242 243 244
 311 312 313 314 | 321 322 323 324   331 332 333 334   341 342 343 344
|411 412 413 414   421 422 423 424   431 432 433 434   441 442 443 444

A backtracking algorithm

find(int[] array, int i):
  if i == array.length:
    return
  sum = sum of array elements up to index i (exclusive)
  for guess from 1 to 4:
    array[i] = guess
    if i == array.length - 1 && sum + guess == 5:
      print array             //found one
      return                  //no more to be found at this i
    else if guess + sum >= 5:
      return                  //dead end
    else:
      find(array, i + 1)      //recurse
  return
Trace:
find(array=[?,?,?], i=0)
.sum = 0
.guess = 1: 
..find(array=[1,?,?], i=1)
...sum = 1
...guess = 1
....find(array=[1,1,?], i=2)
.....sum=2
.....guess=1
......find(array=[1,1,1], i=3) - return
.....guess=2
......find(array=[1,1,2], i=3) - return
.....guess=3
......find(array=[1,1,3], i=3) 
......print: [1,1,3]
......return
...guess=2
....find(array=[1,2,?], i=2)
.....sum=3
.....guess=1
......find(array=[1,2,1], i=3) - return
.....guess=2
......find(array=[1,2,2], i=3) - return
......print: [1,2,2]
......return
...guess=3
....(and so on...)

Backtracking

Variant: All constants as parameters

find(int start, int end, int total, int[] array, int i):
  if i == array.length:
    return
  sum = sum of array elements up to index i (exclusive)
  for guess from start to end:
    array[i] = guess
    if i == array.length - 1 && sum + guess == total:
      print array
      return
    else if guess + sum >= total:
      return
    else:
      find(start, end, total, array, i + 1)
  return

Variant 2: Return list of solutions

find(int start, int end, int total, int[] array, int i):
  list = new empty list
  if i == array.length:
    return list  //still empty
  sum = sum of array elements up to index i (exclusive)
  for guess from start to end:
    array[i] = guess
    if i == array.length - 1 && sum + guess == total:
      list.add(copy of array)
      return
    else if guess + sum >= total:
      return list  //might be empty
    else:
      sublist = find(start, end, total, array, i + 1)  //recurse
      list.addAll(sublist)
  return list //might be empty

Combo-finding algorithm as Java code

import java.util.ArrayList;

public class ComboSumFinder {
  /** Prints all combos of 3 ints, each between 1 to 4, that add up to 5. */
  public static void main(String[] args) {
    ArrayList<int[]> combos = find(3, 1, 4, 5);
    for (int[] combo : combos) {
      System.out.println(java.util.Arrays.toString(combo));
    }
  }

  /**
   * Returns a list of all combos of the given length, formed of integers
   * between start and end (both inclusive), that add up to total.
   */
  public static ArrayList<int[]> find(int length, int start, int end, int total) {
    return find(start, end, total, new int[length], 0);
  }

  private static ArrayList<int[]> find(int start, int end, int total, 
                                             int[] array, int i) {
    ArrayList<int[]> list = new ArrayList<int[]>();
    if (i == array.length) {
      return list;
    }
    //sum of elements in array up to index i (exclusive)
    int sum = 0;
    for (int j = 0; j < i; j++) {
      sum += array[j];
    }

    //try each value for current array element from start to end
    for (int guess = start; guess <= end; guess++) {
      array[i] = guess;
      if (i == array.length - 1 && sum + guess == total) {
        list.add(java.util.Arrays.copyOf(array, array.length));
        break;
      }else if (guess + sum >= total) {
        return list;
      }else {
        ArrayList<int[]> sublist = find(start, end, total, array, i + 1);
        list.addAll(sublist);
      }
    }
    return list;
  }
}

Sudoku

..|2. 
2.|.4 
-----
..|4.
3.|..

Sudoku Algorithm

sudoku(board, row, col):
  if board.isFull:
    return OK
  if (row,col) already set:
    return sudoku(board, next(row,col))  //on to next square
  for guess 1 to 4:
    board.set(row, col, guess)
    if board.checkRow? and board.checkCol? and board.checkSquare?:
      ok? = sudoku(board, next(row,col))
      if ok?:
        return OK
        
  board.unset(row,col)        
  return !OK  //no guess worked here
..|2. 
2.|.4 
-----
..|4.
3.|..

Many classic backgracking problems

A04: Maze Problem

Java Review

Odds and Ends

Parameters vs Local variables (1/2)

Similarities:

So parameters considered by some to be a subset of local variables.

Parameters vs Local variables (2/2)

Differences:

Loop controls: break

Sometimes important (or just easier) to break out of a loop early:

int[] nums = {1, 5, -2, 10};

//sum all values up to first negative number
int sum = 0;
for (int i = 0; i < nums.length; i++) {
  if (nums[i] >= 0) {
    sum += nums[i];
  }else {
    break;
  }
}
System.out.println("Sum of initial positives only: " + sum);

Loop controls: Infinite loops + break

java.util.Scanner keybd = new java.util.Scanner(System.in);
int oneToTen = 0;
while (true) {
  try {
    System.out.print("Enter an int b/w 1 and 10: ");
    oneToTen = keybd.nextInt();
    if (oneToTen >= 1 && oneToTen <= 10) {
      break; //good input
    }else {
      System.out.println("That is not between 1 and 10.");
    }
  }catch (java.util.InputMismatchException e) {
    System.out.println("That is not a number.");
    keybd.nextLine();  //clear input stream
  }
}
//...use oneToTen here...

Loop controls: More

Summary

Next time...