使用不相等的哈希线性探测巨大的键序列

时间:2018-12-15 21:06:33

标签: algorithm performance hash hashtable linear-probing

关于线性探测(哈希表),有一件事对我来说不直观。 如果我将散列结果的key1放到数组索引1中,然后放key2->数组索引2。然后再放key3->再次是数组索引1,这将转到数组索引3。 然后,当我搜索key3时,我应该遍历索引,这些索引包含与我的散列完全不同的散列。这不是浪费吗?如果序列确实很大并且包含许多键(例如,我有20个元素,则为null,对于任何导致数组索引从0到20的键,我都必须遍历所有索引,尽管它们的散列与我的散列不同我可以通过单独的链接消除这种情况。

或者我们的哈希函数(如果写得足够好)会在索引之间平均分配键,并不断将数组的大小调整为最大半满,这可以缓解这种情况吗?

2 个答案:

答案 0 :(得分:1)

当有很多冲突时,线性探测是次优的。请注意,冲突次数不仅取决于哈希,而且还取决于表中的插槽数(通常是质数),因为索引是哈希除以表长度的整数除法的余数。 / p>

但是请注意,除了将碰撞键彼此相邻之外,还可能会利用CPU缓存,这将使RAM中的许多元素一次读取。因此,(原则上)不要认为检查20个探针所花费的时间是检查一个探针所花费的时间的20倍,因为CPU内部及其缓存中发生的事情比进入RAM快得多。虽然没有魔术。如果每次比较的计算都丢弃了缓存中的内容,那么部分节省的资源将丢失。

答案 1 :(得分:0)

您发现的问题确实是可能影响线性探测性能的问题。当您尝试查找某些元素时,您可能必须与初始哈希探针的起始位置相去甚远才能找到您的元素。

也就是说,线性探测在实践中非常快,这主要是由于参考位置的限制。在内存中查找内容的成本并不一致-如果在最近读过的内容附近查找地址,则可能是内存区域已被拉入缓存,查找内容的成本极低。结果,实际上这些探针的成本通常比您自然预期的要少,因为这些探针可能很快。

但是,这并不意味着您可以忽略这一事实。有很多问题需要引起注意。首先,随着表的负载系数的增加,出现击中其他元素的成本开始使得查找所花费的时间越来越长。通常,您会看到人们以大约75%的负载系数重新哈希到更大的表中。其次,您需要具有一个很好的哈希函数,因为如果您使用的是低质量的哈希,它将大量元素放入相似的位置,则由于上述原因,您将获得非常糟糕的性能。

您可以使用两种技术来减轻这种情况。 Robin Hood散列的工作原理是将元素放下后在元素周围移动,以使距离更近的元素被推得更远,从而为距离更近的元素腾出空间。这使得查找的平均成本要高一些,但是会大大降低查找的最坏情况的成本(换句话说,它减少了查找成本的方差,以换取该查找成本的期望值)。跳房子散列的工作方式是设置元素可以移动的最大距离,并保留一个位掩码,以指示附近的哪些元素可能是匹配项,从而减少了查找事物所需的工作量。新的Google flat_map从线性探测开始,并使用了非常聪明的哈希和并行内存操作来使查找变得非常快。