【问题标题】:Why is there no overflow check in the Jenkins one-at-a-time hash function?为什么在 Jenkins 一次一个哈希函数中没有溢出检查?
【发布时间】:2022-01-17 02:58:09
【问题描述】:
uint32_t jenkins_one_at_a_time_hash(const uint8_t* key, size_t length) {
  size_t i = 0;
  uint32_t hash = 0;
  while (i != length) {
    hash += key[i++];
    hash += hash << 10;
    hash ^= hash >> 6;
  }
  hash += hash << 3;
  hash ^= hash >> 11;
  hash += hash << 15;
  return hash;
}

来源:https://en.wikipedia.org/wiki/Jenkins_hash_function

hash&lt;&lt;10 添加到hash 时,如果字符串长度很大,则可能会溢出。当使用多项式散列函数对字符串进行散列时,我们在每次迭代中取mod p 值(puint32_t 范围内的一个大素数),并返回hash % hash_table_length。但是在这个算法中,我们为什么不采取hash = (hash + (hash&lt;&lt;10)) % p来防止溢出呢?我们如何确保返回的hash 值保持在哈希表长度范围内?

【问题讨论】:

    标签: string algorithm hash overflow mod


    【解决方案1】:

    一次一个哈希被设计成将一串字节作为输入,然后输出一个(大致)均匀分布的 32 位值。重要的是,这意味着如果您想使用散列函数将键分配到散列表中,则需要执行一些辅助数学运算以将 32 位散列转换为表索引,因为散列函数本身不是设计的将其输出限制为较少数量的插槽。因此,例如,如果您想使用它来分配密钥,您可以通过表槽数修改结果。

    上一段中的推理本质上归结为“正如最初所写的那样,哈希函数不会尝试适应少量的插槽。”但这里还有一个更深层次的问题:为什么散列函数没有为处理整数溢出做出任何规定,为什么它不在每一步都用一个素数修改其内部值?这与指导哈希函数的设计原则有关。 an article about building families of hash functions with certain desirable properties using invertible transformations 中描述了一次一个哈希。这个想法是通过增量应用将现有散列值和新字节组合在一起的可逆变换来构建长字节流的散列。这样做有一个有用的属性,如果你已经散列字节流 X 和 Y 并且没有发生冲突,那么如果你将相同的字节序列附加到 X 和 Y 上,那么就没有发生冲突的机会。具体操作在散列are indeed invertible 中使用,但是关于为什么是微妙的理由并且依赖于添加移位并允许计算溢出对应于乘以模232 可逆值的事实。从这个意义上说,这里故意没有 mods,因为通过溢出计算的隐式模执行“正确”的模以使所有步骤都可逆。

    这与多项式散列以一些素数为模形成对比。您是对的,在使用多项式哈希策略时,您需要注意整数溢出,因为您特别希望这些隐式 mod 启动 - 毕竟,您正在通过不同的 mod数字!但这对于特定的散列策略来说是特别的。有很多不同的方法可以构建散列函数,每种方法都使用不同的方法。

    【讨论】:

    • 所以,据我了解,多项式哈希是一种不同的策略,它特别要求其内部值被一个素数模数以提供良好的分布。 Jenkins 的一次一次是另一种策略,它不需要任何内部 mod 操作(可能会发生溢出),但最后我总是可以通过表槽数来修改最终哈希,并最终得到一个良好的分布。我说的对吗?
    • 是的!听起来不错。
    猜你喜欢
    • 2019-05-01
    • 1970-01-01
    • 2021-03-02
    • 2011-03-05
    • 2011-08-03
    • 1970-01-01
    • 2011-06-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多