diff --git a/Writerside/topics/Computer-Architecture.topic b/Writerside/topics/Computer-Architecture.topic index a2b7f57..b0cb80e 100644 --- a/Writerside/topics/Computer-Architecture.topic +++ b/Writerside/topics/Computer-Architecture.topic @@ -505,11 +505,7 @@ Conversion"/> -

- Idea: - Just like unsigned, - but shifted on the number line. -

+

Idea: Just like unsigned, but shifted on the number line.

  • diff --git a/Writerside/topics/Data-Structures-and-Algorithms-3.topic b/Writerside/topics/Data-Structures-and-Algorithms-3.topic index 87c7b2a..1979cf7 100644 --- a/Writerside/topics/Data-Structures-and-Algorithms-3.topic +++ b/Writerside/topics/Data-Structures-and-Algorithms-3.topic @@ -1587,126 +1587,55 @@ -

    - Cost of undirected shortest paths: - E \log V + E -

    -

    - Proof: - Undirected shortest paths - (with nonnegative weights) reduces to directed shortest path. -

    +

    Cost of undirected shortest paths: E \log V + E

    +

    Proof: Undirected shortest paths (with nonnegative weights) reduces to + directed shortest path.

  • -

    - E \log V - cost of shortest paths in digraph -

    +

    E \log V cost of shortest paths in digraph

  • -

    - E - cost of reduction -

    +

    E cost of reduction

  • -

    distTo[v] is length of shortest - known - - path from - s - to - v - . -

    +

    distTo[v] is length of shortest known path from + s to v.

    -

    distTo[w] is length of shortest - known - - path from - s - to - w - . -

    +

    distTo[w] is length of shortest known path from + s to w.

    -

    edgeTo[w] is last edge on shortest - known - - path from - s - to - w - . -

    +

    edgeTo[w] is last edge on shortest known path + from s to w.

    -

    If - e = v->w - gives shorter path to - w - - through - v - , update both distTo[w] - and edgeTo[w]. -

    +

    If e = v->w gives shorter path to w through v, update both + distTo[w] and edgeTo[w].

    Edge Relaxation -

    - Correctness Proof: - Shortest-paths optimality conditions -

    -

    Let - G - be an edge-weighted digraph. -

    -

    Then distTo[] are the shortest path distances from - s - iff: -

    +

    Correctness Proof: Shortest-paths optimality conditions

    +

    Let G be an edge-weighted digraph.

    +

    Then distTo[] are the shortest path distances from s iff:

  • distTo[s] = 0.

  • -

    For each vertex v, distTo[v] is the length of some path from - s - to - v - . -

    +

    For each vertex v, distTo[v] is the length of some path from s to v.

  • -

    For each edge - e = v -> w - , distTo[w] <= distTo[v] + - e.weight(). -

    +

    For each edge e = v -> w, distTo[w] <= distTo[v] + e.weight().

  • -

    - Proof -

    +

    Proof

  • -

    Suppose that - s = v_{0} → v_{1} → v_{2} → ... → v_{k} = w - - is a shortest path from - s - to - w - . -

    +

    Suppose that s = v_{0} → v_{1} → v_{2} → ... → v_{k} = w is a shortest path from + s to w.

  • Then,

    @@ -1718,48 +1647,24 @@ \text{distTo}[v_{k}] & = \text{distTo}[v_{k - 1}] + \text{e}_{k}.\text{weight}() \\ \end{align*} -

    - \text{e}_{i} - = - \text{i}^{\text{th}} - edge - on shortest path from - s - to - w - . -

    +

    \text{e}_{i} = \text{i}^{\text{th}} edge on shortest path from s + to w.

  • -

    Add inequalities; simplify; and substitute - \text{distTo}[v_{0}] = \text{distTo}[s] = 0 +

    Add inequalities; simplify; and substitute \text{distTo}[v_{0}] = \text{distTo}[s] = 0

    \text{distTo}[w] = \text{distTo}[v_{k}] \leq \text{e}_{1}.\text{weight}() + \text{e}_{2}.\text{weight}() + ... + \text{e}_{k}.\text{weight}() -

    - \text{e}_{1}.\text{weight}() + \text{e}_{2}.\text{weight}() - + ... + \text{e}_{k}.\text{weight}() - - is the weight of shortest - path from - s - to - w - . -

    +

    \text{e}_{1}.\text{weight}() + \text{e}_{2}.\text{weight}() + ... + \text{e}_{k}.\text{weight}() + is the weight of shortest path from s to w.

  • -

    Thus, distTo[w] is the weight of shortest path to - w - . -

    +

    Thus, distTo[w] is the weight of shortest path to w.

  • -

    - Different Implementations -

    +

    Different Implementations

  • Dijkstra's algorithm (nonnegative weights).

    @@ -1776,79 +1681,49 @@

    Consider vertices in increasing order of distance from s.

    -

    (non-tree vertex with the lowest distTo[] value) -

    +

    (non-tree vertex with the lowest distTo[] value)

    -

    Add vertex to tree and relaw all edges pointing from that vertex. -

    +

    Add vertex to tree and relaw all edges pointing from that vertex.

    -

    - Correctness Proof: - Dijkstra's - algorithm computes a SPT in any edge-weighted digraph with - nonnegative - weights. -

    +

    Correctness Proof: Dijkstra's algorithm computes a SPT in any + edge-weighted digraph with nonnegative weights.

  • -

    Each edge - e = v→w - is relaxed exactly once - (when v is relaxed), leaving - \text{distTo}[w] ≤ \text{distTo}[v] + \text{e.weight()} - . -

    +

    Each edge e = v->w is relaxed exactly once (when v is relaxed), leaving + \text{distTo}[w] ≤ \text{distTo}[v] + \text{e.weight()}.

  • Inequality holds until algorithm terminates because:

  • -

    distTo[w] cannot increase => distTo[] - values are monotone decreasing.

    +

    distTo[w] cannot increase => distTo[] values are monotone + decreasing.

  • -

    distTo[v] will not change => we choose lowest - distTo[] value at each step and edge weights are - nonnegative)

    +

    distTo[v] will not change => we choose lowest distTo[] value at + each step and edge weights are nonnegative)

  • -

    Thus, upon termination, shortest-paths optimality conditions - hold.

    +

    Thus, upon termination, shortest-paths optimality conditions hold.

  • -

    - Prim’s algorithm is essentially the - same algorithm as Dijkstra's algorithm - -

    +

    Prim’s algorithm is essentially the same algorithm as Dijkstra's algorithm +

  • -

    Both are in a family of algorithms that compute a graph's - spanning tree.

    +

    Both are in a family of algorithms that compute a graph's spanning tree.

  • -

    - Prim's - : Closest vertex to - the - tree - (via an undirected - edge). -

    +

    Prim's: Closest vertex to the tree + (via an undirected edge).

  • -

    - Dijkstra's - : Closest vertex - to the - source - (via a - directed path). -

    +

    Dijkstra's: Closest vertex to the + source (via a directed path).

  • @@ -1864,57 +1739,57 @@ public class Dijkstra { - private final double[] distTo; - private final DirectedEdge[] edgeTo; - private final boolean[] marked; - private final PriorityQueue<Integer> pq; - - public Dijkstra(EdgeWeightedDigraph G, int s) { - distTo = new double[G.V()]; - edgeTo = new DirectedEdge[G.V()]; - marked = new boolean[G.V()]; - for (int v = 0; v < G.V(); v++) - distTo[v] = Double.POSITIVE_INFINITY; - distTo[s] = 0.0; - pq = new PriorityQueue<>(Comparator.comparingDouble(v -> distTo[v])); - pq.offer(s); - while (!pq.isEmpty()) { - int v = pq.poll(); - marked[v] = true; - for (DirectedEdge e : G.adj(v)) { - relax(e); - } - } - } + private final double[] distTo; + private final DirectedEdge[] edgeTo; + private final boolean[] marked; + private final PriorityQueue<Integer> pq; + + public Dijkstra(EdgeWeightedDigraph G, int s) { + distTo = new double[G.V()]; + edgeTo = new DirectedEdge[G.V()]; + marked = new boolean[G.V()]; + for (int v = 0; v < G.V(); v++) + distTo[v] = Double.POSITIVE_INFINITY; + distTo[s] = 0.0; + pq = new PriorityQueue<>(Comparator.comparingDouble(v -> distTo[v])); + pq.offer(s); + while (!pq.isEmpty()) { + int v = pq.poll(); + marked[v] = true; + for (DirectedEdge e : G.adj(v)) { + relax(e); + } + } + } - private void relax(DirectedEdge e) { - int v = e.from(); - int w = e.to(); - if (distTo[w] > distTo[v] + e.weight()) { - distTo[w] = distTo[v] + e.weight(); - edgeTo[w] = e; - if (!marked[w]) { - pq.offer(w); - } - } - } + private void relax(DirectedEdge e) { + int v = e.from(); + int w = e.to(); + if (distTo[w] > distTo[v] + e.weight()) { + distTo[w] = distTo[v] + e.weight(); + edgeTo[w] = e; + if (!marked[w]) { + pq.offer(w); + } + } + } - public double distTo(int v) { - return distTo[v]; - } + public double distTo(int v) { + return distTo[v]; + } - public boolean hasPathTo(int v) { - return distTo[v] < Double.POSITIVE_INFINITY; - } + public boolean hasPathTo(int v) { + return distTo[v] < Double.POSITIVE_INFINITY; + } - public Iterable<DirectedEdge&gt pathTo(int v) { - if (!hasPathTo(v)) return null; - List<DirectedEdge> path = new ArrayList<>(); - for (DirectedEdge e = edgeTo[v]; e != null; e = edgeTo[e.from()]) { - path.add(e); - } - return path; - } + public Iterable<DirectedEdge&gt pathTo(int v) { + if (!hasPathTo(v)) return null; + List<DirectedEdge> path = new ArrayList<>(); + for (DirectedEdge e = edgeTo[v]; e != null; e = edgeTo[e.from()]) { + path.add(e); + } + return path; + } } @@ -1929,20 +1804,20 @@ class Dijkstra { private: - std::vector<double> distTo; - std::vector<DirectedEdge> edgeTo; - std::vector<bool> marked; - std::priority_queue<std::pair<double, int>, std::vector<std::pair<double, int>>, - std::greater<>> pq; - - void relax(const DirectedEdge& e); + std::vector<double> distTo; + std::vector<DirectedEdge> edgeTo; + std::vector<bool> marked; + std::priority_queue<std::pair<double, int>, std::vector<std::pair<double, int>>, + std::greater<>> pq; + + void relax(const DirectedEdge& e); public: - explicit Dijkstra(const EdgeWeightedDigraph& G, int s); - - [[nodiscard]] double getdistTo(int v) const; - [[nodiscard]] bool hasPathTo(int v) const; - [[nodiscard]] std::vector<DirectedEdge> pathTo(int v) const; + explicit Dijkstra(const EdgeWeightedDigraph& G, int s); + + [[nodiscard]] double getdistTo(int v) const; + [[nodiscard]] bool hasPathTo(int v) const; + [[nodiscard]] std::vector<DirectedEdge> pathTo(int v) const; }; #endif // DIJKSTRA_H @@ -1956,49 +1831,48 @@ Dijkstra::Dijkstra(const EdgeWeightedDigraph& G, int s) : distTo(G.getV(), std::numeric_limits<double>::infinity()), edgeTo(G.getV(), DirectedEdge()), - marked(G.getV(), false) - { - distTo[s] = 0.0; - pq.emplace(0.0, s); - - while (!pq.empty()) { - int v = pq.top().second; - pq.pop(); - - if (marked[v]) continue; - - marked[v] = true; - for (const auto& e : G.getAdj(v)) { - relax(e); - } - } + marked(G.getV(), false) { + distTo[s] = 0.0; + pq.emplace(0.0, s); + + while (!pq.empty()) { + int v = pq.top().second; + pq.pop(); + + if (marked[v]) continue; + + marked[v] = true; + for (const auto& e : G.getAdj(v)) { + relax(e); + } + } } void Dijkstra::relax(const DirectedEdge& e) { - int v = e.from(); - int w = e.to(); - if (distTo[w] > distTo[v] + e.getWeight()) { - distTo[w] = distTo[v] + e.getWeight(); - edgeTo[w] = e; - pq.emplace(distTo[w], w); - } + int v = e.from(); + int w = e.to(); + if (distTo[w] > distTo[v] + e.getWeight()) { + distTo[w] = distTo[v] + e.getWeight(); + edgeTo[w] = e; + pq.emplace(distTo[w], w); + } } double Dijkstra::getdistTo(int v) const { - return distTo[v]; + return distTo[v]; } bool Dijkstra::hasPathTo(int v) const { - return distTo[v] < std::numeric_limits<double>::infinity(); + return distTo[v] < std::numeric_limits<double>::infinity(); } std::vector<DirectedEdge> Dijkstra::pathTo(int v) const { - if (!hasPathTo(v)) return {}; - std::vector<DirectedEdge> path; - for (DirectedEdge e = edgeTo[v]; e.from() != -1; e = edgeTo[e.from()]) { - path.push_back(e); - } - return path; + if (!hasPathTo(v)) return {}; + std::vector<DirectedEdge> path; + for (DirectedEdge e = edgeTo[v]; e.from() != -1; e = edgeTo[e.from()]) { + path.push_back(e); + } + return path; } @@ -2009,62 +1883,54 @@ class Dijkstra: - def __init__(self, G, s): - self.distTo = [float('inf')] * G.get_V() - self.edgeTo = [None] * G.get_V() - self.marked = [False] * G.get_V() - self.pq = [] - - self.distTo[s] = 0.0 - heapq.heappush(self.pq, (0.0, s)) - - while self.pq: - _, v = heapq.heappop(self.pq) - - if self.marked[v]: - continue - - self.marked[v] = True - for e in G.get_adj(v): - self.relax(e) - - def relax(self, e): - v = e.from_vertex() - w = e.to_vertex() - if self.distTo[w] > self.distTo[v] + e.get_weight(): - self.distTo[w] = self.distTo[v] + e.get_weight() - self.edgeTo[w] = e - heapq.heappush(self.pq, (self.distTo[w], w)) - - def dist_to(self, v): - return self.distTo[v] - - def has_path_to(self, v): - return self.distTo[v] < float('inf') - - def path_to(self, v): - if not self.has_path_to(v): - return None - path = [] - - for e in reversed(self.edgeTo[v:v + 1]): - if e is not None: - path.append(e) - return path + def __init__(self, G, s): + self.distTo = [float('inf')] * G.get_V() + self.edgeTo = [None] * G.get_V() + self.marked = [False] * G.get_V() + self.pq = [] + + self.distTo[s] = 0.0 + heapq.heappush(self.pq, (0.0, s)) + + while self.pq: + _, v = heapq.heappop(self.pq) + + if self.marked[v]: + continue + + self.marked[v] = True + for e in G.get_adj(v): + self.relax(e) + + def relax(self, e): + v = e.from_vertex() + w = e.to_vertex() + if self.distTo[w] > self.distTo[v] + e.get_weight(): + self.distTo[w] = self.distTo[v] + e.get_weight() + self.edgeTo[w] = e + heapq.heappush(self.pq, (self.distTo[w], w)) + + def dist_to(self, v): + return self.distTo[v] + + def has_path_to(self, v): + return self.distTo[v] < float('inf') + + def path_to(self, v): + if not self.has_path_to(v): + return None + path = [] + + for e in reversed(self.edgeTo[v:v + 1]): + if e is not None: + path.append(e) + return path -

    - Property -

    -

    Running time depends on PQ implementation: - V - insert, - V - delete-min, - E - decrease-key. -

    +

    Property

    +

    Running time depends on PQ implementation: V insert, V delete-min, E + decrease-key.

    @@ -2137,9 +2003,7 @@
    PQ Implementation

    *: amortized

    -

    - Bottom Line -

    +

    Bottom Line

  • Array implementation optimal for dense graph.

    @@ -2148,8 +2012,7 @@

    Binary heap much faster for sparse graphs.

  • -

    4-way heap worth the trouble in performance-critical - situations.

    +

    4-way heap worth the trouble in performance-critical situations.

  • Fibonacci heap best in theory, but not worth implementing.

    @@ -2165,35 +2028,23 @@

    Relax all edges pointing from that vertex.

    -

    - Property: - Topological sort - algorithm computes SPT in any edgeweighted DAG in time proportional - to - E + V - . -

    +

    Property: Topological sort algorithm computes SPT in any edgeweighted + DAG in time proportional to E + V.

  • -

    Each edge - e = v→w - is relaxed exactly once - (when v is relaxed), leaving - \text{distTo}[w] ≤ \text{distTo}[v] + \text{e.weight()} - . -

    +

    Each edge e = v->w is relaxed exactly once (when v is relaxed), leaving + \text{distTo}[w] ≤ \text{distTo}[v] + \text{e.weight()}.

  • Inequality holds until algorithm terminates because:

  • -

    distTo[w] cannot increase => distTo[] - values are monotone decreasing.

    +

    distTo[w] cannot increase => distTo[] values are monotone + decreasing.

  • -

    distTo[v] will not change => we choose lowest - distTo[] value at each step and edge weights are - nonnegative)

    +

    distTo[v] will not change => we choose lowest distTo[] value at + each step and edge weights are nonnegative)

  • @@ -2202,10 +2053,7 @@ hold.

    -

    - Longest paths in edge-weighted DAGs: - -

    +

    Longest paths in edge-weighted DAGs:

    Formulate as a shortest paths problem in edge-weighted DAGs.

  • @@ -2228,96 +2076,96 @@ public class ShortestPathTopological { - private final EdgeWeightedDigraph graph; - private final int source; - private final double[] distTo; - private final DirectedEdge[] edgeTo; - - public ShortestPathTopological(EdgeWeightedDigraph graph, int source) { - this.graph = graph; - this.source = source; - distTo = new double[graph.V()]; - edgeTo = new DirectedEdge[graph.V()]; - - for (int v = 0; v < graph.V(); v++) { - distTo[v] = Double.POSITIVE_INFINITY; - } - distTo[source] = 0.0; - - TopologicalSort topologicalSort = new TopologicalSort(); - List<Integer> sorted = topologicalSort.sort(graph); - - for (int v : sorted) { - relax(v); - } - } - - private void relax(int v) { - for (DirectedEdge edge : graph.adj(v)) { - int w = edge.to(); - if (distTo[w] > distTo[v] + edge.weight()) { - distTo[w] = distTo[v] + edge.weight(); - edgeTo[w] = edge; - } - } - } - - public double distTo(int v) { - return distTo[v]; - } - - public boolean hasPathTo(int v) { - return distTo[v] < Double.POSITIVE_INFINITY; - } + private final EdgeWeightedDigraph graph; + private final int source; + private final double[] distTo; + private final DirectedEdge[] edgeTo; - public Iterable<DirectedEdge> pathTo(int v) { - if (!hasPathTo(v)) return null; - List<DirectedEdge> path = new ArrayList<>(); - for (DirectedEdge e = edgeTo[v]; e != null; e = edgeTo[e.from()]) { - path.addFirst(e); - } - return path; - } + public ShortestPathTopological(EdgeWeightedDigraph graph, int source) { + this.graph = graph; + this.source = source; + distTo = new double[graph.V()]; + edgeTo = new DirectedEdge[graph.V()]; + + for (int v = 0; v < graph.V(); v++) { + distTo[v] = Double.POSITIVE_INFINITY; + } + distTo[source] = 0.0; + + TopologicalSort topologicalSort = new TopologicalSort(); + List<Integer> sorted = topologicalSort.sort(graph); + + for (int v : sorted) { + relax(v); + } + } - private static class TopologicalSort { - public List<Integer> sort(EdgeWeightedDigraph graph) { - int V = graph.V(); - List<Integer> sorted = new ArrayList<>(); - int[] inDegree = new int[V]; - Queue<Integer> queue = new LinkedList<>(); + private void relax(int v) { + for (DirectedEdge edge : graph.adj(v)) { + int w = edge.to(); + if (distTo[w] > distTo[v] + edge.weight()) { + distTo[w] = distTo[v] + edge.weight(); + edgeTo[w] = edge; + } + } + } - for (int v = 0; v < V; v++) { - for (DirectedEdge edge : graph.adj(v)) { - inDegree[edge.to()]++; - } - } + public double distTo(int v) { + return distTo[v]; + } - for (int v = 0; v < V; v++) { - if (inDegree[v] == 0) { - queue.offer(v); - } - } + public boolean hasPathTo(int v) { + return distTo[v] < Double.POSITIVE_INFINITY; + } - while (!queue.isEmpty()) { - int u = queue.poll(); - sorted.add(u); + public Iterable<DirectedEdge> pathTo(int v) { + if (!hasPathTo(v)) return null; + List<DirectedEdge> path = new ArrayList<>(); + for (DirectedEdge e = edgeTo[v]; e != null; e = edgeTo[e.from()]) { + path.addFirst(e); + } + return path; + } - for (DirectedEdge edge : graph.adj(u)) { - int v = edge.to(); - inDegree[v]--; - if (inDegree[v] == 0) { - queue.offer(v); - } - } - } + private static class TopologicalSort { + public List<Integer> sort(EdgeWeightedDigraph graph) { + int V = graph.V(); + List<Integer> sorted = new ArrayList<>(); + int[] inDegree = new int[V]; + Queue<Integer> queue = new LinkedList<>(); + + for (int v = 0; v < V; v++) { + for (DirectedEdge edge : graph.adj(v)) { + inDegree[edge.to()]++; + } + } - if (sorted.size() != V) { - throw new IllegalArgumentException("Graph contains a cycle."); - } + for (int v = 0; v < V; v++) { + if (inDegree[v] == 0) { + queue.offer(v); + } + } - return sorted; - } - } + while (!queue.isEmpty()) { + int u = queue.poll(); + sorted.add(u); + + for (DirectedEdge edge : graph.adj(u)) { + int v = edge.to(); + inDegree[v]--; + if (inDegree[v] == 0) { + queue.offer(v); + } + } + } + + if (sorted.size() != V) { + throw new IllegalArgumentException("Graph contains a cycle."); + } + + return sorted; + } + } } @@ -2331,29 +2179,29 @@ class ShortestPathTopological { private: - const EdgeWeightedDigraph& graph; - int source; - std::vector<double> distTo; - std::vector<DirectedEdge> edgeTo; + const EdgeWeightedDigraph& graph; + int source; + std::vector<double> distTo; + std::vector<DirectedEdge> edgeTo; - class TopologicalSort { - public: - explicit TopologicalSort(const EdgeWeightedDigraph& graph); - [[nodiscard]] std::vector<int> sort() const; - private: - const EdgeWeightedDigraph& graph; - }; + class TopologicalSort { + public: + explicit TopologicalSort(const EdgeWeightedDigraph& graph); + [[nodiscard]] std::vector<int> sort() const; + private: + const EdgeWeightedDigraph& graph; + }; - void relax(int v); + void relax(int v); public: - explicit ShortestPathTopological(const EdgeWeightedDigraph& graph, int source); + explicit ShortestPathTopological(const EdgeWeightedDigraph& graph, int source); - [[nodiscard]] double getdistTo(int v) const; + [[nodiscard]] double getdistTo(int v) const; - [[nodiscard]] bool hasPathTo(int v) const; + [[nodiscard]] bool hasPathTo(int v) const; - [[nodiscard]] std::vector<DirectedEdge> pathTo(int v) const; + [[nodiscard]] std::vector<DirectedEdge> pathTo(int v) const; }; #endif // SHORTESTPATHTOPOLOGICAL_H @@ -2371,85 +2219,85 @@ ShortestPathTopological::ShortestPathTopological(const EdgeWeightedDigraph &graph, int source) : graph(graph), source(source), distTo(graph.getV(), std::numeric_limits<double>::infinity()), edgeTo(graph.getV()) { - distTo[source] = 0.0; - - TopologicalSort topologicalSort(graph); - std::vector<int> sorted = topologicalSort.sort(); - - for (int v : sorted) { - relax(v); - } + distTo[source] = 0.0; + + TopologicalSort topologicalSort(graph); + std::vector<int> sorted = topologicalSort.sort(); + + for (int v : sorted) { + relax(v); + } } void ShortestPathTopological::relax(const int v) { - for (const DirectedEdge& edge : graph.getAdj(v)) { - int w = edge.to(); - if (distTo[w] > distTo[v] + edge.getWeight()) { - distTo[w] = distTo[v] + edge.getWeight(); - edgeTo[w] = edge; - } - } + for (const DirectedEdge& edge : graph.getAdj(v)) { + int w = edge.to(); + if (distTo[w] > distTo[v] + edge.getWeight()) { + distTo[w] = distTo[v] + edge.getWeight(); + edgeTo[w] = edge; + } + } } double ShortestPathTopological::getdistTo(const int v) const { - return distTo[v]; + return distTo[v]; } bool ShortestPathTopological::hasPathTo(const int v) const { - return distTo[v] < std::numeric_limits<double>::infinity(); + return distTo[v] < std::numeric_limits<double>::infinity(); } std::vector<DirectedEdge> ShortestPathTopological::pathTo(const int v) const { - std::vector<DirectedEdge> path; - if (!hasPathTo(v)) { - return path; - } + std::vector<DirectedEdge> path; + if (!hasPathTo(v)) { + return path; + } - for (DirectedEdge e = edgeTo[v]; e.from() != -1; e = edgeTo[e.from()]) { - path.push_back(e); - } - std::ranges::reverse(path); - return path; + for (DirectedEdge e = edgeTo[v]; e.from() != -1; e = edgeTo[e.from()]) { + path.push_back(e); + } + std::ranges::reverse(path); + return path; } ShortestPathTopological::TopologicalSort::TopologicalSort(const EdgeWeightedDigraph &graph) : graph(graph) {} std::vector<int> ShortestPathTopological::TopologicalSort::sort() const { - const int V = graph.getV(); - std::vector<int> sorted; - std::vector<int> inDegree(V, 0); - std::queue<int> queue; - - for (int v = 0; v < V; ++v) { - for (const DirectedEdge& e : graph.getAdj(v)) { - inDegree[e.to()]++; - } - } - - for (int v = 0; v < V; ++v) { - if (inDegree[v] == 0) { - queue.push(v); - } - } - - while (!queue.empty()) { - int u = queue.front(); - queue.pop(); - sorted.push_back(u); - - for (const DirectedEdge& e : graph.getAdj(u)) { - int w = e.to(); - if (--inDegree[w] == 0) { - queue.push(w); - } - } - } - - if (sorted.size() != static_cast<size_t>(V)) { - throw std::runtime_error("Graph contains a cycle!"); - } - return sorted; + const int V = graph.getV(); + std::vector<int> sorted; + std::vector<int> inDegree(V, 0); + std::queue<int> queue; + + for (int v = 0; v < V; ++v) { + for (const DirectedEdge& e : graph.getAdj(v)) { + inDegree[e.to()]++; + } + } + + for (int v = 0; v < V; ++v) { + if (inDegree[v] == 0) { + queue.push(v); + } + } + + while (!queue.empty()) { + int u = queue.front(); + queue.pop(); + sorted.push_back(u); + + for (const DirectedEdge& e : graph.getAdj(u)) { + int w = e.to(); + if (--inDegree[w] == 0) { + queue.push(w); + } + } + } + + if (sorted.size() != static_cast<size_t>(V)) { + throw std::runtime_error("Graph contains a cycle!"); + } + return sorted; } @@ -2463,107 +2311,86 @@ class ShortestPathTopological: - def __init__(self, graph: EdgeWeightedDigraph, source: int): - self.graph = graph - self.source = source - self.dist_to = [float('inf')] * graph.get_V() - self.edge_to: List[Optional[DirectedEdge]] = [None] * graph.get_V() - self.dist_to[source] = 0.0 - - topological_order = self._topological_sort() - for v in topological_order: - self._relax(v) - - def _relax(self, v: int): - for edge in self.graph.get_adj(v): - w = edge.to_vertex() - if self.dist_to[w] > self.dist_to[v] + edge.get_weight(): - self.dist_to[w] = self.dist_to[v] + edge.get_weight() - self.edge_to[w] = edge - - def get_dist_to(self, v: int) -> float: - return self.dist_to[v] - - def has_path_to(self, v: int) -> bool: - return self.dist_to[v] < float('inf') - - def path_to(self, v: int) -> Optional[List[str]]: - if not self.has_path_to(v): - return None - path: List[DirectedEdge] = [] - e = self.edge_to[v] - while e is not None: - path.append(e) - e = self.edge_to[e.from_vertex()] - return [str(edge) for edge in path[::-1]] - - def _topological_sort(self) -> List[int]: - V = self.graph.get_V() - in_degree = [0] * V - for v in range(V): - for edge in self.graph.get_adj(v): - in_degree[edge.to_vertex()] += 1 - - queue = deque([v for v in range(V) if in_degree[v] == 0]) - sorted_order = [] - while queue: - u = queue.popleft() - sorted_order.append(u) - for edge in self.graph.get_adj(u): - v = edge.to_vertex() - in_degree[v] -= 1 - if in_degree[v] == 0: - queue.append(v) - - if len(sorted_order) != V: - raise ValueError("Graph contains a cycle.") - - return sorted_order + def __init__(self, graph: EdgeWeightedDigraph, source: int): + self.graph = graph + self.source = source + self.dist_to = [float('inf')] * graph.get_V() + self.edge_to: List[Optional[DirectedEdge]] = [None] * graph.get_V() + self.dist_to[source] = 0.0 + + topological_order = self._topological_sort() + for v in topological_order: + self._relax(v) + + def _relax(self, v: int): + for edge in self.graph.get_adj(v): + w = edge.to_vertex() + if self.dist_to[w] > self.dist_to[v] + edge.get_weight(): + self.dist_to[w] = self.dist_to[v] + edge.get_weight() + self.edge_to[w] = edge + + def get_dist_to(self, v: int) -> float: + return self.dist_to[v] + + def has_path_to(self, v: int) -> bool: + return self.dist_to[v] < float('inf') + + def path_to(self, v: int) -> Optional[List[str]]: + if not self.has_path_to(v): + return None + path: List[DirectedEdge] = [] + e = self.edge_to[v] + while e is not None: + path.append(e) + e = self.edge_to[e.from_vertex()] + return [str(edge) for edge in path[::-1]] + + def _topological_sort(self) -> List[int]: + V = self.graph.get_V() + in_degree = [0] * V + for v in range(V): + for edge in self.graph.get_adj(v): + in_degree[edge.to_vertex()] += 1 + + queue = deque([v for v in range(V) if in_degree[v] == 0]) + sorted_order = [] + while queue: + u = queue.popleft() + sorted_order.append(u) + for edge in self.graph.get_adj(u): + v = edge.to_vertex() + in_degree[v] -= 1 + if in_degree[v] == 0: + queue.append(v) + + if len(sorted_order) != V: + raise ValueError("Graph contains a cycle.") + + return sorted_order -

    - Application Ⅰ - Content-Aware - Resizing - -

    -

    - Seam Carving: - Resize an image - without distortion for display on cell phones and web browsers. -

    +

    Application Ⅰ - Content-Aware Resizing

    +

    Seam Carving: Resize an image without distortion for display on cell + phones and web browsers.

  • -

    Grid DAG: vertex = pixel; edge = from pixel to 3 downward - neighbors.

    +

    Grid DAG: vertex = pixel; edge = from pixel to 3 downward neighbors.

  • -

    Weight of pixel = energy function of 8 neighboring pixels. -

    +

    Weight of pixel = energy function of 8 neighboring pixels.

  • -

    Seam = shortest path (sum of vertex weights) from top to - bottom.

    +

    Seam = shortest path (sum of vertex weights) from top to bottom.

  • Seam Carving -

    - Application Ⅱ - Parallel Job - Scheduling - -

    -

    - Parallel Job Scheduling: - Given a set of jobs with durations and precedence constraints, - schedule the jobs (by finding a start time for each) so as to achieve - the minimum completion time, while respecting the constraints. -

    -

    To solve a parallel job-scheduling problem, create edge-weighted - DAG, use - longest path - from the - source to schedule each job: -

    +

    Application Ⅱ - Parallel Job Scheduling

    +

    Parallel Job Scheduling: Given a set of jobs with durations and + precedence constraints, schedule the jobs (by finding a start time for each) so as to achieve the + minimum completion time, while respecting the constraints.

    +

    To solve a parallel job-scheduling problem, create edge-weighted DAG, use + longest path from the source to schedule each job:

  • Source and sink vertices.

    @@ -2585,49 +2412,31 @@
  • -
  • One edge for each precedence constraint (0 weight).
  • +
  • +

    One edge for each precedence constraint (0 weight).

    +
  • Parallel Job SchedulingParallel Job Scheduling
    -

    - Negative Cycle: - A - negative cycle - - is a directed cycle whose - sum of edge weights is negative. -

    +

    Negative Cycle: A negative cycle + is a directed cycle whose sum of edge weights is negative.

    -

    Assuming all vertices reachable from s, a SPT exists iff no - negative cycles.

    +

    Assuming all vertices reachable from s, a SPT exists iff no negative cycles.

    -

    Initialize distTo[s] = 0 and distTo[v] = ∞ for all - other vertices.

    +

    Initialize distTo[s] = 0 and distTo[v] = ∞ for all other vertices.

    Repeat V times, relax each edge.

    -

    - Practical Improvement: - If - distTo[v] does not change during pass - i - , no need to - relax any edge pointing from v in pass - i+1 - => - maintain - queue - of vertices - whose distTo[] changed. -

    +

    Practical Improvement: If distTo[v] does not change during pass + i, no need to relax any edge pointing from v in pass i+1 => maintain + queue of vertices whose distTo[] changed.

    @@ -2686,7 +2495,6 @@
    Algorithm

    Bellman-Ford

    (queue-based)

    -
    E + V @@ -2721,77 +2529,77 @@ import java.util.Queue; public class BellmanFordSP { - private final double[] distTo; - private final DirectedEdge[] edgeTo; - private final boolean[] onQueue; - private final int[] cost; - private final int s; - private boolean hasNegativeCycle; - - private final Queue<Integer> q; - - public BellmanFordSP(EdgeWeightedDigraph G, int s) { - this.s = s; - distTo = new double[G.V()]; - edgeTo = new DirectedEdge[G.V()]; - onQueue = new boolean[G.V()]; - cost = new int[G.V()]; - for (int v = 0; v < G.V(); v++) - distTo[v] = Double.POSITIVE_INFINITY; - distTo[s] = 0.0; - - q = new LinkedList<>(); - q.add(s); - onQueue[s] = true; - - while (!q.isEmpty()) { - int v = q.remove(); - onQueue[v] = false; - relax(G, v); - } - } - - private void relax(EdgeWeightedDigraph G, int v) { - for (DirectedEdge e : G.adj(v)) { - int w = e.to(); - if (distTo[w] > distTo[v] + e.weight()) { - distTo[w] = distTo[v] + e.weight(); - edgeTo[w] = e; - cost[w]++; - - if (!onQueue[w]) { - q.add(w); - onQueue[w] = true; - } - - if (cost[w] >= G.V()) { - hasNegativeCycle = true; - return; - } - } - } - } + private final double[] distTo; + private final DirectedEdge[] edgeTo; + private final boolean[] onQueue; + private final int[] cost; + private final int s; + private boolean hasNegativeCycle; + + private final Queue<Integer> q; + + public BellmanFordSP(EdgeWeightedDigraph G, int s) { + this.s = s; + distTo = new double[G.V()]; + edgeTo = new DirectedEdge[G.V()]; + onQueue = new boolean[G.V()]; + cost = new int[G.V()]; + for (int v = 0; v < G.V(); v++) + distTo[v] = Double.POSITIVE_INFINITY; + distTo[s] = 0.0; + + q = new LinkedList<>(); + q.add(s); + onQueue[s] = true; + + while (!q.isEmpty()) { + int v = q.remove(); + onQueue[v] = false; + relax(G, v); + } + } - public double distTo(int v) { - return distTo[v]; - } + private void relax(EdgeWeightedDigraph G, int v) { + for (DirectedEdge e : G.adj(v)) { + int w = e.to(); + if (distTo[w] > distTo[v] + e.weight()) { + distTo[w] = distTo[v] + e.weight(); + edgeTo[w] = e; + cost[w]++; + + if (!onQueue[w]) { + q.add(w); + onQueue[w] = true; + } + + if (cost[w] >= G.V()) { + hasNegativeCycle = true; + return; + } + } + } + } - public boolean hasPathTo(int v) { - return distTo[v] < Double.POSITIVE_INFINITY; - } + public double distTo(int v) { + return distTo[v]; + } - public Iterable<DirectedEdge> pathTo(int v) { - if (!hasPathTo(v)) return null; - List<DirectedEdge> path = new ArrayList<>(); - for (DirectedEdge e = edgeTo[v]; e != null; e = edgeTo[e.from()]) { - path.add(e); - } - return path; - } + public boolean hasPathTo(int v) { + return distTo[v] < Double.POSITIVE_INFINITY; + } - public boolean hasNegativeCycle() { - return hasNegativeCycle; - } + public Iterable<DirectedEdge> pathTo(int v) { + if (!hasPathTo(v)) return null; + List<DirectedEdge> path = new ArrayList<>(); + for (DirectedEdge e = edgeTo[v]; e != null; e = edgeTo[e.from()]) { + path.add(e); + } + return path; + } + + public boolean hasNegativeCycle() { + return hasNegativeCycle; + } } @@ -2806,24 +2614,24 @@ class BellmanFordSP { private: - std::vector<double> distTo; - std::vector<DirectedEdge> edgeTo; - std::vector<bool> onQueue; - std::vector<int> cost; - int s; - bool hasNegativeCycle; - - std::queue<int> q; + std::vector<double> distTo; + std::vector<DirectedEdge> edgeTo; + std::vector<bool> onQueue; + std::vector<int> cost; + int s; + bool hasNegativeCycle; + + std::queue<int> q; public: - BellmanFordSP(const EdgeWeightedDigraph& G, int s); - [[nodiscard]] double getdistTo(int v) const; - [[nodiscard]] bool hasPathTo(int v) const; - [[nodiscard]] std::vector<DirectedEdge> pathTo(int v) const; - [[nodiscard]] bool NegativeCycle() const; + BellmanFordSP(const EdgeWeightedDigraph& G, int s); + [[nodiscard]] double getdistTo(int v) const; + [[nodiscard]] bool hasPathTo(int v) const; + [[nodiscard]] std::vector<DirectedEdge> pathTo(int v) const; + [[nodiscard]] bool NegativeCycle() const; private: - void relax(const EdgeWeightedDigraph& G, int v); + void relax(const EdgeWeightedDigraph& G, int v); }; #endif // BELLMANFORDSP_H @@ -2842,60 +2650,60 @@ s(s), hasNegativeCycle(false) { - distTo[s] = 0.0; - q.push(s); - onQueue[s] = true; - - while (!q.empty()) { - int v = q.front(); - q.pop(); - onQueue[v] = false; - relax(G, v); - } + distTo[s] = 0.0; + q.push(s); + onQueue[s] = true; + + while (!q.empty()) { + int v = q.front(); + q.pop(); + onQueue[v] = false; + relax(G, v); + } } double BellmanFordSP::getdistTo(const int v) const { - return distTo[v]; + return distTo[v]; } bool BellmanFordSP::hasPathTo(const int v) const { - return distTo[v] < std::numeric_limits<double>::infinity(); + return distTo[v] < std::numeric_limits<double>::infinity(); } std::vector<DirectedEdge> BellmanFordSP::pathTo(const int v) const { - if (!hasPathTo(v)) { - return {}; - } - std::vector<DirectedEdge> path; - for (DirectedEdge e = edgeTo[v]; e.from() != -1; e = edgeTo[e.from()]) { - path.push_back(e); - } - return path; + if (!hasPathTo(v)) { + return {}; + } + std::vector<DirectedEdge> path; + for (DirectedEdge e = edgeTo[v]; e.from() != -1; e = edgeTo[e.from()]) { + path.push_back(e); + } + return path; } bool BellmanFordSP::NegativeCycle() const { - return hasNegativeCycle; + return hasNegativeCycle; } void BellmanFordSP::relax(const EdgeWeightedDigraph& G, const int v) { - for (const DirectedEdge& e : G.getAdj(v)) { - int w = e.to(); - if (distTo[w] > distTo[v] + e.getWeight()) { - distTo[w] = distTo[v] + e.getWeight(); - edgeTo[w] = e; - cost[w]++; - - if (!onQueue[w]) { - q.push(w); - onQueue[w] = true; - } - - if (cost[w] >= G.getV()) { - hasNegativeCycle = true; - return; - } - } - } + for (const DirectedEdge& e : G.getAdj(v)) { + int w = e.to(); + if (distTo[w] > distTo[v] + e.getWeight()) { + distTo[w] = distTo[v] + e.getWeight(); + edgeTo[w] = e; + cost[w]++; + + if (!onQueue[w]) { + q.push(w); + onQueue[w] = true; + } + + if (cost[w] >= G.getV()) { + hasNegativeCycle = true; + return; + } + } + } } @@ -2904,71 +2712,66 @@ from EdgeWeightedDigraph import EdgeWeightedDigraph class BellmanFordSP: - def __init__(self, G, s): - self.distTo = [float("inf") for _ in range(G.get_V())] - self.edgeTo = [None for _ in range(G.get_V())] - self.onQueue = [False for _ in range(G.get_V())] - self.cost = [0 for _ in range(G.get_V())] - self.s = s - self.hasNegativeCycle = False - - self.distTo[s] = 0.0 - self.q = [s] - self.onQueue[s] = True - - while self.q: - v = self.q.pop(0) - self.onQueue[v] = False - self.relax(G, v) - - def distTo(self, v): - return self.distTo[v] - - def hasPathTo(self, v): - return self.distTo[v] != float("inf") - - def pathTo(self, v): - if not self.hasPathTo(v): - return None - path = [] - e = self.edgeTo[v] - while e is not None: - path.append(e) - e = self.edgeTo[e.from_vertex()] - return path - - def hasNegativeCycle(self): - return self.hasNegativeCycle - - def relax(self, G, v): - for e in G.get_adj(v): - w = e.to_vertex() - if self.distTo[w] > self.distTo[v] + e.get_weight(): - self.distTo[w] = self.distTo[v] + e.get_weight() - self.edgeTo[w] = e - self.cost[w] += 1 - - if not self.onQueue[w]: - self.q.append(w) - self.onQueue[w] = True - - if self.cost[w] >= G.get_V(): - self.hasNegativeCycle = True - return + def __init__(self, G, s): + self.distTo = [float("inf") for _ in range(G.get_V())] + self.edgeTo = [None for _ in range(G.get_V())] + self.onQueue = [False for _ in range(G.get_V())] + self.cost = [0 for _ in range(G.get_V())] + self.s = s + self.hasNegativeCycle = False + + self.distTo[s] = 0.0 + self.q = [s] + self.onQueue[s] = True + + while self.q: + v = self.q.pop(0) + self.onQueue[v] = False + self.relax(G, v) + + def distTo(self, v): + return self.distTo[v] + + def hasPathTo(self, v): + return self.distTo[v] != float("inf") + + def pathTo(self, v): + if not self.hasPathTo(v): + return None + path = [] + e = self.edgeTo[v] + while e is not None: + path.append(e) + e = self.edgeTo[e.from_vertex()] + return path + + def hasNegativeCycle(self): + return self.hasNegativeCycle + + def relax(self, G, v): + for e in G.get_adj(v): + w = e.to_vertex() + if self.distTo[w] > self.distTo[v] + e.get_weight(): + self.distTo[w] = self.distTo[v] + e.get_weight() + self.edgeTo[w] = e + self.cost[w] += 1 + + if not self.onQueue[w]: + self.q.append(w) + self.onQueue[w] = True + + if self.cost[w] >= G.get_V(): + self.hasNegativeCycle = True + return -

    - Find A Negative Cycle -

    -

    If there is a negative cycle, Bellman-Ford gets stuck in loop, - updating distTo[] and edgeTo[] entries of vertices in the cycle.

    -

    If any vertex v is updated in phase V, there exists a negative - cycle (and can trace back edgeTo[v] entries to find it).

    -

    - Application - Arbitrage Detection - -

    +

    Find A Negative Cycle

    +

    If there is a negative cycle, Bellman-Ford gets stuck in loop, updating distTo[] and edgeTo[] entries of + vertices in the cycle.

    +

    If any vertex v is updated in phase V, there exists a negative cycle (and can trace back edgeTo[v] + entries to find it).

    +

    Application - Arbitrage Detection

    Currency exchange graph.

  • @@ -2984,30 +2787,14 @@ Arbitrage Detection -

    Let weight of edge - v->w - be - - ln - (exchange rate from currency - v - to - w - ). -

    +

    Let weight of edge v->w be - ln (exchange rate from currency + v to w).

    -

    Multiplication turns to addition; - \gt 1 - turns to - \lt 0. -

    +

    Multiplication turns to addition; \gt 1 turns to \lt 0.

    -

    Find a directed cycle whose sum of edge weights is - \lt 0 - - (negative cycle). -

    +

    Find a directed cycle whose sum of edge weights is \lt 0 (negative cycle).