假设曲线y = x^2
上有100000个点。你想找到这些点的凸包。所有坐标都是浮点数。
在我的全景扫描实现中,我操作浮点数的唯一地方是我最初按坐标对所有点进行排序,然后我有一个函数来确定三个点是左转还是右转。
点数:
struct point {
double x;
double y;
};
排序比较器:
inline bool operator() (const point &p1, const point &p2) {
return (p1.x < p2.x) || (p1.x == p2.x && p1.y > p2.y);
}
左/右转:
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;
}
我的程序说,在10万个点中,只有68894是凸包的一部分。但由于它们在曲线上,所有它们都应该是凸包的一部分。
对于你的眼睛它没有任何区别。见下图。红点是凸包的一部分。
image http://oi57.tinypic.com/t8lsvs.jpg
但如果你看得足够近,并放大点,你会看到其中一些是蓝色的,所以它们不包括在凸包中。
image http://oi61.tinypic.com/2eol37a.jpg
现在我最初的假设是浮点错误导致了这个问题。
我想我可以使用一个对浮点数有任意精度的外部库,但我对我们在C ++中有的简单数据类型更感兴趣。
我怎样才能提高准确度?我读过关于epsilons的内容,但是如何在这里使用epsilon?我仍然会假设一些彼此接近的点是相同的,所以我不会得到接近100%的准确度。
解决此问题的最佳方法是什么?
答案 0 :(得分:0)
通常使用浮点数学,你需要引入&#34;容差的概念,&#34;有时表示为epsilon。在您的情况下,您可以将ccw()
函数设为三值:true / false / indeterminate。然后,当你试图发现一个新点是否可以成为凸包的一部分时,你会问&#34;它是ccw = true还是不确定&#34;,并且无论哪种方式你都接受这一点。当斜率太接近直线而无法确定时,会发生不确定的结果。
答案 1 :(得分:0)
如果您确实使用(x, x^2)
形式的点,那么所有点都应该在凸包上是正确的。但是,三个点可以是共线的。如果你转移它们或做任何其他奇怪的事情,这就会消失。
如果您选择100000点,我建议使用[-50000,49999]中的整数。您的ccw
函数会将left
和right
计算为绝对值小于2.5e14 <1的整数。 2 ^ 53,所以不会发生任何结果。
无论输入如何,基于坐标的排序都将正常工作。
对于常规输入,以下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;
}
在减法和乘法中都可以进行舍入。如果所有点都位于H * W边界框中,则将使用围绕H * eps / 2的绝对误差计算x坐标差异,并且将使用W * eps /周围的绝对误差计算y坐标差异。 2。因此,将以H * W * eps / 2附近的绝对误差计算乘积。如果fabs(left - right) < 3*H*W*eps/2
,您需要更准确地评估left
和right
。 eps
这里是2 -52 。
如果double
比较没有告诉你任何事情,我可能建议你只使用MPFR。但是,你可以不用它。来自Kahan求和的技巧将从差异中得到低位,而2 27 +1技巧可以帮助您准确计算产品。