【发布时间】:2016-10-01 12:49:47
【问题描述】:
我正在使用哈希表并使用约 350,000 个英语单词的语料库,我想尝试均匀分布。因此,我尝试将它们放入一个长度为 810,049 的数组中(最接近的素数大于输入大小的两倍),我很困惑地看到这样一个简单的 FNV1 实现:
public int getHash(String s, int mod) {
final BigInteger MOD = new BigInteger(Integer.toString(mod));
final BigInteger FNV_offset_basis = new BigInteger("14695981039346656037");
final BigInteger FNV_prime = new BigInteger("1099511628211");
BigInteger hash = new BigInteger(FNV_offset_basis.toString());
for (int i = 0; i < s.length(); i++) {
int charValue = s.charAt(i);
hash = hash.multiply(FNV_prime).mod(MOD);
hash = hash.xor(BigInteger.valueOf((int) charValue & 0xffff)).mod(MOD);
}
return hash.mod(MOD).intValue();
}
导致 64,000 次碰撞,很多,基本上是输入的 20%。我的实施有什么问题?这种方法是否存在某种缺陷?
编辑:除此之外,我还尝试并实现了其他散列算法,如 sdbm 和 djb2,它们的性能完全相同,同样糟糕。在这个语料库上都有这些 ~65k 的碰撞。当我将语料库更改为仅表示为字符串的 350,000 个整数时,开始出现一些差异(例如一种算法有 20,000 次碰撞,而另一种算法有 40,000 次),但碰撞次数仍然高得惊人。为什么?
EDIT2:我刚刚对其进行了测试,Java 的内置 .hashCode() 会导致同样多的冲突,即使你做了一些非常幼稚的事情,比如哈希是所有字符模数乘积的乘积810,049,它的性能只比所有那些臭名昭著的算法差一半(60k 碰撞与 90k 与天真的方法)。
【问题讨论】:
-
你打算让调用者作为
mod传递什么?