60 km of wasted diesel
Last week's BFS sent us Dhaka β Chandpur β Feni β Chattogram β Cox's Bazar: 465 km. Via Cumilla it is 405 km.
Same number of hops, so BFS could not tell them apart. An ambulance, a Pathao rider or a cargo truck pays for kilometres (or minutes, or taka), not for hops.
Today's question: if roads have different costs, which city should a search look at next?
Uniform-cost search: cheapest first
function UNIFORM-COST-SEARCH(problem) frontier β priority queue ordered by g, holding sβ (g = 0) explored β β loop if frontier is empty: return failure node β POP-LOWEST-g(frontier) if IS-GOAL(node.state): return SOLUTION(node) # test on POP add node.state to explored for each child of node: if child.state not in explored or frontier: insert child else if it is in frontier with a higher g: replace that entry
g(n) = cost of the path from the start to n. Adapted from Russell & Norvig's uniform-cost search.
Two changes from BFS
1. The FIFO queue becomes a priority queue on g(n).
2. The goal test moves to when a node is popped, not when it is generated.
(Plus: if a cheaper path to a frontier state turns up, keep the cheaper one.)
Edsger Dijkstra designed his shortest-path algorithm in about twenty minutes, sitting at a cafΓ© in Amsterdam in 1956, without pencil and paper. UCS is Dijkstra's algorithm that stops as soon as the goal is popped.
Live lab: UCS on the road to Cox's Bazar
Frontier
priority queue: pop lowest gExplored set
never expand these againinitStart: frontier = [Dhaka]
Worked example: UCS by hand
Given
Last week's map (km approximate). Start Dhaka, goal Cox's Bazar. Graph search, ties broken alphabetically.
Find
The expansion order, the frontier after each expansion, and the route returned.
Method: uniform-cost graph search (Russell & Norvig)
Solution
Predict the answer first. Then reveal one step at a time.
Why cheapest-first is optimal
- With step costs β₯ 0, UCS pops nodes in non-decreasing order of g. Popping never goes back to something cheaper.
- When the goal is popped with cost C, every node still on the frontier has g β₯ C.
- Any other path to the goal must pass through one of those frontier nodes, so it costs at least C. Nothing cheaper exists: C is optimal.
Test the goal when it is generated and step 2 breaks: you stop with an upper bound, not the minimum. In the next problem that mistake returns a 12-unit path when a 7-unit one exists.
Exam line
"UCS is optimal because it expands nodes in order of increasing path cost, so the first time it selects a goal node for expansion, that node's path is the cheapest."
If every step costs the same, ordering by g is ordering by depth: UCS behaves exactly like BFS.
UCS: complete, optimal, and what it costs
| Question | Uniform-cost search |
|---|---|
| Complete? | Yes, if b is finite and every step costs at least some Ξ΅ > 0 |
| Optimal? | Yes, for any step costs β₯ 0 (with Ξ΅ > 0 for completeness) |
| Time and space | O(b1+βCβ/Ξ΅β) |
C* = cost of the optimal solution, Ξ΅ = the smallest step cost. C*/Ξ΅ is the deepest the optimal path could be, if every step were as cheap as possible.
Why Ξ΅ > 0? With zero-cost steps (a free U-turn, a NoOp action) tree-search UCS can loop forever on a path whose cost never grows.
When all steps cost the same, C*/Ξ΅ = d, and the bound becomes bd+1: BFS with the goal test on pop.
Your turn Β· 1: UCS, and the road that lies
Run UCS graph search from S to G. Show the sorted frontier after each expansion and the path returned.
Then: what would you return if you tested for the goal as soon as it was generated?
Work it out first.
Press Solve when the class has an answer.
Critical thinking Β· when cheapest-first lies
A map app adds a negative cost for roads that pass a sponsor's shop (β10 'reward'). Does UCS still return the cheapest route?
Argue it in pairs for two minutes. There may be more than one defensible answer.
The hospital-robot problem has a free action Wait (cost 0). What can go wrong for UCS, and how do you fix it?
Argue it in pairs for two minutes. There may be more than one defensible answer.
Depth-limited search: DFS on a leash
function DLS(node, β) if IS-GOAL(node.state): return SOLUTION(node) if β = 0: return cutoff # the leash cut β false for each child of node: result β DLS(child, β β 1) if result = cutoff: cut β true else if result β failure: return result return cut ? cutoff : failure
DFS that treats nodes at depth β as if they had no children. Infinite paths and endless loops are no longer a problem.
Two kinds of "no"
failure: searched everything, there is no solution.
cutoff: no solution within β; one may exist deeper.
Choosing β: use what you know. Every city on our map reaches every other in at most 5 roads (Sylhet to Bandarban is the longest), so β = 5 can never miss a route. That number is the map's diameter.
Live lab: DLS and IDS on a tree
Stack (current path)
root on the left, top on the rightβ=0Round β = 0: visit A (depth 0): at the limit, children not expanded (cutoff).
Worked example: IDS round by round
Given
The tree below, goal G. Children are visited left to right; goal test when a node is visited.
Find
The visit order in each round of IDS, the path found, and the total visits compared with BFS.
Method: iterative deepening = DLS with β = 0, 1, 2, β¦ (Russell & Norvig)
Solution
Predict the answer first. Then reveal one step at a time.
Isn't repeating everything wasteful?
Level i is regenerated once for every round from i to d, so d β i + 1 times. Counting generated nodes (root excluded):
| b = 10, d = 5 | Nodes generated |
|---|---|
| BFS: 10 + 100 + 1,000 + 10,000 + 100,000 | 111,110 |
| IDS: 50 + 400 + 3,000 + 20,000 + 100,000 | 123,450 (+11 %) |
Why so little?
Most of a tree is its bottom level. With b = 10 the last level is 90 % of all nodes, and IDS generates it only once. For large d the ratio tends to b/(b β 1).
| b | IDS extra work |
|---|---|
| 2 | β 100 % |
| 3 | β 50 % |
| 10 | β 11 % |
Memory: one path plus its siblings, O(bd). For b = 10, d = 5: about 50 nodes instead of 100,000.
Your turn Β· 2: count the cost of deepening
A uniform tree has branching factor b = 3, and the only goal is the right-most node at depth d = 4 (worst case for both).
(a) How many nodes do BFS and IDS generate (do not count the root)? (b) What is the extra work as a percentage? (c) Compare the largest number of nodes each must keep in memory.
Work it out first.
Press Solve when the class has an answer.
Bidirectional search: dig from both ends
Run one search forward from the start and one backward from the goal; stop when the two frontiers meet.
- Needs the goal as an explicit state (not just a test) and actions you can run backwards.
- Memory O(bd/2): one of the frontiers must be stored to check the meeting.
"Six degrees of separation": LinkedIn finds how you are connected to someone by expanding your contacts and theirs at the same time, and looking for the overlap.
Checkmate in chess is a test (millions of positions pass it), not one state. Searching backward from "all checkmates" is not practical.
The whole uninformed family, one table
| Criterion | BFS | UCS | DFS | DLS | IDS | Bidirectional |
|---|---|---|---|---|---|---|
| Complete? | Yesa | Yesa,b | No | No | Yesa | Yesa,d |
| Optimal? | Yesc | Yes | No | No | Yesc | Yesc,d |
| Time | O(bd) | O(b1+βC*/Ξ΅β) | O(bm) | O(bβ) | O(bd) | O(bd/2) |
| Space | O(bd) | O(b1+βC*/Ξ΅β) | O(bm) | O(bβ) | O(bd) | O(bd/2) |
a if b is finite Β· b if every step cost β₯ Ξ΅ > 0 Β· c if all step costs are equal Β· d if both directions use BFS. b = branching factor, d = depth of the shallowest solution, m = maximum depth, β = depth limit, C* = optimal cost.
Read the IDS column: BFS's guarantees with DFS's memory. That is why it is the default uninformed search when the solution depth is unknown.
Critical thinking Β· which search fits in your phone?
A route planner on a phone has 4 GB free memory. The problem has b = 10 and the solution is at depth d = 12. Each node takes 1 KB; the phone generates 10βΆ nodes per second.
Can BFS do it? Can IDS? What would you tell the app's developer?
Work it out first.
Press Solve when the class has an answer.
Real world: deepening in chess, cheapest-first in networks
Engines such as Stockfish search iteratively deeper: depth 1, 2, 3, β¦ until the clock runs out. Each finished round gives a best move, so the engine always has an answer ready. That is the "anytime" property, and why IDS beats a single deep DFS under time pressure.
Routers running OSPF use Dijkstra's algorithm (UCS without an early stop) to compute the cheapest path to every other router, with link costs set by bandwidth. Your packet to a server in Singapore followed it.
An ambulance dispatcher in Dhaka uses UCS with travel time as cost. At 5 PM the costs change every minute. What goes wrong, and what would you do?
Argue it in pairs for two minutes. There may be more than one defensible answer.
Pop quiz
Question 1 / 5
Uniform-cost search always expands the frontier node with the lowestβ¦
Three things to remember
UCS pops the lowest g(n) and tests the goal on pop. Optimal for costs β₯ 0; complete if every step β₯ Ξ΅ > 0.
DLS is DFS with a depth limit β: cutoff (maybe deeper) β failure (none at all). Complete only if β β₯ d.
IDS runs DLS for β = 0, 1, 2, β¦: complete and optimal like BFS (equal costs), O(bd) memory like DFS, β 11 % extra work for b = 10.
Use the lab's binary tree (A at the root, HβO the leaves, children left to right) with goal K. Write the IDS visit order for every round, count the total visits and compare with BFS. Check yourself with the sim.
Class Test 1 is tomorrow, last 20 minutes: problem formulation, BFS, DFS, UCS, DLS, IDS (traces and the comparison table) and the basics of local search. Bring a pen and a calculator.
Work it out first.
Press Solve when the class has an answer.