C++ 无论如何,避免浮点运算的最大点在线问题

C++ 无论如何,避免浮点运算的最大点在线问题,c++,precision,computational-geometry,C++,Precision,Computational Geometry,我试图解决Leet代码中的“在线最大点数”问题。我不可避免地需要做浮点运算来计算每条线的Y截距和斜率。由于我过去的不良经验,我正在尽可能避免浮点运算。你有什么建议我怎么做 我使用LeEcDebug框架进行开发,几乎可以访问标准的C++库。 尝试使用double或long double,但其中一个测试用例已经将数字推到了这些数据类型准确性的极限 //P1[0] is X coordinate for point P1 and P1[1] is Y coordinate long double s

我试图解决Leet代码中的“在线最大点数”问题。我不可避免地需要做浮点运算来计算每条线的Y截距和斜率。由于我过去的不良经验,我正在尽可能避免浮点运算。你有什么建议我怎么做

我使用LeEcDebug框架进行开发,几乎可以访问标准的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]);        
}
//P1[0]是点P1的X坐标,P1[1]是Y坐标
长双斜率LC(向量和p1、向量和p2)
{
if(p1[0]==p2[0]&&p1[1]==p2[1])
{
返回DBL_MIN;
}
如果(p1[0]==p2[0]&&p1[1]!=p2[1])
{
返回DBL_MAX;
}
返回((长双精度)p2[1]-(长双精度)p1[1])/((长双精度)p2[0]-(长双精度)p1[0]);
}
长双阴截面计算(向量和p1、向量和p2)
{
if(p1[0]==p2[0]&&p1[1]==p2[1])
{
返回DBL_MIN;
}
如果(p1[0]==p2[0]&&p1[1]!=p2[1])
{
返回DBL_MAX;
}
返回((长双精度)p1[1]*(长双精度)p2[0]-(长双精度)p2[1]*(长双精度)p1[0])/(长双精度)(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)

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

{{1,z1,z2},{1,x1,x2},{1,y1,y2}}
如果行列式为0,则它们是共线的。由于使用置换定义计算行列式只涉及乘法和加法/减法,因此所有计算都将保留为整数。它为0的原因是行列式是以x,y,z为顶点的三角形面积的两倍,当且仅当三角形退化时,行列式为0

另一种方法是使用分数对象,特别是由两个整数定义的直线的斜率和截距被标识为分数(“有理数”),而缩减分数由其分子和分母标识,因此可以使用分数对(斜率,截距)作为标识符,由于您从不使用浮点运算,所以不需要处理舍入错误。有关分数的示例实现,请参见,重要的部分是您可以使用算术运算符和规范化


编辑:boost有一个有理数库,如果你想使用它,在给定的点
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
如果线
AB
BC
具有共同斜率,则点
a、b、c
为共线,即:

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次乘法。

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

(Xb - Xa) (Yc  - Ya) - (Yb - Ya) (Xc - Xa) = 0
假设坐标范围需要
N
位,则增量的计算采用
N+1
位,表达式的精确计算采用
2N+2
位。你对此无能为力

在您的情况下,64位整数就足够了



一条建议:避免使用斜率/截距表示法。

请描述您问题中的“线上最大点”问题。我在文章末尾添加了对该问题的简要描述。但问题是我必须为我试图解决的原始问题找到另一个解决方案。我需要一些东西来定义这条线的特征。斜率和Y截距形式的东西,可以作为在字典中查找线的键。一条线上的2或3个点不能用作查找线上点数信息的键。如果要根据斜率和截距来定义它,请注意两者都必须是分数。因此,您可以定义一个分数类,重载该类的算术运算符,并保持分数的简化形式。有很多分数类的实现,我建议您搜索一个您喜欢的实现,或者如果您想练习的话,您可以自己实现一个。以下是一个让您开始学习的方法: