# algorithms.py # Walker M. White (wmw2) # November 1, 2012 """ Module with algorithms from the sequence algorithm design slides.""" import random def dnf(b, h, k): """Dutch National Flag algorithm to arrange the elements of b[h..k] Returns: partition points as a tuple (i,j) Precondition: b is a mutable sequence (e.g. a list). h and k are valid positions in b.""" assert type(b) == list, `b`+' is not a list' assert 0 <= h and h < len(b), `h`+' is not a valid position in the list' assert 0 <= k and k < len(b), `k`+' is not a valid position in the list' # Loop variables to satisfy the invariant t = h j = k i= k+1 # inv: b[h..t-1] < 0, b[t..i-1] unknown, b[i..j] = 0, and b[j+1..k] > 0 while t < i: if b[i-1] < 0: _swap(b,i-1,t) t = t+1 elif b[i-1] == 0: i = i-1 else: _swap(b,i-1,j) i = i-1 j = j-1 # post: b[h..i-1] < 0, b[i..j] = 0, and b[j+1..k] > 0 # Return dividers as a tuple return (i, j) # NOTE: This uses a DIFFERENT invariant than the lab def partition(b, h, k): """Partitions list b[h..k] around a pivot x = b[h] Returns: new position of x Precondition: b is a mutable sequence (e.g. a list). h and k are valid positions in b.""" assert type(b) == list, `b`+' is not a list' assert 0 <= h and h < len(b), `h`+' is not a valid position in the list' assert 0 <= k and k < len(b), `k`+' is not a valid position in the list' # position i is end of first paritition range i = h # position j is BEFORE beginning of second partition range j = k # Find the first element in the list. x = b[h] # invariant: b[h..i-1] < x, b[i] = x, b[i+1..j] unknown, and b[j+1..k] >= x while i < j: if b[i+1] >= x: # Move this to the end of the block. _swap(b,i+1,j) j = j - 1 else: # b[i+1] < x _swap(b,i,i+1) i = i + 1 # post: b[h..i-1] < x, b[i] is x, and b[i+1..k] >= x return i # HELPER FUNCTION def _swap(b, h, k): """Procedure swaps b[h] and b[k] Precondition: b is a mutable sequence (e.g. a list). h and k are valid positions in b.""" # We typically do not enforce preconditions on hidden helpers temp = b[h] b[h] = b[k] b[k] = temp # Linear search def linear_search(b,c): """Returns: index of first occurrence of c in b[0..len(b)-1] OR -1 if c is not found Precondition: b is a sequence""" # Quick way to check if a sequence assert len(b) >= 0, `b`+' is a not a sequence (list, string, or tuple)' # Store in i the index of the first c in b[0..] i = 0 # invariant: c is not in b[0..i-1] while i < len(b) and b[i] != c: i = i + 1; # post: b[i] == c OR (i == len(b) and c is not in b[0..i-1]) return i if i < len(b) else -1 # Binary search def binary_search(b,c): """Returns: index of first occurrence of c in b[0..len(b)-1] OR -1 if c is not found Precondition: b is a sorted sequence""" # Quick way to check if a sequence; CANNOT easily check sorted assert len(b) >= 0, `b`+' is a not a sequence (list, string, or tuple)' # Store in i the value BEFORE beginning of range to search i = 0 # Store in j the end of the range to search (element after) j = len(b) # The middle position of the range mid = (i+j)/2 # invariant; b[0..i-1] < c, b[i..j-1] unknown, b[j..] >= c while j > i: if b[mid] < c: i = mid+1 else: # b[mid] >= c j = mid # Compute a new middle. mid = (i+j)/2 # post: i == j and b[0..i-1] < c and b[j..] >= c return i if b[i] == c else -1 def isort(b): """Insertion Sort: sorts the array b in n^2 time Precondition: b is a mutable sequence (e.g. a list).""" assert type(b) == list, `b`+' is not a list' # Start from beginning of list i = 0 # inv: b[0..i-1] sorted while i < len(b): _push_down(b,i) i = i + 1 # post: b[0..len(b)-1] sorted # HELPER FUNCTION def _push_down(b, k): """Moves the value at position k into its sorted position in b[0.k-1]. Precondition: b[0..k-1] is a sorted list""" # We typically do not enforce preconditions on hidden helpers # Start from position k j = k # inv: b[j..k] is sorted while j > 0: if b[j-1] > b[j]: _swap(b,j-1,j) j = j - 1 # post: b[0..k] is sorted def ssort(b): """Selection Sort: sorts the array b in n^2 time Precondition: b is a mutable sequence (e.g. a list).""" assert type(b) == list, `b`+' is not a list' # Start from beginning of list i = 0 # inv: b[0..i-1] sorted while i < len(b): index = _min_index(b,i); _swap(b,i,index) i = i+1 # post: b[0..len(b)-1] sorted # HELPER FUNCTION def _min_index(b, h): """Returns: the index of the minimum value in b[h..] Precondition: b is a mutable sequence (e.g. a list).""" # We typically do not enforce preconditions on hidden helpers # Start from position h i = h index = h; # inv: index position of min in b[h..i-1] while i < len(b): if b[i] < b[index]: index = i i = i+1 # post: index position of min in b[h..len(b)-1] return index def qsort(b): """Quick Sort: sorts the array b in n log n average time Precondition: b is a mutable sequence (e.g. a list).""" assert type(b) == list, `b`+' is not a list' # Send everything to the recursive helper _qsort_helper(b,0,len(b)-1) def _qsort_helper(b, h, k): """Quick Sort: sorts the array b[h..k] in n log n average time Precondition: b is a mutable sequence (e.g. a list). h and k are valid positions in b.""" # We typically do not enforce preconditions on hidden helpers if k-h < 1: # BASE CASE return # RECURSIVE CASE j = partition(b, h, k) # b[h..j-1] <= b[j] <= b[j+1..k] # Sort b[h..j-1] and b[j+1..k] _qsort_helper(b, h, j-1) _qsort_helper(b, j+1, k) def roll(p): """Returns: a random int in 0..len(p)-1; i returned with prob p[i]. Precondition: p a list of positive floats that sum to at least 1.""" # Do not assert precondition; too complicated r = random.random() # r in [0,1) # Think of interval [0,1] as divided into segments of size p[i] # Store into i the segment number in which r falls. i = 0 sum_of = p[0] while r >= sum_of: sum_of = sum_of + p[i+1] i = i + 1 return i def scramble(b): """Scrambles the list to resort again Precondition: b is a mutable sequence (e.g. a list).""" assert type(b) == list, `b`+' is not a list' # Start from the beginning i = 0 # inv: b[0..i-1] is scrambled while i < len(b): size = len(b)-i pos = int(random.random()*size) _swap(b,i,i+pos) i = i+1 # post: b[0..len(b)] is scrambled