Introduction

Asymptotic notation says how to compare growth rates. This note shows how to obtain the function to compare: how to count the work done by loops, nested loops and recursive calls, and how much memory a program needs beyond its input. The counts below reduce to one of the loop patterns, an exact iteration count, or a stack-depth argument.

Prerequisites

  • Asymptotic Notation: OO, Ω\Omega, Θ\Theta, and the growth hierarchy.
  • Basic summations: arithmetic and geometric series.

Core concepts

The cost model

Analysis assumes the RAM model (CLRS 3e, Section 2.2): instructions run one after another, and each basic operation (arithmetic on word-sized numbers, comparison, assignment, array indexing, a function call and return) takes constant time. Running time is then proportional to the number of basic operations executed.

Two choices must be stated for every analysis:

  1. Input size nn. For sorting and searching it is the number of elements; for a graph it is the pair (∣V∣,∣E∣)(|V|, |E|); for arithmetic on a number NN it is the number of bits, ⌈lg⁡(N+1)⌉\lceil \lg(N+1) \rceil.
  2. Case. Worst case (maximum over inputs of size nn), best case (minimum), or average case (expectation under a stated distribution of inputs).

The input-size choice matters. Trial division tests whether NN is prime with about N\sqrt{N} divisions. That is polynomial in the value NN but 2b/22^{b/2} in the number of bits bb, so it is exponential in the input size. Algorithms polynomial in the numeric value but not in its bit length are called pseudo-polynomial; the O(nW)O(nW) 0/1 knapsack algorithm is the standard example.

Rules for combining costs

  • Sequence. the cost of S1;S2S_1; S_2 is the sum of the costs. Asymptotically, the larger term dominates.
  • Conditional. worst case of if C then S1 else S2 is cost(C) plus the larger branch.
  • Loop. the sum, over all iterations, of the cost of the body plus the test. If the body cost does not depend on the iteration, this is (number of iterations) ×\times (body cost).
  • Nested loops. work from the inside out, expressing the inner count as a function of the outer variable, then sum.
  • Function call. the cost of the body for those arguments; for recursion this gives a recurrence.

Summations used most often

SumClosed form or boundType
∑i=1ni\sum_{i=1}^{n} in(n+1)/2n(n+1)/2exact, Θ(n2)\Theta(n^2)
∑i=1ni2\sum_{i=1}^{n} i^2n(n+1)(2n+1)/6n(n+1)(2n+1)/6exact, Θ(n3)\Theta(n^3)
∑i=1nik\sum_{i=1}^{n} i^k for constant k≥0k \ge 0Θ(nk+1)\Theta(n^{k+1})asymptotic
∑i=0k2i\sum_{i=0}^{k} 2^i2k+1−12^{k+1} - 1exact
∑i=0kri\sum_{i=0}^{k} r^i, 0<r<10 < r < 1less than 1/(1−r)1/(1-r)bound, Θ(1)\Theta(1)
Hn=∑i=1n1/iH_n = \sum_{i=1}^{n} 1/iln⁡n+O(1)\ln n + O(1)asymptotic, Θ(lg⁡n)\Theta(\lg n)
∑i=1nlg⁡i=lg⁡(n!)\sum_{i=1}^{n} \lg i = \lg(n!)Θ(nlg⁡n)\Theta(n \lg n)asymptotic
∑i≥0i/2i\sum_{i \ge 0} i/2^i22exact

A geometric series is dominated by its largest term: an increasing one is Θ(last term)\Theta(\text{last term}) and a decreasing one is Θ(first term)\Theta(\text{first term}).

Loop patterns

Pseudocode is CLRS-like: <- is assignment, loops are inclusive of both bounds, and arrays are 1-indexed.

Pattern 1: dependent nested loop (triangle)

Text
count <- 0
for i <- 1 to n
    for j <- 1 to i
        count <- count + 1

The inner loop runs ii times, so count=∑i=1ni=n(n+1)/2=Θ(n2)\text{count} = \sum_{i=1}^{n} i = n(n+1)/2 = \Theta(n^2). Halving the work (a triangle instead of a square) does not change the order. With three nested loops 1≤i<j<k≤n1 \le i < j < k \le n the count is (n3)=Θ(n3)\binom{n}{3} = \Theta(n^3).

Pattern 2: multiplicative loop

Text
i <- 1
while i < n
    i <- 2 * i

After tt iterations i=2ti = 2^t, and the loop stops when 2t≥n2^t \ge n. So it runs exactly ⌈lg⁡n⌉\lceil \lg n \rceil times for n≥1n \ge 1 (for example 7 times for n=100n = 100, 10 for n=1024n = 1024, 11 for n=1025n = 1025). Halving loops (i <- floor(i/2) until i=0i = 0) behave the same way: ⌊lg⁡n⌋+1\lfloor \lg n \rfloor + 1 iterations from i=n≥1i = n \ge 1. The base of the multiplication only changes the constant: multiplying by 3 gives ⌈log⁡3n⌉=Θ(lg⁡n)\lceil \log_3 n \rceil = \Theta(\lg n).

Pattern 3: logarithmic inner loop

Text
for i <- 1 to n
    j <- 1
    while j <= i
        j <- 2 * j

The inner loop runs ⌊lg⁡i⌋+1\lfloor \lg i \rfloor + 1 times, so the total is ∑i=1n(⌊lg⁡i⌋+1)=Θ(lg⁡(n!))=Θ(nlg⁡n)\sum_{i=1}^{n} (\lfloor \lg i \rfloor + 1) = \Theta(\lg(n!)) = \Theta(n \lg n). For n=8n = 8 the counts are 1, 2, 2, 3, 3, 3, 3, 4, total 21.

Pattern 4: harmonic loop

Text
for i <- 1 to n
    j <- 1
    while j <= n
        j <- j + i

For a fixed ii the inner loop runs ⌈n/i⌉\lceil n/i \rceil times. The total is ∑i=1n⌈n/i⌉\sum_{i=1}^{n} \lceil n/i \rceil, which lies between nHnn H_n and nHn+nn H_n + n, so it is Θ(nlg⁡n)\Theta(n \lg n). For n=10n = 10: 10+5+4+3+2+2+2+2+2+1=3310 + 5 + 4 + 3 + 2 + 2 + 2 + 2 + 2 + 1 = 33. This pattern appears in the sieve of Eratosthenes and in "for each divisor" loops.

Pattern 5: geometric outer loop with linear inner loop

Text
i <- 1
while i <= n
    for j <- 1 to i
        (constant work)
    i <- 2 * i

The inner loop runs 1,2,4,…,2k1, 2, 4, \ldots, 2^k times, where 2k≤n<2k+12^k \le n < 2^{k+1}. The total 2k+1−12^{k+1} - 1 is less than 2n2n, so the fragment is Θ(n)\Theta(n), not Θ(nlg⁡n)\Theta(n \lg n). For n=16n = 16 the total is 31. Multiplying the number of outer iterations (lg⁡n\lg n) by the largest inner count (nn) gives only an upper bound.

Pattern 6: repeated square root

Text
i <- n
while i > 2
    i <- floor(sqrt(i))

If n=22kn = 2^{2^k}, the values are 22k,22k−1,…,22=4,22^{2^k}, 2^{2^{k-1}}, \ldots, 2^{2} = 4, 2, so the loop runs k=lg⁡lg⁡nk = \lg \lg n times: 4 times for n=65536n = 65536. In general the count is Θ(lg⁡lg⁡n)\Theta(\lg \lg n).

Iteration counts of the triangle, doubling and geometric loop patterns
Figure 1. Left: Pattern 1 for n=5n = 5 as a staircase of 15 cells. Middle: Pattern 2 for n=16n = 16, the values 1, 2, 4, 8, 16 of ii on a number line (4 iterations). Right: Pattern 5 for n=16n = 16 as bars of height 1, 2, 4, 8, 16, total 31 and below 2n=322n = 32.

Pattern 7: loops whose total is amortised

Text
j <- 1
for i <- 1 to n
    while j <= n and A[j] < A[i]
        j <- j + 1

The inner while can run up to nn times for one ii, but jj never decreases and never exceeds n+1n + 1, so the inner body runs at most nn times in total. The fragment is Θ(n)\Theta(n) in the worst case, not Θ(n2)\Theta(n^2). Always ask whether an inner index is reset; this is the key to merge, two-pointer scans and BFS-style analyses.

Pattern 8: square-root bounds

Text
i <- 1                          s <- 0; i <- 0
while i * i <= n                while s < n
    i <- i + 1                      i <- i + 1
                                    s <- s + i

The left loop runs ⌊n⌋\lfloor \sqrt{n} \rfloor times (10 for n=100n = 100, 9 for n=99n = 99). In the right loop, after tt iterations s=t(t+1)/2s = t(t+1)/2, so it stops at the least tt with t(t+1)/2≥nt(t+1)/2 \ge n: t=Θ(n)t = \Theta(\sqrt{n}), 14 for n=100n = 100 (since 13⋅14/2=91<100≤10513 \cdot 14/2 = 91 < 100 \le 105). A loop variable that grows by an increasing step is the usual disguise of Θ(n)\Theta(\sqrt{n}).

Pattern 9: nested doubling loops

Text
i <- 1
while i < n
    j <- 1
    while j < i
        j <- 2 * j
    i <- 2 * i

The outer loop takes i=2ti = 2^t for t=0,1,…,T−1t = 0, 1, \ldots, T - 1 with T=⌈lg⁡n⌉T = \lceil \lg n \rceil, and the inner loop runs tt times for i=2ti = 2^t. The total is 0+1+⋯+(T−1)=T(T−1)/2=Θ(lg⁡2n)0 + 1 + \cdots + (T-1) = T(T-1)/2 = \Theta(\lg^2 n): 45 for n=1024n = 1024 (T=10T = 10), 55 for n=1025n = 1025 (T=11T = 11). Neither Θ(lg⁡n)\Theta(\lg n) nor Θ(n)\Theta(n) is right.

Pattern 10: three dependent loops

Text
for i <- 1 to n
    for j <- 1 to i
        for k <- 1 to j
            count <- count + 1

The count is the number of triples 1≤k≤j≤i≤n1 \le k \le j \le i \le n, which is (n+23)=n(n+1)(n+2)/6\binom{n+2}{3} = n(n+1)(n+2)/6: 20 for n=4n = 4, 220 for n=10n = 10. With strict inequalities (k<j<ik < j < i) it is (n3)\binom{n}{3} instead. Both are Θ(n3)\Theta(n^3), but an exact-count question distinguishes them.

Best, worst and average case of a fragment

Linear search for a key xx in A[1..n]A[1..n]:

Text
LINEAR-SEARCH(A, n, x)
    for i <- 1 to n
        if A[i] = x then return i
    return NIL
CaseComparisons with xxOrder
Best: x=A[1]x = A[1]1Θ(1)\Theta(1)
Worst: xx absent or at A[n]A[n]nnΘ(n)\Theta(n)
Average: xx present, each position equally likely(n+1)/2(n+1)/2Θ(n)\Theta(n)

The average case needs an explicit assumption; here, that xx is present and equally likely at each position. Change the assumption (for example, xx absent with probability 1/21/2) and the exact average changes, though here it stays Θ(n)\Theta(n).

Space complexity

What is counted

  • Input space: the memory holding the input. Usually not charged to the algorithm.
  • Auxiliary space: extra memory used during execution: local variables, temporary arrays, and the recursion stack.
  • Total space = input space + auxiliary space.

"Space complexity" may mean auxiliary space or total space; check the wording. An algorithm is in place if it uses O(1)O(1) auxiliary space apart from the input. Some books also call quicksort in place although its stack uses O(lg⁡n)O(\lg n) to O(n)O(n) words; when it matters, state which convention is meant.

The recursion stack

Each active call keeps a stack frame (parameters, locals, return address) of constant size in typical analyses. Auxiliary space for recursion is therefore Θ(maximum recursion depth)\Theta(\text{maximum recursion depth}) times the frame size, not the total number of calls.

Here ϕ=(1+5)/2\phi=(1+\sqrt{5})/2 is the golden ratio.

AlgorithmTotal callsMaximum depthAuxiliary space
Recursive factorial of nnnnnnΘ(n)\Theta(n)
Recursive binary search on nn elementsO(lg⁡n)O(\lg n)O(lg⁡n)O(\lg n)O(lg⁡n)O(\lg n); iterative version O(1)O(1)
Naive Fibonacci F(n)F(n)2F(n+1)−1=Θ(ϕn)2F(n+1) - 1 = \Theta(\phi^n)nnΘ(n)\Theta(n)
Merge sort2n−12n - 1⌈lg⁡n⌉+1\lceil \lg n \rceil + 1 levelsΘ(n)\Theta(n) for the merge buffer
QuicksortΘ(n)\Theta(n)Θ(lg⁡n)\Theta(\lg n) best, Θ(n)\Theta(n) worstas the depth; O(lg⁡n)O(\lg n) if it recurses on the smaller part and loops on the larger

The Fibonacci row shows the distinction clearly: exponential time, linear space, because only one root-to-leaf path of calls is on the stack at any moment. (Call counts are taken with base cases F(0)F(0) and F(1)F(1); see Recursion Tracing and Call Counting.)

Time-space trade-offs

Memory can buy time. Storing the Fibonacci values computed so far (dynamic programming) cuts time from exponential to Θ(n)\Theta(n) at the cost of Θ(n)\Theta(n) space, or Θ(1)\Theta(1) space if only the last two values are kept. A hash table trades Θ(n)\Theta(n) space for expected O(1)O(1) lookups.

Important properties and observations

  • Count iterations exactly when the required result is a counter value; use asymptotics only when an order is required. Watch < versus <= in loop tests: they change exact counts by one and, for multiplicative loops, change ⌈lg⁡n⌉\lceil \lg n \rceil into ⌊lg⁡n⌋+1\lfloor \lg n \rfloor + 1.
  • Multiplying the outer count by the maximum inner count gives an upper bound only (Patterns 5 and 7).
  • A loop that halves or doubles is logarithmic regardless of the factor, as long as the factor is a constant greater than 1. A loop that adds a constant is linear. A loop that squares (or square-roots) is doubly logarithmic.
  • A recursion's space is its depth, not its number of calls.
  • Time is at least space for algorithms that write the memory they use (each cell written costs a step), so an algorithm that allocates and initialises Θ(n)\Theta(n) memory takes Ω(n)\Omega(n) time.

GATE examination perspective

  • Exact counts. The quantity to determine is the final value of a counter or the number of times a statement runs. Tabulate small nn and look for the closed form; then check one value.
  • Order of a fragment. identify which pattern each loop follows; check whether inner bounds depend on outer variables and whether inner indices reset.
  • Space questions. separate input, auxiliary and stack space; for recursion, find the deepest chain of calls.
  • Traps. Pattern 5 is Θ(n)\Theta(n); Pattern 3 is Θ(nlg⁡n)\Theta(n \lg n); the harmonic loop is Θ(nlg⁡n)\Theta(n \lg n); nested doubling loops are Θ(lg⁡2n)\Theta(\lg^2 n); an increasing step is Θ(n)\Theta(\sqrt{n}); a "nested" loop with a non-resetting index is linear.

Summary

Sources

  1. T. H. Cormen, C. E. Leiserson, R. L. Rivest, C. Stein, Introduction to Algorithms, 3rd ed., MIT Press, 2009: Section 2.2 (analysing algorithms, the RAM model, worst and average case).

Next steps