0

I used Johnson's algorithm to find the shortest path that passes through some arbitrary sequences of vertices. You can find more relevant information here:problem description

Here is how I solve this problem, I first use Johnson's algorithm to get all-pairs shortest paths of a directed graph, then I create a new graph that only contains the specified vertices. Lastly, I implement Dijkstra's algorithm on the newly create graph to find the shortest path from the start to the end. My code looks like this:

package test;

import static org.junit.Assert.assertTrue;


import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import algorithm.graph.Dijkstra;

import algorithm.graph.Johnson;

import dataStructure.Graph.Edge;
import dataStructure.Graph.Vertex;


import dataStructure.Graph;

import java.io.IOException;

public class GraphsTest {

public static void main(String[] args) {
    // TODO Auto-generated method stub

    // Compute all-pairs shortest paths using Johnson's algorithm
    String topofilepath = "D:/Documents/Java/workspace/CodeCraftTest/data/case4/topo.csv";
    final Graph<Integer> graph1 = new Graph<Integer>(Graph.TYPE.DIRECTED);
    final int numOfVertices1 = BuildDirectedGraph(topofilepath, graph1);
    System.out.println(numOfVertices1);
    // System.out.println(graph1.getVertices());
    final Map<Vertex<Integer>, Map<Vertex<Integer>, List<Edge<Integer>>>> path = Johnson.getAllPairsShortestPaths(graph1);
    assertTrue("Directed graph contains a negative weight cycle.", (path != null));
    // Code for debug
    for  (Vertex<Integer> vertex1 : path.keySet()){
        final Map<Vertex<Integer>, List<Edge<Integer>>> map1 = path.get(vertex1);
        for (Vertex<Integer> vertex2 : map1.keySet()){
            final List<Edge<Integer>> list1 = map1.get(vertex2);
            final Iterator<Edge<Integer>> iter1 = list1.iterator();
            while (iter1.hasNext()){
                Edge<Integer> e1 = (Edge<Integer>) iter1.next();
                System.out.println(e1);
            }
        }
    }

    // Create a new graph that only contains the specified vertices in demand file
    final Graph<Integer> graph2 = new Graph<Integer>(Graph.TYPE.DIRECTED);
    String demandfilepath = "D:/Documents/Java/workspace/CodeCraftTest/data/case4/demand.csv";
    final int numOfVertices2 = BuildDemandedGraph(demandfilepath, path, graph2);
    System.out.println(numOfVertices2);
    // System.out.println(graph2.getVertices());

    // Implement Dijkstra's algorithm on the specified directed graph
     BufferedReader b = null;
     String line = "";
     String csvSplitBy = ",";
     int startID;
     int endID;

     final Graph.Vertex<Integer> start;
     final Graph.Vertex<Integer> end;

     try{
         b = new BufferedReader(new FileReader(demandfilepath));
         if ((line = b.readLine()) != null){
             String[] entry = line.split(csvSplitBy);
             startID = Integer.parseInt(entry[0]);
             endID = Integer.parseInt(entry[1]);
             start = new Graph.Vertex<Integer>(startID);
             end = new Graph.Vertex<Integer>(endID);
             final Graph.CostPathPair<Integer> pair1 = Dijkstra.getShortestPath(graph2, start, end);
             // System.out.println(pair1.getPath());
         }
     }
     catch(FileNotFoundException e){
            e.printStackTrace();
        }
        catch(IOException e){
            e.printStackTrace();
        }
        finally{
            if (b != null){
                try{
                    b.close();
                }
                catch(IOException e){
                    e.printStackTrace();
                }
            }
        }
}

private static int BuildDirectedGraph(String topofilepath, Graph<Integer> graph) {
    String csvFile = topofilepath;
    BufferedReader br = null;
    String line = "";
    String csvSplitBy = ",";
    String LinkID;
    String SourceID;
    String DestinationID;
    String Cost;
    int linkID;
    int sourceID;
    int destinationID;
    int cost;

    try{
        br = new BufferedReader(new FileReader(csvFile));
        while ((line = br.readLine()) != null)
        {
            String[] entry = line.split(csvSplitBy);
            LinkID = entry[0];
            SourceID = entry[1];
            DestinationID = entry[2];
            Cost = entry[3];

            linkID = Integer.parseInt(LinkID);
            sourceID = Integer.parseInt(SourceID);
            destinationID = Integer.parseInt(DestinationID);
            cost = Integer.parseInt(Cost);

            final Graph.Vertex<Integer> v1 = new Graph.Vertex<Integer>(sourceID);
            final Graph.Vertex<Integer> v2 = new Graph.Vertex<Integer>(destinationID);
            final Graph.Edge<Integer> edge = new Graph.Edge<Integer>(cost, v1, v2);
            if(graph.getVertices().contains(v1) == false)
            {
                graph.addVertex(v1);
            }
            if(graph.getVertices().contains(v2) == false)
            {
                graph.addVertex(v2);
            }
            graph.addEdge(edge);
        }
    }
    catch(FileNotFoundException e)
    {
        e.printStackTrace();
    }
    catch(IOException e)
    {
        e.printStackTrace();
    }
    finally
    {
        if (br != null)
        {
            try{
                br.close();
            }
            catch(IOException e){
                e.printStackTrace();
            }
        }
    }
    return graph.getVertices().size();
}

private static int BuildDemandedGraph(String demandfilepath, Map<Vertex<Integer>, Map<Vertex<Integer>, List<Edge<Integer>>>> path, Graph<Integer> graph)
{
    String csvFile = demandfilepath;
    BufferedReader br = null;
    String line = "";
    String csvSplitBy = ",";

    int startID;
    int endID;
    int cost = 0;

    try{
        br = new BufferedReader(new FileReader(csvFile));
        if ((line = br.readLine()) != null)
        {
            String[] entry = line.split(csvSplitBy);
            startID = Integer.parseInt(entry[0]);
            endID = Integer.parseInt(entry[1]);
            final Graph.Vertex<Integer> start = new Graph.Vertex<Integer>(startID);
            final Graph.Vertex<Integer> end = new Graph.Vertex<Integer>(endID);
            if(graph.getVertices().contains(start) == false)
            {
                graph.addVertex(start);
            }
            if(graph.getVertices().contains(end) == false)
            {
                graph.addVertex(end);
            }

            String[] seqOfVertices = entry[2].split("\\|");
            // System.out.println(seqOfVertices.length);
            for (int i = 0; i < seqOfVertices.length; i++)
            {
                int vertexID = Integer.parseInt(seqOfVertices[i]);
                final Graph.Vertex<Integer> v = new Graph.Vertex<Integer>(vertexID);
                if (graph.getVertices().contains(v) == false)
                {
                    graph.addVertex(v);
                }
            }
            for (Graph.Vertex<Integer> v1 : graph.getVertices()){
                for (Graph.Vertex<Integer> v2 : graph.getVertices()){
                    // debug
                    // System.out.println(v1.getValue());
                    for (Vertex<Integer> vertex : path.keySet()){
                        if (v1.equals(vertex)){
                            final Map<Vertex<Integer>, List<Edge<Integer>>> map1 = path.get(v1);
                            for (Vertex<Integer> v : map1.keySet()){
                                if (v.equals(v2)){
                                    final List<Edge<Integer>> list1 = map1.get(v);
                                    final Iterator<Edge<Integer>> iter1 = list1.iterator();
                                    while(iter1.hasNext()){
                                        Edge<Integer> e1 = (Edge<Integer>) iter1.next();
                                        System.out.println(e1.getCost());
                                        cost += e1.getCost();
                                    }
                                }
                            }
                            final Graph.Edge<Integer> edge = new Graph.Edge<Integer>(cost, v1, v2);
                            graph.addEdge(edge);
                        }
                        if (v2.equals(vertex)){
                            final Map<Vertex<Integer>, List<Edge<Integer>>> map2 = path.get(v2);
                            for (Vertex<Integer> v : map2.keySet()){
                                if (v.equals(v1)){
                                    final List<Edge<Integer>> list2 = map2.get(v);
                                    final Iterator<Edge<Integer>> iter2 = list2.iterator();
                                    while(iter2.hasNext()){
                                        Edge<Integer> e2 = (Edge<Integer>) iter2.next();
                                        cost += e2.getCost();
                                    }
                                }
                            }
                            final Graph.Edge<Integer> edge = new Graph.Edge<Integer>(cost, v2, v1);
                            graph.addEdge(edge);
                        }
                    }
                }
            }
        }
    }
    catch(FileNotFoundException e){
        e.printStackTrace();
    }
    catch(IOException e){
        e.printStackTrace();
    }
    finally{
        if (br != null){
            try{
                br.close();
            }
            catch(IOException e){
                e.printStackTrace();
            }
        }
    }
    return graph.getVertices().size();
}
}

Johnson's algorithm is written by Justin Wetherell, but it doesn't work in this case, I have verified that the graph is properly created, but the returned all shortest paths are a empty list. I can not find the problem. Can you help me spot some bugs that might cause this problem.

package algorithm.graph;


import java.util.HashMap;
import java.util.List;
import java.util.Map;

import dataStructure.Graph;

/**
 * Johnson's algorithm is a way to find the shortest paths between all pairs of
 * vertices in a sparse directed graph. It allows some of the edge weights to be
 * negative numbers, but no negative-weight cycles may exist.
 * 
 * Worst case: O(V^2 log V + VE)
 * 
 * @author Justin Wetherell <phishman3579@gmail.com>
 */

public class Johnson {

private Johnson() { }

public static Map<Graph.Vertex<Integer>, Map<Graph.Vertex<Integer>, List<Graph.Edge<Integer>>>> getAllPairsShortestPaths(Graph<Integer> g) {
    if (g == null)
        throw (new NullPointerException("Graph must be non-NULL."));

    // First, a new node 'connector' is added to the graph, connected by zero-weight edges to each of the other nodes.
    final Graph<Integer> graph = new Graph<Integer>(g);
    final Graph.Vertex<Integer> connector = new Graph.Vertex<Integer>(Integer.MAX_VALUE);

    // Add the connector Vertex to all edges.
    for (Graph.Vertex<Integer> v : graph.getVertices()) {
        final int indexOfV = graph.getVertices().indexOf(v);
        final Graph.Edge<Integer> edge = new Graph.Edge<Integer>(0, connector, graph.getVertices().get(indexOfV));
        connector.addEdge(edge);
        graph.getEdges().add(edge);
    }

    graph.getVertices().add(connector);

    // Second, the Bellman鈥揊ord algorithm is used, starting from the new vertex 'connector', to find for each vertex 'v'
    // the minimum weight h(v) of a path from 'connector' to 'v'. If this step detects a negative cycle, the algorithm is terminated.
    final Map<Graph.Vertex<Integer>, Graph.CostPathPair<Integer>> costs = BellmanFord.getShortestPaths(graph, connector);

    // Next the edges of the original graph are re-weighted using the values computed by the Bellman鈥揊ord algorithm: an edge 
    // from u to v, having length w(u,v), is given the new length w(u,v) + h(u) 鈭� h(v).
    for (Graph.Edge<Integer> e : graph.getEdges()) {
        final int weight = e.getCost();
        final Graph.Vertex<Integer> u = e.getFromVertex();
        final Graph.Vertex<Integer> v = e.getToVertex();

        // Don't worry about the connector
        if (u.equals(connector) || v.equals(connector)) 
            continue;

        // Adjust the costs
        final int uCost = costs.get(u).getCost();
        final int vCost = costs.get(v).getCost();
        final int newWeight = weight + uCost - vCost;
        e.setCost(newWeight);
    }

    // Finally, 'connector' is removed, and Dijkstra's algorithm is used to find the shortest paths from each node (s) to every 
    // other vertex in the re-weighted graph.
    final int indexOfConnector = graph.getVertices().indexOf(connector);
    graph.getVertices().remove(indexOfConnector);
    for (Graph.Edge<Integer> e : connector.getEdges()) {
        final int indexOfConnectorEdge = graph.getEdges().indexOf(e);
        graph.getEdges().remove(indexOfConnectorEdge);
    }

    final Map<Graph.Vertex<Integer>, Map<Graph.Vertex<Integer>, List<Graph.Edge<Integer>>>> allShortestPaths = new HashMap<Graph.Vertex<Integer>, Map<Graph.Vertex<Integer>, List<Graph.Edge<Integer>>>>();
    for (Graph.Vertex<Integer> v : graph.getVertices()) {
        final Map<Graph.Vertex<Integer>, Graph.CostPathPair<Integer>> costPaths = Dijkstra.getShortestPaths(graph, v);
        final Map<Graph.Vertex<Integer>, List<Graph.Edge<Integer>>> paths = new HashMap<Graph.Vertex<Integer>, List<Graph.Edge<Integer>>>();
        for (Graph.Vertex<Integer> v2 : costPaths.keySet()) {
            final Graph.CostPathPair<Integer> pair = costPaths.get(v2);
            paths.put(v2, pair.getPath());
        }
        allShortestPaths.put(v, paths);
    }
    return allShortestPaths;
}
}
Community
  • 1
  • 1
Jun Liu
  • 41
  • 1
  • 2
  • 2
    Maybe ask Justin Wetherell? You've got his email address, after all. – Andy Turner Apr 05 '16 at 08:38
  • 2
    Well, first and most obvious question: does your graph have negative weights? Your question could be better if it was more specific than "here is my code, what is the problem?" – Chris Apr 05 '16 at 08:48
  • Thank you for your comment, the graph does not contain negative weights, and the problem is Johnson's algorithm return nothing useful, it returns an empty map, with all path lists being empty. I have send an email to contain Justin Wetherell, I hope he will reply. I am new to stackoverflow and I will learn how to properly ask and answer questions on the forum. – Jun Liu Apr 06 '16 at 00:39

0 Answers0