无论如何要避免“最大在线点数”问题的浮点运算

时间:2019-05-31 15:43:51

标签: c++ precision computational-geometry

我正在尝试解决Leet代码上的“在线最大点数”问题。我不可避免地需要进行浮点运算来计算每条线的Y截距和斜率。由于过去的糟糕经验,我正在尝试尽可能避免浮点运算。你有什么建议我可以在这里做到吗?

我正在使用LeetCode框架进行开发,并且几乎可以访问标准C ++库。 使用double或long double进行了尝试,但是其中一个测试用例已经将数字推到了这些数据类型的准确性极限。

//P1[0] is X coordinate for point P1 and P1[1] is Y coordinate

long double slopeCalc( vector<int> &p1, vector<int> &p2 )
{
    if( p1[0] == p2[0] && p1[1] == p2[1] )
    {
        return DBL_MIN;
    }

    if( p1[0] == p2[0] && p1[1] != p2[1] )
    {
        return DBL_MAX;
    }

    return ( (long double)p2[1] - (long double)p1[1] ) / ((long double)p2[0] - (long double)p1[0]);
}

long double yIntersectionCalc( vector<int> &p1, vector<int> &p2 )
{
    if( p1[0] == p2[0] && p1[1] == p2[1] )
    {
        return DBL_MIN;
    }

    if( p1[0] == p2[0] && p1[1] != p2[1] )
    {
        return DBL_MAX;
    }

    return ((long double)p1[1]*(long double)p2[0] - (long double)p2[1]*(long double)p1[0]) / (long double)(p2[0] - p1[0]);        
}

如果两个点分别是(0,0)和(94911150,94911151),则斜率计算为1,这是不准确的。我正在尝试避免浮点除法。

注意:在线问题上的最大点数应在2D空间中(在这种情况下为整数坐标)给出点数,并找到一条线上的最大点数。例如,如果点是(0,0),(2,2),(4,3),(1,1),则答案是3,分别是点(0,0),(1,1)和(2 ,2)

3 个答案:

答案 0 :(得分:2)

在整数坐标中,可以将三个点的对齐测试写为表达式

(Xb - Xa) (Yc  - Ya) - (Yb - Ya) (Xc - Xa) = 0

假设坐标范围需要N位,则增量的计算将花费N+1位,而对表达式的精确求值则需要2N+2位。您对此无能为力。

对于您来说,64位整数就足够了。


一条建议:避免使用坡度/截距表示形式。

答案 1 :(得分:0)

如果要避免使用浮点,可以确定点z是否与其他两个点x和y共线,是计算矩阵的行列式

{{1,z1,z2},{1,x1,x2},{1,y1,y2}}

如果行列式为0,则它​​们是共线的。由于使用置换定义计算行列式仅涉及乘法和加法/减法,因此所有计算将保持为整数。之所以为0,是因为行列式是以x,y,z为顶点的三角形面积的两倍,当且仅当三角形退化时,才为零。

另一种方法是使用小数对象,特别是将由两个整数定义的直线的斜率和截距标识为小数(“有理数”),并通过其分子和分母来标识减少的分数,因此您可以使用一对分数(斜率,截距)作为标识符,并且由于您从不使用浮点算术,因此不需要处理舍入误差。有关分数的示例实现,请参见https://martin-thoma.com/fractions-in-cpp/,重要的部分是可以使用算术运算符和规范化。

编辑:boost如果您要使用https://www.boost.org/doc/libs/1_68_0/libs/rational/

,则可以使用有理数库

答案 2 :(得分:0)

给出点a,b,c,查看b,c到公共点a的斜率:

ba.x = b.x - a.x
ba.y = b.y - a.y

ba.s = ba.y / ba.x

ca.x = c.x - a.x
ca.y = c.y - a.y

ca.s = ca.y / ca.x

如果线a,b,cAB具有相同的斜率,则点BC是共线的,即:

ba.s == ca.s

替换并重新排列以消除鸿沟:

ba.y / ba.x == ca.y / ca.x
ba.y * ca.x / ba.x == ca.y
ba.y * ca.x == ca.y * ba.x

用这些公式替换原始公式,那么a,b,c是共线性的iff:

(b.y - a.y) * (c.x - a.x) == (c.y - a.y) * (b.x - a.x)

请注意,行列式答案也可以重新排列为这种形式,这证明了这种方法。但是这种形式只有2个乘法,而对于幼稚的行列式实现只有12个。