我有一些我想要比较的数字。它们表示通过不同空间的路径长度。
对我来说不幸的是,一些不精确导致了错误的比较。例如,在注意到错误的效果之后,我发现我正在进行这样的比较:
a = 384.527100541296
b = 384.52710054129614 // Note the trailing 14
就我的目的而言,a和b应该是平等的。
我注意到guava有一个fuzzyCompare()
方法用于双打,似乎做了我想要的忽略一些精度:
private static final double COMPARISON_PRECISION=1e-10;
private static final Comparator<Double> fuzzyCompare= new Comparator<Double>(){
public int compare(Double o1, Double o2) {
return DoubleMath.fuzzyCompare(o1, o2, COMPARISON_PRECISION);
}
};
public int compareTo(Object o) {
if (o instanceof Spam) {
Spam other = (Spam) (o);
return ComparisonChain.start()
.compare(this.getLength(),other.getLength(),fuzzyCompare)
//...
.result();
} else {
throw new ClassCastException();
}
}
关于模糊比较的警告并未引起我的注意:
这不是总排序,不适合用于 Comparable.compareTo(T)实现。特别是,事实并非如此 传递
我的问题是,这种缺乏传递性是一个真正的问题吗?如果是的话,它会如何呈现?我认为,如果比较真的被真正违反了,它会抛出类似于this question: Java error: Comparison method violates its general contract的错误,并且它甚至不会对我测试过的各种价值做出反应。
或者由于IllegalArgumentException
是运行时错误,我还没有遇到问题,因为只有一些不正确的值会引发问题?
或许它现在做错了,它只是微妙到我没有注意到它?
答案 0 :(得分:6)
您的运营商不具有传递性。考虑a = 0
,b = 0.6
,c = 1.2
,容差为1
。 a==b
,b==c
,但a!=c
。解决方案是将值分区为类(例如通过舍入或截断),并使用Double.compare()
来保持传递性。
首先让我们讨论一下使用fuzzyCompare(double, double, double)
时您的数据是否具有传递性:
在大多数情况下,您的数据将是可传递的,但是可以生成不是的数据。让我们采取以下值:
a = 384.52710054120
b = 384.52710054126
c = 384.52710054132
正如您所看到的,使用我们的新指标时,以下情况属实:a==b
,b==c
,但a!=c
。如您所见,您违反了transitivity。
如果您的Comparator
不具有传递性,是否会出现问题?
方法通过使用文档和/或注释来断言某些条件。 compare
方法承诺该方法是可传递的。对许多案例来说,打破这种承诺可能是好的,但传递性并不重要,但依赖于这种承诺的代码可能会被打破。
如果传递的承诺被破坏,那么代码的示例可能无效?
让我们创建一个场景,其中我们有3个Foo
类型的元素,这些元素根据一些名为Comparator
的{{1}}不可传递。我们称他们为fooComparator
,f1
和f2
。
f3
由于它们不是传递性的,我们假设Comparator<Foo> fooComparator = new Comparator<Foo>(){
public int compare(Foo o1, Foo o2) {
// some non-transitive return value
}
};
&lt; f0
,f1
&lt; f1
,f2
&lt; f2
成立。
如果你把它们放在一个列表中并尝试sort()
它们会发生什么?
f0
如何解决问题
您可以通过将数据映射到另一个数据集来创建传递运算符,并使用在该集合上定义的传递运算符。让我们以较低的精度将实数映射到实数。
考虑以下值:
List<Foo> foos = new LinkedList<>();
Collections.addAll(f1, f2, f3)
Collections.sort(foos, fooComparator);
如果将它们截断为第二个数字(a = 0.01; b = 0.05; c = 0.13; d = 0.19; e = 0.21
)并使用Double.compare()
,则会保留传递性。
您可以看到我们已将我们的值放入三个类Math.truncate(x * 10)/10
。肯定有一些重要的定理证明了这种情况,但我无法记住它的名字......
答案 1 :(得分:1)
缺乏传递性是一个真正的问题
也许,取决于您尝试解决的问题。但是,如果代码期望Comparator
的实现以可传递的方式工作,那么您可能遇到微妙的问题。除了&#34; undefined&#34;。
如果我在评论中看到这段代码,我会感到非常高兴:您正在超载Java与您自己的比较明确的比较概念 - 有效但不同 - 比较的概念。
如果你称之为不同的东西 - fuzzyCompare
,FuzzyComparator
等 - 这两种观念并不存在混淆。
答案 2 :(得分:1)
使用非传递性compareTo
是一个可怕的想法:
TimSort
无济于事,因为算法可能会在几年内被更好的算法取代。SortedMap
可能会非常突破。可能会发生您放置它的内容,将无法找到(对于HashMap
或equals
已损坏的hashCode
,这种情况确实会发生。同样,实施可能会在几年内发生变化,然后一切皆有可能。我强烈建议以不同方式命名您的方法。或者,创建一个记录有相应警告的Comparator
(这可能导致同样的问题,但它更加明确)。
请注意,如果遇到问题Comparable
,即使HashMap
可能会因许多冲突而中断,也会尽可能使用compareTo
。