In this part, you'll get to visually experiment with different C values and lower bounds on N in order to show that a function fits a certain order of growth. To start, do some imports:
import pandas as pd
import matplotlib
from matplotlib import pyplot as plt
from math import log2, log10, ceilAlso remember to do %matplotlib inline
Now paste the following code in a cell (you don't need to understand it for the purposes of this lab):
matplotlib.rcParams["font.size"] = 20
def get_ax():
fig, ax = plt.subplots(figsize=(8,6))
ax.spines["right"].set_visible(False)
ax.spines["top"].set_visible(False)
ax.set_xlim(1, 10)
return ax
def scale_ax():
ax = get_ax()
ax.set_xlabel("N (data size)")
ax.set_ylabel("Steps")
return ax
def plot_func(ax, f, C=1, color="k", label="work"):
start = ax.get_xlim()[0]
width = ax.get_xlim()[1] - ax.get_xlim()[0]
s = pd.Series([],dtype=float)
for i in range(100):
N = start + width * (i+1)/100
s[N] = eval(f)
s.sort_index().plot(ax=ax, color=color, linewidth=3, label=label)
plt.text(s.index[-1], s.iloc[-1], f, verticalalignment='center')
def upper_bound(ax, order, C=1, minN=None):
f = order
if C != 1:
f = "C * (%s)" % order
plot_func(ax, f, C=C, color="r", label="upper bound")
if minN != None:
ax.axvspan(minN, ax.get_xlim()[1], color='0.85')
ax.legend(frameon=False)You need to show that some multiple of N is an upper bound on
N+100 for large values of N. Paste+run this code:
ax = scale_ax()
ax.set_xlim(0, 10) # TODO: change upper bound
plot_func(ax, "N + 100")
upper_bound(ax, order="N") # TODO: pass C and minNIt should look like this:
Clearly g(N)=N is not an upper bound on f(N)=N+100, but remember
that you are allowed to do the following:
- multiple
g(N)by a constantC - require that
Nbe large
To use your first capability, pass in a C value of of 25, changing the call to upper_bound(...) to be like this:
upper_bound(ax, order="N", C=25)It should look like this:
Better, but now the red line is only an upper bound for large N
values. Let's set a lower bound on N, like this:
upper_bound(ax, order="N", C=25, minN=6)It should look like this:
Great!
In general for all of these exercises, your job is to choose C and
minN values so that the red line is above the black line in the
shaded portion.
Finally, you should make sure the upper bound keeps holding for large
N values. If this were a math course, you would prove this is true
for all large N values. But since this is a programming course, let's
just make the xlim really big and make sure there are no obvious
problems, like this:
ax.set_xlim(0, 1e6) # 1 millionLooks good!
Start with this:
ax = scale_ax()
ax.set_xlim(0, 10)
plot_func(ax, "100*N")
upper_bound(ax, order="N")Do you need to set minN for this one, or is choosing the right C good enough?
Start with this:
ax = scale_ax()
ax.set_xlim(0, 10)
plot_func(ax, "N**2 + 5*N + 25")
upper_bound(ax, order="N**2")Start with this:
ax = scale_ax()
ax.set_xlim(0, 10)
plot_func(ax, "2*N + (N/15)**4")
upper_bound(ax, order="N")Use C=3 and minN=1.
What happens when you increase the x limit to 75?
ax.set_xlim(0, 75)It turns out 2*N + (N/15)**4 is NOT in O(N) after all. What
Big O order of growth does 2*N + (N/15)**4 have?
Start with this:
ax = scale_ax()
ax.set_xlim(1, 100)
plot_func(ax, "log2(N)")
upper_bound(ax, order="log10(N)")Any C value that is at least log2(10) will work here. In general,
any log curve with base M is just a constant multiple of another log
curve with base N. Thus, it is common to just say an algorithm is
O(log N), without bothering to specify the base.
Start with this:
ax = scale_ax()
ax.set_xlim(1, 100)
plot_func(ax, "ceil(log2(N))")
upper_bound(ax, order="log2(N)")Start with this:
ax = scale_ax()
ax.set_xlim(1, 100)
plot_func(ax, "N * ceil(log2(N))")
upper_bound(ax, order="N * log2(N)")Replace ???? in the following:
ax = scale_ax()
ax.set_xlim(1, 100)
plot_func(ax, "sum(range(int(N)))")
upper_bound(ax, order=????)


