Lecture 1 Recap:

f(n)  is  O(g(n))f(n) \; is \; O(g(n))

If there exists constants cc and n0n_{0} such that

f(n)cg(n)  for  all  n>n0 f(n) \le c\cdot g(n) \; for \;all \; n > n_{0}

  1. Drop lower order terms and constants
  2. Make your bounds as tight as possible
    • Can we say "2n2n is O(n2)O(n^{2})"?
    • Yes, - remember big-O is just an upper bound
    • but this doesn't give us much information about the function's growth rate

Make the smallest possible class of the functions (the "tightest" possible bound)

  1. Simplify as much as possible
    • Use the smallest expression of the class

Operations:

Type name
O(1)O(1) Constant
O(logn)O(\log n) Logarithmic
O(n)O(n) Linear
O(n2)O(n^{2}) Quadratic
O(2n)O(2^{n}) Exponential
O(n!)O(n!) Factorial

Lecture 2 - Analysis of Recursion Algorithms

Recursion has two steps:

Types of recursion:

Linear recursion:

def linear_sum(S, n) """ Compute the sum of a sequence of the first n numbers of Sequence S""" if n == 0: return 0 # This is the base case else: return linear_sum(S, n-1) + S[n-1]

Example 2: reverse a list:

def reverse_list(my_list, i, j): if i < j: tmp = my_list[i] my_list[i] = my_list[j] my_list[j] = tmp return reverse_list(my_list, i+1, j-1) else: return my_list

Tail recusion

Binary recursion

def binary_sum(S, start, stop): if start >= stop: return 0 elif start == stop-1: return S[start] else: mid = (start + stop) // 2 return binary_sum(S, start, mid) + binary_sum(S, mid, stop)

Multiple recursion

import os import sys def directory_tree(path): if os.path.isdir(path): # is this a directory? for thing in os.listdir(path): childpath = os.path.join(path, thing) directory_tree(path) # note – this could be called many times else: # nope, we’ve bottomed out – let’s just print this file/path print (path)

Recursion activity

Base Case

Recursive case

Algorithm selectionSort(A, n) if n > 1 then maxIndex <- 0 for i <- 1 to n - 1 do if A[i] > A[maxIndex] then maxIndex <- i swap(A[maxIndex], A[n – 1]) selectionSort(A, n - 1)

T(n)={O(1)if n=0O(n)+T(n1)if x1 T(n) = \begin{cases} O(1) & \text{if } n = 0\\ O(n) + T(n-1) & \text{if } x \ge 1 \end{cases}

Running time of selectionSort

Alt text

T(n)=O(n)+T(n1)=O(n)+O(n1)+T(n2)=O(n)+O(n1)+O(n2)+T(n3)T(n)=1+2+3+4+...+n1+nT(n)=n(n1)/2T(n)    is    O(n2) \begin{align} T(n) &= O(n) + T(n – 1)\\ &= O(n) + O(n - 1) + T(n – 2) \\ &= O(n) + O(n - 1) + O(n - 2) + T(n – 3)\\ T(n) &= 1+2+3+4+...+n-1 + n \\ T(n) &= n(n-1)/2 \\ T(n) \; \; &\text{is} \; \; O(n^{2}) \\ \end{align}

Fibonacci Algorithm

Algorithm fib(n) for i if n < 2 then return 1 return fib(n - 1) + fib(n - 2)

Alt text

Divide and conquer algorithm paradigm

Search Algorithm

Binary search, list the element in a sorted list in log2nlog_{2} n time

""" Return true us target is found in indicated portion of a Python list The search only considers the portion from data[low] to data[high] inclusive """ def binary_search(data, target, low, high): if low > high: return False else: mid = (low+ high) // 2 if target == data[mid]: #recur on the portion left of the middle return binary_search(data, target, low, mid-1) else: #recur on the portion right of the middle return binary search(data, target, mid + 1, high)

Binary Search Recurrence

We can express the running time of binary search as:

T(n)={O(1)if n=0O(1)+T(n/2)if x>0 T(n) = \begin{cases} O(1) & \text{if } n = 0\\ O(1) + T(n/2) & \text{if } x > 0 \end{cases}

T(n)=T(n/2)+1 T(n) = T(n/2) + 1

T(n/2)=T((n/2)/2)+1=T(n/4)+1 T(n/2) = T((n/2)/2) + 1 = T(n/4) + 1

T(n/4)=T(n/8)+1 T(n/4) = T(n/8) + 1

At the kkth step: Let k=lognk = \log n

T(n/2k)+1=T(n/2log2n)+log2n T(n/2^{k}) + 1 = T(n / 2^{\log_{2}n}) + \log_{2}n

T(n/2k)+1=T(n/n)+log2n T(n/2^{k}) + 1 = T(n /n) + \log_{2}n

T(n/2k)+1=1+log2n T(n/2^{k}) + 1 = 1 + \log_{2}n

Sorting

Bubblesort

Merge-Sort

DIVIDE STEP

Algorithm mergeSort(A, l, r) Input an array A Output A sorted between indices l and r if l < r m <- floor((l + r) ÷ 2 ) mergeSort(S, l, m) mergeSort(S, m + 1, r) merge(S, l, m, r)

CONQUER STEP

Alt text

Alt text

Analysis of Merge Sort:

We can express the running time of mergesort as:

T(n)={O(1)if n<2O(n)+2T(n/2)if n2 T(n) = \begin{cases} O(1) & \text{if } n < 2 \\ O(n) + 2\cdot T(n/2) & \text{if } n \ge 2 \end{cases}

Quicksort

Divide step

Alt text

Performance Analysis

Expected running time

Alt text

In-place Quick-Sort

Alt text

Alt text