构建A *算法的问题

时间:2016-05-03 13:12:28

标签: java algorithm a-star

我正在尝试构建一个实现A *的应用程序,但我在处理逻辑方面遇到了麻烦。这里的方法采用4个int(startX / Y,goalX / Y),然后使用A *算法,它将构建一个ArrayList并返回它,因此main方法可以迭代并显示路径A * builds。但我得到的是一条跳跃的路径,最终建立一条通往目标节点的非常厚的路径。任何人都可以找出我的错误所在。

注意:open和closed是优先级队列,而Tile实现了可比性

public ArrayList<Tile> findPath(int sX, int sY, int gX, int gY)
{
    ArrayList<Tile> path = new ArrayList<Tile>();

    open.offer(gameMap[sX][sY]);
    Tile currentNode = gameMap[sX][sY];
    Tile goalNode = gameMap[gX][gY];

    int cX;
    int cY;

    while(open.size() > 0){
        currentNode = open.poll();
        closed.offer(currentNode);
        path.add(currentNode);

        cX = currentNode.getX();
        cY = currentNode.getY();

        if(currentNode == goalNode){
            break;
        }
        if((cX > 0 && cX < gameMap.length - 1) && (cY > 0 && cY < gameMap.length -1)){
            for(int i = -1; i < 2; i++){
                for(int j = 1; j > -2; j--){
                    if(i == 0 && j == 0){}
                    else{

                        if((gameMap[cX + i][cX + j].type != 1) && !closed.contains(gameMap[cX + i][cX + j])){

                            if(!open.contains(gameMap[cX + i][cX + j])){
                                open.offer(gameMap[cX + i][cX + j]);
                                gameMap[cX + i][cX + j].parent = currentNode;

                            }
                        }
                    }

                }

            }

        }

    }

    //         while(currentNode != gameMap[sX][sY]){
    //             path.push(currentNode);
    //             currentNode = currentNode.parent;
    //         }

    return path;
}

enter image description here

2 个答案:

答案 0 :(得分:1)

首先,我不认为您的closed集需要成为优先级队列。它只是一组已经查看过的节点。

你似乎错过了A *如何工作的核心部分,这就是为什么我认为这个路径查找器不适合你。

以下是主要想法:

具有启发式功能,可以猜测目的地的距离。理想情况下,该功能将可接受,这意味着它永远不会高估距离。

对于平铺网格,可以使用曼哈顿距离(x差+ y差)完成,因为这是最小距离,因此它始终是可接受的。

每当您从打开的列表中取出一个磁贴并将其添加到闭合集时,您需要更新相邻磁贴的距离的已知值(保持最低的已知值)。由于您已将已放入封闭集的区块的已知值,因此您只需向所有邻居添加1即可。已知值。

通过更新这些值,打开列表可能会改变顺序(这就是为什么优先级队列在这里是一个不错的选择)。启发式值可能保持不变,但已知值将更加精确。

到达目的地后,您将拥有一组已知距离的已关闭节点。然后,您从目的地回溯,查看也在闭合集中的每个邻居,并选择已知距离最小的邻居。

就如何实现这一点而言,您可能需要考虑将Tile类包含在另一个名为SearchTile的类中或类似的类中。它看起来像这样:

//You may not want to use public variables, depending on your needs
public class SearchTile implements Comparable<SearchTile> {
    public final Tile tile;
    //These need to be updated
    public int knownDistance = 0;
    public int heuristicDistance = 0;

    public SearchTile(final Tile tile) {
        this.tile = tile;
    }

    @Override
    public int compareTo(final SearchTile other) {
        if (knownDistance + heuristicDistance > other.knownDistance + other.heuristicDistance) {
            return 1;
        } else if (knownDistance + heuristicDistance < other.knownDistance + other.heuristicDistance) {
            return -1;
        } else {
            return 0;
        }
    } 
}

关于A *的一个很酷的事情是,在理想的情况下,它应该直接到达目的地。在有墙的情况下,它会采取最佳猜测,只要启发式是可以接受的,它就会提出最佳解决方案。

答案 1 :(得分:0)

我还没有完全详细介绍您的实施细节,但我想到您在OPEN中插入节点的方式可能会导致问题:

if(!open.contains(gameMap[cX + i][cX + j])){
     open.offer(gameMap[cX + i][cX + j]);
     gameMap[cX + i][cX + j].parent = currentNode;
}

您的目标是管理在OPEN列表中避免重复的元素,但有时您可能需要更换元素,因为您遇到了以更高的成本达到它的方式。在这种情况下,您需要删除已插入OPEN的节点,并以较低的成本重新引入该节点(因此具有最高优先级)。如果你不允许这样做,你可能会产生次优路径,因为它似乎是你的情况。

另外,该算法的一些逻辑缺失。您应该为您创建的每个节点将累计成本从开始G和目标H的估计成本存储起来。 OPEN列表是根据G + H的值排序的,我在代码中没有注意到这样做。无论如何,我建议你看看ForEach-Object之类的existing implementation of A*,以了解其工作原理的详细信息。

希望我的回答有所帮助!