Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/algorithm/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Algorithm 为什么哈希表扩展通常是通过加倍大小来完成的?_Algorithm_Data Structures_Hash_Hashtable - Fatal编程技术网

Algorithm 为什么哈希表扩展通常是通过加倍大小来完成的?

Algorithm 为什么哈希表扩展通常是通过加倍大小来完成的?,algorithm,data-structures,hash,hashtable,Algorithm,Data Structures,Hash,Hashtable,我已经对哈希表做了一些研究,我一直遵循经验法则,当有一定数量的条目(最大或通过75%的加载因子)时,哈希表应该扩展 几乎总是建议将哈希表的大小加倍(或加倍加1,即2n+1)。然而,我还没有找到一个好的理由 为什么要将大小加倍,而不是,比方说,将其增加25%,或者将其增加到下一个素数或下一个k个素数(例如,3)的大小 我已经知道,选择一个初始哈希表大小是一个好主意,它是一个素数,至少如果您的哈希函数使用模数,如通用哈希。我知道这就是为什么通常建议做2n+1而不是2n(例如) 然而,正如我所说,我还

我已经对哈希表做了一些研究,我一直遵循经验法则,当有一定数量的条目(最大或通过75%的加载因子)时,哈希表应该扩展

几乎总是建议将哈希表的大小加倍(或加倍加1,即2n+1)。然而,我还没有找到一个好的理由

为什么要将大小加倍,而不是,比方说,将其增加25%,或者将其增加到下一个素数或下一个k个素数(例如,3)的大小

我已经知道,选择一个初始哈希表大小是一个好主意,它是一个素数,至少如果您的哈希函数使用模数,如通用哈希。我知道这就是为什么通常建议做2n+1而不是2n(例如)

然而,正如我所说,我还没有看到任何真正的解释来解释为什么加倍或加倍加一实际上是一个好的选择,而不是为新哈希表选择大小的其他方法


(是的,我读过维基百科关于哈希表的文章:)

在扩展任何类型的集合时,将内存加倍是一种常用的策略,可以防止内存碎片,并且不必经常重新分配。正如您所指出的,可能有理由使用素数的元素。了解应用程序和数据后,您还可以预测元素数量的增长,从而选择另一个(更大或更小)的增长因子,而不是加倍


在库中找到的一般实现就是:一般实现。他们必须专注于在各种不同的情况下成为一个合理的选择。在了解上下文的情况下,几乎总是可以编写更专业、更高效的实现。

哈希表不能声明“摊销常量时间插入”,例如,如果调整大小是通过常量增量进行的。在这种情况下,调整大小的成本(随着哈希表的大小而增加)将使一次插入的成本与要插入的元素总数成线性关系。由于调整大小随着表的大小而变得越来越昂贵,因此必须“越来越少地”进行,以保持插入的摊余成本不变

大多数实现都允许平均存储桶占用增长到一个在调整大小之前预先固定的范围(0.5到3之间的任意值,这都是可以接受的值)。按照此约定,在调整大小之后,平均存储桶占用率将变为限制值的一半。通过加倍调整大小,将平均存储桶占用保持在宽度为*2的频带内


子注:由于统计聚类,如果希望多个存储桶最多包含一个元素(忽略缓存大小的复杂影响的最大查找速度),则平均存储桶占用率必须低至0.5;如果希望最小数量的空存储桶(对应于浪费的空间),则平均存储桶占用率必须高达3.

如果您不知道最终将使用多少对象(比如N),
通过将空间加倍,最多可以进行log2N重新分配

我假设如果你选择一个合适的首字母“n”,你会增加几率

2*n+1将在随后的重新分配中产生素数。

同样的推理适用于将vector/ArrayList实现的大小加倍,请参见。

我在这个网站上读到了一篇关于增长策略的非常有趣的讨论。。。只是再也找不到了

虽然
2
是常用的,但已经证明它不是最佳值。一个经常被引用的问题是,它不能很好地处理分配器方案(通常分配两个块的功率),因为它总是需要重新分配,而较小的数量实际上可能被重新分配到同一块中(模拟就地增长),因此速度更快

因此,例如,
VC++
标准库在对邮件列表进行了广泛讨论之后,使用了
1.5
的增长因子(如果使用first fit内存分配策略,理想情况下应该是黄金数)。理由如下:

我很感兴趣的是,是否有任何其他向量实现使用了除2以外的增长因子,我还想知道VC7使用的是1.5还是2(因为我这里没有编译器)

选择1.5比2有一个技术原因——更具体地说,选择小于
1+sqrt(5)/2的值

假设您正在使用first fit内存分配器,并且您正在逐步附加到一个向量。然后每次重新分配时,都会分配新内存,复制元素,然后释放旧内存。这就留下了一个缺口,最终能够使用这个内存将是一件好事。如果向量增长过快,它对于可用内存来说总是太大

结果表明,如果生长因子
=1+sqrt(5)/2
,那么新内存对于留下的洞来说总是太大;如果它是
<1+sqrt(5)/2
,则新内存最终将适合。因此1.5足够小,可以回收内存

当然,如果增长因子
=2
,那么新内存对于迄今为止留下的漏洞来说总是太大;如果是
<2
,新内存最终将适合。大概是因为

  • 初始分配为
    s
  • 第一次调整大小是
    k*s

  • 第二个调整大小是
    k*k*s
    ,它将适合iff
    k*k*s的孔。将哈希容器的大小加倍的一个原因是,如果容器容量始终是2的幂,则不使用通用模将哈希转换为偏移量,通过位移位可以获得相同的结果。模运算是一种缓慢的运算