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