哈希函数简介
散列函数 h()
是一个任意函数,它将任意大小的数据 x ∈ X
映射到固定大小的 y ∈ Y
:y = h(x)
。好的哈希函数有以下限制:
-
散列函数表现得像均匀分布
-
哈希函数是确定性的。
h(x)
应始终为给定的x
返回相同的值 -
快速计算(有运行时
O(1)
)
一般情况下,散列函数的大小小于输入数据的大小:|y| < |x|
。散列函数不可逆,换句话说它可能是碰撞:∃ x1, x2 ∈ X, x1 ≠ x2:
h(x1) = h(x2)
。X
可以是有限集或无限集,Y
是有限集。
散列函数用于计算机科学的许多部分,例如软件工程,密码学,数据库,网络,机器学习等。有许多不同类型的散列函数,具有不同的域特定属性。
哈希值通常是整数值。用于哈希计算的程序语言中有特殊方法。例如,在 C#
GetHashCode()
方法中,所有类型都返回 Int32
值(32 位整数)。在 Java
每个类提供 hashCode()
方法返回 int
。每种数据类型都有自己或用户定义的实现。
哈希方法
确定散列函数有几种方法。不失一般性,让 x ∈ X = {z ∈ ℤ: z ≥ 0}
是正整数。通常 m
是素数(不太接近 2 的精确幂)。
方法 | 哈希函数 |
---|---|
除法 | h(x) = x mod m |
乘法 | h(x) = ⌊m (xA mod 1)⌋, A ∈ {z ∈ ℝ: 0 < z < 1} |
哈希表
散列表中使用的散列函数用于将索引计算到插槽数组中。哈希表是用于实现字典(键值结构)的数据结构。良好实现的哈希表有下一个操作的 O(1)
时间:按键插入,搜索和删除数据。多个密钥可以散列到同一个槽。解决碰撞有两种方法:
-
链接:链表用于存储槽中具有相同哈希值的元素
-
开放寻址:每个插槽中存储零个或一个元素
接下来的方法用于计算开放寻址所需的探测序列
方法 | 式 |
---|---|
线性探测 | h(x, i) = (h'(x) + i) mod m |
二次探测 | h(x, i) = (h'(x) + c1*i + c2*i^2) mod m |
双重哈希 | h(x, i) = ( h1(x) + i* h2(x)) mod m |
其中 i ∈ {0, 1, ..., m-1}
,h'(x),
h1(x), h2(x)
是辅助散列函数,c1, c2
是正辅助常数。
例子
让 x ∈ U{1, 1000}, h = x mod m
。下表显示了非素数和素数时的哈希值。粗体文本表示相同的哈希值。
X |
m = 100(不是素数) | m = 101(素数) |
---|---|---|
723 |
23 | 16 |
103 |
3 | 2 |
738 |
38 | 31 |
292 |
92 | 90 |
61 |
61 | 61 |
87 |
87 | 87 |
995 |
95 | 86 |
549 |
49 | 44 |
991 |
91 | 82 |
757 |
57 | 50 |
920 |
20 | 11 |
626 |
26 | 20 |
557 |
57 | 52 |
831 |
31 | 23 |
619 |
19 | 13 |
链接
-
Thomas H. Cormen,Charles E. Leiserson,Ronald L. Rivest,Clifford Stein。算法简介。