【发布时间】:2016-12-16 15:45:43
【问题描述】:
我遇到以下问题:
- 给定一组无序、任意大的 id(例如 32 位空间中的 1、2、5、6、8)
- 在更大的空间(例如 64 位)中计算哈希码。
简单的方法是为每个单独的 ID 计算一个哈希函数,然后对所有内容进行异或运算。但是,如果您有一个 32 位空间用于 ID 和一个 64 位空间用于散列函数,这可能不是解决此问题的最佳方法(冲突等......)。
我一直在考虑使用 Murmur3 终结器,然后对结果进行异或运算,但我想出于同样的原因,这也行不通(我不确定是否诚实)。同样,简单地将值相乘也应该有效(因为 ab = ba),但我不确定哈希函数有多“好”。
显然,我想到了对 ID 进行排序,然后 Murmur3 会做得很好。不过,如果可以避免的话,我不想排序。
这样的哈希函数有什么好的算法?
更新
好吧,我想我可能有点困惑。
Why is XOR the default way to combine hashes? 上的第二个答案实际上解释了组合哈希函数。在那里提出的案例中,XOR 被认为是一个糟糕的哈希函数,因为“dab”产生与“abd”相同的代码。就我而言,我希望这些东西生成相同的哈希值 - 但我也想尽量减少 -say- “abc” 也生成与 -say- “abd” 相同的哈希值的机会.
大多数散列函数的全部目的是,如果您向它们提供数据,它们很有可能使用完整的键空间。通常,这些散列函数利用数据是顺序这一事实,并乘以一个大数来打乱位。简单来说:
var hash = SomeInitialConstant;
foreach (var id in ids) {
hash = hash * SomeConstant + hashCode(id);
}
// ... optionally shuffle bits around as finalizer
return hash;
现在,如果 ID 始终处于相同的顺序,这可以正常工作。但是,如果 ID 是无序的,这将不起作用,因为 x * constant + y 不是可交换的。
如果您对 ID 进行平方,我认为您最终不会使用整个哈希空间。考虑一下如果你有很大的数字会发生什么,比如 100000、100001 等。它们的平方是 10000000000、10000200001 等等。你不可能得到一个正方形来生成像 900000 这样的数字(仅仅因为 sqrt(900000)是一个带分数的数字)。
更一般地说,10000000000 和 10000200001 之间的所有哈希空间很可能都会丢失。但是,-say- 0 和 10 之间的空间会发生很多冲突,因为小数平方之间的可用哈希空间也很小。
使用大密钥空间的全部目的显然是减少冲突。我希望有一个相当大的哈希空间(比如 256 位),以确保在现实生活场景中几乎不存在冲突。
【问题讨论】:
-
您希望输出是 32 位哈希码还是 64 位哈希码?
-
@JimMischel 我希望我的输出位于更大的域中(例如 64 位)。顺便说一句,我刚刚注意到 64 位作为示例;最终我想搬到 f.ex。 256 位以避免可能的冲突。
-
投反对票的能解释一下吗?
-
我没有投反对票,但我确实考虑了一会儿:这个问题有点令人困惑,不得不花点时间来解决它。无论如何,about combining hashes。好消息是您不必担心“a xor a = 0”(如果 ID 是唯一的),而交换性实际上是您的朋友。
-
顺便说一句:900000 是两个平方的 sum:
(900*900 + 300*300)