【问题标题】:Algorithm for finding the shortest straight line path between two points求两点间最短直线路径的算法
【发布时间】:2021-11-17 02:34:46
【问题描述】:

我有一个问题,我有一个点网格、顶点障碍物和一个起点

我需要确定起点和网格中每个点之间的最短直线路径。值得注意的是,这些点不是图,所以遍历不需要像 A* 或 Dijkstra 那样的图。也就是说,给定如下网格:

S  1  2  3
4  5  6  7
8  x  9  10
11 x  13 14

S 是起点,E 是终点,x 是障碍物,任何数字都代表一个点(将其视为航路点),我需要确定到达每个编号的最短路径来自S。对于直线,这很容易,但是要找到像13 这样的点,路径可以是S - 9 - 13 而不是S - 5 - 9 - 13

原因是这个问题将模拟飞行,它不一定必须遵守在 8 个可能方向上行驶的网格,并且可以飞越部分单元格;这里的点代表每个单元格的中心。

我不是要求实现,只要存在针对此类问题的众所周知的算法即可。

我当前的设计基于从S 中找到初始可见点集,然后构建不可见点的子集。从那里,找到距离S 最远且最接近无法看到的点集的点,然后重复。

【问题讨论】:

  • 您可以将您的电路板转换为图表并使用您提到的寻路算法。
  • @pstatix,如果我理解得很好,你想找到“改变方向”点吗?另外,网格中的所有值都是正数吗?
  • 两个给定点之间只有一条直线,所以问最短的没有意义。如果您将路径限制为由网格节点组成,那么它们通常不会对齐。
  • @GáborPálovics 使用所描述的寻路算法不起作用,因为以 45' 角增量检查下一个节点(移动 NSEW + 对角线)。先前评论建议的任何角度算法是我正在寻找的,将发布带有实现的答案

标签: algorithm geometry


【解决方案1】:

这是个挺有意思的问题,没想到能有这么实际的应用。这个答案是用 Java 写的,因为我最熟悉它,但是算法很简单,代码中的 cmets 应该可以帮助你用任何语言重新实现。

我已将您的数据网格扩展为多行,并添加了E(结束位置)。所以完整的网格看起来像:

S  1  2  3
4  5  6  7
8  x  9  10
11 x  13 14
12 x  E  15  

只说几句:

  • 每个单元格的维度是 1 乘 1。这意味着每个单元格的对角线等于 sqrt(2),因为 d^2 = 1^2 + 1^2
  • 每个单元格称为Position(value, row, column)RowColumn 值就像坐标,Value 只是单元格中的一个值。例如,我们的开始是:new Position("S", 0, 0),结束是new Position("E", 4, 2)
  • 提出的算法是在一些“最短路径算法”之后提出的,例如提到的 A* 或 Dijkstra
  • 所提出的算法只看最短路径的角度,如果在计算中应该考虑最短路径上的值,那么我可以用回溯机制替换它。

算法设置:

// shortest path is stored in "positions" list
Position _S = new Position("S", 0, 0);
Position _5 = new Position("5", 1, 1);
...
Position _E = new Position("E", 4, 2);

List<Position> positions = new ArrayList<>();
Collections.addAll(positions, _S, _5, _9, _13, _E);
// obstruction points are stored in "xxx" list
Position x1 = new Position("x", 2, 1);
Position x2 = new Position("x", 3, 1);
Position x3 = new Position("x", 4, 1);

List<Position> xxx = new ArrayList<>();
Collections.addAll(xxx, x1, x2, x3);

现在算法:

        // iterate from the end
        int index = positions.size()-1;
        while (index > 1) {

            // get three current positions
            Position c = positions.get(index);
            Position b = positions.get(index-1);
            Position a = positions.get(index-2);

            // calculate angle on point "b". Angle is constructed with a-b-c
            int angle = angle(a,b,c);
            if (angle == 0) {                     // angle = 0 means that line is straight
                positions.remove(index-1);        // then just remove the middle one (b)
            } else {                              // in ELSE part check if line goes through any "x" cell
                Line line = new Line(a,c);        
                boolean middleCanBeRejected = true;
                for (Position x : xxx) {
                    if (line.distanceTo(x) < HALF_DIAGONAL) {   // if distance from "x" to "line" is less than sqrt(2), 
                                                                // it means that straight line will pass over "x", 
                                                                // so we are not able to discard the middle point
                        middleCanBeRejected = false;
                        break;
                    }
                }
                if (middleCanBeRejected) {          // still in ELSE
                    positions.remove(index-1);      // check result of FOR above
                }
            }
            index--;
        }

最后是测试用例:

S  1  2  3
4  5  6  7
8  x  9  10
11 x  13 14
12 x  E  15  

Path:
S    0.0, 0.0
9    2.0, 2.0
E    4.0, 2.0
S  1  2  3
4  5  6  7
8  22 9  10
11 23 13 14
12 x  E  15 

Path:
S    0.0, 0.0
E    4.0, 2.0

附录,实用方法和类:

    // -1 -> turn right
    // 0  -> straight
    // +1 -> turn left
    // Point2D.ccw() algorithm from Sedgewick's book
    public static int angle(Position a, Position b, Position c) {
        double area = (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);
        if (area < 0.0D) { return -1; }
        else { return area > 0.0D ? 1 : 0; }
    }
public class Line {

    // A*x + B*y + C = 0
    double A;
    double B;
    double C;

    public Line(Position p1, Position p2) {
        // https://math.stackexchange.com/questions/422602/convert-two-points-to-line-eq-ax-by-c-0
        this.A = p1.y - p2.y;
        this.B = p2.x - p1.x;
        this.C = p1.x*p2.y - p2.x*p1.y;
    }

    public double distanceTo(Position p) {
        double nom = Math.abs(A*p.x + B*p.y + C);
        double den = Math.sqrt(A*A + B*B);
        return nom/den;
    }
}
public class Position {

    String value;
    double x; // row
    double y; // column

    public Position(String value, double x, double y) {
        this.value = value;
        this.x = x;
        this.y = y;
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-03
    • 2017-02-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多