The city of Konigsberg occupied both banks and two islands of a river. The islands and the riverbanks were connected by seven bridges, as shown in the following graph. Is there a path through the city that crosses each bridge exactly once?
connect(i1, i2, b1). connect(i2, i1, b1). connect(rb1, i1, b2). connect(i1, rb1, b2). connect(rb1, i1, b3). connect(i1, rb1, b3). connect(rb1, i2, b4). connect(i2, rb1, b4). connect(rb2, i1, b5). connect(i1, rb2, b5). connect(rb2, i1, b6). connect(i1, rb2, b6). connect(rb2, i2, b7). connect(i2, rb2, b7).
This way does not lend itself to Euler's proof. This shows the important of knowledge representation. One representation (graph) leads to a simple proof. The other does not.
A state space is represented by a four-tuple [N, A, S, GD]
Starting at A, find the shortest path through all the cities, visiting each city exactly once and returning to A.
Reducing Search Complexity: For N cities, a brute-force solution to the traveling salesman problem takes N! time. There are ways to reduce this complexity:
Missionaries and Cannibals Problem: Give the state-space graph for the missionaries and cannibals problem. There are 3 missionaries and 3 cannibals on the side of a river which must be crossed in a boat that can hold at most 2 people at once. If the missionaries ever outnumber the cannibals on one or the other bank, they will convert the cannibals. Find an crossing strategy that gets the whole party to the opposite bank without losing any cannibals. Represent the start state as follows: [[m m m c c c] []]. Don't show transitions to duplicate states.
In data-driven search, sometimes called forward chaining, the problem solver begins with the given facts and a set of legal moves or rules for changing the state. Search proceeds by applying rules to facts to produce new facts. This process continues until (hopefully) it generates a path that satisfies the gaol condition.
In goal-driven search, sometimes called backward chaining, the problem solver begins with the goal to be solved, then finds rules or moves that could be used to generate this goal and determine what conditions must be true to use them. These conditions become the new goals, subgoals, for the search. This process continues, working backward through successive subgoals, until (hopefully) a path is generated that leads back to the facts of the problem.
Data-driven and goal-driven are relative concepts. They depend on how one chooses to represent the search space.
(A ^ B) -> (C ^ D) A B / Therefore, D
This proof started with the "facts", premises 1-3, and then applied rule to derive new facts until a path was derived that contained D (premise 7).1. (A ^ B) -> (C ^ D) 2. A 3. B / Therefore, D 4. A ^ B From 2, 3 by Conjunction 5. C ^ D From 1, 4 by Modus ponens 6. D ^ C From 5 by Commutation 7. D From 6 by Simplification
(A ^ B) -> (C ^ D) A B / Therefore, DFor the same argument, start with the goal state and work backwards
1. D 2. D ^ C This subgoal can produce 1 by Simplification 3. C ^ D This subgoal can produce 2 by Commutation 4. A ^ B This subgoal 5. (A ^ B) -> (C ^ D) and this premise can produce 3 by Modus Ponens 6. A This premise 7. B and this premise can produce 4 by Conjunction
Goal-driven search uses knowledge of the goal to guide the search. Use goal-driven search if;
Car Won't Start: Sketch the state space graph for the problem of diagnosing why your car won't start. Discuss the pros and cons of using a data-driven versus a goal-driven search strategy for this problem.
The first problem you have to solve is what the states in the graph represent. Let the states represent partial knowledge about the cause of the problem, with the goal being complete knowledge of the cause. So the goals are states such as: out-of-gas, dead-battery, and so on. Let the start state be zero knowledge. (See page 43.)
Question: Is this the only way to represent this problem?
The backtrack algorithm uses three lists plus one variable:
The Backtrack Algorithm
SL := [Start]; NSL := [Start]; DE := []; CS := Start; // Initialize while NSL != [] { if CS = goal (or meets goal description) return SL; // Success if CS has no children (except those on DE, SL, or NSL) { while SL != [] and CS = first element of SL { add CS to DE; // Dead End remove first element of SL; // Backtrack remove first element of NSL; CS := first element of NSL; } add CS to SL; } else { place children of CS (except those on DE, SL, NSL) onto NSL; CS := first element of NSL; add CS to SL; } } return FAIL; // Failure
A Trace of Backtrack Algorithm
AFTER CS SL NSL DE ITERATION 0 A [A] [A] [] 1 B [B A] [B C D A] [] 2 E [E B A] [E F B C D A] [] 3 H [H E B A] [H I E F B C D A] [] 4 I [I E B A] [I E F B C D A] [H] 5 F [F B A] [F B C D A] [E I H] 6 J [J F B A] [J F B C D A] [E I H] 7 C [C A] [C D A] [B F J E I H] 8 G [G C A] [G C D A] [B F J E I H]
In addition to the search direction (data- or goal-driven) a search is determined by the order in which the nodes are examined: breadth first or depth first.
Consider the following graph:
Depth First Search examines the nodes in the following order:
A, B, E, K, S, L, T, F, M, C, G, N, H, O, P, U, D, I, Q, J, R
Breadth First Search examines the nodes in the following order:
A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U
In breadth-first search, when a state is examined, all of its siblings are examined before any of its children. The space is searched level-by-level, proceeding all the way across one level before going down to the next level.
In the following algorithm the open list is like NSL in the backtrack algorithm. It contains states that have been generated but whose children have not been examined. The closed list is the union of the DE and SL. It contains states that have already been examined.
open := [Start]; // Initialize closed := []; while open != [] { // While states remain remove the leftmost state from open, call it X; if X is a goal return success; // Success else { generate children of X; put X on closed; eliminate children of X on open or closed; // Avoid loops put remaining children on RIGHT END of open; // Queue } } return failure; // Failure
Note that, unlike backtrack, this algorithm does not store the solution path. If you want the solution path, you should retain ancestor information on the closed list. For example, an ordered pair, (child, parent), can be kept on the closed and open lists. This allows the solution path to be constructed from the closed list.
A Trace of Breadth-first Algorithm: Search for U
AFTER open closed ITERATION 0 [A] [] 1 [B C D] [A] 2 [C D E F] [B A] 3 [D E F G H] [C B A] 4 [E F G H I J] [D C B A] 5 [F G H I J K L] [E D C B A] 6 [G H I J K L M] [F E D C B A] 7 [H I J K L M N] [G F E D C B A] 8 and so on until U is found or open = []
Optimality: Breadth-first search is guaranteed to find the shortest path to the goal, if a path to the goal exists.
In depth-first search, when a state is examined, all of its children and their descendants are examined before any of its siblings.
open := [Start]; // Initialize closed := []; while open != [] { // While states remain remove the leftmost state from open, call it X; if X is a goal return success; // Success else { generate children of X; put X on closed; eliminate children of X on open or closed; // Avoid loops put remaining children on LEFT END of open; // Stack } } return failure; // Failure
A Trace of Depth-first Algorithm: Search for U
AFTER open closed ITERATION 0 [A] [] 1 [B C D] [A] 2 [E F C D] [B A] 3 [K L F C D] [E B A] 4 [S L F C D] [K E B A] 5 [L F C D] [S K E B A] 6 [T F C D] [L S K E B A] 7 [F C D] [T L S K E B A] 8 [M C D] [F T L S K E B A] 9 [C D] [M F T L S K E B A] 10 [G H D] [C M F T L S K E B A] 11 and so on until U is found or open = []
Missionaries and Cannibals Problem: Discuss the advantages of depth-first vs. breadth-first search for this problem.
Perform depth-fist with a bound of 1 level. If goal not found, perform depth-first with a bound of 2. If goal not found, perform depth-first with a bound of 3. . . .
So at each iteration, a complete depth-first search is performed. It is guaranteed to find the shortest path, and its space utilization at any level n is B * n.
Problems in predicate calculus, such as determining whether one expression follows from a set of assertions, can be solved using graph search. Consider this example from the propositional calculus:
An and/or graph lets us represent expressions of the form p ^ q => r.
Using the above AND/OR graph, answer each of the following questions:
Using the following AND/OR graph, where is fred?