【发布时间】:2017-07-31 10:43:21
【问题描述】:
所以我创建了一个我的世界插件,我需要一个图表来创建一个导航系统。我研究了一下,发现我应该可以使用 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);
}
}
当第一次启用插件时,我从一个看起来像这样的配置文件加载所有顶点(这是我正在使用的测试)
我在这里添加顶点和边(不确定是否相关,但认为可能是?):
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;
}
}
【问题讨论】:
-
确认您的假设:是的,Dijkstra 也适用于循环图。它之所以起作用,是因为一旦一个节点被解决,它的最佳距离是已知的并且无法改进,因此 Dijkstra 不会再次将它添加到队列中。并且算法总是会在每一轮中确定一个节点。
-
好的,谢谢@Zabuza,但在什么情况下你会出现无限循环?
-
仅当您有实现错误时(即使对于负边权重它也会终止,但结果是完全错误的)。我没有仔细查看您的代码,因此只有评论。您应该尝试逐步调试程序,也许在一个非常小的示例图上(您可以在其中确认特定步骤是否正确)。您需要附上错误。
-
关于 getShortestPathTo() 的无限循环...也许顶点永远不会为空?调试您的 vertex = vertex.previous 分配以查看发生了什么。还要看看其他可能的无限循环:``` while (!vertexQueue.isEmpty()) { ```
-
与您的问题并不真正相关,但您确定要使用 Dijkstra 吗?如果你的图变大,它可能是一个非常贪婪的算法。