1

我在相等和向字典中添加对象时遇到问题

class DoublePoint
{
    public double X;
    public double Y;
    public double Z;

    public DoublePoint(double x, double y, double z)
    {
        this.X = x; this.Y = y; this.Z = z;
    }

    public override bool Equals(object obj)
    {
        try
        {
            DoublePoint dPoint = obj as DoublePoint;
            return this.X.IsEqualTo(dPoint.X) && this.Y.IsEqualTo(dPoint.Y) && this.Z.IsEqualTo(dPoint.Z);
        }
        catch
        {
            throw;
        }
    }

    public override int GetHashCode()
    {
        return this.X.GetCode() ^ this.Y.GetCode() ^ this.Z.GetCode();
    } 
}

static class extensions
{
    static double Tolerance = 0.001;
    public static bool IsEqualTo(this double d1, double d2)
    {
        return (d1 - d2) <= Tolerance;
    }

    public static int GetCode(this double d1)
    {
        byte[] data = BitConverter.GetBytes(d1);
        int x = BitConverter.ToInt32(data, 0);
        int y = BitConverter.ToInt32(data, 4);
        return x ^ y; 
    }
}

这是我的测试:

DoublePoint d1 = new DoublePoint(1.200, 2.3, 3.4);
        DoublePoint d2 = new DoublePoint(1.2001, 2.3, 3.4);
        DoublePoint d3 = new DoublePoint(1.200, 2.3, 3.4);
        bool isEqual = d1.Equals(d2); // true here


        Dictionary<DoublePoint, int> dict = new Dictionary<DoublePoint, int>();
        dict.Add(d1, 1); 
        dict.Add(d2, 2); // successful, d2 is also added but d2 is equal to d1
        dict.Add(d3, 3); // Error! since we have d1 already in dictionary

有了这个,

  1. 当我添加相同的双点对象(具有一定的公差)时,我可以将它们添加到字典中。如何限制此类对象。

  2. 是比较具有一定公差的双重数据类型的正确方法。

请指教。

谢谢

4

1 回答 1

1

将“相等”定义为“足够接近”存在问题。毫无疑问,它对计算很有用,但是这种“相等”违反了传递性规则:因为Equalsif a.Equals(b) && b.Equals(c), thena.Equals(c) 必须成立(这显然不是您的代码的属性)。

所以,IsEqualTo不幸的是不适合重新定义Equals.

解决问题的可能方法是什么?Equals必须分成不相交的“等效”值组。我通常执行以下操作:定义一个规则以从组中获取“规范”值,因此如果它们的规范组代表相等,则两个值是“相等的”。

简单的例子:对于一个 double 值,d让我们将规范值定义为Math.Floor(d)。所以这样你有 1.0 等于 1.1,0.9 等于 0.0 但等于 1.0。这种方式并不理想(毕竟0.9不等于1.0但等于0.0似乎是错误的),但至少传递性规则是成立的。

特别是对于您的情况,可能是这样的:

class DoublePoint
{
    public double X;
    public double Y;
    public double Z;

    const double epsilon;
    void GetCanonicalValues(out double x, out double y, out double z)
    {
        x = Math.Floor(X / epsilon) * epsilon;
        y = Math.Floor(Y / epsilon) * epsilon;
        z = Math.Floor(Z / epsilon) * epsilon;
    }

    public override bool Equals(object obj)
    {
        DoublePoint that = obj as DoublePoint;
        if (that == null)
            return false;
        double x1, y1, z1, x2, y2, z2;
        this.GetCanonicalValues(out x1, out x2, out z2);
        that.GetCanonicalValues(out x1, out x2, out z2);
        return (x1 == x2) && (y1 == y2) && (z1 == z2); // here we can compare
    }

    ...

您的代码的另一个问题是您的代码GetHashCode不符合Equals: if a.Equals(b)then a.GetHashCode() must equal b.GetHashCode()

您也可以通过使用规范值来解决此问题:

    public override int GetHashCode()
    {
        double x, y, z;
        GetCanonicalValues(out x, out y, out z);
        return x.GetHashCode() ^ y.GetHashCode() ^ z.GetCode();
    }
}

请注意, 的行为Equals可能无法满足您的需求 - 然后您需要以其他方式确保传递性。

于 2013-03-03T13:23:20.443 回答