Skip to content

Commit

Permalink
Update Tree-Search-and-Graph-Search.md
Browse files Browse the repository at this point in the history
Testing out a new GENERIC-SEARCH pseudocode for 4e. @MrDupin @ctjoreilly @redblobgames you've looked at search -- what do you think of this approach?
  • Loading branch information
norvig committed Sep 9, 2017
1 parent 75af378 commit 44e38dc
Showing 1 changed file with 10 additions and 3 deletions.
13 changes: 10 additions & 3 deletions md/Tree-Search-and-Graph-Search.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,25 @@

## AIMA4e
__function__ GENERIC-SEARCH(_problem_) __returns__ a solution, or failure
 _frontier_ ← a queue with one node, the initial state of _problem_
 _frontier_ ← a queue containing one path, to the initial state of _problem_
 _explored_ ← ∅
 _solution_ ← Failure
 __while__ _solution_ cannot possibly be improved __do__
   _p_ ← pop the top node from _frontier_
 __while__ _solution_ can possibly be improved __do__
   _p_ ← pop the top path from _frontier_
   __for__ _c_ __in__ _problem_.successors(_p_) __do__
&emsp;&emsp;&emsp;&emsp;&emsp;__if__ _c_ not in _explored_ or cost(_c_) < cost(previous path to _c_.state) __then__
&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;add _c_ to _frontier_ and to _explored_
&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;__if__ cost(_c_) < cost(_solution_) __then__
&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;_solution_ = _c_
&emsp;__return__ _solution_

---
__Figure__ ?? In the GENERIC-SEARCH algorithm, we keep track of the best _solution_ found so far, as well as a set of states that we have already _explored_, and a _frontier_ of paths from which we will choose
the next path to expand.
In any specific search algorithm, we specify (1) the criteria for ordering the paths in the frontier,
and (2) the procedure for determining when it is no longer possible to improve on a solution.


## AIMA3e
__function__ TREE-SEARCH(_problem_) __returns__ a solution, or failure
&emsp;initialize the frontier using the initial state of _problem_
Expand Down

4 comments on commit 44e38dc

@antmarakis
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer the new version.

From my understanding, there is some added cost in computation (since the cost of a path needs to be calculated each time, in cost(c) and cost(solution) - although I can see that we can store the cost to each path without much hassle), and it also seems that there is an added memory cost (storing paths instead of nodes).

Despite that, I think this is a more "complete" algorithm, since we not only get the cost of the solution, but the path itself. In most search algorithms, to find the final path there needs to be employed some sort of auxiliary functionality (usually setting the parent of each node and then backtracking from the final node). Here we return the path, and the cost can be calculated later using the cost function. This is neat.

Also, I feel like this approach is closer to what search in general entails. We want to search for the best path in a set of possible paths, and this approach does that in a cleaner way. Previously we were basically expanding nodes and keeping score of the cost-so-far, which is conceptually a bit further away from the problem at hand.

Regarding Python, this will be easy to implement + we get to print the path to the goal state instead of just its cost, without the use of external functions. This, I feel, will be beneficial to students.

Finally, and this is just a personal anecdote, when I was studying search algorithms I always wondered why didn't we store paths instead of nodes, since it seemed easier.

All in all, I am for this change.

@redblobgames
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the new version somewhat better except it's unclear to me what 'path' means. The frontier contains nodes, right? What is the node 'state'? Where do we get the "previous path to child's state"? (I haven't seen the text of the 4e book yet so maybe it's explained there)

Some A* code will not “add child to frontier” if there was a previous path to it, but instead reprioritize the existing node. I don't do this in practice but it is a reasonable way of handling the situation so that you don't re-evaluate the node with the worse path after you've evaluated the node with the better path.

@redblobgames
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I see, reading the “code like” version you added after this commit answers some of my questions. Those two versions are pretty similar and seem like they could be merged. I was confused in part because in my code I put the state into the frontier and store the path externally.

The c is returned by successors(parent). Is c a state or a path? If it's a state, then c.state doesn't make sense. If it's a path, then c not in reached doesn't make sense, as reached is a map of states. Maybe you mean c.state not in reached and reached[c.state] <-- c and if c.state is a goal. Static typing ftw :)

@antmarakis
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

About the "what is c" thing, I assumed it was actually both a state and a path, and that it is written a bit "fluidly". The successors(parent) method extends the path in all possible ways. That means, it adds a state to the path whenever adding a state is possible. What it returns is both the extended path and the state.

If that is indeed the case, maybe it needs to be written more clearly? Maybe something like for path, state in successors(parent) or assume c is an object and c.path and c.state its properties. That would clear things up. Re-reading the pseudocode I also got a bit confused as to what c is.

Please sign in to comment.