Algorithm 如何轻松知道迷宫是否有一条从起点到终点的道路?
我使用0,1数组实现了一个迷宫。迷宫中的入口和目标是固定的。入口始终为迷宫的0,0点。目标始终是迷宫的m-1、n-1点。我现在使用广度优先搜索算法,但速度不够快。特别适用于大型迷宫(100*100左右)。有人能帮我做一下这个算法吗 以下是我的解决方案:Algorithm 如何轻松知道迷宫是否有一条从起点到终点的道路?,algorithm,Algorithm,我使用0,1数组实现了一个迷宫。迷宫中的入口和目标是固定的。入口始终为迷宫的0,0点。目标始终是迷宫的m-1、n-1点。我现在使用广度优先搜索算法,但速度不够快。特别适用于大型迷宫(100*100左右)。有人能帮我做一下这个算法吗 以下是我的解决方案: queue = [] position = start_node mark_tried(position) queue << position while(!queue.empty?) p = queue.shift #pop
queue = []
position = start_node
mark_tried(position)
queue << position
while(!queue.empty?)
p = queue.shift #pop the first element
return true if maze.goal?(p)
left = p.left
visit(queue,left) if can_visit?(maze,left)
right = p.right
visit(queue,right) if can_visit?(maze,right)
up = p.up
visit(queue,up) if can_visit?(maze,up)
down = p.down
visit(queue,down) if can_visit?(maze,down)
end
return false
queue=[]
位置=开始节点
mark_(职位)
队列可能是最差的答案
1) 向前走,直到你不能动为止
2) 左转
3) 冲洗并重复
如果你成功了,就有一个结局
更好的解决方案
在迷宫中穿行,为打开和关闭的节点保留两个列表。使用著名的算法
选择“计算下一个节点”和“放弃死角节点”。如果打开的列表中的节点已用完,则不会有出口。以下是一个简单的算法,速度应该快得多:
- 从起点/终点移动到第一个交叉点。您可以忽略该连接点和开始/目标之间的任何内容
- 确定迷宫中所有死角的位置(它们有三面墙)。返回到下一个交叉点,并将此路径从搜索树中删除
以这种方式消除所有死胡同后,应该只剩下一条路(或者如果有几种方法可以达到目标,则有几种方法)。我会用一个实现来解决这个问题。如果您想要更高的速度,您可以优化为仅从交叉点生成节点,而不是从每个平铺/正方形/步骤生成节点。一种不需要访问迷宫中所有节点的方法如下:
- 创建一个整数[],每个迷宫“房间”有一个值
- 创建一个队列,添加[startpoint,count=1,delta=1]和[goal,count=-1,delta=-1]
- 通过以下方式开始为路线着色:
- 从队列头弹出一个对象,将计数放在迷宫点李>
- 检查所有可到达的房间是否有与房间三角形相反的计数标志,如果发现其中一个,则迷宫已解决:双向运行,并连接房间计数上下最大台阶的路线
- 否则,将所有无计数的可到达房间添加到队列尾部,房间计数中添加增量
- 如果队列为空,则无法通过迷宫
这不仅可以确定是否存在路径,还可以显示通过迷宫的最短路径
你不需要回溯,所以它是O(迷宫房间的数量)我不会在那里使用AStar算法,除非我真的需要,因为这可以通过一些简单的“着色”来完成
# maze is a m x n array
def canBeTraversed(maze):
m = len(maze)
n = len(maze[0])
colored = [ [ False for i in range(0,n) ] for j in range(0,m) ]
open = [(0,0),]
while len(open) != 0:
(x,y) = open.pop()
if x == m-1 and y == n-1:
return True
elif x < m and y < n and maze[x][y] != 0 not colored[x][y]:
colored[x][y] = True
open.extend([(x-1,y), (x,y-1), (x+1,y), (x,y+1)])
return False
哪些产出:
# For canBeTraversed
100 x 100 : 0:00:00.002650 average on 100 runs
1000 x 1000 : 0:00:00.198440 average on 100 runs
# For canBeTraversedAStar
100 x 100 : 0:00:00.016100 average on 100 runs
1000 x 1000 : 0:00:01.679220 average on 100 runs
这里显而易见的一点是:要使A*顺利运行,需要进行大量优化,我没有费心去追求
我想说:
不要优化
(仅限专家)暂时不要优化
当你说得太多的时候,你在说多少时间?实际上,100x100网格非常容易用蛮力解析,这是一个笑话://使用这种方法时要小心。由于闭环,可能存在死胡同。这样一个循环只有两堵墙,会导致你永远循环。保留一个正在运行的节点列表。如果你再次遇到同一个节点,你的循环。对于A-Star算法,我已经阅读了wiki页面,我认为我不需要节点的分数,但是没有分数,这只是简单的呼吸优先搜索,没有比我的解决方案更好的了。高是对的——在一个随机迷宫中,你不能可靠地得到A*——线性接近出口并不意味着什么。@尼克:事实上,你可以,因为启发式只需要有一个属性,它决不会低估从一点到目标的距离。因此,你可以使用直线距离。@Peter:你的意思不是高估了吗?低估在A*(例如使用欧几里得距离)中似乎很常见@Matthieu:是的。应该说“永远不要高估”。
def tryIt(func,size, runs):
maze = [ [ 1 for i in range(0,size) ] for j in range(0,size) ]
begin = datetime.datetime.now()
for i in range(0,runs): func(maze)
end = datetime.datetime.now()
print size, 'x', size, ':', (end - begin) / runs, 'average on', runs, 'runs'
tryIt(canBeTraversed,100,100)
tryIt(canBeTraversed,1000,100)
tryIt(canBeTraversedAStar,100,100)
tryIt(canBeTraversedAStar,1000,100)
# For canBeTraversed
100 x 100 : 0:00:00.002650 average on 100 runs
1000 x 1000 : 0:00:00.198440 average on 100 runs
# For canBeTraversedAStar
100 x 100 : 0:00:00.016100 average on 100 runs
1000 x 1000 : 0:00:01.679220 average on 100 runs