此问题与the one here类似。
我们都知道PointF是什么,不是吗?这是数据结构:
public struct PointF
{
public float X;
public float Y;
}
如何以宽容度实施IEqualityComparer<PointF>
?假设我的Equals
代码就像这样
public const float Epsilon = 0.01; //say
public bool Equals(PointF pt1, PointF pt2)
{
return Math.Abs(pt1.X-pt2.X)<Epsilon && Math.Abs(pt1.Y-pt2.Y)<Epsilon;
}
问题:如何实施正确的GetHashCode
以便对于PointF
的字典,我会正确访问该元素?
答案 0 :(得分:14)
您可以将点放在网格中,而不是按距离定义公差 如果两个点位于同一个单元格中,则它们被认为是相同的并且具有相同的哈希码。
public bool Equals(PointF pt1, PointF pt2)
{
return GetCell(pt1.X) == GetCell(pt2.X)
&& GetCell(pt1.Y) == GetCell(pt2.Y);
}
public int GetHashCode(PointF pt)
{
return GetCell(pt.X) ^ GetCell(pt.Y);
}
private static int GetCell(float f)
{
return (int)(f / 10); // cell size is 10 pixels
}
论文:Equals
和GetHashCode
没有符合您要求的实施。
证明:考虑以下三点,A,B和C:
根据您的要求,
Equals(A, B) == true // (i)
Equals(B, C) == true // (ii)
Equals(A, C) == false // (iii)
GetHashCode(A) == GetHashCode(B) // (iv)
GetHashCode(B) == GetHashCode(C) // (v)
GetHashCode(A) != GetHashCode(C) // (vi)
但是从(iv)和(v)开始
GetHashCode(A) == GetHashCode(C)
从而
Equals(A, C) == true
与(iii)和(vi)相矛盾。
由于Equals
和GetHashCode
无法为相同的参数返回不同的值,因此没有符合您要求的实现。
的 q.e.d。强>
答案 1 :(得分:1)
我不认为这是可能的,因为你可以有一个无限的值序列,它们与序列中的上一个和下一个值相等(在容差范围内)但不是任何其他值,GetHashCode
需要返回所有这些都是相同的值。
答案 2 :(得分:0)
嗯,基于网格的答案是好的,但有时你需要对关闭点进行分组,即使它们不在同一个网格单元格中。我的方法是通过分组来实现这一点:如果它们接近或者有一系列连接它们的关闭点,则两个点在同一组中。使用正确的IEqualityComparer
无法完成此语义,因为它需要在生成组之前事先知道所有项目。所以我做了一个简单的LINQ风格的运算符GroupByCluster
,基本上实现了这一点。
代码在这里:http://ideone.com/8l0LH。它在我的VS 2010上编译,但无法在Mono上编译,因为HashSet<>
无法隐式转换为IEnumerable<>
(为什么?)。
这种方法是通用的,因此效率不高:它是输入大小的二次方。对于具体类型,可以使其更有效:例如,对于T = double,我们可以对输入数组进行排序并具有O(n log n)
性能。类似但更复杂的技巧也适用于2D点。
请注意:你的初始命题不可能用IEqualityComparer
来实现,因为你的“近似相等”不是传递的(但IEqualityComparer
中的相等必须如此)。