1.2.38 · D4 · HinglishIntroduction to Programming (Python)

ExercisesClassic recursion — factorial, Fibonacci, binary search

3,105 words14 min read↑ Read in English

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.

Figure — Classic recursion — factorial, Fibonacci, binary search
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 case

Trace: . 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.

Figure — Classic recursion — factorial, Fibonacci, binary search
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 sum

Recurrence: , 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 karo

Trace:

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 hai

Trace:

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 cache fib(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 (jaise def add(x, box=[])) calls ke beech results leak karwata hai — ek classic bug.
  • Safe fix: memo=None use karo aur andar if memo is None: memo = {} set karo, jisse har top-level call ko fresh cache mile jabki recursion mein neeche share hoti rahe. Key insight: memoized fib ki 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 * half

Trace :

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