我有一个比较器
float d = o1.bar - o2.bar;
if (Math.abs(d) <= 0.001) {
return 0;
} else {
return d < 0 ? -1 : 1; // inline Math.copySign
}
本质上,这应该是根据bar
属性比较两个Foo
s,除非值足够接近,在这种情况下,它们应该被声明为相等。(这很重要,因为我在另一处房产上做了另一种分类。)
不过,很明显,这不是一个传递比较器。如果有Foo
sf1
、f2
和f3
,bar
的值分别为1.999
、2.000
和2.001
,那么根据我的比较器,f1==f2
和f2==f3
但f1!=f3
。
调用的排序(myListofFoo, myFo的比较)
给出了一个比较方法违反了它的一般合同!错误很少,但确定。
如何在不生成此错误的情况下将这样的比较器与 Collections.sort(List, Comparator)
一起使用?
或者,有没有什么方法可以存储我的数据,让比较器正常工作?在构造时将每个浮点路由到最近的0.001
将是最简单的解决方案,除了Foo.bar
字段实际上是基于任意距离度量计算的,所以没那么简单。
实际代码是:
float d = metric.distance(vertex, o1)
- metric.distance(vertex, o2);
if (Math.abs(d) < threshold) {
return 0;
} else {
return d < 0 ? -1 : 1; // inline Math.copySign
}
其中o1
、o2
和顶点
是类Point{浮点x;浮点y;}
和度量
是接口距离度量{浮点距离(Point p1, Point p2);}
的实例。值得注意的是,即使在标准欧几里得度量上,这也失败了。
恐怕Java7排序实现不会容忍表现出不敏感的Comparator。如果您想使用标准的JavaSE排序API,您对此无能为力。
但是,事实上,在排序中使用阈值比较实际上在数学上是不正确的。
比较浮点值时的问题是,它们通常一开始就不精确,然后计算通常会在结果中引入更小的误差。当两个结果足够接近时,累积误差可能大于两个值之间的差值...这意味着我们无法判断理想数字(没有误差)是小于、等于还是大于每个误差。我们通过使用阈值进行比较,将“接近相等”视为“相等”来处理这个问题。
当我们对值进行排序(即按顺序排列)时,需要以不同的方式处理值中的错误问题。假设
> < li>
我们有两个号码< code>v1 e1和< code>v2 e2,以及
当我们使用阈值比较比较数字时,阈值大于mod(e1)mod(e2)
如果事实证明 v1 和 v2
足够接近
mod(v1 - v2)
因此,如果我们忽略错误并简单地使用精确比较对数字进行排序,那么当我们使用基于阈值的比较时,我们不会以不正确的顺序排列任何一对数字。
现在假设我们有v1±e1
,v2±e2
和v3±e3
…并且mod(e1)mod(e3)
是我们的阈值大:
>
如果我们按照上面的顺序排序(使用精确的比较),我们仍然会以正确的顺序得到数字。
如果我们使用“与阈值比较”来对值进行排序(排序实现允许这样做!),我们最终可能会得到 v3 ± e3
、v2 ± e2 和
v1 ± e1
的数字。我们有 {v1 ± e1、v2 ± e2}
和 {v2 ± e2、v3 ± e3}
成对相等,但我们也可能有 {v1 ± e3、v3 ± e3}
错误排序,即使我们使用基于阈值的比较进行比较!
底线是您应该简单地实现您的< code >比较器(用于排序目的!)来使用精确比较。阈值比较不适用于此上下文。这与< code>sort算法的编码方式无关...
我猜你实际上想做的是删除重复值(根据你的阈值),然后对其余的进行排序。你为什么不首先根据非四舍五入的值进行自然排序,然后根据你的阈值使用过滤。