# 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