C# 是否可以为匹配多对多的比较器编写哈希代码函数?

C# 是否可以为匹配多对多的比较器编写哈希代码函数?,c#,iequalitycomparer,C#,Iequalitycomparer,我可以为以下比较器逻辑编写哈希代码函数吗 如果(A、B、C)中至少有两个属性匹配,则My的两个实例是相等的 Equals部分很简单,但我对哈希代码部分感到困惑,我的一部分人认为这可能是不可能的 class MyOtherComparer : IEqualityComparer<My> { public bool Equals(My x, My y) { if (Object.ReferenceEquals(x, y)) ret

我可以为以下比较器逻辑编写哈希代码函数吗

如果(A、B、C)中至少有两个属性匹配,则My的两个实例是相等的

Equals部分很简单,但我对哈希代码部分感到困惑,我的一部分人认为这可能是不可能的

class MyOtherComparer : IEqualityComparer<My>
{
    public bool Equals(My x, My y)
    {
        if (Object.ReferenceEquals(x, y)) 
            return true;      

        if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null)) 
            return false;

        int matches = 0;

        if(x.A == y.A) matches++;

        if(x.B == y.B) matches++;

        if(x.C == y.C) matches++;

        // match on two out of three
        return  (matches > 1)
    }

    // If Equals() returns true for a pair of objects 
    // then GetHashCode() must return the same value for these objects.
    public int GetHashCode(My x)
    {
       // ???
    }
}
类MyOtherComparer:IEqualityComparer
{
公共布尔等于(我的x,我的y)
{
if(Object.ReferenceEquals(x,y))
返回true;
if(Object.ReferenceEquals(x,null)| | Object.ReferenceEquals(y,null))
返回false;
int匹配=0;
如果(x.A==y.A)匹配++;
如果(x.B==y.B)匹配++;
如果(x.C==y.C)匹配++;
//三局两胜
返回(匹配项>1)
}
//对于一对对象,If Equals()返回true
//然后GetHashCode()必须为这些对象返回相同的值。
public int GetHashCode(My x)
{
// ???
}
}

更新:除了Reed Copsey的正确答案外,Ethan Brown还明确指出了模糊比较器的一般用途的一个非常重要的问题-请参阅他的答案,以全面了解这个问题/答案的基础。

对于两个相等的对象,哈希代码必须相同,但对于两个不同的对象,它不必不同。您可以为所有对象返回相同的值,以满足
IEqualityComparer
消费者的要求,但我知道在您的情况下,无法从哈希中获得任何速度优势。

是的,这是可能的。最简单的实现是始终返回一个常量

public int GetHashCode(My x) 
{ 
   return 0;
}
文件规定:

需要实现来确保如果Equals方法为两个对象x和y返回true,那么GetHashCode方法为x返回的值必须等于为y返回的值

但是,对于两个不相等的对象,您可以完全自由地返回相同的哈希代码

这就是说,这可能会导致一些算法的性能非常差,因为您将得到大量哈希冲突。但是,鉴于奇数/唯一相等性检查的性质,可能需要这样做


请注意,这在任何情况下都会有问题。根据您的逻辑,可以有三个对象,其中
comparer.Equals(foo,bar)==true
comparer.Equals(foo,baz)==true
comparer.Equals(baz,bar)==false
。在使用
IEqualityComparer
的许多情况下,这可能会出现问题

我可以为以下比较器逻辑编写哈希代码函数吗

对。您总是可以为任何内容编写哈希代码。问题是它的效率有多高。无论发生什么,您都可以:

public int GetHashCode()
{
  return 0;
}

它会一直工作,但效率非常低*

假设我们有两个对象A、B,每个对象都有属性p1、p2和p3。假设A.p1==B.p1和A.p3==B.p3,如果散列函数依赖于p2,则A和B的散列函数不同,因此它们不相等。如果要基于p1和p3计算哈希函数,有许多示例表明哈希函数不会返回正确的哈希值,并且许多相等的对象将不相等。我认为我们不能有一个可变函数。您可以使用常量散列函数,但如果您想将其用作字典或散列表中的散列键,则不会获得接近O(1)的复杂性。

获得非常量散列函数的核心问题是无法确保跨等式的传递性。通常,平等被认为是可传递的。也就是说,A=B和B=C意味着A=C(这进一步意味着A、B和C都具有相同的哈希代码)。然而,根据你对等式的定义,你可以有A=B,B=C和A=理想情况下,不等元素将具有不同的散列码,因此A和C将具有不同的散列码;但它们不能,因为它们都等于B,所以它们必须都有相同的散列码

获得非常量散列函数的唯一方法是了解有关总集合的信息。您必须将集合划分为“相等容器”,其中容器中的每个元素与容器中的其他元素相等(包括一个容器的可能性)。一旦完成了这个分区,就可以使用它来生成一个非常量算法(假设您得到多个bin)来生成哈希代码

关于相等容器的概念,有很多这样的容器配置。作为选择标准,您可能希望最大化存储箱的数量(以提高哈希表查找性能)。退化的情况(如里德·科普西的正确答案所示)是,你把所有东西都放在同一个箱子里(尽管,正如supercat在下面的评论中指出的,“平等箱子”这个名称会产生误导)。这并没有违反哈希值的任何约束,但它会导致那些期望has值生成非退化分区的算法性能低下


正如supercat在下面指出的,要满足哈希值的约束,必须满足以下条件:如果两个元素位于两个不同的容器中,则它们不能相等(但是,同一容器中的两个元素不必相等)。

看到您真正的问题是处理Except扩展方法, 我决定给你提些建议,虽然不是真正的答案

public class EqualityComparer<T> : IEqualityComparer<T>
{
    private readonly Func<T, T, bool> _comparer;
    private readonly Func<T, int> _hashCoder;

    public EqualityComparer(Func<T, T, bool> comparer, Func<T, int> hashCoder = null)
    {
        if (comparer == null)
        {
            throw new ArgumentNullException("comparer");
        }

        this._comparer = comparer;
        this._hashCoder = hashCoder ?? (x => 0);
    }

    public bool Equals(T x, T y)
    {
        return this._comparer(x, y);
    }

    public int GetHashCode(T obj)
    {
        return this._hashCoder(obj);
    }
}
公共类EqualityComparer:IEqualityComparer
{
专用只读函数比较器;
专用只读函数哈希编码器;
public EqualityComparer(Func comparer,Func hashCoder=null)
{
if(比较器==null)
{
抛出新的ArgumentNullException(“comparer”);
}
这个。比较器=比较器;
这个._hashCoder=hashCoder??(x=>0);
}
公共图书馆
arr1.Except(arr2, new EqualityComparer<dynamic>((x, y) =>
     {
         if (ReferenceEquals(x, y))
             return true;

         if (ReferenceEquals(x, null) ||
             ReferenceEquals(y, null))
             return false;

         var matches = 0;

         if (x.A == y.A) matches++;
         if (x.B == y.B) matches++;
         if (x.C == y.C) matches++;

         return (matches > 1);
     }));