图和Dijkstra算法的无限循环?

5
我正在开发一个Minecraft插件,在其中需要创建一个导航系统的图表。我进行了一些研究,并发现应该能够使用Dijkstra算法,但我遇到了一个问题。当寻找最短路径时,有时会出现无限循环的情况(并非总是如此,通常在前2-3次运行后才会进入循环)。
当玩家想要到达目的地时,我会搜索到最近的顶点,并使用该顶点作为参数运行computePaths。然后,当我运行getShortestPathTo时,有时会陷入无限循环中,而我会耗尽内存(这是有道理的,因为我正在将相同的顶点添加到列表中)。您能看出它为什么会卡住吗?据我所知,Dijkstra应该能够处理从A节点到B节点和从B节点到A节点对吧?
以下是我的代码:
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;
}
}

以及顶点类:

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);
    }

}

当插件第一次启用时,我会从类似于以下内容的配置文件中加载所有顶点(这是我正在使用的测试文件):

Config file

在此添加顶点和边缘(不确定是否相关,但认为可能有帮助)。
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;
    }

}

3
确认您的假设:是的,Dijkstra算法也适用于有环图。这是因为一旦一个节点被“settled”,它的最优距离已知且无法改进,因此Dijkstra算法不会再次将其加入队列。而且该算法每轮总会确保至少一个节点被settled。 - Zabuzard
好的,谢谢@Zabuza。但是在什么情况下会出现无限循环? - Sumsar1812
2
只有当您存在实现错误时(即使对于负边权重,它也会终止,但结果是错误的),才会出现此问题。我没有仔细查看您的代码,因此只能发表评论。您应该尝试逐步调试程序,可能在一个非常小的示例图上(在其中可以确认特定步骤是否正确)。您需要找到错误所在。 - Zabuzard
1
关于getShortestPathTo()中的无限循环...也许vertex永远不会为空?调试你的vertex = vertex.previous赋值语句以查看发生了什么。还要注意其他可能的无限循环:while (!vertexQueue.isEmpty()) { - cabreracanal
1
不是很相关于你的问题,但是你确定想使用Dijkstra吗?如果你的图形变得很大,它可能会成为一个非常贪心的算法。 - Logar
你还有什么其他的建议吗? - Sumsar1812
1个回答

2

我想我找到了错误,所以第一次运行计算路径和查找最短路径时没有任何循环,最终我发现我没有正确地重置东西(这应该是显而易见的)。我没有更新顶点后面,所以我添加了一个方法来重置mindistance和previous属性,这似乎解决了问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接