Exercises — Classic recursion — factorial, Fibonacci, binary search
1.2.38 · D4· Coding › Introduction to Programming (Python) › Classic recursion — factorial, Fibonacci, binary search
Shuru karne se pehle, do cheezein jo har recursion mein hoti hain (parent note se), ek baar yaad kar lo:
Level 1 — Recognition
"Kya tum pieces spot kar sakte ho?"
L1.1 — Base case identify karo
Recall Solution
Base case: if n == 0: … return.
Kyun: yahi ek aise branch hai jo countdown ko dobara call kiye bina return karta hai. Ye tab reach hota hai jab input shrink hote hote tak aa jaata hai. Iske bina, countdown(n-1) tak jaata rehta → RecursionError.
L1.2 — Code se recurrence padhna
Recall Solution
kyun? tak ka total hai " khud" ko " tak ke total" ke saath jodna — yahi self-similar structure hai. kyun? Koi bhi number na jodhne par milta hai (empty sum), aur ye chain ko rok deta hai.
L1.3 — Base cases ginaa
Recall Solution
Do base cases: aur . Do kyun? Recurrence do steps peeche jaati hai. Agar sirf anchor karo, toh compute karne par maanga jaayega — floor ke neeche — aur ruk hi nahi payega. Jitne steps recurrence peeche jaaye, utne hi fixed starting values chahiye.
Level 2 — Application
"Kya tum templates chala sakte ho?"
L2.1 — Factorial trace karo
Recall Solution
factorial(4) = 4 * factorial(3)
= 4 * (3 * factorial(2))
= 4 * (3 * (2 * factorial(1)))
= 4 * (3 * (2 * (1 * factorial(0))))
= 4 * (3 * (2 * (1 * 1))) # base case 0! = 1
= 24
Ye order kyun? Har multiplication call stack par wait karti hai jab tak base case factorial(0)=1 return nahi karta, phir products bottom-up resolve hote hain: . Answer: .
L2.2 — Binary search haath se chalao
Picture dekho — har row ek call hai, aur shaded band woh hissa hai jo list mein abhi "alive" hai.

Recall Solution
Indices: 2→0, 4→1, 6→2, 8→3, 10→4, 11→5, 14→6. Start karo lo=0, hi=6 se.
| call | lo | hi | mid | arr[mid] | decision |
|---|---|---|---|---|---|
| 1 | 0 | 6 | 3 | 8 | → right jao, lo=4 |
| 2 | 4 | 6 | 5 | 11 | mila → return 5 |
mid = (0+6)//2 = 3; kyunki hai, poora left half (indices 0–3) phenk diya jaata hai. Agle mein mid = (4+6)//2 = 5, aur arr[5]==11. Answer: index return hota hai.
L2.3 — power(base, n) likho
Recall Solution
Recurrence: ( ka ek factor alag karo), base: (empty product).
def power(b, n):
if n == 0: # base case: b^0 = 1
return 1
return b * power(b, n - 1) # recursive caseTrace: . Answer: .
Level 3 — Analysis
"Kya tum cost aur behaviour ke baare mein reason kar sakte ho?"
L3.1 — Stack kitni deep jaati hai?
Recall Solution
Frames factorial(100), factorial(99), …, factorial(0) ke liye exist karte hain — yani se tak har integer ke liye inclusive: frames.
General formula: factorial(n) depth tak jaati hai. 100 ke liye answer: frames.
Yahi wajah hai ki bahut bade par RecursionError aa sakta hai — Python ka default recursion limit lagbhag frames ka hai.
L3.2 — Naive Fibonacci mein calls gino
Call tree dekho — har circle ek call hai, aur tum simply circles ginte ho.

Recall Solution
Counter ki recurrence: fib(n) khud ke liye ek call, plus uske do children ki saari calls:
Build up karte hain:
Answer: calls. (Ghaur karo ki zyada hai distinct values se — baaki redundant recomputations hain. Dekho Big-O Notation.)
L3.3 — Binary search kitne comparisons karta hai?
Recall Solution
Har comparison remaining range ko half kar deta hai, isliye worst case woh halvings ki sankhya hai jo ko tak shrink karne mein lagti hain: Check: , toh zyada se zyada baar half kar sakte hain. Answer: comparisons. Linear search ke worst case se compare karo — yahi ka fayda hai.
Level 4 — Synthesis
"Kya tum nayi recursions bana sakte ho?"
L4.1 — List ka recursive sum
Recall Solution
Idea: list ka sum hai "uska pehla element" plus "baaki ka sum".
def sum_list(a):
if len(a) == 0: # base case: empty list ka sum 0 hai
return 0
return a[0] + sum_list(a[1:]) # pehla + baaki ka sumRecurrence: , base . Trace: . Answer: .
L4.2 — String ko recursively reverse karo
Recall Solution
Idea: pehle character ko end mein le jao, baaki ko reverse karne ke baad.
def reverse(s):
if len(s) <= 1: # base case: empty ya single char khud ka reverse hai
return s
return reverse(s[1:]) + s[0] # tail reverse karo, phir head append karoTrace:
reverse("cat") = reverse("at") + "c"
= (reverse("t") + "a") + "c"
= ("t" + "a") + "c" # base: "t" reverse hokar "t" hi
= "tac"
Answer: "tac".
L4.3 — Recursive gcd (Euclid's algorithm)
Recall Solution
def gcd(a, b):
if b == 0: # base case: gcd(a,0)=a
return a
return gcd(b, a % b) # chhoti problem: remainder tezi se shrink hota haiTrace:
gcd(48,18) = gcd(18, 48%18) = gcd(18, 12)
= gcd(12, 18%12) = gcd(12, 6)
= gcd(6, 12%6) = gcd(6, 0)
= 6 # base case
Ye rukta kyun hai? Remainder hamesha se strictly chhota hota hai, isliye b har call mein ghatta hai aur zaroori tak pahunchega. Answer: .
Level 5 — Mastery
"Subtle traps aur poori re-derivations."
L5.1 — Mutable default argument bug
Recall Solution
Python mein, ek default argument ek baar create hota hai — jab function define hota hai — har call par nahi. Isliye wahi memo dict un sabhi calls mein share hoti hai jo apni khud ki pass nahi karti.
- Khatara:
fib(50)ki cachefib(30)mein bachi rehti hai. Yahan ye harmless hai (cached Fibonacci values sahi hain aur speed bhi badhti hai), lekin yahi pattern ek list default ke saath (jaisedef add(x, box=[])) calls ke beech results leak karwata hai — ek classic bug. - Safe fix:
memo=Noneuse karo aur andarif memo is None: memo = {}set karo, jisse har top-level call ko fresh cache mile jabki recursion mein neeche share hoti rahe. Key insight: memoizedfibki correctness yahan shared dict ke saath sirf isliye bachi rehti hai kyunki Fibonacci values kabhi badlti nahi; generally is luck par rely mat karo.
L5.2 — Fast exponentiation (divide & conquer)
Recall Solution
Idea (ek Divide and Conquer move): agar even hai, toh — half-power ek baar compute karo, phir square karo. Agar odd hai, toh .
def fast_power(b, n):
if n == 0:
return 1
half = fast_power(b, n // 2)
if n % 2 == 0:
return half * half
return b * half * halfTrace :
3^10 = (3^5)^2
3^5 = 3 * (3^2)^2
3^2 = (3^1)^2
3^1 = 3 * (3^0)^2 = 3*1*1 = 3
Toh , , . Recurrence: — binary search jaise hi shape. Answer: .
L5.3 — Sortedness kyun zaroori hai
Recall Solution
Start karo lo=0, hi=4 se. mid = (0+4)//2 = 2, arr[2] = 5. Kyunki , code assume karta hai ki index 2 ke baaye sab chhota hai aur use phenk deta hai, right mein lo=3 ke saath search karta hai.
call 1: lo=0 hi=4 mid=2 arr[mid]=5 -> 5<7 -> go right lo=3
call 2: lo=3 hi=4 mid=3 arr[mid]=3 -> 3<7 -> go right lo=4
call 3: lo=4 hi=4 mid=4 arr[mid]=7 -> found, return 4
Yahan ittefaaq se mila — lekin ye sirf kismet hai. Agar target = 1 hota (index 1 par), toh pehला step use left mein indices 0–1 ki taraf bhej deta, jahan arr hai [9,1], aur comparisons ke hisaab se galat index ya -1 return ho sakta tha.
Kyun toot jaata hai: halving logic depend karta hai "mid ke baaye ≤ mid ≤ mid ke daaye" par, jo exactly sorted property hai. Ye guarantee hatao aur aadhe ko eliminate karna bejaa ho jaata hai. Fix: pehle sort karo (, dekho Sorting Algorithms) ya linear search use karo.
Recall Ek-line answer key (self-check)
L2.1 24 · L2.2 5 · L2.3 32 · L3.1 101 · L3.2 15 · L3.3 10 · L4.1 14 · L4.2 "tac" · L4.3 6 · L5.2 59049
Connections
- Recursion vs Iteration — yahan har solution ko loop ki tarah rewrite kiya ja sakta hai
- Big-O Notation — L3 poora inhi costs derive karne ke baare mein hai
- Memoization and Dynamic Programming — L5.1 ka caching trap
- Divide and Conquer — L5.2 fast exponentiation aur binary search dono shape share karte hain
- The Call Stack and Stack Frames — L3.1 stack depth
- Sorting Algorithms — L5.3 ka fix