顶点的等于和哈希码

时间:2013-07-11 12:22:31

标签: c# hash geometry equals hashcode

我想要将几个顶点放入Hashtable中。彼此非常接近的顶点被视为相同的顶点。我的C#顶点类看起来像这样:

public class Vertex3D
{
    protected double _x, _y, _z;
    public static readonly double EPSILON = 1e-10;

    public virtual double x 
    {
        get { return _x;}
        set { _x = value; }
    }

    public virtual double y
    {
        get { return _y; }
        set { _y = value; }
    }

    public virtual double z
    {
        get { return _z; }
        set { _z = value; }
    }

    public Vertex3D(double p1, double p2, double p3)
    {
        this._x = p1;
        this._y = p2;
        this._z = p3;
    }

    public override bool Equals(object obj)
    {
        var other = obj as Vertex3D;

        if (other == null)
        {
            return false;
        }
        double diffx = this.x - other.x;
        double diffy = this.y - other.y;
        double diffz = this.z - other.z;
        bool eqx = diffx > -EPSILON && diffx < EPSILON;
        bool eqy = diffy > -EPSILON && diffy < EPSILON;
        bool eqz = diffz > -EPSILON && diffz < EPSILON;
        return eqx && eqy && eqz;
    }

    public override int GetHashCode()
    {
        return this.x.GetHashCode() ^ this.y.GetHashCode() ^ this.z.GetHashCode();
    }

    public override string ToString()
    {
        return "Vertex:" + " " + x + " " + y + " " + z;
    }

现在假设我将以下两个顶点放入字典中(字典是一个不允许空键的哈希表):

    Dictionary<Vertex3D, Vertex3D> vertexList = new Dictionary<Vertex3D, Vertex3D>();
    Vertex3D v0 = new Vertex3D(0.000000000000000037842417475065449, -1,   0.00000000000000011646698526992202));
    Vertex3D v1 = new Vertex3D(0, -1, 0));
    vertexList.Add(v0, v0);
    vertexList.Add(v1, v1);

问题是我的equals和hashcode实现有问题。上述两个顶点被认为是相等的,因为彼此之间的距离小于EPSILON。但是他们没有返回相同的哈希码。

如何正确实现equals和hashcode?

2 个答案:

答案 0 :(得分:1)

通常,如果mutable-thing引用都引用同一个对象,那么它们应该被认为是等价的。只有对不可变事物的引用才能使用任何其他相等的定义。如果Object包含虚函数来测试两个引用由不同对象持有的场景中的等价性将是有帮助的,这两个引用都不会公开它对可能改变它的任何东西的引用。不幸的是,尽管有效不可变实例的可变类型模式非常常见(例如,几乎所有不可变集合都使用一个或多个可变类型对象,例如数组来保存它们的数据),但是没有标准模式等价测试。

如果要使用Object.Equals将顶点存储在字典中进行相等性测试,则它应该是不可变类型。或者,您可以定义自定义IEqualityComparer<T>以用于字典,但您应该知道Dictionary只应用于查找完美匹配。如果您希望能够找到在给定点的EPSILON范围内的任何点,则应使用将舍入值映射到精确值列表的值(值应舍入为2的幂,至少为两倍。伟大的epsilon)。如果从点中的某些或所有坐标添加或减去EPSILON会导致它以不同的方式进行舍入,则该点应包含在字典中,并以每种可能的方式舍入。

答案 1 :(得分:1)

Hashtables需要等价类,但您的Equals()不具有传递性。因此,您不能为此目的使用哈希表。 (例如,如果你允许附近的物体通过舍入到格点来进行比较,那么你将具有传递性和等价类。但是那时仍然会有任意的近点,直到表示的精度,它落在相反的两侧阈值,因此在不同的等价类中)

还有其他数据结构,例如octtrees,旨在加速寻找附近的点。我建议你使用其中一种。