Recursion

Context: FIT1008_MOC · defining an Algorithm in terms of itself · backbone clustering Notation, Accumulator, Auxiliary Function, vs-Iteration, and →Iteration-via-Stack

Quick Revision

  • 🎯 Objective: reduce to smaller subproblems of the same kind until a base case ➔ cost via recurrence, correctness via induction.
  • 📦 Core Components: base + call + convergence + combine | classified by count/route/tail | reshaped by accumulator/auxiliary function.
  • ⚡ Key Constraint: stack frames (no Python TCO) ➔ overflow; removed by an accumulator (forward) or explicit Stack (ADT) (backward).

📝 Core

1. Recursion (Four Components)

  • Core mechanism ➔ a function calls itself on strictly smaller inputs.
  • Four parts ➔ base case(s) + recursive call(s) + convergence + combination.
  • Cost / correctness ➔ cost = recurrence (unroll/recursion tree); correctness = structural induction; no base ⟹ RecursionError.

2. Notation (Classifying)

  • Count ➔ unary / binary / -ary (the branching factor of the call tree).
  • Route ➔ direct vs indirect/mutual.
  • Tail ➔ recursive call’s result is the result (nothing on the way back) ➔ binary ⟹ unless subproblems shrink fast or memoised.

3. Accumulator (Forward-Carry → Tail)

  • Mechanism ➔ extra parameter carries the partial result forward, combining on the way in.
  • Effect ➔ makes the recursion tail-recursive, kills recomputation (Fibonacci ).
  • Boundary ➔ only when result builds forward; memoisation/DP is the general alternative ( space).

4. Auxiliary Function (Driver + Worker)

  • Pattern ➔ private worker carries a converging argument the public type can’t shrink (a LinkList can’t recurse on self).
  • Parameter jobs ➔ converge (current.link) | accumulate | carry context (item, lo/hi).
  • or short-circuit ➔ a free second base case; plumbing hidden behind a clean public signature.

5. Recursion vs Iteration

  • Equivalence ➔ loops vs self-calls are Turing-equivalent; difference = where state lives.
  • Practical cap ➔ TCO-less languages ⟹ iteration needed for large .
  • Conversion seed ➔ the base case is the negation of the loop’s continuation guard.

6. →Iteration via Explicit Stack

  • Mechanism ➔ simulate the run-time stack with an explicit Stack (ADT) — push pending work, pop in a loop.
  • When ➔ general conversion when an accumulator can’t (work builds on the way back).
  • Payoff ➔ same space but on the heap ➔ no fixed call-stack limit.

⚙️ Core Implementation

🔹 Basic vs Accumulator (factorial / Fibonacci)

🔹 Explicit-Stack de-recursification (power)

⚖️ Core Decision Matrix

Recursion shapeConvert viaRecurrence → costExample
Tail / forwardaccumulator → plain loop (no stack)factorial, fib_aux
Non-tail, single callone explicit stack of argumentspower, binary search
Multiple calls (balanced)explicit stack of work itemsMerge Sort
Multiple calls (overlapping)accumulator or memoisation/DPnaive Fibonacci

When It Flips: same time Big-O, but recursion uses space vs iteration's — TCO would erase it (Python/Java lack it). The equivalence is to general iteration; the primitive-recursive / bounded-for fragment can't express Ackermann.

📊 Exam Execution Trace

Manual Execution Trace

power_iter(2, 5) (so ):

Step / StateTrigger Opstntmp Payload
0 (Init)push[5]2
1push[5,2]1
2push[5,2,1]01
3pop 1 (odd)[5,2]
4pop 2 (even)[5]
5pop 5 (odd)[]

Applied Exercise

Problem: Unroll the factorial recurrence to its closed-form cost. Derivation Proof / Hand-Calculation Walkthrough:

Final Extracted Output: unary recursion ⟹ a path of frames ⟹ time and stack space.

🧠 Active Recall