加速 Dijkstra 的算法来解决 3D 迷宫
Speeding up Dijkstra's algorithm to solve a 3D Maze
我正在尝试编写一个可以解决 3D 迷宫的 Python 脚本,我正在使用 Dijkstra 算法和优先级队列(包含在模块 heapq 中)来完成它。这是我的主要功能代码:
from heapq import *
def dijkstra(start,end,vertices,obstacles):
covered=[]
s=vertices.index(s)
currentVertex=s
liveDistances={}
for i in range(len(vertices)):
liveDistances[i]=inf
liveDistances[s]=0
next=[[liveDistances[s],s]]
while next:
np,currentVertex=heappop(next)
covered.append(currentVertex)
for u in sons(vertices[currentVertex]):
v=vertices.index(u)
if v in covered:continue
if 1+liveDistances[currentVertex]<liveDistances[v]:
liveDistances[v]=1+liveDistances[currentVertex]
heappush(next,[liveDistances[v],v])
if liveDistances[vertices.index(e)]!=inf:
return liveDistances[vertices.index(e)]
else:
return "No path!"
所以基本上它只是将 Dijkstra 应用于 3D 图形。
该程序运行良好,但我想知道它在 10 秒内解决一个 100x100 的 2D 迷宫或在 2 分钟内解决一个 30x30x30 的迷宫是否正常?
我在这里实施错误吗?或者它只是正确的执行时间?我可以增强它吗?
我寻求增强的原因是因为我被要求在不到 5 秒(时间限制)内解决问题(在最大 40x40x40 的 3D 迷宫中找到最短路径)。
你知道 A* 搜索 ("A-star") 吗?这是对 Dijkstra 算法的修改,当您对情况的几何结构有所了解时,它可以提供很大帮助。未经修改的 Dijkstra 假设任何边缘都可能是通往目标的极短路径的起点,但情况的几何形状通常不允许这样做,或者至少使其不太可能。
基本思想是,如果您试图找到从丹佛到匹兹堡的最短路径,则通往盐湖城的边不太可能有帮助。因此,您通过向它们添加从该节点到目标的距离的下限来偏置堆上的权重——在这种 2D 情况下,这将是直线或大圆距离,因为没有实际的道路可以比那个短。这种启发式倾向于将错误的选择推到更深的堆中,因此它们通常永远不会被探索。
不过,我无法证明您构建 3D 迷宫的方法适用于 A* 搜索。
我怀疑很多时间会花在这两行:
v=vertices.index(u)
if v in covered:continue
这两行都是 O(n) 操作,其中 n 是图中的顶点数。
我建议您将第一个替换为字典(从顶点名称映射到顶点索引),然后将 covered
从列表更改为集合。
这应该使两个操作的时间复杂度为 O(1),并且可以使您的速度提高几个数量级。
我最初的方法是使用一些基本的回溯:
boolean[][][] visited = new boolean[h][w][d];
boolean foundPath = false;
public void path(int y, int x,int z) {
visited[y][x][z] = true;
if (x == targetx && y == target && z == targetz) {
foundPath = true;
return;
}
if (!visited[y - 1][x][z] && !foundPath) //if up
path(y - 1, x, z);
if (!visited[y + 1][x][z] && !foundPath) //if down
path(y + 1, x, z);
if (!visited[y][x - 1][z] && !foundPath) //if left
path(y, x - 1, z);
if (!visited[y][x + 1][z] && !foundPath) //if right
path(y, x + 1, z);
if (!visited[y][x][z+1] && !foundPath) //if forward
path(y, x, z + 1);
if (!visited[y][x][z-1] && !foundPath) //if backward
path(y, x + 1, z - 1);
if (foundPath) return;
visited[y][x][z] = false;
}
我正在尝试编写一个可以解决 3D 迷宫的 Python 脚本,我正在使用 Dijkstra 算法和优先级队列(包含在模块 heapq 中)来完成它。这是我的主要功能代码:
from heapq import *
def dijkstra(start,end,vertices,obstacles):
covered=[]
s=vertices.index(s)
currentVertex=s
liveDistances={}
for i in range(len(vertices)):
liveDistances[i]=inf
liveDistances[s]=0
next=[[liveDistances[s],s]]
while next:
np,currentVertex=heappop(next)
covered.append(currentVertex)
for u in sons(vertices[currentVertex]):
v=vertices.index(u)
if v in covered:continue
if 1+liveDistances[currentVertex]<liveDistances[v]:
liveDistances[v]=1+liveDistances[currentVertex]
heappush(next,[liveDistances[v],v])
if liveDistances[vertices.index(e)]!=inf:
return liveDistances[vertices.index(e)]
else:
return "No path!"
所以基本上它只是将 Dijkstra 应用于 3D 图形。
该程序运行良好,但我想知道它在 10 秒内解决一个 100x100 的 2D 迷宫或在 2 分钟内解决一个 30x30x30 的迷宫是否正常? 我在这里实施错误吗?或者它只是正确的执行时间?我可以增强它吗?
我寻求增强的原因是因为我被要求在不到 5 秒(时间限制)内解决问题(在最大 40x40x40 的 3D 迷宫中找到最短路径)。
你知道 A* 搜索 ("A-star") 吗?这是对 Dijkstra 算法的修改,当您对情况的几何结构有所了解时,它可以提供很大帮助。未经修改的 Dijkstra 假设任何边缘都可能是通往目标的极短路径的起点,但情况的几何形状通常不允许这样做,或者至少使其不太可能。
基本思想是,如果您试图找到从丹佛到匹兹堡的最短路径,则通往盐湖城的边不太可能有帮助。因此,您通过向它们添加从该节点到目标的距离的下限来偏置堆上的权重——在这种 2D 情况下,这将是直线或大圆距离,因为没有实际的道路可以比那个短。这种启发式倾向于将错误的选择推到更深的堆中,因此它们通常永远不会被探索。
不过,我无法证明您构建 3D 迷宫的方法适用于 A* 搜索。
我怀疑很多时间会花在这两行:
v=vertices.index(u)
if v in covered:continue
这两行都是 O(n) 操作,其中 n 是图中的顶点数。
我建议您将第一个替换为字典(从顶点名称映射到顶点索引),然后将 covered
从列表更改为集合。
这应该使两个操作的时间复杂度为 O(1),并且可以使您的速度提高几个数量级。
我最初的方法是使用一些基本的回溯:
boolean[][][] visited = new boolean[h][w][d];
boolean foundPath = false;
public void path(int y, int x,int z) {
visited[y][x][z] = true;
if (x == targetx && y == target && z == targetz) {
foundPath = true;
return;
}
if (!visited[y - 1][x][z] && !foundPath) //if up
path(y - 1, x, z);
if (!visited[y + 1][x][z] && !foundPath) //if down
path(y + 1, x, z);
if (!visited[y][x - 1][z] && !foundPath) //if left
path(y, x - 1, z);
if (!visited[y][x + 1][z] && !foundPath) //if right
path(y, x + 1, z);
if (!visited[y][x][z+1] && !foundPath) //if forward
path(y, x, z + 1);
if (!visited[y][x][z-1] && !foundPath) //if backward
path(y, x + 1, z - 1);
if (foundPath) return;
visited[y][x][z] = false;
}