C# 快速简单的哈希代码组合

C# 快速简单的哈希代码组合,c#,algorithm,hash,hashcode,C#,Algorithm,Hash,Hashcode,人们可以推荐快速简单的方法来组合两个对象的哈希代码吗。我不太担心冲突,因为我有一个哈希表,它可以有效地处理冲突,我只希望能够尽快生成代码 从SO和网络上看,似乎有几个主要的候选人: XORing 素数乘法异或 简单的数字运算,如乘法/除法(带溢出检查或换行) 构建一个字符串,然后使用字符串类哈希代码方法 人们会推荐什么?为什么?如果您的输入哈希值大小相同、分布均匀且彼此不相关,那么异或就可以了。而且速度很快 我所建议的情况就是你想做的 H = hash(A) ^ hash(B); // A an

人们可以推荐快速简单的方法来组合两个对象的哈希代码吗。我不太担心冲突,因为我有一个哈希表,它可以有效地处理冲突,我只希望能够尽快生成代码

从SO和网络上看,似乎有几个主要的候选人:

  • XORing
  • 素数乘法异或
  • 简单的数字运算,如乘法/除法(带溢出检查或换行)
  • 构建一个字符串,然后使用字符串类哈希代码方法

  • 人们会推荐什么?为什么?

    如果您的输入哈希值大小相同、分布均匀且彼此不相关,那么异或就可以了。而且速度很快

    我所建议的情况就是你想做的

    H = hash(A) ^ hash(B); // A and B are different types, so there's no way A == B.
    

    当然,如果A和B可以以合理的(不可忽略的)概率散列到相同的值,那么您不应该以这种方式使用XOR。

    我个人会避免XOR-这意味着任何两个相等的值都将导致0-因此散列(1,1)=散列(2,2)=散列(3,3)等。也可以使用散列(5,0)=散列(0,5)等等,这可能会偶尔出现。我特意将其用于集合散列-如果您想要散列一系列项目,而不关心排序,那么这很好

    我通常使用:

    unchecked
    {
        int hash = 17;
        hash = hash * 31 + firstField.GetHashCode();
        hash = hash * 31 + secondField.GetHashCode();
        return hash;
    }
    

    这是Josh Bloch在有效Java中建议的形式。上次我回答了一个类似的问题,我设法找到了一篇文章,其中详细讨论了这一点——IIRC,没有人真正知道它为什么工作得很好,但它确实如此。它还易于记忆、易于实现,并且易于扩展到任意数量的字段。

    我建议使用System.Security.Cryptography中的内置哈希函数,而不是自己滚动。

    如果您希望速度快,并且没有太多冲突,那么XOR是最快的。要防止聚集在零附近,可以执行以下操作:

    finalHash = hash1 ^ hash2;
    return finalHash != 0 ? finalHash : hash1;
    

    当然,一些原型应该能让您了解性能和集群。

    虽然Jon Skeet的答案中概述的模板通常作为哈希函数家族运行良好,常数的选择很重要,答案中提到的
    17
    的种子和
    31
    的因子对于常见用例根本不起作用。在大多数用例中,散列值比
    int.MaxValue
    更接近于零,联合散列的项目数只有几十个或更少


    对于散列整数元组
    {x,y}
    ,其中
    -1000,我认为.NET Framework团队在测试其实现方面做得不错,因此我将使用它:

    // System.String.GetHashCode(): http://referencesource.microsoft.com/#mscorlib/system/string.cs,0a17bbac4851d0d4
    // System.Web.Util.StringUtil.GetStringHashCode(System.String): http://referencesource.microsoft.com/#System.Web/Util/StringUtil.cs,c97063570b4e791a
    public static int CombineHashCodes(IEnumerable<int> hashCodes)
    {
        int hash1 = (5381 << 16) + 5381;
        int hash2 = hash1;
    
        int i = 0;
        foreach (var hashCode in hashCodes)
        {
            if (i % 2 == 0)
                hash1 = ((hash1 << 5) + hash1 + (hash1 >> 27)) ^ hashCode;
            else
                hash2 = ((hash2 << 5) + hash2 + (hash2 >> 27)) ^ hashCode;
    
            ++i;
        }
    
        return hash1 + (hash2 * 1566083941);
    }
    
    //System.String.GetHashCode():http://referencesource.microsoft.com/#mscorlib/system/string.cs,0a17bbac4851d0d4
    //System.Web.Util.StringUtil.GetStringHashCode(System.String):http://referencesource.microsoft.com/#System.Web/Util/StringUtil.cs,c97063570b4e791a
    公共静态int组合哈希码(IEnumerable哈希码)
    {
    int hash1=(5381 27))^hashCode;
    其他的
    hash2=((hash2>27))^hashCode;
    ++一,;
    }
    返回hash1+(hash2*1566083941);
    }
    
    另一个实现来自和方法。这一个更简单,但可能没有上面方法那样好的分布:

    // System.Web.Util.HashCodeCombiner.CombineHashCodes(System.Int32, System.Int32): http://referencesource.microsoft.com/#System.Web/Util/HashCodeCombiner.cs,21fb74ad8bb43f6b
    // System.Array.CombineHashCodes(System.Int32, System.Int32): http://referencesource.microsoft.com/#mscorlib/system/array.cs,87d117c8cc772cca
    public static int CombineHashCodes(IEnumerable<int> hashCodes)
    {
        int hash = 5381;
    
        foreach (var hashCode in hashCodes)
            hash = ((hash << 5) + hash) ^ hashCode;
    
        return hash;
    }
    
    //System.Web.Util.HashCodeCombiner.CombineHashCodes(System.Int32,System.Int32):http://referencesource.microsoft.com/#System.Web/Util/HashCodeCombiner.cs,21fb74ad8bb43f6b
    //System.Array.CombineHashCodes(System.Int32,System.Int32):http://referencesource.microsoft.com/#mscorlib/system/array.cs,87d117c8cc772cca
    公共静态int组合哈希码(IEnumerable哈希码)
    {
    int hash=5381;
    foreach(hashCodes中的var hashCode)
    
    hash=((hash使用tuple中的组合逻辑。示例使用c#7 tuple


    如果使用的是.NET<强>核心2.1 或.NETFramework <强> 4.61或更高版本,请考虑使用Stutt来帮助生成复合哈希代码。它有两种操作模式:添加和组合。< /P> 使用
    组合
    的示例通常更简单,最多可用于八个项目:

    public override int GetHashCode()
    {
        return HashCode.Combine(object1, object2);
    }
    
    使用
    添加
    的示例:

    public override int GetHashCode()
    {
        var hash = new HashCode();
        hash.Add(this.object1);
        hash.Add(this.object2);
        return hash.ToHashCode();
    }
    
    优点:

    • 作为.NET本身的一部分,从.NET核心2.1/.NET标准2.1开始(尽管如此,请参见下面的con)
      • 对于.NETFramework4.6.1及更高版本,可以使用NuGet包对这种类型进行后端口
    • 根据作者和评论员之前所做的工作,看起来具有良好的性能和混合特性
    • 自动处理空值
    • 采用实例的重载
    缺点:

    • 在.NET 4.6.1之前的.NET Framework上不可用。
      HashCode
      是.NET标准2.1的一部分。截至2019年9月,.NET团队已经,如
    • 通用,因此它不会处理超特定的情况以及手工编写的代码

    假设您有一个相关的toString()函数(您的不同字段将出现在该函数中),我将返回其哈希代码:

    this.toString().hashCode();
    
    这不是很快,但它应该可以很好地避免碰撞。

    这是对特殊酱汁的精妙研究溶液的重新包装。
    它使用值元组(
    ITuple
    )。
    这允许参数
    种子
    因子
    的默认值

    public static int CombineHashes(this ITuple tupled, int seed=1009, int factor=9176)
    {
        var hash = seed;
    
        for (var i = 0; i < tupled.Length; i++)
        {
            unchecked
            {
                hash = hash * factor + tupled[i].GetHashCode();
            }
        }
    
        return hash;
    }
    

    我如何判断我的散列码是否均匀分布,是否有一个简单的基准来实现这一点?我知道冲突率很低,但这是否一定对应于均匀分布?看起来像Dan Bernstein的(或Chris Torek的)散列,只是使用不同的常量。没有人知道为什么它工作得很好。警告一句,这是Berstein散列的一个变体,因为没有人知道为什么它在测试中工作得很好,所以当散列非常重要时,不建议使用它。另外,您应该将此代码包装在
    未检查的{}
    块中。GetHashCode()sh
    this.toString().hashCode();
    
    public static int CombineHashes(this ITuple tupled, int seed=1009, int factor=9176)
    {
        var hash = seed;
    
        for (var i = 0; i < tupled.Length; i++)
        {
            unchecked
            {
                hash = hash * factor + tupled[i].GetHashCode();
            }
        }
    
        return hash;
    }
    
    var hash1 = ("Foo", "Bar", 42).CombineHashes();    
    var hash2 = ("Jon", "Skeet", "Constants").CombineHashes(seed=17, factor=31);