Quiz 2
Registry Synced

🔄 Depth-First Iterative Deepening (DFID)

1143 words
6 min read

Reading compass

Now · 1. 🎯 Learning Objectives

🔄 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/(b1)b/(b-1) compared to BFS
  • Determine when DFID is preferred over both DFS and BFS

2. 📋 Prerequisites

PrerequisiteCourseWhy It Matters
DFSW2 T2DFID calls depth-limited DFS repeatedly
BFSW2 T3Understanding optimality that DFID matches
State spacesW2 T1Understanding 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:
  1. Run DFS with a depth limit of 0 (explore only root)
  2. If no solution, run DFS with depth limit 1
  3. If no solution, run DFS with depth limit 2
  4. 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

text
DFID(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/(b1)b/(b-1) times more nodes than BFS (for large b). Proof: Sum of nodes visited by DFID up to depth d:
NDFID=(d+1)b0+(d)b1+(d1)b2+...+(1)bdN_{DFID} = (d+1)b^0 + (d)b^1 + (d-1)b^2 + ... + (1)b^d
BFS visits:
NBFS=b0+b1+...+bd=(bd+11)/(b1)N_{BFS} = b^0 + b^1 + ... + b^d = (b^{d+1} - 1)/(b - 1)
For large b:
NDFID(b/(b1))×NBFSN_{DFID} \approx (b/(b-1)) \times N_{BFS}
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

PropertyValue
Space complexityO(bd)O(bd) — same as DFS
Time complexityO(bd)O(b^d) — same as BFS
Complete?Yes
Optimal?Yes (shortest path)
Overhead vs BFSb/(b1)b/(b-1)
When to useLarge state spaces, need optimality, limited memory

3.6 Algorithm Comparison

AlgorithmTimeSpaceOptimal?Complete?
BFSO(bd)O(b^d)O(bd)O(b^d)YesYes
DFSO(bd)O(b^d)O(bd)O(bd)NoNo (tree)
DFIDO(bd)O(b^d)O(bd)O(bd)YesYes
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

ConceptFormula
DFID overhead factorb/(b1)b/(b-1) for large b
DFID spaceO(bd)O(bd)
DFID timeO(bd)O(b^d)
Total DFID nodes(d+1)b0+db1+(d1)b2+...+bd(d+1)b^0 + db^1 + (d-1)b^2 + ... + b^d

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/(b1)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
Document outline

Keep your place and jump directly to a heading.

Table of Contents
System Normal // Awaiting Context

Intelligence Hub

Navigate the knowledge graph to generate context. The Hub adapts dynamically to surface backlinks, related notes, and metadata insights.