【问题标题】:Find shortest path using A Star with difficult restriction使用难以限制的 A Star 找到最短路径
【发布时间】:2015-12-21 20:09:53
【问题描述】:

我需要解决以下问题: 我有一个网格,你可以在其中向 8 个方向移动,N、S、E、W、NE、NW、SE、SW。

正交移动的成本始终为 1。如果先前的移动是正交的,或者如果先前的移动是对角的并且成本为 2,则对角移动的成本为 1,否则成本为 2。

所以举几个例子来更好地解释:

  1. 移动 NE,NE 将花费 1+2 = 3

  2. 移动 NE,E,NE 将花费 1+1+1 = 3

  3. 移动 NE,NE,NE 将花费 1+2+1 = 4

我认为这足以了解它的要点。

我不知道如何实现可以实现此目的的 A* 算法。我的启发式函数:

private double heuresticDistance(Node p1, Node p2){
        double dx = Math.abs(p1.x - p2.x);
        double dy = Math.abs(p1.y - p2.y);
        // D = 1d, D2 = 1.5d;
        return D * (dx + dy) + (D2 - 2 * D) * Math.min(dx, dy);
    }

显然,在这种情况下它并不好,并且在某些情况下它不会走最短路径(成本方面),这意味着它可能会采用不同的方式来降低成本。

如果有多个,它总是会找到最便宜的或最便宜的一个,这一点非常重要。

你能给我一些提示吗?我的 A* 实现非常简单,我想我是根据 wikipedia 伪代码编写的。

编辑:

public class Node{
    public int x,y;
}

【问题讨论】:

  • Node的定义是什么?
  • 这不是家庭作业。我正在编写一个游戏,这是我需要实现的,以便它正常工作。 @David 节点只包含 x 和 y 坐标。
  • 网格有障碍物吗?
  • 确实如此。但是我想要找到的路径将始终存在,因为它之前已检查过(使用递归函数,该函数知道要花费的剩余移动点以及它是否来自对角线移动以及先前移动的成本是多少)。每个节点有 8 个邻居,除非有障碍物。我有关于采用哪个网格单元的信息以及使用该信息生成节点邻居的函数(它创建 8 个邻居并删除被阻止的那些)

标签: algorithm path-finding a-star


【解决方案1】:

您的启发式函数不是admissible

查看从(0, 0)(1, 1) 的路径。您的启发式方法告诉您它等于1 * (1 + 1) + (1.5 - 2) * 1 = 1.5。但路径是1。所以你高估了你的目标,从而使你的启发式算法不可接受,这导致 A* 找到错误的路径。

仔细查看 A*,您会发现它需要可受理性(我也不记得一致性对您的情况是否重要)。

【讨论】:

  • 我写了一个函数,它总是在没有障碍的网格中找到两个节点之间的最低成本,以便它找到最短路径就足够了吗?还是必须始终在当前网格中的两个节点之间找到最低成本?如果它返回的成本低于现实呢?
  • @Greyshack 你必须编写一个总是低估真实距离的启发式算法。最简单的方法是通过消除约束(在您的情况下是障碍)来做到这一点。所以是的,这对于启发式方法来说就足够了。
  • 它总能找到最短路径?
  • @Greyshack 如果您的启发式方法是可接受且一致的,那么可以。请阅读关于 A* 的 wiki 页面
  • 但是在这种情况下我不能总是返回 1 甚至 0 吗?或者最简单的就是返回 dx 和 dy 值的最大值。我试过了,它并不总是产生最短路径。根据我认为的维基百科定义,后者是一致的。
【解决方案2】:

参考维基百科文章A* search algorithm。您的启发式方法不是“单调的”,因为给定路径的成本取决于过去的选择。这是我的尝试。

任何路径都可以拆分为一系列正交运动和一系列对角运动。在启发式中,让我们移动对角线直到我们与目标正交。这确保了最大数量的额外费用。 NE-NE-E-E。

另一方面,当我们均匀地混合正交和对角线运动时,会出现最好的情况:E-NE-E-NE。所以对于每个正交运动,我们可以将一个对角运动视为正交运动。

private double heuresticDistance(Node p1, Node p2, bool lastWasDiagonal) {
    int dx = Math.abs(p1.x - p2.x);
    int dy = Math.abs(p1.x - p2.x);
    int diagonals = Math.min(dx, dy);
    int orthagonals = Math.max(dx, dy) - diagonals;
    // example: dx=3, dy=5. Move diagonally 3 times and orthagonally 2 times.

    int temp = diagonals + orthagonals;
    diagonals = Math.max(diagonals - orthagonals, 0);
    orthagonals = temp - diagonals;

    int lastDiagonalBonus = 0;
    if( lastWasDiagonal && orthagonals < 1 )
        lastDiagonalBonus = 1;  // the special case that last move was diagonal and we have no orthagonal moves available to compensate

    if( diagonals % 2 == 1 ) {
        diagonals--; orthagonals++;
    }
    return diagonals * 3 / 2 + orthagonals * 1 + lastDiagonalBonus;
}

【讨论】:

  • 您是否考虑到第一个对角线可能会花费 2,因为我们是通过对角线移动到达节点的?而且通过首先正交以降低第一个对角线的成本可能是最优的?
  • 尽管我包含了布尔值来处理这种情况,但我完全忘记了。问题已更新。您可能想检查我的算法逻辑是否合理,使用笔和纸进行一些测试用例。
  • 如果最后一个对角线成本为 2,那么这个成本为 1。我认为没有这些信息你就做不到
  • 是的,您编写的任何启发式方法都需要知道上一步是否对角线以及是否受到惩罚。也许您可以修改您的程序以使用分数运动? IE。每个对角线移动 1.5。
  • 我无法修改它,因为它基于具有此类规则的游戏并且我正在编写它的数字版本:D 它必须完全相同。我最终会弄清楚的
猜你喜欢
  • 2012-02-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多