在哈希表中,除留取余法是一种常用的哈希函数构造方法,形式为 \(h(key) = key \bmod p\),其中 p 是一个正整数,通常取为质数。以下是取 p 为质数(一般是小于等于哈希表大小 m 的最大质数)比取其他数更优的证明和原因:
-
避免哈希值的聚集:
- 当 p 不是质数时,可能会导致哈希值分布不均匀,出现哈希值聚集的情况。例如,如果 \(p = ab\)(\(a,b > 1\)),那么对于 \(key = k_1a\) 和 \(key = k_2a\)(\(k_1 \neq k_2\)),有 \(h(k_1a)=(k_1a)\bmod(ab)\) 和 \(h(k_2a)=(k_2a)\bmod(ab)\)。在这种情况下,这些 key 对应的哈希值可能会有一定的规律,使得它们集中在某些值上,从而增加冲突的概率。
- 而当 p 是质数时,从数论的角度看,对于任意的 key,\(key \bmod p\) 的结果会更加均匀地分布在 \(\{0, 1, \cdots, p - 1\}\) 这个范围内。因为质数 p 除了 1 和它本身外没有其他因数,不会出现因为 key 的某些因数与 p 的因数之间的特殊关系而导致哈希值聚集的情况。
-
减少冲突概率:
- 设哈希表的大小为 m,取 p 为小于等于 m 的最大质数。这样做的好处是可以使得哈希函数尽可能地利用哈希表的空间,减少冲突。
- 假设我们有一组关键字 \(key_1, key_2, \cdots\),如果 p 与关键字的某些常见模式(例如关键字可能是某个数的倍数等)没有特殊的关系(这正是质数的特性),那么不同关键字映射到相同哈希值(冲突)的概率就会降低。
- 例如,在实际应用中,若关键字是整数序列,若 p 取合数,比如 \(p = 4\),那么所有偶数 key 的 \(key \bmod 4\) 的结果可能只在 0 和 2 之间取值,而奇数 key 的结果在 1 和 3 之间取值,这就容易导致冲突。而当 p 是质数时,这种规律性的冲突情况就会大大减少。
-
数学证明:
- 从数学角度严格证明,对于任意整数 a 和质数 p,根据同余定理,\(a \bmod p\) 的结果是唯一确定的,且在 \([0, p - 1]\) 范围内。对于不同的 \(a_1\) 和 \(a_2\),如果 \(a_1 \neq a_2\),那么 \(a_1 \bmod p = a_2 \bmod p\) 的概率相对较小。
- 假设 \(a_1 = k_1p + r_1\),\(a_2 = k_2p + r_2\),其中 \(0\leq r_1,r_2 < p\)。若 \(a_1 \bmod p = a_2 \bmod p\),则 \(r_1 = r_2\),即 \(a_1 - a_2=(k_1 - k_2)p\)。当 p 是质数时,要使 \(a_1 \bmod p = a_2 \bmod p\) 成立,\(a_1\) 和 \(a_2\) 之间的差值必须是 p 的整数倍。而对于合数 p,存在更多的非零整数 \(a_1\) 和 \(a_2\),它们的差值不是 p 本身,但却能使得 \(a_1 \bmod p = a_2 \bmod p\),从而增加了冲突的可能性。
综上所述,在除留取余法中取 p 为小于等于哈希表大小 m 的最大质数,可以使哈希函数产生的哈希值更均匀地分布,减少冲突的发生,提高哈希表的性能 。
- 首先明确同余运算的定义:
- 对于整数 m 和 n,以及正整数 p,\(m\bmod p = r\),其中 \(m = qp + r\),\(0\leq r\lt p\),q 为整数。
- 已知 \(p = ab\)(\(a,b\gt1\)),对于 \(key_1 = k_1a\) 和 \(key_2 = k_2a\)(\(k_1\neq k_2\)),计算 \(h(k_1a)=(k_1a)\bmod(ab)\) 和 \(h(k_2a)=(k_2a)\bmod(ab)\)。
- 根据除法运算,将 \(k_1a\) 除以 ab,使用带余除法 \(k_1a = q_1(ab)+r_1\),其中 \(0\leq r_1\lt ab\),则 \(h(k_1a)=r_1\);同理,\(k_2a = q_2(ab)+r_2\),其中 \(0\leq r_2\lt ab\),则 \(h(k_2a)=r_2\)。
- 然后进行进一步的分析:
- 因为 \(k_1a\) 和 \(k_2a\) 都含有因数 a,且除数是 ab。设 \(k_1 = m_1b + s_1\),\(k_2 = m_2b + s_2\),其中 \(0\leq s_1,s_2\lt b\)。
- 那么 \(k_1a=(m_1b + s_1)a=m_1ab + s_1a\),所以 \((k_1a)\bmod(ab)=s_1a\)(因为 \(m_1ab\) 是 ab 的倍数,在求余运算中被舍去)。
- 同理,\(k_2a=(m_2b + s_2)a=m_2ab + s_2a\),所以 \((k_2a)\bmod(ab)=s_2a\)。
- 这里 \(s_1\) 和 \(s_2\) 的取值范围是 \(\{0,1,\cdots,b - 1\}\),所以 \(s_1a\) 和 \(s_2a\) 的取值只能是 a 的倍数且小于 ab,即 \(s_1a\in\{0,a,2a,\cdots,(b - 1)a\}\),\(s_2a\in\{0,a,2a,\cdots,(b - 1)a\}\)。
- 也就是说,对于所有形如 ka(k 为整数)的关键字,它们通过哈希函数 \(h(key)=key\bmod(ab)\) 得到的哈希值只能是 a 的倍数且小于 ab,这些哈希值集中在 b 个值 \(\{0,a,2a,\cdots,(b - 1)a\}\) 上。
- 例如,若 \(a = 2\),\(b = 3\),则 \(p = ab = 6\)。对于 \(key_1=k_1\times2\) 和 \(key_2 = k_2\times2\),\(h(k_1\times2)=(k_1\times2)\bmod6\) 的值只能是 \(0,2,4\)(当 \(k_1 = 0\) 时,\((0\times2)\bmod6 = 0\);当 \(k_1 = 1\) 时,\((1\times2)\bmod6 = 2\);当 \(k_1 = 2\) 时,\((2\times2)\bmod6 = 4\);当 \(k_1 = 3\) 时,\((3\times2)\bmod6 = 0\) 等)。
- 最后对比均匀分布的情况:
- 一个理想的哈希函数应该使哈希值均匀分布在 \(\{0,1,\cdots,ab - 1\}\) 这 ab 个值上。
- 但在上述情况中,对于形如 ka 的关键字,哈希值只分布在 b 个值上(\(b\lt ab\)),这就导致了哈希值分布不均匀,出现了哈希值聚集的情况。
- 因为哈希值聚集,当我们插入更多的关键字时,这些关键字的哈希值更容易相同,从而增加了冲突的概率。
所以,当 p 不是质数(表示为 \(p = ab\),\(a,b\gt1\))时,对于特定形式的关键字(如 ka),会导致哈希值分布不均匀,增加冲突的可能性。