双精度的位格式将符号存储在第一位。用于double的C#哈希算法是上下32位的二进制异或。
这样,当您对双精度A及其负数-A进行哈希运算时,哈希值的唯一区别是在第一位。
要散列多个字段,大多数参考文献建议使用如下所示的内容:
public override int GetHashCode()
{
unchecked
{
int hash = 17;
hash = hash * 23 + field1.GetHashCode();
hash = hash * 23 + field2.GetHashCode();
return hash;
}
}
最后,考虑两个分别具有两个双精度的对象,如下所示:
对象1:{A,-B} 对象2:{-A,B}
对于A和B中的任何两个双精度值,这两个对象似乎总是散列为相同的值(使用上面的方法,而不管种子和乘数如何)。大致的理由是,熵存储在最高有效位中,并且该位溢出会丢失重要信息。
我希望对具有类似性能但具有出色熵的双打使用不同的哈希值。有什么建议吗?
编辑:请不要写/评论碰撞的必然性。
答案 0 :(得分:1)
您可以在long
上进行哈希处理。将int hash
替换为long hash
,例如:
public override int GetHashCode()
{
unchecked
{
long hash = 17;
hash = hash * 23 + field1.GetHashCode();
hash = hash * 23 + field2.GetHashCode();
return hash.GetHashCode();
}
}
现在,您消除了溢出问题。通过我已经完成的测试(在double
上进行了测试:-)),就解决了您的问题。
答案 1 :(得分:0)
您刚刚指出了为什么简单的XOR并不是组合哈希的最佳实践。
作为参考,以下是System.Tuple<T1,T2>
结合2个散列的方式:
// From System.Web.Util.HashCodeCombiner
internal static int CombineHashCodes(int h1, int h2)
{
return (((h1 << 5) + h1) ^ h2);
}
https://referencesource.microsoft.com/#mscorlib/system/tuple.cs,1806cf6634f5a371
用GetHashCode()实现替换组件类型确实很棘手,但是在组合哈希之后,您可能会引入一个附加术语。
例如,在组件符号位的串联中进行XOR,以确保Hash(d,-d)!= Hash(-d,d)。
internal static int GetSign(double d)
{
return d >= 0 ? 1 : 0;
}
public override int GetHashCode()
{
var signs = GetSign(d2) << 1 | GetSign(d1);
var h = CombineHashCodes(d1.GetHashCode(), d2.GetHashCode());
return h ^ signs;
}