使用C#HashSet解决相等不相等的问题
我基于我最近发现的关于Dictionary
性能特征,因此我使用Dictionary
,其中bool
被忽略但据说我可以使用HashSet
。
例如:
Dictionary overlap; class bounds { public float top_left_x, top_left_y, width, height; public bool equal(bounds other) { return upper_left_x + width > other.upper_left_x && upper_left_x other.upper_left_y && upper_left_y < other.upper_left_y + other.height; } public ... GetHashCode() { ...; } }
在这里,我没有使用等于检查相等,而是重叠,这在其他地方肯定会令人讨厌,但我有理由这样做。
我假设如果可以在O(1)时间内从一个键中查找一个值,那么键也可以从其自身中查找。
所以我可能会将数千个边界重叠并执行此操作:
overlap.ContainsKey(new bounds(...));
如果给定的绑定与集合中的任何其他绑定重叠,则在O(1)时间内找出。
我还想知道如果我改变一个边界的(x,y)位置会发生什么,大概就像删除然后再次将它添加到集合中,性能明智,非常昂贵?
我将什么放入GetHashCode函数?
目标
如果这有效,那么我在使用这种机制后找出给定边界重叠的其他边界。
在这个系统中很少有边界移动,并且在填充集合后没有添加新的边界。 新添加的边界需要能够重叠旧的边界。
结论
有关详细信息,请参阅下面的反馈。
总之,不可能实现O(1)性能,因为与默认等于不同,检查重叠是不可传递的。
然而,间隔树是一个很好的解决方案。
在这里,我没有使用等于检查相等,而是重叠,这在其他地方肯定会令人讨厌,但我有理由这样做。
我假设这意味着你将有一个场景,其中A.Equals(B)为真,B.Equals(C)为真,但A.Equals(C)为假。 换句话说,您的等于不可传递。
这违反了Equals()的规则,因此Dictionary不适合你。 Equals / GetHashCode规则是(来自http://msdn.microsoft.com/en-us/library/system.object.gethashcode.aspx ):
如果两个对象比较相等,则每个对象的GetHashCode方法必须返回相同的值。
如果您的Equals不可传递,那么您不可能编写有效的GetHashCode。
在这里使用等式关系是完全错误的关系 ,因为等式需要是等价关系。 也就是说,它必须是自反的 – 对于任何A,A == A.它必须是对称的 – A == B意味着B == A.并且它必须是可传递的 – 如果A == B且B == C然后A == C.
您提议违反过渡性财产; “重叠”不是传递关系,因此“重叠”不是等价关系,因此您不能将相等定义为重叠 。
而不是试图做这个危险的事情,解决真正的问题。 您的目标显然是采用一组间隔,然后快速确定给定间隔是否与任何间隔重叠。 您想要的数据结构称为区间树 ; 它专门针对这个问题进行了优化,因此请使用它 。 在任何情况下,您都不应尝试将哈希集用作间隔树。 使用正确的工具:
如果您使用我上面提到的派生类方法,您需要以下内容:
public class Bounds { public Point position; public Point size; // I know the width and height don't really compose // a point, but this is just for demonstration public override int GetHashCode(){...} } public class OverlappingBounds : Bounds { public override bool Equals(object other) { // your implementation here } } // Usage: if (_bounds.ContainsKey(new OverlappingBounds(...))){...}
但由于GetHashCode()方法需要始终返回相同的值,因此运行时复杂度很可能是O(n)而不是O(1)。
您不能使用Dictionary
或HashSet
来检查边界是否重叠。 为了能够使用字典(或散列集),您需要一个满足以下属性的Equals()
和GetHashCode()
方法:
-
Equals()
方法是等价关系 -
a.Equals(b)
必须暗示a.GetHashCode() == b.GetHashCode()
您无法满足这些要求中的任何一个,因此您必须使用另一个数据结构: 间隔树 。
您无法保证在自定义hashcode calculation
字典上的O(1)
性能。 如果我在GetHashCode()
方法中放入一些WebService请求,它应该控制2个提供的项的相等性,很明显时间永远不会像预期的那样是O(1)
。 好吧,这是一种“边缘情况”,但只是提出一个想法。
通过你想要做的事情(假设这甚至可能), imo ,你否定了Dictionary
提供的好处,所以恒定的密钥恢复时间也在大集合上。
它需要在你拥有的合理数量的对象上进行测量 ,但我会首先尝试像对象持有者一样使用List
,并做出类似这样的事情:
var bounds = new List {.... initialization... } Bound providedBound = //something. Some data filled in it. var overlappedany = bounds.Any (b=>return b.Equals(providedBound));