Python 理解单目标迷宫的A*启发法

Python 理解单目标迷宫的A*启发法,python,artificial-intelligence,path-finding,a-star,heuristics,Python,Artificial Intelligence,Path Finding,A Star,Heuristics,我有一个迷宫,如下所示: |||||||||||||||||||||||||||||||||||| | P| | ||||||||||||||||||||||| |||||||| | | || | | ||||||| || | | || | | | | |||| ||||||||| || ||||| | || | | | | || || | | || | | | | |

我有一个迷宫,如下所示:

||||||||||||||||||||||||||||||||||||
|                                 P|
| ||||||||||||||||||||||| |||||||| |
| ||   |   |      |||||||   ||     |
| || | | | | |||| ||||||||| || |||||
| || | | | |             || ||     |
| || | | | | | ||||  |||    |||||| |
| |  | | |   |    || ||||||||      |
| || | | |||||||| ||        || |||||
| || |   ||       ||||||||| ||     |
|    |||||| |||||||      || |||||| |
||||||      |       |||| || |      |
|      |||||| ||||| |    || || |||||
| ||||||      |       ||||| ||     |
|        |||||| ||||||||||| ||  || |
||||||||||                  |||||| |
|+         ||||||||||||||||        |
||||||||||||||||||||||||||||||||||||
目标是让
p
找到
+
,子目标为

  • +
    的路径成本最低(1跳=成本+1)
  • 搜索的单元数(展开的节点)最小化
我试图理解为什么我的A*启发式算法的性能比我的贪婪的Best First实现差得多。以下是每种类型的两位代码:

#Greedy Best First -- Manhattan Distance
self.heuristic = abs(goalNodeXY[1] - self.xy[1]) + abs(goalNodeXY[0] - self.xy[0])

#A* -- Manhattan Distance + Path Cost from 'startNode' to 'currentNode'
return abs(goalNodeXY[1] - self.xy[1]) + abs(goalNodeXY[0] - self.xy[0]) + self.costFromStart
在这两种算法中,我都使用了一个
heapq
,根据启发式值进行优先级排序。以下两种情况的主搜索循环相同:

theFrontier = []
heapq.heappush(theFrontier, (stateNode.heuristic, stateNode)) #populate frontier with 'start copy' as only available Node

#while !goal and frontier !empty
while not GOAL_STATE and theFrontier:
    stateNode = heapq.heappop(theFrontier)[1] #heappop returns tuple of (weighted-idx, data)
    CHECKED_NODES.append(stateNode.xy)
    while stateNode.moves and not GOAL_STATE:
        EXPANDED_NODES += 1
        moveDirection = heapq.heappop(stateNode.moves)[1]

        nextNode = Node()
        nextNode.setParent(stateNode)
        #this makes a call to setHeuristic
        nextNode.setLocation((stateNode.xy[0] + moveDirection[0], stateNode.xy[1] + moveDirection[1]))
        if nextNode.xy not in CHECKED_NODES and not isInFrontier(nextNode):
            if nextNode.checkGoal(): break
            nextNode.populateMoves()
            heapq.heappush(theFrontier, (nextNode.heuristic,nextNode))
现在我们来讨论这个问题。虽然A*找到了最佳路径,但这样做的成本相当高。为了找到
cost:68
的最佳路径,它扩展(导航和搜索)452个节点来完成此操作。

而贪婪的最佳实现只在160次扩展中找到了次优路径(成本:74)


我真的很想知道我错在哪里。我意识到贪婪的最佳优先算法可以自然地表现出这样的行为,但是节点扩展中的差距太大了,我觉得这里一定出了问题。。任何帮助都将不胜感激。如果我在上面粘贴的内容在某种程度上不清楚,我很乐意添加详细信息。

A*search试图找到问题的最佳解决方案,而贪婪的best first则试图找到任何解决方案。A*有一个非常非常困难的任务,它必须投入大量的工作来探索可能是最好的每一条路径,而贪婪的最佳优先算法只是直接寻找最接近目标的选项。

A*提供了问题的最佳答案,贪婪的最佳优先搜索提供了任何解决方案

预计A*必须做更多的工作

如果您希望a*的变化不再是最优的,但返回解的速度要快得多,您可以查看加权a*。它只包括给启发式设置权重(权重>1)。在实践中,它会给您带来巨大的性能提升

例如,您可以尝试以下方法:

return 2*(abs(goalNodeXY[1] - self.xy[1]) + abs(goalNodeXY[0] - self.xy[0])) + self.costFromStart

因为这个问题还没有解决,即使OP提出的错误可以解决,我觉得我需要问这个问题,也许可以回答什么是错误的,为什么Fezvez的答案可以解决这个问题:你是否用*算法检查了所有节点的启发式值,并注意到了一些奇怪的事情?他们不都是平等的吗?因为即使您的启发式对于最佳优先算法是正确的,它也不能直接适用于您的a*算法。我用java做了一个类似的项目,我遇到了这个问题,这就是为什么我要问这个问题。例如,假设您有以下兴趣点:

  • 开始(P)-(0,0)
  • 结束(+)-(20,20)
  • P1-(2,2)->(你的启发式)+(路径成本)=((20-2)+(20-2))+((2-0)+(2-0))=40
  • P2-(4,3)->(你的启发式)+(路径成本)=(20-4)+(20-3))+((4-0)+(3-0))=40
如果我没弄错的话,你在迷宫中的所有点都是这样。现在,考虑到A*算法通常与具有启发式(和路径成本)的广度优先算法一样实现,因为启发式算法总是给出相同的总数(F=h+g),它实际上成为广度优先算法,也为您提供了可能的最佳解,但在实践中,它总是比A*通常要慢。现在正如费兹韦兹所建议的,给你的启发式赋予权重可能只是将两个世界中最好的(最佳优先和广度优先)混合在一起,并与上面给出的点类似:

  • 开始(P)-(0,0)
  • 结束(+)-(20,20)
  • P1-(2,2)->2*(你的启发式)+(路径成本)=2*((20-2)+(20-2))+((2-0)+(2-0))=76
  • P2-(4,3)->2*(你的启发式)+(路径成本)=2*((20-4)+(20-3))+((4-0)+(3-0))=73

别介意我之前的评论。这是完全正常的行为。起初我以为+是个开始。找到这样的问题的最佳解决方案是困难的;这就是为什么我们通常不费心的原因。我发现有一件事并没有真正解决两者之间的差异,但总体上确实提高了效率,那就是在主循环的末尾添加了以下内容:
CHECKED\u NODES.append(nextNode.xy)
——这似乎将我对这两种算法的扩展减少了一半。。。