I have written an implementation of the A* algorithm, taken mainly from This wiki page, however I have a major problem; in that I believe I am visiting way too many nodes while calculating a route therefore ruining my performance. I've been trying to figure out the issue for a few days and I can't see what's wrong. Please note, all my data structures are self implemented however I've tested them and believe they're not the issue.
I've included my Priority Queue implementation just in case.
closedVertices is a Hash map of Vertices.
private Vertex routeCalculation(Vertex startLocation, Vertex endLocation, int routetype)
{
Vertex vertexNeighbour;
pqOpen.AddItem(startLocation);
while (!(pqOpen.IsEmpty()))
{
tempVertex = pqOpen.GetNextItem();
for (int i = 0; i < tempVertex.neighbors.GetNoOfItems(); i++) //for each neighbor of tempVertex
{
currentRoad = tempVertex.neighbors.GetItem(i);
currentRoad.visited = true;
vertexNeighbour = allVertices.GetNewValue(currentRoad.toid);
//if the neighbor is in closed set, move to next neighbor
checkClosed();
nodesVisited++;
setG_Score();
//checks if neighbor is in open set
findNeighbour();
//if neighbour is not in open set
if (!foundNeighbor || temp_g_score < vertexNeighbour.getTentativeDistance())
{
vertexNeighbour.setTentativeDistance(temp_g_score);
//calculate H once, store it and then do an if statement to see if it's been used before - if true, grab from memory, else calculate.
if (vertexNeighbour.visited == false)
vertexNeighbour.setH(heuristic(endLocation, vertexNeighbour));
vertexNeighbour.setF(vertexNeighbour.getH() + vertexNeighbour.getTentativeDistance());
// if neighbor isn't in open set, add it to open set
if (!(foundNeighbor))
{
pqOpen.AddItem(vertexNeighbour);
}
else
{
pqOpen.siftUp(foundNeighbourIndex);
}
}
}
}
}
return null;
}
Can anyone see where I may be exploring too many nodes?
Also, I've attempted to implement a way to calculate the quickest (timed) route, by modifying F by the speed of the road. Am I right in saying this the correct way to do it?
(I divided the speed of the road by 100 because it was taking a long time to execute otherwise).
I found my own error; I had implemented the way in which I calculate the heuristic for each node wrong - I had an IF statement to see if the H had already been calculated however I had done this wrong and therefore it never actually calculated the H for some nodes; resulting in excessive node exploration. I simply removed the line: if (vertexNeighbour.visited == false) and now I have perfect calculations.
However I am still trying to figure out how to calculate the fastest route in terms of time.
Related
I'am trying to improve the Bellman-Ford algorithm's performance and I would like to know if the improvement is correct.
I run the relaxing part not V-1 but V times, and I got a boolean variable involved, which is set true if any relax happened during the iteration of the outer loop. If no relax happened at the n. iteration where n <= V, it returns from the loop with the shortest path, but if it relaxes at n = V iteration, that means we have a negative cycle.
I thought it might improve runtime, since sometime we don't have to iterate for V-1 times to find the shortest path, and we can return earlier, and it's also more elegant than checking the cycle with another block of code.
AdjacencyListALD graph;
int[] distTo;
int[] edgeTo;
public BellmanFord(AdjacencyListALD g)
{
graph = g;
}
public int findSP(int source, int dest)
{
// initialization
distTo = new int[graph.SIZE];
edgeTo = new int[graph.SIZE];
for (int i = 0;i<graph.SIZE;i++)
{
distTo[i] = Integer.MAX_VALUE;
}
distTo[source] = 0;
// relaxing V-1 times + 1 for checking negative cycle = V times
for(int i = 0;i<(graph.SIZE);i++)
{
boolean hasRelaxed=false;
for(int j = 0;j<graph.SIZE;j++)
{
for(int x=0;x<graph.sources[j].length;x++)
{
int s = j;
int d = graph.sources[j].get(x).label;
int w = graph.sources[j].get(x).weight;
if(distTo[d] > distTo[s]+w)
{
distTo[d] = distTo[s]+w;
hasRelaxed = true;
}
}
}
if(!hasRelaxed)
return distTo[dest];
}
System.out.println("Negative cycle detected");
return -1;
}
Good comments on the need for testing. That's a given. But it doesn't address the underlying question, whether the OP's modifications to Bellman-Ford constitute an improvement to the algorithm. And the answer is, yes, this is actually a well-known improvement, as G. Bach pointed out in comments.
The OP's observation is that if, in any relaxation iteration, nothing relaxes, then there will be no changes in subsequent iterations and we can therefore just stop. Absolutely correct. There are no outside influences on the values assigned to the vertices. The only thing updating those values is the relaxation step itself. If it finds nothing to do on any iteration there is no way that something to do will materialize out of the aether. Ergo we can terminate.
This doesn't affect the complexity of the algorithm, nor does it help with worst case graphs, but it can reduce actual running time in practice.
As for running the relaxation one more time (|V| times rather than the usual |V|-1), this is just another way of stating the check for negative cycles that follows the relaxation step. It's just another way of saying that, when we terminate by running |V|-1 relaxation iterations, we need to see if any improvement can still be calculated, which reveals a negative cycle.
Bottom line: OP's approach is sound. Now, yes, test the code.
I'm trying to code the A* algorithm using a self implemented PQ and Vector. It has verticies as junctions and edges as roads. I was able to correctly code the dijkstra algorithm however I needed to improve the performance.
Currently my algorithm breaks throwing a null pointer exception as commented in the code. I've been researching the algorithm for the past 5+ hours whilst trying to implement it. I don't fully understand the method of comparing paths as well as I did for the dijkstra implementation.
Here is my current code:
private Vertex dijkstra (int start, int end)
{
Vertex current;
while (!(pqOpen.IsEmpty()))
{
current = pqOpen.GetNextItem();
else
{
for (int i = 0; i < current.neighbors.GetNoOfItems(); i++)
{
if (hold != null && hold.neighbors.GetItem(i).fromid != hold.city)
{
int x = 1;
if (hold2 == null || hold.getTentativeDistance() < hold2.getTentativeDistance())
{
hold2.from = hold; //throws null pointer here
hold2.setTentativeDistance(hold.getTentativeDistance());
hold2.setF(hold2.getTentativeDistance() + heuristic(hold2, endLoc));
System.out.println(hold2.from.city);
}
}
}
}
return null;
}
private double heuristic(Vertex goal, Vertex next)
{
return Math.sqrt(Math.pow((goal.x - next.x), 2) + Math.pow((goal.y - next.y), 2));
}
I've got a feeling that I've totally misunderstood the pseudo code about half way down the algorithm but as of yet I haven't found a representation that I can wrap my head around - so could someone possibly take a look at my code and point out where and what I'm doing wrong?
Here is a link to the website I used for reference: http://wiki.gamegardens.com/Path_Finding_Tutorial#Pseudo-code_A.2A
I also tried the wikipedia one, but their methods confused me even more: http://en.wikipedia.org/wiki/A*_search_algorithm
Currently it does loop round a few times and stores some nodes, however they are incorrect.
Edit:
I've reverted my code and attempted it a different way. I'm now using these pseudo-code examples to understand the algorithm. http://web.mit.edu/eranki/www/tutorials/search/ and http://www.redblobgames.com/pathfinding/a-star/introduction.html
On the first one, I think my code is correct until line 13 on the pseudo code. From that point I get confused about the term he uses 'position'.
The if statement that I commented out, is from my dijkstra algorithm. However I think the if statements in the pseudo code I meantioned earlier should be something similar to what the IF statements are.
Could anyone possibly help me understand line 13 in the pseudocode in relation to my code?
I did not read all the code, but this part is obviously bad :)
if (hold2 == null)
{
hold2.from = hold; //throws null pointer here
}
See? If the hold2 is null, you are trying to asign value to field from of hold2 which is null in this case, therefore it throws an exception.
I'm creating an A* search at the moment ( wiki page with pseudocode ) and I've been spending the last hour or so coming up with heuristic equations. When I think I finally found a good one, I removed the print statement that was allowing me to see what states were being visited. For some reason, that made my search go much much slower. If I add the print back in, it becomes fast again. What could possibly be going on?
I even tried changing what it prints. No matter what I am printing (as long as it is 2 characters or more), the result is the same.
Some of the code:
I apologize beforehand for messy code, this is my first time working with something like this:
while(!toVisit.isEmpty()){//toVisit is a set of states that need to be visited
int f = Integer.MAX_VALUE;
State temp;
State visiting = new State();
Iterator<State> it = toVisit.iterator();
while(it.hasNext()){//find state with smallest f value
temp = it.next();
if(temp.getF() < f){
f = temp.getF();
visiting = temp;//should be state with smallest f by end of loop
}
}
System.out.println("Visiting: ");//THIS LINE HERE
//LINE THAT MAGICALY MAKES IT FAST ^^^^
if(numConflicts(visiting.getList()) == 0){//checking if visiting state is the solution
best = visiting.getList();//sets best answer
return visiting;//ends algorithm
}
........
info on toVisit and visiting.getList():
HashSet<State> toVisit = new HashSet<State>();//from Java.util
public ArrayList<Node> State.getList(){return list;}
Node is my own class. It only contains some coordinates
This consistently solves the problem in about 6 seconds. If I change that line to print nothing or something shorter than about 2 characters, it takes anywhere from 20 to 70 seconds
I've been using examples of others' implementations of the A* pathfinding algorithm as a crutch to help me write my first implementation. I'm having some trouble with the logic in one of the more readable examples I've found.
I'm not here to pick apart this code, really, I'm trying to figure out if I am right or if I am misunderstanding the mechanics here. If I need to review how A* works I will but if this code is incorrect I need to find other sources to learn from.
It appears to me that the logic found here is flawed in two places both contained here:
for(Node neighbor : current.getNeighborList()) {
neighborIsBetter;
//if we have already searched this Node, don't bother and continue to the next
if (closedList.contains(neighbor))
continue;
//also just continue if the neighbor is an obstacle
if (!neighbor.isObstacle) {
// calculate how long the path is if we choose this neighbor as the next step in the path
float neighborDistanceFromStart = (current.getDistanceFromStart() + map.getDistanceBetween(current, neighbor));
//add neighbor to the open list if it is not there
if(!openList.contains(neighbor)) {
--> openList.add(neighbor);
neighborIsBetter = true;
//if neighbor is closer to start it could also be better
--> } else if(neighborDistanceFromStart < current.getDistanceFromStart()) {
neighborIsBetter = true;
} else {
neighborIsBetter = false;
}
// set neighbors parameters if it is better
if (neighborIsBetter) {
neighbor.setPreviousNode(current);
neighbor.setDistanceFromStart(neighborDistanceFromStart);
neighbor.setHeuristicDistanceFromGoal(heuristic.getEstimatedDistanceToGoal(neighbor.getX(), neighbor.getY(), map.getGoalLocationX(), map.getGoalLocationY()));
}
}
}
source
The first line I marked (-->), seems incorrect to me. If you look at the implementation of the list being used(below) it sorts based on heuristicDistanceFromGoal which is set several lines below the .add.
public int compareTo(Node otherNode) {
float thisTotalDistanceFromGoal = heuristicDistanceFromGoal + distanceFromStart;
float otherTotalDistanceFromGoal = otherNode.getHeuristicDistanceFromGoal() + otherNode.getDistanceFromStart();
if (thisTotalDistanceFromGoal < otherTotalDistanceFromGoal) {
return -1;
} else if (thisTotalDistanceFromGoal > otherTotalDistanceFromGoal) {
return 1;
} else {
return 0;
}
}
The second line I marked should always evaluate to false. It reads:
} else if(neighborDistanceFromStart < current.getDistanceFromStart()) {
Which can be simplified to:
if((current.getDistanceFromStart() + map.getDistanceBetween(current, neighbor)) < current.getDistanceFromStart())
And again to:
if(map.getDistanceBetween(current, neighbor) < 0)
Which would be fine except getDistanceBetween() should always return a positive value (see here).
Am I on or off track?
First of all, you are mostly on track. I strongly suspect the code you posted is still under development and has some problems. But, still your assumption of distance is positive is not correct in general. A* is a graph search algorithm and edges can have negative weights as well in general. Hence, I assume they tried to implement the most general case. The openList.add seems totally fine to me though. Your queue should be sorted with heuristic distance. Just check the wiki page https://en.wikipedia.org/wiki/A_star, the related line is f_score[neighbor] := g_score[neighbor] + heuristic_cost_estimate(neighbor, goal). And, main idea behind this is, you always underestimate the distance (admissible heuristic); hence, if the goal is found, none of the not-explored nodes can be optimal. You can read more at http://en.wikipedia.org/wiki/Admissible_heuristic
Second of all, If you want a stable AI code base, You can simply use http://code.google.com/p/aima-java/. It is the implementation of the algorithms in AIMA (Artificial Intelligence A Modern Approach)
I'm writing code to automate simulate the actions of both Theseus and the Minoutaur as shown in this logic game; http://www.logicmazes.com/theseus.html
For each maze I provide it with the positions of the maze, and which positions are available eg from position 0 the next states are 1,2 or stay on 0. I run a QLearning instantiation which calculates the best path for theseus to escape the maze assuming no minotaur. then the minotaur is introduced. Theseus makes his first move towards the exit and is inevitably caught, resulting in reweighting of the best path. using maze 3 in the game as a test, this approach led to theseus moving up and down on the middle line indefinatly as this was the only moves that didnt get it killed.
As per a suggestion recieved here within the last few days i adjusted my code to consider state to be both the position of thesesus and the minotaur at a given time. when theseus would move the state would be added to a list of "visited states".By comparing the state resulting from the suggested move to the list of visited states, I am able to ensure that theseus would not make a move that would result in a previous state.
The problem is i need to be able to revisit in some cases. Eg using maze 3 as example and minotaur moving 2x for every theseus move.
Theseus move 4 -> 5, state added(t5, m1). mino move 1->5. Theseus caught, reset. 4-> 5 is a bad move so theseus moves 4->3, mino catches on his turn. now both(t5, m1) and (t3 m1) are on the visited list
what happens is all possible states from the initial state get added to the dont visit list, meaning that my code loops indefinitly and cannot provide a solution.
public void move()
{
int randomness =10;
State tempState = new State();
boolean rejectMove = true;
int keepCurrent = currentPosition;
int keepMinotaur = minotaurPosition;
previousPosition = currentPosition;
do
{
minotaurPosition = keepMinotaur;
currentPosition = keepCurrent;
rejectMove = false;
if (states.size() > 10)
{
states.clear();
}
if(this.policy(currentPosition) == this.minotaurPosition )
{
randomness = 100;
}
if(Math.random()*100 <= randomness)
{
System.out.println("Random move");
int[] actionsFromState = actions[currentPosition];
int max = actionsFromState.length;
Random r = new Random();
int s = r.nextInt(max);
previousPosition = currentPosition;
currentPosition = actions[currentPosition][s];
}
else
{
previousPosition = currentPosition;
currentPosition = policy(currentPosition);
}
tempState.setAttributes(minotaurPosition, currentPosition);
randomness = 10;
for(int i=0; i<states.size(); i++)
{
if(states.get(i).getMinotaurPosition() == tempState.getMinotaurPosition() && states.get(i).theseusPosition == tempState.getTheseusPosition())
{
rejectMove = true;
changeReward(100);
}
}
}
while(rejectMove == true);
states.add(tempState);
}
above is the move method of theseus; showing it occasionally suggesting a random move
The problem here is a discrepancy between the "never visit a state you've previously been in" approach and your "reinforcement learning" approach. When I recommended the "never visit a state you've previously been in" approach, I was making the assumption that you were using backtracking: once Theseus got caught, you would unwind the stack to the last place where he made an unforced choice, and then try a different option. (That is, I assumed you were using a simple depth-first-search of the state-space.) In that sort of approach, there's never any reason to visit a state you've previously visited.
For your "reinforcement learning" approach, where you're completely resetting the maze every time Theseus gets caught, you'll need to change that. I suppose you can change the "never visit a state you've previously been in" rule to a two-pronged rule:
never visit a state you've been in during this run of the maze. (This is to prevent infinite loops.)
disprefer visiting a state you've been in during a run of the maze where Theseus got caught. (This is the "learning" part: if a choice has previously worked out poorly, it should be made less often.)
For what is worth, the simplest way to solve this problem optimally is to use ALPHA-BETA, which is a search algorithm for deterministic two-player games (like tic-tac-toe, checkers, chess). Here's a summary of how to implement it for your case:
Create a class that represents the current state of the game, which
should include: Thesesus's position, the Minoutaur's position and
whose turn is it. Say you call this class GameState
Create a heuristic function that takes an instance of GameState as paraemter, and returns a double that's calculated as follows:
Let Dt be the Manhattan distance (number of squares) that Theseus is from the exit.
Let Dm be the Manhattan distance (number of squares) that the Minotaur is from Theseus.
Let T be 1 if it's Theseus turn and -1 if it's the Minotaur's.
If Dm is not zero and Dt is not zero, return Dm + (Dt/2) * T
If Dm is zero, return -Infinity * T
If Dt is zero, return Infinity * T
The heuristic function above returns the value that Wikipedia refers to as "the heuristic value of node" for a given GameState (node) in the pseudocode of the algorithm.
You now have all the elements to code it in Java.