## Code for UW CSE 160 lecture on algorithmic speed import math import timeit import matplotlib.pyplot as plt import random ########################################################################### ### Timing execution of a function and plotting results ### def time_fn(fn, n, repeat=1): """Return the time taken by fn(n)""" return timeit.Timer(fn + '(' + str(n) + ')', "from __main__ import " + fn).timeit(repeat) def fn_times(fn, n, step=10): """Timing data for calls to the function, with argument from 0 up to n. Result is a pair of lists: a list of lengths and a list of times.""" points = [step ** i for i in range(0, int(math.log(n, step)+1.5))] return (points, [time_fn(fn, i) for i in points] ) # print fn_times("make_pairs_quadratic", 10000, 10) def start_plot(xlabel): plt.clf() plt.xlabel(xlabel) plt.ylabel("Time") def plot_fn(fn, n, step): (args, times) = fn_times(fn, n, step) plt.plot(args, times, label=fn) plt.legend() ########################################################################### ### Making pairs ("zipping"): quadratic vs. linear ### def make_pairs_quadratic(list1, list2): """Return a list of pairs. Each pair is made of corresponding elements of list1 and list2. list1 and list2 must be of the same length.""" result = [] for i1 in range(len(list1)): elt1 = list1[i1] for i2 in range(len(list2)): elt2 = list2[i2] if i1 == i2: result.append([elt1, elt2]) return result assert make_pairs_quadratic([100, 200], [101, 201]) == [[100, 101], [200, 201]] assert make_pairs_quadratic([0, 1, 2, 3, 4, 5], [0, 1, 4, 9, 16, 25]) == [[0, 0], [1, 1], [2, 4], [3, 9], [4, 16], [5, 25]] def make_pairs_quadratic_by_len(n): list = range(n) make_pairs_quadratic(list, list) def make_pairs_linear(list1, list2): """Return a list of pairs. Each pair is made of corresponding elements of list1 and list2. list1 and list2 must be of the same length.""" result = [] for i in range(len(list1)): elt1 = list1[i] elt2 = list2[i] result.append([elt1, elt2]) return result assert make_pairs_linear([0, 1, 2, 3, 4, 5], [0, 1, 4, 9, 16, 25]) == [[0, 0], [1, 1], [2, 4], [3, 9], [4, 16], [5, 25]] def make_pairs_linear_by_len(n): list = range(n) make_pairs_linear(list, list) def plot_make_pairs_quadratic(n, step): start_plot("Length") plot_fn("make_pairs_quadratic_by_len", n, step) plt.show() def plots1(): plot_make_pairs_quadratic(1000, 2) plot_make_pairs_quadratic(10000, 2) def plot_make_pairs_quadratic_and_linear(n, step): start_plot("Length") plot_fn("make_pairs_quadratic_by_len", n, step) plot_fn("make_pairs_linear_by_len", n, step) plt.show() def plots2(): plot_make_pairs_quadratic_and_linear(1000, 2) plot_make_pairs_quadratic_and_linear(10000, 2) ########################################################################### ### Searching: linear search vs. binary search ### def lsearch(value, lst): """Return index of value in list lst. The value must be in the list.""" return lst.index(value) def bsearch(value, sortedlist): """Return index of value in sortedlist. The value must be in the list. sortedlist must be sorted.""" return bsearch2(value, sortedlist, 0, len(sortedlist)) def bsearch2(value, sortedlist, lower, upper): """Return index of value, which appears in lower:upper, inclusive. This is a helper function for bsearch().""" if lower > upper: raise ValueError(str(value) + " is not in list") mid = (lower + upper) / 2 midvalue = sortedlist[mid] if value == midvalue: return mid if value > midvalue: return bsearch2(value, sortedlist, mid+1, upper) if value < midvalue: return bsearch2(value, sortedlist, lower, mid-1) raise AssertionError("This can't happen") def test_search(search_fn): assert lsearch(5, [1, 2, 3, 4, 5]) == 4 assert lsearch(1, [1, 2, 3, 4, 5]) == 0 assert bsearch(5, [1, 2, 3, 4, 5]) == 4 assert bsearch(1, [1, 2, 3, 4, 5]) == 0 test_search("lsearch") test_search("bsearch") # Maps from a number to a list that long. # Precomputed to avoid biasing timings on long lists. powers_of_ten_lists = { 10**i : range(10**i) for i in range(8) } def run_lsearch(length): thelist = powers_of_ten_lists[length] lsearch(random.randint(0,length-1), thelist) def run_bsearch(length): thelist = powers_of_ten_lists[length] bsearch(random.randint(0,length-1), thelist) def plot_lsearch(n, step=10): start_plot("Length") plot_fn("run_lsearch", n, step) plt.show() def plots3(): plot_lsearch(1000) plot_lsearch(10000000) def plot_lbsearch(n, step=10): start_plot("Length") plot_fn("run_lsearch", n, step) plot_fn("run_bsearch", n, step) plt.show() def plots4(): plot_lbsearch(1000) plot_lbsearch(10000000) ########################################################################### ### Sorting: selection sort vs. quicksort ### def selection_sort(lst): """Return a sorted version of the input list. The input list is not modified.""" remaining = list(lst) result = [] while len(remaining) > 0: minval = min(remaining) remaining.remove(minval) result.append(minval) return result def min_index(lst): """Return the index of the smallest element of lst.""" return lst.index(min(lst)) assert selection_sort([3, 1, 4, 1, 5, 9, 2, 6, 5]) == [1, 1, 2, 3, 4, 5, 5, 6, 9] def quicksort(thelist): """Return a sorted version of the input list thelist. The input list is not modified.""" if len(thelist) < 2: return thelist pivot = thelist[0] smaller = [elt for elt in thelist if elt < pivot] pivots = [elt for elt in thelist if elt == pivot] larger = [elt for elt in thelist if elt > pivot] return quicksort(smaller) + pivots + quicksort(larger) assert quicksort([3, 1, 4, 1, 5, 9, 2, 6, 5]) == [1, 1, 2, 3, 4, 5, 5, 6, 9] def run_ssort(length): thelist = [random.random() for i in range(length)] selection_sort(thelist) def run_qsort(length): thelist = [random.random() for i in range(length)] quicksort(thelist) def plot_ssort(n, step=2): start_plot("Length") plot_fn("run_ssort", n, step) plt.show() def plots5(): plot_ssort(1000) plot_ssort(10000) plot_ssort(20000) def plot_sqsort(n, step=2): start_plot("Length") plot_fn("run_ssort", n, step) plot_fn("run_qsort", n, step) plt.show() def plots6(): plot_sqsort(1000) plot_sqsort(10000) plot_sqsort(20000) def plot_qsort(n, step=2): start_plot("Length") plot_fn("run_qsort", n, step) plt.show() def plots7(): plot_qsort(20000) plot_qsort(500000) ########################################################################### ### All the plots for the lecture ### def lecture(): plots1() plots2() plots3() plots4() plots5() plots6() plots7() lecture()