CS52 - Spring 2016 - Class 22

Example code in this lecture

   bb_search.sml
   producer.sml
   bb_odometer.sml

Lecture notes

  • admin
       - assignment 9 out tomorrow

  • problem solving via exhaustive search
       1) define the state space
          a) figure out what a "state" is, e.g. for roomers it was a list of 5 entries with values 1-5, first entry = baker, second cooper, etc.
          b) write a next state function that takes in a state and returns the "next" state. This function should loop through *all* possible combinations eventually.
             - to avoid an infinite space the function should return NONE when it has listed all possibilities
             - will return SOME value when it hasn't

          - the state space can then be defined as a lazy list of states using the producer function

       2) define what states constitute a solution
          - write a function that takes as input a state and returns true if it's a solution, false otherwise.

       3) search!
          - filter the lazy list (i.e. all possible options) using the solution test

  • n-queens problem
       - place n queens on an n by n chess board such that none of the queens are attacking

       - try the 4-queens problem. As you do it, think about how you're reasoning about finding the solution?
          
       - Are you doing an exhaustive search?
          - Most people don't do an exhaustive search
       
       - instead do a partial search until you reach a point where you can't get to a solution. Back up and try another solution
          - For example:
             - put queen in upper left
             - put queen in third row, 2nd column
             - at this point, we can't put *any* queen in the third column
             - backup and try fourth row, 2nd column
             - can put a queen in the 2nd row, third column
             - no options left in the fourth column
                - There are NO solutions with a queen in the upper left corner
             - keep trying

  • n-queens as an exhaustive search (let's think about it this way first to compare and contrast)
       1) define the state space
          - how can we represent a "state", i.e. a placement of queens?
             - can use the same idea as with roomers
             - a list with n entries (representing a column)
             - each entry has a value from 1 to n representing the row the queen is in in that column
          - next state (I'll let you figure this out, but it shouldn't be bad :)

       2) solution check (see assignment 9)

       3) search!

       - how many states are there for the 4-queens problem?
          - 4^4 = 2^8 = 256 (not too many)
          
       - how many states are there for the 8-queens problem?
          - 8^8 = 2^24 = ~16M (16,777,216 to be exact)

  • n-queens problems as tree search
       - start with an empty board and look for possible solutions by adding a queen at a time

       - look at 4-queens slides

  • look at bb_search.sml code
       - bbResult datatype
          - three values
          - reflects our need to distinguish between three values for partial configurations

       - bbSearch
          - what defines this datatype?
             - holds a value (like lazy lists)
             - like the lazy lists we transition to the "rest" of the data by using functions that take () as parameters
             - unlike lazy lists, there are *two* ways of transitioning to other bbSearch entries corresponding to our two ways of transitioning between partial states
                - we'll assume the first corresponds to increment
                - and the second to extend


  • look at the bbSolve function in bb_search.sml code
       - What is its type signature?
          - ('a -> bbResult) -> 'a bbSearch -> 'a list
             - ('a -> bbResult) is a function that scores partial solutions
             - 'a bbSearch is the state space
             - 'a list is the list of correct solutions!
       - BBEmpty: if we're at the end of the structure, it's not a solution
       - BBValue:
          - pattern match to the value (x) and the two functions for transitioning to other bbSearch entries in the structure
          - What do the three cases correspond to?
             - PENDING: if we have a pending solution, extend the partial state by calling extend with ()
             - INCORRECT: this is not a good partial solution, so increment it to try a different one
             - CORRECT:
                - do the same thing as a correct solution, i.e. try another increment of this one (think about searching and finding a solution to the n-queens problem)
                - BUT, keep track of the current state as a *solution*

  • look at bbProducer function in bb_search.sml code
       - What does this function do?
          - builds a bbSearch object!

       - What does it take as input?
          - increment: ('a -> 'a option)
          - extend: ('a -> 'a option)
          - partial state: 'a option

       - Gives back a new bbSearch object
          - if the partial state is NONE -> we're at the end of the structure, so BBEmpty
          - if there is some partial state build a new bbSearch object
             - first argument is the partial state (s)
             - second argument is a function that when called makes a recursive call to bbProducer with the *increment* of r
             - third argument is a function that when called makes a recursive call to bbProducer with the *extend* of r

       - Note this very closely parallels the lazy list producer function (see producer.sml code)
          - Key difference:
             - lazy lists: only one way to transition between states when we're doing exhaustive search
             - bbSearch: can transition in two ways, extending and incrementing

  • defining the problem
       - so far what we've written is problem agnostic

       - the problem is then defined by three things:
          - increment
          - extend
          - the partial solution evaluator

  • increment and extend
       - many problems can be represented as an odometer-like state (e.g. n-queens, assignment 9 problems :)

       - problem setup:
          - partial state is a list of numbers
          - increment: increase the left-most digit by 1 (could do rightmost digit, but turns out to be more efficient to process on the left)
          - extend: add a 1 to the *beginning* (left) of the list

       - what does the bbOdoIncr4 function do in bb_odometer.sml code do?
          - increments a partial state assuming it has values from 1 to 4

          - what will it produce on the following input?
             [1]
             [2]
             [1, 2, 3]
             [3, 2, 2]
             [4, 2, 3]
             [4,4,4,1]
             
          - Answers
             [2]
             [3]
             [2,2,3]
             [3, 2, 3];
             [4,2,3]
             [3,3]
             [2]

          - Key difference from the original odometer is that we have a variable number of digits

       - we can generalize the function to allow odometers of whatever digit size
          - look at the bbOdoIncr function in bb_odometer.sml code

       - how can we write extend, i.e. function that "extends" the solution by adding a 1 to the front?
          - look at the bbOdOExt function in bb_odometer.sml code
             - really easy! :)

  • now that we have increment and extend implemented we can use bbProducer to make states:
       > val bbO4 = bbProducer bbOdoIncr4 bbOdoExt (SOME []);
       val it = BBValue ([],fn,fn) : int list bbSearch

       - what is the (SOME [])?
          - the starting state, in this case the empty odometer

       - I've included two helper functions inc and ext that just call the increment and extend functions associated with a bbSearch object

  • look at the first part of the search for a solution of the 4-queens problem
       - Remember the basic algorithm:
          - if it's a partial solution: extend
          - if it's an incorrect solution: increment
          - if it's a solution: remember it! (and increment)

       - val s = bbO4;
       val s = BBValue ([],fn,fn) : int list bbSearch
       - val s = ext s;
       val s = BBValue ([1],fn,fn) : int list bbSearch
       - val s = ext s;
       val s = BBValue ([1,1],fn,fn) : int list bbSearch
       - val s = inc s;
       val s = BBValue ([2,1],fn,fn) : int list bbSearch
       - val s = inc s;
       val s = BBValue ([3,1],fn,fn) : int list bbSearch
       - val s = ext s;
       val s = BBValue ([1,3,1],fn,fn) : int list bbSearch
       - val s = inc s;
       val s = BBValue ([2,3,1],fn,fn) : int list bbSearch
       - val s = inc s;
       val s = BBValue ([3,3,1],fn,fn) : int list bbSearch
       - val s = inc s;
       val s = BBValue ([4,3,1],fn,fn) : int list bbSearch
       - val s = inc s;
       val s = BBValue ([4,1],fn,fn) : int list bbSearch
       - val s = ext s;
       val s = BBValue ([1,4,1],fn,fn) : int list bbSearch
       - val s = inc s;
       val s = BBValue ([2,4,1],fn,fn) : int list bbSearch
       - val s = ext s;
       val s = BBValue ([1,2,4,1],fn,fn) : int list bbSearch
       - val s = inc s;
       val s = BBValue ([2,2,4,1],fn,fn) : int list bbSearch
       - val s = inc s;
       val s = BBValue ([3,2,4,1],fn,fn) : int list bbSearch
       - val s = inc s;
       val s = BBValue ([4,2,4,1],fn,fn) : int list bbSearch
       - val s = inc s;
       val s = BBValue ([3,4,1],fn,fn) : int list bbSearch
       - val s = inc s;
       val s = BBValue ([4,4,1],fn,fn) : int list bbSearch
       - val s = inc s;
       val s = BBValue ([2],fn,fn) : int list bbSearch

       
  • backtracking
       - backtracking happens because of increment
          - notice in bbSolve that both when we find a solution and when we find an incorrect solution, we increment the current partial solution
          - if we have tried out all possibilities down this branch (e.g. [4, 1]), increment will backtrack to the next option
       - think about [4,4,1] in terms of queens
          - we've placed a queen in column 1 row 1
          - we've placed a queen in column 2 row 4
          - we've tried out all possibilities in the fourth column
          - where now?
             - we backtrack and try out [2], i.e. queen in the second position
          - when/why will this happen?
             - if [4, 4, 1] is incorrect (or correct) and we call increment

  • all we that we have left to implement is a function that goes from a partial state to bbResult (CORRECT, PENDING or INCORRECT)
       - this is the key thing you'll be writing for assignment 9

       - let's solve a simple problem: find all of the states in a list that have all the same numbers
       
       - look at the allEqualPred function in bb_odometer.sml code
          - returns PENDING if all values are equal and it isn't of length l
          - returns CORRECT if all values are equal and it is of length l
          - returns INCORRECT if the values aren't all equal

       - we can then use this with bbSolve and one of the odometers:
          > bbSolve (allEqualPred 5) bbO4;
          val it = [[1,1,1,1,1],[2,2,2,2,2],[3,3,3,3,3],[4,4,4,4,4]] : int list list
          > bbSolve (allEqualPred 7) bbO4;
          val it = [[1,1,1,1,1,1,1],[2,2,2,2,2,2,2],[3,3,3,3,3,3,3],[4,4,4,4,4,4,4]] : int list list
          > bbSolve (allEqualPred 2) bbO4;
          val it = [[1,1],[2,2],[3,3],[4,4]] : int list list

  • solving the n queens problem
       - I've written two bbQueensPredicate functions
          - one that returns INCORRECT and PENDING properly
          - one that always returns PENDING unless the state is complete
             - this is basically exhaustive search

       > bbSolve (bbQueensPredicate 4) (bbQueensProducer 4);
       val it = [[3,1,4,2],[2,4,1,3]] : int list list

       > bbSolve (bbQueensPredicateBad 4) (bbQueensProducer 4);
       val it = [[3,1,4,2],[2,4,1,3]] : int list list

       - we can see that they both find the two solutions to the 4-queens problem fairly quickly

       - for the 8-queens problem, however, we start to see a significant slowdown
          - but the exhaustive search finishes in < 10 seconds

       - for the 9-queens problem
          - branch and bound finishes quickly
          - exhaustive search takes much, much longer (but does finish)


  • no more SML (in class)!