Asymptotic Notations

Basis
Last updated: Tags: Algorithms, Asymptotic Analysis

You want to say that binary search is “faster” than linear search. But faster on what hardware? With what compiler settings? For inputs of what size? To make comparisons that hold regardless of machine, language, and constant factors, you need a precise language for describing growth rates — and that language is asymptotic notation.

This checkpoint defines the three core notations — OO, Ω\Omega, and Θ\Theta — from first principles, and shows how they let you classify algorithms by the shape of their scaling behavior rather than their absolute performance.

The core idea: bounding a function

Let T(n)T(n) be the number of operations an algorithm performs on an input of size nn. You rarely know the exact formula for T(n)T(n) — it depends on constant factors that vary between machines and implementations. What you can determine is the shape of TT: whether it grows like nn, like n2n^2, like logn\log n, or like 2n2^n.

Asymptotic notation captures that shape by asking: what simpler function g(n)g(n) can you use to bound T(n)T(n) from above, from below, or both — at least for all sufficiently large nn?

The phrase “for sufficiently large nn” is essential. You are not making a claim about small inputs, where constant factors dominate. You are describing what happens as nn \to \infty, which is what determines long-run scalability.

Big-O: upper bounds

Big-O notation expresses that a function is bounded above by a multiple of another function, eventually.

Definition. f(n)=O(g(n))f(n) = O(g(n)) if and only if there exist constants c>0c > 0 and n00n_0 \geq 0 such that

f(n)cg(n)for all nn0.(1)f(n) \leq c \cdot g(n) \quad \text{for all } n \geq n_0. \tag{1}

In plain language: ff grows no faster than gg, up to a constant multiple. The constants cc and n0n_0 act as witnesses — you just need them to exist, not to be small.

Example. Show that 3n2+5n+1=O(n2)3n^2 + 5n + 1 = O(n^2).

You need cc and n0n_0 such that 3n2+5n+1cn23n^2 + 5n + 1 \leq c \cdot n^2 for all nn0n \geq n_0. For n1n \geq 1, each of 5n5n and 11 is at most n2n^2, so:

3n2+5n+13n2+5n2+n2=9n2.3n^2 + 5n + 1 \leq 3n^2 + 5n^2 + n^2 = 9n^2.

Taking c=9c = 9 and n0=1n_0 = 1 satisfies the definition. Therefore 3n2+5n+1=O(n2)3n^2 + 5n + 1 = O(n^2).

One subtlety: 3n2+5n+1=O(n3)3n^2 + 5n + 1 = O(n^3) is also true by the definition — n3n^3 is an upper bound too. But O(n2)O(n^2) is the tightest upper bound, and that is always the one worth reporting.

Big-Ω: lower bounds

Big-Ω notation is the mirror of big-O: it expresses that a function is bounded below by a multiple of another function.

Definition. f(n)=Ω(g(n))f(n) = \Omega(g(n)) if and only if there exist constants c>0c > 0 and n00n_0 \geq 0 such that

f(n)cg(n)for all nn0.(2)f(n) \geq c \cdot g(n) \quad \text{for all } n \geq n_0. \tag{2}

In plain language: ff grows at least as fast as gg, up to a constant multiple.

Example. Show that 3n2+5n+1=Ω(n2)3n^2 + 5n + 1 = \Omega(n^2).

For all n0n \geq 0: 3n2+5n+13n23n^2 + 5n + 1 \geq 3n^2. Taking c=3c = 3 and n0=0n_0 = 0 satisfies the definition.

Big-Ω is used to express lower bounds on algorithms: “any algorithm for this problem must perform at least Ω(nlogn)\Omega(n \log n) comparisons” means no algorithm can sort faster than nlognn \log n in the comparison model.

Big-Θ: tight bounds

When a function is simultaneously O(g)O(g) and Ω(g)\Omega(g), it is bounded above and below by multiples of gg — it grows at exactly the same rate as gg, up to constants.

Definition. f(n)=Θ(g(n))f(n) = \Theta(g(n)) if and only if

f(n)=O(g(n))andf(n)=Ω(g(n)).(3)f(n) = O(g(n)) \quad \text{and} \quad f(n) = \Omega(g(n)). \tag{3}

Equivalently, there exist constants c1,c2>0c_1, c_2 > 0 and n00n_0 \geq 0 such that

c1g(n)f(n)c2g(n)for all nn0.c_1 \cdot g(n) \leq f(n) \leq c_2 \cdot g(n) \quad \text{for all } n \geq n_0.

Example. 3n2+5n+1=Θ(n2)3n^2 + 5n + 1 = \Theta(n^2), as the two earlier proofs together establish.

When you have a Θ\Theta bound, you have a complete picture: the growth rate is pinned down, not just bounded from one side.

Using limits to determine the notation

Since you are already comfortable with limits from Limits, there is a practical shortcut. For positive functions ff and gg, compute:

L=limnf(n)g(n).L = \lim_{n \to \infty} \frac{f(n)}{g(n)}.

The value of LL tells you the relationship directly:

LLRelationshipMeaning
L=0L = 0f=o(g)f = o(g)ff grows strictly slower than gg
0<L<0 < L < \inftyf=Θ(g)f = \Theta(g)ff and gg grow at the same rate
L=L = \inftyf=ω(g)f = \omega(g)ff grows strictly faster than gg

The lowercase notations oo (little-o) and ω\omega (little-omega) are the strict versions of OO and Ω\Omega: f=o(g)f = o(g) means the ratio f/g0f/g \to 0, while f=O(g)f = O(g) only requires the ratio to be bounded. Every oo relationship implies the corresponding OO relationship, but not vice versa.

Example. Is logn=O(n)\log n = O(n)?

limnlognn=0,\lim_{n \to \infty} \frac{\log n}{n} = 0,

so logn=o(n)\log n = o(n), which implies logn=O(n)\log n = O(n). In fact logn\log n grows strictly slower than nn.

Example. Is n2=O(nlogn)n^2 = O(n \log n)?

limnn2nlogn=limnnlogn=,\lim_{n \to \infty} \frac{n^2}{n \log n} = \lim_{n \to \infty} \frac{n}{\log n} = \infty,

so n2=ω(nlogn)n^2 = \omega(n \log n) — it grows strictly faster. Therefore n2O(nlogn)n^2 \neq O(n \log n).

Common growth rates

The table below lists the classes you will encounter most often, from slowest to fastest growth:

NotationNameExample
O(1)O(1)ConstantArray access by index
O(logn)O(\log n)LogarithmicBinary search
O(n)O(n)LinearScanning a list once
O(nlogn)O(n \log n)LinearithmicMerge sort
O(n2)O(n^2)QuadraticComparing all pairs
O(n3)O(n^3)CubicNaive matrix multiplication
O(2n)O(2^n)ExponentialBrute-force subset search
O(n!)O(n!)FactorialBrute-force permutation

These classes form a strict hierarchy under the oo relation:

O(1)O(logn)O(n)O(nlogn)O(n2)O(2n)O(n!).O(1) \subsetneq O(\log n) \subsetneq O(n) \subsetneq O(n \log n) \subsetneq O(n^2) \subsetneq O(2^n) \subsetneq O(n!).

An algorithm with O(nlogn)O(n \log n) running time will always outperform an O(n2)O(n^2) algorithm on large enough inputs, regardless of the constant factors.

Common misconceptions

“Big-O gives the exact running time.” It does not. O(n2)O(n^2) says the running time is bounded by cn2c \cdot n^2 for some constant cc — it says nothing about cc‘s size. An algorithm that does 109n210^9 \cdot n^2 operations is O(n2)O(n^2), as is one that does n2n^2 operations. Big-O describes shape, not magnitude.

“The algorithm with the smallest big-O is always fastest in practice.” Not for small inputs. An O(nlogn)O(n \log n) algorithm may be slower than an O(n2)O(n^2) one for small nn if the former has a much larger constant factor. Asymptotic analysis describes large-nn behavior; for small inputs, you need to measure.

f=O(g)f = O(g) means fgf \approx g.” No. OO expresses an upper bound, not approximate equality. A constant function is O(n100)O(n^{100}), but that tells you almost nothing useful. Always report the tightest bound you can establish.

Summary

  • Big-O (f=O(g)f = O(g)): ff is eventually bounded above by cgc \cdot g for some constant cc — ”ff grows no faster than gg.”
  • Big-Ω (f=Ω(g)f = \Omega(g)): ff is eventually bounded below by cgc \cdot g — ”ff grows at least as fast as gg.”
  • Big-Θ (f=Θ(g)f = \Theta(g)): both bounds hold — ”ff and gg grow at the same rate, up to constants.”
  • The limit limnf(n)/g(n)\lim_{n \to \infty} f(n) / g(n) tells you the relationship directly: 00 means oo, finite and positive means Θ\Theta, \infty means ω\omega.
  • The growth hierarchy from slow to fast: O(1),O(logn),O(n),O(nlogn),O(n2),O(2n),O(n!)O(1), O(\log n), O(n), O(n \log n), O(n^2), O(2^n), O(n!).
  • Big-O is an upper bound on growth rate, not an approximation — it does not reveal constant factors or exact formulas.