Why Dynamic Programming and When Recursion Alone Fails
📖 Scenario: Imagine you want to find the number of ways to climb a staircase with 5 steps. You can climb either 1 step or 2 steps at a time. This is a common problem where recursion alone can be slow because it repeats the same calculations many times.
🎯 Goal: You will build a simple program to count the ways to climb the stairs using recursion first, then add a helper to remember past results (dynamic programming) to make it faster.
📋 What You'll Learn
Create a recursive function to count ways to climb stairs
Add a cache (memory) to store results of subproblems
Use the cache to avoid repeated calculations
Print the final number of ways to climb 5 steps
💡 Why This Matters
🌍 Real World
Counting ways to do tasks with overlapping steps, like climbing stairs or making change, is common in real life.
💼 Career
Understanding dynamic programming helps solve optimization problems efficiently in software development and technical interviews.
Progress0 / 4 steps
1
Create a recursive function to count ways to climb stairs
Create a function called count_ways that takes an integer n and returns the number of ways to climb n steps. Use recursion with these rules: if n is 0 or 1, return 1; otherwise, return count_ways(n - 1) + count_ways(n - 2).
DSA Rust
Hint
Think about the base cases first: when there are 0 or 1 steps, there is only 1 way to climb. For more steps, add the ways to climb the previous step and two steps before.
2
Add a cache to store results of subproblems
Create a mutable Vec<Option<usize>> called cache with size n + 1 filled with None. This will store results for each step count to avoid repeated calculations.
DSA Rust
Hint
Use a vector of Option<usize> to store results. Check if the value is already computed before calculating.
3
Use the cache in the recursive function to avoid repeated calculations
Modify the count_ways function to take a mutable reference to cache and use it to return cached results if available. Calculate and store the result in cache[n] if not cached.
DSA Rust
Hint
Pass the cache as a mutable reference to the function and store results after computing them.
4
Print the final number of ways to climb 5 steps
Add a println! statement to print the value of ways with the message: "Number of ways to climb 5 steps: {ways}".
DSA Rust
Hint
Use println!("Number of ways to climb 5 steps: {}", ways); to show the result.
Practice
(1/5)
1. Why do we use dynamic programming instead of plain recursion for some problems?
easy
A. Because recursion alone can repeat the same calculations many times, making it slow.
B. Because recursion cannot solve problems with multiple steps.
C. Because dynamic programming uses loops instead of functions.
D. Because recursion always causes errors in programs.
Solution
Step 1: Understand recursion's limitation
Recursion solves problems by calling itself but can repeat the same work many times.
Step 2: How dynamic programming helps
Dynamic programming saves results of subproblems to avoid repeating calculations, making it faster.
Final Answer:
Because recursion alone can repeat the same calculations many times, making it slow. -> Option A
Quick Check:
Recursion repeats work, DP saves results [OK]
Hint: Recursion repeats work; DP remembers results to save time [OK]
2. Which of the following Rust function signatures correctly shows a recursive function for Fibonacci numbers?
easy
A. fn fib(n: u32) -> u32 { loop { n = n + 1; } }
B. fn fib(n: u32) -> u32 { if n <= 1 { n } else { fib(n-1) + fib(n-2) } }
C. fn fib(n: u32) -> u32 { return n * n; }
D. fn fib(n: u32) -> u32 { let mut sum = 0; for i in 0..n { sum += i; } sum }
Solution
Step 1: Identify recursive call pattern
fn fib(n: u32) -> u32 { if n <= 1 { n } else { fib(n-1) + fib(n-2) } } calls fib(n-1) and fib(n-2) inside the function, which is classic recursion for Fibonacci.
Step 2: Check other options for recursion
Options A, C, and D do not call fib recursively; they use loops or simple calculations.
Final Answer:
fn fib(n: u32) -> u32 { if n <= 1 { n } else { fib(n-1) + fib(n-2) } } -> Option B
Quick Check:
Recursive calls in function = fn fib(n: u32) -> u32 { if n <= 1 { n } else { fib(n-1) + fib(n-2) } } [OK]
Hint: Look for function calling itself inside its body [OK]
Common Mistakes:
Choosing loop-based or arithmetic-only functions
Missing the recursive calls in the function body
Confusing loops with recursion
3. What is the output of this Rust code using recursion to calculate Fibonacci of 4?
fn fib(n: u32) -> u32 {
if n <= 1 {
n
} else {
fib(n - 1) + fib(n - 2)
}
}
fn main() {
println!("{}", fib(4));
}