如何理解优先队列深度优先搜索?

6
以下是使用一个栈和一个大表格标记访问节点的深度优先搜索(DFS)的伪代码实现:
DFS(N0):
   StackInit(S)
   S.push((N0, null))
   if isGoal(N0) then do
      return true
   markVisited(N0)
   S.push((null, N0))
   while !isEmpty(S) then do
      (N, parent) := S.pop()
      R := next((N, parent))
      if isNull(R) then do
         continue             // So no new node add to this layer.
      S.push((R, parent))
      if marked(R) then do
         continue
      if isGoal(R) then do    // If it's goal don't have to explore it.
         return true
      markVisited(R)
      if depthMax((R, parent)) then do
         continue
      S.push((null, R))
   return false

我需要解决的问题是对它的改进:将堆栈S替换为优先级队列PQ。该算法用于模拟IDA*算法(这在一本不幸没有用英语写成的教材中被说明,因此我不会提供参考书名):
DFS2(N0, f, LIMIT):
   PriorityQueueInit(PQ)
   // A node (N, parent) stored in PQ represents a path from `N0` to `N`\
        passing the node `parent`; A node with smaller value on f() is \
        prioritized than those with larger value.
   PQ.push((N0, null))
   if isGoal(N0) then do
      return true
   markVisited(N0)
   PQ.push((null, N0))
   while !isEmpty(PQ) then do     // (1)
      (N, parent) := PQ.poll()
      R := next((N, parent))      // (2)
      if isNull(R) then do
         continue
      PQ.offer((R, parent))
      if marked(R) then do
         continue
      if isGoal(R) then do
         return true
      markVisited(R)
      if f((R, parent)) > LIMIT then do
         continue
      PQ.offer((null, R))
   return false
  • (1): 在A*算法中,优先队列用于存储未被探索的节点,即开放列表。而在我提供的第一个DFS伪代码中,堆栈S是关闭列表,因此我认为在第二个伪代码中PQ也是关闭列表。那么第二个伪代码如何模拟IDA*算法,并使用关闭列表?
  • (2): 它从PQ中获取当前最小的节点,该节点可能不是节点N的兄弟节点,即它从包含节点N的当前子树“跳转”到另一个子树。这行代码的目的是什么?

有人能展示一下第二个算法的正确性吗,即为什么它可以用于IDA*算法?


更多信息更新:我已经花费了很多时间和精力研究这个问题,但由于以下原因,它似乎非常困难:

  1. All graphs appear in the textbook are drawn tree-like, i.e. only one parent for each node, to show the concepts. This makes me confused: Does the second algorithm only work for trees?

  2. Considering the line

    if f((R, parent)) > LIMIT then do ...
    

    If the second one also works for graph, not just tree, then there might be many parents go to R, should I consider all cases or just the current one, parent?


我以前没有遇到过 *迭代加深A**。你能指出为什么使用“模拟”比“执行”/“实现”更合适吗? - greybeard
@greybeard:是的,我指的是根据书中所述,可以使用DFS2(N0, f, LIMIT)来实现迭代加深A*算法。 - VimNing
由于这篇文章中有几个问题,我不确定您期望什么样的答案。为了正确回答它,您需要在答案中包含哪些要点? - Tomer Shetah
2
@TomerShetah:我的困惑在于我不相信第二个算法会实现IDA算法。因此,你只能回答这一句话:“有人能展示第二个算法的正确性吗?也就是说,为什么它可以用于IDA算法?” - VimNing
@TomerShetah: 不用谢。 - VimNing
1个回答

2

这里有很多内容。

据我所知,此代码始终返回到达队列顶部的第一个目标节点。在下面对f和next的假设下,该目标节点是f-optimal的。但我不会称其为IDA*。

首先,通常情况下,A*和IDA*都会同时打开当前节点的所有邻居节点。但是这段代码没有这样做。它使用迭代器但只有一个循环。第一次推送是当前节点的下一个兄弟节点,第二次推送是子节点。我想这没问题,除非兄弟节点应按增加f顺序枚举,以便有前途的兄弟节点不会在不前途的兄弟节点之后被隐藏。

其次,与A*不同,IDA*没有关闭列表。从某种意义上说,IDA*始终具有搜索树,因为如果它到达两个等价节点,则仍将它们视为不同的节点。A*具有关闭列表,但比此处介绍的更复杂。 A*处理循环的方式是,如果发现到已关闭节点的更便宜路径,则重新打开该节点。这段代码没有这样做,因此仅当永远不需要重新打开节点时才正确。因此,此代码需要f是所谓的一致启发式(在每条路径上,f沿路径不会下降),而A*仅需要f是可行的(f从不低估到达目标状态的成本)。


首先,通常情况下A和IDA都会同时打开当前节点的所有邻居。是的!我得出了同样的结论。我阅读了单源Dijkstra最短路径算法的证明,在证明中关键点就在于此。 - VimNing
代码中的这一行 R := next((N, parent)) // (2)next() 函数确实是按照您所说的 f-order 进行排序的。 - VimNing
从你的回答中我确信书中提供的代码是错误的,我会联系老师(也是这本书的作者)以确保这一点。这里是赏金~ - VimNing

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接