Big-O in Python: Timing O(1), O(n), and O(n^2) for Real

Big-O describes how an algorithm’s work grows as input size n grows. This post times three functions with different Big-O shapes on the same input sizes and looks at what the growth actually does to runtime.

The code

import time


def constant_time(data):
    # O(1): one lookup, no matter how big data is
    return data[0]


def linear_time(data):
    # O(n): touches every element once
    total = 0
    for x in data:
        total += x
    return total


def quadratic_time(data):
    # O(n^2): nested loop over data, work grows with n*n
    count = 0
    for x in data:
        for y in data:
            if x == y:
                count += 1
    return count


def timeit(fn, data):
    start = time.perf_counter()
    fn(data)
    return time.perf_counter() - start


sizes = [500, 1000, 2000, 4000]

print(f"{'n':>6} | {'O(1)':>10} | {'O(n)':>10} | {'O(n^2)':>10}")
for n in sizes:
    data = list(range(n))
    t_const = timeit(constant_time, data)
    t_linear = timeit(linear_time, data)
    t_quad = timeit(quadratic_time, data)
    print(f"{n:>6} | {t_const:>10.6f} | {t_linear:>10.6f} | {t_quad:>10.6f}")

Each function runs against the same data list at four sizes (500, 1000, 2000, 4000), and timeit wraps each call with time.perf_counter() to measure real elapsed seconds.

Running it

Actual run, real timings in seconds:

     n |       O(1) |       O(n) |     O(n^2)
   500 |   0.000001 |   0.000010 |   0.002113
  1000 |   0.000001 |   0.000017 |   0.008253
  2000 |   0.000000 |   0.000033 |   0.033541
  4000 |   0.000000 |   0.000066 |   0.135899

constant_time stayed at effectively zero across all four sizes, since it only ever reads data[0]. linear_time roughly doubled each time n doubled: 0.000010 -> 0.000017 -> 0.000033 -> 0.000066. quadratic_time roughly quadrupled each time n doubled: 0.002113 -> 0.008253 -> 0.033541 -> 0.135899, and by n=4000 it dwarfs the other two entirely.

Takeaway

Same input sizes, three different growth patterns in the actual timings: flat for O(1), a doubling pattern for O(n), and a quadrupling pattern for O(n^2) as n doubled each step.