我在“破解编码面试”一书中遇到了这个问题。
在笛卡尔平面上给出两条线,确定两条线是否相交。
以下是解决方案:
public class Line {
static double epsilon = 0.000001;
public double slope;
public double yintercept;
public Line(double s, double y) {
slope = s;
yintercept = y;
}
public boolean intersect(Line line2) {
return Math.abs(slope - line2.slope) > epsilon ||
Math.abs(yintercept - line2.yintercept) < epsilon;
}
}
为什么它没有简单的解决方案,如果斜率不相同,那么它们将相交。为什么epsilon和y拦截。
在建议中说明了
不要假设斜率和y截距是整数。理解浮点表示的局限性。切勿使用
==
检查是否相等。
答案 0 :(得分:9)
“解决方案”是错误的。
隐含在这个“解决方案”中的概念是,已经传递的参数是不准确的,在调用intersect
之前,值已经受到可能产生舍入错误结果的计算。因为值中存在错误,所以如果精确计算则相等的数字是不相等的。为了将它们视为相等,这个“解决方案”接受一些实际上不相等的值。
这种推理的一个缺陷是intersect
例程不知道错误有多大,因此无法知道它应该使用epsilon
的值。理想值可能为零,也可能是一百万。根据所提供的信息,使用的值1e-5在任何工程原理中都没有依据。更重要的是,没有使用绝对错误的基础,正如此代码所做的那样。根据具体情况,适当的使用容忍度可能是相对误差,ULP中指定的误差或其他一些技术。没有理由相信这个代码在传递的参数时会返回true
,这些参数理想地表示相交的行但是以某种未知的方式计算过。
另一个缺陷是,例程错误地接受不相等的相等值。例程将报告为不交叉许多相交的行。这段代码没有解决例程返回错误答案的问题;它只更改了返回错误答案的案例,并且可能大大增加了错误答案的数量。
答案 1 :(得分:5)
首先,因为简单的解决方案是,如果斜率不相同,它们将相交是不完整的。它们可以具有相同的斜率和截距,因此也是相同的。
建议说的epsilon是因为计算机中的数字表示不准确。根据{{3}},double有大约15个精确计算的数字,因此斜率和截距可能由于先前的计算而具有舍入误差,因此使用==进行简单检查可能会导致它们不相同而它们只是相差一个舍入误差。
答案 2 :(得分:4)
为什么它没有简单的解决方案,如果斜坡不是 同样,那么他们会相交。为什么epsilon和y拦截。
该解决方案考虑了floating-point算术引起的近似误差。 由于浮点数并不代表所有可能的实数,而是一个相对较小的子集(在[-1,+ 1]区间内更密集),所以当你必须处理浮点运算时使用阈值来执行相等是很常见的检查。
epsilon valure表示一个阈值,在该阈值下,2个不同的浮点值将被视为等于。
答案 3 :(得分:1)
在这一切之下,数字在处理时都会转换为二进制数。不可能将大多数浮点数表示为精确二进制数(因为它们将是1和0的无限系列),因此通过截断二进制序列来进行近似。例如,浮点数0.1(即:十分之一)不能表示为精确的二进制数,而是由近似值表示,看起来像0.000110011 ...此二进制数的截断会导致潜在的舍入误差,因此确切的等式“==”可能会导致错误的响应,而事实上正是这种舍入错误导致了假的否定。介绍一个epsilon试图通过说“低于这个数字我们认为是零的任何东西”来避免这些错误。请参阅wikipedia中的“二进制分数”部分以了解更多内容。