Java 冲突解决方案:二次探测与单独链接

Java 冲突解决方案:二次探测与单独链接,java,algorithm,hash,performance,hashtable,Java,Algorithm,Hash,Performance,Hashtable,好的,我一直在用哈希表和不同的冲突解决问题做一些实验。我正试图找出哪一种查找更有效,一种使用单独链接或二次探测来解决冲突的哈希表。我的结果表明,即使对于较小的负载系数(如0.4或0.2),单独链接也比二次探测快。是这样还是我的结果错了 这两种方法在处理成本方面的差异是 (带链接) -间接寻址,即指针解引用 vs. (使用二次探测) -[简单但复合]算术公式的评估 -索引到新位置 -可能的重复(由于探测值和存储在这些位置的非目标值之间的冲突;链接不必担心某些问题) 因此,链接速度更快也就不足为奇了

好的,我一直在用哈希表和不同的冲突解决问题做一些实验。我正试图找出哪一种查找更有效,一种使用单独链接或二次探测来解决冲突的哈希表。我的结果表明,即使对于较小的负载系数(如0.4或0.2),单独链接也比二次探测快。是这样还是我的结果错了

这两种方法在处理成本方面的差异是
(带链接)
-间接寻址,即指针解引用
vs.
(使用二次探测)
-[简单但复合]算术公式的评估
-索引到新位置
-可能的重复(由于探测值和存储在这些位置的非目标值之间的冲突;链接不必担心某些问题)

因此,链接速度更快也就不足为奇了;指针解引用是大多数CPU的“本机”指令,具有可比性(在大多数情况下相同)将算术运算和可能的冲突留作开销,不利于探测。最简单的探测序列公式将需要一些CPU指令(初始化stepNr,通常是stepNr的一些移位,添加到当前位置/探测)这本身就比指针解引用慢好几倍。(可能的警告:请稍后参阅“编辑”,因为它讨论了链接如何导致更多CPU级缓存未命中,从而使其效率低于线性探测)

二次(或其他形式)链接的优点如下

  • 更简单的存储管理逻辑(无动态分配)
  • 更快的插入(因为存储更简单)
  • 总体上减少了存储需求
考虑到这种空间与速度(或插入时间与搜索时间)的折衷,链接的存储开销(主要针对指针本身,不考虑可能的堆管理开销)用于存储[探测的内容]“探测位置”的预先计算值。由于这些计算很容易完成,链接方法在搜索时速度更快。
编辑(谢谢,Ants Aasma)
对于这个论点[关于预先计算的位置]的一个警告是,在现代CPU及其缓存上,当缓存未命中时,运行小计算的成本可能远低于访问[数据]内存的成本。这表明按顺序探测(或更一般地说,具有探测功能,可产生物理上靠近碰撞点的位置)由于缓存未命中率较低,可能优于链接策略。因此,纯顺序探测方法是最好的探测函数,因为它的计算非常简单,但更重要的是,它最大限度地提高了缓存命中率。记住这一点,当哈希函数具有良好的分布和负载系数很小(因此在经过初始碰撞的短/局部搜索路径中)应尝试使用线性(或非常局部)探测方法;但应避免使用提供非物理局部搜索路径的探测功能


很难具体评论问题中提到的实验,例如不知道散列的大小(如果该大小与CPU中的字/寄存器的大小相匹配,则运算速度会更快),或者不知道冲突率(假设散列函数分布良好)。
在您不断尝试这一点时,收集一组单独的计时/统计数据,用于使用散列槽访问项目,而不是产生冲突的项目。

“…即使对于较小的负载系数…”中的“偶数”表示您的期望,即链接的相对优势应随着负载的增加而进一步增加,因此随着碰撞的增多。我也希望会出现这种情况。

此外,增加负载可能说明探测的另一个缺点:探测周期和/或更一般地说,没有空间容纳特定项目的情况。

在今天的硬件中,计算CPU周期不会产生任何有意义的结果。相关数字是缓存未命中的数量,即单个内存访问丢失缓存很容易需要几百个周期。这表明,如果哈希函数具有良好的分布,并且负载因子合理,则可以通过执行顺序探测来获得更好的性能。@John:我建议您也使用
纯线性探测函数来进行试验。
。如前所述,它将e重要的是,当负载系数处于中间状态时,探测方法的[hash]冲突率可能处于最佳状态:负载较低时,冲突太少,无法将链接置于显著劣势;负载过高时,[Probling]的数量碰撞开始损害与链接相关的探测方法。+1这值得不止一次投票,因为我不明白it@RobertG,哈哈……也许我会继续保持下去,比如迟钝的答案和通常花哨但有缺陷的曝光,这样人们就会支持我;-)说真的,论点相对简单(是的,可能概述得不好,这是我的一个常见问题);如果您有兴趣,我可以帮助您理解(或进一步混淆您……)对于小负载因素,二次探测提供了与线性探测相当的缓存性能(因为两者的单次碰撞行为相同),但在某些哈希值聚集的情况下,它不太可能陷入退化行为。