# More program performance

## Big-O notation

Last time, we talked about analyzing the performance of programs. When deciding whether a function would run in constant, linear, or quadratic time, we ignored the constants and just looked at the biggest term. We can define this somewhat more formally using big-O notation.

Let’s look again at the last function we considered last time:

def distinct(l: list) -> list:
d = []
for x in l:
if x not in d:
d.append(x)
return d


We decided that this function’s running time is quadratic in its input (in the worst case). For a list of length $$n$$, we calculated the number of operations as $$((n * (n - 1)) / 2) + n + 2$$.

A computer scientist would say that on a list of $$n$$ elements, this function runs in $$O(n^2)$$ time. The formal definition looks like this:

If we have (mathematical, not Python!) functions $$f(x)$$, then $$f(x)$$ is in $$O(g(x))$$ if and only if there are constants $$x_0$$ and $$C$$ such that for all $$x > x_0$$, $$f(x) < C*g(x)$$.

For the function above, our constants could be, say, $$x_0=4$$ and $$C=4$$.

In this class, we won’t expect you to rigorously prove that a function’s running time is in a particular big-O class. We will, though, use (for instance) $$O(n)$$ as a shorthand for “linear.”

As a bit of practice, here are our rainfall solutions:

# version 1

def average_rainfall(sensor_input: lst) -> float:
total_rainfall = 0
total_rainfall += rainfall

# version 2

def list_before(l: list, item) -> list:
result = []
for element in l:
if element == item:
return result
result.append(element)
return result

def average_rainfall(sensor_input: lst) -> float:

See the lecture capture for details; for inputs of size $$n$$, both solutions run in $$O(n)$$ time.