So I creating a minecraft plugin where I am in need of a graph to create a navigation system. I researched a bit and found out that I should be able to use Dijkstra, but I have a problem. When searching for the shortest path I am sometimes getting an infinite loop(not always, it usally works the first 2-3 runs but after that it goes into the loop).
When the player wants to get to a destination I search for the closest vertex and use computePaths with that vertex as parameter. When I then run the getShortestPathTo it sometimes gets stuck in an infinite loop and I run out of memory(which makes sence since im adding the same vertexes to the list). Can you see why it is getting stuck? As far as I knew Dijkstra should be able to handle going from A node to B node and from B node to A node right?
Below is my code:
public class Dijkstra {
public static void computePaths(Vertex source) {
source.minDistance = 0.;
PriorityQueue<Vertex> vertexQueue = new PriorityQueue<Vertex>();
vertexQueue.add(source);
while (!vertexQueue.isEmpty()) {
Vertex u = vertexQueue.poll();
// Visit each edge exiting u
for (Edge e : u.adjacencies) {
Vertex v = e.target;
double weight = e.weight;
double distanceThroughU = u.minDistance + weight;
if (distanceThroughU < v.minDistance) {
vertexQueue.remove(v);
v.minDistance = distanceThroughU;
v.previous = u;
vertexQueue.add(v);
}
}
}
}
public static List<Vertex> getShortestPathTo(Vertex target) {
List<Vertex> path = new ArrayList<Vertex>();
for (Vertex vertex = target; vertex != null; vertex = vertex.previous) {
path.add(vertex);
}
Collections.reverse(path);
return path;
}
}
and the vertex class:
public class Vertex implements Comparable<Vertex>
{
public final String name;
public Edge[] adjacencies;
public double minDistance = Double.POSITIVE_INFINITY;
public Vertex previous;
public Location location;
public Vertex(String argName) { name = argName; }
public Vertex(String argName,Location l) { name = argName; location = l;}
public String toString() { return name; }
public int compareTo(Vertex other)
{
return Double.compare(minDistance, other.minDistance);
}
}
When first the plugin is enabled I load all the vertexes from a config file looking something like this(It is the test one i am using)
I am adding vertexes and edges here(not sure if relevent but thought it might be?):
public void loadAllVertex() {
ConfigurationSection section = nodeConfig.config.getConfigurationSection("nodes");
for (String key: section.getKeys(false)) {
String locationString = nodeConfig.getString("nodes." + key + ".location");
if (locationString == null)
return;
String[] locationSplit = locationString.split(",");
if (locationSplit.length <=1) {
log.log(Level.SEVERE, "Location is not specified correctly in nodes.yml");
return;
}
Location l = new Location(Bukkit.getWorlds().get(0),Integer.parseInt(locationSplit[0]),Integer.parseInt(locationSplit[1]),Integer.parseInt(locationSplit[2]));
Vertex tmpVertex = new Vertex(key, l);
allNodes.add(tmpVertex);
}
for (Vertex v : allNodes) {
String path = "nodes." + v.name + ".connectedTo";
List<String> connectedTo = nodeConfig.getStringList(path,true);
List<Edge> edges = new ArrayList<>();
for (String sideNodeName : connectedTo) {
Vertex vertexCon = GraphUtils.getVertexByName(allNodes, sideNodeName);
if (vertexCon == null) {
log.warning("The '" + sideNodeName + "' node is not defined");
return;
}
//A.adjacencies = new Edge[]{ new Edge(M, 8) };
edges.add(new Edge(vertexCon,vertexCon.location.distance(v.location)));
}
Edge[] arrayEdges = new Edge[edges.size()];
arrayEdges = edges.toArray(arrayEdges);
v.adjacencies = arrayEdges;
}
}
Think i found the error, so I never had any loops the first time I ran the compute path and findshortestpath so I finally figured out that I could not be resetting things correctly(should have been obvious I know) - I didn't update the vertexes afterwards. So I added a method to reset the mindistance and previous attributes and this seems to have done the trick.
Related
Below is my bfs algorithm, the algorithm works and finds the node given the start and target. But I want to save edges for the used path in a linkedList to draw the path.
My BFS:
public DGPath breadthFirstSearch(String startId, String targetId) {
V start = this.getVertexById(startId);
V target = this.getVertexById(targetId);
if (start == null || target == null) return null;
DGPath path = new DGPath();
path.start = start;
path.visited.add(start);
// easy target
if (start == target) return path;
// TODO calculate the path from start to target by breadth-first-search
// register all visited vertices while going, for statistical purposes
// if you hit the target: complete the path and bail out !!!
Queue<V> fifoQueue = new LinkedList<>();
Map<V,V> visitedFrom = new HashMap<>();
fifoQueue.offer(start);
visitedFrom.put(start, null);
while (!fifoQueue.isEmpty()) {
V current = fifoQueue.poll();
for (E e : current.getEdges()) {
V neighbour = e.getTo();
path.visited.add(neighbour);
if (neighbour == target) {
while (current != null) {
path.getEdges().addFirst(e);
current = visitedFrom.get(current);
}
return path;
} else if (!visitedFrom.containsKey(neighbour)) {
visitedFrom.put(neighbour,current);
fifoQueue.offer(neighbour);
}
}
}
// no path found, graph was not connected ???
return null;
}
The DGPath is the class that creates the path as shown below:
public class DGPath {
private V start = null;
private LinkedList<E> edges = new LinkedList<>();
private double totalWeight = 0.0;
private Set<V> visited = new HashSet<>();
/**
* representation invariants:
* 1. The edges are connected by vertices, i.e. FOR ALL i: 0 < i < edges.length: edges[i].from == edges[i-1].to
* 2. The path begins at vertex == start
* 3. if edges is empty, the path also ends at vertex == start
* otherwise edges[0].from == start and the path continues along edges[i].to for all 0 <= i < edges.length
**/
#Override
public String toString() {
StringBuilder sb = new StringBuilder(
String.format("Weight=%f Length=%d Visited=%d (",
this.totalWeight, 1 + this.edges.size(), this.visited.size()));
sb.append(start.getId());
for (E e : edges) {
sb.append(", " + e.getTo().getId());
}
sb.append(")");
return sb.toString();
}
public V getStart() {
return start;
}
public LinkedList<E> getEdges() {
return edges;
}
public double getTotalWeight() {
return totalWeight;
}
public Set<V> getVisited() {
return visited;
}
}
I want to save the right edges in de linkedlist edges from the BGPath class (called path in my BFS algo method). So I already saved the used vertices in a map to go back to the root. But when I add the edge to the path it just saves the last edge used multiple times.. The problem is the vertex can have multiple edges, so I need to add the edge from the previous that was pointing to the last "current" until I'm back to the root. But I cant wrap my head around the right way to do this.
The line where I now add the edge to the list of edges is: path.getEdges().add(e)
I think, your problem is the same line, where your are adding the edges, that line is adding the same edge in inner while loop again and again, so you are only traversing back but not adding those nodes to your edges list.
I think, it should be like this
while (!fifoQueue.isEmpty()) {
V current = fifoQueue.poll();
for (E e : current.getEdges()) {
V neighbour = e.getTo();
path.visited.add(neighbour);
if (neighbour == target) {
path.getEdges().addFirst(e):
while (current != null) {
path.getEdges().addFirst(current) ;
current = visitedFrom.get(current);
}
return path;
} else if (!visitedFrom.containsKey(neighbour)) {
visitedFrom.put(neighbour,current);
fifoQueue.offer(neighbour);
}
}
}
// no path found, graph was not connected ???
return null;
}
I have an assignment to create an A* algorithm through a road graph of the UK through a given template (all files for this assignment I have uploaded here
I think, however, that the problem is somewhere in my Planner.java file - the A* algorithm is not finding a node to expand, it just abruptly stops without expanding any nodes at all.
Here is the code for the Planner :
public class Planner implements PlannerInterface<Object> {
// declaration and instantination of our open and closed lists
private final OpenList openList = new OpenList();
private final ArrayList<SearchThroughNodes> closedList;
public boolean empty() {
int lengthOfItem = 0;
return (lengthOfItem == 0);
}
// constructor of closed list
public Planner() {
this.closedList = new ArrayList<>();
}
#Override
public List<GraphEdge> plan(RoadGraph graph, GraphNode origin, GraphNode destination) {
// Temporary costs and other data needed
SearchThroughNodes currentNode, temp1, temp2;
temp2 = new SearchThroughNodes(null, 0, 0, null);
GraphNode temp;
double temporaryCost, heuristics;
ArrayList<GraphEdge> finalResult;
finalResult = new ArrayList<>();
// check if origin and destination exist a.k.a are not set to null
boolean originExistence;
boolean destinationExistence;
destinationExistence = false;
originExistence = false;
if (origin != null && destination != null) {
originExistence = true;
destinationExistence = true;
}
// Pre-requisites for our A-Star to work
if (originExistence == true && !destination.equals(origin) && destinationExistence == true ) {
openList.add(new SearchThroughNodes(origin, 0, Utils.distanceInKM(origin, destination) / 120, null));
} else {
return null;
}
// A-star loop
while (!openList.empty()) {
// Get BEST node to expand
currentNode = (SearchThroughNodes) openList.get();
if (closedList.contains(currentNode)) {
continue;
}
// We reached the destination
// go back through nodes and read path
GraphNode checkCurrNode;
checkCurrNode = currentNode.getGraphNode();
if (destination.equals(checkCurrNode)) {
temp1 = currentNode;
temp2 = (SearchThroughNodes) currentNode.getPrecedingItem();
while (!temp2.getGraphNode().equals(origin)) {
finalResult.add(0, graph.getEdge(temp2.getGraphNode().getId(), temp1.getGraphNode().getId()) );
temp1 = temp2;
temp2 = (SearchThroughNodes) temp2.getPrecedingItem();
}
finalResult.add(0, graph.getEdge(temp2.getGraphNode().getId(), temp1.getGraphNode().getId()));
return finalResult;
}
closedList.add(currentNode);
long currentNodeId = currentNode.getGraphNode().getId();
List<GraphEdge> outEdges = graph.getNodeOutcomingEdges(currentNodeId);
//if expandable
if (outEdges != null) {
// traverse all nodes after currentNode
for (GraphEdge edge : graph.getNodeOutcomingEdges(currentNodeId)) {
long getFromEdge;
getFromEdge = edge.getToNodeId();
//Look at node at the end of the current edge
temp = graph.getNodeByNodeId(getFromEdge);
//store nodeID in tmp2 for traversal of openList
long tmp2GetID;
tmp2GetID = temp.getId();
temp2.setNodeID(tmp2GetID);
// set current edge length in kms,edge max allowed speed and current node cost to variables
// to later compute the temporary cost
double edgeLengthInKMs = edge.getLengthInMetres() / 1000;
double edgeMaxAllowSpeed = edge.getAllowedMaxSpeedInKmph();
double currCost = currentNode.getCost();
//new heuristics and cost
temporaryCost = currCost + edgeLengthInKMs / edgeMaxAllowSpeed;
heuristics = Utils.distanceInKM(temp, destination) / 120;
// Proceed here if node wasn't expanded
if (!closedList.contains(temp2)) {
// if node isn't contained currently in closedList
// if not,check and update accordingly
if (!openList.contains(temp2)) {
openList.add(new SearchThroughNodes(temp,
temporaryCost, heuristics, currentNode));
} else {
temp1 = openList.getNode(temp2);
double tempOneCost = temp1.getCost();
if (tempOneCost > temporaryCost) {
temp1.update(temporaryCost, currentNode);
}
openList.insert(temp1);
}
}
}
}
}
return null;
}
#Override
public AbstractOpenList<Object> getOpenList() {
return openList;
}
}
I have been trying to fix this Depth First Search problem but I cant figure it out. I want to print all paths but somehow it only prints some paths. I can figure out the mistake here for such a long time:
void printAllPathsUtil(Vertex v, Vertex d, ArrayList<Vertex> path){
v.state = VISITED;
path.add(v);
if (v == d) {
for (Vertex p : path) {
System.out.print("Print: " + p.value + " ");
}
System.out.println();
}
else {
for (Vertex city : v.outboundCity){
if (city.state == UNVISITED) {
printAllPathsUtil(city, d, path);
}
}
}
path.remove(v);
v.state = UNVISITED;
}
void printAllPaths(Vertex v, Vertex u){
clearStates();
ArrayList<Vertex> path = new ArrayList<>();
printAllPathsUtil(v, u, path);
}
Vertex Class is something like this:
public class Vertex{
String value;
Vertex previous = null;
int minDistance = Integer.MAX_VALUE;
List<Vertex> inboundCity;
List<Vertex> outboundCity;
State state;
}
I think you should have this two lines inside the loop:
path.remove(v);
v.state = UNVISITED;
You should remove vertexes from path and "unvisit" them right after your recursion is terminated, not when you end the loop
I just wondering where to put this code
if(node == goal){
System.out.print("Found path: ");
for(int n : stack){
System.out.print(n + " ");
}
}
in this method:
public void performRecursiveDFS(Graph G, int node, int goal) {
Stack<Integer> stack = new Stack<Integer>();
stack.push(node);
while (!stack.isEmpty()) {
node = stack.pop();
if (!visited[node]) {
visited[node] = true;
for (int w : G.adjList(node)) {
stack.push(w);
}
}
}
}
For the reason is, I want to print the path from the start node to the goal node. For example like this, Found path: 0 1 2 3 4 7. I tried putting it after node = stack.pop() but it showed me something like this Found path: 3. Any reason/suggestion? Is there something wrong with my code? If so, please direct me in detail. Questions are welcome. Thanks in advance.
You have implemented part of the DFS that keep track of visited nodes and you can extend it to know source path also that link to next node
Full DFS implementation will look something like below that will handle paths request also
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Collections;
import java.util.Deque;
public class DirectedDFS {
private final Digraph g;
private final int source;
private boolean[] marked;
private int[] edgeFrom;
public DirectedDFS(Digraph g, int source) {
this.g = g;
this.source = source;
init(g);
dfs(g, source);
}
private void init(Digraph g) {
this.marked = new boolean[g.V()];
this.edgeFrom = new int[g.V()];
Arrays.fill(edgeFrom, -1);
}
private void dfs(Digraph g, int source) {
marked[source] = true;
for (int vertex : g.adj(source)) {
if (!marked[vertex]) {
edgeFrom[vertex] = source;
dfs(g, vertex);
}
}
}
public boolean hasPath(int v) {
return marked[v];
}
public Iterable<Integer> path(int v) {
if (hasPath(v)) {
final Deque<Integer> paths = new ArrayDeque<>();
int vertex = v;
paths.push(v);
while ((vertex = edgeFrom[vertex]) != -1) {
paths.push(vertex);
}
return paths;
}
return (Iterable<Integer>) Collections.emptyIterator();
}
}
The stack is currently storing nodes to check next not nodes that have already been checked to get to the current point.
To get the path taken you will need to create a list and add each node that is checked to that list.
Once you get a node you can check if that node is the goal node then you can print out the list
You currently aren't storing the path to the current node so you can't print it. A fairly easy way to achieve this is to use a map to plot the path back to the origin. This can replace your use of the visited array.
Here's how it would look:
public void performRecursiveDFS(Graph G, int start, int goal) {
Stack<Integer> remaining = new Stack<Integer>();
remaining.push(start);
Map<Integer, Integer> previous = new HashMap<>();
while (!remaining .isEmpty()) {
int current = remaining.pop();
if (current == goal) {
Deque<Integer> path = new LinkedList<>();
path.addFirst(current);
while (previous.containsKey(path.peek())
path.addFirst(previous.get(path.peek()));
// print path
} else if (!previous.containsKey(current)) {
for (int w : G.adjList(current)) {
previous.put(w, current);
remaining.push(w);
}
}
}
}
I am fairly new to java and I have been struggling with this exercise for two weeks now(It's an homework exercise in my school). I need to create a topological sort and print out all of the possible connections. I have read a lot about topological sorting now, but we have this certain line of code that we have to work with. I'm pretty sure I could do the topological sorting when I have the list of vertices. My problem is, I don't know how to list all of the vertices from this given code. Could anyone give me some tips or leads or perhaps an example, I would really really appreciate it.
Here is the given code we need to work with:
import java.util.*;
public class Answer {
public static void main (String[] args) {
Answer a = new Answer();
a.run();
}
public void run() {
// TODO!!! YOUR TESTS HERE!
Graph g = new Graph ("G");
Vertex a = new Vertex ("A");
Vertex b = new Vertex ("B");
Vertex c = new Vertex ("C");
g.first = a;
a.next = b;
b.next = c;
Edge ab = new Edge ("AB");
Edge ac = new Edge ("AC");
Edge ba = new Edge ("BA");
Edge ca = new Edge ("CA");
a.first = ab;
b.first = ba;
c.first = ca;
ab.next = ac;
ab.target = b;
ac.target = c;
ba.target = a;
ca.target = a;
System.out.println (g);
}
class Vertex {
String id;
Vertex next;
Edge first;
Vertex (String s, Vertex v, Edge e) {
id = s;
next = v;
first = e;
}
Vertex (String s) {
this (s, null, null);
}
#Override
public String toString() {
return id;
}
// TODO!!! Your Vertex methods here!
} // Vertex
class Edge {
String id;
Vertex target;
Edge next;
Edge (String s, Vertex v, Edge e) {
id = s;
target = v;
next = e;
}
Edge (String s) {
this (s, null, null);
}
#Override
public String toString() {
return id;
}
// TODO!!! Your Edge methods here!
} // Edge
class Graph {
String id;
Vertex first;
Graph (String s, Vertex v) {
id = s;
first = v;
}
Graph (String s) {
this (s, null);
}
#Override
public String toString() {
String nl = System.getProperty ("line.separator");
StringBuffer sb = new StringBuffer (nl);
sb.append (id + nl);
Vertex v = first;
while (v != null) {
sb.append (v.toString() + " --> ");
Edge e = v.first;
while (e != null) {
sb.append (e.toString());
sb.append ("(" + v.toString() + "->"
+ e.target.toString() + ") ");
e = e.next;
}
sb.append (nl);
v = v.next;
}
return sb.toString();
}
// TODO!!! Your Graph methods here!
} // Graph
}
Apparently, the graph has a reference to the first vertex, and the vertices themselves are linked together into a singly linked list. This code should be all you need to collect the vertices into a Java list:
public List<Vertex> allVertices(Graph g) {
final List<Vertex> vertices = new ArrayList<>();
for (Vertex v = g.first; v != null; v = v.next)
vertices.add(v);
return vertices;
}
I would suggest that you add a "lastvisited" integer field to the edge which is set to zero, or use a boolean "visited"(true/false). Then start at one vertex. Assuming the graph is connected, you will reach all vertexes by going over the unvisited edges for one vertex, then following the edges to the vertex it leads to, marking the edge as followed, and calling your count function for this vertex recursively.
I.E.: count(node) = sum(my unvisited edges) mark_edge_as_visited(edge), count(edge.target)
Please note that you also have to consider that the graph appears to be a directed graph, so an edge leading from a to b and from b to a is counted as two edges.
Edit: I made a mistake, you also need to mark the vertex as visited, or it will be visited twice (I was thinking of an undirected graph).