【问题标题】:Optimal algorithm to solve this maze?解决这个迷宫的最佳算法?
【发布时间】:2017-04-22 11:10:42
【问题描述】:

我目前遇到了我们大学的讲师给我们的一个挑战。我们一直在研究最流行的寻路算法,例如 Dijkstra 和 A*。虽然,我认为这个挑战练习需要其他东西,这让我很难过。

需要解决的迷宫的可视化表示:


颜色图例
蓝色 = 起始节点
灰色 = 路径
绿色 = 目标节点

它应该解决的方法是,当移动完成时,它必须一直这样做,直到它与迷宫边缘或障碍物(黑色边框)发生碰撞。它还需要以尽可能少的行移动来解决(在本例中为 7)

我的问题:有人可以将我推向正确的方向来研究什么算法吗?我认为 Dijkstra/A* 不是要走的路,考虑到最短路径并不总是正确给定分配的路径。

【问题讨论】:

  • 是的。您的示例显示了 7 条边,每条边的成本为 1。

标签: algorithm


【解决方案1】:

用Dijkstra/A*还是可以解决的,需要改变的是邻居的配置。


先说一点背景:

Dijkstra 和 A* 是在图上制定的通用寻路算法。当我们有一个角色在网格上移动而不是图形时,图形的位置可能并不那么明显。但它仍然存在,构建图表的一种方法如下:

  • 图形的节点对应于网格的单元格
  • 与相邻单元格对应的节点之间存在边。

实际上,在大多数涉及一些配置和它们之间的转换的问题中,都可以构造一个对应的图,并应用 Dijkstra/A*。因此,也可以解决sliding puzzlerubik's cube 等问题,这些问题显然与在网格上移动的角色有很大不同。但是它们有状态和状态之间的转换,因此可以尝试图搜索方法(这些方法,尤其是像 Dijkstra 算法这样的不知情的方法,由于搜索空间大,可能并不总是可行,但原则上可以应用它们)。


在您提到的问题中,图表与典型角色移动的图表没有太大区别:

  • 节点仍然可以是网格的单元格
  • 现在,从一个节点到节点之间的边将对应于有效运动(在边界或障碍物附近结束),在这种情况下,这些边并不总是与网格单元的四个空间直接相邻节点重合。李>

正如 Tamas Hegedus 在 cmets 部分中指出的那样,如果使用 A*,应该选择什么启发式函数并不明显。

基于曼哈顿或欧几里得距离的标准启发式方法在这里无效,因为它们可能高估到目标的距离。

一种有效的启发式方法是 id(row != destination_row) + id(col != destination_col),其中 id 是身份函数,其中 id(false) = 0 和 id(true) = 1。

【讨论】:

  • 你会使用什么启发式函数?
  • @TamasHegedus 好问题,标准启发式(基于曼哈顿,欧几里得距离)在这里无效,因为它们会高估目标距离。一个有效的选择是 id(row != destination_row) + id(columnn != destination_column),其中 id 是 id(true) = 1 和 id(false) = 0 的恒等函数。我不认为这是非常有用,但它是有效的,可能比不提供信息略好。我会继续思考 :) 如果您有任何想法,请告诉我(或者应该单独提出不同的问题)。
  • 您好,我尝试使用给定的启发式公式来实现这个想法,虽然它适用于这个迷宫示例,但它不适用于其他示例。您是否考虑过适用于任何迷宫的可能启发式方法? :)
  • 你好,是的,启发式应该适用于任何迷宫,可能错误在其他地方。您也可以尝试设置 heuristic = 0,这也是有效的(虽然它不是很有趣,让 A* 的行为就像 Dijkstra 的算法一样),只是为了测试 - 在这种情况下 A* 是否仍然存在问题?
  • @qwertyman 这是另一个迷宫:prntscr.com/f2vege 我添加了橙色边缘,蓝色是起始节点,绿色是目标节点。我还添加了起始节点的 F 成本边缘。如您所述,正在计算 H 成本。 G-cost 我保持不变:目标节点到边缘和起始节点之间的距离(getTarget().distance(destination))。有什么想法吗?
【解决方案2】:

Dijkstra/A* 很好。您需要仔细考虑您所考虑的图节点和图边。

站在蓝色单元格中(我们称之为5,5),你有三个有效的动作:

  • 向右移动一个单元格(到6,5

  • 向左移动四个单元格(到1,5

  • 向上移动五个单元格(到5,1

请注意,您不能5,5 转到4,55,4。将相同的推理应用于新节点(例如,从5,1,您可以转到1,110,15,5),您将获得运行 Dijkstra/A* 的图表。

【讨论】:

    【解决方案3】:

    您需要评估每一个可能的移动,并采取以最小距离产生的移动。类似于以下内容:

    int minDistance(int x, int y, int prevX, int prevY, int distance) {
      if (CollionWithBorder(x, y)  // can't take this path
        return int.MAX_VALUE;
    
      if (NoCollionWithBorder(x, y)  // it's OK to take this path
      {
        // update the distance only when there is a long change in direction
        if (LongDirectionChange(x, y, prevX, prevY))
          distance = distance + 1;
      )
    
      if (ReachedDestination(x, y)  // we're done
        return distance;
    
      // find the path with the minimum distance      
      return min(minDistance(x, y + 1, x, y, distance),  // go right
                 minDistance(x + 1, y, x, y, distance),  // go up
                 minDistance(x - 1, y, x, y, distance),  // go down
                 minDistance(x, y - 1, x, y, distance)); // go left
    }
    
    bool LongDirectionChange(x, y, prevX, prevY) {
      if (y-2 == prevY && x == prevX) ||(y == prevY && x-2 == prevX)
        return true;
      return false;
    }
    

    这是假设不允许对角线移动。如果是,请将它们添加到 min() 调用中:

      minDistance(x + 1, y + 1, distance),  // go up diagonally to right
      minDistance(x - 1, y - 1, distance),  // go down diagonally to left
      minDistance(x + 1, y - 1, distance),  // go up diagonally to left
      minDistance(x - 1, y + 1, distance),  // go down diagonally to right
    

    【讨论】:

    • 这对于典型情况来说是正确的,但在此处提出的具体问题中,代理沿单行/列进行长时间移动(直到第一个障碍物)会产生单个成本单位。
    • 是的,你是对的。我编辑了代码以更改当我们沿行或列移动时如何增加距离。
    猜你喜欢
    • 1970-01-01
    • 2023-03-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多