+++ /dev/null
-{
-\newcommand{\ImplPath}{\ProjRootPrefix/impl/dijkstra}
-
-\TopicHeader{Weighted Shortest Path: Dijkstra's Agorithm}
-
- Dijkstra's algorithm is useful for computing the shortest path between nodes in a graph where edges have nonnegative weights.
- The worst-case performance of the algorithm is $O(E + V \log V)$, if the graph has $E$ edges and $V$ nodes.
-
- All implementations here provide as output, for each node, the following information:
- \begin{itemize}
- \item whether the node was reachable from the search root
- \item the shortest distance between the root and the node
- \item the previous node along the shortest path, i.e. the node that was being explored when this node was first found
- \end{itemize}
- Note that the path of nodes from the root to any reachable node can be computed by following the previous-node links iteratively until the search root is reached.
-
- The implementations use directed graphs, so in order to emulate searching an undirected graph, two directed edges going in opposite directions should be made for each edge in the desired undirected graph.
-
- \TopicSubHeader{Python 3}
-
- \inputminted{python}{\ImplPath/python3/dijkstra.py}
-
- The \texttt{Node} constructor accepts a \texttt{data} parameter.
- Any value can be passed in, and it will be stored in the node to facilitate associating the nodes with data specific to the problem.
- Here's a usage example:
-
- \inputminted{python}{\ImplPath/python3/usage.py}
-
- In this implementation, two dictionaries are returned which provide the distance and previous-node information.
- To see if a node is reachable, simply check whether the node is a key in the first (distance) dictionary.
-
- \TopicSubHeader{Java}
-
- \centerline{\texttt{Graph.java}}
- \inputminted{java}{\ImplPath/java/Graph.java}
-
- The code may be used as follows.
- If you need to attach some data to the nodes, you could add a field to the \texttt{Node} class or use a \texttt{Map} to keep track of the association.
-
- \inputminted{java}{\ImplPath/java/Usage.java}
-
- The visited flag, previous-node links, and distances are accessible as fields on the \texttt{Node} objects after the search.
-}
+++ /dev/null
-{
-\newcommand{\ImplPath}{\ProjRootPrefix/impl/hull-2d}
-
-\TopicHeader{Convex Hull (2D)}
-
- Convex hull algorithms are given a collection of points and output a subset of just the outermost points.
- Formally, the output is the points of a convex polygon within which lie all points in the input.
-
- The implementations here use a technique called Graham scan to compute the convex hull with time complexity $O(n \log n)$, where $n$ is the number of input points.
- The output points are ordered: the bottom-most (and then leftmost) point appears first, then the remaining hull points in counterclockwise order.
-
- \TopicSubHeader{Java}
-
- \centerline{\texttt{Hull.java}}
- \inputminted{java}{\ImplPath/java/Hull.java}
-
- This may be used as follows:
-
- \inputminted{java}{\ImplPath/java/Usage.java}
-
- \TopicSubHeader{Python 3}
-
- \inputminted{python}{\ImplPath/python3/hull.py}
-
- To run the algorithm, pass a set of $(x, y)$ pairs (\texttt{tuple}s) to \texttt{hull}.
-
- A sample usage:
-
- \inputminted{python}{\ImplPath/python3/usage.py}
-}
+++ /dev/null
-{
-\newcommand{\ImplPath}{\ProjRootPrefix/impl/network-flows}
-
-\TopicHeader{Network Flows: Edmonds--Karp}
-
- Network flows algorithms find the maximum flow through and the minimum cut of a flow network.
- This terminology and relevant algorithms are covered in Algorithms (CS 280) at Oberlin.
-
- The implementations here use the Edmonds--Karp algorithm on flow networks with integer capacities.
- The time complexity of the algorithm is $O\left( VE^2 \right)$, where $V$ and $E$ are the number of vertices and edges in the network, respectively.
-
- \TopicSubHeader{Java}
-
- Coming soon...
-
- \TopicSubHeader{Python 3}
-
- \inputminted{python}{\ImplPath/python3/flows.py}
-
- To use the code:
-
- \inputminted{python}{\ImplPath/python3/usage.py}
-
- The \texttt{cut\_src} variable here is a set of node objects representing the source side of the minimum cut.
- If the set \texttt{n} contains all nodes in the network, the sink side is computed by \texttt{n - cut\_src}.
-}
config = {
# whether to insert page breaks before reference items to ease lookup
- 'RefPageBrk': False,
+ 'RefPageBrk': True,
# languages for which to exclude implementations (java, python3) (to be implemented)
'exclude_langs': [],
}
Contributors
- - Jakob Cornell, creator: LaTeX code, build system, Python/Java algorithm implementations
+ - Jakob Cornell, creator: LaTeX code, build system, Python/Java implementations
Attribution
- - Dijkstra's Algorithm implementations and complexity analysis based on content here: https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm (reused under CC-BY-SA)
- - convex hull implementation and complexity analysis based on content here: https://en.wikipedia.org/wiki/Graham_scan (reused under CC-BY-SA)
+ - Dijkstra's Algorithm implementations and complexity analysis based on content here:
+ https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm (reused under CC-BY-SA)
+ - convex hull implementation and complexity analysis based on content here:
+ https://en.wikipedia.org/wiki/Graham_scan (reused under CC-BY-SA)
+ - Kruskal's Algorithm implementations and complexity analysis based on content here:
+ https://en.wikipedia.org/wiki/Kruskal's_algorithm (reused under CC-BY-SA)
+ - Disjoint sets implementation and complexity analysis based on content here:
+ https://en.wikipedia.org/wiki/Disjoint-set_data_structure (reused under CC-BY-SA)
\usepackage{parskip} % replaces paragraph indentation with vertical space
\usepackage{float} % used for captions on code listings
\usepackage{ifthen} % for conditional compilation
+\usepackage[toc,page]{appendix}
\newcommand{\ProjRootPrefix}{..}
\newcommand{\TopicHeader}{\subsection}
\newcommand{\TopicSubHeader}{\subsubsection}
+% values used in includes
+\newcommand{\ImplPath}{\ProjRootPrefix/impl/algorithms}
+\newcommand{\DsImplPath}{\ProjRootPrefix/impl/structures}
+
\newcommand{\RefBreak}{
\ifthenelse{\boolean{RefPageBrk}}{\pagebreak}{}
}
\pagebreak
\section{Algorithms}
- \input{\ProjRootPrefix/algorithms/dijkstra/topic.tex}
+ \input{\ProjRootPrefix/latex/algorithms/dijkstra.tex}
+ \RefBreak
+ \input{\ProjRootPrefix/latex/algorithms/hull-2d.tex}
\RefBreak
- \input{\ProjRootPrefix/algorithms/hull-2d/topic.tex}
+ \input{\ProjRootPrefix/latex/algorithms/network-flows.tex}
+ \RefBreak
+ \input{\ProjRootPrefix/latex/algorithms/kruskals.tex}
+
\RefBreak
- \input{\ProjRootPrefix/algorithms/network-flows/topic.tex}
+ \begin{appendix}
+ \section{Data Structures}
+ \input{\ProjRootPrefix/latex/structures/disjoint-sets.tex}
+ \end{appendix}
\end{document}
--- /dev/null
+import java.util.*;
+
+public class Graph {
+ public Set<Node> nodes = new HashSet<>();
+
+ // only needed for repeated search
+ public void reset() {
+ for (Node n : nodes) {
+ n.seen = false;
+ n.dist = Long.MAX_VALUE;
+ n.prev = null;
+ }
+ }
+
+ public void search(Node root) {
+ Set<Node> q = new HashSet<>();
+ root.dist = 0l;
+ q.add(root);
+
+ while (!q.isEmpty()) {
+ Node a = Collections.min(q);
+ q.remove(a);
+ if (a.seen) continue;
+ a.seen = true;
+ for (Node b : a.adj.keySet()) {
+ long d = a.dist + a.adj.get(b);
+ if (d < b.dist) {
+ b.dist = d;
+ b.prev = a;
+ q.add(b);
+ }
+ }
+ }
+ }
+}
+
+class Node implements Comparable<Node> {
+ public boolean seen;
+ public long dist = Long.MAX_VALUE;
+ public Node prev;
+ public Map<Node, Long> adj = new HashMap<>();
+
+ public boolean equals(Object o) {
+ return dist == ((Node) o).dist;
+ }
+
+ public int compareTo(Node n) {
+ return Long.compare(dist, n.dist);
+ }
+}
--- /dev/null
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Collections;
+
+public class Test {
+ public static void main(String[] args) {
+ Graph graph = new Graph();
+ Node a = new Node();
+ Node b = new Node();
+ Node c = new Node();
+ Node d = new Node();
+ Node e = new Node();
+ Node f = new Node();
+
+ Collections.addAll(graph.nodes, new Node[] {a,b,c,d,e,f});
+
+ a.adj.put(c, 6l);
+ b.adj.put(a, 9l);
+ c.adj.put(a, 6l);
+ d.adj.put(b, 2l);
+ d.adj.put(c, 11l);
+ e.adj.put(b, 14l);
+ e.adj.put(d, 9l);
+ e.adj.put(f, 7l);
+ f.adj.put(c, 15l);
+ f.adj.put(d, 10l);
+
+ graph.search(e);
+
+ Map<Node, Long> actDists = new HashMap<>();
+ for (Node n : graph.nodes) {
+ actDists.put(n, n.dist);
+ }
+ Map<Node, Long> expDists = new HashMap<>();
+ expDists.put(a, 20l);
+ expDists.put(b, 11l);
+ expDists.put(c, 20l);
+ expDists.put(d, 9l);
+ expDists.put(e, 0l);
+ expDists.put(f, 7l);
+ assert actDists.equals(expDists);
+
+ Map<Node, Node> actPrevs = new HashMap<>();
+ for (Node n : graph.nodes) {
+ actPrevs.put(n, n.prev);
+ }
+ Map<Node, Node> expPrevs = new HashMap<>();
+ expPrevs.put(a, b);
+ expPrevs.put(b, d);
+ expPrevs.put(c, d);
+ expPrevs.put(d, e);
+ expPrevs.put(e, null);
+ expPrevs.put(f, e);
+ assert actPrevs.equals(expPrevs);
+
+ System.out.println("Pass");
+ }
+}
--- /dev/null
+import java.util.Collections;
+
+public class Usage {
+ public static void main(String[] args) {
+ Graph graph = new Graph();
+ Node a = new Node();
+ Node b = new Node();
+ Node c = new Node();
+
+ // add nodes to graph
+ graph.nodes.add(a);
+ graph.nodes.add(b);
+ graph.nodes.add(c);
+
+ a.adj.put(b, 5l); // make an edge from `a' to `b' of weight 5
+ b.adj.put(c, 3l);
+ c.adj.put(a, 10l);
+ c.adj.put(b, 0l);
+
+ // search the whole graph, starting at node `a'
+ graph.search(a);
+
+ System.out.println(b.dist);
+ System.out.println(c.prev == b);
+
+ // be sure to reset the graph before performing another search
+ graph.reset();
+ }
+}
--- /dev/null
+.PHONY: test
+
+SOURCES := Graph.java Test.java Usage.java
+
+test: output/Test.class
+ @java -enableassertions -cp output Test
+
+output/Test.class: $(SOURCES)
+ @mkdir -p output
+ javac -d output Test.java Usage.java
--- /dev/null
+import math
+
+class Node:
+ def __init__(s, data = None):
+ s.data = data
+ s.adj = {}
+
+ def search(s):
+ dists = {s: 0}
+ prev = {}
+ expl = set()
+ q = {s}
+ while q:
+ a = min(q, key = lambda n: dists.get(n, math.inf))
+ q.remove(a)
+ if a not in expl:
+ expl.add(a)
+ for n in a.adj:
+ d = dists[a] + a.adj[n]
+ if d < dists.get(n, math.inf):
+ dists[n] = d
+ prev[n] = a
+ q.add(n)
+ return (dists, prev)
--- /dev/null
+.PHONY: test
+
+SOURCES := dijkstra.py test.py
+
+test: $(SOURCES)
+ @python3 test.py
--- /dev/null
+from dijkstra import *
+
+a, b, c, d, e, f = [Node() for _ in range(6)]
+
+a.adj[c] = 6
+b.adj[a] = 9
+c.adj[a] = 6
+d.adj[b] = 2
+d.adj[c] = 11
+e.adj[b] = 14
+e.adj[d] = 9
+e.adj[f] = 7
+f.adj[c] = 15
+f.adj[d] = 10
+
+(dists, prev) = e.search()
+
+assert dists == {
+ a: 20,
+ b: 11,
+ c: 20,
+ d: 9,
+ e: 0,
+ f: 7,
+}
+
+assert prev == {
+ a: b,
+ b: d,
+ c: d,
+ d: e,
+ f: e,
+}
+
+print("Pass")
--- /dev/null
+a = Node()
+b = Node('foo bar')
+c = Node()
+d = Node([1, 2, 3])
+e = Node()
+
+a.adj[b] = 5 # make an edge from `a' to `b' with weight 5
+b.adj[c] = 5
+c.adj[d] = 5
+a.adj[d] = 30
+
+(dists, prev) = a.search() # search the graph outward from `a'
+
+assert dists[d] == 15
+assert prev[c] == b
--- /dev/null
+import java.util.*;
+
+public class Hull {
+ static class Point {
+ long x;
+ long y;
+ Point(long x, long y) {
+ this.x = x;
+ this.y = y;
+ }
+ }
+
+ static long dir(Point a, Point b, Point c) {
+ return (b.x - a.x)*(c.y - a.y) - (b.y - a.y)*(c.x - a.x);
+ }
+ static List<Point> hull(Set<Point> pts) {
+ if (pts.isEmpty()) {
+ return new ArrayList<>();
+ }
+ Point s = Collections.min(pts, new Comparator<Point>() {
+ public int compare(Point a, Point b) {
+ int yCmp = Long.compare(a.y, b.y);
+ return yCmp == 0 ? Long.compare(a.x, b.x) : yCmp;
+ }
+ });
+ LinkedList<Point> srt = new LinkedList<>(pts);
+ srt.remove(s);
+ Collections.sort(srt, new Comparator<Point>() {
+ double cos(Point p) {
+ return (p.x - s.x) / Math.sqrt((s.x - p.x)*(s.x - p.x) + (s.y - p.y)*(s.y - p.y));
+ }
+ public int compare(Point a, Point b) {
+ return -Double.compare(cos(a), cos(b));
+ }
+ });
+ srt.addFirst(s);
+ if (srt.size() < 3) {
+ return srt;
+ }
+ LinkedList<Point> out = new LinkedList<>(srt.subList(0, 3));
+ for (Point p : srt.subList(3, srt.size())) {
+ while (dir(out.get(out.size() - 2), out.get(out.size() - 1), p) <= 0) {
+ out.removeLast();
+ }
+ out.add(p);
+ }
+ return out;
+ }
+}
--- /dev/null
+import java.util.*;
+
+public class Test {
+ public static void main(String[] args) {
+ Hull.Point a = new Hull.Point(0, 0);
+ Hull.Point b = new Hull.Point(-2, 2);
+ Hull.Point c = new Hull.Point(0, 4);
+ Hull.Point d = new Hull.Point(2, 2);
+ Hull.Point e = new Hull.Point(0, 2);
+
+ List<Hull.Point> hull = Hull.hull(new HashSet<>(Arrays.asList(new Hull.Point[] {a, b, c, d, e})));
+ assert hull.equals(Arrays.asList(new Hull.Point[] {a, d, c, b}));
+
+ System.out.println("Pass");
+ }
+}
--- /dev/null
+import java.util.*;
+
+public class Usage {
+ public static void main(String[] args) {
+ Hull.Point a = new Hull.Point(0, 3);
+ Hull.Point b = new Hull.Point(-2, 2);
+ Hull.Point c = new Hull.Point(1, -6);
+
+ // construct a set of points for input
+ Set<Hull.Point> points = new HashSet<>(Arrays.asList(new Hull.Point[] {a, b, c}));
+ // or manually:
+ Set<Hull.Point> points2 = new HashSet<>();
+ points2.add(a);
+ points2.add(b);
+ points2.add(c);
+
+ List<Hull.Point> out = Hull.hull(points);
+ System.out.println(out.get(0) == c);
+ System.out.println(out.contains(b));
+ }
+}
--- /dev/null
+.PHONY: test
+
+SOURCES := Hull.java Test.java Usage.java
+
+test: output/Test.class
+ @java -enableassertions -cp output Test
+
+output/Test.class: $(SOURCES)
+ @mkdir -p output
+ javac -d output Test.java Usage.java
--- /dev/null
+import math
+
+def hull(points):
+ if not points:
+ return []
+ sx, sy = s = min(points, key = lambda p: p[::-1])
+ cos = lambda p: (p[0] - sx) / math.sqrt((sx - p[0])**2 + (sy - p[1])**2)
+ dir_ = lambda ax, ay, bx, by, cx, cy: (bx - ax)*(cy - ay) - (by - ay)*(cx - ax)
+ points = [s] + sorted(points - {s}, key = cos, reverse = True)
+ stack = points[:3]
+ for p in points[3:]:
+ while dir_(*stack[-2], *stack[-1], *p) <= 0:
+ stack.pop()
+ stack.append(p)
+ return stack
--- /dev/null
+.PHONY: test
+
+SOURCES := hull.py test.py
+
+test: $(SOURCES)
+ @python3 test.py
--- /dev/null
+from hull import *
+
+points = {(0,0), (-2,2), (0,4), (2,2), (0,2)}
+assert hull(points) == [(0,0), (2,2), (0,4), (-2,2)]
+
+print("Pass")
--- /dev/null
+points = {(0,0), (-2,2), (0,4), (2,2), (0,2)}
+assert hull(points) == [(0,0), (2,2), (0,4), (-2,2)]
--- /dev/null
+def min_span_tree(graph):
+ ds = DisjointSets()
+ for e in graph:
+ for n in e:
+ ds.add(n)
+ out = set()
+ for e in sorted(graph, key = lambda e: graph[e]):
+ a, b = e
+ if ds.find(a) is not ds.find(b):
+ out.add(e)
+ ds.union(a, b)
+ return out
--- /dev/null
+.PHONY: test
+
+PP := ../../../structures/disjoint-sets/python3
+
+SOURCES := kruskals.py test.py $(PP)/disjoint_sets.py
+
+test: $(SOURCES)
+ @PYTHONPATH=$(PP) python3 test.py
--- /dev/null
+from kruskals import min_span_tree
+from disjoint_sets import DisjointSets
+import kruskals
+
+kruskals.DisjointSets = DisjointSets
+
+_graph = {
+ ('a', 'b'): 7,
+ ('a', 'd'): 5,
+ ('b', 'c'): 8,
+ ('b', 'd'): 9,
+ ('b', 'e'): 7,
+ ('c', 'e'): 5,
+ ('d', 'e'): 15,
+ ('d', 'f'): 6,
+ ('e', 'f'): 8,
+ ('e', 'g'): 9,
+ ('f', 'g'): 11,
+}
+graph = {frozenset(k): v for (k, v) in _graph.items()}
+
+exp = set(map(frozenset, [
+ ('a', 'b'),
+ ('a', 'd'),
+ ('b', 'e'),
+ ('c', 'e'),
+ ('d', 'f'),
+ ('e', 'g'),
+]))
+
+assert min_span_tree(graph) == exp
+print("Pass")
--- /dev/null
+graph = {
+ frozenset(["a", "b"]): 10,
+ frozenset(["b", "c"]): 12,
+ frozenset(["a", "c"]): 50,
+}
+
+tree = min_span_tree(graph)
+assert frozenset(["b", "c"]) in tree
+assert frozenset(["a", "c"]) not in tree
+total_cost = sum(graph[e] for e in tree)
--- /dev/null
+import java.util.*;
+
+public class Ek {
+ static class Node {
+ public Set<Edge> edges = new HashSet<>();
+
+ public static Edge connect(Node from, Node to, long cap) {
+ Edge e = new Edge(from, to, cap);
+ from.edges.add(e);
+ to.edges.add(e);
+ return e;
+ }
+ }
+
+ static class Edge {
+ public final Node from, to;
+ public final long cap;
+ public long flow;
+
+ public Edge(Node from, Node to, long cap) {
+ self.from = from;
+ self.to = to;
+ self.cap = cap;
+ self.flow = 0;
+ }
+ public long capTo(Edge node) {
+ return node.equals(from) ? flow : cap - flow;
+ }
+ public void addTo(Node to, long amt) {
+ flow += node.equals(from) ? -amt : amt;
+ }
+ public Node other(Node node) {
+ return node.equals(from) ? to : from;
+ }
+ }
+
+ static class EkResult {
+ public long val;
+ public Set<Node> cutSrc;
+ }
+
+ static
+
+ public static EkResult ek(Set<Node> nodes, Node src, Node sink) {
+
+ }
+}
--- /dev/null
+import java.util.*;
+
+class FlowNet {
+ Map<Node, Boolean> nodes = new HashMap<>();
+ long value;
+ Node source, sink;
+
+ void addNode(Node n) {
+ nodes.put(n, false);
+ }
+
+ Edge edgeFor(Node one, Node two) {
+ return one.edgeTo.containsKey(two) ? one.edgeTo.get(two) : two.edgeTo.get(one);
+ }
+
+ long findMax() {
+ long value = 0;
+
+ AP augPath = getPath();
+ while (augPath != null) {
+ Node from, to = augPath.path.pop();
+ while (!augPath.path.isEmpty()) {
+ from = to;
+ to = augPath.path.pop();
+ edgeFor(from, to).sendTo(to, augPath.value);
+ }
+ value += augPath.value;
+ augPath = getPath();
+ }
+
+ return value;
+ }
+
+ // for min cut
+ Set<Node> srcSide() {
+ Set<Node> srcSide = new HashSet<>();
+ for (Node n : nodes.keySet()) {
+ if (nodes.get(n)) {
+ srcSide.add(n);
+ }
+ }
+ return srcSide;
+ }
+
+ AP getPath() {
+ Map<Node, Node> prev = new HashMap<>();
+ Queue<Node> q = new LinkedList<>();
+ q.offer(source);
+ prev.put(source, null);
+ while (!q.isEmpty()) {
+ Node n = q.poll();
+ for (Node other : n.adj) {
+ Edge e = edgeFor(n, other);
+ if (e.capTo(other) > 0 && !prev.containsKey(other)) {
+ prev.put(other, n);
+ q.offer(other);
+ }
+ }
+ }
+ for (Node n : nodes.keySet()) {
+ nodes.put(n, prev.keySet().contains(n));
+ }
+
+ if (nodes.get(sink)) {
+ LinkedList<Node> path = new LinkedList<>();
+ long value = Long.MAX_VALUE;
+
+ Node n = sink, p = prev.get(n);
+ path.push(n);
+ while (p != null) {
+ value = Math.min(value, edgeFor(p, n).capTo(n));
+ path.push(p);
+ n = p;
+ p = prev.get(p);
+ }
+
+ AP ap = new AP();
+ ap.path = path;
+ ap.value = value;
+ return ap;
+ } else {
+ return null;
+ }
+ }
+
+ class AP {
+ LinkedList<Node> path;
+ long value;
+ }
+}
+
+class Node {
+ Set<Node> adj = new HashSet<>();
+ Map<Node, Edge> edgeTo = new HashMap<>();
+}
+
+class Edge {
+ Node from, to;
+ long cap, flow;
+
+ Edge(Node f, Node t, long c) {
+ from = f;
+ to = t;
+ cap = c;
+ f.adj.add(t);
+ t.adj.add(f);
+ f.edgeTo.put(t, this);
+ }
+
+ long capTo(Node n) {
+ return n == to ? cap - flow : flow;
+ }
+
+ void sendTo(Node n, long amt) {
+ flow += n == to ? amt : -amt;
+ }
+}
--- /dev/null
+public class Main {
+ public static void main(String[] args) {
+ FlowNet net = new FlowNet();
+ Node s = new Node();
+ Node t = new Node();
+ Node u = new Node();
+ Node v = new Node();
+ for (Node n : new Node[] {s,t,u,v}) {
+ net.addNode(n);
+ }
+ net.source = s;
+ net.sink = t;
+ new Edge(s, u, 20);
+ new Edge(s, v, 10);
+ new Edge(u, v, 30);
+ new Edge(u, t, 10);
+ new Edge(v, t, 20);
+ System.out.println(net.findMax());
+ for (Node n : net.srcSide()) {
+ char name = 0;
+ if (n.equals(s)) name = 's';
+ if (n.equals(t)) name = 't';
+ if (n.equals(u)) name = 'u';
+ if (n.equals(v)) name = 'v';
+ System.out.println(name);
+ }
+ }
+}
--- /dev/null
+Usage:
+
+ FlowNet net = new FlowNet();
+
+ Node s = new Node();
+ Node t = new Node();
+ Node u = new Node();
+ Node v = new Node();
+ for (Node n : new Node[] {s,t,u,v}) {
+ net.addNode(n);
+ }
+ net.source = s;
+ net.sink = t;
+
+ new Edge(s, u, 20);
+ new Edge(s, v, 10);
+ new Edge(u, v, 30);
+ new Edge(u, t, 10);
+ new Edge(v, t, 20);
+
+ System.out.println(net.findMax());
+ System.out.println(net.srcSide());
--- /dev/null
+from collections import deque
+
+class Edge:
+ def __init__(s, frm, to, cap):
+ s.frm = frm
+ s.to = to
+ s.cap = cap
+ s.flow = 0
+
+ def cap_to(s, node):
+ return {s.frm: s.flow, s.to: s.cap - s.flow}[node]
+
+ def add_to(s, node, amt):
+ s.flow += {s.frm: -amt, s.to: amt}[node]
+
+ def other(s, node):
+ return {s.frm: s.to, s.to: s.frm}[node]
+
+class Node:
+ def __init__(s, data = None):
+ s.data = data
+ s.edges = set()
+
+ @staticmethod
+ def connect(f, t, cap):
+ e = Edge(f, t, cap)
+ f.edges.add(e)
+ t.edges.add(e)
+ return e
+
+def ek(nodes, src, sink):
+ def path():
+ prev = {}
+ q = deque([src])
+ while q:
+ n = q.popleft()
+ for e in n.edges:
+ o = e.other(n)
+ if o not in prev and e.cap_to(o) > 0:
+ prev[o] = e
+ q.append(o)
+ d = sink
+ p = deque()
+ while d in prev and d is not src:
+ p.appendleft((prev[d], d))
+ d = prev[d].other(d)
+ return p if d is src else []
+
+ p = path()
+ val = 0
+ while p:
+ amt = min(e.cap_to(d) for e, d in p)
+ val += amt
+ for e, d in p:
+ e.add_to(d, amt)
+ p = path()
+
+ cut_src = set()
+ w = [src]
+ while w:
+ n = w.pop()
+ cut_src.add(n)
+ for e in n.edges:
+ o = e.other(n)
+ if o not in cut_src and e.cap_to(o) != 0:
+ w.append(o)
+
+ return (val, cut_src)
--- /dev/null
+.PHONY: test
+
+SOURCES := flows.py test.py
+
+test: $(SOURCES)
+ @python3 test.py
--- /dev/null
+from flows import *
+
+src, a, b, c, d, e, f, sink = nodes = [Node() for _ in range(8)]
+
+src_to_a = Node.connect(src, a, 4)
+Node.connect(src, b, 4)
+Node.connect(src, c, 2)
+Node.connect(a, d, 3)
+Node.connect(b, e, 3)
+Node.connect(c, e, 3)
+Node.connect(c, f, 1)
+Node.connect(d, sink, 4)
+Node.connect(e, sink, 5)
+Node.connect(f, sink, 5)
+
+val, cut_src = ek(nodes, src, sink)
+
+assert val == 8
+assert src_to_a.cap_to(src) == 3
+
+print("Pass")
--- /dev/null
+# pass a value here to store data in the node (optional)
+src = Node('foo')
+a = Node('bar')
+b = Node()
+sink = Node(1234)
+
+Node.edge(src, a, 5) # add an edge from `src' to `a' with capacity 5
+Node.edge(a, b, 3)
+edge = Node.edge(b, sink, 6) # can capture an edge object for later use
+
+val, cut_src = ek(nodes, src, sink)
+
+assert val == 3 # flow value
+
+print(edge.cap_to(b)) # amount of flow from `b' to `sink' in the saturated network
+++ /dev/null
-import java.util.*;
-
-public class Graph {
- public Set<Node> nodes = new HashSet<>();
-
- // only needed for repeated search
- public void reset() {
- for (Node n : nodes) {
- n.seen = false;
- n.dist = Long.MAX_VALUE;
- n.prev = null;
- }
- }
-
- public void search(Node root) {
- Set<Node> q = new HashSet<>();
- root.dist = 0l;
- q.add(root);
-
- while (!q.isEmpty()) {
- Node a = Collections.min(q);
- q.remove(a);
- if (a.seen) continue;
- a.seen = true;
- for (Node b : a.adj.keySet()) {
- long d = a.dist + a.adj.get(b);
- if (d < b.dist) {
- b.dist = d;
- b.prev = a;
- q.add(b);
- }
- }
- }
- }
-}
-
-class Node implements Comparable<Node> {
- public boolean seen;
- public long dist = Long.MAX_VALUE;
- public Node prev;
- public Map<Node, Long> adj = new HashMap<>();
-
- public boolean equals(Object o) {
- return dist == ((Node) o).dist;
- }
-
- public int compareTo(Node n) {
- return Long.compare(dist, n.dist);
- }
-}
+++ /dev/null
-import java.util.Map;
-import java.util.HashMap;
-import java.util.Collections;
-
-public class Test {
- public static void main(String[] args) {
- Graph graph = new Graph();
- Node a = new Node();
- Node b = new Node();
- Node c = new Node();
- Node d = new Node();
- Node e = new Node();
- Node f = new Node();
-
- Collections.addAll(graph.nodes, new Node[] {a,b,c,d,e,f});
-
- a.adj.put(c, 6l);
- b.adj.put(a, 9l);
- c.adj.put(a, 6l);
- d.adj.put(b, 2l);
- d.adj.put(c, 11l);
- e.adj.put(b, 14l);
- e.adj.put(d, 9l);
- e.adj.put(f, 7l);
- f.adj.put(c, 15l);
- f.adj.put(d, 10l);
-
- graph.search(e);
-
- Map<Node, Long> actDists = new HashMap<>();
- for (Node n : graph.nodes) {
- actDists.put(n, n.dist);
- }
- Map<Node, Long> expDists = new HashMap<>();
- expDists.put(a, 20l);
- expDists.put(b, 11l);
- expDists.put(c, 20l);
- expDists.put(d, 9l);
- expDists.put(e, 0l);
- expDists.put(f, 7l);
- assert actDists.equals(expDists);
-
- Map<Node, Node> actPrevs = new HashMap<>();
- for (Node n : graph.nodes) {
- actPrevs.put(n, n.prev);
- }
- Map<Node, Node> expPrevs = new HashMap<>();
- expPrevs.put(a, b);
- expPrevs.put(b, d);
- expPrevs.put(c, d);
- expPrevs.put(d, e);
- expPrevs.put(e, null);
- expPrevs.put(f, e);
- assert actPrevs.equals(expPrevs);
-
- System.out.println("Pass");
- }
-}
+++ /dev/null
-import java.util.Collections;
-
-public class Usage {
- public static void main(String[] args) {
- Graph graph = new Graph();
- Node a = new Node();
- Node b = new Node();
- Node c = new Node();
-
- // add nodes to graph
- graph.nodes.add(a);
- graph.nodes.add(b);
- graph.nodes.add(c);
-
- a.adj.put(b, 5l); // make an edge from `a' to `b' of weight 5
- b.adj.put(c, 3l);
- c.adj.put(a, 10l);
- c.adj.put(b, 0l);
-
- // search the whole graph, starting at node `a'
- graph.search(a);
-
- System.out.println(b.dist);
- System.out.println(c.prev == b);
-
- // be sure to reset the graph before performing another search
- graph.reset();
- }
-}
+++ /dev/null
-.PHONY: test
-
-SOURCES := Graph.java Test.java Usage.java
-
-test: output/Test.class
- @java -enableassertions -cp output Test
-
-output/Test.class: $(SOURCES)
- @mkdir -p output
- javac -d output Test.java Usage.java
+++ /dev/null
-import math
-
-class Node:
- def __init__(s, data = None):
- s.data = data
- s.adj = {}
-
- def search(s):
- dists = {s: 0}
- prev = {}
- expl = set()
- q = {s}
- while q:
- a = min(q, key = lambda n: dists.get(n, math.inf))
- q.remove(a)
- if a not in expl:
- expl.add(a)
- for n in a.adj:
- d = dists[a] + a.adj[n]
- if d < dists.get(n, math.inf):
- dists[n] = d
- prev[n] = a
- q.add(n)
- return (dists, prev)
+++ /dev/null
-.PHONY: test
-
-SOURCES := dijkstra.py test.py
-
-test: $(SOURCES)
- @python3 test.py
+++ /dev/null
-from dijkstra import *
-
-a, b, c, d, e, f = [Node() for _ in range(6)]
-
-a.adj[c] = 6
-b.adj[a] = 9
-c.adj[a] = 6
-d.adj[b] = 2
-d.adj[c] = 11
-e.adj[b] = 14
-e.adj[d] = 9
-e.adj[f] = 7
-f.adj[c] = 15
-f.adj[d] = 10
-
-(dists, prev) = e.search()
-
-assert dists == {
- a: 20,
- b: 11,
- c: 20,
- d: 9,
- e: 0,
- f: 7,
-}
-
-assert prev == {
- a: b,
- b: d,
- c: d,
- d: e,
- f: e,
-}
-
-print("Pass")
+++ /dev/null
-a = Node()
-b = Node('foo bar')
-c = Node()
-d = Node([1, 2, 3])
-e = Node()
-
-a.adj[b] = 5 # make an edge from `a' to `b' with weight 5
-b.adj[c] = 5
-c.adj[d] = 5
-a.adj[d] = 30
-
-(dists, prev) = a.search() # search the graph outward from `a'
-
-assert dists[d] == 15
-assert prev[c] == b
+++ /dev/null
-import java.util.*;
-
-public class Hull {
- static class Point {
- long x;
- long y;
- Point(long x, long y) {
- this.x = x;
- this.y = y;
- }
- }
-
- static long dir(Point a, Point b, Point c) {
- return (b.x - a.x)*(c.y - a.y) - (b.y - a.y)*(c.x - a.x);
- }
- static List<Point> hull(Set<Point> pts) {
- if (pts.isEmpty()) {
- return new ArrayList<>();
- }
- Point s = Collections.min(pts, new Comparator<Point>() {
- public int compare(Point a, Point b) {
- int yCmp = Long.compare(a.y, b.y);
- return yCmp == 0 ? Long.compare(a.x, b.x) : yCmp;
- }
- });
- LinkedList<Point> srt = new LinkedList<>(pts);
- srt.remove(s);
- Collections.sort(srt, new Comparator<Point>() {
- double cos(Point p) {
- return (p.x - s.x) / Math.sqrt((s.x - p.x)*(s.x - p.x) + (s.y - p.y)*(s.y - p.y));
- }
- public int compare(Point a, Point b) {
- return -Double.compare(cos(a), cos(b));
- }
- });
- srt.addFirst(s);
- if (srt.size() < 3) {
- return srt;
- }
- LinkedList<Point> out = new LinkedList<>(srt.subList(0, 3));
- for (Point p : srt.subList(3, srt.size())) {
- while (dir(out.get(out.size() - 2), out.get(out.size() - 1), p) <= 0) {
- out.removeLast();
- }
- out.add(p);
- }
- return out;
- }
-}
+++ /dev/null
-import java.util.*;
-
-public class Test {
- public static void main(String[] args) {
- Hull.Point a = new Hull.Point(0, 0);
- Hull.Point b = new Hull.Point(-2, 2);
- Hull.Point c = new Hull.Point(0, 4);
- Hull.Point d = new Hull.Point(2, 2);
- Hull.Point e = new Hull.Point(0, 2);
-
- List<Hull.Point> hull = Hull.hull(new HashSet<>(Arrays.asList(new Hull.Point[] {a, b, c, d, e})));
- assert hull.equals(Arrays.asList(new Hull.Point[] {a, d, c, b}));
-
- System.out.println("Pass");
- }
-}
+++ /dev/null
-import java.util.*;
-
-public class Usage {
- public static void main(String[] args) {
- Hull.Point a = new Hull.Point(0, 3);
- Hull.Point b = new Hull.Point(-2, 2);
- Hull.Point c = new Hull.Point(1, -6);
-
- // construct a set of points for input
- Set<Hull.Point> points = new HashSet<>(Arrays.asList(new Hull.Point[] {a, b, c}));
- // or manually:
- Set<Hull.Point> points2 = new HashSet<>();
- points2.add(a);
- points2.add(b);
- points2.add(c);
-
- List<Hull.Point> out = Hull.hull(points);
- System.out.println(out.get(0) == c);
- System.out.println(out.contains(b));
- }
-}
+++ /dev/null
-.PHONY: test
-
-SOURCES := Hull.java Test.java Usage.java
-
-test: output/Test.class
- @java -enableassertions -cp output Test
-
-output/Test.class: $(SOURCES)
- @mkdir -p output
- javac -d output Test.java Usage.java
+++ /dev/null
-import math
-
-def hull(points):
- if not points:
- return []
- sx, sy = s = min(points, key = lambda p: p[::-1])
- cos = lambda p: (p[0] - sx) / math.sqrt((sx - p[0])**2 + (sy - p[1])**2)
- dir_ = lambda ax, ay, bx, by, cx, cy: (bx - ax)*(cy - ay) - (by - ay)*(cx - ax)
- points = [s] + sorted(points - {s}, key = cos, reverse = True)
- stack = points[:3]
- for p in points[3:]:
- while dir_(*stack[-2], *stack[-1], *p) <= 0:
- stack.pop()
- stack.append(p)
- return stack
+++ /dev/null
-.PHONY: test
-
-SOURCES := hull.py test.py
-
-test: $(SOURCES)
- @python3 test.py
+++ /dev/null
-from hull import *
-
-points = {(0,0), (-2,2), (0,4), (2,2), (0,2)}
-assert hull(points) == [(0,0), (2,2), (0,4), (-2,2)]
-
-print("Pass")
+++ /dev/null
-points = {(0,0), (-2,2), (0,4), (2,2), (0,2)}
-assert hull(points) == [(0,0), (2,2), (0,4), (-2,2)]
+++ /dev/null
-import java.util.*;
-
-class FlowNet {
- Map<Node, Boolean> nodes = new HashMap<>();
- long value;
- Node source, sink;
-
- void addNode(Node n) {
- nodes.put(n, false);
- }
-
- Edge edgeFor(Node one, Node two) {
- return one.edgeTo.containsKey(two) ? one.edgeTo.get(two) : two.edgeTo.get(one);
- }
-
- long findMax() {
- long value = 0;
-
- AP augPath = getPath();
- while (augPath != null) {
- Node from, to = augPath.path.pop();
- while (!augPath.path.isEmpty()) {
- from = to;
- to = augPath.path.pop();
- edgeFor(from, to).sendTo(to, augPath.value);
- }
- value += augPath.value;
- augPath = getPath();
- }
-
- return value;
- }
-
- // for min cut
- Set<Node> srcSide() {
- Set<Node> srcSide = new HashSet<>();
- for (Node n : nodes.keySet()) {
- if (nodes.get(n)) {
- srcSide.add(n);
- }
- }
- return srcSide;
- }
-
- AP getPath() {
- Map<Node, Node> prev = new HashMap<>();
- Queue<Node> q = new LinkedList<>();
- q.offer(source);
- prev.put(source, null);
- while (!q.isEmpty()) {
- Node n = q.poll();
- for (Node other : n.adj) {
- Edge e = edgeFor(n, other);
- if (e.capTo(other) > 0 && !prev.containsKey(other)) {
- prev.put(other, n);
- q.offer(other);
- }
- }
- }
- for (Node n : nodes.keySet()) {
- nodes.put(n, prev.keySet().contains(n));
- }
-
- if (nodes.get(sink)) {
- LinkedList<Node> path = new LinkedList<>();
- long value = Long.MAX_VALUE;
-
- Node n = sink, p = prev.get(n);
- path.push(n);
- while (p != null) {
- value = Math.min(value, edgeFor(p, n).capTo(n));
- path.push(p);
- n = p;
- p = prev.get(p);
- }
-
- AP ap = new AP();
- ap.path = path;
- ap.value = value;
- return ap;
- } else {
- return null;
- }
- }
-
- class AP {
- LinkedList<Node> path;
- long value;
- }
-}
-
-class Node {
- Set<Node> adj = new HashSet<>();
- Map<Node, Edge> edgeTo = new HashMap<>();
-}
-
-class Edge {
- Node from, to;
- long cap, flow;
-
- Edge(Node f, Node t, long c) {
- from = f;
- to = t;
- cap = c;
- f.adj.add(t);
- t.adj.add(f);
- f.edgeTo.put(t, this);
- }
-
- long capTo(Node n) {
- return n == to ? cap - flow : flow;
- }
-
- void sendTo(Node n, long amt) {
- flow += n == to ? amt : -amt;
- }
-}
+++ /dev/null
-public class Main {
- public static void main(String[] args) {
- FlowNet net = new FlowNet();
- Node s = new Node();
- Node t = new Node();
- Node u = new Node();
- Node v = new Node();
- for (Node n : new Node[] {s,t,u,v}) {
- net.addNode(n);
- }
- net.source = s;
- net.sink = t;
- new Edge(s, u, 20);
- new Edge(s, v, 10);
- new Edge(u, v, 30);
- new Edge(u, t, 10);
- new Edge(v, t, 20);
- System.out.println(net.findMax());
- for (Node n : net.srcSide()) {
- char name = 0;
- if (n.equals(s)) name = 's';
- if (n.equals(t)) name = 't';
- if (n.equals(u)) name = 'u';
- if (n.equals(v)) name = 'v';
- System.out.println(name);
- }
- }
-}
+++ /dev/null
-Usage:
-
- FlowNet net = new FlowNet();
-
- Node s = new Node();
- Node t = new Node();
- Node u = new Node();
- Node v = new Node();
- for (Node n : new Node[] {s,t,u,v}) {
- net.addNode(n);
- }
- net.source = s;
- net.sink = t;
-
- new Edge(s, u, 20);
- new Edge(s, v, 10);
- new Edge(u, v, 30);
- new Edge(u, t, 10);
- new Edge(v, t, 20);
-
- System.out.println(net.findMax());
- System.out.println(net.srcSide());
+++ /dev/null
-from collections import deque
-
-class Edge:
- def __init__(s, frm, to, cap):
- s.frm = frm
- s.to = to
- s.cap = cap
- s.flow = 0
-
- def cap_to(s, node):
- return {s.frm: s.flow, s.to: s.cap - s.flow}[node]
-
- def add_to(s, node, amt):
- s.flow += {s.frm: -amt, s.to: amt}[node]
-
- def other(s, node):
- return {s.frm: s.to, s.to: s.frm}[node]
-
-class Node:
- def __init__(s, data = None):
- s.data = data
- s.edges = set()
-
- @staticmethod
- def edge(f, t, cap):
- e = Edge(f, t, cap)
- f.edges.add(e)
- t.edges.add(e)
- return e
-
-def ek(nodes, src, sink):
- def path():
- prev = {}
- q = deque([src])
- while q:
- n = q.popleft()
- for e in n.edges:
- o = e.other(n)
- if o not in prev and e.cap_to(o) > 0:
- prev[o] = e
- q.append(o)
- d = sink
- p = deque()
- while d in prev and d is not src:
- p.appendleft((prev[d], d))
- d = prev[d].other(d)
- return p if d is src else []
-
- p = path()
- val = 0
- while p:
- amt = min(e.cap_to(d) for e, d in p)
- val += amt
- for e, d in p:
- e.add_to(d, amt)
- p = path()
-
- cut_src = set()
- w = [src]
- while w:
- n = w.pop()
- cut_src.add(n)
- for e in n.edges:
- o = e.other(n)
- if o not in cut_src and e.cap_to(o) != 0:
- w.append(o)
-
- return (val, cut_src)
+++ /dev/null
-.PHONY: test
-
-SOURCES := flows.py test.py
-
-test: $(SOURCES)
- @python3 test.py
+++ /dev/null
-from flows import *
-
-src, a, b, c, d, e, f, sink = nodes = [Node() for _ in range(8)]
-
-src_to_a = Node.edge(src, a, 4)
-Node.edge(src, b, 4)
-Node.edge(src, c, 2)
-Node.edge(a, d, 3)
-Node.edge(b, e, 3)
-Node.edge(c, e, 3)
-Node.edge(c, f, 1)
-Node.edge(d, sink, 4)
-Node.edge(e, sink, 5)
-Node.edge(f, sink, 5)
-
-val, cut_src = ek(nodes, src, sink)
-
-assert val == 8
-assert src_to_a.cap_to(src) == 3
-
-print("Pass")
+++ /dev/null
-# pass a value here to store data in the node (optional)
-src = Node('foo')
-a = Node('bar')
-b = Node()
-sink = Node(1234)
-
-Node.edge(src, a, 5) # add an edge from `src' to `a' with capacity 5
-Node.edge(a, b, 3)
-edge = Node.edge(b, sink, 6) # can capture an edge object for later use
-
-val, cut_src = ek(nodes, src, sink)
-
-assert val == 3 # flow value
-
-print(edge.cap_to(b)) # amount of flow from `b' to `sink' in the saturated network
--- /dev/null
+class Node:
+ pass
+
+class DisjointSets(dict):
+ def add(self, val):
+ if val not in self:
+ self[val] = n = Node()
+ n.val = val
+ n.parent = None
+ n.size = 1
+
+ def find(self, val):
+ n = self[val]
+ while n.parent:
+ newp = n.parent.parent or n.parent
+ n.parent = newp
+ n = newp
+ return n
+
+ def union(self, v1, v2):
+ r1 = self.find(v1)
+ r2 = self.find(v2)
+ if r1 != r2:
+ ch, p = sorted([r1, r2], key = lambda n: n.size)
+ ch.parent = p
+ p.size += ch.size
--- /dev/null
+# Make a new disjoint sets instance
+ds = DisjointSets()
+
+# Add singleton values to the structure
+ds.add("my value")
+ds.add(0)
+
+# Union and Find operations
+ds.union("my value", 0)
+assert ds.find("my value") is ds.find(0)
--- /dev/null
+{
+ \newcommand{\ThisImpl}{\ImplPath/dijkstra}
+
+ \TopicHeader{Weighted Shortest Path: Dijkstra's Agorithm}
+
+ Dijkstra's algorithm is useful for computing the shortest path between nodes in a graph where edges have nonnegative weights.
+ The worst-case performance of the algorithm is $O(E + V \log V)$, if the graph has $E$ edges and $V$ nodes.
+
+ All implementations here provide as output, for each node, the following information:
+ \begin{itemize}
+ \item whether the node was reachable from the search root
+ \item the shortest distance between the root and the node
+ \item the previous node along the shortest path, i.e. the node that was being explored when this node was first found
+ \end{itemize}
+ Note that the path of nodes from the root to any reachable node can be computed by following the previous-node links iteratively until the search root is reached.
+
+ The implementations use directed graphs, so in order to emulate searching an undirected graph, two directed edges going in opposite directions should be made for each edge in the desired undirected graph.
+
+ \TopicSubHeader{Python 3}
+
+ \inputminted{python}{\ThisImpl/python3/dijkstra.py}
+
+ The \texttt{Node} constructor accepts a \texttt{data} parameter.
+ Any value can be passed in, and it will be stored in the node to facilitate associating the nodes with data specific to the problem.
+ Here's a usage example:
+
+ \inputminted{python}{\ThisImpl/python3/usage.py}
+
+ In this implementation, two dictionaries are returned which provide the distance and previous-node information.
+ To see if a node is reachable, simply check whether the node is a key in the first (distance) dictionary.
+
+ \TopicSubHeader{Java}
+
+ \centerline{\texttt{Graph.java}}
+ \inputminted{java}{\ThisImpl/java/Graph.java}
+
+ The code may be used as follows.
+ If you need to attach some data to the nodes, you could add a field to the \texttt{Node} class or use a \texttt{Map} to keep track of the association.
+
+ \inputminted{java}{\ThisImpl/java/Usage.java}
+
+ The visited flag, previous-node links, and distances are accessible as fields on the \texttt{Node} objects after the search.
+}
--- /dev/null
+{
+ \newcommand{\ThisImpl}{\ImplPath/hull-2d}
+
+ \TopicHeader{Convex Hull (2D)}
+
+ Convex hull algorithms are given a collection of points and output a subset of just the outermost points.
+ Formally, the output is the points of a convex polygon within which lie all points in the input.
+
+ The implementations here use a technique called Graham scan to compute the convex hull with time complexity $O(n \log n)$, where $n$ is the number of input points.
+ The output points are ordered: the bottom-most (and then leftmost) point appears first, then the remaining hull points in counterclockwise order.
+
+ \TopicSubHeader{Java}
+
+ \centerline{\texttt{Hull.java}}
+ \inputminted{java}{\ThisImpl/java/Hull.java}
+
+ This may be used as follows:
+
+ \inputminted{java}{\ThisImpl/java/Usage.java}
+
+ \TopicSubHeader{Python 3}
+
+ \inputminted{python}{\ThisImpl/python3/hull.py}
+
+ To run the algorithm, pass a set of $(x, y)$ pairs (\texttt{tuple}s) to \texttt{hull}.
+
+ A sample usage:
+
+ \inputminted{python}{\ThisImpl/python3/usage.py}
+}
--- /dev/null
+{
+ \newcommand{\ThisImpl}{\ImplPath/kruskals}
+
+ \TopicHeader{Minimum Spanning Tree: Kruskal's Algorithm}
+
+ Kruskal's algorithm finds a spanning tree in a weighted undirected graph (or a collection of
+ spanning trees if the graph isn't connected) whose total edge cost is minimal. The algorithm
+ runs in $O(E \log V)$ (or equivalently $O(E \log E)$) time, where $V$ is the number of
+ vertices and $E$ the number of edges in the graph.
+
+ \TopicSubHeader{Python 3}
+
+ \inputminted{python}{\ThisImpl/python3/kruskals.py}
+
+ \paragraph{Note} This implementation uses the disjoint sets type defined on page
+ \pageref{structures-disjoint-sets-python3}.
+
+ The input for this algorithm is a graph, and in this implementation the graph is
+ represented by a dictionary mapping edges to their costs. The cost values are just
+ numbers, but the edges are iterables of two nodes. Because the edges need to be used as
+ dictionary keys, they need to be hashable, and because the graph is undirected the
+ programmer should be careful not to accidentally look up the edge $(b, a)$ when only
+ $(a, b)$ is present. In Python, the simplest way to accomplish this is to use immutable
+ sets, so that the order in which the two nodes are listed within an edge doesn't make a
+ difference. Tuples also work, but this approach requires the programmer to be consistent
+ about the order of nodes within each edge.
+
+ The nodes can be of any type, as long as hashing and equality behave correctly. Many
+ primitive types will work fine, as well as typical class types you define.
+
+ The algorithm returns a set of edges, that is, a subset of the keys in the input
+ dictionary. The costs of the edges can then be looked up using the graph dictionary
+ passed to the implementation.
+
+ Here's a simple usage example:
+
+ \inputminted{python}{\ThisImpl/python3/usage.py}
+
+ \TopicSubHeader{Java}
+
+ Coming soon\ldots
+}
--- /dev/null
+{
+ \newcommand{\ThisImpl}{\ImplPath/network-flows}
+
+ \TopicHeader{Network Flows: Edmonds--Karp}
+
+ Network flows algorithms find the maximum flow through and the minimum cut of a flow network.
+ This terminology and relevant algorithms are covered in Algorithms (CS 280) at Oberlin.
+
+ The implementations here use the Edmonds--Karp algorithm on flow networks with integer capacities.
+ The time complexity of the algorithm is $O\left( VE^2 \right)$, where $V$ and $E$ are the number of vertices and edges in the network, respectively.
+
+ \TopicSubHeader{Java}
+
+ Coming soon...
+
+ \TopicSubHeader{Python 3}
+
+ \inputminted{python}{\ThisImpl/python3/flows.py}
+
+ To use the code:
+
+ \inputminted{python}{\ThisImpl/python3/usage.py}
+
+ The \texttt{cut\_src} variable here is a set of node objects representing the source side of the minimum cut.
+ If the set \texttt{n} contains all nodes in the network, the sink side is computed by \texttt{n - cut\_src}.
+}
--- /dev/null
+{
+ \newcommand{\ThisImpl}{\DsImplPath/disjoint-sets}
+
+ \TopicHeader{Disjoint Sets}
+
+ This data structure is a forest of disjoint sets with path halving and union by size. The
+ \textit{union} and \textit{find} operations are essentially constant-time amortized.
+
+ \TopicSubHeader{Python 3} \label{structures-disjoint-sets-python3}
+
+ \inputminted{python}{\ThisImpl/python3/disjoint_sets.py}
+
+ Basic usage:
+
+ \inputminted{python}{\ThisImpl/python3/usage.py}
+
+ The values in the sets may be of any type, as long as they are hashable. The data
+ structures uses hashing and equality testing to locate nodes. Generally, this means you
+ can safely use immutable built-in types or your own custom object types as values in the
+ sets.
+
+ \TopicSubHeader{Java} \label{structures-disjoint-sets-java}
+
+ Coming soon\ldots
+}
# These are more than the files we really need to depend on, but keep things simple.
-ALG_FILES := $(shell find algorithms -type f)
+ALG_FILES := $(shell find latex -type f)
IMPL_FILES := $(shell find impl -type f)
BUILD_CMD := ( cd output && pdflatex -shell-escape ../doc.tex )
-- add tests for implementations and automate
- implement configurable sections (select specific algorithms)
+- implement configurable languages
- copy useful stuff from https://en.wikibooks.org/wiki/Algorithm_implementation
- DFS, BFS, Bellman Ford, area of polygons, minimum spanning tree, brute force TSP
- canonical problems (24: ecna17 twentyfour, coin problem)
import subprocess
from pathlib import Path
-for alg in Path('impl').iterdir():
+for alg in Path('impl', 'algorithms').iterdir():
if alg.is_dir():
for lang in alg.iterdir():
if lang.is_dir() and lang.joinpath('makefile').exists():