CS52 - Fall 2015 - Class 21
Example code in this lecture
bb_search.sml
producer.sml
bb_odometer.sml
Lecture notes
admin
- office hours today: 3-4:30 (instead of 2:30-4)
- 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?
- ('s -> bbResult) -> 's bbSearch -> 's list
- ('s -> bbResult) is a function that scores partial solutions
- 's bbSearch is the state space
- 's 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: fn () -> 'a bbSearch
- extend: fn () -> 'a bbSearch
- 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 (of length 0 up to 4)
- 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 structure 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)!