【问题标题】:Efficient Path finding algorithm avoiding zigzag's [duplicate]避免曲折的有效路径查找算法
【发布时间】:2014-12-05 18:47:30
【问题描述】:

我正在开发一个用电线连接物体的软件。这种布线有一条规则,即这些电线不能穿过其他物体,并且不接受对角线移动。

我知道的所有最短路径算法(A*、dijkstra 等)都可以找到这种类型的路径:

我不想在第二个屏幕截图中出现不必要的曲折。我如何实现这个目标?

注意:任何想尝试算法的人都可以使用this 应用程序。

另一个注意事项:这是我不想要的确切情况。它找到的是之字形路径,而不是“向右直到到达目标的 x 位置,向上直到到达目标的 y 位置”的路径,这与之字形路径的成本相同。

【问题讨论】:

  • 我知道 A* 将始终找到那些锯齿形图案,因为它使用了启发式方法,但解决此问题的一种方法是将启发式方法设置为有利于水平/垂直运动而不是对角线运动。我还没有看到你的算法,但它应该不难实现。这也适用于 Dijkstra,因为两种算法的工作方式几乎相同。
  • 您可以使用 L1 (abs[x + y]) 而不是 L2 (sqrt[x² + y²]) 距离作为启发式方法来避免这种情况。如果你不能斜着走,那无论如何这是一个更好的启发式方法。但这只是一个 hack,它是否有效也取决于节点从队列中弹出的顺序。
  • @larsmans 我已经在我的实现中尝试过这种启发式方法。但没有运气,它不起作用。
  • 您是否考虑过在方向变化时增加额外成本(如果 dx 或 dy 发生变化,则向 pathLength 添加 1(或更少)),因此它会发现只有 1 个方向变化而不是当前找到的 8 个路径。
  • A* 可以更改,因此额外转动比仅移动更昂贵。为此,您还需要添加从 (N,S,W,E) 填充单元格的方向,因此对于直线移动,成本可以是例如每个单元格 1 个,对于转弯移动(改变方向/zig/ zag) 是 2。这种方式 zig zag 路径将被认为是更长的 den 笔直路径......(呵呵,jast 看到之前的评论有相同的建议......抱歉重复)

标签: algorithm shortest-path path-finding


【解决方案1】:

您当前的算法会找到一条最短路径Pmin,但改进后的算法应该找到一条最短路径,且转弯次数最少(Pmin, Tmin)。一般解决方案要求您使用一对数字而不是单个数字。如果新发现的Pnew 小于当前Pmin 或者如果它相等但Tnew 小于Tmin 则将(Pnew, Tnew) 作为新的最小路径。

如果电路板足够小,您仍然可以使用当前使用的单个数字,但该数字必须是复合数字C = P * N + T,其中N 是足够大且足够小的常数。它必须大于该棋盘的最大可能 T,这几乎是棋盘中的瓷砖总数。它还必须足够小,以便在算法碰巧处理棋盘中最大的路径时不会出现整数溢出,这也是棋盘中的瓷砖总数。所以N必须满足这两个条件(B是棋盘的总数):

N > B
B * N < INT_MAX

如果B 大于SQRT(INT_MAX),那么这个系统是无法解决的,你应该使用一对值。 N 应该是 SQRT(INT_MAX),对于 232 来说是 216。

现在的主要问题是如何计算所有回合,但这取决于您拥有的算法。添加那部分应该不会太难。

【讨论】:

  • 我接受这个作为答案,因为它提供了我如何对转弯进行惩罚的详细信息。
【解决方案2】:

直观地说,你可以通过给你的代理一个“动力”来做到这一点。

具体来说,将状态空间的大小增加四倍;您可以跟踪代理最后一次向上、向右、向左还是向下移动。将您的网络中的成本扩大一些较大的因素,并为改变方向的移动分配一个小的惩罚。

【讨论】:

  • 我喜欢这种想法。但是,将状态空间加倍似乎就足够了:“通过水平移动到达这里”与“通过垂直移动到达这里”。
【解决方案3】:

使用一对值(双精度数、整数等)进行距离计算。

第一个是距离,第二个是圈数。

按词法排序,所以第一个比第二个更重要。

这在数学和编程上都比“对转弯使用小罚分”更简洁。

每个节点都是重复的。节点“垂直进入”和“水平进入”,因为它们对匝数有影响。

启发式是曼哈顿距离,如果您与目标不完全水平或垂直对齐,则需要转弯。

不利的一面是,这种技术会妨碍跳转点优化,因为到某个位置的对称路径要少得多(因为某些路径的转弯比其他路径多)。

【讨论】:

  • 能否详细说明“每个节点都重复”?
  • @inverted 一个网格节点。在描述到达特定网格节点的成本时,您需要知道您是 e-w 还是 n-s 输入了该节点,因为当您离开该节点时,这将改变您的轮数。现在我认为如果轮流仅用于平局,您可以使用 3 状态场(此成本在 n-s 入口,e-w,两者)。
  • 哦,所以“重复”是指每个节点都有一个额外的字段来存储条目对齐方式?
【解决方案4】:

在内部,算法会评估许多可能的路径,并选择最短的路径。

如果你稍微调整算法,那么它会为每次方向变化计算一个额外的惩罚,然后它会选择方向变化最少的最短路径。

【讨论】:

    【解决方案5】:

    您只需稍微修改 A* 算法的启发式。我的头顶上的一个:如果你不喜欢这些转弯,你可以惩罚每个转弯。

    因此,您的启发式方法将取决于转弯次数和到目标的曼哈顿距离。 重要的一点是不要忘记它不应高估到目标的距离。阅读更多如何选择heuristic here。

    【讨论】:

      【解决方案6】:

      我并不特别熟悉搜索算法,但这将是最好的程序化方法,如下所示。

      我们使用的对象:

      vertex { //generic x,y coordinate
          int x;
          int y;
      }
      vertices[3]; //3 vertices: 0, 1, 2 (0 is start, 1 is mid, 2 is end);
      

      还有我们的算法,它依赖于已经发现的最有效路径,没有像¯|_|¯这样的怪异现象

      boolean hasObstacles = false;
      
      int increment = 0;
      //there's some other ways to set this up, but this should make the most sense to explaining which way we need to go
      if(vertices[0].x < vertices[2].x)
          increment = 1;
      else
          increment = -1;
      
      for(int i = vertices[0].x; i != vertices[2].x; i = i + increment) {
          //check for collision at coordinate (i, vertices[0].y), set hasObstacles to true if collision
      }
      
      if(vertices[0].y < vertices[2].y)
          increment = 1;
      else
          increment = -1;
      
      for(int i = vertices[0].y; i != vertices[2].y; i = i + increment) {
          //check for collision at coordinate (vertices[2].x, i), set hasObstacles to true if collision
      }
      
      if(!hasObstacles) {
          //we can remove these 3 vertices and add this point to our list of vertices.
          vertex corner = new vertex(vertices[2].x, vertices[0].y) // relocation of point
      }
      

      扫描应该一次进行一个顶点。如果这 3 个顶点被替换为单个顶点,则下一次扫描应使用该新顶点作为 0。

      【讨论】:

      • 这是一种直观的启发式方法。如果我找不到更好的方法,我会实现它。
      【解决方案7】:

      现在,您的算法回答了“最佳路径经过哪些方格?”的问题。您的图表的每个正方形都有一个节点,每对相邻的正方形都有一条边。

      改成“最优路径在哪里跨越方块之间的边界?”

      你的图表会改变:

      • 图形节点:相邻方格之间每条边的中间 + 开始 + 结束。
      • 图形边:在每个正方形中,它们连接每对正方形边。

      现在您可以对相对方形边缘的连接和相邻方形边缘的连接进行不同的定价。赋予第二个更大的权重将减少之字形的数量。

      【讨论】:

        【解决方案8】:

        您的问题很重要,因为例如如果你贪婪地尽可能地向上或向右走,那么你可能会遇到一个需要疯狂之字形路径才能完成的物体迷宫,而如果你在狭窄的迷宫之前停下来,你可能会改变通过基本上绕过迷宫来减少方向。你可能会在你的道路上的任何地方遇到这种困境,而不仅仅是在开始时。解决此问题的一种方法是使用 Dijkstra 并定义您可以前往的位置网格,然后将移动定义为 2 步长而不是 1 步长。如果移动在一个方向上是纯水平或纯垂直,则定义两个连接网格点之间的距离非常小,如果移动在中间改变方向,则非常大。那么,假设从起点到终点的路径长度是偶数,那么在这个双移动框架中从起点到终点的最短路径将是使之字形量最小的路径。如果从开始到结束的路径长度是奇数,则使用网格点水平和垂直距离一个空间开始,然后从开始到结束的路径长度将是偶数(尽管您必须为两者运行路径查找可能修改的起始位置)。

        【讨论】:

          猜你喜欢
          • 2012-02-29
          • 2019-11-27
          • 2015-04-21
          • 2015-07-21
          • 1970-01-01
          • 1970-01-01
          • 2020-02-15
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多