【问题标题】:How many hash buckets多少个哈希桶
【发布时间】:2010-09-18 13:04:35
【问题描述】:

如果我注意到哈希表(或基于哈希表构建的任何其他数据结构)正在填满,那么您应该在什么时候构建一个包含更多存储桶的新表。到目前为止,给定表中的 n 个项目,您如何计算在新的项目中要使用多少个桶?

假设我有 100 个存储桶。当里面有 50 个项目时,我应该重新组织它吗? 500? 5000?还是我应该寻找最满的桶和钥匙?那么当我达到这一点时,我要制作多大的新哈希表?

与此相关,如果你事先知道大概有多少项会进入,有没有办法计算桶的数量以获得良好的平均性能?

我知道真正的答案取决于许多其他考虑因素,例如在特定示例中速度与大小的重要性,但我正在寻找一般准则。

我也知道我不应该优化这类事情,除非良好的分析表明这是一个瓶颈。我只是在考虑一个会使用大量哈希表的项目,并且想知道如何解决这个问题。

【问题讨论】:

    标签: optimization hashtable


    【解决方案1】:

    一个好的经验法则(并不总是理想的,嗯,只是一个经验法则)是在哈希表填充到 80% 时重新散列。这意味着如果你有 100 个桶和 80 件物品,无论你之前有多少碰撞,都需要时间来增加容量。

    你应该增加多少?好吧,也没有完美的价值。最简单的解决方案是每增加一倍容量。所以它会达到 200、400、800 等等。如果您认为这太多了(毕竟当哈希表变得非常大并且您可能永远无法填满 16 MB 时,它会从 8 MB 内存跃升至 16 MB),请选择较小的增长因子。建议至少 1/3(从 100 增长到 133)我会说,作为妥协,也许让它每次增长 50%。

    请注意,这一切还取决于如何处理冲突。处理它们的一种简单方法(我个人最喜欢的)是在发生碰撞时将项目存储在链表中。如果将 3 个项目放在同一个键上,则仍然最多只有 3 次比较才能找到它。由于链表对搜索非常无效,您可能希望更早地增加容量,例如如果使用 60% 的容量来保持哈希表的速度。 OTOH,您可以做一些更复杂的事情并保留有关碰撞次数的统计信息。只要你几乎没有任何冲突(如果你有一个非常好的散列函数),就根本不需要重新散列,即使它的 99% 的容量都在使用中。此外,如果您以复杂的方式处理冲突(例如,每个节点又是一个排序表,并且您可以在这些表中执行二进制搜索),如果表加载到 200%,您的查找可能仍然足够快(因此您有两倍的项目作为容量)。在这种情况下,您可以统计最大的排序表有多大,当它变得大于 8 个条目时,您认为这太慢了,然后您重新散列。

    重新散列非常慢,因此应尽可能避免。因此,如果您需要重新散列,不要只是将容量增长得太少,否则在添加更多项目时您必须很快再次重新散列。所以当你需要重新散列时,让容量明显大于当前表中的项目数,其他的都是容量太少。

    【讨论】:

      【解决方案2】:

      一般来说,你要注意负载因子(非正式地,你已经说过了),它被正式定义为 α = n / N,即使用的比率到总桶数。为了使哈希表正常运行(或至少从数学角度推断其性能),它应该是 α

      其他一切都取决于经验测试:如果您发现哈希表从 α > 0.5 开始表现不佳,请确保保持在该值以下。该值还取决于您的冲突解决技术。使用链式散列可能需要其他负载因子,而不是使用开放寻址的散列。另一个因素是缓存位置。如果您的表变得太大,它将不适合主内存。由于您对数组的访问是随机的,因此从缓存加载可能会成为瓶颈。

      【讨论】:

        【解决方案3】:

        通常有两种类型的哈希表:开放式和封闭式。

        在一个开放的哈希表中,您可以根据哈希找到正确的存储桶,然后构建一个挂在该存储桶上的项目列表。

        在一个封闭的哈希表中,您可以使用哈希值找到初始存储桶,如果它被占用,您将探测下一个值。在简单的情况下,您可以通过查找下一个空闲存储桶来执行此操作,或者您可以从您的项目中创建第二个哈希值并逐步进行(尽管您必须确保这是以哈希表大小为模的素数,因此您将访问所有桶)。

        打开的哈希表通常不会调整大小。您将初始大小设置为您认为对问题合理的大小。正如其他人指出的那样,您可以在一个开放的哈希表上调整大小,但是现在推理这个数据结构的性能变得非常困难。如果您在给定存储桶的长度为 L 时调整大小,那么您可能最终只调整整个哈希表中 L 个项目的大小,这是非常低效的。

        当负载因子(哈希表中的项目数/存储桶数)达到某个预定义值时,将调整关闭的哈希表的大小。我倾向于使用 80%,但确切的值不太可能太关键。

        封闭哈希表的好处是插入项目的摊销成本始终为 O(1)(假设哈希函数良好)。由于调整大小的成本,插入特定项目可能是 O(N),但这种情况很少发生。

        【讨论】:

        • 在打开的哈希表中插入不是也 O(1) 摊销吗?
        【解决方案4】:

        取决于您正在构建的哈希表的类型。如果您使用基于固定数组的哈希表(而不是存储桶的链表),则应在表已满或达到最大探测计数时调整数组大小(取决于您是否更关心速度或记忆)。如果您使用的是链表,那么内存就不是那么重要了,因为您不必探测空白空间,因此调整大小并不是什么大问题。

        哈希表的关键是哈希算法,而不是桶的数量。理想情况下,您总是希望每个存储桶中最多有一个项目,因此理想情况下,您应该在哈希表中的项目数 = 存储桶数时调整大小。如果您的数据分布不均匀,则最好使用更好的哈希算法而不是更好的调整大小策略。

        【讨论】:

          【解决方案5】:

          如果您使用线性哈希,表格本身会通过保持恒定的负载因子来自动调整大小。

          【讨论】:

            猜你喜欢
            • 2014-08-13
            • 2016-11-02
            • 1970-01-01
            • 1970-01-01
            • 2011-08-22
            • 1970-01-01
            • 2017-04-24
            • 1970-01-01
            • 2016-10-08
            相关资源
            最近更新 更多