Understanding the formal definition of Big-O

This is the third in a three post series. The first post explains Big-O from a self-taught programmer's perspective. The second post talks about how to calculate Big-O.

In my original Big-O piece, I’ve made a bunch of simplifying assumptions and told you a bunch of half truths. It might seem a bit disingenuous, but really.. these half-truths are helpful because (unless you’re going after a formal CS degree) they don’t really matter.

But for those who are looking to get a formal CS degree, these sorts of differences matter to professors and the like. For that, let’s actually parse what the formal definition means.

The formal definition via wikipedia:

Let f and g be two functions defined on some subset of the real numbers. One writes

f(x) = O(g(x)) as x -> infinity

if and only if there is a positive constant M such that for all sufficiently large values of x, the absolute value of f(x) is at most M multiplied by the absolute value of g(x). That is, f(x) = O(g(x)) if and only if there exists a positive real number M and real number x0 such that |f(x)| <= M * |g(x)| for all x >= x0.

Ugh. Let’s parse that.

We have two undefined functions called f and g. Our f function is the thing we’re trying to find the Big-O of. It’s a block of code.

The g function is an attempt to classify our f function as O(n) (or some other O, but we’ll use O(n) for example purposes).

Following this definition, we’ll know that our f function is O(n) if and only if we can graph the two of these and (starting at an x value of your choosing) the lines never cross one another.

You get to choose the x value to start at in order to account for the step in calculating Big-O where we drop all of the constants. No one cares if the Big-O is “~O(45n)~”. We drop that because it’s the same shape of a curve, though a bit steeper. Because the definition above says we can choose our own constant (M), we could just choose 46. 46n will always be above 45n and never cross once n=1.

The same can’t be true for smaller Big-O functions. No matter what real-number (e.g. not infinity) constant we choose to multiply log(n) by, O(n) will always cross it at some point.

As always, graphing makes this a bit clearer. Here is an example of our 45n vs 46n example above.

45n_vs_46n.png

You’ll see that once n=1, they never cross each other again. If we try the 1,000,000 * log(n) function versus a simple O(n), we’ll see they cross each other around the x=16,000,000 mark.

logn_vs_n.png

Because the red line is crossed, it means the green line doesn’t “bound” the red line (i.e. it is not a boundary).

You might notice that this definition means that the O(45n) function is also O(n³) because it will also never cross. This is true, but not particularly useful.