【问题标题】:Are there any hash functions that allow you to resize the table without also rehashing (removing + reinserting) the contents?是否有任何散列函数允许您调整表的大小而无需重新散列(删除+重新插入)内容?
【发布时间】:2009-08-25 01:49:06
【问题描述】:

是否可以使用某种哈希函数和方法(除法或双重哈希)来制作一个可以调整大小的链式哈希表,而无需重新插入(重新哈希)表中已有的每个元素?

【问题讨论】:

  • 顺便说一句,我并不大惊小怪,但这里有一些具有出色算法经验的人可能会为您提供一种方法或类似的概念。
  • SO 允许您多次选中/取消选中答案。我总是选择出现的第一个好的答案,然后反复回来,选择提供的任何更好的答案。
  • 好吧,你可能会发现这不鼓励回复,但你当然可以随意运行它:)

标签: algorithm hash


【解决方案1】:

您仍然需要重新插入,但更便宜的方法是在应用模数之前存储哈希值。这样可以节省很大一部分rehashing的计算成本。

使用这种方法,也可以缩小表格的大小。

【讨论】:

  • 非常聪明,如果你的哈希算法很耗时
【解决方案2】:

我只能假设您想要避免重新散列所有内容的原因是,由此产生的高延迟操作不是吞吐量问题,而是响应性问题(无论是人为还是在 SLA 意义上)

理论上,您可以像这样使用修改后的封闭寻址哈希表:

  1. 记住之前添加元素的所有尺寸
  2. 在调整大小时,通过 sizeWhenUsed -> 存储桶的映射将旧存储桶保持在内部链接(显然,如果存储桶为空,则无需打扰)
  3. 密钥 k 的映射在任何时候都只存在于一个“内部哈希表”中。
  4. 在添加一个值时,您必须首先在所有其他映射中查找它,以确定该条目是否已经存在并被映射。如果它是从旧的删除它并将其添加到新的。
    • 如果内部映射变为空/低于特定大小,则应将其删除并将剩余元素移至当前哈希表中。
  5. 只要内部散列的数量保持不变,这不会及时影响数据结构的大 O 行为,尽管它会在内存中。
    • 但这会影响实际性能,因为必须进行 X 次额外检查,其中 X 是维护的旧哈希数。
    • 如果存储桶列表中浪费的空间(如果为空,存储桶本身将为空,因此除非填充,否则成本为零)变得很重要(为此使用一个软糖因子),那么在某个时候,您可能不得不在重新散列时采取除非您愿意消耗基本上无限的内存,否则将内容移到当前表中会很受打击。

如果您愿意重新散列,散列大小的降级将以所需的方式(释放内存)起作用。这是不可避免的。

您可以在开放式寻址方案中使用一些复杂的附加数据来“标记”该单元正在使用的内部哈希值,但是要正确删除将非常复杂并且非常昂贵,除非你只是把它们当作浪费的空间。我永远不会尝试这个。

我也不建议使用前一种方法,除非底层数据在散列中花费的时间很少,因此相关的流失会趋于稳定地“擦除”旧大小的散列。不过,针对这种行为进行调整并使用适当大小进行预设的哈希可能会表现得更好。

由于上述方案只是用浪费的内存和吞吐量来减少昂贵的操作,而投机性(充其量)可能会减少这种浪费,我建议只需将散列的大小预先设置为大于所需的大小,因此永远不会调整大小是一个更明智的选择。

【讨论】:

    【解决方案3】:

    可能不会 - 散列必须不使用任何类型的模数,这意味着无论如何它都将具有所需的表大小,具体取决于数据。

    【讨论】:

      【解决方案4】:

      所有哈希表都必须通过链接或探测或其他方式处理冲突,因此,我怀疑如果在调整表大小时您只是调整了表的大小(IE,您不会重新插入所有内容),您将有一个功能性但高度非最优的哈希表。

      【讨论】:

      • 在一般情况下更改表大小将更改项目哈希到的存储桶。因此,对散列表中的项目进行散列可能会返回不同的存储桶,并且您错误地得出该项目不在散列表中的结论。您必须在插入项目时存储每个项目的表大小,以便能够在重新散列该项目时重现该项目被散列到的存储桶。但是当你散列一个项目时,你怎么能找到这个值呢?您只能执行线性搜索。
      【解决方案5】:

      我假设您问这个问题是因为您想避免调整哈希表大小的高成本。您需要一个保证恒定时间的哈希表(当然,假设没有冲突问题)。这是可以做到的。

      诀窍是在当前哈希表填满时迭代地初始化下一个大小的哈希表。在您需要时,它已经准备好了。

      添加元素的快速伪代码:

      if resizing then
          smallTable = bigTable
          bigTable = new T[smallTable.length * 2] //if allocation zeroes memory, we lose O(1)
          set state to zeroing
      elseif zeroing then
          zero a small amount of the bigTable memory
          if done zeroing then set state to transfering
      elseif transfering then
          transfer a few values in the small table to the big table
          if done transfering then set state to resizing
      end if
      
      add new item to small array
      add new item to large array
      

      【讨论】:

        猜你喜欢
        • 2022-01-26
        • 2020-03-06
        • 2015-01-28
        • 1970-01-01
        • 1970-01-01
        • 2020-08-27
        • 2015-09-14
        • 2016-04-14
        • 1970-01-01
        相关资源
        最近更新 更多