Growth of Functions, Asymptotic Notation, and Common Functions Explained

Growth of Functions, Asymptotic Notation, and Common Functions

Before I can meaningfully compare two algorithms, I need a language for describing how their running time grows as the input gets larger. That’s exactly what asymptotic notation gives me. I don’t care that one algorithm takes 3 milliseconds and another takes 5 milliseconds on a tiny input — I care about how each one scales as $n$ grows into the millions or billions. This file is my reference for the notation, the common growth-rate functions I encounter constantly, and the intuition for how to compare them.

History and Background

Asymptotic notation, particularly Big-O notation, has its roots in number theory, introduced by German mathematician Paul Bachmann in 1894 and later popularized by Edmund Landau — which is why Big-O is sometimes called “Landau notation.” Computer scientist Donald Knuth is largely credited with adapting and popularizing this notation for algorithm analysis in the 1970s, especially through his multi-volume work The Art of Computer Programming, and his 1976 paper “Big Omicron and big Omega and big Theta,” which formalized the trio of notations ($O$, $\Omega$, $\Theta$) that I use constantly today.

Problem Statement

I want a rigorous, mathematically precise way to describe and compare the growth rates of functions — typically representing the running time or space usage of algorithms — as their input size $n$ grows toward infinity, while ignoring constant factors and lower-order terms that don’t matter for large inputs.

Core Concepts

How It Works

When I want to classify a function $f(n)$ using asymptotic notation, I follow this process:

  1. I identify the dominant term of $f(n)$ as $n$ grows large, discarding lower-order terms and constant multipliers.
  2. I choose the appropriate notation ($O$, $\Omega$, or $\Theta$) depending on whether I want to express an upper bound, lower bound, or tight bound.
  3. I find witnesses — constants $c$ and $n_0$ — that satisfy the formal definition, proving the asymptotic relationship holds for all sufficiently large $n$.

Working Principle

The underlying mechanism is to abstract away hardware-specific and implementation-specific details (like exact constant factors, cache effects, or compiler optimizations) and focus purely on how the function’s growth rate compares to well-known reference functions as $n \to \infty$. This lets me meaningfully compare algorithms across different machines, languages, and implementations, since two implementations of the same $O(n \log n)$ algorithm will always eventually behave similarly at scale, even if their constant factors differ.

Mathematical Foundation

Big-O (upper bound):

$$ f(n) = O(g(n)) \iff \exists, c > 0,\ n_0 > 0 \text{ such that } 0 \leq f(n) \leq c \cdot g(n) \text{ for all } n \geq n_0 $$

Big-Omega (lower bound):

$$ f(n) = \Omega(g(n)) \iff \exists, c > 0,\ n_0 > 0 \text{ such that } 0 \leq c \cdot g(n) \leq f(n) \text{ for all } n \geq n_0 $$

Big-Theta (tight bound):

$$ f(n) = \Theta(g(n)) \iff \exists, c_1, c_2 > 0,\ n_0 > 0 \text{ such that } c_1 g(n) \leq f(n) \leq c_2 g(n) \text{ for all } n \geq n_0 $$

Little-o (strict upper bound):

$$ f(n) = o(g(n)) \iff \lim_{n \to \infty} \frac{f(n)}{g(n)} = 0 $$

Little-omega (strict lower bound):

$$ f(n) = \omega(g(n)) \iff \lim_{n \to \infty} \frac{f(n)}{g(n)} = \infty $$

Common growth-rate hierarchy (from slowest to fastest):

$$ O(1) < O(\log n) < O(n) < O(n \log n) < O(n^2) < O(n^3) < O(2^n) < O(n!) $$

Example proof that $3n^2 + 5n + 2 = O(n^2)$: I need to find $c$ and $n_0$ such that $3n^2 + 5n + 2 \leq c n^2$ for all $n \geq n_0$. For $n \geq 1$, I have $5n \leq 5n^2$ and $2 \leq 2n^2$, so:

$$ 3n^2 + 5n + 2 \leq 3n^2 + 5n^2 + 2n^2 = 10n^2 $$

Choosing $c = 10$ and $n_0 = 1$ satisfies the definition, so $3n^2 + 5n + 2 = O(n^2)$.

Diagrams

flowchart TD
    A["Function f(n)"] --> B{"Want upper, lower, or tight bound?"}
    B -->|Upper bound| C["Use Big-O: f(n) ≤ c·g(n)"]
    B -->|Lower bound| D["Use Big-Omega: f(n) ≥ c·g(n)"]
    B -->|Tight bound| E["Use Big-Theta: c1·g(n) ≤ f(n) ≤ c2·g(n)"]

Pseudocode

Since asymptotic notation is a mathematical classification tool rather than an executable process, I represent the classification logic as pseudocode:

CLASSIFY-GROWTH(f, g):
    ratio = f(n) / g(n) as n -> infinity

    if ratio approaches a positive constant:
        return "f(n) = Θ(g(n))"
    else if ratio approaches 0:
        return "f(n) = o(g(n))"          // f grows strictly slower
    else if ratio approaches infinity:
        return "f(n) = ω(g(n))"          // f grows strictly faster
    else:
        return "no simple asymptotic relationship (oscillating ratio)"

Step-by-Step Example

Let me classify $f(n) = 2n^2 + 3n$ against $g(n) = n^2$.

  1. I compute the ratio: $\dfrac{f(n)}{g(n)} = \dfrac{2n^2 + 3n}{n^2} = 2 + \dfrac{3}{n}$.
  2. As $n \to \infty$, $\dfrac{3}{n} \to 0$, so the ratio approaches exactly 2, a positive constant.
  3. Since the ratio approaches a positive constant, $f(n) = \Theta(n^2)$.
  4. I can also directly verify $f(n) = O(n^2)$ (choosing $c=5$, $n_0=1$) and $f(n) = \Omega(n^2)$ (choosing $c=2$, $n_0=1$), confirming the tight bound.

Time Complexity

Not applicable in the traditional sense — asymptotic notation is the tool I use to express time complexity, not an algorithm with its own running time. Classifying a given function’s growth rate is typically an $O(1)$ or simple calculus operation (taking a limit), done by hand or via symbolic computation.

Space Complexity

Not applicable, for the same reason — asymptotic notation is descriptive, not computational, so it has no memory footprint of its own.

Correctness Analysis

The correctness of any asymptotic classification depends on rigorously satisfying the formal definitions — finding valid witness constants $c$ (and $c_1, c_2$ for Theta) and a threshold $n_0$ beyond which the inequality holds for all larger $n$, not just some. A common trap is checking only a few small values of $n$ and assuming the pattern continues, when in fact the relationship might only hold asymptotically after some large threshold, or might not hold at all if a lower-order term happens to dominate for an unusually wide range of small $n$.

Advantages

Disadvantages

Applications

Implementation in C

Since asymptotic notation is a mathematical concept, I demonstrate it here with a small program that empirically measures how different growth-rate functions scale, helping build intuition for the differences between them.

#include <stdio.h>
#include <math.h>

/* Prints the values of several common growth-rate functions
   for increasing n, to build intuition about their relative scale. */
int main() {
    printf("%-10s %-12s %-12s %-14s %-14s %-16s\n",
           "n", "log2(n)", "n", "n*log2(n)", "n^2", "2^n (capped)");

    for (int n = 1; n <= 1024; n *= 2) {
        double logn = (n == 1) ? 0 : log2((double)n);
        double nlogn = n * logn;
        double nsquared = (double)n * n;
        double exp2n = (n <= 20) ? pow(2, n) : -1; /* avoid overflow for display */

        printf("%-10d %-12.2f %-12d %-14.2f %-14.2f ", n, logn, n, nlogn, nsquared);
        if (exp2n >= 0)
            printf("%-16.0f\n", exp2n);
        else
            printf("%-16s\n", "(too large)");
    }

    return 0;
}

Sample Input and Output

n          log2(n)      n            n*log2(n)      n^2            2^n (capped)    
1          0.00         1            0.00           1.00           2               
2          1.00         2            2.00           4.00           4               
4          2.00         4            8.00           16.00          16              
8          3.00         8            24.00          64.00          256             
16         4.00         16           64.00          256.00         65536           
32         5.00         32           160.00         1024.00        (too large)     
64         6.00         64           384.00         4096.00        (too large)     
...
1024       10.00        1024         10240.00       1048576.00     (too large)     

This table makes the growth-rate hierarchy tangible — by $n=1024$, $2^n$ is already astronomically larger than $n^2$, even though at $n=4$ they were comparable.

Optimization Techniques

Common Mistakes

Further Reading

Exit mobile version