我假设这个图是一个规则的矩形网格,其中有障碍节点,所有的解都不应该通过这个网格。此外,我假设从一个节点到相邻节点的旅行是1。我也意识到曼哈顿的距离是用来启发式的。
考虑到这些,恐怕你的做法是错误的。
首先,您应该使用迭代方法,而不是递归方法。考虑到图的大小,如果它是正确的实现,您肯定会得到stackoverflows。
第二,关于价值、价值、价值和/或成本的概念似乎存在问题。我觉得有必要对这些术语作一个非正式的描述。
a*使用的简单公式是f=g+h,其中
g是当前计算的从起始节点到当前节点的旅行成本。因此,对于起始节点,g value应该是0,从start node可以到达的任何节点的g值应该是1(我的假设是,从一个节点到一个相邻的节点旅行),我想强调这里的“当前”术语,因为在算法运行期间,节点的g value可能会发生变化。
h是成本的启发式部分,表示从当前节点到最终节点的成本。与G部分不同,一个节点的H值根本不会改变(好吧,我有一个疑问,这里可能有这样一个启发式的?,您的不会,让我们继续),它应该只计算一次。您的实现似乎使用了曼哈顿距离作为启发式,这绝对是此类图的最佳启发式。但要注意,我的朋友,这里似乎也有一个小问题:差异的绝对值应该单独考虑,然后再加以总结。
f是这些值的总和,表示从当前节点传递解决方案的可能成本(假设您的图形和启发式算法,任何计算出的f值应该等于或小于实际成本,这是好的)。
有了这些,我就可以使用这样的搜索节点:
public class SearchNode {
private int xCoordinate;
private int yCoordinate;
private double gScore;
private double hScore;
public double getfScore() {
return gScore + hScore;
}
public double getgScore() {
return gScore;
}
public void setgScore(int gScore) {
this.gScore = gScore;
}
public SearchNode(int xCoordinate,int yCoordinate, double gScore, SearchNode endNode) {
this.gScore=gScore;
this.hScore = //Manhattan distance from this node to end node
this.xCoordinate =xCoordinate;
this.yCoordinate = yCoordinate;
}
public int getxCoordinate() {
return xCoordinate;
}
public int getyCoordinate() {
return yCoordinate;
}
}
然后该算法可以实现如下:
private ArrayList<SearchNode> closedNodes = new ArrayList<SearchNode>();
private ArrayList<SearchNode> openNodes = new ArrayList<SearchNode>();
//create the start and end nodes
SearchNode end = new SearchNode(380, 560, -1, null);
SearchNode start = new SearchNode(115,655, 0, end);
// add start node to the openSet
openNodes.Add(start);
while(openNodes.Count > 0) // while there still is a node to test
{
// I am afraid there is another severe problem here.
// OpenSet should be PriorityQueue like collection, not a regular Collection.
// I suggest you to take a look at a Minimum BinaryHeap implementation. It has a logN complexity
// of insertion and deletion and Constant Complexity access.
// take the Node with the smallest FValue from the openSet. (With BinHeap constant time!)
SearchNode current = openNodes.GetSmallestFvaluedNode(); // this should both retrieve and remove the node fromt he openset.
// if it is the endNode, then we are node. The FValue (or the Gvalue as well since h value is zero here) is equal to the cost.
if (current.EqualTo(end)) // not reference equality, you should check the x,y values
{
return current.getfScore();
}
//check the neighbourNodes, they may have been created in a previous iteration and already present in the OpenNodes collection. If it is the case, their G values should be compared with the currently calculated ones.
// dont forget to check the limit values, we probably do not need nodes with negative or greater than the grid size coordinate values, I am not writing it
// also here is the right place to check for the blocking nodes with a simple for loop I am not writing it either
double neighbourGValue = current.getgScore() + 1;
if (openNodes.Contains(current.getXCoordinate(), current.getYCoordinate() + 1))
{
// then compare the gValue of it with the current calculated value.
SearchNode neighbour = openNodess.getNode(current.getXCoordinate(), current.getYCoordinate() + 1);
if(neighbour.getgScore() > neighbourGValue)
neighbour.setgScore(neighbourGValue);
}
else if(!closedNodes.Contains(current.getXCoordinate(), current.getYCoordinate()))
{
// create and add a fresh Node
SearchNode n = new SearchNode(current.getXCoordinate(), current.getYCoordinate() + 1, neighbourGValue, endNode);
openNodes.Add(n);
}
// do the same for the other sides : [x+1,y - x-1,y - x, y-1]
// lastly add the currentNode to the CloseNodes.
closedNodes.Add(current);
}
// if the loop is terminated without finding a result, then there is no way from the given start node to the end node.
return -1;
对于上述实现,我唯一想到的问题是
if (openNodes.Contains(current.getXCoordinate(), current.getYCoordinate() + 1))
即使将开放集实现为最小二进制堆,也无法轻松检查非最小F值节点。我现在记不起细节了,但我记得用logn复杂性实现这个。此外,我的实现是在一次调用中访问和更改该节点的g值(如果需要的话),因此您不必再花费时间来检索它。无论g值是否更改,如果存在具有给定坐标的节点,则返回true,因此不会生成新节点。
最后一件事是,当我写完这些东西后,我意识到了。您说,对于给定的任何输入,您的实现都在计算相同的结果。如果你提到的输入是障碍节点,那么大多数情况下,不管它是什么实现,它都会找到相同的距离,因为它在寻找尽可能短的距离。在下面的图片中,我试图解释这一点。