Interactive companion · Chapter 4 · Informed search algorithms

Informed Search Lab

Informed search uses an estimate of how far the goal still is to decide where to look next.

Based on Chapter 4, “Informed search algorithms,” in Alaa Khamis, Optimization Algorithms: AI techniques for design, planning, and control problems, Manning Publications, 2024 (ISBN 978-1633438835).

Dijkstra’s algorithm (blind) and A* (informed) on the same route.

Part 1 · Section 4.1

Informed search

A heuristic function h(n) estimates the remaining cost from node n and steers the search.

Figure 4.1 in Section 4.1 maps the whole family of informed search algorithms.

Part 2 · Section 4.2

Minimum spanning trees

A minimum spanning tree (MST) connects every vertex of a graph, with no cycles, at the smallest total edge weight.

The muddy city problem

Which muddy roads should be paved to connect every place at least cost? Pick an algorithm and press Next step to grow the tree.

Algorithm 4.1 and Listing 4.1 in Section 4.2 walk through Kruskal’s algorithm on the muddy city step by step.

Pave the muddy city

Listing 4.1

Campus cables (Listing 4.2)

Kruskal’s algorithm finds the least cable that reaches every campus intersection.

Listing 4.2 builds this MST on the full 1 km drivable network.

MST of the campus streets

Part 3 · Section 4.3.1

Hill climbing

Hill climbing moves to a better neighbor until none is better: fast and frugal, but easily stuck on a local peak, plateau or ridge.

Algorithm 4.2 and Figure 4.13 in Section 4.3.1 compare simple, steepest-ascent, stochastic and random-restart hill climbing and contrast them with gradient descent.

Local and global peaks (Figure 4.12)

Click the curve to pick a start, then press Climb: which peak you reach depends on where you begin.

Climb in the fog

Click the curve to choose a start

8-puzzle: hill climbing vs. A*

Choose a board and press Compare: hill climbing chases fewer misplaced tiles and may get stuck; A* ranks boards by f = g + h.

Figure 4.17 and Listing 4.3 trace hill climbing on the book’s 8-puzzle move by move.

Heuristics on the 8-puzzle

Goal: blank top left, then 1 to 8

Part 4 · Sections 4.3.2 and 4.3.3

Beam search, best-first search and A*

Beam search keeps the w best routes per level; greedy best-first follows h(n); A* ranks by f = g + h and is optimal when h is admissible (never overestimates). Step through each below.

Sections 4.3.2 and 4.3.3 develop A*, weighted A* (equations 4.1 and 4.2) and Listings 4.4 and 4.5 on this graph.

8 points of interest (Figures 4.14 to 4.22)

From S to G

Part 5 · Section 4.3.3

A* on a grid

Run each search (or Compare all) and watch how far it spreads; click to edit walls.

Blind vs. informed on a grid

Click to edit walls
startgoalexpandedfrontierpath

Haversine distance

On a map, the heuristic is the great-circle distance. Enter two points to compute it.

Equation 4.3 gives the haversine formula behind this calculator.

Great-circle distance

Part 6 · Section 4.3.4

Contraction hierarchies

Contraction hierarchies (CH) contract nodes, adding shortcuts, then search only up the hierarchy from both ends. Press Next step to watch.

Section 4.3.4 and Figures 4.23 to 4.46 explain witness paths, node ordering and why CH is so fast.

CH step by step (Figures 4.29 to 4.46)

Query from 1 to 5
shortcut / forward searchbackward searchreached by bothmeeting node and router: rank (contraction order)

Part 7 · Section 4.4

Campus routing with informed search

Route from the King Edward VII statue to the Bahen Centre: pick an algorithm and press Run, or Compare all.

Section 4.4 and Listings 4.7 to 4.10 solve this problem with each algorithm and compare them in Table 4.1.

Campus routing lab

Statue → Bahen Centre
exploredexplored backward (CH)other candidate routesroute

Part 8 · Code

Python in Google Colab

Open the book’s notebooks in Colab, or the companion notebook with the from-scratch code below.

Notebooks

From-scratch implementations

# Minimum spanning trees for the muddy city problem (Section 4.2): Kruskal, Prim and Borůvka
import heapq

EDGES = [("A", "B", 10), ("A", "C", 5), ("A", "D", 3), ("A", "E", 4), ("B", "C", 2), ("B", "F", 6), ("C", "E", 11),
         ("C", "F", 3), ("C", "G", 2), ("D", "E", 3), ("D", "H", 5), ("E", "G", 5), ("E", "H", 1), ("F", "G", 2),
         ("F", "I", 7), ("F", "J", 13), ("G", "H", 4), ("G", "I", 3), ("H", "I", 6), ("I", "J", 2)]   # Listing 4.1
NODES = sorted({n for e in EDGES for n in e[:2]})


class DisjointSet:
    def __init__(self, items):
        self.parent = {i: i for i in items}

    def find(self, x):
        while self.parent[x] != x:
            self.parent[x] = self.parent[self.parent[x]]
            x = self.parent[x]
        return x

    def union(self, a, b):
        ra, rb = self.find(a), self.find(b)
        if ra == rb:
            return False                    # same component: the edge would close a cycle
        self.parent[ra] = rb
        return True


def kruskal(nodes, edges):
    """Algorithm 4.1: add edges by increasing weight unless they form a cycle."""
    ds, tree = DisjointSet(nodes), []
    for u, v, w in sorted(edges, key=lambda e: e[2]):
        if ds.union(u, v):
            tree.append((u, v, w))
            print(f"add {u}-{v} ({w})")
        else:
            print(f"skip {u}-{v} ({w}): cycle")
        if len(tree) == len(nodes) - 1:
            break
    return tree


def prim(nodes, edges, root="A"):
    """Jarník-Prim: grow one tree from the root, always taking the cheapest edge that leaves it."""
    adj = {n: [] for n in nodes}
    for u, v, w in edges:
        adj[u].append((w, u, v)); adj[v].append((w, v, u))
    inside, heap, tree = {root}, list(adj[root]), []
    heapq.heapify(heap)
    while heap and len(inside) < len(nodes):
        w, u, v = heapq.heappop(heap)
        if v in inside:
            continue
        inside.add(v); tree.append((u, v, w))
        for e in adj[v]:
            if e[2] not in inside:
                heapq.heappush(heap, e)
    return tree


def boruvka(nodes, edges):
    """Borůvka: every component picks its cheapest outgoing edge; merge; repeat."""
    ds, tree = DisjointSet(nodes), set()
    while len(tree) < len(nodes) - 1:
        cheapest = {}
        for u, v, w in edges:
            ru, rv = ds.find(u), ds.find(v)
            if ru == rv:
                continue
            for r in (ru, rv):
                if r not in cheapest or w < cheapest[r][2]:
                    cheapest[r] = (u, v, w)
        for u, v, w in cheapest.values():
            if ds.union(u, v):
                tree.add((u, v, w))
    return list(tree)


T = kruskal(NODES, EDGES)
print("Kruskal:", sum(w for *_, w in T))          # total cost 22
print("Prim:   ", sum(w for *_, w in prim(NODES, EDGES)))
print("Borůvka:", sum(w for *_, w in boruvka(NODES, EDGES)))

To run: open the companion notebook in Colab and press Shift+Enter in each cell, or paste a tab’s code into any notebook.

Part 9 · Summary

What to remember

Heuristics

An estimate of the remaining cost steers the search toward the goal.

Spanning trees

Kruskal, Prim and Borůvka greedily connect every node at minimum cost.

Greedy vs. A*

Greedy methods are fast but can miss the optimum; A* with an admissible heuristic cannot.

Hierarchies

Contraction hierarchies trade heavy preprocessing for very fast exact queries.

References: J. B. Kruskal, 1956. P. E. Hart, N. J. Nilsson and B. Raphael, “A formal basis for the heuristic determination of minimum cost paths,” 1968. R. Geisberger, P. Sanders, D. Schultes and D. Delling, “Contraction hierarchies: Faster and simpler hierarchical routing in road networks,” 2008. P. Sanders and D. Schultes, “Highway hierarchies hasten exact shortest path queries,” 2005. A. Khamis, Optimization Algorithms: AI techniques for design, planning, and control problems, Manning, 2024, Chapter 4. Road data © OpenStreetMap contributors (ODbL), via OSMnx.

Part 10 · Quiz

Check your understanding

Two quick questions on Chapter 4. Pick an answer to check it.

Q1. A* ranks nodes by f(n) = g(n) + h(n). Under which condition is A* guaranteed to return an optimal path?

Q2. Why can hill climbing fail on the 8-puzzle while A* with the same misplaced-tiles heuristic solves it?

For more exercises with full solutions, see Appendix C, “Exercises and solutions,” in Optimization Algorithms.