使用C#HashSet解决相等不相等的问题

时间:2012-01-06 23:22:30

标签: c# object hash dictionary hashset

我的基础是我最近发现的关于Dictionary的性能特征,所以我使用Dictionary<type, bool>bool被忽略,但据说我可以使用{{} 1}}而不是。

例如:

HashSet

这里我没有使用等于检查相等,而是重叠,这在其他地方肯定会很烦人,但我有理由这样做。

我假设如果在O(1)时间内可以从一个键中查找一个值,那么一个键就可以自己查找。

因此,我可能会将数千个边界重叠并执行此操作:

Dictionary<bounds, bool> 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_x + other.width &&
        upper_left_y + height > other.upper_left_y &&
        upper_left_y < other.upper_left_y + other.height;
    }

    public ... GetHashCode()
    {
        ...;
    }
}

如果给定的绑定与集合中的任何其他绑定重叠,则在O(1)时间内找出。

我也想知道如果我改变一个边界的(x,y)位置会发生什么,大概就像删除然后再次将它添加到集合中,性能明智,非常昂贵?

我将什么内容放入GetHashCode函数?

目标

如果这样可行,那么我在使用这种机制后找出给定边界重叠的其他边界。

此系统中很少有边界移动,并且在填充集合后不会添加任何新边界。新添加的边界需要能够重叠旧边界。

结论

有关详细信息,请参阅下面的反馈。

总之,不可能实现O(1)性能,因为与默认等于不同,检查重叠是不可传递的。

然而,区间树是一个很好的解决方案。

5 个答案:

答案 0 :(得分:10)

这里使用的等式关系完全是错误的关系因为等式需要是等价关系。也就是说,对于任何A,它必须是自反 - A == A.它必须是对称的 - A == B意味着B == A.并且它必须传递 - 如果A == B且B == C则A == C.

您提议违反传递财产; “重叠”不是传递关系,因此“重叠”不是等价关系,因此你不能将相等定义为重叠

而不是试图做这个危险的事情,解决真正的问题。您的目标显然是采用一组间隔,然后快速确定给定间隔是否与任何间隔重叠。您想要的数据结构称为间隔树; 专门针对该问题进行了优化,因此请使用在任何情况下都不应该尝试将哈希集用作间隔树。使用正确的工具进行工作:

http://wikipedia.org/wiki/Interval_tree

答案 1 :(得分:8)

  

这里我没有使用等于检查相等,而是重叠,这在其他地方肯定会很烦人,但我有理由这样做。

我假设这意味着你将有一个场景,其中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。

答案 2 :(得分:1)

如果您使用上面提到的派生类方法,则需要以下内容:

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)。

答案 3 :(得分:1)

您无法使用DictionaryHashSet来检查边界是否重叠。为了能够使用字典(或散列集),您需要符合以下属性的Equals()GetHashCode()方法:

  1. Equals()方法是equivalence relation
  2. a.Equals(b)必须暗示a.GetHashCode() == b.GetHashCode()
  3. 您无法满足其中任何一项要求,因此您必须使用其他数据结构:An Interval tree

答案 4 :(得分:0)

您无法保证自定义O(1)字典的hashcode calculation效果。如果我在GetHashCode()方法中放入一些WebService请求,它应该控制2个提供项的相等性,很明显时间永远不会像预期的那样O(1)。好吧,这是一种“边缘案例”,但只是提出一个想法。

通过您认为可以做的方式(假设这甚至可能), imo ,您否定了Dictionary<K,V>提供的好处,因此恒定密钥恢复时间也在大集合。

需要在合理数量的对象上进行测量,但我会首先尝试使用 List<T>喜欢对象持有者,并做出类似这样的事情:

var bounds = new List<Bound> {.... initialization... }
Bound providedBound = //something. Some data filled in it. 
var overlappedany = bounds.Any<Bound>(b=>return b.Equals(providedBound));