我只是注意到我从对象中获得了不同的哈希码,具体取决于我是为 x86 还是 64 构建的。我也可以这样做吗?
I just noticed I get different hashcodes from objects depending on if I build for x86 or 64. Can I do that aswell?
我注意到当我为 x86 或 x64 构建时,我从其他对象获得的哈希码不同。
到目前为止,我已经实现了大部分自己的哈希函数,如下所示:
int someIntValueA;
int someIntValueB;
const int SHORT_MASK = 0xFFFF;
public override int GetHashCode()
{
return (someIntValueA & SHORT_MASK) + ((someIntValueB & SHORT_MASK) << 16);
}
将值存储在 long 中并从中获取哈希码是否会在 64 位系统上给我提供更广泛的范围,或者这是个坏主意?
public override int GetHashCode()
{
long maybeBiggerSpectrumPossible = someIntValueA + (someIntValueB << 32);
return maybeBiggerSpectrumPossible.GetHashCode();
}
不,那会更糟。
假设您的 int 值通常在 short 范围内:介于 -30000 和 +30000 之间。并进一步假设它们中的大多数都接近中间值,比如说,介于 0 和 1000 之间。这是非常典型的。使用您的第一个哈希码,您可以将 all 两个整数的位都放入哈希码中,并且它们不会相互干扰;在典型条件下,碰撞次数为零。
但是当你用 long 来做你的把戏时,你依赖于 GetHashCode 的 long 实现所做的事情,即高 32 位与低 32 位的异或。所以你的新实现只是一种缓慢的写法int1 ^ int2
。在典型情况下,其中几乎全是零位,因此到处都是冲突。
您建议的方法不会使任何事情变得更好(恰恰相反)。
然而……
例如,SpookyHash 被设计为在 64 位系统上运行特别快,因为在计算数学时,作者正在考虑在 64 位系统上什么会很快,xxHash 有 32 位和 64-位变体,旨在分别为 32 位和 64 位计算提供可比较的哈希质量和更快的速度。
利用不同机器上不同算术运算的差异性能的一般想法是有效的。
你在哈希计算中使用更大的中间存储的一般想法也是有效的只要这些额外的位进入后续操作。
所以在非常一般的层面上,答案是肯定的,即使您的特定实施未能通过。
现在,实际上,当您坐下来编写哈希码实现时,您应该担心这个吗?
这取决于。有一段时间我非常看好使用像 SpookyHash 这样的算法,并且当哈希基于大量源数据时它表现非常好(即使在 32 位系统上)。但另一方面,它可以更好,特别是当与较小的基于散列的集合和字典一起使用时,真正快速的糟糕而不是缓慢的美妙。所以没有一个解决方案适合所有人的答案。只需两个输入整数,您的初始解决方案就可能在许多用途上击败像 xxHash 或 SpookyHash 这样的超可用性算法。如果你也有一个 >> 16
来旋转而不是移动,你可能会做得更好(有趣的是,一些抖动已为此优化),但我们根本没有涉及 64 位和 32 位版本.
在 64 位和 32 位中采用不同的方法确实发现可能有很大改进的情况下,有大量数据需要混合,特别是如果它采用 blittable 形式(如 string
或 byte[]
),您可以通过 long*
或 int*
访问,具体取决于框架。
所以,通常你可以忽略位数的问题,但是如果你发现自己在想"this hashcode has to go through so much stuff to get an answer; can I make it better?"那么也许是时候考虑这些问题了。
我注意到当我为 x86 或 x64 构建时,我从其他对象获得的哈希码不同。 到目前为止,我已经实现了大部分自己的哈希函数,如下所示:
int someIntValueA;
int someIntValueB;
const int SHORT_MASK = 0xFFFF;
public override int GetHashCode()
{
return (someIntValueA & SHORT_MASK) + ((someIntValueB & SHORT_MASK) << 16);
}
将值存储在 long 中并从中获取哈希码是否会在 64 位系统上给我提供更广泛的范围,或者这是个坏主意?
public override int GetHashCode()
{
long maybeBiggerSpectrumPossible = someIntValueA + (someIntValueB << 32);
return maybeBiggerSpectrumPossible.GetHashCode();
}
不,那会更糟。
假设您的 int 值通常在 short 范围内:介于 -30000 和 +30000 之间。并进一步假设它们中的大多数都接近中间值,比如说,介于 0 和 1000 之间。这是非常典型的。使用您的第一个哈希码,您可以将 all 两个整数的位都放入哈希码中,并且它们不会相互干扰;在典型条件下,碰撞次数为零。
但是当你用 long 来做你的把戏时,你依赖于 GetHashCode 的 long 实现所做的事情,即高 32 位与低 32 位的异或。所以你的新实现只是一种缓慢的写法int1 ^ int2
。在典型情况下,其中几乎全是零位,因此到处都是冲突。
您建议的方法不会使任何事情变得更好(恰恰相反)。
然而……
例如,SpookyHash 被设计为在 64 位系统上运行特别快,因为在计算数学时,作者正在考虑在 64 位系统上什么会很快,xxHash 有 32 位和 64-位变体,旨在分别为 32 位和 64 位计算提供可比较的哈希质量和更快的速度。
利用不同机器上不同算术运算的差异性能的一般想法是有效的。
你在哈希计算中使用更大的中间存储的一般想法也是有效的只要这些额外的位进入后续操作。
所以在非常一般的层面上,答案是肯定的,即使您的特定实施未能通过。
现在,实际上,当您坐下来编写哈希码实现时,您应该担心这个吗?
这取决于。有一段时间我非常看好使用像 SpookyHash 这样的算法,并且当哈希基于大量源数据时它表现非常好(即使在 32 位系统上)。但另一方面,它可以更好,特别是当与较小的基于散列的集合和字典一起使用时,真正快速的糟糕而不是缓慢的美妙。所以没有一个解决方案适合所有人的答案。只需两个输入整数,您的初始解决方案就可能在许多用途上击败像 xxHash 或 SpookyHash 这样的超可用性算法。如果你也有一个 >> 16
来旋转而不是移动,你可能会做得更好(有趣的是,一些抖动已为此优化),但我们根本没有涉及 64 位和 32 位版本.
在 64 位和 32 位中采用不同的方法确实发现可能有很大改进的情况下,有大量数据需要混合,特别是如果它采用 blittable 形式(如 string
或 byte[]
),您可以通过 long*
或 int*
访问,具体取决于框架。
所以,通常你可以忽略位数的问题,但是如果你发现自己在想"this hashcode has to go through so much stuff to get an answer; can I make it better?"那么也许是时候考虑这些问题了。