5

我希望使用定义的公差( )创建一个HashSet实数(目前s) ,(cf 因为 using仅适用于完全相等并且是最终类,我不能使用它。我最初的想法是扩展(例如), 使用方法并创建一个新类,在其中使用此值。但是我想检查是否已经存在现有的解决方案和现有的 F/OSS 库。DoubleepsilonAssert.assertEquals(double, double, double)
Double.equals()DoubleHashSetDoubleHashSetsetEpsilon(double)ComparableDoubleequals()DoubleHashSet

(将来我希望将其扩展到实数元组 - 例如矩形和立方体 - 所以通用方法更可取

注意:@NPE 暗示这是不可能的。不幸的是,我怀疑这在形式上是正确的:-) 所以我想知道是否有近似的方法......其他人一定有这个问题并大致解决了它。(我已经经常使用一个工具Real.isEqual(a, b, epsilon),它非常有用。)我准备接受一些不常见的及物性错误。

注意:我将使用 TreeSet 来解决“几乎等于()”的问题。稍后我将比较 complexNumbers、矩形(和更复杂的对象),能够设置两个事物相等的限制非常有用。complexNumbers 没有简单的自然排序(也许 Cantor 方法会起作用),但我们可以判断它们是否几乎相等。

4

3 回答 3

5

这种方法存在一些基本缺陷。

HashSet用于equals()检查两个元素是否相等。合同equals()的要求如下:

它是可传递的:对于任何非空引用值xyz,如果x.equals(y)返回truey.equals(z)返回true,则x.equals(z)应该返回true

现在考虑以下示例:

x = 0.0
y = 0.9 * epsilon
z = 1.8 * epsilon

很明显,您提出的比较方案将打破传递性要求(x等于yy等于z,但x不等于z)。在这些情况下,HashSet无法正常运行。

此外hashCode(),由于以下要求,将产生额外的挑战:

如果根据方法两个对象相等equals(Object),则对两个对象中的每一个调用该hashCode方法必须产生相同的整数结果。

hashCode()可以通过使用 aTreeSet而不是来回避该要求HashSet

于 2013-04-14T09:40:58.527 回答
2

我要做的是在使用双打之前将它们四舍五入(假设这是合适的)

例如

public static double roundByFactor(double d, long factor) {
    return (double) Math.round(d * factor) / factor;
}

TDoubleHashSet set = new TDoubleHashSet(); // more efficient than HashSet<Double>
set.add(roundByFactor(1.001, 100));
set.add(roundByFactor(1.005, 100));
set.add(roundByFactor(1.01, 100));
// set has two elements.

您可以将此行为包装在您自己的 DoubleHashSet 中。如果要保留原始值,可以使用 HashMap 或 TDoubleDoubleHashMap ,其中键是舍入值,值是原始值。

于 2013-04-14T10:31:31.820 回答
0

我已经实施了@NPE 的方法(我已经接受了他/她的回答,所以他/她得到了分数 :-) 并在此处提供代码

//Create a comparator:
public class RealComparator implements Comparator<Double> {

    private double epsilon = 0.0d;

    public RealComparator(double eps) {
        this.setEpsilon(eps);
    }

    /**
     * if Math.abs(d0-d1) <= epsilon  
     * return -1 if either arg is null
     */
    public int compare(Double d0, Double d1) {
        if (d0 == null || d1 == null) {
            return -1;
        }
        double delta = Math.abs(d0 - d1);
        if (delta <= epsilon) {
            return 0;
        }
        return (d0 < d1) ? -1 : 1;
    }

    /** set the tolerance
     * negative values are converted to positive
     * @param epsilon
     */
    public void setEpsilon(double epsilon) {
        this.epsilon = Math.abs(epsilon);
    }

并测试它

public final static Double ONE = 1.0;
public final static Double THREE = 3.0;

@Test
public void testTreeSet(){
    RealComparator comparator = new RealComparator(0.0);
    Set<Double> set = new TreeSet<Double>(comparator);
    set.add(ONE);
    set.add(ONE);
    set.add(THREE);
    Assert.assertEquals(2, set.size());
}
@Test
public void testTreeSet1(){
    RealComparator comparator = new RealComparator(0.0);
    Set<Double> set = new TreeSet<Double>(comparator);
    set.add(ONE);
    set.add(ONE-0.001);
    set.add(THREE);
    Assert.assertEquals(3, set.size());
}
@Test
public void testTreeSet2(){
    RealComparator comparator = new RealComparator(0.01);
    Set<Double> set = new TreeSet<Double>(comparator);
    set.add(ONE);
    set.add(ONE - 0.001);
    set.add(THREE);
    Assert.assertEquals(2, set.size());
}
@Test
public void testTreeSet3(){
    RealComparator comparator = new RealComparator(0.01);
    Set<Double> set = new TreeSet<Double>(comparator);
    set.add(ONE - 0.001);
    set.add(ONE);
    set.add(THREE);
    Assert.assertEquals(2, set.size());
}
于 2013-04-14T12:39:24.360 回答