Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/algorithm/11.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Algorithm 状态空间搜索:A*和广度优先搜索_Algorithm_Search_Breadth First Search_A Star_State Space - Fatal编程技术网

Algorithm 状态空间搜索:A*和广度优先搜索

Algorithm 状态空间搜索:A*和广度优先搜索,algorithm,search,breadth-first-search,a-star,state-space,Algorithm,Search,Breadth First Search,A Star,State Space,所以我为游戏Sokoban实现了两个不同的解算器 求解器很简单,给定一个起始状态(位置),如果初始状态是目标状态,则返回结果。否则生成子状态并将它们存储到与算法对应的任何数据结构中。(BFS的队列和a*的优先级队列)然后从数据结构中弹出第一个子状态,检查其目标状态是否为else生成子状态并存储到结构中,重复此过程,直到找到目标状态 目前,A*算法确实比BFS更好,因此在找到结果之前生成的节点更少。然而,我的问题是A*算法需要更长的计算时间。例如,在其中一个级别中,BFS算法生成26000个节点,

所以我为游戏Sokoban实现了两个不同的解算器

求解器很简单,给定一个起始状态(位置),如果初始状态是目标状态,则返回结果。否则生成子状态并将它们存储到与算法对应的任何数据结构中。(BFS的队列和a*的优先级队列)然后从数据结构中弹出第一个子状态,检查其目标状态是否为else生成子状态并存储到结构中,重复此过程,直到找到目标状态

目前,A*算法确实比BFS更好,因此在找到结果之前生成的节点更少。然而,我的问题是A*算法需要更长的计算时间。例如,在其中一个级别中,BFS算法生成26000个节点,而A*只生成3488个节点,但A*比BFS花费的时间长一秒

通过使用时间分析器,我得出结论,用于存储节点的优先级队列数据结构是导致这种速度减慢的原因。因为每次将节点插入队列时,优先级队列都必须运行启发式函数算法,以确定新状态是否应该是最接近目标状态的状态(因此它将该节点放在队列中的第一个位置)


现在我的问题是,你们认为这是我的实现中的一个问题,还是这是正常的,因为在计算启发式函数时会产生开销。

你们确定实际上使用的是堆栈而不是BFS的队列吗?如果您使用的是堆栈,那么您所做的就是DFS,这可能并不总是为您提供到目标的最短路径

至于为什么A*较慢,我认为如果不知道启发式函数的复杂性,就很难说了,假设您的优先级队列正在进行O(Log(N))插入/删除(如果不是,那就是您的问题)。如果你的启发式算法做了大量的工作,它可能会主导所需的计算,并抵消从较少节点中获得的任何收益


如果启发式函数确实是罪魁祸首,一个可能的解决方案是使用更简单的启发式。

您的实现存在问题。当你想解决你的问题而不是用一个基本的(但可能是精确的)方法时,可以使用启发式。他们用最优性换取速度:你冒着得到一个非完美解决方案的风险来换取快速得到它。使用启发式方法使您的方法比其确切版本慢是没有意义的


当然,如果不了解更多关于实现的信息,就不可能更有帮助,但我可以向您保证,如果您的启发式算法使您的解算器速度变慢,那么使用它是不值得的。

另一个需要研究的假设(假设是我们在没有代码可供查看的情况下所能做的最好的假设):也许你不必要地经常计算启发式分数(我认为在你的情况下,计算启发式分数是一件相对昂贵的事情)

在代码中,您在哪里计算启发式分数?你呢

  • 在将其推入优先级队列之前,对每个节点执行一次此操作,然后将结果存储在某个
    节点
    对象中,以便在需要时立即检索
  • 或者,您是否只计算优先级队列用于确定节点顺序的
    比较器
    函数/函子/etc.中的启发式分数
  • 在第二种情况下,您将非常频繁地重新计算节点的启发式分数,之前您已经对这些节点进行了计算。例如,假设优先级队列中当前有100个节点,并尝试插入一个新节点。在第二种情况下,插入新节点可能需要与这100个现有节点中的一组进行比较,然后才能确定新节点属于何处,因此可能需要一组额外的启发式分数计算

    如果使用第一个选项(这是我的建议),那么要添加的100个现有节点和新节点都将已经计算出它们的启发式分数(精确计算一次),并作为成员变量存储在内存中。与一组现有节点执行一组比较将非常便宜,它只需要获取已经计算的启发式分数,而不需要重新计算它们

    实际上,根据您问题中的文本,我怀疑您当前确实在使用(效率低下的)第二个实现。否则,优先级队列根本不会调用启发式函数


    如果使用上面描述的更高效的第一个实现,您仍然在为一个过于昂贵的启发式函数而挣扎,那么您可以尝试研究是否有可能在生成后续状态期间编写一个更高效的增量实现。这是否可能在很大程度上取决于你的启发式函数在做什么。但是,我可以想象,在某些情况下,如果你已经

    • 当前状态
      s
    • 当前启发式分数
      h(s)
    • 移动到后续状态
      s'

    您可能能够导出一个高效的增量算法,该算法根据移动的内容计算如何增量修改
    h(s)
    ,以确定
    h(s')
    (然后您可以将其立即存储在
    s'
    的节点中)

    如果使用Python编写,
    PriorityQueue
    速度较慢,改用
    heapq
    (除非你是多线程)是的,我确实在使用你提到的第二种方法,现在我已经实现了第一种方法,我看到在运行时间方面有了巨大的改进!多谢各位