All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jgrapht.alg.shortestpath.FloydWarshallShortestPaths Maven / Gradle / Ivy

The newest version!
/*
 * (C) Copyright 2009-2023, by Tom Larkworthy and Contributors.
 *
 * JGraphT : a free Java graph-theory library
 *
 * See the CONTRIBUTORS.md file distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0, or the
 * GNU Lesser General Public License v2.1 or later
 * which is available at
 * http://www.gnu.org/licenses/old-licenses/lgpl-2.1-standalone.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR LGPL-2.1-or-later
 */
package org.jgrapht.alg.shortestpath;

import org.jgrapht.*;
import org.jgrapht.alg.util.*;
import org.jgrapht.graph.*;
import org.jgrapht.util.*;

import java.util.*;

/**
 * The Floyd-Warshall algorithm.
 * 
 * 

* The Floyd-Warshall algorithm * finds all shortest paths (all $n^2$ of them) in $O(n^3)$ time. Note that during construction * time, no computations are performed! All computations are performed the first time one of the * member methods of this class is invoked. The results are stored, so all subsequent calls to the * same method are computationally efficient. * * @param the graph vertex type * @param the graph edge type * * @author Tom Larkworthy * @author Soren Davidsen ([email protected]) * @author Joris Kinable * @author Dimitrios Michail */ public class FloydWarshallShortestPaths extends BaseShortestPathAlgorithm { private final List vertices; private final List degrees; private final Map vertexIndices; // minimum vertex with degree at least 1 private final int minDegreeOne; // minimum vertex with degree at least 2 private final int minDegreeTwo; private double[][] d = null; private Object[][] backtrace = null; private Object[][] lastHopMatrix = null; /** * Create a new instance of the Floyd-Warshall all-pairs shortest path algorithm. * * @param graph the input graph */ public FloydWarshallShortestPaths(Graph graph) { super(graph); /* * Sort vertices by degree in ascending order and index them. Also compute the minimum * vertex which has degree at least one and at least two. */ this.vertices = new ArrayList<>(graph.vertexSet()); Collections.sort(vertices, VertexDegreeComparator.of(graph)); this.degrees = new ArrayList<>(); this.vertexIndices = CollectionUtil.newHashMapWithExpectedSize(this.vertices.size()); int i = 0; int minDegreeOne = vertices.size(); int minDegreeTwo = vertices.size(); for (V vertex : vertices) { vertexIndices.put(vertex, i); int degree = graph.degreeOf(vertex); degrees.add(degree); if (degree > 1) { if (i < minDegreeOne) { minDegreeOne = i; } if (i < minDegreeTwo) { minDegreeTwo = i; } } else if (i < minDegreeOne && degree == 1) { minDegreeOne = i; } ++i; } this.minDegreeOne = minDegreeOne; this.minDegreeTwo = minDegreeTwo; } /** * Get the total number of shortest paths. Does not count the paths from a vertex to itself. * * @return total number of shortest paths */ public int getShortestPathsCount() { lazyCalculateMatrix(); // count shortest paths int n = vertices.size(); int nShortestPaths = 0; for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { if (i != j && Double.isFinite(d[i][j])) { nShortestPaths++; } } } return nShortestPaths; } /** * {@inheritDoc} */ @Override public GraphPath getPath(V a, V b) { if (!graph.containsVertex(a)) { throw new IllegalArgumentException(GRAPH_MUST_CONTAIN_THE_SOURCE_VERTEX); } if (!graph.containsVertex(b)) { throw new IllegalArgumentException(GRAPH_MUST_CONTAIN_THE_SINK_VERTEX); } lazyCalculateMatrix(); int vA = vertexIndices.get(a); int vB = vertexIndices.get(b); if (backtrace[vA][vB] == null) { // No path exists return createEmptyPath(a, b); } // Reconstruct the path List edges = new ArrayList<>(); V u = a; while (!u.equals(b)) { int vU = vertexIndices.get(u); E e = TypeUtil.uncheckedCast(backtrace[vU][vB]); edges.add(e); u = Graphs.getOppositeVertex(graph, e, u); } return new GraphWalk<>(graph, a, b, null, edges, d[vA][vB]); } /** * {@inheritDoc} */ @Override public double getPathWeight(V source, V sink) { if (!graph.containsVertex(source)) { throw new IllegalArgumentException(GRAPH_MUST_CONTAIN_THE_SOURCE_VERTEX); } if (!graph.containsVertex(sink)) { throw new IllegalArgumentException(GRAPH_MUST_CONTAIN_THE_SINK_VERTEX); } lazyCalculateMatrix(); return d[vertexIndices.get(source)][vertexIndices.get(sink)]; } /** * {@inheritDoc} */ @Override public SingleSourcePaths getPaths(V source) { return new FloydWarshallSingleSourcePaths(source); } /** * Returns the first hop, i.e., the second node on the shortest path from $a$ to $b$. Lookup * time is $O(1)$. If the shortest path from $a$ to $b$ is $a,c,d,e,b$, this method returns $c$. * If the next invocation would query the first hop on the shortest path from $c$ to $b$, vertex * $d$ would be returned, etc. This method is computationally cheaper than calling * {@link #getPath(Object, Object)} and then reading the first vertex. * * @param a source vertex * @param b target vertex * @return next hop on the shortest path from a to b, or null when there exists no path from $a$ * to $b$. */ public V getFirstHop(V a, V b) { lazyCalculateMatrix(); int vA = vertexIndices.get(a); int vB = vertexIndices.get(b); if (backtrace[vA][vB] == null) { // No path exists return null; } else { E e = TypeUtil.uncheckedCast(backtrace[vA][vB]); return Graphs.getOppositeVertex(graph, e, a); } } /** * Returns the last hop, i.e., the second to last node on the shortest path from $a$ to $b$. * Lookup time is $O(1)$. If the shortest path from $a$ to $b$ is $a,c,d,e,b$, this method * returns $e$. If the next invocation would query the next hop on the shortest path from $c$ to * $e$, vertex $d$ would be returned, etc. This method is computationally cheaper than calling * {@link #getPath(Object, Object)} and then reading the vertex. The first invocation of this * method populates a last hop matrix. * * @param a source vertex * @param b target vertex * @return last hop on the shortest path from $a$ to $b$, or null when there exists no path from * $a$ to $b$. */ public V getLastHop(V a, V b) { lazyCalculateMatrix(); int vA = vertexIndices.get(a); int vB = vertexIndices.get(b); if (backtrace[vA][vB] == null) { // No path exists return null; } else { populateLastHopMatrix(); E e = TypeUtil.uncheckedCast(lastHopMatrix[vA][vB]); return Graphs.getOppositeVertex(graph, e, b); } } /** * Calculates the matrix of all shortest paths, but does not populate the last hops matrix. */ private void lazyCalculateMatrix() { if (d != null) { // already done return; } int n = vertices.size(); // init the backtrace matrix backtrace = new Object[n][n]; // initialize matrix, 0 d = new double[n][n]; for (int i = 0; i < n; i++) { Arrays.fill(d[i], Double.POSITIVE_INFINITY); } // initialize matrix, 1 for (int i = 0; i < n; i++) { d[i][i] = 0.0; } // initialize matrix, 2 if (graph.getType().isUndirected()) { for (E edge : graph.edgeSet()) { V source = graph.getEdgeSource(edge); V target = graph.getEdgeTarget(edge); if (!source.equals(target)) { int v1 = vertexIndices.get(source); int v2 = vertexIndices.get(target); double edgeWeight = graph.getEdgeWeight(edge); if (Double.compare(edgeWeight, d[v1][v2]) < 0) { d[v1][v2] = d[v2][v1] = edgeWeight; backtrace[v1][v2] = edge; backtrace[v2][v1] = edge; } } } } else { // This works for both Directed and Mixed graphs! Iterating over // the arcs and querying source/sink does not suffice for graphs // which contain both edges and arcs for (V v1 : graph.vertexSet()) { int i1 = vertexIndices.get(v1); for (E e : graph.outgoingEdgesOf(v1)) { V v2 = Graphs.getOppositeVertex(graph, e, v1); if (!v1.equals(v2)) { int i2 = vertexIndices.get(v2); double edgeWeight = graph.getEdgeWeight(e); if (Double.compare(edgeWeight, d[i1][i2]) < 0) { d[i1][i2] = edgeWeight; backtrace[i1][i2] = e; } } } } } // run fw alg for (int k = minDegreeTwo; k < n; k++) { for (int i = minDegreeOne; i < n; i++) { if (i == k) { continue; } for (int j = minDegreeOne; j < n; j++) { if (i == j || j == k) { continue; } double sumIKKJ = d[i][k] + d[k][j]; if (Double.compare(sumIKKJ, d[i][j]) < 0) { d[i][j] = sumIKKJ; backtrace[i][j] = backtrace[i][k]; } } } } } /** * Populate the last hop matrix, using the earlier computed backtrace matrix. */ private void populateLastHopMatrix() { lazyCalculateMatrix(); if (lastHopMatrix != null) { return; } // Initialize matrix int n = vertices.size(); lastHopMatrix = new Object[n][n]; // Populate matrix for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { if (i == j || lastHopMatrix[i][j] != null || backtrace[i][j] == null) { continue; } // Reconstruct the path from i to j V u = vertices.get(i); V b = vertices.get(j); while (!u.equals(b)) { int vU = vertexIndices.get(u); E e = TypeUtil.uncheckedCast(backtrace[vU][j]); V other = Graphs.getOppositeVertex(graph, e, u); lastHopMatrix[i][vertexIndices.get(other)] = e; u = other; } } } } class FloydWarshallSingleSourcePaths implements SingleSourcePaths { private final V source; public FloydWarshallSingleSourcePaths(V source) { this.source = source; } @Override public Graph getGraph() { return graph; } @Override public V getSourceVertex() { return source; } @Override public double getWeight(V sink) { return FloydWarshallShortestPaths.this.getPathWeight(source, sink); } @Override public GraphPath getPath(V sink) { return FloydWarshallShortestPaths.this.getPath(source, sink); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy