{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "## CS 200: Analysis of Algorithms\n", "\n", "\n", "
\n",
"\n",
" \n",
"Video: Analyzing Algorithms\n",
" \n",
"Big-O Cheat Sheet\n",
" \n",
" \n",
"### Timing functions\n",
" \n",
"We can use the Jupyter magic command %timeit\n",
" "
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"def sum1(n):\n",
" sum = 0\n",
" for x in range(n+1):\n",
" sum += x\n",
" return sum"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"5000050000"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"sum1(100000)"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"4.01 ms ± 10.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n"
]
}
],
"source": [
"%timeit sum1(100000)"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"from functools import reduce"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"def sum2(n):\n",
" return reduce(lambda x,y: x+y, range(n+1))"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"5000050000"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"sum2(100000)"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"7.08 ms ± 101 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n"
]
}
],
"source": [
"%timeit sum2(100000)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Timing functions\n",
"\n",
"We create our own function to time execution time."
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [],
"source": [
"import time"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"371360.807213458"
]
},
"execution_count": 16,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"time.perf_counter()"
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {},
"outputs": [],
"source": [
"def functime(func, arg):\n",
" ''' stopwatch function to time execution time '''\n",
" tic = time.perf_counter()\n",
" val = func(arg)\n",
" toc = time.perf_counter()\n",
" elapsed = toc - tic\n",
" return (val, arg, elapsed) "
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(5000050000, 100000, 0.0041978079825639725)"
]
},
"execution_count": 18,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"functime(sum1,100000)"
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(500000500000, 1000000, 0.04292349598836154)"
]
},
"execution_count": 19,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"functime(sum1,1000000)"
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(5000050000, 100000, 0.007542783976532519)"
]
},
"execution_count": 20,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"functime(sum2,100000)"
]
},
{
"cell_type": "code",
"execution_count": 21,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(500000500000, 1000000, 0.07486499997321516)"
]
},
"execution_count": 21,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"functime(sum2,1000000)"
]
},
{
"cell_type": "code",
"execution_count": 22,
"metadata": {},
"outputs": [],
"source": [
"def sum3(n):\n",
" ''' Guaus's formula for sum of first n integers '''\n",
" return int((n*(n+1))/2)"
]
},
{
"cell_type": "code",
"execution_count": 23,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"50005000"
]
},
"execution_count": 23,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"sum3(10000)"
]
},
{
"cell_type": "code",
"execution_count": 24,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(5000050000, 100000, 2.4819746613502502e-06)"
]
},
"execution_count": 24,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"functime(sum3,100000)"
]
},
{
"cell_type": "code",
"execution_count": 25,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(500000500000, 1000000, 2.082961145788431e-06)"
]
},
"execution_count": 25,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"functime(sum3, 1000000)"
]
},
{
"cell_type": "code",
"execution_count": 26,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(50000005000000, 10000000, 2.208980731666088e-06)"
]
},
"execution_count": 26,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"functime(sum3,10000000)"
]
},
{
"cell_type": "code",
"execution_count": 27,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(5050, 100, 2.128013875335455e-06)"
]
},
"execution_count": 27,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"functime(sum3, 100)"
]
},
{
"cell_type": "code",
"execution_count": 28,
"metadata": {},
"outputs": [],
"source": [
"def mapfunctime(func, arglist = [1000,10000,100000,1000000]):\n",
" return list(map(lambda x: functime(func, x), arglist))"
]
},
{
"cell_type": "code",
"execution_count": 29,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[(500500, 1000, 4.1875988245010376e-05),\n",
" (50005000, 10000, 0.00040575896855443716),\n",
" (5000050000, 100000, 0.00431140698492527),\n",
" (500000500000, 1000000, 0.042788993974681944)]"
]
},
"execution_count": 29,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"mapfunctime(sum1)"
]
},
{
"cell_type": "code",
"execution_count": 30,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[(500500, 1000, 7.408205419778824e-05),\n",
" (50005000, 10000, 0.0007085069664753973),\n",
" (5000050000, 100000, 0.007762196997646242),\n",
" (500000500000, 1000000, 0.0752282990142703)]"
]
},
"execution_count": 30,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"mapfunctime(sum2)"
]
},
{
"cell_type": "code",
"execution_count": 31,
"metadata": {},
"outputs": [],
"source": [
"x = mapfunctime(sum3)"
]
},
{
"cell_type": "code",
"execution_count": 32,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[1000, 10000, 100000, 1000000]"
]
},
"execution_count": 32,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"[b for a,b,c in x]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Plotting the execution times"
]
},
{
"cell_type": "code",
"execution_count": 33,
"metadata": {},
"outputs": [],
"source": [
"import matplotlib.pyplot as plt\n",
"import numpy as np"
]
},
{
"cell_type": "code",
"execution_count": 34,
"metadata": {},
"outputs": [],
"source": [
"def plottimes(func):\n",
" ''' plot the execution time on XY graph '''\n",
" fig, ax = plt.subplots() # Create a figure containing a single axes.\n",
" ax.set_xlabel('elements') # Add an x-label to the axes.\n",
" ax.set_ylabel('execution time') # Add a y-label to the axes.\n",
" ax.set_title(\"Complexity: \" + func.__name__) # Add a title to the axes.\n",
" data = mapfunctime(func)\n",
" x = [arg for val, arg, time in data]\n",
" y = [time for val, arg, time in data]\n",
" ax.plot(x, y)\n"
]
},
{
"cell_type": "code",
"execution_count": 35,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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\n",
"text/plain": [
"