Here is my implementation of Dijkstra, based on pseudocode provided in class. (This was a school assignment, but the project has already been turned in by a teammate. I'm just trying to figure out why my version does not work.)
When I create my own graph with the same graph info as the txt file, it gives me the correct output- the shortest path to each vertex from a given source. When I read in a text file, it does not. It reads in the file and prints the correct adjacency list, but does not give the shortest paths.
Here's where it goes wrong when it runs on the file: on the first iteration of relax, it updates the adjacent vertex distances and parent, but returns to the dijkstra method and the distance/parent are no longer updated. Why is that?
The provided txt file looks like this:
4
0 1,1 3,2
1 2,4
2 1,6 4,7
3 0,3 1,9 2,2
4 0,10 3,5
Sorry if this is a mess, I'm learning!
import java.util.PriorityQueue;
import java.util.Collections;
import java.util.Scanner;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.LinkedList;
import java.io.IOException;
/** Class to implement Dijkstra algorithm.
* Includes classes for Vertex, Edge and Graph.
*/
public class Dijkstra {
private LinkedList<Vertex> shortestPath;
private LinkedList<Vertex> path;
private PriorityQueue<Vertex> pq;
private PriorityQueue<Vertex> pq2;
static final int INFINITY = Integer.MAX_VALUE;
/** Main method reads in a txt file and prints
* the shortest path to each vertex from a given source.
* #throws FileNotFoundException
* #throws IOException
*/
public static void main(String[] args) throws FileNotFoundException,
IOException {
// ourGraph is a sample graph to test output
// vertices and edges are the same as txt file
Vertex v0 = new Vertex("0");
Vertex v1 = new Vertex("1");
Vertex v2 = new Vertex("2");
Vertex v3 = new Vertex("3");
Vertex v4 = new Vertex("4");
Graph ourGraph = new Graph(v4);
ourGraph.addVertex(v0);
ourGraph.addVertex(v1);
ourGraph.addVertex(v2);
ourGraph.addVertex(v3);
ourGraph.addVertex(v4);
ourGraph.addEdge(v0, v1, 1);
ourGraph.addEdge(v0, v3, 2);
ourGraph.addEdge(v1, v2, 4);
ourGraph.addEdge(v2, v1, 6);
ourGraph.addEdge(v2, v4, 7);
ourGraph.addEdge(v3, v0, 3);
ourGraph.addEdge(v3, v1, 9);
ourGraph.addEdge(v3, v2, 2);
ourGraph.addEdge(v4, v0, 10);
ourGraph.addEdge(v4, v3, 5);
ourGraph.printGraph(); // prints correct output
Dijkstra d = new Dijkstra();
d.getDijkstra(ourGraph, v4); // runs Dijkstra with v4 as source
for (Vertex v : ourGraph.nodes) {
d.printShortestPath(ourGraph, v); // correct output for shortest paths
}
Scanner scanner = new Scanner(System.in);
System.out.print("Please enter file name: ");
String fileName = scanner.nextLine();
Scanner file = new Scanner(new File(fileName));
String sourceID = file.nextLine();
Graph g = new Graph(new Vertex(sourceID));
while (file.hasNext()) {
String[] currentLine = file.nextLine().split(" |,");
Vertex vertex = new Vertex(currentLine[0]);
g.addVertex(vertex);
// set the graph's source vertex
if (vertex.getName().equals(sourceID)) {
g.source = vertex;
}
// read current line for adjacent vertices and their edge weights
for (int i = 1; i < currentLine.length; i++) {
g.addEdge(vertex, new Vertex(currentLine[i]),
Integer.parseInt(currentLine[++i]));
}
}
g.printGraph(); // prints expected graph
Dijkstra d2 = new Dijkstra();
d2.getDijkstra(g, g.source);
for (Vertex vx : g.nodes) {
d2.printShortestPath(g, vx);
}
}
/* Vertex class with fields for name, parent,
* distance, and edge list.
*/
static class Vertex implements Comparable<Vertex> {
private String name;
private Vertex p;
private int d;
private LinkedList<Edge> edgeList;
Vertex(String n) {
this.name = n;
this.p = null;
this.d = INFINITY;
edgeList = new LinkedList<>();
}
public String getName() {
return name;
}
public LinkedList<Edge> getEdges() {
return edgeList;
}
#Override
public int compareTo(Vertex other) {
return Integer.compare(this.d, other.d);
}
}
static class Edge {
private int weight;
private Vertex source;
private Vertex destination;
Edge(Vertex d, int w) {
this.destination = d;
this.weight = w;
}
public int getWeight() {
return weight;
}
public Vertex getSource() {
return source;
}
public Vertex getDestination() {
return destination;
}
}
static class Graph {
private LinkedList<Vertex> nodes;
private Vertex source;
Graph(Vertex s) {
nodes = new LinkedList<>();
this.source = s;
}
public void addSource(Vertex s) {
this.source = s;
}
public void addEdge(Vertex s, Vertex d, int weight) {
s.getEdges().add(new Edge(d, weight));
}
public void addVertex(Vertex v) {
nodes.add(v);
}
public void printGraph() {
for (Vertex v : nodes) {
System.out.print("vertex: " + v.getName() + ": ");
for (Edge e : v.getEdges()) {
System.out.print(e.getDestination().getName()
+ "," + e.getWeight() + " ");
}
System.out.print("\n");
}
}
}
/** method to calculate shortest path using
* Dijkstra's algorithm.
* #param graph with vertices and edges
* #param source as starting vertex
* #return a LinkedList of vertices as shortest path
*/
public LinkedList<Vertex> getDijkstra(Graph graph, Vertex source) {
initializeSingleSource(graph, source);
shortestPath = new LinkedList<Vertex>();
pq = new PriorityQueue<Vertex>();
pq.addAll(graph.nodes);
while (!pq.isEmpty()) {
// used a second pq to re-min-heapify after min is removed
pq2 = new PriorityQueue<Vertex>();
pq2.addAll(pq);
Vertex u = pq2.poll();
if (!shortestPath.contains(u)) {
shortestPath.add(u);
}
for (Edge e : u.getEdges()) {
relax(u, e.getDestination(), e.getWeight());
}
pq.remove(u);
}
return shortestPath;
}
/** initializes each vertex distance to infinity and
* each parent to null. Sets source distance to 0.
* #param graph for input
* #param source is source vertex of graph
*/
public void initializeSingleSource(Graph graph, Vertex source) {
for (Vertex v : graph.nodes) {
v.d = INFINITY;
}
source.d = 0;
}
/** Relax checks if the distance of the destination
* vertex is greater than the distance of the start plus
* the edge weight and updates distance and parent attributes.
* #param u vertex is start
* #param v is destination vertex
* #param weight is edge weight
*/
public void relax(Vertex u, Vertex v, int weight) {
if (v.d > u.d + weight) {
v.d = u.d + weight;
v.p = u;
}
}
/** getPath puts shortest path in order for a given target.
* #param g for graph input
* #param target is target vertex of shortest path from the
* graph's source
* #return LinkedList of shortest path
*/
public LinkedList<Vertex> getPath(Graph g, Vertex target) {
LinkedList<Vertex> path = new LinkedList<Vertex>();
Vertex step = target;
int i = shortestPath.indexOf(step);
while (step.p != null) {
path.add(step);
step = step.p;
}
Collections.reverse(path);
return path;
}
/** prints a formatted list of a single vertex's shortest path.
* from the graph's source
* #param g is graph
* #param target is target vertex of shortest path
*/
public void printShortestPath(Graph g, Vertex target) {
shortestPath = getPath(g, target);
System.out.print(target.getName() + ": ");
for (Vertex v : shortestPath) {
System.out.print(v.getName() + " ");
}
System.out.print("\n");
}
}
First the structure of the file is as follows:
4
0 1,1 3,2
1 2,4
2 1,6 4,7
3 0,3 1,9 2,2
4 0,10 3,5
Now for your problem here, the cause of this problem is the creation of new vertices when reading the file:
while (file.hasNext()) {
String[] currentLine = file.nextLine().split(" |,");
Vertex vertex = new Vertex(currentLine[0]);
g.addVertex(vertex);
// set the graph's source vertex
if (vertex.getName().equals(sourceID)) {
g.source = vertex;
}
// read current line for adjacent vertices and their edge weights
for (int i = 1; i < currentLine.length; i++) {
g.addEdge(vertex, new Vertex(currentLine[i]),
Integer.parseInt(currentLine[++i]));
}
}
when you do:
g.addEdge(vertex, new Vertex(currentLine[i]), Integer.parseInt(currentLine[++i]));
here you create a new vertex with the name currentLine[i] even if there's already one with the same name in the graph, it'll create a new one and will not magically reuse the existing one.
To remedy this problem, add a function in your Graph class for example to get an existing vertex by name or create one if it's not found.
In your Graph class add the following method:
public Vertex getOrCreateVertex(String name) {
for (Vertex v : nodes) {
if (v.name.equals(name)) {
return v;
}
}
Vertex newVertex = new Vertex(name);
nodes.add(newVertex);
return newVertex;
}
And change the part reading the file as follows:
while (file.hasNext()) {
String[] currentLine = file.nextLine().split(" |,");
Vertex vertex = g.getOrCreateVertex(currentLine[0]);
// set the graph's source vertex
if (vertex.getName().equals(sourceID)) {
g.source = vertex;
}
// read current line for adjacent vertices and their edge weights
for (int i = 1; i < currentLine.length; i++) {
g.addEdge(vertex, g.getOrCreateVertex(currentLine[i]),
Integer.parseInt(currentLine[++i]));
}
}
Related
I using this exact code for this. I modified it a little. So far I added a start and end node index to the calculateShortestDistances() method. Also the path ArrayList for collecting the path node indexes. Also: new to Java...
How do I collect the indexes of nodes in the path ArrayList?
I just can't come up with the solution on a level that I am not even positive this code could do what I want. I only have intuition on my side and little time.
What I tried:
Adding the nextNode value to the list then removing it if it was not
a shorter distance.
Adding the neighbourIndex to the list then removing it if it was not a shorter distance.
I made a Path.java with ArrayList but that was went nowhere (it was a class with a public variable named path) but it went nowhere.
Main.java:
public class Main {
public static void main(String[] args) {
Edge[] edges = {
new Edge(0, 2, 1), new Edge(0, 3, 4), new Edge(0, 4, 2),
new Edge(0, 1, 3), new Edge(1, 3, 2), new Edge(1, 4, 3),
new Edge(1, 5, 1), new Edge(2, 4, 1), new Edge(3, 5, 4),
new Edge(4, 5, 2), new Edge(4, 6, 7), new Edge(4, 7, 2),
new Edge(5, 6, 4), new Edge(6, 7, 5)
};
Graph g = new Graph(edges);
g.calculateShortestDistances(4,6);
g.printResult(); // let's try it !
System.out.println(g.path);
}
}
Graph.java:
This is the Graph.java file. Here I added a sAt and eAt variable, so I can tell it what path I am after. Also I created a public path ArrayList, where I intend to collect the path.
import java.util.ArrayList;
// now we must create graph object and implement dijkstra algorithm
public class Graph {
private Node[] nodes;
private int noOfNodes;
private Edge[] edges;
private int noOfEdges;
private int sAt;
private int eAt;
public ArrayList<Integer> path = new ArrayList<>();
public Graph(Edge[] edges) {
this.edges = edges;
// create all nodes ready to be updated with the edges
this.noOfNodes = calculateNoOfNodes(edges);
this.nodes = new Node[this.noOfNodes];
for (int n = 0; n < this.noOfNodes; n++) {
this.nodes[n] = new Node();
}
// add all the edges to the nodes, each edge added to two nodes (to and from)
this.noOfEdges = edges.length;
for (int edgeToAdd = 0; edgeToAdd < this.noOfEdges; edgeToAdd++) {
this.nodes[edges[edgeToAdd].getFromNodeIndex()].getEdges().add(edges[edgeToAdd]);
this.nodes[edges[edgeToAdd].getToNodeIndex()].getEdges().add(edges[edgeToAdd]);
}
}
private int calculateNoOfNodes(Edge[] edges) {
int noOfNodes = 0;
for (Edge e : edges) {
if (e.getToNodeIndex() > noOfNodes)
noOfNodes = e.getToNodeIndex();
if (e.getFromNodeIndex() > noOfNodes)
noOfNodes = e.getFromNodeIndex();
}
noOfNodes++;
return noOfNodes;
}
public void calculateShortestDistances(int startAt, int endAt) {
// node 0 as source
this.sAt = startAt;
this.eAt = endAt;
this.nodes[startAt].setDistanceFromSource(0);
int nextNode = startAt;
// visit every node
for (int i = 0; i < this.nodes.length; i++) {
// loop around the edges of current node
ArrayList<Edge> currentNodeEdges = this.nodes[nextNode].getEdges();
for (int joinedEdge = 0; joinedEdge < currentNodeEdges.size(); joinedEdge++) {
int neighbourIndex = currentNodeEdges.get(joinedEdge).getNeighbourIndex(nextNode);
// only if not visited
if (!this.nodes[neighbourIndex].isVisited()) {
int tentative = this.nodes[nextNode].getDistanceFromSource() + currentNodeEdges.get(joinedEdge).getLength();
if (tentative < nodes[neighbourIndex].getDistanceFromSource()) {
nodes[neighbourIndex].setDistanceFromSource(tentative);
}
}
}
// all neighbours checked so node visited
nodes[nextNode].setVisited(true);
// next node must be with shortest distance
nextNode = getNodeShortestDistanced();
}
}
// now we're going to implement this method in next part !
private int getNodeShortestDistanced() {
int storedNodeIndex = 0;
int storedDist = Integer.MAX_VALUE;
for (int i = 0; i < this.nodes.length; i++) {
int currentDist = this.nodes[i].getDistanceFromSource();
if (!this.nodes[i].isVisited() && currentDist < storedDist) {
storedDist = currentDist;
storedNodeIndex = i;
}
}
return storedNodeIndex;
}
// display result
public void printResult() {
String output = "Number of nodes = " + this.noOfNodes;
output += "\nNumber of edges = " + this.noOfEdges;
output += "\nDistance from "+sAt+" to "+eAt+":" + nodes[eAt].getDistanceFromSource();
System.out.println(output);
}
public Node[] getNodes() {
return nodes;
}
public int getNoOfNodes() {
return noOfNodes;
}
public Edge[] getEdges() {
return edges;
}
public int getNoOfEdges() {
return noOfEdges;
}
}
Addittionally here are the Edge.java and the Node.java classes.
Node.java:
import java.util.ArrayList;
public class Node {
private int distanceFromSource = Integer.MAX_VALUE;
private boolean visited;
private ArrayList<Edge> edges = new ArrayList<Edge>(); // now we must create edges
public int getDistanceFromSource() {
return distanceFromSource;
}
public void setDistanceFromSource(int distanceFromSource) {
this.distanceFromSource = distanceFromSource;
}
public boolean isVisited() {
return visited;
}
public void setVisited(boolean visited) {
this.visited = visited;
}
public ArrayList<Edge> getEdges() {
return edges;
}
public void setEdges(ArrayList<Edge> edges) {
this.edges = edges;
}
}
Edge.java
public class Edge {
private int fromNodeIndex;
private int toNodeIndex;
private int length;
public Edge(int fromNodeIndex, int toNodeIndex, int length) {
this.fromNodeIndex = fromNodeIndex;
this.toNodeIndex = toNodeIndex;
this.length = length;
}
public int getFromNodeIndex() {
return fromNodeIndex;
}
public int getToNodeIndex() {
return toNodeIndex;
}
public int getLength() {
return length;
}
// determines the neighbouring node of a supplied node, based on the two nodes connected by this edge
public int getNeighbourIndex(int nodeIndex) {
if (this.fromNodeIndex == nodeIndex) {
return this.toNodeIndex;
} else {
return this.fromNodeIndex;
}
}
}
I know it looks like a homework. Trust me it isn't. On the other hand I have not much time to finish it, that is why I do it at Sunday. Also I am aware how Dijkstra algorithm works, I understand the concept, I can do it on paper. But collecting the path is beyond me.
Thanks for Christian H. Kuhn's and second's comments I managed to come up with the code.
I modified it as follows (I only put in the relevant parts)
Node.java
Here I added a setPredecessor(Integer predecessor) and a getPredecessor() methods to set and get the value of the private variable predecessor (so I follow the original code's style too).
[...]
private int predecessor;
[...]
public int getPredecessor(){
return predecessor;
}
public void setPredecessor(int predecessor){
this.predecessor = predecessor;
}
[...]
Graph.java
Here I created the calculatePath() and getPath() methods. calculatePath() does what the commenters told me to do. The getPath() returns the ArrayLists for others to use.
[...]
private int sAt;
private int eAt;
private ArrayList<Integer> path = new ArrayList<Integer>();
[...]
public void calculateShortestDistances(int startAt, int endAt) {
[...]
if (tentative < nodes[neighbourIndex].getDistanceFromSource()) {
nodes[neighbourIndex].setDistanceFromSource(tentative);
nodes[neighbourIndex].setPredecessor(nextNode);
}
[...]
public void calculatePath(){
int nodeNow = eAt;
while(nodeNow != sAt){
path.add(nodes[nodeNow].getPredecessor());
nodeNow = nodes[nodeNow].getPredecessor();
}
}
public ArrayList<Integer> getPath(){
return path;
}
[...]
Main.java so here I can do this now:
[...]
Graph g = new Graph(edges);
g.calculateShortestDistances(5,8);
g.calculatePath();
String results = "";
ArrayList<Integer> path = g.getPath();
System.out.println(path);
[...]
I know it shows the path backwards, but that is not a problem, as I can always reverse it. The point is: I not only have the the distance from node to node, but the path through nodes too. Thank you for the help.
import java.util.*;
class Graph{
private int numVertices;
class AdjacentNode{
public int parentNodeIndex;
public AdjacentNode next;
public AdjacentNode(int parentNum, AdjacentNode neighbor){
this.parentNodeIndex=parentNum;
next = neighbor;
}
}
class Vertex{
String name;
AdjacentNode neighborList;
public Vertex (String name){
this.name = name;
}
public String toString(){
return "(" + name+ ")";
}
}
Vertex [] adjLists;
public Graph (int n){
adjLists = new Vertex[n];
numVertices=0;
}
public int getNumVertices(){
return numVertices;
}
public void setNumVertices(int v){
numVertices = v;
}
public void add_vertex(String label){
int num = getNumVertices();
setNumVertices(num+1);
adjLists[num]= new Vertex(label);
}
public void add_edge (String startNode, String endNode)
{
int v1 = indexForName(startNode);
int v2 = indexForName(endNode);
adjLists[v1].neighborList = new AdjacentNode(v2,
adjLists[v1].neighborList);
}
int indexForName(String n){
for (int i=0; i< adjLists.length; i++){
if(adjLists[i].name.equals(n)){
return i;
}
}
return -1;
}
public ArrayList<String> getAdjacentNodes(String parentNode){
int v1 = indexForName(parentNode);
ArrayList<String> a= new ArrayList<String>();
System.out.println("The adjacent nodes of " + parentNode + " =>");
for (AdjacentNode neighbor=adjLists[v1].neighborList; neighbor!=null;
neighbor = neighbor.next){
a.add(0, adjLists[neighbor.parentNodeIndex].name);
Collections.sort(a);
}
return a;
}
public void print (){
System.out.println();
for(int i =0; i<adjLists.length; i++){
System.out.print(adjLists[i].name);
for (AdjacentNode neighbor=adjLists[i].neighborList; neighbor!=null;
neighbor = neighbor.next){
System.out.print("-->" + adjLists[neighbor.parentNodeIndex].name);
}
System.out.println("\n");
}
}
}
This is my Graph class and Vertex class to implement an adjacency list of a directed unweighted graph. How do i modify this to implement an adjacency list of a undirected, weighted graph? I want to add a method to my graph class that is
public int getGraphsTotalWeight() in order to get the sum of the weights of all edges in the graph.
My graph class is suppose to have the following methods implemented, which I tried to add onto my code above.
/* Add a new node to the graph. The newly added node will have no edges */
public void add_vertex(String label)
/* Add an edge to the graph connecting node ‘startNode’ to node ‘endNode’. If such edge already exist in the graph, your code should ignore adding. Your code should run in O(|E|) in the worst case. */
public void add_edge(String startNode, String endNode)
/** Returns a list of names of all adjacent Nodes to node parentNode. List should be in alphabetical order. */
public ArrayList<String> getAdjacentNodes(String parentNode)
/** Returns a weight of the graph (i.e. the sum of the weights of all edges in the graph. */
public int getGraphsTotalWeight()
I have a directed network model made up of a set of nodes connected by links that grows with each model iteration. In order to find the "average shortest path" in the final model iteration, I have implemented Dijkstra's algorithm that calculates the shortest path from all nodes to all nodes. To be more specific, the algorithm calculates the shortest path from each of the networks 3,000 nodes to all other 3,000 nodes (if a path exists), roughly 9,000,000 pathlengths and then finds the average path length. When I try this, I run out of memory. I am able to get an average path length up until about 500 nodes, where roughly 250,000 path lengths are calculated in under 12h. My question is, is there any way to improve the code in a way that might make it more efficient? Or is it not feasible to calculate that many paths?
Code below... the algorithm itself is adapted from Vogella http://www.vogella.com/tutorials/JavaAlgorithmsDijkstra/article.html
Nodes in the nework represent trees and edges or links represent nets.
Dijstra Algorithm
package network;
imports...
public class DijkstraAlgorithm {
private Context<Object> context;
private Geography<Object> geography;
private int id;
List<Tree> vertices = Tree.getVertices();
List<Nets> edges = Nets.getEdges();
private Set<Tree> settledNodes;
private Set<Tree> unSettledNodes;
private Map<Tree, Tree> predecessors;
private Map<Tree, Integer> distance;
public DijkstraAlgorithm(Graph graph) {
this.context = context;
this.geography = geography;
this.id = id;
this.vertices = vertices;
this.edges = edges;
}
setters and getters....
public void execute(Tree source){
settledNodes = new HashSet<Tree>();
unSettledNodes = new HashSet<Tree>();
distance = new HashMap<Tree, Integer>();
predecessors = new HashMap<Tree, Tree>();
distance.put(source, 0);
unSettledNodes.add(source);
while (unSettledNodes.size()>0){
Tree node = getMinimum(unSettledNodes);
settledNodes.add(node);
unSettledNodes.remove(node);
findMinimalDistances(node);
}
}
private void findMinimalDistances(Tree node){
List<Tree>adjacentNodes = getNeighbors(node);
for (Tree target: adjacentNodes){
if (getShortestDistance(target)>getShortestDistance(node)+getDistance(node,target)){
distance.put(target, getShortestDistance(node) + getDistance(node, target));
predecessors.put(target, node);
unSettledNodes.add(target);
}
}
}
private int getDistance(Tree node, Tree target){
for (Nets edge: edges){
if (edge.getStartTrees().equals(node) && edge.getEndTrees().equals(target)){
return edge.getId();
}
}
throw new RuntimeException("Should not happen");
}
private List<Tree> getNeighbors(Tree node){
List<Tree> neighbors = new ArrayList<Tree>();
for (Nets edge: edges) {
if(edge.getStartTrees().equals(node) && !isSettled(edge.getEndTrees())){
neighbors.add(edge.getEndTrees());
}
}
return neighbors;
}
private Tree getMinimum(Set<Tree>vertexes){
Tree minimum = null;
for (Tree vertex: vertexes) {
if (minimum == null){
minimum = vertex;
} else {
if (getShortestDistance(vertex)< getShortestDistance(minimum)){
minimum = vertex;
}
}
}
return minimum;
}
private boolean isSettled(Tree vertex){
return settledNodes.contains(vertex);
}
private int getShortestDistance(Tree destination) {
Integer d = distance.get(destination);
if (d == null) {
return Integer.MAX_VALUE;
} else {
return d;
}
}
public LinkedList<Tree> getPath(Tree target){
LinkedList<Tree>path = new LinkedList<Tree>();
Tree step = target;
if(predecessors.get(step)== null){
return null;
}
path.add(step);
while (predecessors.get(step)!=null){
step = predecessors.get(step);
path.add(step);
}
Collections.reverse(path);
return path;
}
}
Graph
package network;
imports...
public class Graph {
private Context<Object> context;
private Geography<Object> geography;
private int id;
List<Tree> vertices = new ArrayList<>();
List<Nets> edges = new ArrayList<>();
List <Integer> intermediateNodes = new ArrayList<>();
public Graph(Context context, Geography geography, int id, List vertices, List edges) {
this.context = context;
this.geography = geography;
this.id = id;
this.vertices = vertices;
this.edges = edges;
}
setters... getters...
//updates graph
#ScheduledMethod(start =1, interval =1, priority =1)
public void countNodesAndVertices() {
this.setVertices(Tree.getVertices());
this.setEdges(Nets.getEdges());
//run Dijkstra at the 400th iteration of network development
#ScheduledMethod(start =400, priority =1)
public void Dijkstra(){
Graph graph2 = new Graph (context, geography, id, vertices, edges);
graph2.setEdges(this.getEdges());
graph2.setVertices(this.getVertices());
for(Tree t: graph2.getVertices()){
}
DijkstraAlgorithm dijkstra = new DijkstraAlgorithm(graph2);
//create list of pathlengths (links, not nodes)
List <Double> pathlengths = new ArrayList<>();
//go through all nodes as starting nodes
for (int i = 0; i<vertices.size();i++){
//find the shortest path to all nodes as end nodes
for (int j = 0; j<vertices.size();j++){
if(i != j){
Tree startTree = vertices.get(i);
Tree endTree = vertices.get(j);
dijkstra.execute(vertices.get(i));
//create a list that contains the path of nodes
LinkedList<Tree> path = dijkstra.getPath(vertices.get(j));
//if the path is not null and greater than 0
if (path != null && path.size()>0){
//calculate the pathlength (-1, which is the size of the path length of links)
double listsize = path.size()-1;
//add it to the list
pathlengths.add(listsize);
}
}
}
}
calculateAvgShortestPath(pathlengths);
}
//calculate the average
public void calculateAvgShortestPath(List<Double>pathlengths){
Double sum = 0.0;
for (Double cc: pathlengths){
sum+= cc;
}
Double avgPathLength = sum/pathlengths.size();
System.out.println("The average path length is: " + avgPathLength);
}
}
One quick improvement is to move the line:
dijkstra.execute(vertices.get(i));
up 6 lines (so it is in the i loop, but not the j loop).
This should improve runtime by the number of nodes (i.e. 3000 times faster).
It should still give identical results because Dijkstra's algorithm calculates the shortest path from the start node to ALL destination nodes, so there is no need to rerun it for each pair of start/end.
There are several optimizations that you could make. Like for instance using a Fibonacci heap (or even a standard java priority queue) would definitely speed things up. However, the memory issue will likely persist for a dataset that large regardless. The only real way to deal with a dataset that big is to use a distributed implementation. I believe that there is a shortest path implementation in the Spark Graphx library that you could use.
I'm working on Dijkstra's algorithm,and I need to find all possible shortest paths. Dijkstra's algorithm returns only one short path, if another path has the same cost I would like to print it. I'm out of ideas, please help me.
Thank you.
Here's my algorithm:
public class Dijkstra {
private static final Graph.Edge[] GRAPH = {
new Graph.Edge("a", "b", 7),
new Graph.Edge("a", "c", 9),
new Graph.Edge("a", "f", 14),
new Graph.Edge("b", "c", 10),
new Graph.Edge("b", "d", 13),
new Graph.Edge("c", "d", 11),
new Graph.Edge("c", "f", 2),
new Graph.Edge("d", "e", 6),
new Graph.Edge("e", "f", 9),
};
private static final String START = "a";
private static final String END = "e";
public static void main(String[] args) {
Graph g = new Graph(GRAPH);
g.dijkstra(START);
g.printPath(END);
//g.printAllPaths();
}
}
import java.io.*;
import java.util.*;
class Graph {
private final Map<String, Vertex>
graph; // mapping of vertex names to Vertex objects, built from a set of Edges
/** One edge of the graph (only used by Graph constructor) */
public static class Edge {
public final String v1, v2;
public final int dist;
public Edge(String v1, String v2, int dist) {
this.v1 = v1;
this.v2 = v2;
this.dist = dist;
}
}
/** One vertex of the graph, complete with mappings to neighbouring vertices */
public static class Vertex implements Comparable<Vertex> {
public final String name;
public int dist = Integer.MAX_VALUE; // MAX_VALUE assumed to be infinity
public Vertex previous = null;
public final Map<Vertex, Integer> neighbours = new HashMap<>();
public Vertex(String name) {
this.name = name;
}
private void printPath() {
if (this == this.previous) {
System.out.printf("%s", this.name);
} else if (this.previous == null) {
System.out.printf("%s(unreached)", this.name);
} else {
this.previous.printPath();
System.out.printf(" -> %s(%d)", this.name, this.dist);
}
}
public int compareTo(Vertex other) {
return Integer.compare(dist, other.dist);
}
}
/** Builds a graph from a set of edges */
public Graph(Edge[] edges) {
graph = new HashMap<>(edges.length);
//one pass to find all vertices
for (Edge e : edges) {
if (!graph.containsKey(e.v1)) graph.put(e.v1, new Vertex(e.v1));
if (!graph.containsKey(e.v2)) graph.put(e.v2, new Vertex(e.v2));
}
//another pass to set neighbouring vertices
for (Edge e : edges) {
graph.get(e.v1).neighbours.put(graph.get(e.v2), e.dist);
//graph.get(e.v2).neighbours.put(graph.get(e.v1), e.dist); // also do this for an undirected graph
}
}
/** Runs dijkstra using a specified source vertex */
public void dijkstra(String startName) {
if (!graph.containsKey(startName)) {
System.err.printf("Graph doesn't contain start vertex \"%s\"\n", startName);
return;
}
final Vertex source = graph.get(startName);
NavigableSet<Vertex> q = new TreeSet<>();
// set-up vertices
for (Vertex v : graph.values()) {
v.previous = v == source ? source : null;
v.dist = v == source ? 0 : Integer.MAX_VALUE;
q.add(v);
}
dijkstra(q);
}
/** Implementation of dijkstra's algorithm using a binary heap. */
private void dijkstra(final NavigableSet<Vertex> q) {
Vertex u, v;
while (!q.isEmpty()) {
u = q.pollFirst(); // vertex with shortest distance (first iteration will return source)
if (u.dist == Integer.MAX_VALUE)
break; // we can ignore u (and any other remaining vertices) since they are unreachable
//look at distances to each neighbour
for (Map.Entry<Vertex, Integer> a : u.neighbours.entrySet()) {
v = a.getKey(); //the neighbour in this iteration
final int alternateDist = u.dist + a.getValue();
if (alternateDist < v.dist) { // shorter path to neighbour found
q.remove(v);
v.dist = alternateDist;
v.previous = u;
q.add(v);
} else if (alternateDist == v.dist) {
// Here I Would do something
}
}
}
}
/** Prints a path from the source to the specified vertex */
public void printPath(String endName) {
if (!graph.containsKey(endName)) {
System.err.printf("Graph doesn't contain end vertex \"%s\"\n", endName);
return;
}
graph.get(endName).printPath();
System.out.println();
}
/** Prints the path from the source to every vertex (output order is not guaranteed) */
public void printAllPaths() {
for (Vertex v : graph.values()) {
v.printPath();
System.out.println();
}
}
public void printAllPaths2() {
graph.get("e").printPath();
System.out.println();
}
}
Have a look into so called k-shortest path algorithms. These solve the problem of enumerating the first, second, ..., kth shortest path in a graph. There are several algorithms in the literature, see for example this paper, or Yen's algorithm.
Note, that most algorithms do not require that you specify k upfront, ie. you can use them to enumerate shortest paths in an increasing order, and stop when the length has strictly increased.
I'm back with another similar question. I am currently working on a Java program that will check if a graph is 2-colorable, i.e. if it contains no odd cycles (cycles of odd number length). The entire algorithm is supposed to run in O(V+E) time (V being all vertices and E being all edges in the graph). My current algorithm does a Depth First Search, recording all vertices in the path it takes, then looks for a back edge, and then records between which vertices the edge is between. Next it traces a path from one end of the back edge until it hits the other vertex on the other end of the edge, thus retracing the cycle that the back edge completes.
I was under the impression that this kind of traversing could be done in O(V+E) time for all cycles that exist in my graph, but I must be missing something, because my algorithm is running for a ridiculously long time for very large graphs (10k nodes, no idea how many edges).
Is my algorithm completely wrong? And if so, can anyone point me in the right direction for a better way to record these cycles or possibly tell if they have odd numbers of vertices? Thanks for any and all help you guys can give. Code is below if you need it.
Addition: Sorry I forgot, if the graph is not 2-colorable, I need to provide an odd cycle that proves that it is not.
package algorithms311;
import java.util.*;
import java.io.*;
public class CS311 {
public static LinkedList[] DFSIter(Vertex[] v) {
LinkedList[] VOandBE = new LinkedList[2];
VOandBE[0] = new LinkedList();
VOandBE[1] = new LinkedList();
Stack stack = new Stack();
stack.push(v[0]);
v[0].setColor("gray");
while(!stack.empty()) {
Vertex u = (Vertex) stack.peek();
LinkedList adjList = u.getAdjList();
VOandBE[0].add(u.getId());
boolean allVisited = true;
for(int i = 0; i < adjList.size(); i++) {
if(v[(Integer)adjList.get(i)].getColor().equals("white")) {
allVisited = false;
break;
}
else if(v[(Integer)adjList.get(i)].getColor().equals("gray") && u.getPrev() != (Integer)adjList.get(i)) {
int[] edge = new int[2]; //pair of vertices
edge[0] = u.getId(); //from u
edge[1] = (Integer)adjList.get(i); //to v
VOandBE[1].add(edge);
}
}
if(allVisited) {
u.setColor("black");
stack.pop();
}
else {
for(int i = 0; i < adjList.size(); i++) {
if(v[(Integer)adjList.get(i)].getColor().equals("white")) {
stack.push(v[(Integer)adjList.get(i)]);
v[(Integer)adjList.get(i)].setColor("gray");
v[(Integer)adjList.get(i)].setPrev(u.getId());
break;
}
}
}
}
return VOandBE;
}
public static void checkForTwoColor(String g) { //input is a graph formatted as assigned
String graph = g;
try {
// --Read First Line of Input File
// --Find Number of Vertices
FileReader file1 = new FileReader("W:\\Documents\\NetBeansProjects\\algorithms311\\src\\algorithms311\\" + graph);
BufferedReader bReaderNumEdges = new BufferedReader(file1);
String numVertS = bReaderNumEdges.readLine();
int numVert = Integer.parseInt(numVertS);
System.out.println(numVert + " vertices");
// --Make Vertices
Vertex vertex[] = new Vertex[numVert];
for(int k = 0; k <= numVert - 1; k++) {
vertex[k] = new Vertex(k);
}
// --Adj Lists
FileReader file2 = new FileReader("W:\\Documents\\NetBeansProjects\\algorithms311\\src\\algorithms311\\" + graph);
BufferedReader bReaderEdges = new BufferedReader(file2);
bReaderEdges.readLine(); //skip first line, that's how many vertices there are
String edge;
while((edge = bReaderEdges.readLine()) != null) {
StringTokenizer ST = new StringTokenizer(edge);
int vArr[] = new int[2];
for(int j = 0; ST.hasMoreTokens(); j++) {
vArr[j] = Integer.parseInt(ST.nextToken());
}
vertex[vArr[0]-1].addAdj(vArr[1]-1);
vertex[vArr[1]-1].addAdj(vArr[0]-1);
}
LinkedList[] l = new LinkedList[2];
l = DFSIter(vertex);//DFS(vertex);
System.out.println(l[0]);
for(int i = 0; i < l[1].size(); i++) {
int[] j = (int[])l[1].get(i);
System.out.print(" [" + j[0] + ", " + j[1] + "] ");
}
LinkedList oddCycle = new LinkedList();
boolean is2Colorable = true;
//System.out.println("iterate through list of back edges");
for(int i = 0; i < l[1].size(); i++) { //iterate through the list of back edges
//System.out.println(i);
int[] q = (int[])(l[1].get(i)); // q = pair of vertices that make up a back edge
int u = q[0]; // edge (u,v)
int v = q[1];
LinkedList cycle = new LinkedList();
if(l[0].indexOf(u) < l[0].indexOf(v)) { //check if u is before v
for(int z = l[0].indexOf(u); z <= l[0].indexOf(v); z++) { //if it is, look for u first; from u to v
cycle.add(l[0].get(z));
}
}
else if(l[0].indexOf(v) < l[0].indexOf(u)) {
for(int z = l[0].indexOf(v); z <= l[0].indexOf(u); z++) { //if it is, look for u first; from u to v
cycle.add(l[0].get(z));
}
}
if((cycle.size() & 1) != 0) { //if it has an odd cycle, print out the cyclic nodes or write them to a file
is2Colorable = false;
oddCycle = cycle;
break;
}
}
if(!is2Colorable) {
System.out.println("Graph is not 2-colorable, odd cycle exists");
if(oddCycle.size() <= 50) {
System.out.println(oddCycle);
}
else {
try {
BufferedWriter outFile = new BufferedWriter(new FileWriter("W:\\Documents\\NetBeansProjects\\algorithms311\\src\\algorithms311\\" + graph + "OddCycle.txt"));
String cyc = oddCycle.toString();
outFile.write(cyc);
outFile.close();
}
catch (IOException e) {
System.out.println("Could not write file");
}
}
}
}
catch (IOException e) {
System.out.println("Could not open file");
}
System.out.println("Done!");
}
public static void main(String[] args) {
//checkForTwoColor("smallgraph1");
//checkForTwoColor("smallgraph2");
//checkForTwoColor("smallgraph3");
//checkForTwoColor("smallgraph4");
checkForTwoColor("smallgraph5");
//checkForTwoColor("largegraph1");
}
}
Vertex class
package algorithms311;
import java.util.*;
public class Vertex implements Comparable {
public int id;
public LinkedList adjVert = new LinkedList();
public String color = "white";
public int dTime;
public int fTime;
public int prev;
public boolean visited = false;
public Vertex(int idnum) {
id = idnum;
}
public int getId() {
return id;
}
public int compareTo(Object obj) {
Vertex vert = (Vertex) obj;
return id-vert.getId();
}
#Override public String toString(){
return "Vertex # " + id;
}
public void setColor(String newColor) {
color = newColor;
}
public String getColor() {
return color;
}
public void setDTime(int d) {
dTime = d;
}
public void setFTime(int f) {
fTime = f;
}
public int getDTime() {
return dTime;
}
public int getFTime() {
return fTime;
}
public void setPrev(int v) {
prev = v;
}
public int getPrev() {
return prev;
}
public LinkedList getAdjList() {
return adjVert;
}
public void addAdj(int a) { //adds a vertex id to this vertex's adj list
adjVert.add(a);
}
public void visited() {
visited = true;
}
public boolean wasVisited() {
return visited;
}
}
I was under the impression that this kind of traversing could be done in O(V+E) time for all cycles that exist in my graph
There may be much more cycles than O(V+E) in a graph. If you iterate all of them, you will run long.
Back to your original idea, you could just try to implement a straightforward algorithm to color graph in two colors (mark an arbitrary node as black, all neighbors in white, all their neighbors in black, etc; that would be a breadth-first search). That is indeed done in O(V+E) time. If you succeed, then graph is 2-colorable. If you fail, it's not.
Edit: If you need a cycle that proves graph is not 2-colorable, just record for each node the vertex you traversed into it from. When you happen to traverse from black vertex A to black vertex B (thus needing to color black B into white and proving your graph is not 2-colorable), you get the cycle by looking back to parents:
X -> Y -> Z -> U -> V -> P -> Q -> A
\-> D -> E -> B
Then, A-B-E-D-V-P-Q (the paths up to their common ancestor) is the cycle you needed.
Note that in this version you don't have to check all cycles, you just output a first cycle, where back-edge in the tree has both vertexes colored in the same color.
you are describing a bipartite graph. a bipartite graph is 2 colorable and it contains no odd length cycles. You can use BFS to prove that a graph is bipartite or not. Hope this helps.