Flawed logic in A* implementation - java

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)

Related

How to get the predicted move sequence that a negamax algorithm is basing its evaluation upon?

My chess algorithm is based on negamax. The relevant part is:
private double deepEvaluateBoard(Board board, int currentDepth, double alpha, double beta, Move initialMove) {
if (board.isCheckmate() || board.isDraw() || currentDepth <= 0) {
this.moveHistorys.put(initialMove, board.getMoveHistory()); // this is not working
return evaluateBoard(board); // evaluateBoard evaluates from the perspective of color whose turn it is.
} else {
double totalPositionValue = -1e40;
List<Move> allPossibleMoves = board.getAllPossibleMoves();
for (Move move : allPossibleMoves) {
board.makeMove(move);
totalPositionValue = max(-deepEvaluateBoard(board, currentDepth - 1, -beta, -alpha, initialMove), value);
board.unMakeMove(1);
alpha = max(alpha, totalPositionValue);
if (alpha >= beta) {
break;
}
}
return totalPositionValue;
}
}
It would greatly help debugging if I was be able to access the move sequence that the negamax algorithm bases its evaluation on (where on the decision tree the evaluated value is found).
Currently I am trying to save the move history of the board into a hashmap that is a field of the enclosing class. However, it is not working for some reason, as the produced move sequences are not optimal.
Since developing an intuition for negamax is not very easy, I have ended up on banging my head against the wall on this one for quite some time now. I would much appreciate if someone could point me in the right direction!

Bellman-Ford improvement: does it work?

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.

Java A* Implementation Issues

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.

Java a* algorithm implementation issue

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.

Sudoku solver Recurrence stack with try and fail technique

I am building a Sudoku solver that use the Try and Fail technique to solve any problem. My algorithm is:
1)Update (method that remove any possible value that already given as a final value to element in the same Row, column or squar)
2)Get the minimum element that has minimum number of possible values
3)start solve assuming the first possible value is the final value
4)save the current sate into a stack
5)Try to solve
5-a)If solved, return
5-b)if not solved and with invalid Sudoku, then Pop previous state
6)Repeat step 3) for all possible vaues (9)
7)Repeat step 2) until the puzzel is solved
This is my code
Stack<Element[][]> myStack= new Stack<>();
private Element[][] mySudoku;
public void solve(){
update();//remove all final values from all possible values for each element
if(isSudokuSolved(mySudoku)){
return;
}
//find a cell that is not confirmed and has the minimal candidates
int celli=-1,cellj=-1, p=10;
for(int i=0;i<9;i++){
for(int j=0;j<9;j++){
if(mySudoku[i][j].getValue()==0){
if(mySudoku[i][j].getPossibleValues().size()<p){
celli=i;
cellj=j;
p=mySudoku[i][j].getPossibleValues().size();
}
}
}
}
try {
for (int c = 0; c < mySudoku[celli][cellj].getPossibleValues().size() - 1; c++) {
//save state
Element[][] copy=deepCopy(mySudoku);//copy the current state
myStack.push(copy);
//apply candidate to cell
mySudoku[celli][cellj].setValue(mySudoku[celli][cellj].getPossibleValues().get(c));
update();//check is solved
if(checkValidInputSudoku(mySudoku)){
solve();
}else{
try {
mySudoku = myStack.pop();
} catch (EmptyStackException est) {
//do nothing
}
}
}
} catch (Exception e) {
}
//if we have reached here then we are at the last possible value for the candidates so confirm candidate in cell
if(celli!=-1 && cellj!=-1 && p!=10) {//Some problems happen here "out of Boundry -1 Error"
mySudoku[celli][cellj].setValue(mySudoku[celli][cellj].getPossibleValues().get(mySudoku[celli][cellj].getPossibleValues().size()-1));
}
}//end of solve method
I have spent more than 6 hours trying to find out the problem. I have checked for the Update() method, deepCopy() method and checkValidInputSudoku() method. They all works fine. Thank you in Advance
I can see one problem in your code. You have a loop that is sawing off the branch it sits on:
for(int c = 0; c < mySudoku[celli][cellj].getPossibleValues().size() - 1; c++) {
...
mySudoku[celli][cellj].setValue(mySudoku[celli]cellj].getPossibleValues().get(c));
...
}
Apart from that, you are missing one of the values, it should be for(c=0; c!=size; ++c), i.e. not size - 1. Also, calling getPossibleValues() just once would make this code much more readable. Lastly, catching and ignoring a stack underflow is just stupid, because it hides errors in your algorithm, as far as I can tell. If you don't know how to handle an error, don't just silence it. Since java requires you to catch it, put it in the outermost place possible or at least abort or do something, but don't ignore it!
One more thing: You are recursing and passing the context data via mySodoku and myStack. This is completely missing the point of recursion (or at least the way it's typically used), because the function call stack is the only stack you need. Using these to pass parameters only makes things more complicated than necessary. Instead, the function should return a partial sodoku puzzle and return either the fully solved puzzle or null. Using is easier to distinguish than the exception you're using now, and it's a regular and expected thing, not really exceptional. Then, when trying different choices, you set the cell to the values in turn and recurse, until the call doesn't return null. If none of the choices returns a solution, you clear the cell and return null yourself.
solve(sodoku):
if sodoku is solved:
return true
if sodoku is invalid:
return false
c = some empty cell
for v in 1...9:
// set to a value and recurse
c = v
if solve(sodoku):
// found a solution
return true
// no solution found, clear cell and return failure
c = null
return false
BTW: This strategy is called "backtracking". Using a cell with the least amount of possible values is called "pruning", which allows you to cut off whole branches from the search tree. Actually determining the possible values also helps avoiding a few futile attempts.

Categories

Resources