先决条件:散列 |第一套(介绍)
什么是哈希函数?
给定大的电话号码转换为实用小整型值的函数。映射的整数值用作哈希表中的索引。简单来说,哈希函数将一个大数字或字符串映射到一个小整数,作为哈希表中的索引。
好的散列函数是什么意思?
一个好的散列函数应该具有以下特性:
- 有效计算。
- 应该均匀分布键(每个表位置每个键的可能性相同)
例如:对于电话号码,一个不好的哈希函数是取前三位数字。更好的函数被认为是最后三位数字。请注意,这可能不是最好的散列函数。可能有更好的方法。
在实践中,我们经常可以使用启发式技术来创建一个性能良好的哈希函数。在此设计过程中,有关密钥分布的定性信息可能很有用。通常,散列函数应该依赖于密钥的每一位,因此两个密钥仅在一位或一组位上不同(无论该组是在密钥的开头、结尾还是中间或存在于整个键中)散列成不同的值。因此,简单地提取密钥的一部分的散列函数是不合适的。同样,如果两个键只是简单地数字化或彼此的字符排列(例如 139 和 319) ,它们也应该散列成不同的值。
两种启发式方法是除法散列和乘法散列,如下所示:
- 模组方法:
- 在这种创建哈希函数的方法中,我们通过将键的余数除以 table_size 将键映射到表的一个槽中。也就是说,哈希函数是
h(key) = key mod table_size
i.e. key % table_size
- 由于它只需要一次除法运算,因此除法散列非常快。
- 在使用除法方法时,我们通常会避免 table_size 的某些值,例如 table_size 不应是数字的幂,假设r ,因为如果table_size = r^p ,则 h(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乘以范围0 < c < 1内的常数实数c并提取k * c的小数部分。
- 然后我们将此值乘以 table_size m并取结果的下限。它可以表示为
h(k) = floor (m * (k * c mod 1))
or
h(k) = floor (m * frac (k * c))
- 其中标准库math.h 中提供的函数floor(x)产生实数 x 的整数部分,而frac(x)产生小数部分。 [压裂(x) = x – 地板(x)]
- 乘法方法的一个优点是,m的值不是关键的,我们通常选择它是2的(M = 2 p表示一些整数p)的功率,因为我们可以很容易实现在大多数计算机的函数
- 假设机器的字长为w位,并且该密钥适合单个字。
- 我们将c限制为s / (2 w )形式的分数,其中 s 是0 < s < 2 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形式的分数。
- 然后key * s = 327706022297664 = (76300 * 2^32) + 17612864,
- 所以r1 = 76300 和r0 = 176122864。
- r0 的 14 个最高有效位产生值h(key) = 67。
如果您希望与专家一起参加现场课程,请参阅DSA 现场工作专业课程和学生竞争性编程现场课程。