AI-101 · Lesson 3 of 12 · 29 min
Search and problem solving
Long before machine learning dominated AI, researchers built programs that solved problems by searching: trying sequences of actions, looking ahead, and choosing the sequence that reaches a goal. Search is still everywhere: route planners find the fastest way home, logistics software schedules deliveries, game programs choose moves, chip designers place components, and the reasoning of modern AI systems often combines learned intuition with explicit search. This lesson introduces the idea of a search problem, compares blind and informed search, shows how programs play games by looking ahead, and explains why many problems are too large to search exhaustively, which is where heuristics and learning come in.
Problems as state spaces
Many problems can be described as a state space:
- States: the possible situations, such as positions on a map, arrangements of a puzzle or configurations of a game board.
- An initial state: where the problem starts.
- Actions: what can be done in each state, such as moving north, sliding a tile or placing a piece.
- A transition model: what state results from each action.
- A goal test: whether a state solves the problem.
- A cost: how expensive each action is, such as distance, time or money.
A solution is a sequence of actions leading from the initial state to a goal; an optimal solution has the lowest total cost. Once a problem is described this way, the same general algorithms can solve route planning, puzzles and scheduling, which is part of what made search such a powerful early idea in AI.
Blind search: breadth-first and depth-first
The simplest search algorithms know nothing about where the goal is. They differ only in the order in which they explore states:
- Breadth-first search (BFS) explores all states one step away, then all states two steps away, and so on. It always finds the shortest solution (in number of steps) but must remember every state at the current depth, which can need enormous memory.
- Depth-first search (DFS) follows one path as deep as possible before backing up and trying another. It needs little memory but can wander down very long or endless paths and does not guarantee the shortest solution.
- Uniform-cost search (the idea behind Dijkstra's algorithm) expands the cheapest path found so far, so it finds the lowest-cost solution when actions have different costs, such as roads of different lengths.
The difficulty is scale. If each state has \(b\) possible actions (the branching factor) and the solution is \(d\) steps away, a blind search may examine about \(b^d\) states. With 10 actions per state and a 10-step solution, that is 10 billion states.
Worked problem
How fast search spaces grow
A puzzle has a branching factor of 3 and solutions 20 moves deep. A computer examines 100 million states per second. How long might a blind search take? What if the depth is 30?
Show solution
- Depth 20: \(3^{20} = 3.5 \times 10^{9}\) states; \(3.5 \times 10^9 / 10^8 = 35\) seconds.
- Depth 30: \(3^{30} = 2.1 \times 10^{14}\) states; \(2.1 \times 10^6\) seconds, about 24 days.
Answer: About 35 seconds at depth 20 but about 24 days at depth 30: ten more steps multiply the work by \(3^{10} = 59{,}049\). This exponential growth, often called combinatorial explosion, is the central problem of search.
Informed search and heuristics
Informed search uses extra knowledge to explore promising states first. The knowledge comes as a heuristic: an estimate of how far a state is from the goal. For a route on a map, the straight-line distance to the destination is a natural heuristic; for a sliding puzzle, the number of tiles out of place.
The A* algorithm (pronounced "A star", published in 1968) expands the state with the lowest value of
where \(g(n)\) is the cost already spent to reach state \(n\) and \(h(n)\) is the heuristic estimate of the remaining cost. If the heuristic never overestimates the true remaining cost (it is admissible), A* is guaranteed to find an optimal solution, and the better the heuristic, the fewer states it explores. A* and its descendants are used in route planning, robotics, video game characters and many scheduling problems.
from collections import deque
import heapq
grid = [
"....................",
"....................",
"......#########.....",
"S.............#....G",
"......#########.....",
"....................",
"....................",
]
rows, cols = len(grid), len(grid[0])
start = next((r, c) for r in range(rows) for c in range(cols) if grid[r][c] == "S")
goal = next((r, c) for r in range(rows) for c in range(cols) if grid[r][c] == "G")
def moves(p):
r, c = p
for dr, dc in [(1, 0), (-1, 0), (0, 1), (0, -1)]:
nr, nc = r + dr, c + dc
if 0 <= nr < rows and 0 <= nc < cols and grid[nr][nc] != "#":
yield (nr, nc)
def bfs():
frontier, seen, expanded = deque([(start, 0)]), {start}, 0
while frontier:
p, d = frontier.popleft()
expanded += 1
if p == goal:
return d, expanded
for q in moves(p):
if q not in seen:
seen.add(q)
frontier.append((q, d + 1))
def astar():
h = lambda p: abs(p[0] - goal[0]) + abs(p[1] - goal[1])
frontier, best, expanded = [(h(start), 0, start)], {start: 0}, 0
while frontier:
f, g, p = heapq.heappop(frontier)
expanded += 1
if p == goal:
return g, expanded
for q in moves(p):
if g + 1 < best.get(q, 10 ** 9):
best[q] = g + 1
heapq.heappush(frontier, (g + 1 + h(q), g + 1, q))
open_cells = sum(row.count(".") for row in grid) + 2
print("open cells:", open_cells)
print("breadth-first search: path length %d, cells expanded %d" % bfs())
print("A* with Manhattan distance: path length %d, cells expanded %d" % astar())open cells: 121 breadth-first search: path length 23, cells expanded 121 A* with Manhattan distance: path length 23, cells expanded 81
Both algorithms find the same 23-step path, but A* expands a third fewer cells because its heuristic (the Manhattan distance, counting horizontal and vertical steps to the goal) steers it toward the goal. On larger maps with good heuristics, the savings are often many orders of magnitude.
Worked problem
Is the heuristic admissible?
A delivery robot moves on a grid where each step costs 1 and it can only move horizontally or vertically. Which of these heuristics are admissible: (a) the straight-line distance, (b) the Manhattan distance, (c) twice the Manhattan distance?
Show solution
- The true cost is at least the Manhattan distance, because each step changes one coordinate by 1.
- (a) Straight-line distance is never more than the Manhattan distance, so it never overestimates: admissible.
- (b) Manhattan distance equals the true cost with no obstacles and underestimates with obstacles: admissible.
- (c) Twice the Manhattan distance overestimates: not admissible.
Answer: (a) and (b) are admissible, and (b) is better because it is closer to the true cost, so A* explores fewer states. Heuristic (c) may make the search faster but can miss the optimal path, a trade-off sometimes accepted in practice.
Playing games: minimax
Games add an opponent who chooses moves to beat you. Adversarial search assumes the opponent plays well. The minimax algorithm looks ahead through possible moves for both players: at your turn you choose the move with the highest value, at the opponent's turn you assume they choose the move with the lowest value for you, and at the end of the game (or at a depth limit) positions are scored as wins, losses, draws or estimated values.
lines = ["XO.", ".X.", "O.."]
board = [c for row in lines for c in row]
WINS = [(0, 1, 2), (3, 4, 5), (6, 7, 8), (0, 3, 6), (1, 4, 7), (2, 5, 8), (0, 4, 8), (2, 4, 6)]
count = 0
def winner(b):
for a, c, d in WINS:
if b[a] != "." and b[a] == b[c] == b[d]:
return b[a]
return None
def minimax(b, player):
global count
count += 1
w = winner(b)
if w == "X":
return 1, None
if w == "O":
return -1, None
moves = [i for i, v in enumerate(b) if v == "."]
if not moves:
return 0, None
best = None
for m in moves:
b[m] = player
score, _ = minimax(b, "O" if player == "X" else "X")
b[m] = "."
if best is None or (player == "X" and score > best[0]) or (player == "O" and score < best[0]):
best = (score, m)
return best
score, move = minimax(board, "O")
print("O to move. Best move: square", move, "(row", move // 3, "col", move % 3, ")")
print("value with perfect play:", {1: "X wins", 0: "draw", -1: "O wins"}[score])
print("positions examined:", count)
count = 0
minimax(["."] * 9, "X")
print("positions examined from an empty board:", count)O to move. Best move: square 8 (row 2 col 2 ) value with perfect play: draw positions examined: 210 positions examined from an empty board: 549946
Even tic-tac-toe has over half a million positions in its full game tree. Chess has an estimated \(10^{120}\) possible games, far too many to search. Game programs therefore search to a limited depth, estimate the value of positions with an evaluation function, and prune branches that cannot affect the decision (alpha-beta pruning, which can cut the work roughly to the square root). IBM's Deep Blue, which beat world champion Garry Kasparov in 1997, searched about 200 million positions per second using hand-crafted evaluation functions.
Combining search with learning
For the game of Go, with roughly 250 legal moves per position and games of about 150 moves, brute-force search was hopeless and hand-written evaluation functions were poor. DeepMind's AlphaGo (2016) combined Monte Carlo tree search, which samples promising lines of play rather than searching all of them, with deep neural networks that learned to suggest good moves and to evaluate positions from millions of games. AlphaZero (2017) then learned chess, shogi and Go from scratch by playing against itself, reaching superhuman strength in each within hours of training on powerful hardware.
The same combination of learned intuition and explicit search appears in modern AI more broadly: reasoning models generate and check multiple lines of thought; code-generating systems test candidate programs; and scientific tools such as AlphaFold and materials discovery systems search vast spaces guided by learned models.
Constraint satisfaction and optimization
Some problems are not about finding a path but about finding an assignment that satisfies constraints: scheduling exams so no student has two at once, assigning frequencies to radio towers, or filling in a Sudoku. Constraint satisfaction algorithms assign values one variable at a time, check constraints immediately, and backtrack when a choice leads to a dead end; techniques such as constraint propagation (eliminating impossible values early) make them very effective in practice.
Other problems seek the best solution among many acceptable ones: the shortest delivery route through 100 stops (the traveling salesperson problem), the cheapest power plant schedule, or the most efficient layout of a factory. When exact search is too expensive, local search methods start from a reasonable solution and improve it step by step (hill climbing), sometimes accepting worse moves to escape local optima (simulated annealing), or evolve a population of solutions (genetic algorithms). These methods rarely guarantee the best answer but often find very good ones quickly.
Worked problem
Routes through many stops
How many different routes visit 10 delivery stops once each and return to the depot? What about 20 stops?
Show solution
- Number of orderings of \(n\) stops: \(n!\); each route and its reverse are equivalent, so \(n!/2\).
- 10 stops: \(10!/2 = 1{,}814{,}400\).
- 20 stops: \(20!/2 = 1.2 \times 10^{18}\).
Answer: About 1.8 million routes for 10 stops, easily checked by computer, but about \(10^{18}\) for 20 stops, beyond brute force. Delivery companies solve problems with hundreds of stops daily using heuristics and optimization methods that find routes within a few percent of the best possible.
What search teaches about AI
Search shows several ideas that recur throughout AI. Representing a problem well is half the work. Exhaustive approaches fail on realistic problems because of exponential growth, so intelligent systems need knowledge, heuristics or learned intuition to focus their effort. And a system can be superhuman in a narrow, well-defined domain such as chess while having no understanding of anything outside it. Lesson 4 turns to another classic approach: representing knowledge explicitly and reasoning with it, including under uncertainty.
Common mistakes
MistakeAssuming faster computers make brute-force search practical.
InsteadExponential growth outpaces hardware; use heuristics, pruning or learning.
MistakeUsing a heuristic that overestimates and expecting optimal answers.
InsteadA* is guaranteed optimal only with admissible heuristics.
MistakeThinking game-playing programs search every possibility.
InsteadThey search selectively with evaluation functions and pruning.
MistakeTreating search and learning as rivals.
InsteadModern systems often combine learned intuition with search.
MistakeIgnoring how a problem is represented.
InsteadA good state representation can make a hard problem easy.
Summary and key terms
- Search problems have states, actions, transitions, a goal test and costs.
- Blind search (BFS, DFS, uniform cost) explores without knowing where the goal is.
- Search spaces grow as b^d, causing combinatorial explosion.
- A* uses f = g + h; with an admissible heuristic it finds optimal solutions efficiently.
- Minimax plays games by assuming an optimal opponent; pruning and evaluation functions make it practical.
- AlphaGo and AlphaZero combined tree search with neural networks.
- Constraint satisfaction and local search handle scheduling and optimization problems.
- State space
- The set of all situations a problem can be in.
- Branching factor
- The number of actions available in a typical state.
- Breadth-first search
- Exploring all states at one depth before going deeper.
- Depth-first search
- Following one path as deep as possible before backtracking.
- Heuristic
- An estimate of the remaining cost to reach a goal.
- Admissible heuristic
- A heuristic that never overestimates the true remaining cost.
- A* search
- Informed search that expands states with the lowest g + h.
- Minimax
- Game search assuming each player chooses their best move.
- Alpha-beta pruning
- Skipping game-tree branches that cannot affect the decision.
- Local search
- Improving a complete solution step by step.
Practice problems
Work each problem on paper first, then reveal the answer to check.
List the parts of a search problem.
Show answer
States, initial state, actions, transition model, goal test, costs.
Branching factor 4, depth 12. About how many states?
Show answer
4¹² ≈ 16.8 million.
Which blind search guarantees the fewest steps?
Show answer
Breadth-first search.
Why does DFS use less memory than BFS?
Show answer
It stores only the current path and siblings, not a whole level.
Write the A* evaluation function and explain its parts.
Show answer
f = g + h: cost so far plus estimated cost remaining.
Is straight-line distance admissible for road travel?
Show answer
Yes; roads are never shorter than the straight line.
What does minimax assume about the opponent?
Show answer
That the opponent always chooses its best move.
Why can't chess programs search the whole game tree?
Show answer
There are about 10¹²⁰ possible games.
What two techniques did AlphaGo combine?
Show answer
Monte Carlo tree search and deep neural networks.
Routes through 8 stops returning to the depot?
Show answer
8!/2 = 20,160.
Lesson quiz · 5 questions
Score 60% or more to complete this lesson.
Create a free account to save your progress, take quizzes and earn a certificate.