【问题标题】:Sorting points by their polar angle in Java在Java中按极角对点进行排序
【发布时间】:2013-05-12 15:46:24
【问题描述】:

我正在使用格雷厄姆扫描算法来找到一组点的凸包 我正在尝试按极角对点进行排序,但我不知道该怎么做(我已经按 Y 坐标对点集进行了排序)。

我已经写的是这样的:

public double angle(Coord o, Coord a)
{
    return Math.atan((double)(a.y - o.y) / (double)(a.x - o.x));
}

其中Coord 是我的X 和Y 坐标为double 的类。

我还查看了 Stack Overflow 中的一篇类似帖子,其中有人试图用 C++ 实现这个角度,但我不明白 qsqrt。我们在 Java 中有这样的东西吗?

qreal Interpolation::dp(QPointF pt1, QPointF pt2)
{
    return (pt2.x()-pt1.x())/qSqrt((pt2.x()-pt1.x())*(pt2.x()-pt1.x()) + (pt2.y()-pt1.y())*(pt2.y()-pt1.y()));
}

如果有人可以帮助我,我会很高兴。

【问题讨论】:

    标签: java grahams-scan


    【解决方案1】:

    您无需计算极角即可对其进行排序。由于三角函数在一个象限内是单调的(总是增加或总是减少),只需按函数本身排序,例如你的棕褐色。如果您从最底点开始实施格雷厄姆扫描,则只需查看前两个象限,因此按 cotan 排序最容易,因为它在两个象限上都是单调的。

    换句话说,您可以只按- (x - x1) / (y - y1) 排序(其中 (x1, y1) 是您的起点坐标),这样计算起来会更快。首先,您当然需要将y == y1 的点分开,然后根据 (x - x1)` 的符号将它们添加到列表的顶部或底部,但它们很容易识别,因为你'已经按 y 排序以找到您的起点。

    【讨论】:

    • 我应该在 java 中使用什么来找到 cotan 的公式?只需将我的代码替换为: public double angle(Coord o, Coord a) { return 1.0/Math.tan((double)(a.y - o.y) / (double)(a.x - o.x)); }
    • 为起点,每个地方写不同的东西。从哪里开始重要吗?
    • (x - x1) / (y - y1) 是 cotan (1/tan) 的公式——相邻而不是相反。我只做了负数,这样它就会随着角度的增加而增加。我没有听说过 Graham 扫描,所以我的回复基于 Wikipedia 文章,该文章建议从最底层开始。如果你从最左边的点开始,这个想法不会改变。在这种情况下,最容易使用切线:(y - y1) / (x - x1)
    • 必须有负号吗?因为我使用了你的公式,但没有负号
    • 它只是切换排序顺序。常规角度沿逆时针方向增加,但余切沿顺时针方向增加。
    【解决方案2】:

    如上所述,计算极角本身是一种非常草率的处理方式。您可以定义一个简单的比较器并使用叉积按极角排序。这是 C++ 中的代码(我用于自己的 Graham 扫描):

    struct Point {
        int x, y;
    }
    
    int operator^(Point p1, Point p2) {
        return p1.x * p2.y - p1.y * p2.x;
    }
    
    bool operator<(Point p1, Point p2) 
    {
        if(p1.y == 0 && p1.x > 0) 
            return true; //angle of p1 is 0, thus p2 > p1
    
        if(p2.y == 0 && p2.x > 0) 
            return false; //angle of p2 is 0 , thus p1 > p2
    
        if(p1.y > 0 && p2.y < 0) 
            return true; //p1 is between 0 and 180, p2 between 180 and 360
    
        if(p1.y <0 && p2.y > 0) 
             return false;
    
        return (p1 ^ p2) > 0; //return true if p1 is clockwise from p2
    }
    

    您可以通过定义Point 类在Java 中实现相同的功能。基本上我已经重载了^ 运算符来返回叉积。其余的很明显,希望这会有所帮助!

    【讨论】:

    • 你不需要对叉积进行标准化,因为如果你要排序,叉积有一个量级吗?即假设您有原点 po(-1,-4),那么您在 p1(-1,4) 有一个点,在 p2(-2,-3) 有另一个点。从图形上看,p2 具有较大的极角。原点和第一个点的“叉积”(引号中,因为叉积只对向量而不是点进行)是 (-1*4 - -1*-4) = -8 但原点和第二点的叉积(因为距离更小)是 (-1*3 - -2*-4) = -5 这不是更负数并且排序不正确。按距离划分。
    【解决方案3】:

    Math.atan() 返回 -pi/2 到 pi/2 之间的角度。您必须调整其他两个坐标的结果。

    如果您想要与凸包中心的角度,您必须首先转换坐标,以使centroid 成为原点。

    【讨论】:

      猜你喜欢
      • 2021-06-19
      • 2019-06-07
      • 1970-01-01
      • 1970-01-01
      • 2014-04-03
      • 1970-01-01
      • 2017-09-22
      • 2015-07-03
      • 1970-01-01
      相关资源
      最近更新 更多