Skip to content

Instantly share code, notes, and snippets.

@mk12
Last active April 17, 2017 13:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mk12/11f2b73aca26a0b485a6b84da7aa3933 to your computer and use it in GitHub Desktop.
Save mk12/11f2b73aca26a0b485a6b84da7aa3933 to your computer and use it in GitHub Desktop.
Preparation for the CS 341 final exam
#!/usr/bin/env python3
from heapq import heappush, heappop
import random
# Practice for the final exam
###
### Algorithms
###
### Part 1: Divide and conquer
def select_sort(A):
n = len(A)
for i in range(n):
min_index = i
for j in range(i + 1, n):
if A[j] < A[min_index]:
min_index = j
A[i], A[min_index] = A[min_index], A[i]
return A
def merge_sort(A):
n = len(A)
if n <= 1:
return A
L = merge_sort(A[:n//2])
R = merge_sort(A[n//2:])
combined = []
a = 0
b = 0
for i in range(n):
if a >= len(L):
combined.append(R[b])
b += 1
elif b >= len(R):
combined.append(L[a])
a += 1
elif L[a] < R[b]:
combined.append(L[a])
a += 1
else:
combined.append(R[b])
b += 1
return combined
def linear_search(A, x):
for i in range(len(A)):
if A[i] == x:
return True
return False
def dc_search(A, x):
n = len(A)
if n == 0:
return False
if n == 1:
return A[0] == x
return dc_search(A[:n//2], x) or dc_search(A[n//2:], x)
def naive_select(A, k):
n = len(A)
assert k < n
A.sort()
return A[k]
def quick_select(A, k):
n = len(A)
assert k < n
if n == 1:
return A[0]
p = random.randint(0, n - 1)
L = [A[i] for i in range(n) if i != p and A[i] <= A[p]]
R = [A[i] for i in range(n) if i != p and A[i] > A[p]]
if len(L) == k:
return A[p]
if len(L) < k:
return quick_select(R, k - len(L) - 1)
return quick_select(L, k)
def mom_select(A, k):
n = len(A)
assert k < n
if n == 1:
return A[0]
M = []
i = 0
while i < n:
j = min(i + 5, n)
M.append(naive_select(A[i:j], (j - i) // 2))
i += 5
p = A.index(mom_select(M, len(M) // 2))
L = [A[i] for i in range(n) if i != p and A[i] <= A[p]]
R = [A[i] for i in range(n) if i != p and A[i] > A[p]]
if len(L) == k:
return A[p]
if len(L) < k:
return mom_select(R, k - len(L) - 1)
return mom_select(L, k)
def dominates(p, q):
return p[0] > q[0] and p[1] > q[1]
def dist_sq(p, q):
return (p[0] - q[0])**2 + (p[1] - q[1])**2
def naive_2d_maxima(P):
return {p for p in P if not any(dominates(q, p) for q in P)}
def dc_2d_maxima(P):
P.sort()
def go(P):
n = len(P)
if n == 0:
return set()
if n == 1:
return {P[0]}
ML = go(P[:n//2])
MR = go(P[n//2:])
right_max_y = max(p[1] for p in MR)
return MR | {p for p in ML if p[1] > right_max_y}
return go(P)
def naive_closest_pair(P):
n = len(P)
assert n >= 2
pair = None
dist = float('inf')
for i, p in enumerate(P):
for q in P[i+1:]:
d = dist_sq(p, q)
if d < dist:
dist = d
pair = (p, q)
return pair
def dc_closest_pair(P):
P.sort()
def go(P):
n = len(P)
assert n >= 2
if n == 2:
return (P[0], P[1])
if n == 3:
return min([(P[0], P[1]), (P[0], P[2]), (P[1], P[2])],
key=lambda p: dist_sq(*p))
pairL = go(P[:n//2])
pairR = go(P[n//2:])
mid_x = P[n//2][0]
delta = min(dist_sq(*pairL), dist_sq(*pairR))
S = [p for p in P if abs(p[0] - mid_x) <= delta]
S.sort(key=lambda p: p[1])
pairS = None
dist = float('inf')
for i, p in enumerate(S):
j = i + 1
while j < len(S) and S[j][1] - S[i][1] < delta:
d = dist_sq(S[i], S[j])
if d < dist:
dist = d
pairS = (S[i], S[j])
j += 1
return min((pairL, pairR, pairS), key=lambda p: dist_sq(*p))
return go(P)
### Part 2: Greedy algorithms
def activity_selection(R):
result = []
R.sort(key=lambda r: r[1])
for s, f in R:
if not result or s >= result[-1][1]:
result.append((s, f))
return result
def job_scheduling(J):
J.sort()
return J
def weighted_job_scheduling(J):
J.sort(key=lambda job: job[0] / job[1])
return J
def stable_marriage(M, W):
assert len(M) == len(W)
n = len(M)
def prefers(w, m1, m2):
ranks = W[w]
return m1 in ranks and ranks.index(m1) < ranks.index(m2)
free_men = list(M.keys())
women_to_men = {}
while free_men:
m = free_men.pop(0)
w = M[m].pop(0)
if w in women_to_men:
current = women_to_men[w]
if prefers(w, m, current):
women_to_men[w] = m
free_men.append(current)
else:
free_men.append(m)
else:
women_to_men[w] = m
return {(m, w) for w, m in women_to_men.items()}
### Part 3: Dynamic programming
def lin_ind_set(V):
n = len(V)
if n == 0:
return [], 0
if n == 1:
return [0], V[0]
A = [0] * n
# Base cases
A[0] = V[0]
A[1] = max(V[0], V[1])
# Bottom-up iterative solution
for i in range(2, n):
A[i] = max(A[i-1], A[i-2] + V[i])
# Backtrack to get optimal subset
S = []
i = n - 1
while i >= 0:
if i != 0 and A[i] == A[i-1]:
i -= 1
else:
S.insert(0, i)
i -= 2
assert sum(V[i] for i in S) == A[-1]
return S, A[-1]
def seq_alignment(X, Y, alpha, delta):
m = len(X)
n = len(Y)
C = [[0] * (n + 1) for _ in range(m + 1)]
# Base cases
for i in range(m + 1):
C[i][0] = i * delta
for j in range(n + 1):
C[0][j] = j * delta
# Bottom-up iterative solution
for i in range(1, m + 1):
for j in range(1, n + 1):
C[i][j] = min(
C[i-1][j] + delta,
C[i][j-1] + delta,
C[i-1][j-1] + alpha(X[i-1], Y[j-1]))
# Backtrack
seq_x = []
seq_y = []
i = m
j = n
while i > 0 and j > 0:
if C[i][j] == C[i-1][j] + delta:
seq_x.insert(0, X[i-1])
seq_y.insert(0, '-')
i -= 1
elif C[i][j] == C[i][j-1] + delta:
seq_x.insert(0, '-')
seq_y.insert(0, Y[j-1])
j -= 1
else:
assert C[i][j] == C[i-1][j-1] + alpha(X[i-1], Y[j-1])
seq_x.insert(0, X[i-1])
seq_y.insert(0, Y[j-1])
i -= 1
j -= 1
assert i == 0 or j == 0
while i > 0:
seq_x.insert(0, X[i-1])
seq_y.insert(0, '-')
i -= 1
while j > 0:
seq_x.insert(0, '-')
seq_y.insert(0, Y[j-1])
j -= 1
def calc_cost(x, y):
if x == '-' or y == '-':
return delta
return alpha(x, y)
cost = sum(calc_cost(x, y) for x, y in zip(seq_x, seq_y))
assert cost == C[m][n]
return "".join(seq_x), "".join(seq_y), cost
def optimal_parens(D):
n = len(D) - 1
assert n >= 1
C = [[0] * n for _ in range(n)]
# Base case: C[i][i] == 0 for i from 0 to n-1
# Bottom-up iterative solution
for i in range(n, -1, -1):
for j in range(i + 1, n):
C[i][j] = min(
C[i][k] + C[k+1][j] + D[i] * D[k+1] * D[j+1]
for k in range(i, j))
# Backtrack: too hard
return C[0][n-1]
def value_activity_selection(R):
n = len(R)
if n == 0:
return [], 0
R.sort(key=lambda r: r[1])
C = [0] * n
# Base case
C[0] = R[0][2]
# Build helper array
z = [0] * n
for i in range(n):
j = i - 1
while j >= 0 and R[j][1] > R[i][0]:
j -= 1
z[i] = j
# Bottom-up iterative solution
for i in range(1, n):
if z[i] == -1:
C[i] = max(C[i-1], R[i][2])
else:
C[i] = max(C[i-1], C[z[i]] + R[i][2])
# Backtrack
i = n - 1
schedule = []
while i >= 0:
if i > 0 and C[i] == C[i-1]:
i -= 1
else:
schedule.insert(0, R[i])
i = z[i]
cost = sum(r[2] for r in schedule)
assert cost == C[n-1]
return schedule, cost
### Part 4: Graph algorithms
WHITE = 0
GREY = 1
BLACK = 2
def breadth_first_search(G, s):
color = {v: WHITE for v in G}
color[s] = GREY
pi = {s: None}
dist = {s: 0}
queue = [s]
while queue:
u = queue.pop(0)
for v in G[u]:
if color[v] == WHITE:
color[v] = GREY
pi[v] = u
dist[v] = dist[u] + 1
queue.append(v)
color[u] = BLACK
return color, pi, dist
def depth_first_search(G, s):
color = {v: WHITE for v in G}
pi = {s: None}
time = 0
finish_time = {}
stack = [s]
while stack:
u = stack.pop()
if color[u] == WHITE:
color[u] = GREY
stack.append(u)
for v in G[u]:
if color[v] == WHITE:
stack.append(v)
pi[v] = u
elif color[u] == GREY:
color[u] = BLACK
finish_time[u] = time
time += 1
return color, pi, finish_time
def depth_first_search_rec(G, s=None):
color = {v: WHITE for v in G}
pi = {}
time = 0
finish_time = {}
def visit(u):
nonlocal time
color[u] = GREY
for v in reversed(G[u]):
if color[v] == WHITE:
pi[v] = u
visit(v)
color[u] = BLACK
finish_time[u] = time
time += 1
if s:
# Single source
pi[s] = None
visit(s)
else:
# DFS forest
for s in G:
if color[s] == WHITE:
pi[s] = None
visit(s)
return color, pi, finish_time
def bipartition(G):
seen = set()
partition = {}
for s in G:
if not s in seen:
color, _, dist = breadth_first_search(G, s)
seen.update(v for v, c in color.items() if c == BLACK)
for v, d in dist.items():
partition[v] = d % 2
for u in G:
for v in G[u]:
if partition[u] == partition[v]:
return False
return partition
def undirected_has_cycle(G):
color = {v: WHITE for v in G}
cycle = False
def visit(u, p):
nonlocal cycle
if cycle:
return
color[u] = GREY
for v in G[u]:
if color[v] == WHITE:
visit(v, u)
elif v != p and color[v] == GREY:
cycle = True
color[u] = BLACK
for s in G:
if color[s] == WHITE:
visit(s, None)
if cycle:
return True
return cycle
def topological_sort(G):
color = {v: WHITE for v in G}
order = []
dag = True
def visit(u):
nonlocal dag
if not dag:
return
order.append(u)
color[u] = GREY
for v in G[u]:
if color[v] == WHITE:
visit(v)
elif color[v] == GREY:
dag = False
color[u] = BLACK
for s in G:
if color[s] == WHITE:
visit(s)
if not dag:
return False
return order
def topological_sort_alt(G):
in_degree = {v : 0 for v in G}
for neighbours in G.values():
for v in neighbours:
in_degree[v] += 1
queue = []
for v in G:
if in_degree[v] == 0:
queue.append(v)
order = []
while queue:
u = queue.pop(0)
order.append(u)
for v in G[u]:
in_degree[v] -= 1
if in_degree[v] == 0:
queue.append(v)
if len(order) < len(G):
return False
return order
def reverse_graph(G):
Grev = {v: [] for v in G}
for u, neighbours in G.items():
for v in neighbours:
Grev[v].append(u)
return Grev
def kosaraju(G):
f = depth_first_search_rec(G)[2]
Grev = reverse_graph(G)
color = {v: WHITE for v in G}
sccs = []
def visit(u):
color[u] = GREY
sccs[-1].append(u)
for v in Grev[u]:
if color[v] == WHITE:
visit(v)
color[u] = BLACK
for s in sorted(G.keys(), key=lambda v: f[v], reverse=True):
if color[s] == WHITE:
sccs.append([])
visit(s)
return sccs
def kruskal(G, w, s):
mst = {v: [] for v in G}
edges = sorted(w.keys(), key=lambda e: w[e])
for u, v in edges:
mst[u].append(v)
mst[v].append(u)
if undirected_has_cycle(mst):
mst[u].pop()
mst[v].pop()
return breadth_first_search(mst, s)[1]
def prim(G, w, s):
def get_weight(u, v):
if (u, v) in w:
return w[u, v]
return w[v, u]
pi = {s: None}
connected = {s}
pq = []
for v in G[s]:
heappush(pq, (get_weight(s, v), s, v))
while pq:
_, u, v = heappop(pq)
assert u in connected
if not v in connected:
connected.add(v)
pi[v] = u
for z in G[v]:
heappush(pq, (get_weight(v, z), v, z))
return pi
def bfs_sssp(G, s):
return breadth_first_search(G, s)[2]
def dijkstra(G, s, w=None):
def get_weight(u, v):
return w[u, v] if w else 1
dist = {s: 0}
entries = {}
pq = []
for v in G[s]:
d = get_weight(s, v)
entry = [d, v, True]
dist[v] = d
entries[v] = entry
heappush(pq, entry)
while pq:
u_dist, u, valid = heappop(pq)
if valid:
for v in G[u]:
new_dist = u_dist + get_weight(u, v)
if not v in dist or new_dist < dist[v]:
dist[v] = new_dist
entry = [new_dist, v, True]
if v in entries:
entries[v][2] = False
entries[v] = entry
heappush(pq, entry)
return dist
def dag_sssp(G, s, w=None):
def get_weight(u, v):
return w[u, v] if w else 1
dist = {s: 0}
order = topological_sort(G)
assert order
for u in order:
for v in G[u]:
new_dist = dist[u] + get_weight(u, v)
if not v in dist or new_dist < dist[v]:
dist[v] = new_dist
return dist
def floyd_warshall(W):
n = len(W)
assert all(len(row) == n for row in W)
D = [row[:] for row in W]
E = [[0] * n for _ in range(n)]
for m in range(n):
for i in range(n):
for j in range(n):
E[i][j] = min(D[i][j], D[i][m] + D[m][j])
D, E = E, D
return D
def complement_graph(G):
return {u: [v for v in G if u != v and not v in G[u]] for u in G}
### Part 5: Intractability and undecidability
def clique_to_vertex_cover(G, k, vertex_cover):
n = len(G)
assert k <= n
Gcomp = complement_graph(G)
return vertex_cover(Gcomp, n - k)
###
### Tests
###
def test_sort(sort):
assert sort([]) == []
assert sort([1]) == [1]
assert sort([1, 2, 3]) == [1, 2, 3]
assert sort([3, 2, 1]) == [1, 2, 3]
assert sort([99, -1, 0, 0, 2, 42]) == [-1, 0, 0, 2, 42, 99]
def test_search(search):
assert not search([], 1)
assert not search([1], 2)
assert not search([1, 3], 2)
assert search([1], 1)
assert search([1, 2], 1)
assert search([2, 1], 1)
assert search([1, 2, 3], 2)
def test_select(select):
assert select([1], 0) == 1
assert select([1, 2, 3], 0) == 1
assert select([1, 2, 3], 1) == 2
assert select([1, 2, 3], 2) == 3
assert select([-100, 3, -3], 2) == 3
assert select([5, 3, 2, 5, 2, 3, 3, 3, 4], 6) == 4
def test_2d_maxima(maxima):
assert maxima([]) == set()
assert maxima([(1, 1)]) == {(1,1)}
assert maxima([(1, 1), (2, 2), (3, 3)]) == {(3, 3)}
assert maxima([(3, 1), (2, 2), (1, 3)]) == {(3, 1), (2, 2), (1, 3)}
assert (maxima([(1, 4), (4, 2), (2, -1), (0, 0), (2, 6)])
== {(4, 2), (2, 6)})
def test_closest_pair(closest):
assert closest([(0, 0), (0, 0)]) == ((0, 0), (0, 0))
assert closest([(1, 2), (3, 4)]) == ((1, 2), (3, 4))
assert closest([(0 ,0), (1, 2), (1, 0)]) == ((0, 0), (1, 0))
assert closest([(0 ,0), (4, 2), (3, 7), (8, 8), (2, 3)]) == ((4, 2), (2, 3))
def test_activity_selection(activity):
assert activity([]) == []
assert activity([(1, 2)]) == [(1, 2)]
assert activity([(1, 2), (1, 3), (2, 3), (3, 4)]) == [(1, 2), (2, 3), (3, 4)]
assert (activity([(1, 4), (-1, 5), (2, 3), (5, 6), (4, 6), (5, 6)])
== [(2, 3), (5, 6)])
def test_job_scheduling(schedule):
assert schedule([]) == []
assert schedule([1, 2, 3]) == [1, 2, 3]
assert schedule([3, 2, 1]) == [1, 2, 3]
def test_weighted_job_scheduling(schedule):
assert schedule([]) == []
assert schedule([(1, 1), (2, 1), (3, 1)]) == [(1, 1), (2, 1), (3, 1)]
assert schedule([(3, 1), (2, 1), (1, 1)]) == [(1, 1), (2, 1), (3, 1)]
assert schedule([(1, 1), (1, 2), (1, 3)]) == [(1, 3), (1, 2), (1, 1)]
assert schedule([(2, 3), (1, 4), (9, 3)]) == [(1, 4), (2, 3), (9, 3)]
def test_stable_marriage(marry):
assert marry({}, {}) == set()
assert (marry({"A": [1, 2], "B": [2, 1]}, {1: ["A", "B"], 2: ["B", "A"]})
== {("A", 1), ("B", 2)})
assert (marry({"A": [1, 2], "B": [1, 2]}, {1: ["A", "B"], 2: ["A", "B"]})
== {("A", 1), ("B", 2)})
assert (marry(
{"A": [1, 2, 3], "B": [3, 1, 2], "C": [1, 3, 2]},
{1: ["C", "A", "B"], 2: ["A", "C", "B"], 3: ["C", "A", "B"]})
== {("A", 2), ("B", 3), ("C", 1)})
def test_lin_ind_set(ind_set):
assert ind_set([]) == ([], 0)
assert ind_set([100]) == ([0], 100)
assert ind_set([1, 2, 3, 4]) == ([1, 3], 6)
assert ind_set([3, 7, 9, 6]) == ([1, 3], 13)
assert ind_set([3, 1, 1, 4, 1, 8, 7, 9, 11]) == ([0, 3, 5, 8], 26)
def test_seq_alignment(align):
def alpha_fn(map):
return lambda x, y: map.get(tuple(sorted([x, y])), 0)
alpha1 = lambda x, y: 0
alpha2 = lambda x, y: 1
alpha3 = lambda x, y: int(x != y)
alpha4 = alpha_fn({
("A", "G"): 1, ("A", "C"): 2, ("A", "T"): 10, ("C", "T"): 5})
alpha5 = alpha_fn({
("A", "C"): 1, ("C", "T"): 2, ("G", "T"): 3})
assert align("", "", alpha1, 0) == ("", "", 0)
assert align("", "", alpha2, 10) == ("", "", 0)
assert align("", "", alpha5, 99) == ("", "", 0)
assert align("AAA", "AAA", alpha1, 0) == ("---AAA", "AAA---", 0)
assert align("AAA", "AAA", alpha1, 1) == ("AAA", "AAA", 0)
assert align("AAA", "AAA", alpha2, 0) == ("---AAA", "AAA---", 0)
assert align("AAA", "AAA", alpha2, 2) == ("AAA", "AAA", 3)
assert align("AAA", "AAA", alpha3, 0) == ("---AAA", "AAA---", 0)
assert align("AAA", "AAA", alpha3, 1) == ("AAA", "AAA", 0)
assert align("AGA", "GGT", alpha1, 0) == ("---AGA", "GGT---", 0)
assert align("AGA", "GGT", alpha1, 1) == ("AGA", "GGT", 0)
assert align("AGA", "GGT", alpha2, 2) == ("AGA", "GGT", 3)
assert align("AGA", "GGT", alpha3, 0) == ("---AGA", "GGT---", 0)
assert align("AGA", "GGT", alpha3, 1) == ("AGA", "GGT", 2)
assert align("AGA", "GGT", alpha4, 1) == ("AG-A", "GGT-", 3)
assert align("AGA", "GGT", alpha4, 2) == ("AG-A", "GGT-", 5)
assert align("AGA", "GGT", alpha4, 4) == ("AG-A", "GGT-", 9)
assert align("AGA", "GGT", alpha4, 5) == ("AG-A", "GGT-", 11)
assert align("AGA", "GGT", alpha4, 6) == ("AGA", "GGT", 11)
assert (align("AAGTACTC", "GAATTCCAG", alpha4, 1) ==
("-AAGT--ACTC", "GAATTCCAG--", 5))
assert (align("AAGTACTC", "GAATTCCAG", alpha4, 2) ==
("-AAGTAC-TC", "GAATTCCAG-", 8))
assert (align("AAGTACTC", "GAATTCCAG", alpha5, 1) ==
("AAGTAC-TC", "GAATTCCAG", 1))
def test_optimal_parens(parens):
assert parens([1, 1]) == 0
assert parens([100, 100]) == 0
assert parens([1, 1, 1]) == 1
assert parens([100, 100, 100]) == 1000000
assert parens([10, 5, 10, 10]) == 1000
def test_value_activity_selection(select):
assert select([]) == ([], 0)
assert select([(0, 1, 5)]) == ([(0, 1, 5)], 5)
assert select([(0, 2, 5), (1, 3, 10)]) == ([(1, 3, 10)], 10)
assert select([(0, 2, 10), (1, 3, 5)]) == ([(0, 2, 10)], 10)
assert (select(
[(0, 2, 1), (1, 3, 4), (5, 6, 2), (4, 7, 3), (1, 4, 5), (2, 5, 6)])
== ([(0, 2, 1), (2, 5, 6), (5, 6, 2)], 9))
def test_graph_search(search):
assert search({0: []}, 0)[:2] == ({0: BLACK}, {0: None})
assert (search({0: [1], 1: [0]}, 0)[:2] ==
({0: BLACK, 1: BLACK}, {0: None, 1: 0}))
assert (search({0: [1], 1: [0]}, 1)[:2] ==
({0: BLACK, 1: BLACK}, {0: 1, 1: None}))
assert (search({0: [1], 1: []}, 0)[:2] ==
({0: BLACK, 1: BLACK}, {0: None, 1: 0}))
assert (search({0: [1], 1: []}, 1)[:2] ==
({0: WHITE, 1: BLACK}, {1: None}))
assert (search({"A": ["B", "C"], "B": ["C"], "C": ["A"]}, "A")[:2] ==
({"A": BLACK, "B": BLACK, "C": BLACK},
{"A": None, "B": "A", "C": "A"}))
assert (search({"A": ["B", "C"], "B": ["C"], "C": ["A"]}, "B")[:2] ==
({"A": BLACK, "B": BLACK, "C": BLACK},
{"B": None, "C": "B", "A": "C"}))
assert (search({"A": ["B", "C"], "B": ["C"], "C": ["A"]}, "C")[:2] ==
({"A": BLACK, "B": BLACK, "C": BLACK},
{"C": None, "A": "C", "B": "A"}))
def test_graph_search_bfs(search):
assert (search(
{1: [2, 3], 2: [1, 4], 3: [1, 5], 4: [2, 6], 5: [3, 6], 6: [4, 5]}, 1)
== ({v: BLACK for v in range(1, 7)},
{1: None, 2: 1, 3: 1, 4: 2, 5: 3, 6: 4},
{1: 0, 2: 1, 3: 1, 4: 2, 5: 2, 6: 3}))
assert (search(
{1: [2, 3], 2: [1, 4], 3: [1, 5], 4: [2, 6], 5: [3, 6], 6: [4, 5]}, 6)
== ({v: BLACK for v in range(1, 7)},
{6: None, 4: 6, 5: 6, 2: 4, 3: 5, 1: 2},
{6: 0, 4: 1, 5: 1, 2: 2, 3: 2, 1: 3}))
assert (search(
{1: [2, 3], 2: [4], 3: [5], 4: [6], 5: [6], 6: []}, 1)
== ({v: BLACK for v in range(1, 7)},
{1: None, 2: 1, 3: 1, 4: 2, 5: 3, 6: 4},
{1: 0, 2: 1, 3: 1, 4: 2, 5: 2, 6: 3}))
assert (search(
{1: [2, 3], 2: [4], 3: [5], 4: [6], 5: [6], 6: []}, 6)
== ({v: BLACK if v == 6 else WHITE for v in range(1, 7)},
{6: None},
{6: 0}))
def test_graph_search_dfs(search):
assert (search(
{1: [2, 3], 2: [1, 4], 3: [1, 5], 4: [2, 6], 5: [3, 6], 6: [4, 5]}, 1)
== ({v: BLACK for v in range(1, 7)},
{1: None, 3: 1, 5: 3, 6: 5, 4: 6, 2: 4},
{2: 0, 4: 1, 6: 2, 5: 3, 3: 4, 1: 5}))
assert (search(
{1: [2, 3], 2: [1, 4], 3: [1, 5], 4: [2, 6], 5: [3, 6], 6: [4, 5]}, 6)
== ({v: BLACK for v in range(1, 7)},
{6: None, 5: 6, 3: 5, 1: 3, 2: 1, 4: 2},
{4: 0, 2: 1, 1: 2, 3: 3, 5: 4, 6: 5}))
assert (search(
{1: [2, 3], 2: [4], 3: [5], 4: [6], 5: [6], 6: []}, 1)
== ({v: BLACK for v in range(1, 7)},
{1: None, 2: 1, 3: 1, 4: 2, 5: 3, 6: 5},
{6: 0, 5: 1, 3: 2, 4: 3, 2: 4, 1: 5}))
assert (search(
{1: [2, 3], 2: [4], 3: [5], 4: [6], 5: [6], 6: []}, 6)
== ({v: BLACK if v == 6 else WHITE for v in range(1, 7)},
{6: None},
{6: 0}))
def test_bipartition(bipart):
assert bipart({}) == {}
assert bipart({0: []}) == {0: 0}
assert bipart({0: [], 1: []}) == {0: 0, 1: 0}
assert bipart({0: [1], 1: [0]}) == {0: 0, 1: 1}
assert bipart({0: [1], 1: [0, 2], 2: [1]}) == {0: 0, 1: 1, 2: 0}
assert not bipart({0: [1, 2], 1: [0, 2], 2: [1, 0]})
assert bipart({0: [], 1: [2], 2: [1]}) == {0: 0, 1: 0, 2: 1}
assert (bipart({0: [1, 3], 1: [0, 2], 2: [1, 3], 3: [0, 2]})
== {0: 0, 1: 1, 2: 0, 3: 1})
assert not bipart({0: [1, 2, 3], 1: [0, 2], 2: [0, 1, 3], 3: [0, 2]})
def test_undirected_has_cycle(has_cycle):
assert not has_cycle({})
assert not has_cycle({0: []})
assert not has_cycle({0: [], 1: []})
assert not has_cycle({0: [1], 1: [0]})
assert has_cycle({0: [1, 2], 1: [0, 2], 2: [0, 1]})
assert has_cycle({0: [1, 3], 1: [0, 2], 2: [1, 3], 3: [2, 0]})
assert not has_cycle({0: [1], 1: [0, 2], 2: [1, 3], 3: [2]})
def test_topological_sort(sort):
assert sort({}) == []
assert sort({0: []}) == [0]
assert sort({0: [], 1: []}) == [0, 1]
assert sort({0: [1], 1: []}) == [0, 1]
assert not sort({0: [1], 1: [0]})
assert sort({0: [1], 1: [2], 2: [3], 3: []}) == [0, 1, 2, 3]
assert (sort({0: [1], 1: [2, 3], 2: [4], 3: [5], 4: [], 5: []})
in [[0, 1, 2, 4, 3, 5], [0, 1, 2, 3, 4, 5]])
assert not sort({0: [1], 1: [2, 3], 2: [4], 3: [5], 4: [], 5: [0]})
def test_reverse_graph(reverse):
assert reverse({}) == {}
assert reverse({0: [], 1: []}) == {0: [], 1: []}
assert reverse({0: [1], 1: []}) == {0: [], 1: [0]}
assert reverse({0: [1], 1: [0]}) == {0: [1], 1: [0]}
def test_find_sccs(find):
assert find({}) == []
assert find({0: []}) == [[0]]
assert find({0: [], 1: []}) == [[1], [0]]
assert find({0: [1], 1: []}) == [[0], [1]]
assert find({0: [], 1: [0]}) == [[1], [0]]
assert find({0: [1], 1: [0]}) == [[0, 1]]
assert find({0: [1, 2], 1: [0, 2], 2: [0, 1]}) == [[0, 1, 2]]
def test_minimum_spanning_tree(mst):
assert mst({0: [1], 1: [0]}, {(0, 1): 5}, 0) == {0: None, 1: 0}
assert (mst({0: [1, 2], 1: [0, 2], 2: [0, 1]},
{(0, 1): 5, (1, 2): 10, (0, 2): 15},
0)
== {0: None, 1: 0, 2: 1})
assert (mst({0: [1, 2], 1: [0, 2], 2: [0, 1]},
{(0, 1): 15, (1, 2): 5, (0, 2): 10},
0)
== {0: None, 2: 0, 1: 2})
def test_single_source_shortest_path(sssp, weighted, cyclic):
assert sssp({0: []}, 0) == {0: 0}
assert sssp({0: [], 1: []}, 0) == {0: 0}
assert sssp({0: [1], 1: []}, 0) == {0: 0, 1: 1}
if cyclic:
assert sssp({0: [1, 2], 1: [0, 2], 2: [0, 1]}, 0) == {0: 0, 1: 1, 2: 1}
if weighted:
assert (sssp({0: [1, 2], 1: [2], 2: []},
0,
{(0, 1): 1, (0, 2): 10, (1, 2): 2})
== {0: 0, 1: 1, 2: 3})
assert (sssp({0: [1], 1: [2, 3, 4], 2: [3, 4], 3: [], 4: []},
0,
{(0, 1): 1, (1, 2): 6, (1, 3): 4, (1, 4): 1, (2, 3): 1,
(2, 4): 2})
== {0: 0, 1: 1, 2: 7, 3: 5, 4: 2})
if weighted and cyclic:
assert (sssp({0: [1], 1: [2, 3, 4], 2: [0, 3, 4], 3: [], 4: []},
0,
{(0, 1): 1, (1, 2): 6, (1, 3): 4, (1, 4): 1, (2, 3): 1,
(2, 4): 2, (2, 0): 3})
== {0: 0, 1: 1, 2: 7, 3: 5, 4: 2})
def test_all_pairs_shortest_path(apsp):
I = float('inf')
assert (apsp([[0, 1, 2, 4],
[I, 0, I, -1],
[I, I, 0, 3],
[I, I, I, 0]])
== [[0, 1, 2, 0],
[I, 0, I, -1],
[I, I, 0, 3],
[I, I, I, 0]])
assert (apsp([[0, 1, 4, 3],
[I, 0, 2, I],
[-1, I, 0, I],
[I, -2, 1, 0]])
== [[0, 1, 3, 3],
[1, 0, 2, 4],
[-1, 0, 0, 2],
[-1, -2, 0, 0]])
def test_complement_graph(complement):
assert complement({}) == {}
assert complement({0: []}) == {0: []}
assert complement({0: [], 1: []}) == {0: [1], 1: [0]}
assert complement({0: [1, 2], 1: [0], 2: [0]}) == {0: [], 1: [2], 2: [1]}
def test_all():
test_sort(select_sort)
test_sort(merge_sort)
test_search(linear_search)
test_search(dc_search)
test_select(naive_select)
test_select(mom_select)
test_2d_maxima(naive_2d_maxima)
test_2d_maxima(dc_2d_maxima)
test_closest_pair(naive_closest_pair)
test_closest_pair(dc_closest_pair)
test_activity_selection(activity_selection)
test_job_scheduling(job_scheduling)
test_weighted_job_scheduling(weighted_job_scheduling)
test_stable_marriage(stable_marriage)
test_lin_ind_set(lin_ind_set)
test_seq_alignment(seq_alignment)
test_optimal_parens(optimal_parens)
test_value_activity_selection(value_activity_selection)
test_graph_search(breadth_first_search)
test_graph_search(depth_first_search)
test_graph_search(depth_first_search_rec)
test_graph_search_bfs(breadth_first_search)
test_graph_search_dfs(depth_first_search)
test_graph_search_dfs(depth_first_search_rec)
test_bipartition(bipartition)
test_undirected_has_cycle(undirected_has_cycle)
test_topological_sort(topological_sort)
test_topological_sort(topological_sort_alt)
test_reverse_graph(reverse_graph)
test_find_sccs(kosaraju)
test_minimum_spanning_tree(kruskal)
test_minimum_spanning_tree(prim)
test_single_source_shortest_path(bfs_sssp, weighted=False, cyclic=True)
test_single_source_shortest_path(dijkstra, weighted=True, cyclic=True)
test_single_source_shortest_path(dag_sssp, weighted=True, cyclic=False)
test_all_pairs_shortest_path(floyd_warshall)
test_complement_graph(complement_graph)
###
### Main
###
if __name__ == "__main__":
test_all()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment