\documentclass[a4paper,12pt]{report}
\usepackage{color}
\usepackage{graphicx}
\usepackage{subfig}
\usepackage{listings}
\usepackage{media9}
\usepackage{hyperref}
\usepackage{amssymb}
\usepackage{mathtools}
\usepackage{bbm}
% \usepackage{algorithm2e}
% \usepackage[ruled,vlined]{algorithm2e}
\usepackage[vlined,ruled,commentsnumbered]{algorithm2e}

% Some nice shortcuts.
\newtheorem{theorem}{Theorem}
\DeclarePairedDelimiter{\ceil}{\lceil}{\rceil}
\DeclarePairedDelimiter{\floor}{\lfloor}{\rfloor}

\definecolor{dkgreen}{rgb}{0,0.6,0}
\definecolor{gray}{rgb}{0.5,0.5,0.5}
\definecolor{mauve}{rgb}{0.58,0,0.82}

\begin{document}

\title{ECE358: Foundations of Computing}
\author{Aman Bhargava}
\date{September-December 2020}
\maketitle

\tableofcontents

\section{Introduction and Course Information}

This document offers an overview of the ECE358 course. They comprise my condensed course notes for the course. No promises are made relating to the correctness or completeness of the course notes. These notes are meant to highlight difficult concepts and explain them simply, not to comprehensively review the entire course.

\paragraph{Course Information}
\begin{itemize}
\item Professors: Prof. Andreas Veneris and Prof. Zissis Poulos
\item Course: Engineering Science, Machine Intelligence Option
\item Term: 2020 Fall
\end{itemize}

\chapter{Math Review}

\section{Logarithms}

\paragraph{Logarithm Rules: }
\begin{itemize}
\item \textbf{Definition: } $a = b^c$ iff $log_b a = c$.
\item $a = b^{\log_b a}$.
\item $\log_c(ab) = \log_c a + log_c b$.
\item $\log_c(a^n) = n\log_c(a)$.
\item $\log_b(a) = \log_a(b)$.
\item $\log(1/a) = -\log a$.
\item $\log(a/c) = \log a - \log c$.
\item $a^{\log n} = n^{\log a}$.
\end{itemize}

\paragraph{Variations on Logarithm}

\begin{equation}
\log^{(i)} n = \begin{cases}
n & \text{iff } i = 0 \\
\log(\log^{(i)} n) & \text{otherwise}
\end{cases}
\end{equation}

\begin{equation}
\log^* n = \begin{cases}
0 & \text{if } n \leq 1 \\
1 + log^*(\log n) & \text{otherwise}
\end{cases}
\end{equation}

\section{Sequences and Series}

\begin{theorem}{Fibonnaci Definition:}
\begin{equation}
F_i = F_{i-1} + F_{i-2}
\end{equation}

Where $F_0 = 0$, $F_1 = 1$.

\begin{equation}
F_i = \frac{\phi^i - \hat \phi^i}{\sqrt{5}}
\end{equation}

Where $\phi = \frac{1+\sqrt{5}}{2}$, $\hat \phi = \frac{1-\sqrt 5}{2}$
\end{theorem}

\begin{theorem}{Arithmetic Series:}
\begin{equation}
\sum_{i=1}^{n} = \frac{n(n+1)}{2} = \Theta(n^2)
\end{equation}
\end{theorem}

\begin{theorem}{Geometric Series:}
\begin{equation}
\sum_{k=0}^{n} x^k = \frac{x^{n+1}-1}{x-1}
\end{equation}
\end{theorem}

\begin{theorem}{Infinite Series:}
\begin{equation}
\sum_{k=0}^{\infty} x^k = \frac{1}{1-x} \text{ iff } |x| < 1
\end{equation}
\end{theorem}

\begin{theorem}{Telescoping Series:}
\begin{equation}
\sum_{i=1}^{n} a_i - a_{i-1} = a_n - a_0 \\
\sum_{i = 1}^{n} a_i - a_{i+1} = a_0 - a_n
\end{equation}
\end{theorem}

\section{Combinatorics}

\begin{theorem}{Binomial Coefficient:}
\begin{equation}
(x+y)^r = \sum_{i = 0}^{r} {r\choose i} x^i y^{r-i}
\end{equation}

Where $r\choose i$ is the binomial coefficient which equals $$\frac{r!}{i!(r-i)!}$$

\end{theorem}

% TODO: Get the rest of the information from ECE286 notes.

\chapter{Asymptotics}

\paragraph{What are Asymptotics? } Analysis method for performance and complexity of an algorithm (space/memory, time/clock cycle complexity).

\begin{itemize}
\item Tight Bound: $\Theta()$.
\item Worst Case: $O()$.
\item Best Case: $\Omega()$
\item Average \& Expected Case: \textit{Randomized + probabilistic analysis. Not a focus for the course.}
\item Amortized Case: Discused later on. ``Average worst case''.
\end{itemize}

\paragraph{Useful Facts}
\begin{itemize}
\item $\Theta$ bound is not always possible to find.
\item \textbf{Transitivity: } if $f(n) = \Theta(g(n))$ and $g(n) = \Theta(h(n))$, then $f(n) = \Theta(h(n))$.
\item \textbf{Symmetry: } $f(n) = \Theta(g(n))$ if and only if $g(n) = \Theta(f(n))$.
\item \textbf{Transpose: } $f(n) = O(g(n))$ if and only if $g(n) \Omega(f(n))$.
\item $n^a \in O(n^b)$ iff $a \leq b$.
\item $\log_a(n) \in O(\log_b(n)) \, \forall a, b$.
\item $c^n \in O(d^n)$ iff $c\leq d$.
\item If $f(n) \in O(f'(n))$ and $g(n) \in O(g'(n))$ then $$f(n)\cdot g(n) \in O(f'(n)\cdot g'(n))$$
$$f(n) + g(n) \in O(\max(f'(n), g'(n))$$
\end{itemize}

\begin{theorem}{Big O Definition:}
$f(n) = O(g(n))$ if and only if:
There exists $c > 0$ and $n_0 > 0$ such that
\begin{equation}
0 \leq f(n) \leq cg(n)\,\forall n \geq n_0
\end{equation}
\end{theorem}

\begin{theorem}{Big $\Omega$ Definition:}
\paragraph{``Lower Bound'' Big $\Omega$ Definition: } $f(n) = \Omega(h(n))$ if and only if:
There exists $c_1, n_1 > 0$ such that
\begin{equation}
0 \leq c_1 h(n) \leq f(n)\, \forall n \geq n_1
\end{equation}
\end{theorem}

\begin{theorem}{Big $\Theta$ Definition:}
\paragraph{``Tight Bound'' Big $\Theta$ Definition: } $f(n) = \Theta(g(n))$ if and only if:

There exists $c_1, c_2, n_0 > 0$ such that

\begin{equation}
0 \leq c_1 g(n) \leq f(n) \leq c_2 g(n) \, \forall n > n_0
\end{equation}
\end{theorem}

\chapter{Graphs}

\section{Definitions}

\begin{theorem}{Graph Definition:}
A \textbf{graph} is a collection of \textbf{vertices} and \textbf{edges} represented by
\begin{equation}
G = (V,E)
\end{equation}
Graphs can be:
\begin{itemize}
\item Directed or Undirected.
\item Weighted or Unweighted.
\end{itemize}

\end{theorem}

\paragraph{Path: } A sequence of \textbf{edges} between adjacent vertices.
\begin{itemize}
\item \textbf{Simple Path: } No vertex is repeated in the path.
\item \textbf{Cycle: } A path that starts and ends with the same vertex.
\item A \textbf{connected} graph has a path between any two vertices (else it is \textbf{disconnected})
\end{itemize}

\paragraph{Bipartite Graphs: } A graph is \textbf{bipartite} if and only if vertices $V$ can be divided into $V_1, V_2$ such that:
\begin{enumerate}
\item $V_1 \cap V_2 = \emptyset$.
\item $V_1 \cup V_2 = V$.
\item Adjacencies exist only between elements and $V_1$ and $V_2$ (i.e., vertices within each set are disconnected from eachother, but are connected to elements from the other set).
\end{enumerate}

\paragraph{Vertex Degree } is the number of \textbf{edges} adjacent to a vertex (includes incoming and outgoing edges).
\begin{itemize}
\item \textbf{In-degree} is the number of incoming edges.
\item \textbf{Out-degree} is the number of outgoing edges.
\end{itemize}

\paragraph{Clique: } For all $v_1, v_2\in V$, there exists an edge connecting them (i.e. a fully-connected set of vertices).

\paragraph{Representations for Graphs: } We either use an \textbf{adjacency matrix} or an \textbf{adjacency matrix}.

\begin{itemize}
\item \textbf{Adjacency List: } Each element in the list represents a vertex and ``has'' a collection of \textbf{pointers} connecting them to other elements of the list.
\begin{itemize}
\item \textbf{Time Complexity: } $O(n)$ -- Determining if $E_{v_1,v_2}$ exists would, at worst, require you to check $n$ pointers arising from vertex $v_1$ and/or $v_2$.
\item \textbf{Space Complexity: } $O(|E|)$ -- with worst case that $|E| = n^2$ for clique.
\end{itemize}

\item \textbf{Adjacency Matrix: } Matrix contains weights connecting $v_i$ to $v_j$ at index $M_{i,j}$.
\begin{itemize}
\item \textbf{Time Complexity: } $O(1)$ -- Just check address $M_{i,j}$ and $M_{j,i}$.
\item \textbf{Space Complexity: } $O(n^2$ every time.
\end{itemize}
\end{itemize}

\section{Graph Traversals}

\textit{How do we choose an order to traverse all nodes in a graph?}

\subsection{Breadth First Search}

\paragraph{Key Ideas: }
\begin{itemize}
\item From a given point, we traverse all immediate neighbours.
\item Only then do we go to second-order neighbours of the original point.
\item Implemented with a \textbf{Queue} (first-in-first-out).
\end{itemize}

\paragraph{Breadth First Search Algorithm: } Given $G=(V,E,s$) where $s$ is the starting vertex,
\begin{enumerate}
\item Initialize $d[u] = \infty\,\forall u\in V$. This is the \textbf{distance} for each vertex.
\item Let $Q$ be an empty queue, and \textbf{enqueue} vertex $s$.
\item While $Q$ is not empty:
\begin{enumerate}
\item $u = $ dequeue$(Q)$.
\item For $v\in adj(u)$:
\begin{enumerate}
\item if $d[v] = \infty$, let $d[v] = d[u]+1$ and \textbf{enqueue} $v\to Q$.
\end{enumerate}
\end{enumerate}
\end{enumerate}

\paragraph{Performance: } Time complexity is $O(|V|+|E|)$.

\subsection{Depth First Search}

\paragraph{Key Ideas: }
\begin{itemize}
\item \textbf{Input: } $G = (V,E)$ (no source vertex this time).
\item \textbf{Output: }
\begin{enumerate}
\item $d[v]$: Time taken to \textbf{discover} vertex $v$ (in number of algorithm ``steps'').
\item $f[v]$: Time taken to \textbf{finish} with vertex $v$ (meaning to exhaust all `down stream' vertices).
\item $\Pi[v]$: Predecessor of $v$ (i.e. the vertex immediately before it in the search).
\end{enumerate}
\end{itemize}

\paragraph{Color Coding: }
\begin{itemize}
\item \textbf{White: } Node is as of yet undiscovered.
\item \textbf{Gray: } Node has been discovered, but the descendants have not all been discovered (node is not ``exhausted'').
\item \textbf{Black: } The algorithm has traversed the node AND all its descendants.
\end{itemize}

\begin{algorithm}[H]
\SetKwInOut{Input}{Input}\SetKwInOut{Output}{Output}
\SetAlgoLined
\Input{$G = (V,E)$}
\Output{List of discovery times, finishing times, and predecessors for each $v\in V$.}
	\BlankLine
	\BlankLine
	%\nlset{\color{blue}$O(n)$}Some order $n$ operation\;
	$\text{color}[u] = \text{White}\,\forall u\in V$\;
	$\text{time} = 0$\;
	\For{each $u\in V$}{
		\If{$\text{color}[u] = \text{White}$}{
			$\text{DFS-Visit}(u)$\;
		}
	}
	\caption{Depth First Search Algorithm.}
\end{algorithm}

\begin{algorithm}[H]
\SetKwInOut{Input}{Input}\SetKwInOut{Output}{Output}
\SetAlgoLined
\Input{Vertex $u$}
\Output{Recursively traverses nodes depth-first.}
	\BlankLine
	\BlankLine
	%\nlset{\color{blue}$O(n)$}Some order $n$ operation\;
	$\text{color}[u] \leftarrow \text{Gray}$\;
	$\text{time} += 1$\;
	$d[u] = \text{time}$\;

	\For{each $v\in \text{adj}(u)$}{
		\If{$v = \text{White}$}{
			$\text{DFS-Visit}(v)$\;
		}
	}

	$\text{color}[u] \leftarrow \text{Black}$\;
	$\text{time} += 1$\;
	$f[u] \leftarrow \text{time}$\;

	\caption{DFS-Visit Algorithm (subroutine for DFS Search)}
\end{algorithm}

\paragraph{Edge Classification: } \textbf{DFS} Produces one or more \textbf{trees}, leading to the following classification.
\begin{enumerate}
\item \textbf{Tree Edges: } Edges found during DFS exploration.
\item \textbf{Back Edge: } $(u,v)$ when $u$ is a descendant of $v$.
\item \textbf{Forward Edge: } $(u,v)$ when $v$ is a descendant of $u$ \textbf{but} $(u,v)$ is not a tree edge.
\item \textbf{Cross Edge: } Any other type of edge.
\end{enumerate}

\begin{theorem}{White Path Theorem: }
$v$ is a descendant of $u$ if, and only if: At time $d[u]$, there exists a \textbf{white-only} path from $v\to u$.
\end{theorem}

\begin{theorem}{Parenthesis Theorem: }
For all $u, v\in V$: Exactly \textbf{one} of the following holds:
\begin{enumerate}
\item $d[u] < f[u] < d[v] < f[v]$ or $d[v] < f[v] < d[u] < f[u]$. That is, \textbf{neither $u, v$ are descended from eachother}.
\item $d[u]< d[v] < f[v] < f[u]$. That is, $v$ is a descendant of $u$.
\item $d[v] < d[u] < f[u] < f[v]$. That is, $u$ is a descendant of $v$.
\end{enumerate}
\end{theorem}

\begin{theorem}{Depth First Search Edge Types: }
The depth-first search of \textbf{undirected graph} $G$ yields only \textit{tree edges} and \textit{back edges} .
\end{theorem}

\subsection{Topological Sort}

\paragraph{Key Idea: } All DAG (directed, acyclic graphs) have \textit{some} amount of \textbf{implied order}. Think of it like a dependency graph. We use topological sort to turn \textbf{partial order} $\to$ \textbf{total order} (that is, an absolute list).

\begin{algorithm}[H]
\SetKwInOut{Input}{Input}\SetKwInOut{Output}{Output}
\SetAlgoLined
\Input{DAG $G = (V,E)$}
\Output{Total ordering of elements of graph.}
	\BlankLine
	\BlankLine
	%\nlset{\color{blue}$O(n)$}Some order $n$ operation\;
	Call $\text{Depth First Traversal}(V,E)$\;
	Return the vertices in \textbf{decreasing order} of \textbf{finish time}\;
	\caption{Topological Sorting Algorithm.}
\end{algorithm}

\section{Minimum Spanning Trees}

\paragraph{Key Idea: } Given a weighted graph $G = (V, E)$, we want a tree $G' = (E', V)$ where $E' \subseteq E$ such that there is a path between all vertices \textbf{and} $\sum_{e\in E'} e$ is minimized.
\begin{itemize}
\item $|E'| = |V|-1$.
\item There are \textbf{no cycles} (hence ``tree'').
\item It may or may not be a \textbf{unique} solution.
\end{itemize}

\subsection{Generic Minimum Spanning Tree Approach}

\paragraph{High-Level Description: }
\begin{itemize}
\item Set $A$ is instantiated as empty.
\item Edges from the graph are added to $A$, ensuring \textbf{loop invariant} $A\subset\text{Some Minimum Spanning Tree}$.
\item Edge $(u,v)$ is \textbf{safe} iff $A \cup \{(u,v)\}$ is also a subset of some minimum spanning tree.
\end{itemize}

\paragraph{Generic MST Algorithm: } Given graph $G$ with weights $w$,
\begin{enumerate}
\item Let $A$ be an empty set.
\item While $A$ is not a spanning tree:
\begin{enumerate}
\item Find a \textbf{safe} edge $(u,v)$.
\item $A \leftarrow A \cup (u,v)$.
\end{enumerate}
\end{enumerate}

\paragraph{Terminology: } Relating to how to pick a \textbf{safe} edge.
\begin{itemize}
\item Let $S$ represent the set of vertices currently ``covered'' by edges in $A$, our current working minimum spanning tree.
\item We can partition $V$ into set $S$ and $V-S$.
\item Edge $(u,v)$ \textbf{crosses} the \textbf{cut} $(S,V-S)$ if one of $u,v$ is in $S$ and the other is in $V-S$.
\item A cut \textbf{respects} $A$ iff no edge in $A$ \textbf{crosses} the cut.
\item The \textbf{light edge} with respect to cut $(S,V-S)$ is the lowest weight edge that \textbf{crosses} the cut.
\item \textbf{The light edge is the safe edge}.
\end{itemize}

\subsection{Prims Algorithm}

\begin{algorithm}[H]
\SetKwInOut{Input}{Input}\SetKwInOut{Output}{Output}
\SetAlgoLined
\Input{Graph $G=(V,E)$, weights $w$, root node $r$.}
\Output{Minimum spanning tree of $G$.}
	\BlankLine
	\BlankLine
	%\nlset{\color{blue}$O(n)$}Some order $n$ operation\;
	$Q \leftarrow \{\emptyset\}$\;
	\For{each $u\in V$}{
		$\text{key}[u] \leftarrow \infty$\;
		$\Pi[u] \leftarrow \text{NIL}$\;
		$\text{Insert} u\to Q$\;
	}

	Set key of root node $r$ to $0$ in $Q$.

	\While{$Q \neq \{\emptyset\}$}{
		$u\leftarrow \text{Pop-Minimum}(Q)$\;
		\For{each $v\in \text{Adj}(u)$}{
			\If{$v\in Q$ and $w(u,v) \leq \text{key}(v)$}{
				$\Pi[v] \leftarrow u$\;
				Set the key of $v$ in $Q$ to $w(u, v)$\;
			}
		}
	}

	\caption{Prim's Algorithm}
\end{algorithm}

\paragraph{Complexity: } Time complexity is $O(|E| + |V|\log |V|)$ when using Fibonnaci heaps.

\section{Shortest Paths}

\paragraph{Goal: } Find path of minimum total weight between two vertices in a graph. Shortest path from $u\to v$ is represented by $\delta(u,v)$. It's the set of vertices from one to the other.

\paragraph{Variants of Shortest Paths Problem: }
\begin{enumerate}
\item Single Source Shortest Paths (SSSP).
\item Single Destination Shortest Paths.
\item Single Pair -- \textit{most efficient method is to just run SSSP}.
\item All Pairs Shortest Paths.
\end{enumerate}

\paragraph{Key Algorithms: }
\begin{enumerate}
\item Dijkstra's -- for no negative weights.
\item Bellman-Ford -- when there are negative weights.
\item Difference Constraints -- a surprising application of shortest paths.
\end{enumerate}

\paragraph{Properties and Assumptions of Shortest Paths: }
\begin{enumerate}
\item No negative weight cycles allowed accessible from source node.
\item \textbf{Optimal Substructure: } If $p$ is in the optimal path from $u\to v$ $\delta(u,v)$, then sub-path $u\to p$ is the optimal path $\delta(u,p)$.
\item A shortest path will \textbf{never contain a cycle}.
\begin{itemize}
\item Negative weight cycles aren't allowed (per above).
\item Positive weight cycles would be a pointless cost expenditure.
\item 0-value cycles are redundant (by convention, we just skip them).
\end{itemize}
\end{enumerate}

\subsection{Djikstra's Algorithm}

\paragraph{Key Points: }
\begin{itemize}
\item No negative weight edges allowed.
\item We let $S$ be the set of vertices with known shortest path weight.
\item Priority queue $Q$ is the remaining set $V-S$ of vertices.
\item Priority is dictated by $d[v]$.
\item \textbf{Time Complexity: } If we use a binary heap, we get $$O(|E| \log |V|)$$ though Fibbonacci heap is faster.
\end{itemize}

\begin{algorithm}[H]
\SetKwInOut{Input}{Input}\SetKwInOut{Output}{Output}
\SetAlgoLined
\Input{Graph $G = (V, E)$, weights $w$ and starting node $s$.}
\Output{Shortest paths for each $v\in V$ starting at $s$.}
	\BlankLine
	\BlankLine
	%\nlset{\color{blue}$O(n)$}Some order $n$ operation\;
	$\text{Init-Single-Source}(V, s)$\;
	$S\leftarrow \{\emptyset\}$\;
	$Q\leftarrow V$\;

	\While{$Q\neq \{\emptyset\}$}{
		$u\leftarrow \text{Extract-Min}(Q)$\;
		$S \leftarrow S \cup \{u\}$\;
		\For{each $v\in \text{adj}(u)$}{
			$\text{Relax}(u, v, w)$;
		}
	}

	\caption{Dijkstra's Algorithm}
\end{algorithm}

\begin{algorithm}[H]
\SetKwInOut{Input}{Input}\SetKwInOut{Output}{Output}
\SetAlgoLined
\Input{Graph $G = (V,E)$ and source node node $s$}
\Output{Initialized Graph for starting Single Source Shortest Paths.}
	\BlankLine
	\BlankLine
	%\nlset{\color{blue}$O(n)$}Some order $n$ operation\;
	\For{each vertex $v\in V$}{
		$v.d \leftarrow \infty$\;
		$v.\pi \leftarrow \text{NIL}$\;
	}
	$s.d \leftarrow 0$\;
	\caption{Initialize Single Source}
\end{algorithm}

\begin{algorithm}[H]
\SetKwInOut{Input}{Input}\SetKwInOut{Output}{Output}
\SetAlgoLined
\Input{Edges $u,v$ and weight function $w:V\times V \to \mathbb{R}$}
\Output{Corrects the path to $v$ from the source, using neighbour $w$ if necessary.}
	\BlankLine
	\BlankLine
	%\nlset{\color{blue}$O(n)$}Some order $n$ operation\;
	\If{$v.d > u.d + w(u,v)$}{
		$v.d \leftarrow u.d + w(u,v)$\;
		$v.\pi \leftarrow u$\;
	}
	\caption{Relax Algorithm}
\end{algorithm}

\subsection{Bellman-Ford Algorithm}

\paragraph{Key Points:}
\begin{itemize}
\item Solves single-source shortest paths (allowing for \textbf{negative edges}, unlike Dijkstra's).
\item Returns \textbf{False} if there is a negative weight cycle.
\item \textbf{Time Complexity: } $O(|V|\cdot|E|)$.
\end{itemize}

\begin{algorithm}[H]
\SetKwInOut{Input}{Input}\SetKwInOut{Output}{Output}
\SetAlgoLined
\Input{Graph $G = (V,E)$, weight function $w:E\to \mathbb{R}$, source node $s$}
\Output{Boolean indicating if the question is valid, single-source shortest path result if possible.}
	\BlankLine
	\BlankLine
	%\nlset{\color{blue}$O(n)$}Some order $n$ operation\;
	$\text{Initialize-Single-Source}(G,s)$\;
	\For{$i = 1 \text{ to } |V|-1$}{
		\For{each $(u, v) \in E$}{
			$\text{Relax}(u,v,w)$\;
		}
	}
	\For{each $(u,v) \in E$}{
		\If{$v.d > u.d + w(u,v)$}{
			return FALSE\;
		}
	}
	return TRUE;
	\caption{Bellman-Ford Algorithm.}
\end{algorithm}

\subsection{Application: Difference Constraints}

\paragraph{Key Points: }
\begin{itemize}
\item Under specific conditions, we can convert Linear Programs into graph problems.
\item We can then use \textbf{Bellman-Ford} to solve for a feasible solution to the linear program.
\end{itemize}

\paragraph{Requirements for Conversion to Graph Problem: } Within $Ax \leq b$,
\begin{itemize}
\item Each row of a must have all zeros except for one $-1$ and one $+1$ entry.
\item The graph approach only gives \textbf{a single feasible point}, if any.
\item No additional cost optimization is done in this method.
\end{itemize}

\paragraph{Conversion to Graph Problem: }
\begin{enumerate}
\item Vertices $V = \{v_0, v_1, ..., v_n\}$. Each $v_i, \, i\in \{1,..., n\}$ represents an index of vector $x$, our feasible point. $v_0$ is a special additional point.
\item $E = \{(v_i, v_j) = x_i-x_j \leq b_k\}\cup \{(v_0, v_i):0 \forall i\in [n]\}$. In other words,
\begin{itemize}
\item Each connection from $v_i$ to $v_j$ is directed with weight $b_k$, the difference constraint $b_k$ that $x_i-x_j \leq b_k$.
\item We also have 0-weight edges connecting $v_0 \to v_i$ for all $i\in [n]$.
\end{itemize}
\end{enumerate}

\paragraph{Solution: }
\begin{equation}
x^* = \begin{bmatrix}
\delta(v_0, v_1) \\
\delta(v_0, v_2) \\
\vdots \\
\delta(v_0, v_n) \\
\end{bmatrix}
\end{equation}

As determined by \textbf{Bellman-Ford} with $v_0$ as the source node.

% 26.1-2 without proofs.
\section{Maximum Flow}

\paragraph{Key Idea: } Directed graphs can represent \textbf{flow} of resources. If we add properties of \textbf{conservation} and a few others, we can come up with the hard problem of how to allocate flow through the network to get the maximum total transmission from one side to the other, but not exceed the limit of each `pipe' or overflow at a juncture.

\subsection{Formal Definition}

\begin{theorem}{Flow Network Definition: }
Flow network $G = (V,E)$ is a \textbf{directed} graph where:
\begin{itemize}
\item Edges represent \textbf{capacity} $c$, and $c(u,v)\geq 0$.
\item If $E$ has edge $(u,v)$, then $(v,u)$ \textbf{cannot exist}.
\item $c(u,v) = 0$ when $(u,v)\notin E$.
\item \textbf{Two important vertices: } Source $s$ and sink $t$.
\item It is assumed that each vertex is on a path between $s$ and $t$.
\item Therefore, $|E| \geq |V|-1$.
\end{itemize}

\paragraph{Flow: } \textbf{flow} in $G$ is a function mapping two vertices to a real number ($f:V\times V \to \mathbb{R}$) under the constraints:
\begin{enumerate}
\item \textbf{Capacity: } $u \leq f(u,v) \leq c(u,v)$ for all $u,v\in V$.
\item \textbf{Conservation: } $\sum_{v\in V} f(v,u) = \sum_{v\in V} f(u,v)$ for all $u,v\in V$ except for the source $s$ and sink $t$.
\end{enumerate}

The \textbf{value} of a flow is the total flow through the system. It is given by
\begin{equation}
|f| = \sum_{v\in V}^{} f(s,v) - \sum_{v\in V}^{} f(v,s)
\end{equation}

In other words, it's the total flow out of the source minus any flow coming into the source.
\end{theorem}

\paragraph{Maximum Flow Problem: } Given graph $G$ with defined $s$, $t$: What is the maximum possible value for $|f|$?

\paragraph{Dealing with ``antiparallel edges'': } Originally we said we couldn't have edges from $v\to u$ if we have one from $u\to v$. We get around this by \textbf{transforming} an antiparallel graph to one with no problematic edges:
\begin{itemize}
\item For each antiparallel edge $(v_1, v_2)$:
\item Split into two edges $(v_1, v')$ and $(v', v_2)$.
\item Set capacity of both to be the same as the original.
\item End behavior is \textbf{exactly identical}.
\end{itemize}

\paragraph{Multiple Sources and Sinks: }
\begin{itemize}
\item For $m$ sources and $n$ sinks:
\item Create a super source $s$ with edges leading to each $s_i$ (i.e. $(s,s_i) \forall i\in [m]$).
\item Make the capacity $c(s,s_i) = \infty$.
\item Do the same, with a super sink $t$ leading from each of the $n$ sinks. Should also have infinite capacity.
\item End behavior is the same.
\end{itemize}

\subsection{Ford-Fulkerson Method}

\paragraph{Residual Capacity of Edge: } Amount of additional flow allowable through an edge $(u,v)$ given an existing flow $f(u,v)$.
\begin{equation}
c_f(u,v) = c(u,v) - f(u,v)
\end{equation}

\paragraph{Residual Network: } Residual network is represented by $G_f = (V, E_f)$. $E_f$ is given by
\begin{equation}
E_f = \{(u,v)\in V^2: c_f(u,v) > 0\}
\end{equation}
Note that $G_f$ depends on the specific flow $f$ at a given time.
\begin{itemize}
\item \textbf{Key Point: } Every edge ALSO has a residual edge doubling back on it with $c_f(v,u) = f(u,v)$.
\end{itemize}

\begin{equation}
c_f(u,v) = \begin{cases}
c(u,v) - f(u,v) & \text{if } (u,v)\in E,\\
f(v,u) & \text{if } (u,v) \in E,\\
0 & \text{otherwise}.
\end{cases}
\end{equation}

\textit{This is a very clever definition.}

\paragraph{Using Residual Network: } If we can find a path from source $\to$ sink in $G_f$, then we can increase flow along that path in real graph $G$ by the \textbf{minimum value in the path in the residual graph}.

\paragraph{Augmenting Path: } Is a simple path from source to sink $s\to t$ in $G_f$.

\paragraph{Residual Capacity of Augmenting Path $p$: } $c_f(p) = \min\{c_f(u,v):(u,v)\in p\}$.

\begin{algorithm}[H]
\SetKwInOut{Input}{Input}\SetKwInOut{Output}{Output}
\SetAlgoLined
\Input{Directed flow graph $G$, source $s$, sink $t$}
\Output{Optimal flow $f$}
	\BlankLine
	\BlankLine
	%\nlset{\color{blue}$O(n)$}Some order $n$ operation\;
	Initialize flow $f(u,v) \leftarrow 0$ $\forall (u,v)\in E$\;
	\While{$\exists$ augmenting path $p$ in residual graph $G_f$}{
		augment flow $f$ along $p$ (see above)\;
	}
	\caption{Ford Fulkerson Method}
\end{algorithm}

\paragraph{Importance of $0$ weight $\to$ Non-Existence: } Once a residual is reduced to zero, the edge no longer exists. This is what enables the algorithm to terminate.

\paragraph{Cut of Flow Network: } \textbf{cut} $(S,T)$ of $G = (V,E)$ partitions $V\to S,T=V-S$. Net flow from $S\to T$ is given by
\begin{equation}
f(S,T) = \sum_{u\in S}^{} \sum_{v\in T}^{} f(u,v) - \sum_{u\in S}^{} \sum_{v\in T}^{} f(v,u)
\end{equation}

\paragraph{Capacity of Cut: }
\begin{equation}
c(S,T) = \sum_{u\in S}^{} \sum_{v\in T}^{} c(u,v)
\end{equation}

\paragraph{Minimum Cut: } Cut with minimum capacity relative to all cuts of the network.

\paragraph{Why does Ford-Fulkerson Terminate? }

\begin{theorem}{Maximum Flow Minimum Cut Theorem: }
For any flow $f$ on graph $G$, all of these statements are equivalent:
\begin{itemize}
\item $f$ is a maximum flow of $G$.
\item $G_f$ has no augmenting path.
\item $|f| = c(S,T)$ for some cut (the \textbf{minimum cut}).
\end{itemize}
\end{theorem}

\paragraph{Runtime of Ford-Fulkerson: } $O(|f_{\max}|\cdot E)$.
\begin{itemize}
\item Using Breadth-First Search to search for Augmenting Paths ($O(E)$).
\item We have to repeat this, at most, $f_{\max}$ times.
\end{itemize}

\subsection{Edmond-Karp's Algorithm}

More efficient version of Ford-Fulkerson.
\begin{itemize}
\item \textbf{Always} choose the augmenting path with \textbf{minimum number of edges}.
\item Find this by running shortest paths on the graph but using edges of uniform weight $1$.
\end{itemize}

\paragraph{Runtime of Edmond-Karp: } $O(VE^2)$ (vast improvement from $O(|f_{\max}|E)$).

\subsection{Maximum Bipartite Matching}

\paragraph{Bipartite Graph Review: } You can split vertices into two disjoint sets. All edges must have one end in set 1 and the other in set 2.

\paragraph{Bipartite Matching: } Find $M\subseteq E$ for bipartite graph $G$ such that:
\begin{equation}
\forall v\in V \exists \text{ at most one edge from } M \text{incident on } V
\end{equation}

\paragraph{Maximal Matching: } You cannot increase the size of $M$ without violating the constraint.

\paragraph{Maximum Matching: } Greatest possible $|M|$. This is what we are interested in solving for.

\paragraph{Reduction to Maximum Flow: }
\begin{enumerate}
\item Introduce \textbf{super source} $s$ that connects to each vertex in set 1 with \textbf{infinite connectivity} from $s$ outward.
\item Introduce \textbf{super sink} $t$ that connects to each vertex in set 2 with \textbf{infinite connectivity} in direction of $t$.
\item Let each connection between two sets have weight $1$.
\item Run \textbf{Edmond-Karp} Algorithm.
\end{enumerate}

After this process, $|f| = |M_{maximum}|$.

\chapter{Trees}

\section{Definitions}

\begin{theorem}{Tree Definition:}
A \textbf{tree} is a \textbf{connected, acyclic, undirected} graph.
\begin{itemize}
\item \textbf{Root node} is usually defined.
\item \textbf{Parent node} is the `next node up' going toward the root.
\item \textbf{Child node} is one of the `next node(s) down' going away from the root.
\item \textbf{Binary tree: } $\leq 2$ children per node.
\item \textbf{Depth of node: } Length of path from \textbf{root $\to$ node}.
\item \textbf{Height of node: } Number of edges on the \textbf{longest path} from the node $\to$ leaf.
\item \textbf{Complete $k$-ary tree: } Every \textit{internal node} has $k$ children and all leaves are at the same depth.
\end{itemize}
\end{theorem}

\begin{theorem}{One-for-all Tree Theorem:}
\textbf{If one of these is true, they all are: }
\begin{itemize}
\item $G$ is a tree.
\item Every pair of vertices $v_1, v_2\in G$ is connected by a \textbf{unique, simple path}.
\item $G$ is disconnected, but becomes disconnected when one edge is removed.
\item $G$ is connected with $|E| = |V|-1$.
\item $G$ is acyclic.
\item If an edge is added $G$ \textbf{becomes cyclic}.
\end{itemize}
\end{theorem}

\section{Binary Search Trees}

\begin{theorem}{Binary Search Tree Definition:}
For any node $x$ of a binary search tree:
\begin{itemize}
\item If $y\in $ left sub-tree: $y.key \leq x.key$.
\item If $y\in $ right sub-tree: $y.key \geq x.key$.
\end{itemize}
\end{theorem}

\paragraph{Traversals: }
\begin{enumerate}
\item \textbf{In-Order: } LNR -- Actually prints them from least to greatest.
\item \textbf{Pre-Order: } NLR
\item \textbf{Post-Order: } LRN
\end{enumerate}

\subsection{Querying Binary Search Trees}

We can perform minimum, maximum, successor, predecessor in $O(h)$ time where $h$ is the height of the tree.

\paragraph{Tree Search: } Given a node $x$ and value we are looking for $k$:
\begin{enumerate}
\item If $x$.key is $k$ or $x$ is NIL: return $x$. \textit{We either found it or it wasn't there}.
\item If $x$.key $< k$: Run \textbf{Tree Search} on the left sub-tree.
\item Otherwise, run \textbf{Tree Search} on the right sub-tree.
\end{enumerate}

\paragraph{Iterative Tree Search: } Same input/output as above: \textbf{Repeat while $x$.key is not $k$ and NIL}
\begin{enumerate}
\item If $x$.key $<k$: $x = x$.left.
\item Else, $x = x$.right.
\item After break condition above, return $x$.
\end{enumerate}

\paragraph{Minimum Tree Search: } To find the minimum, keep setting $x = x$.left while $x$.left isn't NIL.

\paragraph{Tree Successor: } To find the successor of node $x$:
\begin{enumerate}
\item If $x$ has a right child: Call \textbf{Tree Minimum} on $x$.right.
\item Else:
\begin{enumerate}
\item $y = x$.parent
\item While $y \neq$ NIL and $x == y$.right:
\begin{enumerate}
\item $x = y$
\item $y = y.parent$
\end{enumerate}
\item return $y$
\end{enumerate}
\end{enumerate}

\paragraph{Tree Insert: } To insert $z$ into BST $x$, repeatedly compare $z$ to nodes of $x$, moving right/left according to result of comparison. Eventually, you will add $z$ as a leaf node.

\paragraph{Tree Deletion: } To delete node from $z$ from BST:
\begin{enumerate}
\item $z$ has \textbf{no children} $\to$ modify $z$.parent.child = NIL.
\item $z$ has \textbf{one child} $\to$ modify z.parent.child $\leftarrow$ $z$.child.
\item $z$ has \textbf{two children}:
\begin{enumerate}
\item Let $y$ = Tree Successor($z$) [must be from right sub-tree of $z$]
\item Convenient case: $y = z$.right. In this case, just replace $z$ with $y$.
\item Replace $z \leftarrow y$, ensuring $y$ keeps its right sub-tree.
\item $z$.right $\to$ $y$.right.
\item $z$.left $\to$ $y$.left.
\end{enumerate}
\end{enumerate}

\section{Red-Black Trees}

\begin{theorem}{Red Black Tree Definition:}
A red-black tree is a \textbf{binary search tree} where each node has a ``color'' of either \textbf{red or black}.Each BST operation has additional rules that relate to color, with the end results being:
\begin{itemize}
\item No path from the \textbf{root} to a \textbf{leaf} is more than \textbf{two times the shortest path}.
\item The tree is approximately balanced and therefore is efficient.
\end{itemize}

\paragraph{Properties of Red-Black Tree}
\begin{enumerate}
\item All nodes are either \textbf{red or black}.
\item The root node is black.
\item All leaves are black.
\item Red nodes have black children.
\item All simple paths from a node to descendant leaves have \textbf{the same numberof black nodes}.
\end{enumerate}

\paragraph{Resultant Properties}
\begin{enumerate}
\item bh$(x)$ is the \textbf{``black height''} of node $x$ (numberof black nodes from (but not including) $x$ to a leaf.
\item Red-black tree with $n$ internal nodes has height $\leq 2\log(n+1)$.
\item All leaves are set to $T$.nil values instead of regular NIL.
\end{enumerate}
\end{theorem}

\paragraph{Easy Red-black Tree Operations: } These are exactly the same as BST operations. They all run in $O(\log n)$ time, too.
\begin{enumerate}
\item Search.
\item Minimum.
\item Maximum.
\item Successor.
\item Predecessor.
\end{enumerate}

\paragraph{Rotations: } Rotations swap two nodes while maintaining the BST properties. You can visualize or ``feel'' it as taking the edge connecting two nodes and physically \textbf{twisting} it either right or left for their respective rotation.

\paragraph{Red-Black Insert Fixup: } This algorithm reinstates the red-black properties of the tree around a given recently-inserted node $z$.

\paragraph{Red-Black Insertion: } To insert node $z$...
\begin{enumerate}
\item Perform conventional BST insertion of $z$, setting the new node's children to $T$.nil instead of NIL.
\item Set $z$'s color to RED.
\item Call \textbf{Red-Black Insert Fixup}$(T, z)$.
\end{enumerate}

\paragraph{Red-Black Transplant: } Same function as before, just replace 'NIL' with $T$.nil.

\paragraph{Red-Black Deletion: } Deletion is substantially more involved and uses a particular \textbf{Red-Black Delete Fixup} function.

\chapter{Proof Methods}

\section{Induction}

\paragraph{Basic Idea: } To prove by induction, we show that the statement holds for some base case $n = 1$, then show that the statement holding for aritrary $n$ implies that the statement holds for $n+1$. That concludes the proof.

\paragraph{Basis: } We prove that the statement holds for some set values of $n$ (usually $n = 1$ or $n = 0,1,...,4$).

\paragraph{Hypothesis: } We hypothesize that the thing we are trying to prove is true.

\paragraph{Inductive Step: } We ``plug in'' the $n+1$ case to the hypothesis and show that it boils down to the $n$ case and some algebraic equivalence. Then you can make the claim that it holds for all $n$.

% TODO: Add an illustrative example.

\section{Contradiction}

\paragraph{Basic Idea: } Given a true or false proposition $P$, we assume that $\neg P$ (``not $P$'') holds. After some clever algebra and symbol-shunting, we get a contradiction. This implies that $P$ must hold instead.

\paragraph{Illustrative Example: } $P$: If $x^2 - 5x + 4 < 0$, then $x > 0$.
\begin{itemize}
\item To prove $P$, we \textbf{assume towards a contradiction} (ATaC) that $\neg P$ holds. That is, we assume that if $x^2 - 5x + 4 < 0$ then $x \leq 0$.
\item But then:
\begin{equation}
\begin{split}
x^2 &< 5x-4 \\
x^2 &< 0 \\
\end{split}
\end{equation}
Results in a contradiction! Therefore $P$ must hold.
\end{itemize}

\section{Master Theorem}

This provides a ``hammer'' to prove the time complexity of recurrent functions.
\begin{theorem}{The Master Theorem:}
Let $a \geq 1$ and $b \geq 1$ and $f(n)$ be some function.
\begin{equation}
T(n) = aT(\frac{n}{b}) + f(n)
\end{equation}

\paragraph{Case 1: } $f(n) = O(n^{\log_b a - \epsilon})$ for some $\epsilon > 0$. Then $$T(n) = \Theta(n^{\log_b a})$$

\paragraph{Case 2: } $f(n) = \Theta(n^{\log_b a})$. Then $$T(n) = \Theta(n^{\log_b a} \log n)$$

\paragraph{Case 3: } $f(n) = \Omega(n^{\log_b a + \epsilon})$ for some $\epsilon > 0$ \textbf{AND} $af(n/b) \leq c f(n)$ for $0 < c < 1$. Then $$T(n) = \Theta(f(n))$$.
\end{theorem}

\section{Substitution}

\textbf{Substitution} is a method for determining the \textbf{closed from} runtime of an algorithm via induction.

\paragraph{Example -- Mergesort: } The runtime for mergesort is recursively defined as $T(n) = 2T(\ceil{n/2}) + n$.
\begin{enumerate}
\item We start by \textbf{guessing} $T(n) = O(n\log n)$.
\item We \textbf{hypothesize} that $T(n) = O(n\log n)$ for all cases $\leq n$, meaning that
$$T(n/2) \leq c \floor{n/2}\log \floor{n/2}$$
\item \textbf{Inductive step:} We prove that $$T(n) \leq cn\log n$$ Which is pretty obvious from there.
\end{enumerate}

\chapter{Sorting Algorithms}

% \section{Mergesort}
% TODO: Add pseudocode for mergesort

\section{Heapsort}
\subsection{Heaps}

\begin{theorem}{Heap Definition:}
A heap is an \textbf{array} representing a \textbf{binary tree} with the following properties:
\begin{itemize}
\item For \textbf{max heap}: $A[\text{parent}(i)] \geq A[i]$.
\item For \textbf{min heap}: $A[\text{parent}(i)] \leq A[i]$.
\item \textbf{Height} of tree is $\Theta(\log n)$ \textit{(i.e. number of edges to get from root to leaf node)}.
\end{itemize}

Parent-child relationships are as follows:
\begin{itemize}
\item $\text{Parent}(i) = \floor{i/2}$ (Parent node of $i$).
\item $\text{Left}(i) = 2i$ (Left child).
\item $\text{Right}(i) = 2i+1$ (Right child).
\end{itemize}
\end{theorem}

\subsection{Heap Algorithms}

\textbf{Max-Heapify} is an algorithm that sorts array $A$ into a heap at index $i$, assuming that right and left sub-trees satisfy the heap property at $i$.

\begin{algorithm}[H]
\SetKwInOut{Input}{Input}\SetKwInOut{Output}{Output}
\SetAlgoLined
\Input{Array $A$, index $i$. Left and right sub-trees of $i$ should already satisfy heap property.}
\Output{$A$ is sorted in place to satisfy heap property.}
    \BlankLine
    \BlankLine
    %\nlset{\color{blue}$O(n)$}Some order $n$ operation\;
    $l$ = left($i$)\;
    $r$ = right($i$)\;

    Let largest = $\max(l,r,i)$.

    \eIf{$i \neq$ largest}{
        Exchange $i$, largest\;
        MaxHeapify($A$, largest)\;
    }{}
    \caption{Max Heapify}
\end{algorithm}

\paragraph{Algorithm Complexity: } $O(1)$ Space complexity (sorts in place), $O(\log n)$ time complexity.

\paragraph{Build Heap } is an algorithm that sorts a completely unsorted array $A$ into a heap.

\begin{algorithm}[H]
\SetKwInOut{Input}{Input}\SetKwInOut{Output}{Output}
\SetAlgoLined
\Input{Unsorted list $A$}
\Output{Heap $A$}
	\BlankLine
	\BlankLine
	%\nlset{\color{blue}$O(n)$}Some order $n$ operation\;
	\For{$i = \floor{|A|/2}:1$}{
		MaxHeapify($A,i$)\;
	}

	\caption{Build Heap}
\end{algorithm}

\paragraph{Algorithm Complexity: } $O(1)$ Space Complexity, $O(n)$ time complexity.

\paragraph{Heap Sort } is a sorting algorithm that runs in $O(n\log n)$ time.

\begin{algorithm}[H]
\SetKwInOut{Input}{Input}\SetKwInOut{Output}{Output}
\SetAlgoLined
\Input{Unsorted array $A$}
\Output{Sorted array $A$}
	\BlankLine
	\BlankLine
	%\nlset{\color{blue}$O(n)$}Some order $n$ operation\;
	%\nlset{\color{blue}$O(n)$} BuildMaxHeap(A)
	\For{$i = |A|:2$}{
		exchange $A[i]$, $A[1]$\;
		$A$.heapsize -= 1\;
 		MaxHeapify($A,1$)\;
	}
	\caption{Heap Sort Algorithm}
\end{algorithm}

\paragraph{Algorithm Complexity: } $O(1)$ Space Complexity, $O(n)$ time complexity.

\section{Quicksort}

\paragraph{Key Facts: } Quick sort sorts in $\Theta(n^2)$ in the worst case and $\Theta(n\lg n)$ in the average case. The factors on the average case are small, so it ends up being very practical.

\paragraph{Algorithm Description: } Given algorithm $A$, we:
\begin{enumerate}
\item Divide $A$ into two \textbf{partitions}. We usually select the last element as the partition.
\item We call the partition function to put all values less than or equal to the pivot before the pivot and all values greater than the pivot after the pivot in $\Theta(n)$ time.
\item Then we call the partition function on the two remaining partitions recursively.
\item We end with a sorted list.
\end{enumerate}

\begin{algorithm}[H]
\SetKwInOut{Input}{Input}\SetKwInOut{Output}{Output}
\SetAlgoLined
\Input{Array $A$, partition start index $r$, partition end index $r$.}
\Output{$A$ partitioned about $A[r]$, index of partition returned.}
	\BlankLine
	\BlankLine
	%\nlset{\color{blue}$O(n)$}Some order $n$ operation\;
	$x = A[r]$
	$i = p-1$
	\For{$j = p:r-1$}{
		\If{$A[j] \leq x$}{
			i = i+1
			exchange $A[i], A[j]$
		}
	}
	exchange $A[i+1], A[r]$
	return i+1
	\caption{Partition Subroutine for Quicksort ($\Theta(n)$)}
\end{algorithm}

\begin{algorithm}[H]
\SetKwInOut{Input}{Input}\SetKwInOut{Output}{Output}
\SetAlgoLined
\Input{Unsorted $A$, partition start index $p$, end index $r$}
\Output{Sorted $A$}
	\BlankLine
	\BlankLine
	%\nlset{\color{blue}$O(n)$}Some order $n$ operation\;
	\If{$p < r$}{
		q = Partition($A, p, r$)\;
		Quicksort($A, p, q-1$)\;
		Quicksort($A, q+1, r$)\;
	}
	\caption{Quicksort Algorithm.}
\end{algorithm}

\paragraph{Algorithm Complexity: } $\Theta(n^2)$ worst case, $O(n\log n)$ average case with small coefficients.

\section{Linear Time Sort}

\subsection{Counting Sort}

\paragraph{Key Idea: } If we know that the input are integers and can be used as the address in an array, we can go through one-by-one and count the number of each possible element. This requires a lot of space, though, if the range of integers is high.

\begin{algorithm}[H]
\SetKwInOut{Input}{Input}\SetKwInOut{Output}{Output}
\SetAlgoLined
\Input{Input array $A$, output array $B$, upper bound on integers $k$}
\Output{}
	\BlankLine
	\BlankLine
	%\nlset{\color{blue}$O(n)$}Some order $n$ operation\;
	$C$ = array of zeros of size $k+1$\;

	\For{$j = 1:|A|$}{
		$C[A[j]]$++\;
	}

	\For{$i = 1:k$}{
		$C[i]$ += $C[i] + C[i-1]$\;
	}

	\For{$j = |A|:1$}{
		$B[C[A_j]] = A[j]$\;
		$C[A_j]$--\;
	}
	\caption{Counting Sort Algorithm.}
\end{algorithm}
\paragraph{Time Complexity: } $\Theta(k+n)$.

\paragraph{Stability: } The order of the entries with the same values is maintained from input to output. Relevant for \textbf{radix sort}, and for when ``satellite'' data is carried with ``keys'' used in sort.

\subsection{Radix Sort}

\paragraph{Algorithm Idea: } Start by sorting based on least significant digit then the most. This, again, only works for integer sorting or similar. We make use of a \textbf{stable sort} (usually \textbf{counting sort}).

\begin{algorithm}[H]
\SetKwInOut{Input}{Input}\SetKwInOut{Output}{Output}
\SetAlgoLined
\Input{Integer array $A$, number of digits in each number $d$}
\Output{Sorted array $A$}
	\BlankLine
	\BlankLine
	%\nlset{\color{blue}$O(n)$}Some order $n$ operation\;
	\For{$i = 1:d$}{
		StableSort $A$ based on digit $i$\;
	}

	\caption{Radix Sorting Algorithm.}
\end{algorithm}

\paragraph{Algorithm Complexity: } $O(d(n+k))$ time complexity, assuming that StableSort is $O(n+k)$.

For $b$-bit numbers and any $r\in [0,b]$:
$$\text{Radix Sort} = \Theta((\frac{b}{r} (n+2^r)))$$

\section{Order Statistics}

\begin{theorem}{Order Statistic Definition:}
The \textbf{Order Statistic} of set $A$ is the $i$th smallest element of the set. So,
\begin{itemize}
\item $\max A$ is the $n$th order statistic of $A$.
\item $\min A$ is the $1$st order statistic of $A$.
\item Median $A$ is the $\floor{\frac{n+1}{2}}$ order statistic.
\end{itemize}
\end{theorem}

\subsection{Minimum and Maximum Algorithms}

Minimum and maximum algorithms just iterate through to find min or max.

\paragraph{Finding minimum and maximum efficiently: } Iterate through, but compare \textbf{next two elements} at a time. Compare the larger element to the current maximum and the smaller of the two to the current minimum.
\begin{itemize}
\item \textbf{Even length array: } $3n/2-2$ comparisons.
\item \textbf{Odd length array: } $3\floor{n/2}$ comparisons.
\end{itemize}

\subsection{Selection in Average Linear Time}

\paragraph{Outcome: } It is possible to find the $i$th smallest element in average case $O(n)$ time (worst case $\Theta(n^2)$).

\begin{algorithm}[H]
\SetKwInOut{Input}{Input}\SetKwInOut{Output}{Output}
\SetAlgoLined
\Input{Array $A$, partition bounds $p,r$, desired order statistic $i$}
\Output{The $i$th order statistic of $A$}
	\BlankLine
	\BlankLine
	%\nlset{\color{blue}$O(n)$}Some order $n$ operation\;
	\If{$p = r$}{
		return $A[p]$\;
	}

	$q$ = RandomizedPartition($A,p,r$)\;
	$k = q-p+1$\;

	\eIf{$i=k$}{
		return $A[q]$\;
	}{$i < k$}{
		return RandomizedSelect($A,p,q-1,i$)\;
	}{
		return RandomizedSelect($A,q+1,r,i-k$)\;
	}
	\caption{Randomized Select Algorithm}
\end{algorithm}

\subsection{Selection in Worst Case Linear Time}

\paragraph{Outcome: } This algorithm can select the $i$th order statistic in $O(n)$ time.

\paragraph{Algorithm: }
\begin{enumerate}
\item Partition $A$ into $\floor{n/5}$ groups of 5 and one ``extras'' group.
\item Find median of each group of find using \textbf{insertion sort}.
\item Let $x = $ \textbf{Select}(medians, $\floor{number of groups/2}$).
\item Let $k = $ \textbf{Partition}($A, 0, |A|, \text{pivot}=x$). $k$ is now the number of elements larger than $x$.
\item If $i=k$ then return $x$. Else, if $i < k$ then return \textbf{Select}($A[0:k],i$). Otherwise, return \textbf{Select}(A[k:],i-k).
\end{enumerate}

By substitution, we can show that the algorithm runs in $O(n)$.

\chapter{Hash Tables}

Perform well for tasks like \textbf{insert, search, and delete}. $\Theta(n)$ worst case for search, $O(1)$ is the practical average.

\section{Direct Addressing}

\begin{itemize}
\item Each element we want to store has a \textbf{key} k in \textbf{universe} $U$.
\item $|U| = m$, $U = \{0, 1, ..., m-1\}$.
\item $T[0,...,m-1]$ is the \textbf{direct address table}.
\end{itemize}

\paragraph{Direct Address Search}$(T,k)$: return $T[k]$.

\paragraph{Direct Address Insert}$(T,x)$: $T[x$.key$]=x$.

\paragraph{Direct Address Delete}$(T, x)$: $T[x.\text{key}] =$ NIL.

The problem arises when the universe $U$ gets large. This happens when you have a ton of unique elements.

\section{Hash tables}

\begin{theorem}{Hash Function Definition:}
A hash function $h$ maps the latent universe of keys $U$ to a universe of some specific size $m$. That is,
\begin{equation}
h:U\to \{0,..., m-1\}
\end{equation}
\end{theorem}

\paragraph{Collision Resoution with Chaining: } Just add to a linked list.
\begin{enumerate}
\item \textbf{Insertion: } $x$ is added to the \textbf{head} of the linked list.
\item \textbf{Search: } Search for element in the linked list that it hashes to.
\item \textbf{Delete: } Delete from linked list as usual.
\end{enumerate}

\paragraph{Performance: } If the number of elements $n = O(m)$, you'll get, on average, $O(1)$ performance.

\paragraph{Simple Uniform Hashing: } Each element has same probability of hashing to given value.

\section{Hash Functions}

Here are a few types of commonly-used hash functions. Note that most datatypes (strings, characters, etc.) can be converted to natural numbers by place value. They just might be very large.

\subsection{Divisision Method}

To map key $k$ to one of $m$ slots, we can let
\begin{equation}
h(k) = k \% m
\end{equation}

\paragraph{Considerations: }
\begin{itemize}
\item $m$ should not be $2^n$ because then it'd just extract bottom $n$ bits.
\item Usually good to pick prime number $m$.
\end{itemize}

\subsection{Multiplication Method}

We multiply $k$ by some $A\in [0,1]$ then use division method.
\begin{equation}
h(k) = \floor{m(kA\% 1)}
\end{equation}

\paragraph{Considerations: }
\begin{itemize}
\item Value of $m$ does not matter any more ($2^n$ makes for easy hardware implementation.
\item $A \approx \frac{\sqrt 5 - 1}{2}$ tends to work well.
\end{itemize}

\subsection{Open Addressing}

The key idea is to let $h()$ take in a ``probe number'' $i$. We increment this number to make sure hash table gets filled without doubling up on a single slot.

\begin{itemize}
\item \textbf{Probe sequence} of $k$ is $\langle h(k,0), ..., h(k, m-1) \rangle$.
\item The probe sequence for each $k$ is a \textbf{permutation of} $\langle 0,...,m-1  \rangle$.
\item Because of this property, we can always fill up the table perfectly.
\end{itemize}

\paragraph{Insertion: } Probe table, incrementing $i$ until you reach an empty slot. Add the element there.
\paragraph{Search: } Keep probing until you either find the element \textbf{or you find NIL}.
\paragraph{Deletion: } When you find value, set it to DELETED -- NIL would stop searches that would have to traverse.

\paragraph{Performance of Open Addressing: }
\begin{itemize}
\item If $n/m \leq 1$: The expected number of probes for an \textbf{unsuccessful} search is $$\gamma \leq \frac{1}{1-\frac{n}{m}}$$
\item Inserting to open address table also takes $\gamma$ probes on average.
\item Number of probes for a successful search is $$\leq \frac{1}{\alpha} \ln \frac{1}{1-\alpha}$$
Where $\alpha = n/m \leq 1$.
\end{itemize}

\paragraph{Linear Probing: } If $h'(k)$ is single-variable hash function, we can make a probing hash function via:
\begin{equation}
h_{lin}(k,i) = (h'(k) + i)\% m
\end{equation}

\textbf{Drawback:} Long strings of filled slots build up one after another.

\paragraph{Quadratic Probing: } $h'(k)$ is a single-variable hash function:
\begin{equation}
h(k,i) = (h'(k) + c_1 i + c_2 i^2)\% m
\end{equation}

\textit{Where $c_1, c_2 \geq 0$, $i\in [m-1]$}.
\begin{itemize}
\item Better performance than linear.
\item We still struggle with \textbf{secondary clustering} -- if $h'(k_1) = h'(k_2)$, we still have the same hash sequences.
\end{itemize}

\paragraph{Double Hashing}

\begin{equation}
h(k,i) = (h_1(k) + ih_2(k)) \% m)
\end{equation}
Now probe sequences are highly unlikely to overlap for different keys. \textbf{CONDITIONS: }
\begin{itemize}
\item $h_2(k)$ should be ``relatively prime'' to $m$. The entire should be searched by multiples of each $h_2$.
\item \textbf{Method 1: } $m = 2^x$, make sure $h_2(k)$ maps to an odd number.
\item \textbf{Method 2: } $m$ is prime, make sure $h_2(k)$ maps to positive inteer less than $m$.
\end{itemize}

\chapter{Dynamic Programming}

\paragraph{Motivation: } We often solve the same sub-problem many times, particularly during recursive optimization algorithms.

\paragraph{Solution: } Memorize the intermediate optimization answers.

\section{Elements of Dynamic Programming}

\begin{enumerate}
\item Optimal Substructure.
\item Overlapping sub-problems.
\end{enumerate}

\paragraph{Optimal sub-structure: } Optimal solution is $f(\text{input}, \text{optimal solutions to sub-problem})$. A common pattern is:
\begin{enumerate}
\item Make some choice (hopefully optimal choice).
\item Realize you need to solve basically the same problem (but smaller).
\item Observer recurrence.
\item Prove \textbf{by contradiction} that optimal sub-solution leads to the optimal solution.
\end{enumerate}

\paragraph{Tips and Tricks}
\begin{itemize}
\item Keep sub-problem space as simple as possible.
\item Think through sequential alternatives you might try to make things countable.
\item You can try to create a sub-problem graph, too.
\item \textbf{PRACTICE}.
\end{itemize}

\section{Examples}
\textit{High-level descriptions of dynamic programming algorithms.} It seems that most problems are variations on a smaller set of dynamic programming problems.

\subsection{Rod Cutting}
\paragraph{Given: } A rod of length $n$ and a vector $p = [15, 20, 31, ..., 100]$ representing selling price for different lengths of rod.

\paragraph{Wanted: } Maximum price to sell the rod for given optimal cutting.

\paragraph{Dynamic Programming Solution: }
\begin{itemize}
\item The first step is to determine where to place your first cut.
\item To decide between a 1-unit long cut and a $p[10]$ unit long cut, you would want to know the reward from each possible cut AND the maximum possible reward for what remains.
\item Therefore, \begin{equation}
\text{optim}(n, p) = \max\{p_n, p_{n-1} + \text{optim}(1,p), ..., p_1+\text{optim}(n-1,p)\}
\end{equation}
\item With a general recursive strategy, we would end up calculating the same $\text{optim}(n,p)$ many time.
\item Therefore, as soon as we calculate some $\text{optim}(n,p)$, we store it in a hash table (called \textbf{memoization}.
\end{itemize}

% \subsection{Matrix Chain Multiplication}

%\subsection{Longest Common Subsequence}
%TODO: Add notes (or at least study in detail) examples for dynamic programming.

% \subsection{Knapsack Problem}

% \subsection{Huffman Codes}

\chapter{Greedy Algorithms}

\section{Introduction}

The essence of a greedy algorithm is to select the \textbf{locally optimal} decision at every opportunity. It is generally efficient, and when it is optimal, is a very convenient choice. Greedy algorithms approximate NP-complete problems well.

% \section{Examples}

% \subsection{Class Scheduling}

%TODO: Populate this section.

\chapter{Amortized Analysis}

\paragraph{What is amortized analysis? }
\begin{itemize}
\item We are often interested in the \textbf{aggregate} cost over a sequence of operations.
\item There are often connections between the number of one operation and another.
\begin{itemize}
\item For instance, you can only pop as many items as you have pushed to a stack.
\end{itemize}
\item Amortized analysis guarantees the \textbf{average performance for the worst case} via \textit{deterministic analysis}.
\end{itemize}

\section{Aggregate Method}
\begin{enumerate}
\item Define an $O(1)$ operation for the problem.
\item Observe the worst-case number of operations for $n$ executions of a process.
\item Divide the cost for $n$ executions by $n$ to get amortized cost.
\end{enumerate}

\paragraph{Example: Stack }
\begin{itemize}
\item Push and pop are $O(1)$.
\item Multipop is $O(n)$ -- this is when you pop every element.
\item Therefore, for $n$ operations, we have at most $n\cdot O(n)$, or $n$ multipops.
\item \textbf{Aggregate:}
\begin{itemize}
\item We cannot pop more items than have been pushed.
\item The maximum number of items pushed is $n$.
\item Therefore the average worst case cost for $n$ operations is $O(n)$, meaning that we have average $O(1)$ for each operation.
\end{itemize}
\end{itemize}

\section{Accounting Method}

\paragraph{Central Ideas:}
\begin{itemize}
\item We charge some number of \textbf{dollars} for each operation. This is the \textbf{amortized cost}.
\item The datastructure gets those \textbf{dollars} to complete the operation.
\item We define some ``real cost'' of each operation. The exact dollar value doesn't matter, the important thing is \textbf{whether it is a function of $n$ or just constant}.
\item If our imposed \textbf{amortized cost} is more than the ``real cost'', the data structure gets \textbf{credit}.
\begin{itemize}
\item Else, the data structure \textbf{uses credit} it has stored to complete the operation.
\item \textbf{NEGATIVE BALANCE MEANS A WRONG CREDIT ALLOCATION}.
\end{itemize}
\end{itemize}

\section{Splay Trees}

\paragraph{Motivation: } We need efficient insertion, deletion, search, and sorting.
\begin{itemize}
\item If key access frequencies are known, we can use \textbf{dynamic programming}.
\item if the key frequencies are unknown, we use \textbf{splay trees}.
\end{itemize}

\textbf{Splay trees are theoretically optimal} in the amortized case.

\paragraph{Key Idea: } We simply use a conventional BST, but introduce a \textbf{splay} operation.
\begin{itemize}
\item An unbalanced tree is \textbf{credit rich}.
\item A balanced tree is not credit rich.
\item Credit is spent to make the tree more balanced, generally.
\end{itemize}

\begin{algorithm}[H]
\SetKwInOut{Input}{Input}\SetKwInOut{Output}{Output}
\SetAlgoLined
\Input{Node $x$ of splay tree.}
\Output{Tree is splayed (re-balanced) in place.}
	\BlankLine
	\BlankLine
	%\nlset{\color{blue}$O(n)$}Some order $n$ operation\;
	\While{$x\neq \text{root}$}{
		\If{$P(x) = \text{root}$}{
			rotate $P(x)$\;
		}
		\eIf{$P(x)$ and $x$ are both (left or right) children}{
			rotate $P(P(x))$\;
			rotate $P(x)$\;
		}{
			rotate $P(x)$\;
			rotate new $P(x)$\;
		}

	}
	\caption{Splay Algorithm}
\end{algorithm}

\paragraph{Cost of Splay: }
\begin{itemize}
\item $wt(x)$ is defined as the \textbf{number of nodes} in the sub-tree rooted at $x$ (descendants of $x$ including $x$).
\item $\text{rank}(x) = \ceil{\log wt(x)}$ is the \textbf{height} of sub-tree rooted at $x$.
\item \textbf{Credit Invariant: } Node $x$ has \$ $\text{rank}(x)$ in credit.
\item Therefore, each \{zig, zig-zig, zig-zag\} costs $$3[\text{new rank}(x) - \text{old rank}(x)]$$
\item The amortized cost of \textbf{splay} is therefore $$O(\log n)$$
\end{itemize}

\paragraph{Splay Tree Operations: }
\begin{enumerate}
\item \textbf{Search: }
\begin{enumerate}
\item BST Search for $x$.
\item \textbf{Splay}$(x)$.
\item \textit{Cost: } $O(\log n)$
\end{enumerate}

\item \textbf{Insert: }
\begin{enumerate}
\item BST Insert $x$ ($x$ is now a leaf).
\item \textbf{Splay}$(x)$.
\item \textit{Cost: } $O(\log n)$
\end{enumerate}

\item \textbf{Delete}
\begin{enumerate}
\item BST Delete $x$.
\item \textbf{Splay}$(P(x))$.
\item \textit{Cost: } $O(\log n)$
\end{enumerate}

\item \textbf{Split: } If you want to create two new sub-BST's, one with all values less than $x$ and one with values greater than $x$,
\begin{enumerate}
\item Perform \textbf{Splay Search}($x$) -- $x$ is now the root.
\item Split off the right and left trees.
\item \textit{Cost: } $O(\log n)$
\end{enumerate}

\item \textbf{Join: } Basically the reverse operation of \textbf{split}. Given two splay trees, one with values $\leq x$ and one with values $> x$,
\begin{enumerate}
\item Make the $\leq x$ tree the left child of $x$, the $> x$ the right child of $x$.
\item \textbf{Credit the tree} $O(\log n)$ dollars so that $x$ satisfies the credit invariant.
\item \textit{Cost: } $O(\log n)$
\end{enumerate}
\end{enumerate}

\chapter{Theory of Computation and Automata}

\section{Definitions and Preliminaries}

\subsection{Framing}

\paragraph{Course Focus: } Decision problems (i.e. problems with ``yes'' and ``no'') answers. E.g. `does this graph have a clique of size 5 or more?''

\paragraph{Measure of Difficulty: } A problem is more difficult if it takes \textbf{more computational steps} for a machine to solve. The key factor is whether it takes \textbf{polynomial time} or \textbf{non-polynomial time} (e.g., exponential time).

\paragraph{Motivation: } We utilize an extra layer of abstraction to answer questions about \textbf{classes of problems} (e.g. shortest paths, finding cliques, etc.). It is infeasible to go through the (literally infinite) potential \textbf{instances} of a problem class.

\subsection{Definitions}

\paragraph{Alphabet: } Finite, ordered set of symbols used to convey meaning. All alphabets are \textbf{reducible} to the binary alphabet, so we stick to that for the most part.

\paragraph{String: } A \textit{string over alphabet $A$} is a sequence of symbols from a given alphabet $A$.
\begin{itemize}
\item Strings with no characters are represented by $\varepsilon$.
\item Union between alphabets is possible, but you need to define some order.
\end{itemize}

\paragraph{Concatenation: } We represent concatenation of string $\alpha$ and $\beta$ as $\alpha\beta$.
\begin{itemize}
\item $\alpha^i$ means concatenating $i$ copies of string $\alpha$.
\item $\alpha^0 = \varepsilon$.
\end{itemize}

\paragraph{Set of all Strings: } $\Sigma^*$ is the set of all possible strings over alphabet $\Sigma$.

\paragraph{Reverse: } $\alpha^R$ is the reverse string of $\alpha$.

\begin{theorem}{Definition of a Language:}
A language is a \textbf{set of strings} over an alphabet. $L$ is a \textbf{language} over alphabet $\Sigma$ if
\begin{equation}
L\subseteq \Sigma^*
\end{equation}

Each element of $L$ is a \textbf{string over the language}.

\paragraph{Operations on Languages: }
\begin{enumerate}
\item $L_1 \cup L_2$: Union of two languages
\item $L_1 \cap L_2$: Intersection of two languages
\item $\bar L$: Compliment of language $L$.
\item $L_1 - L_2$: Strings in $L_1$ not contained in $L_2$.
\item $L^i$: Concatenating $i$ copies of \textbf{each string} in language $L$.
\item $L^* = L^0 \cup L^1 \cup L^2 \cup ...$: \textbf{Kleene Closure} of $L$ -- taking all possible concatenations of strings in the language.
\end{enumerate}
\end{theorem}

We think of languages as \textbf{problems}. The decision problem for language $L$ is: ``Does string $\alpha$ belong in $L$ or not?''.

\section{Regular Languages}

Regular languages are a ``stepping stone'' for getting to more advanced languages. They're pretty powerful, and we still see them today in \textbf{regular expressions}.

\paragraph{Regular Expressions: } Regular expressions are defined over an alphabet. They are a way to represent sets of strings as combinations of other strings and other regular expressions. For alphabet $\Sigma$:
\begin{enumerate}
\item $\varepsilon$ is a regular expression.
\item All $\alpha \in \Sigma$ are regular expressions.
\item Let R and S be valid regular expressions.
\begin{itemize}
\item Then ``R+S'' is a regular expression meaning \textbf{either R or S}.
\item then ``RS'' (concatenation) is a regular expression that searches for R followed by S.
\item R* is a regular expression looking for the \textbf{Kleene Closure} of the set of strings described by R.
\item (R) is also a regular expression.
\end{itemize}
\end{enumerate}

\paragraph{Regular Language: } All the strings that a given \textbf{regular expressions} $R$ defines. Represented by $L(R)$.

\section{Complexity Classes P, NP}

\begin{theorem}{Complexity Class $P$ Definition: }
All languages for which there exists an algorithm $A$ that \textbf{decides} $L$ in \textbf{polynomial time} belong to class $P$.
\end{theorem}

\begin{theorem}{Complexity Class $NP$ Definition: }
$NP$ is the set of languages $L$ where:
\begin{itemize}
\item There exists a \textbf{polynomial time} algorithm $A$ and constant $c$
\item such that, given a ``certificate'' (\textbf{candidate solution}) $y$ of size $|y| = O(|x|^c)$
\item $A(x,y) = 1$.
\item That is, $A$ can \textbf{verify} a candidate solution for an instance of the problem in polynomial time with respect to the size of the initial problem.
\end{itemize}
\end{theorem}

\begin{theorem}{Relationship between class $P$ and $NP$}
\begin{equation}
P\subseteq NP
\end{equation}
Therefore, if we have a polynomial-time algorithm $A$ that can decide $L$, then $L$ is definitely a member of both $P$ and $NP$.
\end{theorem}

\begin{theorem}{P is Closed under Compliment}
If $L\in P$, then $\bar L \in P$. Simply flip the output of the algorithm that can decide $L$ in polynomial time to decide $\bar L$ in polynomial time.
\end{theorem}

\paragraph{Open Questions in the Field: }
\begin{itemize}
\item $NP = co-NP$?
\item $P=NP\cap co-NP$?
\item $NP = P$?
\end{itemize}

\begin{theorem}{Reducibility: }
Language $L_1$ is \textbf{polynomially reducible} to $L_2$ (i.e. $L_1 \leq_P L_2$) iff $\exists$ polynomial-time algorithm $f()$ such that
\begin{equation}
x\in L_1 \text{ iff } f(x) \in L_2
\end{equation}
\end{theorem}

\begin{theorem}{Reducibility implication: }
If $L_1 \leq_P L_2$ and $L_2\in P$, then $L_1\in P$.
\end{theorem}

\section{NP-Completeness}

\begin{theorem}{NP-Completeness Definition: }
$L\in NP-\text{Complete}$ iff:
\begin{itemize}
\item $L\in NP$ ($L$ can be decided in polynomial time).
\item For every $L'\in NP$, $L'\leq_P L$. (Any problem in NP can be transformed into $L$ in polynomial time).
\end{itemize}
\end{theorem}

\subsection{Methodology to Prove NP-Completeness}

\paragraph{Given language $L$, we show it is NP-Complete by:}
\begin{enumerate}
\item Prove that the language can be verified in polynomial time ($L\in NP$) \textit{(5/15 marks, easy)}.
\item Select known $L'\in NPC$.
\begin{enumerate}
\item Describe algorithm $f()$ that converts instances of $L'$ into instances of $L$ such that
\begin{equation}
[x\in L'] \leq_P [f(x)\in L]
\end{equation}
\item Prove that $x\in L'$ iff $f(x)\in L$ (be sure to prove this both ways).
\item Show that $f()$ takes polynomial time to solve. Usually, this means saying ``we introduce polynomial objects for $L$ for each object in $L'$.''.
\end{enumerate}
\end{enumerate}

\end{document}


\documentclass[../main.tex]{subfiles}
\begin{document}
\subsection{One-to-all see \autoref{q:onetoall}}
\subsection{LU factorisation see \autoref{q:lu}}
\subsection{Shared Memory}
\label{q:sm2}
\begin{question}
	We have p threads running the same SPMD program on a shared memory computer.
	The algorithm given in \autoref{lst:matavg} should calculate the global average of the matrix and make that average available to all processors.
\begin{lstlisting}[caption={Matrix average program on a shared memory computer},label=lst:matavg]
void main(){
	double r[m];
	double a[m][n];

	for (i=s; i<p; i+=p){
		for(j = 0; j <n ; j ++){
			r[i] += a[i][j];
		}
	}

	double avg = average(r);
}
\end{lstlisting}
\begin{enumerate}
	\item Complete the average function so that it does this in parallel.
	\item Are there any problems with the algorithm with relation to efficiency? If so how can you solve them.
\end{enumerate}

\end{question}

\begin{solution}
\begin{enumerate}
	\item
	\item
\end{enumerate}
\end{solution}
\subsection{BSP Quicksort 2}
\label{q:qsort2}
\begin{question}
Assume that the BSP quicksort algorithm always -magically- choses the optimal pivot.
\begin{enumerate}
	\item Give a high-level description of the algorithm, it's total cost and for each superstep it's bsp cost.
	\item Why is chosing an optimal pivot even more important in a parallel version of quicksort than it is in a sequential version.
\end{enumerate}
\end{question}
\begin{solution}
\begin{enumerate}
	\item
	\item
\end{enumerate}
\end{solution}
\end{document}


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Short Sectioned Assignment
% LaTeX Template
% Version 1.0 (5/5/12)
%
% This template has been downloaded from:
% http://www.LaTeXTemplates.com
%
% Original author:
% Frits Wenneker (http://www.howtotex.com)
%
% License:
% CC BY-NC-SA 3.0 (http://creativecommons.org/licenses/by-nc-sa/3.0/)
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%----------------------------------------------------------------------------------------
%	PACKAGES AND OTHER DOCUMENT CONFIGURATIONS
%----------------------------------------------------------------------------------------

\documentclass[paper=a4, fontsize=11pt]{scrartcl} % A4 paper and 11pt font size

\usepackage[T1]{fontenc} % Use 8-bit encoding that has 256 glyphs
\usepackage{fourier} % Use the Adobe Utopia font for the document - comment this line to return to the LaTeX default
\usepackage[english]{babel} % English language/hyphenation
\usepackage{amsmath,amsfonts,amsthm} % Math packages
\usepackage{titling}
\usepackage{lipsum} % Used for inserting dummy 'Lorem ipsum' text into the template

\usepackage{multicol}
\setlength{\columnsep}{1cm}

\usepackage{enumitem}

\usepackage{setspace} %\begin{doublespacing} \end...

\usepackage{sectsty} % Allows customizing section commands
\allsectionsfont{\centering \normalfont\scshape} % Make all sections centered, the default font and small caps

\usepackage{fancyhdr} % Custom headers and footers
\pagestyle{fancyplain} % Makes all pages in the document conform to the custom headers and footers
\fancyhead{} % No page header - if you want one, create it in the same way as the footers below
\fancyfoot[L]{} % Empty left footer
\fancyfoot[C]{} % Empty center footer
\fancyfoot[R]{\thepage} % Page numbering for right footer
\renewcommand{\headrulewidth}{0pt} % Remove header underlines
\renewcommand{\footrulewidth}{0pt} % Remove footer underlines
\setlength{\headheight}{13.6pt} % Customize the height of the header

\numberwithin{equation}{section} % Number equations within sections (i.e. 1.1, 1.2, 2.1, 2.2 instead of 1, 2, 3, 4)
\numberwithin{figure}{section} % Number figures within sections (i.e. 1.1, 1.2, 2.1, 2.2 instead of 1, 2, 3, 4)
\numberwithin{table}{section} % Number tables within sections (i.e. 1.1, 1.2, 2.1, 2.2 instead of 1, 2, 3, 4)

\setlength\parindent{0pt} % Removes all indentation from paragraphs - comment this line for an assignment with lots of text

%----------------------------------------------------------------------------------------
%	TITLE SECTION
%----------------------------------------------------------------------------------------

\newcommand{\horrule}[1]{\rule{\linewidth}{#1}} % Create horizontal rule command with 1 argument of height

\title{
\normalfont \normalsize
\textsc{Northridge Preparatory School} \\ [25pt] % Your university, school and/or department name(s)
\horrule{0.5pt} \\[0.4cm] % Thin top horizontal rule
\huge Worksheet\#3 : Variable Assignment Practice I\\ % The assignment title
\horrule{2pt} \\[0.5cm] % Thick bottom horizontal rule
}

\preauthor{}
\postauthor{}
\author{} % Your name

\predate{}
\postdate{}
\date{} % Today's date or a custom date

\begin{document}

\maketitle % Print the title

%----------------------------------------------------------------------------------------
%	PROBLEM 1
%----------------------------------------------------------------------------------------

\section{Modeling With Expressions}

In order to write mathematical programs that effectively model real-life situations, you must identify the key variables.  The following problems outline a series of increasingly complex situations which require more variables.  This time, the variables are identified for you.  Your task is to create a mathematical expression that accurately represents their relationship to total profit.

\subsection{Open For Business!}

Summer is here, and you want to make some money.  So you decide to start a business mowing lawns around your neighborhood. You decide to charge \$5.00 per lawn.  This Saturday, two of your neighbors hire you once, and one hires you twice (front \& back).  How much did you make this week?

\begin{multicols}{3}

\begin{doublespacing}

\underline{Variable}

Price per lawn

Number of lawns

\underline{Variable Name}

\rule{2.5cm}{0.15mm}

\rule{2.5cm}{0.15mm}

\underline{Variable Value}

\rule{2.5cm}{0.15mm}

\rule{2.5cm}{0.15mm}

\end{doublespacing}
\end{multicols}

\begin{doublespacing}
Write out the equation for the Total Profit \textbf{using only the variable names}:

\vspace{0.5cm}

Profit =   \rule{10cm}{0.15mm}

%SOLUTION:   P = n*l

\end{doublespacing}

\pagebreak
%------------------------------------------------
\subsection{Thirst For Success}

Mowing lawns out in the hot sun is hard work.  You need to make sure you don't get dehydrated.  So you decide to use some of the money you earn to buy some sports drinks.  You will need one 12-oz bottle per lawn you mow except the last (You go home at the end and eat a popsicle).  Each bottle costs \$0.50.\\

This Saturday, 3 neighbors hire you once and 2 hire you twice.  How much did you make?

\begin{multicols}{3}

\begin{doublespacing}

\underline{Variable}

Price per lawn

Number of lawns

Price per drink

\underline{Variable Name}

\rule{2.5cm}{0.15mm}

\rule{2.5cm}{0.15mm}

\rule{2.5cm}{0.15mm}

\underline{Variable Value}

\rule{2.5cm}{0.15mm}

\rule{2.5cm}{0.15mm}

\rule{2.5cm}{0.15mm}

\end{doublespacing}
\end{multicols}

(Notice that you do not need a separate variable name for "Number of drinks".  Why not?)

\begin{doublespacing}
Write out the equation for the Total Profit \textbf{using only the variable names}:

\vspace{0.5cm}

Profit =   \rule{10cm}{0.15mm}

%SOLUTION:  P = l*n - d*(n-1)

\end{doublespacing}

%------------------------------------------------
\pagebreak
\subsection{Expansion}

You do such a good job that households in more distant parts of your neighborhood want to hire you.  But it's a real pain to lug all your equipment around, and it takes up time and car fuel.  You decide to charge a \$2  flat distance fee to anyone who lives more than  5 miles away.\\

This weekend, near your house 5 neighbors hire you once, and two hire you twice.  Far from your house, 1 person hires you once, and 3 people hire you twice.  How much did you make?

\begin{multicols}{3}

\begin{doublespacing}

\underline{Variable}

Price per lawn

Number of lawns

Price per drink

Distance fee

Number of far houses

\underline{Variable Name}

\rule{2.5cm}{0.15mm}

\rule{2.5cm}{0.15mm}

\rule{2.5cm}{0.15mm}

\rule{2.5cm}{0.15mm}

\rule{2.5cm}{0.15mm}

\underline{Variable Value}

\rule{2.5cm}{0.15mm}

\rule{2.5cm}{0.15mm}

\rule{2.5cm}{0.15mm}

\rule{2.5cm}{0.15mm}

\rule{2.5cm}{0.15mm}

\end{doublespacing}
\end{multicols}

\begin{doublespacing}
Write out the equation for the Total Profit \textbf{using only the variable names}:

\vspace{0.5cm}

Profit =   \rule{10cm}{0.15mm}

%SOLUTION: P = l*n - d*(n-1) + fee*far

\end{doublespacing}

%THE FOLLOWING WAS ADDED TO WKST #7
%\pagebreak
%%------------------------------------------------
%
%\subsection{Help Wanted}
%
%Business is booming!  You now have more customers than you can handle on your own.  So you decide to hire your two younger brothers to mow the houses within walking distance.  They of course demand to be paid and supplied with sports drinks.  You decide to pay them each \$3.00 per lawn (only one person mows one lawn) and provide them each with one 12-oz sports drink per lawn except the last.
%
%This weekend, near your house 10 neighbors hire you once, and 4 hire you twice.  Far from your house, 3 people hire you once, and 3 hire you twice.  How much did you make?
%
%\begin{multicols}{3}
%
%\begin{doublespacing}
%
%\underline{Variable}
%
%Price per lawn
%
%Number of lawns
%
%Price per drink
%
%Number of Employees
%
%Employee fee per lawn
%
%Distance fee
%
%Number of far houses
%
%\underline{Variable Name}
%
%\rule{2.5cm}{0.15mm}
%
%\rule{2.5cm}{0.15mm}
%
%\rule{2.5cm}{0.15mm}
%
%\rule{2.5cm}{0.15mm}
%
%\rule{2.5cm}{0.15mm}
%
%\rule{2.5cm}{0.15mm}
%
%\rule{2.5cm}{0.15mm}
%
%\underline{Variable Value}
%
%\rule{2.5cm}{0.15mm}
%
%\rule{2.5cm}{0.15mm}
%
%\rule{2.5cm}{0.15mm}
%
%\rule{2.5cm}{0.15mm}
%
%\rule{2.5cm}{0.15mm}
%
%\rule{2.5cm}{0.15mm}
%
%\rule{2.5cm}{0.15mm}
%
%\end{doublespacing}
%\end{multicols}
%
%
%
%\begin{doublespacing}
%Write out the equation for the Total Profit \textbf{using only the variable names}:
%
%\vspace{0.5cm}
%
%Profit =   \rule{10cm}{0.15mm}
%
%\end{doublespacing}
%
%
%\pagebreak
%
%

\end{document}

Worksheet 3 - Variable Assignment Practice(easy 1-3) - ch 2.tex


%
% 6.006 problem set 1
%
\documentclass[12pt,twoside]{article}

\usepackage{amsmath}

\input{macros}

\setlength{\oddsidemargin}{0pt}
\setlength{\evensidemargin}{0pt}
\setlength{\textwidth}{6.5in}
\setlength{\topmargin}{0in}
\setlength{\textheight}{8.5in}

% Fill these in!
\newcommand{\theproblemsetnum}{1}
\newcommand{\releasedate}{September 8, 2011}
\newcommand{\partaduedate}{Thursday, September 15}
\newcommand{\tabUnit}{3ex}
\newcommand{\tabT}{\hspace*{\tabUnit}}

\begin{document}

\handout{Problem Set \theproblemsetnum}{\releasedate}

\newif\ifsolution
\solutiontrue
\newcommand{\solution}{\textbf{Your Solution:}}

\textbf{Both theory and programming questions} are due {\bf \partaduedate} at {\bf 11:59PM}.
%
Please download the .zip archive for this problem set, and refer to the
\texttt{README.txt} file for instructions on preparing your solutions.
%
Remember, your goal is to communicate. Full credit will be given only
to a correct solution which is described clearly. Convoluted and
obtuse descriptions might receive low marks, even when they are
correct. Also, aim for concise solutions, as it will save you time
spent on write-ups, and also help you conceptualize the key idea of
the problem.

We will provide the solutions to the problem set 10 hours after the problem set
is due, which you will use to find any errors in the proof that you submitted.
You will need to submit a critique of your solutions by \textbf{Tuesday,
September 20th, 11:59PM}. Your grade will be based on both your solutions and
your critique of the solutions.

\setlength{\parindent}{0pt}

\medskip

\hrulefill

\textbf{Collaborators:}
%%% COLLABORATORS START %%%
None.
%%% COLLABORATORS END %%%

\begin{problems}

\problem \points{15} \textbf{Asymptotic Practice}

For each group of functions, sort the functions in increasing order of
asymptotic (big-O) complexity:

\begin{problemparts}

\problempart \points{5} \textbf{Group 1:}

$$
\begin{array}{rcl}
f_1(n) &=& n^{0.999999} \log n \\
f_2(n) &=& 10000000 n \\
f_3(n) &=& 1.000001^n \\
f_4(n) &=& n^2
\end{array}
$$

\ifsolution \solution{}
%%% PROBLEM 1(a) SOLUTION START %%%
1, 2, 3, 4
%%% PROBLEM 1(a) SOLUTION END %%%
\fi

\problempart \points{5} \textbf{Group 2:}

$$
\begin{array}{rcl}
f_1(n) &=& 2^{2^{1000000}} \\
f_2(n) &=& 2^{100000n} \\
f_3(n) &=& \displaystyle \binom{n}{2} \\
f_4(n) &=& n \sqrt{n}
\end{array}
$$

\ifsolution \solution{}
%%% PROBLEM 1(b) SOLUTION START %%%
1, 2, 3, 4
%%% PROBLEM 1(b) SOLUTION END %%%
\fi

\problempart \points{5} \textbf{Group 3:}

$$
\begin{array}{rcl}
f_1(n) &=& n^{\sqrt{n}} \\
f_2(n) &=& 2^n \\
f_3(n) &=& n^{10} \cdot 2^{n / 2} \\
f_4(n) &=& \displaystyle\sum_{i = 1}^{n} (i + 1)
\end{array}
$$

\ifsolution \solution{}
%%% PROBLEM 1(c) SOLUTION START %%%
1, 2, 3, 4
%%% PROBLEM 1(c) SOLUTION END %%%
\fi

\end{problemparts}

\problem \points{15} \textbf{Recurrence Relation Resolution}

For each of the following recurrence relations,
pick the correct asymptotic runtime:

\begin{problemparts}

\problempart \points{5}
Select the correct asymptotic complexity
of an algorithm with runtime $T(n, n)$
where
$$
\begin{array}{rcll}
T(x, c) &=& \Theta(x) & \textrm{ for $c \le 2$}, \\
T(c, y) &=& \Theta(y) & \textrm{ for $c \le 2$, and} \\
T(x, y) &=& \Theta(x + y) + T(x / 2, y / 2).
\end{array}
$$

\begin{enumerate}
\item $\Theta(\log n)$.
\item $\Theta(n)$.
\item $\Theta(n \log n)$.
\item $\Theta(n \log^2 n)$.
\item $\Theta(n^2)$.
\item $\Theta(2^n)$.
\end{enumerate}

\ifsolution \solution{}
%%% PROBLEM 2(a) SOLUTION START %%%
1
%%% PROBLEM 2(a) SOLUTION END %%%
\fi

\problempart \points{5}
Select the correct asymptotic complexity
of an algorithm with runtime $T(n, n)$
where
$$
\begin{array}{rcll}
T(x, c) &=& \Theta(x) & \textrm{ for $c \le 2$}, \\
T(c, y) &=& \Theta(y) & \textrm{ for $c \le 2$, and} \\
T(x, y) &=& \Theta(x) + T(x, y / 2).
\end{array}
$$

\begin{enumerate}
\item $\Theta(\log n)$.
\item $\Theta(n)$.
\item $\Theta(n \log n)$.
\item $\Theta(n \log^2 n)$.
\item $\Theta(n^2)$.
\item $\Theta(2^n)$.
\end{enumerate}

\ifsolution \solution{}
%%% PROBLEM 2(b) SOLUTION START %%%
1
%%% PROBLEM 2(b) SOLUTION END %%%
\fi

\problempart \points{5}
Select the correct asymptotic complexity
of an algorithm with runtime $T(n, n)$
where
$$
\begin{array}{rcll}
T(x, c) &=& \Theta(x) & \textrm{ for $c \le 2$}, \\
T(x, y) &=& \Theta(x) + S(x, y / 2), \\
S(c, y) &=& \Theta(y) & \textrm{ for $c \le 2$, and} \\
S(x, y) &=& \Theta(y) + T(x / 2, y).
\end{array}
$$

\begin{enumerate}
\item $\Theta(\log n)$.
\item $\Theta(n)$.
\item $\Theta(n \log n)$.
\item $\Theta(n \log^2 n)$.
\item $\Theta(n^2)$.
\item $\Theta(2^n)$.
\end{enumerate}

\ifsolution \solution{}
%%% PROBLEM 2(c) SOLUTION START %%%
1
%%% PROBLEM 2(c) SOLUTION END %%%
\fi

\end{problemparts}

\section*{Peak-Finding}

In Lecture 1,
you saw the peak-finding problem.
As a reminder,
a \emph{peak} in a matrix
is a location with the property that its four neighbors
(north, south, east, and west)
have value less than or equal to the value of the peak.
We have posted Python code for solving this problem
to the website in a file called \texttt{ps1.zip}.
In the file \texttt{algorithms.py},
there are four different algorithms
which have been written
to solve the peak-finding problem,
only some of which are correct.
Your goal is to figure out
which of these algorithms are correct
and which are efficient.

\problem \points{16} \textbf{Peak-Finding Correctness}

\begin{problemparts}

\problempart \points{4} Is \texttt{algorithm1} correct?
\begin{enumerate}
\item Yes.
\item No.
\end{enumerate}

\ifsolution \solution{}
%%% PROBLEM 3(a) SOLUTION START %%%
1
%%% PROBLEM 3(a) SOLUTION END %%%
\fi

\problempart \points{4} Is \texttt{algorithm2} correct?
\begin{enumerate}
\item Yes.
\item No.
\end{enumerate}

\ifsolution \solution{}
%%% PROBLEM 3(b) SOLUTION START %%%
1
%%% PROBLEM 3(b) SOLUTION END %%%
\fi

\problempart \points{4} Is \texttt{algorithm3} correct?
\begin{enumerate}
\item Yes.
\item No.
\end{enumerate}

\ifsolution \solution{}
%%% PROBLEM 3(c) SOLUTION START %%%
1
%%% PROBLEM 3(c) SOLUTION END %%%
\fi

\problempart \points{4} Is \texttt{algorithm4} correct?
\begin{enumerate}
\item Yes.
\item No.
\end{enumerate}

\ifsolution \solution{}
%%% PROBLEM 3(d) SOLUTION START %%%
1
%%% PROBLEM 3(d) SOLUTION END %%%
\fi

\end{problemparts}

\problem \points{16} \textbf{Peak-Finding Efficiency}

\begin{problemparts}

\problempart \points{4} What is the worst-case runtime of \texttt{algorithm1} on a problem of size $n \times n$?
\begin{enumerate}
\item $\Theta(\log n)$.
\item $\Theta(n)$.
\item $\Theta(n \log n)$.
\item $\Theta(n \log^2 n)$.
\item $\Theta(n^2)$.
\item $\Theta(2^n)$.
\end{enumerate}

\ifsolution \solution{}
%%% PROBLEM 4(a) SOLUTION START %%%
1
%%% PROBLEM 4(a) SOLUTION END %%%
\fi

\problempart \points{4} What is the worst-case runtime of \texttt{algorithm2} on a problem of size $n \times n$?
\begin{enumerate}
\item $\Theta(\log n)$.
\item $\Theta(n)$.
\item $\Theta(n \log n)$.
\item $\Theta(n \log^2 n)$.
\item $\Theta(n^2)$.
\item $\Theta(2^n)$.
\end{enumerate}

\ifsolution \solution{}
%%% PROBLEM 4(b) SOLUTION START %%%
1
%%% PROBLEM 4(b) SOLUTION END %%%
\fi

\problempart \points{4} What is the worst-case runtime of \texttt{algorithm3} on a problem of size $n \times n$?
\begin{enumerate}
\item $\Theta(\log n)$.
\item $\Theta(n)$.
\item $\Theta(n \log n)$.
\item $\Theta(n \log^2 n)$.
\item $\Theta(n^2)$.
\item $\Theta(2^n)$.
\end{enumerate}

\ifsolution \solution{}
%%% PROBLEM 4(c) SOLUTION START %%%
1
%%% PROBLEM 4(c) SOLUTION END %%%
\fi

\problempart \points{4} What is the worst-case runtime of \texttt{algorithm4} on a problem of size $n \times n$?
\begin{enumerate}
\item $\Theta(\log n)$.
\item $\Theta(n)$.
\item $\Theta(n \log n)$.
\item $\Theta(n \log^2 n)$.
\item $\Theta(n^2)$.
\item $\Theta(2^n)$.
\end{enumerate}

\ifsolution \solution{}
%%% PROBLEM 4(d) SOLUTION START %%%
1
%%% PROBLEM 4(d) SOLUTION END %%%
\fi

\end{problemparts}

\problem \points{19} \textbf{Peak-Finding Proof}

Please modify the proof below to construct a proof of correctness
for the \emph{most efficient correct algorithm}
among \texttt{algorithm2}, \texttt{algorithm3}, and \texttt{algorithm4}.

The following is the proof of correctness
for \texttt{algorithm1},
which was sketched in Lecture 1.

\begin{quote}
We wish to show that \texttt{algorithm1}
will always return a peak,
as long as the problem is not empty.
To that end,
we wish to prove the following two statements:

{\bf 1. If the peak problem is not empty,
then \texttt{algorithm1} will always return a location.}
Say that we start with a problem of size $m \times n$.
The recursive subproblem examined by \texttt{algorithm1}
will have dimensions
$m \times \lfloor n / 2 \rfloor$ or
$m \times \left(n - \lfloor n / 2 \rfloor - 1 \right)$.
Therefore, the number of columns in the problem
strictly decreases with each recursive call
as long as $n > 0$.
So \texttt{algorithm1} either returns a location at some point,
or eventually examines a subproblem with a non-positive
number of columns.
The only way for the number of columns to become strictly negative,
according to the formulas that determine the size of the subproblem,
is to have $n = 0$ at some point.
So if \texttt{algorithm1} doesn't return a location,
it must eventually examine an empty subproblem.

We wish to show that there is no way that this can occur.
Assume, to the contrary,
that \texttt{algorithm1} does examine an empty subproblem.
Just prior to this,
it must examine a subproblem of size
$m \times 1$ or $m \times 2$.
If the problem is of size $m \times 1$,
then calculating the maximum of the central column
is equivalent to calculating the maximum of the entire problem.
Hence, the maximum that the algorithm finds must be a peak,
and it will halt and return the location.
If the problem has dimensions $m \times 2$,
then there are two possibilities:
either the maximum of the central column is a peak
(in which case the algorithm will halt and return the location),
or it has a strictly better neighbor in the other column
(in which case the algorithm will recurse
on the non-empty subproblem with dimensions $m \times 1$,
thus reducing to the previous case).
So \texttt{algorithm1} can never recurse into an empty subproblem,
and therefore \texttt{algorithm1} must eventually return a location.

{\bf 2. If \texttt{algorithm1} returns a location,
it will be a peak in the original problem.}
If \texttt{algorithm1} returns a location $(r_1, c_1)$,
then that location must have the best value in column $c_1$,
and must have been a peak within some recursive subproblem.
Assume, for the sake of contradiction,
that $(r_1, c_1)$ is not also a peak within the original problem.
Then as the location $(r_1, c_1)$ is passed up the chain of recursive calls,
it must eventually reach a level where it stops being a peak.
At that level, the location $(r_1, c_1)$
must be adjacent to the dividing column $c_2$ (where $|c_1 - c_2| = 1$),
and the values must satisfy the inequality
$val(r_1, c_1) < val(r_1, c_2)$.

Let $(r_2, c_2)$ be
the location of the maximum value found by \texttt{algorithm1}
in the dividing column.
As a result, it must be that $val(r_1, c_2) \le val(r_2, c_2)$.
Because the algorithm chose to recurse
on the half containing $(r_1, c_1)$,
we know that $val(r_2, c_2) < val(r_2, c_1)$.
Hence, we have the following chain of inequalities:
$$val(r_1, c_1) < val(r_1, c_2) \le val(r_2, c_2) < val(r_2, c_1)$$
But in order for \texttt{algorithm1} to return $(r_1, c_1)$ as a peak,
the value at $(r_1, c_1)$ must have been the greatest in its column,
making $val(r_1, c_1) \ge val(r_2, c_1)$.
Hence, we have a contradiction.
\end{quote}

\ifsolution \solution{}
%%% PROBLEM 5 SOLUTION START %%%
Write your proof here.
%%% PROBLEM 5 SOLUTION END %%%
\fi

\problem \points{19} \textbf{Peak-Finding Counterexamples}

For each incorrect algorithm,
upload a Python file giving a counterexample
(i.e. a matrix for which the algorithm returns a location
that is not a peak).

\ifsolution \solution{}
%%% PROBLEM 6 SOLUTION START %%%
\begin{verbatim}
problemMatrix = [
    [0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0]
]

problemMatrix = [
    [0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0]
]
\end{verbatim}
%%% PROBLEM 6 SOLUTION END %%%
\fi

\end{problems}

\end{document}

Session 1 (Algorithmic Thinking)/notes/Original Problem Set/ps1_sol.tex


\section{Interacting with the HOL4 REPL}

The HOL4 REPL is an extended version of the Poly/ML~\cite{polymlweb} REPL, and
behaves like the REPL's of other interpreted languages.
In general, it is recommend to first open a script file before starting
the REPL.

\textbf{Important setup note:} We strongly recommend opening a file
\texttt{tutorialScript.sml} in the folder \texttt{\$LASSIEDIR/examples}, to make
sure that the below code can be run easily.
This is done by either manually creating an empty file named \texttt{tutorialScript.sml} in \texttt{\$LASSIEDIR/examples}
and opening it with the toolbar buttons in emacs, or using \ekey{C-x C-f} (\ekey{Control} and \ekey{x}, then \ekey{Control} and \ekey{f})
to open it within emacs, by typing in the path.

We will explain in \autoref{sec:libraries} how the file should start to make its
contents reusable. For now we simply use it as a scratchpad.
The REPL is started by pressing \ekey{M-h H} (\ekey{Alt} and \ekey{h}, then \ekey{H}),
and HOL4 code is send to the REPL with the keybinding \ekey{M-h M-r}
(pressing \ekey{Alt} and \ekey{h}, then \ekey{Alt} and \ekey{r}).

For example, type
\begin{lstlisting}
  3 + 5;
\end{lstlisting}

anywhere in the currently opened script file.
Sending is then done by first highlighting the code with emacs using \ekey{C-space}.
Here, \ekey{C} stands for \ekey{Control}, so to start marking text, press
\ekey{Control} together with the \ekey{space} key.
The arrow keys are used for marking the code to be send to the REPL, and once it
has been completely selected, press \ekey{M-h M-r}.
Alternatively, holding down the shift key while moving the cursor will also select text.

The REPL should print:
\begin{lstlisting}[frame=single]
> 3+5;
val it = 8: int
\end{lstlisting}

All of the functionality of the Poly/ML REPL, and in general, the Standard ML
basis library (see e.g. \url{https://smlfamily.github.io/Basis/} for a reference)
are available in the HOL4 REPL.
Thus HOL4 supports creating and manipulating lists, strings, options, and
simple I/O.

It is strongly recommended to type or copy the code snippets from this tutorial
into an actual script file and sending them to the REPL with the keybinding \ekey{M-h M-r}.
This makes sure that the code can be experimented with and commands that have
been entered are not lost in the limbo of the REPL printouts.

As a quick point of reference, \autoref{tbl:keybindings} gives a short,
executive summary of the most commonly used keybindings, taken from the
documentation of the HOL4 emacs mode (\url{https://hol-theorem-prover.org/hol-mode.html}).

\begin{table}
  \centering
\begin{tabular}{@{}cll@{}}
  \toprule
  Keybinding & \multicolumn{1}{c}{Effect} & \multicolumn{1}{c}{Remark}\\
  \midrule
  \ekey{M-h H} & Start a new HOL4 session & \\
  \ekey{M-h M-r} & Send marked text to REPL & \\
  \ekey{M-h g} & Start a new proof & Must be within a \texttt{Theorem}, \texttt{Proof} block\\
  \ekey{M-h e} & Applies a tactic & Marked SML code must have type `tactic'\\
  \ekey{M-h d} & Stop current interactive proof \\
  \bottomrule
\end{tabular}
  \caption{Most common HOL4-mode keybindings}\label{tbl:keybindings}
\end{table}
%%% Local Variables:
%%% mode: latex
%%% TeX-master: "lassie-tutorial"
%%% End:


% Instructions: you don't need to change anything in the macros, but
% feel free to define new commands as you wish. Starting from the main
% body, change the specs (e.g., your
% name). use \begin{solution} \end{solution} environment to write your
% solutions. Don't forget to list your collaborators.

\documentclass[12pt,answers]{exam}
%============Macros==================%
\usepackage{amsmath,amsfonts,amssymb,amsthm}
\usepackage[margin=1in]{geometry}
%--------------Cosmetic----------------%
\usepackage{mathtools}
\usepackage{hyperref}
\usepackage{fullpage}
\usepackage{microtype}
\usepackage{xspace}
\usepackage[svgnames]{xcolor}
\usepackage[sc]{mathpazo}
\usepackage{enumitem}
\setlist[enumerate]{itemsep=1pt,topsep=2pt}
\setlist[itemize]{itemsep=1pt,topsep=2pt}
%----------Header--------------------%
\def\course{CS 584/684 Algorithm Design and Analysis}
\def\term{Portland State U, Winter 2021}
\def\prof{Lecturer: Fang Song}
\newcommand{\handout}[5]{
   \renewcommand{\thepage}{\arabic{page}}
   \begin{center}
   \framebox{
      \vbox{
    \hbox to 5.78in { \hfill \large{\course} \hfill }
       \vspace{2mm}
       \hbox to 5.78in { {\Large \hfill \textbf{#5}  \hfill} }
       \vspace{2mm}
       \hbox to 5.78in { \term \hfill \emph{#2}}
       \hbox to 5.78in { {#3 \hfill \emph{#4}}}
      }
   }
   \end{center}
   \vspace*{4mm}
}
\newcommand{\hw}[4]{\handout{#1}{#2}{#3}{#4}{Homework #1}}

%-----defs and commands-----%
\def\veps{\varepsilon}
\newcommand{\bit}{\{0,1\}}
\newcommand{\negl}{\text{negl}}
\newcommand{\corr}[1]{{\color{blue}{#1}}}
\newcommand{\alg}[1]{\textsf{#1}}
%=======Main document==============%
\begin{document}

%----Specs: change accordingly-----%
\newif\ifstudent % comment out false
\studenttrue
% \studentfalse

\def\texbp{5} % bonus for typing in latex
\def\hwnum{7} %
\def\issuedate{03/02/21} %
\def\duedate{03/11/21} %
\def\yourname{your name} % type your name here

%------------------------------%
\ifstudent
\hw{\hwnum}{\issuedate}{Student: \yourname}{Due: \duedate}%
\else
\hw{\hwnum}{\issuedate}{\prof}{Due: \duedate}%
\fi

\noindent \textbf{Instructions.} This problem set contains \numpages\
pages (including this cover page) and \numquestions\ questions. A
random subset of problems will be graded.

\begin{itemize}
\item Your solutions will be graded on \emph{correctness} and
  \emph{clarity}. You should only submit work that you believe to be
  correct, and you will get significantly more partial credit if you
  clearly identify the gap(s) in your solution. It is good practice to
  start any long solution with an informal (but accurate) summary that
  describes the main idea. You may opt for the ``I take 15\%'' option.

\item You need to submit a PDF file before the deadline. Either a
  clear scan of you handwriting or a typeset document is accepted. You
  will get $\texbp$ bonus points for typing in LaTeX (Download and use
  the accompany TeX file).

\item You may collaborate with others on this problem set. However,
  you must \textbf{{write up your own solutions}} and \textbf{{list
      your collaborators and any external sources}} for each
  problem. Be ready to explain your solutions orally to a course staff
  if asked.

\item For problems that require you to provide an algorithm, you must
  give a precise description of the algorithm, together with a proof
  of correctness and an analysis of its running time. You may use
  algorithms from class as subroutines. You may also use any facts
  that we proved in class or from the book.

\item \textbf{If you describe a Greedy algorithm, you will get no
    credit without a formal proof of correctness, even if your
    algorithm is correct.}

\item A proof of NP-completeness should include two parts: 1) the
    problem is in NP; and 2) the problem is NP-hard.

  \end{itemize}

  \paragraph{Exercises. Do not turn in.}
\begin{questions}
  \question A clique in an undirected graph $G =(V, E)$ is a subset
  $V' \subseteq V$ of vertices, each pair of which is connected by an
  edge in $E$. (In other words, a clique is a \emph{complete} subgraph
  of $G$. The size of a clique is the number of vertices it
  contains. The Clique problems asks to decide whether a clique of a
  given size $k$ exists in the graph. Show that Clique is NP-complete.
\end{questions}
\newpage
\paragraph{Problems to turn in.}

\begin{questions}

\question (Demand) Suppose instead of capacities, we consider networks
  where each edge $u\to v$ has a non-negative \emph{demand}
  $d(u\to v)$. Now an $(s,t)$-flow $f$ is \emph{feasible} if and only
  if $f(u\to v)\ge d(u\to v)$ for every edge $u\to v$. (Feasible flow
  values can now be arbitrarily large.) A natural problem in this
  setting is to find a feasible $(s,t)$-flow of \emph{minimum} value.

  \begin{parts}
  \part[10] Describe an efficient algorithm to compute a feasible
    $(s,t)$-flow, given the graph, the demand function, and vertices
    $s$ and $t$ as input. (Hint: find a flow that is non-zero
    everywhere, and then scale it up to make it feasible.)

  \part[10] Suppose you have access to a subroutine \alg{MaxFlow} that
    computes \emph{maximum} flows in networks with edge
    capacities. Describe an efficient algorithm to compute a
    \emph{minimum} flow in a given network with edge demands; your
    algorithm should call \alg{MaxFlow} exactly once.

  \part[10] State and prove an analogue of the max-flow min-cut theorem
    for this setting. (Do minimum flows correspond to maximum cuts?)
  \end{parts}

  \newpage

  \question (Integer linear programming) An \emph{integer
    linear-programming} problem is a linear-programming problem with
  the additional constraint that the variables $x$ must take on
  \emph{integral} values.  Specifically, given an integer $m\times n$
  matrix $A$, an integer vector $b$ of dimension $m$ and integer
  vector $c$ of dimension $n$, we want to maximize $c^T\cdot x$ under
  the constraints $Ax\le b$ and $x\ge 0$.

  \begin{parts}
    \part[7] Show that \emph{weak duality} (CLRS Lemma 29.8) holds for an
    integer linear program.
    \part[8] Show that \emph{strong duality} (CLRS Theorem 29.10) does
    not always hold for an integer linear program.
\newpage
    \bonuspart[10] Given a primal linear program in standard form, let us
    define $P$ to be the optimal objective value for the primal linear
    program, $D$ to be the optimal objective value for its dual, $IP$
    to be the optimal objective value for the integer version of the
    primal (that is, the primal with the added constraint that the
    variables take on integer values), and $ID$ to be the optimal
    objective value for the integer version of the dual. Assuming that
    both the primal integer program and the dual integer program are
    feasible and bounded, show that
    \[ IP\le P = D \le ID \, .\]

    \newpage

  \part[10] Consider further \emph{0-1 integer programming}, where one
  needs to decide if there exists an integer vector $x$ of dimension
  $n$ with elements in the set \bit such that $Ax \le b$. Prove that
  0-1 integer programming is NP-complete.
  \end{parts}
  \newpage

  \question (Randomized and approximate algorithms)

  \begin{parts}

  \part[10] (Hat-check) Each of $n$ customers gives a hat to a hat-check
  person at a restaurant. The hat-check person gives the hats back to
  the customers in a uniformly random order. What is the expected
  number of customers who get back their own hat?
  \newpage

  \part[10] (3-Coloring) Suppose you are given a graph $G=(V,E)$, and we
  want to color each node with one of three colors, even if we aren't
  necessarily able to give different colors to every pair of adjacent
  nodes. We say an edge $(u,v)$ is satisfied if the colors assigned to
  $u$ and $v$ are different.

  Consider a coloring scheme that \emph{maximizes} the number of
  satisfied edges, and let $c^*$ denote this number. Give a poly-time
  algorithm that produces a coloring that satisfies at least
  $\frac 2 3 c^*$ edges. If you want to use an randomized algorithm,
  the \emph{expected} number of edges it satisfies should be at least
  $\frac 2 3 c^*$.

  \newpage

  \part[10] You have received a present containing $n$ pieces of candy
  with weights $W[1,\ldots,n]$ (in ounces). You want to load the candy
  into as many boxes as possible, so that each box contains \emph{at
    least} $L$ ounces of candy. Describe an efficient
  $2$-approximation algorithm for this problem. (Hint: First consider
  the case where every piece of candy weighs less than $L$ ounces.)
  \end{parts}

\end{questions}
\end{document}