English 中文(简体)
如何测试哈希函数?
原标题:
  • 时间:2008-12-24 22:50:59
  •  标签:

有没有一种方法可以测试哈希函数的质量?在哈希表中使用时,我希望有很好的分布,并且如果这在单元测试中可以验证,那就太好了。

编辑:为了澄清,我的问题是我在Java中使用long值,以这样的方式,即第一个32位编码了一个ID,第二个32位编码了另一个ID。不幸的是,Java对长值的哈希只是将第一个32位与第二个32位进行异或,这在我的情况下在HashMap中使用时导致性能非常差。因此,我需要一个不同的哈希,并希望有一个单元测试,以便这个问题不会再次出现。

最佳回答

你需要使用从相同(或类似)分布中提取的数据测试你的哈希函数,这样它才能按照你期望的方式工作。当查看作用于64位长整型的哈希函数时,如果输入值从所有可能的长整型值中均匀提取,则默认的Java哈希函数非常出色。

然而,您提到您的应用程序使用 long 来存储两个基本独立的 32 位值。尝试生成与您实际使用的值类似的样本,然后进行测试。

对于测试本身,取您的样本输入值,对每个值进行哈希,并将结果放入一个集合中。计算所得集合的大小,将其与输入集合的大小进行比较,这将告诉您您的哈希函数生成的冲突数量。

针对您的特定应用,不要简单地将它们进行异或,尝试以典型良好哈希函数组合两个独立 int 的方式组合这些32位值。即乘以一个素数,再加上。

问题回答

首先,我认为你必须定义你自己对"良好传播"的含义。你是指所有可能输入的良好传播,还是仅限于可能性较高的输入的良好传播?

例如,如果您正在进行哈希字符串,表示正确的全名(名字+姓氏),您可能不会关心数字ASCII字符如何哈希。

关于测试,你最好的选择可能是获取你期望的一组巨大或随机的输入数据,并将其通过哈希函数,观察分布情况如何。可能不太可能有一个神奇的程序能够说“是的,这是一个适合你使用情况的好哈希函数”。不过,如果你能以程序方式生成输入数据,你应该很容易地创建一个单元测试来生成大量的数据,然后验证分布是否在你的“好”的定义范围内。

编辑:在您使用64位长整型的情况下,是否真的有必要使用哈希映射?为什么不直接使用平衡树,并直接使用长整型作为键,而不是对其进行再哈希?您可能会在总节点大小上付出一点代价(键值的大小增加了2倍),但最终可以节省性能。

如果您正在使用链接哈希表,那么您真正关心的是碰撞的数量。这可以作为哈希表上的一个简单计数器轻松实现。每次插入一个项目且表必须链接时,增加一个链计数器。更好的哈希算法将导致更少的碰撞。一个好的通用哈希函数是:djb2。

根据你的澄清:

我在Java中使用了长整型值,这样的方式是前32个位编码了一个ID,后32位编码了另一个ID。不幸的是,Java对长整型值的哈希只是将第一个32位与第二个32位进行异或运算,在我的情况下,这导致在使用HashMap时性能非常差。

看起来你在分配两个ID值和HashMap实例大小之间有一些不愉快的"共鸣"。

您是明确设置地图大小还是使用默认设置?QAD 检查似乎表明一个 HashMap<Long,String> 从一个 16 桶结构开始,并在溢出时加倍。这意味着 ID 值的低位比特位实际上只参与了哈希桶选择。您可以尝试使用一个带有初始大小参数的构造函数,并使用质数的初始大小创建您的地图。

另外,Dave L的建议是定义自己的长键哈希,这将使您避免低位依赖性问题。

另一种观察这个问题的方式是,你正在使用原始类型(长整型)来避免定义一个真正的类。我建议你考虑定义业务类所能带来的好处,然后在你自己的类上适当实现哈希编码、相等性和其他方法来管理这个问题。





相关问题
热门标签