🔄 Depth-First Iterative Deepening (DFID)
1143 words
6 min read
Visual companion
Python
Type and operator map
Python Week 1: the first filter for runtime behavior
View
Revision summary
What this note is really saying
Short form
# 🔄 Depth-First Iterative Deepening (DFID) ## 1. 🎯 Learning Objectives By the end of this topic, you will be able to: - Explain why DFID combines the best of DFS (linear space) and BFS (optimality) - Trace DFID on a small graph showing each depth-limited DFS pass - Calculate the DFID overhead factor $b/(b-1)$ comp...

🔄 Depth-First Iterative Deepening (DFID)
1. 🎯 Learning Objectives
By the end of this topic, you will be able to:
- Explain why DFID combines the best of DFS (linear space) and BFS (optimality)
- Trace DFID on a small graph showing each depth-limited DFS pass
- Calculate the DFID overhead factor b/(b−1) compared to BFS
- Determine when DFID is preferred over both DFS and BFS
2. 📋 Prerequisites
| Prerequisite | Course | Why It Matters |
|---|---|---|
| DFS | W2 T2 | DFID calls depth-limited DFS repeatedly |
| BFS | W2 T3 | Understanding optimality that DFID matches |
| State spaces | W2 T1 | Understanding tree structure |
3. 📖 Core Content
3.1 Intuition: The Best of Both Worlds
DFID (also called Iterative Deepening Search — IDS) combines DFS's memory efficiency with BFS's optimality. The idea is simple yet clever:
- Run DFS with a depth limit of 0 (explore only root)
- If no solution, run DFS with depth limit 1
- If no solution, run DFS with depth limit 2
- Continue increasing depth limit until a solution is found Each iteration starts over from scratch, but each successive DFS goes one level deeper. This seems wasteful — we keep re-exploring the same nodes — but the overhead is surprisingly small for large branching factors.
3.2 The DFID Algorithm
textDFID(initial_state, goal_test, move_gen, max_depth=infinity): for depth_limit = 0 to max_depth: result = DepthLimitedDFS(initial_state, 0, depth_limit, goal_test, move_gen) if result == CUTOFF: continue // Try next depth if result == FAILURE and depth_limit == max_depth: return NO_SOLUTION if result is a solution path: return result DepthLimitedDFS(state, depth, limit, goal_test, move_gen): if goal_test(state): return [state] // Goal found if depth == limit: return CUTOFF // Hit depth bound cutoff_occurred = False for child in move_gen(state): result = DepthLimitedDFS(child, depth + 1, limit, goal_test, move_gen) if result == CUTOFF: cutoff_occurred = True elif result != FAILURE: return [state] + result if cutoff_occurred: return CUTOFF return FAILURE
3.3 Worked Example: DFID on a Tree
Consider a binary tree with goal at depth 3 (goal = leaf in rightmost branch).
(Diagram)
Iteration 1: depth_limit = 0 DFS from A, limit 0. A not goal, depth = limit → CUTOFF. Nodes visited: A (1 node)
Iteration 2: depth_limit = 1 DFS from A, limit 1. A not goal, depth (0) < limit (1). Expand children:
- Go to B, depth 1 = limit → CUTOFF
- Go to C, depth 1 = limit → CUTOFF Nodes visited: A, B, C (3 nodes) Iteration 3: depth_limit = 2 DFS from A, limit 2. Explore:
- A → B (depth 1, not goal, depth < limit). Expand B's children:
- A → B → D (depth 2 = limit). Not goal → CUTOFF
- A → B → E (depth 2 = limit). Not goal → CUTOFF
- A → C (depth 1). Expand C's children:
- A → C → F (depth 2 = limit). Not goal → CUTOFF
- A → C → G (depth 2 = limit). Not goal → CUTOFF Nodes visited: A, B, D, E, C, F, G (7 nodes) Iteration 4: depth_limit = 3 DFS from A, limit 3. Explore:
- A → B → D → H... (depth 3 = limit, H = goal!) Nodes visited until goal: A, B, D, H (assuming goal on leftmost path) Total nodes visited across all iterations: 1 + 3 + 7 + 4 = 15 nodes BFS would visit: A, B, C, D, E, F, G, H = 8 nodes Overhead: 15/8 ≈ 1.875
3.4 DFID Overhead Analysis
The overhead of DFID comes from re-exploring nodes at each iteration. But:
Theorem: DFID visits at most b/(b−1) times more nodes than BFS (for large b).
Proof: Sum of nodes visited by DFID up to depth d:
BFS visits:
For large b:
Examples:
- b = 2: DFID overhead ≈ 2×
- b = 10: DFID overhead ≈ 1.11×
- b = 100: DFID overhead ≈ 1.01× Key insight: When b is large (which is when BFS is most memory-prohibitive), DFID's overhead is minimal.
3.5 DFID Properties
| Property | Value |
|---|---|
| Space complexity | O(bd) — same as DFS |
| Time complexity | O(bd) — same as BFS |
| Complete? | Yes |
| Optimal? | Yes (shortest path) |
| Overhead vs BFS | b/(b−1) |
| When to use | Large state spaces, need optimality, limited memory |
3.6 Algorithm Comparison
| Algorithm | Time | Space | Optimal? | Complete? |
|---|---|---|---|---|
| BFS | O(bd) | O(bd) | Yes | Yes |
| DFS | O(bd) | O(bd) | No | No (tree) |
| DFID | O(bd) | O(bd) | Yes | Yes |
DFID achieves everything BFS does but with DFS's memory footprint!
3.7 When to Use DFID
Good for:
- Large, deep search spaces with limited memory
- Problems where optimality is required
- Problems with unknown solution depth
- Large branching factors (overhead is minimal) Bad for:
- Problems where a solution is shallow but many nodes at shallow depths
- Problems where repeated state checking adds significant overhead
- Real-time systems where predictable response time is needed
4. 📐 Key Formulas / Concepts
| Concept | Formula |
|---|---|
| DFID overhead factor | b/(b−1) for large b |
| DFID space | O(bd) |
| DFID time | O(bd) |
| Total DFID nodes | (d+1)b0+db1+(d−1)b2+...+bd |
5. ⚠️ Common Pitfalls
Pitfall 1: Thinking DFID is Wasteful
The mistake: DFID must be terrible because it re-explores nodes. Correct understanding: The overhead is small (b/(b−1)) for large b. Most nodes are at the deepest level, which is only explored once.
Pitfall 2: Confusing DFID with BFS
The mistake: Thinking DFID stores nodes at each level like BFS. Correct approach: DFID uses DFS's stack-based storage at every iteration. It never stores more than O(bd) nodes.
Pitfall 3: Implementing DFID Without Depth Limit
The mistake: Running standard DFS and expecting iterative deepening behavior. Correct approach: The depth limit is essential. Without it, DFS goes to full depth on the first pass.
6. 📝 Practice Questions
Q1: If b=5 and goal is at depth 3, what is the DFID overhead vs BFS?Answer: Overhead = b/(b-1) = 5/4 = 1.25. DFID visits about 25% more nodes than BFS but uses O(bd) instead of O(b^d) memory. Q2: Why does DFID with large b have minimal overhead?Answer: In a tree with branching factor b, most nodes (about (b-1)/b fraction) are at the deepest level. Since DFID only visits the deepest level once, the overhead from re-exploring shallower levels is proportional to b/(b-1), which approaches 1 for large b. Q3: Trace DFID on a graph with b=2 and goal at depth 2.Answer: L=0: visit root (1 node). L=1: visit root, child1, child2 (3 nodes). L=2: visit root, child1, grandchild1, grandchild2, child2, grandchild3, grandchild4 — until goal found. Total ≈ 10 nodes. BFS would visit ≈ 7 nodes. Overhead ≈ 1.43. Join Discord PreviousBFS (Breadth-First Search)NextBest First Search