先决条件:哈希|设置1(简介)
什么是哈希函数?
给定大的电话号码转换为实用小整型值的函数。映射的整数值用作哈希表中的索引。简而言之,哈希函数将一个较大的数字或字符串映射为一个较小的整数,可用作哈希表中的索引。
良好的哈希函数是什么意思?
一个好的哈希函数应具有以下属性:
- 高效可计算。
- 应该统一分配密钥(每个密钥在每个表中的位置均等)
例如:对于电话号码,无效的哈希函数是采用前三位数字。更好的函数被认为是后三位数字。请注意,这可能不是最好的哈希函数。可能有更好的方法。
在实践中,我们经常可以使用启发式技术来创建性能良好的哈希函数。有关密钥分配的定性信息在此设计过程中可能会很有用。通常,哈希函数应取决于键的每个单个位,以便两个键的不同之处仅是一位或一组位(无论该组是在键的开始,结尾还是中间),或者存在于整个键中)散列为不同的值。因此,仅提取键的一部分的哈希函数是不合适的。类似地,如果两个键只是简单地数字或彼此的字符排列(例如139和319) ,则它们也应散列为不同的值。
两种启发式方法是按除法散列和乘以散列哈希,如下所示:
- mod方法:
- 在这种创建哈希函数的方法中,我们通过将键的其余部分除以table_size来将键映射到表的插槽之一中。也就是说,哈希函数是
h(key) = key mod table_size
i.e. key % table_size
- 由于只需要一个除法运算,因此除法散列就相当快。
- 当使用除法时,我们通常避免table_size的某些值(例如table_size)不应该是r的幂,因为如果table_size = r ^ p ,则h(key)只是key的p个最低位。除非我们知道所有低阶p位模式的可能性均等,否则最好将散列函数设计为依赖于密钥的所有位。
- 已经发现,当表的大小为素数时,使用除法的最佳效果。但是,即使table_size是质数,也需要附加限制。如果r是计算机上可能的字符代码数,并且table_size是使r%table_size等于1的质数,则哈希函数h(key)= key%table_size就是以下字符中二进制表示形式的总和密钥mod table_size。
- 假设r = 256并且table_size = 17,其中r%table_size即256%17 = 1。
- 因此对于key = 37599 ,其哈希为
37599 % 17 = 12
- 但是对于key = 573 ,其哈希函数也为
573 % 17 = 12
- 因此可以看出,通过该哈希函数,许多键可以具有相同的哈希。这称为碰撞。
- 不太接近2的幂的素数通常是table_size的不错选择。
- 乘法方法:
- 在乘法方法中,我们将密钥k与范围为<
的常数实数c相乘,并提取k * c的小数部分。 - 然后,我们将此值乘以table_size m并得出结果的下限。它可以表示为
- 在乘法方法中,我们将密钥k与范围为<
h(k) = floor (m * (k * c mod 1))
or
h(k) = floor (m * frac (k * c))
- 在标准库math.h中可用的floor(x)函数产生实数x的整数部分,而frac(x)得到分数部分。 [frac(x)= x – floor(x)]
- 乘法方法的一个优点是,m的值不是关键的,我们通常选择它是2的(M = 2 p表示一些整数p)的功率,因为我们可以很容易实现在大多数计算机的函数
- 假设机器的字大小为w位,并且该键适合单个字。
- 我们将c限制为s /(2 w )形式的分数,其中s是0
w范围内的整数。 - 参照图,我们首先将密钥乘以w位整数s = c * 2 w 。结果是2w位值
r1 * 2w + r0
where r1 = high-order word of the product
r0 = lower order word of the product
- 尽管此方法适用于常数c的任何值,但某些值比其他值更好。
c ~ (sqrt (5) – 1) / 2 = 0.618033988 . . .
- 可能会运作良好。
- 假设k = 123456, p = 14
- m = 2 ^ 14 = 16384,而w = 32。
- 根据Knuth的建议, c为s / 2 ^ 32形式的分数。
- 然后键* s = 327706022297664 =(76300 * 2 ^ 32)+ 17612864,
- 因此r1 = 76300和r0 = 176122864。
- r0的14个最高有效位产生值h(key)= 67。