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