CPSC 322 - Lecture 17 - October 18, 2004

CPSC 322 - Lecture 17

Heuristic Search


I.  Can we make search less expensive?

So far we've been looking at blind search of a 
state space represented as a pre-existing or
pre-constructed graph.  But a little math shows us
that this approach can be very expensive computationally
as the problem grows in size to real-world proportions,
both in terms of constructing the graph in advance and 
searching it once it's constructed:

8-tile puzzle:   9! nodes = 362,880
                 you could enter all the information about 
                   these nodes in 4.2 days if you can enter 1 every second
                 blind search will find a goal very quickly

15-tile puzzle:  15! nodes = 1,307,674,368,000
                 at 1 per second, it would take you 41,466 years to
                   enter all the info about these nodes
                 but blind search will still find a goal in at most 
                   20 minutes if it looks at 10^9 nodes per second

chess:           10^120 nodes = a lot!
                 It will take a long long time to enter all these
                   nodes, and 
                 It will take a long long time to find a goal    

So the obvious (I hope) questions to ask now are 
"How do we cut down the amount of computation required
to do the searching?" and "How do we cut down on the 
amount of time (and space) required to construct the 
state space that we're exploring?"  Let's start with
the first question.


II.  Making your search smarter

Searches like what we've seen so far are, in a word, dumb.  
They don't know which next state might be any better than any 
other next state.  These searches can be methodical (e.g., 
look at the first on the list) or random (e.g., Calvin's 
decision: "Arbitrarily, I choose left.").  These searches 
settle for finding the goal state, but they don't care about 
how much effort goes into finding a path from the start state
to the goal state, nor do they worry much about the cost
of traversing the path once it's found (i.e., whether the 
path is optimal or not).

Usually, however, we don't have lots of time to allocate
to finding solutions to our problems.  We'd like to try
to find the goal state in as few steps as we can.  
That is, we'd like to try to find the "optimal path" from the 
initial state to the goal state, and we can help ourselves 
out here if we can put a little more "intelligence" into our 
search.

One time-honored way of doing this is to find a method to 
measure the "goodness" of a state -- that is, to determine 
how close a given state is to the goal state.  If we could 
make that evaluation consistently and correctly, then when we 
look at a list of states in trying to decide which to use 
next to generate new states, we could pick the state closest 
to the goal, instead of just picking the first one we see or 
picking one at random.

Most of the time, though, such measurements of a state's 
goodness are just estimates.  If the estimate is wrong, you 
could spend a lot of time and effort searching paths that 
will never get you to the goal, or at least that will give 
you less optimal solutions.  The better the ability to 
estimate goodness, the better is the chance for optimality.  
But unless the estimate is always right, there's no guarantee 
of success.  These measures of goodness are one example of 
something called "heuristics":  techniques that aid in the 
discovery of solutions to problems most of the time, but 
don't guarantee that they'll lead to solutions all of the 
time.


III. Heuristics and the 8-tile puzzle

Let's look again at the 8-tile puzzle we saw earlier.
There we described a blind, exhaustive, brute-force, depth-
first search for finding the goal state.  Could you do 
better?  Probably yes.  If you could come up with a way to 
estimate how close any given arrangement of tiles was to the 
goal, you could always choose to explore the state that was 
nearest the goal.  To do this, you'd have to figure out a way 
to codify the metrics for this evaluation in such a way that 
a computer could use them.  One heuristic might be to just 
count the number of tiles that are not in the place they belong.  
So if your goal state looks like this:

                  1 2 3
                  8   4
                  7 6 5

and your start state followed by the next possible states 
looks like this:

                  2 8 3
                  1 6 4
                  7   5
                   /|\
                  / | \
                 /  |  \
                /   |   \
               /    |    \
              /     |     \
             /      |      \
            /       |       \
          2 8 3   2 8 3   2 8 3
          1 6 4   1   4   1 6 4
            7 5   7 6 5   7 5

          score   score   score
            6       3       6

which of these next states is closer to the goal using our 
heuristic?  The middle state has only three tiles in the wrong
place (this assumes that we're going to count the empty space as
a tile), while the other two states have six tiles in 
the wrong place.  So for our next step in the search, we'd 
choose to generate all the states possible from that middle 
state.  Then we'd apply our evaluation heuristic again, and 
so on.  Of course, we could get more sophisticated with our 
heuristic measures.  For example, we could try to estimate 
how many moves it would take to get all the tiles in their 
appropriate places instead of just counting how many were 
already in the right place.  That metric is commonly called
the "Manhattan distance".  That might give us a better 
measure of goodness, or it might just cause us to spend extra 
time computing the goodness without any real return on the 
investment, or it might just completely mislead the search.  
We'd have to play with it for awhile to see if it would help 
us.

In the slides you'll see examples of using both tiles-out-
of-place and Manhattan distance metrics in the context of
heuristic best-first search.


IV.  Generating the graph as you need it

Way back at the beginning of this lecture, we raised
two questions.  The second question was "How do we cut 
down on the amount of time (and space) required to 
construct the state space that we're exploring?"  

The solution, in theory, is relatively simple.
Instead of looking up the next neighbors of a given
node on the frontier list, we have our search algorithm
apply a "move generator" to the given node to generate
those next neighbors nodes.  Here's the heuristic 
best-first search algorithm, modified to generate 
neighbor nodes as needed instead of finding neighbors
in a pre-constructed graph data structure:

  Given a set of start nodes, a set of goal nodes, 
  and a graph (i.e., the nodes and arcs):

  apply heuristic h(n) to start nodes
  make a "list" of the start nodes - let's call it the "frontier"
  sort the frontier by h(n) values

  repeat
    if no nodes on the frontier then terminate with failure
    choose one node from the front of the frontier and remove it
    if the chosen node matches the goal node
      then terminate with success
      else generate next nodes (neighbors)
           and put next nodes and h(n) values on frontier
           and sort frontier by h(n) values
  end repeat

In the slides you'll see a little bit of CILOG code 
to show you how to generate neighbor nodes in the context 
of the 8-tile puzzle, and in a subsequent set of lecture 
slides you'll see an improvement that actually makes that 
little bit of CILOG code into something useful.

Last revised: October 29, 2004