Language agnostic 为什么散列函数应该使用素数模?

Language agnostic 为什么散列函数应该使用素数模?,language-agnostic,data-structures,hash,Language Agnostic,Data Structures,Hash,很久以前,我以1.25美元从廉价桌上买了一本数据结构书。在这本书中,对散列函数的解释说,由于“数学的本质”,它最终应该被一个素数修改 你对一本1.25美元的书有什么期望 不管怎么说,我已经思考了数年数学的本质,但仍然无法理解它 当存在素数个存储桶时,数字的分布真的更均匀吗? 或者这是一个老程序员的故事,每个人都接受,因为其他人都接受它? 解释得很清楚,还有图片 编辑:作为一个总结,使用素数是因为当值乘以所选素数并将它们全部相加时,获得唯一值的机会最大。例如,给定一个字符串,将每个字母值与素数相乘

很久以前,我以1.25美元从廉价桌上买了一本数据结构书。在这本书中,对散列函数的解释说,由于“数学的本质”,它最终应该被一个素数修改

你对一本1.25美元的书有什么期望

不管怎么说,我已经思考了数年数学的本质,但仍然无法理解它

当存在素数个存储桶时,数字的分布真的更均匀吗?

或者这是一个老程序员的故事,每个人都接受,因为其他人都接受它?

解释得很清楚,还有图片

编辑:作为一个总结,使用素数是因为当值乘以所选素数并将它们全部相加时,获得唯一值的机会最大。例如,给定一个字符串,将每个字母值与素数相乘,然后将所有字母值相加,即可得到其哈希值

更好的问题是,为什么数字是31

素数是唯一的数。他们是 独一无二的是,一个素数的乘积 任何其他数字都是最好的 独一无二的机会(不是独一无二的 (当然是质数本身)由于 素数用于 写下来。此属性用于 散列函数

给定一个字符串“Samuel”,你可以 通过乘法生成唯一的散列 每个组成数字或 带素数和加法的字母 把他们弄起来。这就是为什么使用素数

然而,使用素数是一个古老的概念 技术。这里的关键是要理解 只要你能产生一个 您可以移动的唯一键 也适用于其他哈希技术。去 有关此主题的更多信息,请参见此处


为了提供另一种视角,这里有一个网站:


它主张使用尽可能多的存储桶,而不是将存储桶四舍五入到素数。这似乎是一个合理的可能性。直观地说,我当然可以看到更多的bucket会更好,但我无法对此进行数学论证。

这取决于哈希函数的选择

许多散列函数通过将数据中的各种元素与一些因子相乘来组合数据中的各种元素,这些因子是与机器字长对应的二次幂的模(通过让计算溢出,该模是免费的)

您不希望在数据元素的乘数和哈希表的大小之间有任何公共因子,因为这样一来,改变数据元素可能不会将数据分散到整个表中。如果您为表的大小选择一个素数,那么这样一个公共因子是不太可能的


另一方面,这些因素通常是由奇数素数组成的,因此您也应该安全地使用哈希表的二次幂(例如,Eclipse在生成Java hashCode()方法时使用31)。

通常,简单的哈希函数通过获取输入的“组成部分”(字符串中的字符)来工作,将它们乘以某个常数的幂,然后以某种整数类型将它们相加。例如,字符串的典型(尽管不是特别好)散列可能是:

(first char) + k * (second char) + k^2 * (third char) + ...
然后,如果输入一组具有相同第一个字符的字符串,那么结果都将是相同的模k,至少直到整数类型溢出为止

[举个例子,Java的字符串哈希代码与此非常相似-它的字符顺序相反,k=31。因此,在以相同方式结束的字符串之间可以得到模31的惊人关系,而在除结尾附近以外的相同字符串之间可以得到模2^32的惊人关系。这不会严重扰乱哈希表的行为。]

哈希表的工作原理是将哈希的模数取为桶数

在哈希表中,重要的是不要为可能的情况产生冲突,因为冲突会降低哈希表的效率

现在,假设有人将一大堆值放入一个哈希表中,这些值之间有一些关系,就像所有值都有相同的第一个字符一样。我想说,这是一种相当可预测的使用模式,所以我们不希望它产生太多冲突

事实证明,“由于数学的本质”,如果散列中使用的常数和桶的数量是相同的,那么在一些常见情况下冲突会最小化。如果它们不是,那么在输入之间存在一些相当简单的关系,对于这些关系,冲突不会最小化。所有的散列值都等于公因子的模,这意味着它们都将落入具有该值的桶中的1/n个桶中,该值等于公因子的模。你会得到n倍的碰撞,其中n是公因子。因为n至少是2,我认为对于一个相当简单的用例来说,产生至少两倍于正常情况的冲突是不可接受的。如果某个用户要将我们的发行版拆分为多个存储桶,我们希望这是一个反常的意外,而不是简单的可预测的使用

现在,哈希表实现显然无法控制放入其中的项。他们不能阻止他们成为亲戚。所以要做的是确保常数和桶计数是互质的。这样,您就不会仅依靠“最后一个”组件来确定铲斗相对于某个小公因数的模量。据我所知,实现这一目标不一定非要首要,只要互质

但是如果哈希函数和哈希表是独立编写的,那么哈希表就不知道哈希函数是如何工作的。它可能使用一个小因子的常数。如果幸运的话,它的工作方式可能会完全不同,并且是非线性的。如果散列足够好,那么任何桶计数都可以。但是偏执的哈希表不能假设一个好的哈希函数,所以应该使用素数个桶。类似地,一个p