1.2 A* and Heuristics: The Estimate Must Speak the Same Language as the Cost Model
Uniform-cost search can find the cheapest route while expanding in every direction. A lower bound on the remaining cost helps prioritize promising states. A sense of direction should save computation without quietly spending the optimality guarantee.
A* does not arbitrarily prune bad paths. Instead, it reorders expansion using
Learning Objectives
- Distinguish between cumulative cost
, heuristic , and priority ; - Determine whether a heuristic is admissible and consistent;
- Implement A* that handles outdated queue entries and reopens nodes;
- Evaluate the heuristic benefit and the trade-offs of optimality in Weighted A*.
1. The Three Components of A*
: the known lowest cost from the start node to node ; : an estimated remaining cost from node to the goal; : an estimated total cost of the path through node to the goal.
When
The heuristic must use the same units as edge costs. For instance, if
2. Admissible and Consistent
Admissible:
meaning the heuristic does not overestimate the true minimum remaining cost.
Consistent (or monotone): for every edge
Consistency resembles the triangle inequality and makes
3. An A* implementation that supports reopening
This Python 3 fragment (partial) reuses reconstruct from the previous lesson and needs successor, goal, and heuristic functions. States must be hashable and cannot be None; costs must be finite and non-negative. The termination guarantee also requires a zero heuristic at every goal and a finite graph or suitable completeness conditions.
import heapq
from itertools import count
def a_star(edges, start, is_goal, heuristic):
tie = count()
best_g = {start: 0.0}
parent = {start: None}
frontier = [(heuristic(start), 0.0, next(tie), start)]
while frontier:
_, g, _, state = heapq.heappop(frontier)
if g != best_g.get(state):
continue # Better path has been enqueued, old entry invalidated
if is_goal(state):
return reconstruct(parent, state), g
for next_state, step_cost in edges(state):
if step_cost < 0:
raise ValueError("A* requires non-negative edge costs")
new_g = g + step_cost
if new_g < best_g.get(next_state, float("inf")):
best_g[next_state] = new_g
parent[next_state] = state
new_f = new_g + heuristic(next_state)
heapq.heappush(
frontier,
(new_f, new_g, next(tie), next_state),
)
return NoneEach queue entry stores the value of g when it was enqueued. The search compares that value with best_g when popping and skips stale entries. There is no permanent closed set, so states are reprocessed if a lower cost is discovered, making it suitable for inconsistent heuristics.
Terminate when a goal is popped with the current minimum f, rather than when it is first generated. Generation can occur before a cheaper path is expanded.
A small reopening example uses S→A=3, S→B=1, B→A=1, and A→G=3, with h(S)=0, h(A)=0, h(B)=4, and h(G)=0. All estimates are admissible, but B→A violates consistency. A is expanded at g=3, then reopened through B at g=2, producing cost 5. Permanently closing A leaves the cost-6 route. See Berkeley CS 188 on A* for admissibility and reopening.
4. Grid Heuristics Must Match the Actions
When movement is allowed in four directions, each step has unit cost, and there are no teleportations, the Manhattan distance:
provides a natural lower bound. When diagonal moves are allowed at unit cost, Manhattan overestimates the true cost; in such cases, Chebyshev distance may be considered. When diagonal movement costs
Obstacles typically make the actual path longer, but they do not violate these lower bounds. However, teleportation, varying terrain costs, or negative costs can alter the validity of these assumptions. A heuristic is not automatically valid simply because its name includes "distance."
5. Constructing Heuristics from Relaxed Problems
Remove some constraints from the original problem to obtain a relaxed version that is easier to solve; the optimal cost of this relaxed problem typically serves as a lower bound for the original problem.
Examples:
- Ignore walls, resulting in grid geometric distance;
- In the sliding puzzle, allow tiles to pass through one another, yielding the number of misplaced tiles or the Manhattan sum;
- In route planning, ignore traffic congestion and use theoretical minimum travel time.
Pattern databases can also be precomputed. However, the heuristic computation itself incurs cost: running an expensive optimizer at each node may be slower than simply expanding more nodes.
6. Comparing Heuristic Dominance
If both heuristics are admissible and
Evaluation should report:
- Final path cost;
- Number of nodes expanded, generated, or reopened;
- Peak frontier size;
- Heuristic computation time;
- Total runtime and memory usage.
Comparing only path-finding speed may incorrectly classify a heuristic that returns suboptimal solutions as "better."
7. Inadmissible Heuristics and Weighted A*
Weighted A* uses:
It leans more toward the goal, often reducing search space, but typically sacrifices strict optimality. Under specific conditions, it can provide bounded-suboptimal guarantees, however, such guarantees must be explicitly tied to the algorithm version and assumptions; one cannot simply state "inadmissible heuristics are faster" in general.
In real-time systems, anytime algorithms can be employed: a feasible solution is returned immediately, and the solution bound is progressively tightened over time. The choice depends on the allowable suboptimality ratio, response time constraints, and the cost of failure.
8. Tie-breaking Affects Actual Cost
Many nodes may have the same f. Breaking ties by choosing the larger g, the smaller h, or insertion order does not alter the optimal cost under satisfying constraints, but it significantly changes the number of expansions and the resulting search paths. It is essential to ensure that the heap maintains a stable tie counter even when state objects are not comparable.
Common Misconceptions
- A* will prune all suboptimal directions: It primarily adjusts the priority of expansions, not eliminates them outright.
- Manhattan or straight-line heuristics are always acceptable: This depends entirely on the action and cost model.
- You can return to the goal as soon as it's first encountered: At generation time, it's not guaranteed that the
gvalue is minimal. - Having a visited set makes the algorithm more efficient: Permanent closure can cause inconsistent heuristics to miss better paths.
Exercise
- Determine whether Manhattan distance is admissible for four-directional, eight-directional, and teleportation grids.
- Construct an admissible but inconsistent heuristic and observe the effect on node re-expansion.
- Compare the number of nodes expanded and heuristic computation time for
, Manhattan distance, and stronger lower bounds. - Run Weighted A* with different weights
, and plot curves of execution time versus suboptimality ratio.
Summary
The guarantees of A* stem from its lower-bound property and correct graph-search implementation, not from the heuristic appearing "closer to the goal." The heuristic must align with state, action, and cost representations, and must evaluate paths using both total cost and expansion cost.
The next lesson replaces the search space with a decision tree of opponents' responses: when the environment reacts to your actions, the goal becomes a dynamic path rather than a fixed one.