其他程序员 . 我的一个项目需要帮助 . 我正在制作迷宫解决方案 . 它读取的图像文件必须是黑白的(黑色像素是墙,白色像素是路径),顶部只有一个像素是迷宫的入口,底部只有一个白色像素,出口 .
代码有三个主要部分:
1)程序首先在迷宫中创建节点,遵守一组规则 . 例如,这是一个简单的迷宫:
这里是所有以红色绘制的节点:
节点就像角落,十字路口,每个可以改变方向的点 . 还测量每个节点与迷宫出口之间的距离 . 当它生成所有节点时,它将它们放在一个列表中 .
2)一旦生成所有节点,程序将遍历列表中的所有节点,并尝试在其他节点的每个方向上搜索,“链接”它们, Build 可能的路径 . 例如,如果它检测到节点上方有一条路径,它将从节点的坐标中搜索一行中的每个像素,并向上,再次遍历所有节点列表以查看是否有另一个节点匹配这些坐标 . 如果它在某个时刻找到一个,它会将它们链接起来,并开始向右搜索(如果有一条通向右边的路径),等等 .
3)一旦所有节点链接在一起并 Build 了每个可能的路径,它将从迷宫的入口节点开始,并运行我的A *算法的实现以找出正确的路径,并返回如果它在一个死路 . 如你所见,它毫无困难地解决了迷宫问题 .
所以我的程序有效 . 那有什么问题呢?问题是节点链接部分 . 在小型迷宫上,需要半秒钟 . 但是让迷宫变得更大,那么节点的数量将急剧增加 . 而且由于它遍历节点列表很多时间(每个像素搜索每个节点一次),你可以想象我是否有60万个节点......这需要很长时间 .
所以's what I' m请求帮助:更好,更快的方式将节点链接在一起 . 我在pastebin上发布了整个代码(https://pastebin.com/xtmTm7wb,对不起,如果它有点乱,我编程的时候已经很晚了) . 节点链接部分从第133行开始并在第196行结束 .
这是节点链接代码:
counter = 0
last = 0
for node in nodelist:
a = node.pos[0]
b = node.pos[1]
if node.paths[0]:
for i in range(b-1,0,-1):
if mazepix[a,i] == blackpix:
break
if any(x.pos == (a,i) for x in nodelist):
for iteration in nodelist:
if iteration.pos == (a,i):
indexofnodetolinkto = iteration.index
break
node.connections.append(indexofnodetolinkto)
# print("Linking node %d and node %d..."%(node.index, indexofnodetolinkto))
break
if node.paths[1]:
for i in range(a+1,maze.size[0]):
if mazepix[i,b] == blackpix:
break
if any(x.pos == (i,b) for x in nodelist):
for iteration in nodelist:
if iteration.pos == (i,b):
indexofnodetolinkto = iteration.index
break
node.connections.append(indexofnodetolinkto)
# print("Linking node %d and node %d..."%(node.index, indexofnodetolinkto))
break
if node.paths[2]:
for i in range(b+1,maze.size[1]):
if mazepix[a,i] == blackpix:
break
if any(x.pos == (a,i) for x in nodelist):
for iteration in nodelist:
if iteration.pos == (a,i):
indexofnodetolinkto = iteration.index
break
node.connections.append(indexofnodetolinkto)
# print("Linking node %d and node %d..."%(node.index, indexofnodetolinkto))
break
if node.paths[3]:
for i in range(a-1,0,-1):
if mazepix[i,b] == blackpix:
break
if any(x.pos == (i,b) for x in nodelist):
for iteration in nodelist:
if iteration.pos == (i,b):
indexofnodetolinkto = iteration.index
break
node.connections.append(indexofnodetolinkto)
# print("Linking node %d and node %d..."%(node.index, indexofnodetolinkto))
break
counter += 1
percent = (counter/nbrofnodes)*100
if int(percent)%10 == 0 and int(percent) != last:
print("Linking %d%% done..."%percent)
last = int(percent)
print("All node linked.")
谢谢你,如果你阅读了所有这些,我知道这不是一个非常精确的问题,但是我花了很多时间来努力完成这项工作,现在我确实坚持了我可以改进它的方法^^ ” .
2 回答
你的程序非常慢,因为这部分需要很长时间,而且你为每个节点做了很多次:
有很多方法可以让它快得多 .
您可以使用位置作为键将节点放入字典中,因此您可以只查找位置以查找其中的节点 .
更好的是,您可以将节点放入行列表和列列表中,按位置排序,然后只尝试连接列表中的相邻节点 .
但最好的办法是完全忘记这些节点,直接在位图上进行BFS搜索 .
由于这是一个有趣的问题,我用一个简单的BFS编写了一个快速版本 . 我不想破坏你所有的乐趣,所以这里只是BFS部分,这样你就可以直接在图像上看到我的意思了:
我们不是使用带有对象和链接的单独集来记住路径,而是将像素标记为直接在图像中访问 . 我们只需检查所有4个方向,然后在需要时查找相邻的白色像素,而不是使用任何类型的实际链接来链接一个像素 .
这是一个逐级的BFS,所以我们总是知道新的像素离入口有多远,我们标记一个访问像素的颜色表示它与入口的距离(模3) . 这允许我们在找到出口时重建最短路径 .
编辑:已经很长一段时间了,OP已经有了他的乐趣,所以这里是完整的python解算器:
你的迷宫只有301x301像素,所以在我看来0.5秒对于解决方案来说太大了 . 当我使用栅格A *方法时:
整个解决方案只需
~1.873ms
,这与您的巨大差异~500ms
. 粗图A *有更大的开销,所以我很好奇并想测试它所以我编码我的版本(在 C++ 基于相同如上面的链接中的代码),结果是从图像获取的图形占用~3ms
并且图形A *占用~0.18ms
所以仍然与您的巨大差异(/ - 计算机设置差异) .So what to check for first?
我不是python编码器,但我没有在你的代码中看到任何图像访问 . 您应该检查您的图像访问是否快速 . 对于使用类似东西的新手而言,这是常见的错误
那些通常很慢(根据我在GDI Win32上的经验,比直接像素访问慢1000-10000倍)并且如果纠正会产生巨大的差异 . 有关详情,请参阅:
列表使用的另一个常见错误是在没有预分配的情况下逐步添加元素到列表 . 对于小列表来说,这不是问题,但是对于大量元素,在添加元素的情况下重新分配会通过一遍又一遍地复制内容来减慢速度 . 在列表中插入和删除元素也是如此 . 改进列表访问会产生巨大的影响,特别是对于多项式复杂性,如
O(n^2)
和更慢......Algorithm
算法的微小变化可以产生巨大的影响 . 在您的情况下,我使用 DIP 边缘检测技术和拓扑排序边缘的加速结构的组合 . 这会将
O(n)
或O(n^2)
搜索更改为简单的O(1)
操作 . 这个想法是按照xy
和yx
排序迷宫的所有顶点的有序列表 . 如果每个顶点在这样的结构中知道它的索引,它可以很容易地获得它的邻居顶点......Stack/heap trashing
这减缓了很多事情 . 特别是具有递归功能 . 较大的递归级别和操作数大小通过效果更差 .
Here my simple C++ example based on the link above
和用法:
代码基于 VCL (使用第二个链接中描述的位图),我也使用我的动态列表模板,因此:
List<double> xxx;
与double xxx[];
相同xxx.add(5);
将5
添加到列表的末尾xxx[7]
访问数组元素(安全)xxx.dat[7]
访问数组元素(不安全但快速直接访问)xxx.num
是数组的实际使用大小xxx.reset()
清除数组并设置xxx.num=0
xxx.allocate(100)
预分配100
项目的空间对不起,不是一个python编码器,但认为代码是直截了当的...所以我跳了你没有问题移植/适应你的环境 .
这里输出:
看起来它模仿你的...代码仍未优化,可以进一步改进...我认为你应该仔细看看
mx,my
和mxy[][],myx[][]
变量 . 这些是topologicaly索引排序的顶点,可以实现对代码的巨大加速...[Edit1]
我更新了A *搜索代码(thx到Matt Timmermans),所以这里有更新的结果: