自定义epsilon规避浮点数误差的作用及CTCI共线点问题精度问询
关于浮点数运算误差与二维点共线问题的解答
1. 使用自定义epsilon避免浮点数运算错误的意义是什么?
咱得先搞懂根源:浮点数(比如double)的二进制存储天生就有精度局限——举个最经典的例子,0.1在二进制里是无限循环的,所以存进double后其实是个近似值,这就导致0.1+0.2的结果并不是精确的0.3,而是一个接近0.3的数。
自定义epsilon(就是一个极小的阈值,比如1e-9)的核心作用,就是帮我们绕过这种天生的精度缺陷:
- 替代直接的浮点数相等判断:不用写
a == b,而是判断abs(a - b) < epsilon,忽略那些因精度问题产生的微小差异 - 避免逻辑误判:比如本来应该共线的点,因为斜率计算的微小偏差,被错误判定为属于不同直线
- 让浮点数相关的比较逻辑更贴合实际需求:你可以根据场景调整epsilon的大小,平衡精度和鲁棒性
2. 如何避免二维点共线问题中斜率计算的浮点数误差?
你提到的哈希斜率截距的思路本身没问题,但用double存斜率确实容易踩精度坑。这里给你几个更靠谱的替代方案:
方案一:用最简分数表示斜率(强推!)
斜率本质是(y2-y1)/(x2-x1),我们可以把它转化为分子分母互质的整数对来存储,全程用整数运算,完全避开浮点数:
- 先计算分子
dy = y2 - y1,分母dx = x2 - x1 - 求出
dy和dx的最大公约数(GCD),然后把两者都除以这个GCD,得到最简形式 - 统一符号规则:比如让分母为正,这样(2,4)和(-1,-2)都会被转化为(1,2),避免同一斜率的不同表示被当成不同key
- 特殊情况处理:垂直直线(dx=0)可以用一个特殊标记(比如
(1, 0)),水平直线(dy=0)用(0, 1)
哈希的时候就用这个最简整数对作为key,完全没有精度问题,逻辑也清晰。
方案二:用叉积判断共线,跳过斜率计算
如果只是统计共线的点,其实根本不用算斜率:
选一个固定点p,遍历其他所有点q,用向量pq的方向来分组——两个点q1和q2与p共线的话,向量pq1和pq2的叉积为0,也就是满足:(q1.x - p.x) * (q2.y - p.y) == (q1.y - p.y) * (q2.x - p.x)
- 这种方法全程用整数运算(如果点坐标是整数的话),完全没浮点数的事
- 统计每个方向上的点数量,最多的那个就是经过
p的直线的最大点数,遍历所有点后取全局最大值就行
方案三:必须用浮点数?结合epsilon做模糊匹配
如果因为某些限制一定要用double存斜率,那就得靠epsilon来“抹平”误差:
- 不能直接用斜率作为哈希key,而是要把斜率按epsilon的倍数“归桶”,或者在哈希前检查当前斜率是否和已有的某个斜率差值小于epsilon,再合并计数
- 但这种方法不如前两种可靠,因为epsilon的选择很讲究:选大了会把不同斜率归为同一类,选小了还是会有漏判的情况,只能作为备选
内容的提问来源于stack exchange,提问作者Yos




