Algorithms

Module 1· 16 min read· 7 Questions·completed

1. Asymptotic Complexity & Recurrences

Big-O, Big-Ω, Big-Θ, small-o/ω notations, Master Theorem (all cases & logarithmic extensions), Substitution Method, and Recursion Trees across C, C++, and Python

NPTEL / GATE CS / UGC NET Foundation Module Exam Weightage: 2–3 Direct Questions on Master Theorem cases, comparing growth rates of functions, and recursion trees.


1. Prerequisites & What You Should Know#

Before studying asymptotic analysis, ensure you understand:

  • Basic Mathematics: Logarithms (log2n\log_2 n), exponents, summations (\sum), and limits.
  • Loops & Recursion: How to count the number of operations a loop or recursive call executes.
  • Functions: What a mathematical function is — input goes in, output comes out.

2. What is Asymptotic Analysis? (Conceptual Explanation)#

2.1 The "How Fast Does It Grow?" Question#

When you write an algorithm, you need to ask: "As my input grows larger and larger, how does the time (or memory) my algorithm uses grow?"

Think of it like comparing commute speeds:

  • Walking: Time grows linearly with distance — 2× distance = 2× time.
  • Driving: Time grows more slowly (highways) — 2× distance ≠ 2× time.
  • Teleporting: Time is constant — 1 mile or 1000 miles, same time.

Asymptotic analysis measures the growth rate of algorithms, not the exact time.

2.2 Why Not Just Time It with a Stopwatch?#

FactorProblem with Wall Clock Timing
HardwareA faster CPU gives shorter times for the SAME algorithm
Other ProgramsBackground apps steal CPU time, giving inconsistent measurements
Input SizeAlgorithm A may be faster on small inputs but slower on large ones
LanguageC is faster than Python for the same algorithm

Asymptotic analysis is hardware-independent, language-independent, and focuses on the fundamental growth rate!

2.3 Counting Operations: The Key Idea#

Diagram / Text
// How many times does the innermost operation execute?

// Example 1: O(n) — Linear
for (int i = 0; i < n; i++) {    ← loop runs n times
    sum += arr[i];                ← 1 operation per iteration
}
// Total: n operations → O(n)

// Example 2: O(n²) — Quadratic
for (int i = 0; i < n; i++) {        ← outer runs n times
    for (int j = 0; j < n; j++) {    ← inner runs n times FOR EACH i
        count++;                      ← 1 operation
    }
}
// Total: n × n = n² operations → O(n²)

// Example 3: O(log n) — Logarithmic
while (n > 1) {      ← how many times can you halve n?
    n = n / 2;        ← halving each time
}
// n → n/2 → n/4 → ... → 1 takes log₂(n) steps → O(log n)

2.4 While Loop Condition Testing vs. Body Execution (NPTEL Week 1 Quiz & GATE Trap)#

A classic conceptual pitfall in algorithmic analysis (frequently tested in NPTEL Week 1 Quizzes and GATE CS) is the subtle distinction between:

  1. How many times the loop body executes (the statements inside { ... })
  2. How many times the loop condition is tested / evaluated (the boolean expression in while (...))

The Fundamental Rule of Loop Condition Testing#

For any standard pre-tested loop (while (condition) or for (; condition; )), the condition must be evaluated one extra time beyond the iterations that succeed, in order to evaluate to false and terminate the loop:

extTotalConditionEvaluations=(extNumberofSuccessfulIterations)+1 ext{Total Condition Evaluations} = ( ext{Number of Successful Iterations}) + 1


Case Study: NPTEL Week 1 Quiz Problem#

Consider the following function called with arguments m=165m = 165 and n=15n = 15:

Python
def f(m, n):
    ans = 1
    while m >= 0:
        ans = ans * (ans + 1)
        m = m - n
    return ans

Problem: How many times is the while condition tested if f(165,15)f(165, 15) is called?

Step-by-Step Execution Trace Table#

Test #Current mmCondition (m0m \ge 0)EvaluationNext mm (m15m - 15)Loop Body Runs?
11651651650165 \ge 0True150150Yes (Iteration 1)
21501501500150 \ge 0True135135Yes (Iteration 2)
31351351350135 \ge 0True120120Yes (Iteration 3)
41201201200120 \ge 0True105105Yes (Iteration 4)
51051051050105 \ge 0True9090Yes (Iteration 5)
6909090090 \ge 0True7575Yes (Iteration 6)
7757575075 \ge 0True6060Yes (Iteration 7)
8606060060 \ge 0True4545Yes (Iteration 8)
9454545045 \ge 0True3030Yes (Iteration 9)
10303030030 \ge 0True1515Yes (Iteration 10)
11151515015 \ge 0True00Yes (Iteration 11)
1200000 \ge 0True15-15Yes (Iteration 12)
1315-15150-15 \ge 0False(loop terminates)No (Exit)

Mathematical Derivation#

  1. The sequence of values tested for mm forms an Arithmetic Progression (AP): mk=16515k,for k=0,1,2,m_k = 165 - 15k, \quad \text{for } k = 0, 1, 2, \dots
  2. The loop condition is mk0m_k \ge 0: 16515k0    15k165    k11165 - 15k \ge 0 \implies 15k \le 165 \implies k \le 11
  3. Since kk starts at 00 and goes up to 1111, there are 110+1=1211 - 0 + 1 = 12 iterations where the condition is True (the loop body executes 12 times).
  4. For k=12k = 12, m12=16515(12)=15m_{12} = 165 - 15(12) = -15. The condition 150-15 \ge 0 is tested a 13th time and returns False.
  5. Final Answer: The condition is tested 12+1=1312 + 1 = 13 times!
Warning

Why Students Lose Points on This (The Two Traps):

  • Trap 1: Writing 11. Calculating 165/15=11165 / 15 = 11 forgets that m0m \ge 0 is inclusive of 00. When m=0m=0, the condition is still True.
  • Trap 2: Writing 12. Counting loop body executions (1212) forgets that the while loop must evaluate the condition one final time to evaluate to False and break out of the loop.

2.5 Loop Invariants: Program State & Correctness Analysis (NPTEL Week 1 Quiz Q5)#

A Loop Invariant is a formal condition or mathematical relation about program variables that is guaranteed to remain True before and after each iteration:

  1. Initialization: True prior to the first iteration.
  2. Maintenance: If True before iteration kk, it remains True before iteration k+1k+1.
  3. Termination: When the loop terminates, the invariant holds and provides a guarantee about the final computed output.

Case Study: NPTEL Loop Invariant Question#

Consider the following program tracking integers partitioned into prime vs. composite accumulators:

C
i = 0; j = 0; k = 0;
for (m = last; m >= first; m = m - 1) {
    k = k - m;
    if (composite(m)) {
        i = i - m;
    } else {
        j = j - m;
    }
}

Question: Which condition (...) can replace if (...) at the end to guarantee it prints "True"?

Invariant Proof#

  • Before Loop: i=0,j=0,k=0    k=i+j=0i = 0, j = 0, k = 0 \implies k = i + j = 0 holds.
  • In Each Iteration:
    • kk changes by Δk=m\Delta k = -m.
    • Exactly one branch executes:
      • If composite(m) is True: Δi=m\Delta i = -m, Δj=0    Δ(i+j)=m+0=m=Δk\Delta j = 0 \implies \Delta(i + j) = -m + 0 = -m = \Delta k.
      • If composite(m) is False: Δi=0\Delta i = 0, Δj=m    Δ(i+j)=0m=m=Δk\Delta j = -m \implies \Delta(i + j) = 0 - m = -m = \Delta k.
  • At Loop Termination: Since Δk=Δ(i+j)\Delta k = \Delta(i + j) on every single step regardless of mm, the equality k == i + j is a universal loop invariant that holds for ANY values of first and last!

3. Asymptotic Notations: Mathematical Definitions#

3.1 Big-O Notation (Asymptotic Upper Bound)#

f(n)=O(g(n))    c>0,n0>0 such that 0f(n)cg(n)nn0f(n) = O(g(n)) \iff \exists \, c > 0, n_0 > 0 \text{ such that } 0 \le f(n) \le c \cdot g(n) \quad \forall \, n \ge n_0 Meaning: f(n)f(n) grows at most as fast as g(n)g(n) (Worst-Case rate).

Note

Understanding Worst-Case Big-O: Upper Bound vs. Exact Requirement (NPTEL Week 1 Quiz Q2): If an algorithm for finding a path in an nn-dimensional maze (e.g. AmazeMe) has worst-case complexity O(n4logn)O(n^4 \log n):

  • Correct Interpretation: For every sufficiently large nn, every input maze of dimension nn can be solved within time proportional to n4lognn^4 \log n.
  • Common Misconception: It does not mean every input requires n4lognn^4 \log n time (that would be lower bound Ω\Omega). Big-O only guarantees that runtime will never exceed this asymptotic upper ceiling!

3.2 Big-Ω\Omega Notation (Asymptotic Lower Bound)#

f(n)=Ω(g(n))    c>0,n0>0 such that 0cg(n)f(n)nn0f(n) = \Omega(g(n)) \iff \exists \, c > 0, n_0 > 0 \text{ such that } 0 \le c \cdot g(n) \le f(n) \quad \forall \, n \ge n_0 Meaning: f(n)f(n) grows at least as fast as g(n)g(n) (Best-Case rate).

3.3 Big-Θ\Theta Notation (Asymptotically Tight Bound)#

f(n)=Θ(g(n))    f(n)=O(g(n)) and f(n)=Ω(g(n))f(n) = \Theta(g(n)) \iff f(n) = O(g(n)) \text{ and } f(n) = \Omega(g(n))

3.4 Strict Bounds: Little-o and Little-ω\omega#

  • f(n)=o(g(n))f(n) = o(g(n)): limnf(n)g(n)=0\lim_{n \to \infty} \frac{f(n)}{g(n)} = 0 (Strictly slower growth).
  • f(n)=ω(g(n))f(n) = \omega(g(n)): limnf(n)g(n)=\lim_{n \to \infty} \frac{f(n)}{g(n)} = \infty (Strictly faster growth).

3.5 Quick Analogy#

NotationAnalogyMeaning
f=O(g)f = O(g)fgf \le gff grows no faster than gg
f=Ω(g)f = \Omega(g)fgf \ge gff grows no slower than gg
f=Θ(g)f = \Theta(g)f=gf = gff grows at the same rate as gg
f=o(g)f = o(g)f<gf < gff grows strictly slower than gg
f=ω(g)f = \omega(g)f>gf > gff grows strictly faster than gg

4. Master Theorem for Divide-and-Conquer Recurrences#

For recurrences of the form: T(n)=aT(nb)+f(n)=aT(nb)+Θ(nklogpn)T(n) = a \, T\left(\frac{n}{b}\right) + f(n) = a \, T\left(\frac{n}{b}\right) + \Theta(n^k \log^p n) where a1a \ge 1, b>1b > 1, k0k \ge 0, and pp is a real number:

CaseConditionSolution T(n)T(n)Example
Case 1logba>k\log_b a > kΘ(nlogba)\Theta\left(n^{\log_b a}\right)T(n)=4T(n/2)+n    Θ(n2)T(n) = 4T(n/2) + n \implies \Theta(n^2)
Case 2alogba=k\log_b a = k and p>1p > -1Θ(nklogp+1n)\Theta\left(n^k \log^{p+1} n\right)T(n)=2T(n/2)+n    Θ(nlogn)T(n) = 2T(n/2) + n \implies \Theta(n \log n)
Case 2blogba=k\log_b a = k and p=1p = -1Θ(nkloglogn)\Theta\left(n^k \log \log n\right)
Case 2clogba=k\log_b a = k and p<1p < -1Θ(nk)\Theta\left(n^k\right)
Case 3logba<k\log_b a < k (af(n/b)cf(n)a f(n/b) \le c f(n))Θ(f(n))\Theta\left(f(n)\right)T(n)=2T(n/2)+n2    Θ(n2)T(n) = 2T(n/2) + n^2 \implies \Theta(n^2)

Master Theorem: Step-by-Step Example#

Diagram / Text
Solve: T(n) = 4T(n/2) + n

Step 1: Identify a=4, b=2, f(n)=n, so k=1, p=0
Step 2: Compute log_b(a) = log_2(4) = 2
Step 3: Compare log_b(a) with k:  2 > 1 → Case 1!
Step 4: Solution: T(n) = Θ(n^(log_2 4)) = Θ(n²)

Another: T(n) = 2T(n/2) + n
  a=2, b=2, k=1, log_2(2)=1, 1==1, p=0>-1 → Case 2a
  T(n) = Θ(n¹ · log^(0+1) n) = Θ(n log n) ← This is Merge Sort!

5. Hierarchy of Growth Rates#

O(1)<O(loglogn)<O(logn)<O(n)<O(n)<O(nlogn)<O(n2)<O(n3)<O(2n)<O(n!)<O(nn)O(1) < O(\log \log n) < O(\log n) < O(\sqrt{n}) < O(n) < O(n \log n) < O(n^2) < O(n^3) < O(2^n) < O(n!) < O(n^n)

Diagram / Text
For n = 1,000,000 (one million):

O(1)        → 1 operation              (instant)
O(log n)    → ~20 operations           (instant)
O(n)        → 1,000,000 operations     (~1 ms)
O(n log n)  → ~20,000,000 operations   (~20 ms)
O(n²)       → 1,000,000,000,000 ops    (~16 minutes!)
O(2^n)      → 2^1000000 operations     (longer than universe age)

5.1 Estimating Wall-Clock Runtime on Modern CPUs (NPTEL Week 1 Quiz Q3)#

Modern CPUs execute roughly 10910^9 basic operations per second (1 GHz1\text{ GHz} baseline):

Estimated Time (seconds)=Total Operations T(n)Processor Operations/sec\text{Estimated Time (seconds)} = \frac{\text{Total Operations } T(n)}{\text{Processor Operations/sec}}

Worked Example: O(n3)O(n^3) with n=30,000n = 30,000#

  • Total Operations: T(n)=n3=(30,000)3=(3×104)3=27×1012T(n) = n^3 = (30,000)^3 = (3 \times 10^4)^3 = 27 \times 10^{12} operations.
  • CPU Speed: 10910^9 ops/sec.
  • Execution Time: Time=27×1012109=27,000 seconds\text{Time} = \frac{27 \times 10^{12}}{10^9} = 27,000\text{ seconds} In Minutes:27,00060=450 minutes\text{In Minutes:} \quad \frac{27,000}{60} = 450\text{ minutes} In Hours:45060=7.5 hours\text{In Hours:} \quad \frac{450}{60} = 7.5\text{ hours}
  • Conclusion: 7.5 hours7.5\text{ hours} is strictly Under 8 hours (eliminating options Under 8 minutes, while Under 8 hours is the tightest valid bound).

5.2 Comparing Growth Rates & Upper Bounds for Fractional Powers (NPTEL Week 1 Quiz Q4)#

Consider f(n)=nn=n1.5f(n) = n\sqrt{n} = n^{1.5}:

  • Versus nlognn \log n: limnn1.5nlogn=limnnlogn=\lim_{n \to \infty} \frac{n^{1.5}}{n \log n} = \lim_{n \to \infty} \frac{\sqrt{n}}{\log n} = \infty. Thus nnn\sqrt{n} grows strictly faster than nlognn \log n, so f(n)O(nlogn)f(n) \ne O(n \log n) (Statement A is False).
  • Versus n2n^2: n1.5n2n^{1.5} \le n^2 for n1n \ge 1. Hence f(n)=O(n2)f(n) = O(n^2) is True (Statement B is True).
  • Versus n4n^4: n1.5n4n^{1.5} \le n^4 for n1n \ge 1. Because Big-O is an upper bound, f(n)=O(n4)f(n) = O(n^4) is also True (Statement C is True).
  • Takeaway: (B) and (C) are true, but (A) is false!

6. Implementation in C, C++, and Python with Syntax Logic#

Clock Cycles & Recurrence Simulators
#include <stdio.h>
#include <time.h>

/**
 * C Syntax Logic Note:
 * 1. clock_t & clock(): Provided by <time.h> to measure processor execution time.
 * 2. CLOCKS_PER_SEC: Macro defining processor clock ticks per second (typically 1,000,000).
 * 3. Double casting: (double)(end - start) / CLOCKS_PER_SEC converts CPU ticks into seconds.
 */

// O(log n): Recursive division
int binarySearchSteps(int n) {
    if (n <= 1) return 1;
    return 1 + binarySearchSteps(n / 2); // Recurrence: T(n) = T(n/2) + O(1)
}

// O(n^2): Nested loops
long long quadraticWork(int n) {
    long long count = 0;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            count++;
        }
    }
    return count;
}

void benchmark(int n) {
    clock_t start = clock();
    long long ops = quadraticWork(n);
    clock_t end = clock();

    double cpu_time = ((double)(end - start)) / CLOCKS_PER_SEC;
    printf("N = %d -> Operations = %lld, Elapsed = %f seconds\n", n, ops, cpu_time);
}

7. GATE, UGC NET & NPTEL Key Exam Insights#

Important

GATE Trap: Big-O is NOT "exact complexity"! O(n2)O(n^2) means "at most n2n^2 growth". So O(n)O(n) is technically also O(n2)O(n^2) — it's just not a tight bound. For tight bound, use ThetaTheta. Most algorithms are described with ThetaTheta in GATE solutions.

Tip

NPTEL / GATE Rule of Thumb for Loop Counting:

  • For while (i < n); i++ starting at 00: Condition tested n+1n + 1 times; body executes nn times.
  • For while (i <= n); i++ starting at 00: Condition tested n+2n + 2 times; body executes n+1n + 1 times.
  • For step size ss starting at mm down to 0\ge 0: Body executes m/s+1\lfloor m/s \rfloor + 1 times; Condition tested m/s+2\lfloor m/s \rfloor + 2 times.
Note

Common Exam Question Patterns:

  1. "How many times is the condition evaluated?" (NPTEL Week 1 / GATE CS)
  2. "Arrange functions in increasing order of asymptotic growth" (GATE CS)
  3. "Apply Master Theorem or state why it does not apply" (GATE CS & UGC NET)
  4. "Compute exact closed-form using substitution method"

Practice Quiz

Test your understanding with step-by-step solutions

Practice Quiz

7 questions · 90s per question

Each question has a 90-second time limit. Unanswered questions will be auto-submitted when time runs out.