凸壳算法 - 格雷厄姆扫描最快的比较功能?

时间:2017-01-12 22:05:50

标签: c++ algorithm sorting math grahams-scan

我已经实施了Graham扫描,但我发现我的程序的瓶颈是排序(80%的时间)。我想改进它,现在我正在做以下事情:

 std::sort(intersections.begin() + 1, intersections.end(), [&minElement](Point const& a, Point const& b)
 {return angle (minElement - a, XAxis) < angle (minElement - b,XAxis);});

这给了我确切的角度,但它并不便宜,因为我的角度函数如下所示:

float angle (const Point& v1, const Point& v2) {
    return dot (v1, v2) / (v1.Length () * v2.Length ());
}

在Length函数中,我必须做一个平方根,这是最昂贵的操作之一。但是这样我得到了很好的订购。

我尝试按Slope,dot,ccw排序数组,甚至只从我的比较中删除sqrt,但没有一个能为我提供相同的排序。你能给我一些建议吗?

2 个答案:

答案 0 :(得分:3)

当您按相对角度对点进行排序时,您无需知道两点的确切角度。相反,你只需要知道一个点是在另一个点的左侧还是右侧。

图像,例如,你想要比较两个点(x 1 ,y 1 )和(x 2 ,y 2 ),假设最底点位于(x p ,y p )。看两个向量v 1 =(x 1 - x p ,y 1 - y p )和v 2 =(x 2 - x p ,y 2 - y <子> p )。要确定v 1 是否位于v 2 的左侧或右侧,这意味着您要查看从v 开始的角度符号1 到v 2 。如果它是正数,则v 2 位于v 1 的左侧,如果它是负数,则v 1 位于v的左侧<子> 2

2D十字产品具有很好的属性

  

v 1 ×v 2 = | v 1 | | V <子> 2 | SIN(θ)

其中θ是从v 1 到v 2 的角度。这意味着θ> 0如果v 1 在v 2 的右边,反之亦然,这很好,因为这可以让你比较哪一个纯粹是通过采用十字产品! / p>

换句话说:

  • 如果v 1 ×v 2 &gt; 0,然后v 2 位于v 1 的左侧。
  • 如果v 1 ×v 2 = 0,则这些点是共线的。
  • 如果v 1 ×v 2 &lt; 0,然后v 2 位于v 1 的右侧。

二维交叉乘积公式由

给出
  

(Δx 1 ,Δy 1 )×(Δx 2 ,Δy 2 )=(Δx< sub> 1 Δy 2 - Δx 2 Δy 1

其中,此处,Δx 1 表示x 1 - x p 等。

因此,您可以计算上述数量,然后查看其符号以确定这两个点如何相互关联。不需要平方根!

答案 1 :(得分:0)

您可以通过缓存进行临时优化。

首先,Length()可以将其结果缓存在成员变量中。您只需要在更改的情况下使该值无效(可能情况并非如此)。你可以进行延迟计算,因此长度将检查它是否有值,如果没有则计算/存储,然后返回存储的值。

其次,对于给定的minElement和xAxis, angle(minElement - a,XAxis)成为1参数的函数,因此您可以在排序之前为每个点保存(缓存)它的值并使用比较器准备好的价值观。

对这些事情采取天真的方法:在主要算法中使用Point的子类,因此每个点都已经有了缓存值的必要方法和位置。