简体   繁体   English

出现浮动精度问题时,如何实际解决凸包?

[英]how can you practically solve the convex hull when there are floating precision issues?

Suppose that you have 100000 points on the curve y = x^2 . 假设曲线y = x^2上有100000个点。 You want to find the convex hull of these points. 您想找到这些点的凸包。 All the coordinates are floating numbers. 所有坐标均为浮点数。

In my graham scan implementation the only place where I operate on floating numbers is when I initially sort all the points by their coordinates and then I have one function that determines whether three points make a left or a right turn. 在我的graham扫描实现中,我对浮点数进行操作的唯一地方是最初按它们的坐标对所有点进行排序,然后使用一个函数确定三个点是向左转还是向右转。

Points: 要点:

struct point {
   double x; 
   double y;
};

Sorting comparator: 排序比较器:

inline bool operator() (const point &p1, const point &p2) {
    return (p1.x < p2.x) || (p1.x == p2.x && p1.y > p2.y);
}

Left/Right turn: 左/右转:

inline int ccw(point *p1, point *p2, point *p3) {
  double left  = (p1->x - p3->x)*(p2->y - p3->y);
  double right = (p1->y - p3->y)*(p2->x - p3->x);
  double res = left - right;
  return res > 0;
}

My program says that out of 100 000 points only 68894 are part of the convex hull. 我的程序说,在10万个点中,只有68894个是凸包的一部分。 But since they are on the curve, all of them should be part of the convex hull. 但是由于它们在曲线上,因此它们都应该是凸包的一部分。

For your eye it doesn't make any difference. 对您而言,这没有任何区别。 See figure below. 参见下图。 The red points are part of the convex hull. 红点是凸包的一部分。

图片

But if you look close enough, and zoom into the points, you'll see that some of them are blue, so they are not included in the convex hull. 但是,如果您看起来足够近,并放大这些点,您会发现其中一些是蓝色的,因此它们不包含在凸包中。

图片

Now my initial assumption is that floating point errors are causing this problem. 现在,我最初的假设是浮点错误导致了此问题。

I guess I could use an external library that has an arbitrary precision for floating point numbers, but I'm more interested in the simple data types that we have for example in C++. 我想我可以使用对浮点数具有任意精度的外部库,但是我对例如C ++中的简单数据类型更感兴趣。

How could I increase the accuracy? 如何提高准确性? I've read about epsilons, but how would using an epsilon help here? 我已经读过关于epsilon的信息,但是在这里使用epsilon会有什么帮助? I would still assume some points that are close to each other to be the same, so I won't get an accuracy closer to 100%. 我仍然会假设彼此接近的一些点是相同的,因此我不会获得接近100%的精度。

What's the best way to approach this problem? 解决此问题的最佳方法是什么?

Very often with floating-point math you need to introduce the concept of "tolerance," sometimes denoted as epsilon. 在浮点数学中,通常需要引入“公差”的概念,有时也称为epsilon。 In your case, you could make your ccw() function three-valued: true/false/indeterminate. 就您而言,您可以将ccw()函数设置为三值:true / false / indeterminate。 Then when you're trying to discover if a new point can be part of the convex hull, you ask "is it ccw=true or indeterminate", and either way you accept the point. 然后,当您尝试发现新点是否可以成为凸包的一部分时,您会问“是ccw = true还是不确定”,以及接受该点的任何一种方式。 The indeterminate result would occur when the slope is too close to a straight line to be decided. 当斜率太接近无法确定的直线时,将出现不确定的结果。

You are correct that all of the points should be on the convex hull if you are indeed using points of the form (x, x^2) . 如果确实使用(x, x^2)形式的点,则所有点都应该在凸包上是正确的。 However, three points may be collinear. 但是,三个点可能是共线的。 If you're shifting them or doing anything else weird, this goes out the window. 如果您要转移它们或进行其他任何奇怪的操作,那么此操作将不起作用。

If you get to choose your 100000 points, I would suggest using the integers in [-50000,49999]. 如果您可以选择100000点,我建议使用[-50000,49999]中的整数。 Your ccw function will compute left and right to be integers smaller in absolute value than 2.5e14 < 2^53, so no roundoff will occur. 您的ccw函数将leftright计算绝对值小于2.5e14 <2 ^ 53的整数,因此不会发生舍入。

Your coordinate-based sort will work correctly regardless of the input. 无论输入内容如何,​​基于坐标的排序都将正常工作。

For general inputs, the following ccw predicate is buggy: 对于常规输入,以下ccw谓词有问题:

inline int ccw(point *p1, point *p2, point *p3) {
  double left  = (p1->x - p3->x)*(p2->y - p3->y);
  double right = (p1->y - p3->y)*(p2->x - p3->x);
  double res = left - right;
  return res > 0;
}

There can be roundoff both in the subtractions and in the multiplications. 在减法和乘法中都可以取整。 If all of your points lie in a H*W bounding box, the x-coordinate differences will be computed with an absolute error around H*eps/2 and the y-coordinate differences will be computed with an absolute error around W*eps/2. 如果您所有的点都在H * W边界框中,则x坐标差将以H * eps / 2的绝对误差计算,而y坐标差将以W * eps /的绝对误差计算。 2。 The products will therefore be computed with an absolute error around H*W*eps/2. 因此,将以绝对误差H * W * eps / 2来计算乘积。 If fabs(left - right) < 3*H*W*eps/2 , you need to evaluate left and right more precisely. 如果fabs(left - right) < 3*H*W*eps/2 ,您需要评估leftright更精确。 eps here is 2 -52 . eps这里是2 -52

I'd probably recommend just using MPFR if the double comparison doesn't tell you anything. 如果double比较没有告诉您任何信息,我可能会建议仅使用MPFR。 You can do it without, however. 但是,您可以不这样做。 The trick from Kahan summation will get you the low bits from the differences, and the 2 27 +1 trick can help you compute the products exactly. Kahan求和的技巧将为您提供差值的低位,而2 27 +1技巧将帮助您准确地计算乘积。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM