Archipelago

Lec 1全域哈希与完美哈希

讲义完成更新于 2026-09-17
承接

这是当前课程已收录的第一篇。

Lec 1 全域哈希与完美哈希(Universal & Perfect Hashing)

TL;DR

  • 全域哈希的随机化保证:通过从 2-全域哈希族 H\mathcal{H} 中随机选取哈希函数,保证对全域中任意两个不同元素发生碰撞的概率不超过 1/n1/n,摆脱对输入分布的依赖。
  • 两级哈希(FKS 结构)实现最坏 O(1)O(1) 查找:外层哈希将 mm 个元素分入 n=mn=m 个桶;内层对包含 SiS_i 个元素的桶分配 Si2S_i^2 大小的二级哈希表,总空间期望为 O(m)O(m)。
  • 空间与碰撞的期望平衡:结合马尔可夫不等式,第一层总二级空间 ∑Si2≤4m\sum S_i^2 \le 4m 且第二层各桶完全无碰撞的概率均大于 1/21/2,可通过拉斯维加斯算法在线性期望时间内完成构造。

字典查找与负载均衡

字典查找问题与哈希碰撞挑战

字典查找(Dictionary Problem)与负载均衡(Load Balancing)是算法理论与系统设计的基石。在理想情况下,我们希望在 O(1)O(1) 时间内维护一个动态或静态集合:

  • 插入(Insert(x)):将元素 xx 加入集合 SS
  • 删除(Delete(x)):从集合 SS 中移除元素 xx
  • 查询(Query(x)):检查 xx 是否在集合 SS 中

假设全域为 UU(例如所有 IPv6 地址,大小可达 21282^{128}),而实际存储的子集 S⊆US \subseteq U 规模为 ∣S∣=m≪∣U∣|S| = m \ll |U|。我们希望用一个大小为 nn(通常 n=O(m)n = O(m))的数组来存储。哈希函数 h:U→[n]h: U \to [n](其中 [n]={0,1,…,n−1}[n] = \{0, 1, \dots, n-1\})负责将全域元素映射到槽位中。

根据鸽巢原理,只要 ∣U∣>n|U| > n,就必然存在 x≠yx \ne y 满足 h(x)=h(y)h(x) = h(y),即发生碰撞(Collision)。在链式哈希(Hashing with Chaining)中,碰撞元素被挂接在同一个槽位的链表上。若哈希函数固定,恶意对手总能构造出全部映射到同一槽位的 mm 个元素,导致查询退化为单链表遍历的 O(m)O(m)。

完全真随机的哈希函数需要存储每个元素的映射,占用 ∣U∣log⁡n|U| \log n 比特空间,这在存储开销上是不可接受的。因此,核心问题在于:如何用极小的紧凑描述,构造一个在任意给定输入集合上都能表现得“足够随机”的哈希函数族?

2-全域哈希族(2-Universal Hashing)

形式化定义

Carter 与 Wegman(1979)提出了全域哈希(Universal Hashing)的概念。

设 H\mathcal{H} 是从全域 UU 到 [n][n] 的一组哈希函数的集合。如果对于任意一对不同的元素 x,y∈Ux, y \in U(x≠yx \ne y),当从 H\mathcal{H} 中均匀随机选取 hh 时,满足:

Pr⁡h∈H[h(x)=h(y)]≤1n\Pr_{h \in \mathcal{H}}[h(x) = h(y)] \le \frac{1}{n}

则称哈希函数族 H\mathcal{H} 是 2-全域的(2-Universal)。这意味着,对于任意两个固定元素,它们发生碰撞的几率不大于将它们真随机独立投掷进 nn 个桶的几率。

2-全域哈希族的基本碰撞界

考虑集合 S⊆US \subseteq U(∣S∣=m|S| = m)。令指示随机变量 Ix,y=1I_{x,y} = 1 当且仅当 h(x)=h(y)h(x) = h(y)。由 2-全域性质可知:

E[Ix,y]=Pr⁡[h(x)=h(y)]≤1n\mathbb{E}[I_{x,y}] = \Pr[h(x) = h(y)] \le \frac{1}{n}

总碰撞对数记为 C=∑{x,y}⊆S,x≠yIx,yC = \sum_{\{x,y\} \subseteq S, x \ne y} I_{x,y}。根据期望的线性性质:

E[C]=∑{x,y}⊆S,x≠yE[Ix,y]≤(m2)⋅1n=m(m−1)2n\mathbb{E}[C] = \sum_{\{x,y\} \subseteq S, x \ne y} \mathbb{E}[I_{x,y}] \le \binom{m}{2} \cdot \frac{1}{n} = \frac{m(m-1)}{2n}

查询元素 xx 时所在链表长度 LxL_x 的期望值为:

E[Lx]=1+∑y∈S,y≠xE[Ix,y]≤1+m−1n\mathbb{E}[L_x] = 1 + \sum_{y \in S, y \ne x} \mathbb{E}[I_{x,y}] \le 1 + \frac{m-1}{n}

当表大小取 n≥mn \ge m 时,链表的期望长度不超过 22,即单次操作的期望时间为 O(1)O(1)。

此外,若分配 n≥m2n \ge m^2 的空间,总碰撞数期望满足:

E[C]≤m(m−1)2m2<12\mathbb{E}[C] \le \frac{m(m-1)}{2m^2} < \frac{1}{2}

根据马尔可夫不等式(Markov's Inequality):

Pr⁡[C≥1]≤E[C]1<12\Pr[C \ge 1] \le \frac{\mathbb{E}[C]}{1} < \frac{1}{2}

这意味着,当空间为 m2m^2 量级时,随机抽取一个函数 h∈Hh \in \mathcal{H},有一半以上的概率没有任何碰撞!

FKS 两级完美哈希(Fredman-Komlós-Szemerédi)

单纯分配 m2m^2 空间代价过高。Fredman, Komlós 与 Szemerédi(1984)提出了著名的 FKS 两级完美哈希,在保持 O(m)O(m) 线性空间的同时,实现最坏情况 O(1)O(1) 的无碰撞查询。

结构设计与空间核算

FKS 采用两级哈希表架构:

  1. 第一级(主表):大小取 n=mn = m,从 2-全域哈希族中选取函数 h:U→[m]h: U \to [m]。记映射到第 ii 个桶的元素集合为 SiS_i,其大小为 Si=∣Si∣S_i = |S_i|。
  2. 第二级(桶内子表):为每个桶 ii 分配一个独立的二级哈希表,其大小设为 ni=Si2n_i = S_i^2。二级哈希函数 hih_i 同样来自 2-全域族,映射到 [Si2][S_i^2]。

由前面的生日悖论界可知,在大小为 Si2S_i^2 的二级表中,发生内部碰撞的概率严格小于 1/21/2。

关键在于总空间消耗 ∑i=1mni=∑i=1mSi2\sum_{i=1}^m n_i = \sum_{i=1}^m S_i^2。注意到:

∑i=1mSi2=∑i=1m(Si(Si−1)+Si)=2∑i=1m(Si2)+∑i=1mSi=2C+m\sum_{i=1}^m S_i^2 = \sum_{i=1}^m \left( S_i(S_i - 1) + S_i \right) = 2 \sum_{i=1}^m \binom{S_i}{2} + \sum_{i=1}^m S_i = 2C + m

其中 CC 正是第一级哈希中所有元素的碰撞对数!因此总空间的数学期望为:

E[∑i=1mSi2]=2E[C]+m≤2⋅m(m−1)2m+m<2m\mathbb{E}\left[\sum_{i=1}^m S_i^2\right] = 2\mathbb{E}[C] + m \le 2 \cdot \frac{m(m-1)}{2m} + m < 2m

完美哈希的拉斯维加斯构造算法

利用马尔可夫不等式,可以设计出高效的静态表建表算法:

  1. 第一级建表:随机选择 h∈Hh \in \mathcal{H}。计算 ∑i=1mSi2\sum_{i=1}^m S_i^2。由于 E[∑Si2]<2m\mathbb{E}[\sum S_i^2] < 2m,满足 ∑i=1mSi2>4m\sum_{i=1}^m S_i^2 > 4m 的概率小于 1/21/2。若超过 4m4m 则重新抽样 hh。期望尝试次数不超过 22 次。
  2. 第二级建表:对于每个非空桶 ii(Si>1S_i > 1),在大小为 Si2S_i^2 的表上随机选择 hih_i。若产生碰撞则重新选择 hih_i。每次成功的概率均 >1/2> 1/2,期望重选次数不超过 22 次。

整个构造算法是拉斯维加斯类型的(输出永远正确,运行时间为随机变量),总建表时间的期望为 O(m)O(m),空间最坏为 O(m)O(m),而查询任意元素只需计算 h(x)h(x) 和 hh(x)(x)h_{h(x)}(x) 两次哈希,最坏时间保证为严格的 O(1)O(1)!

2-全域哈希族的代数构造

如何构造出占用描述空间极小、计算极快的 2-全域哈希函数族?

仿射变换构造法

选取一个大素数 pp,使得全域大小 ∣U∣≤p|U| \le p。定义函数族:

H={ha,b(x)=((ax+b) mod p) mod n  |  a∈{1,2,…,p−1},b∈{0,1,…,p−1}}\mathcal{H} = \left\{ h_{a,b}(x) = ((ax + b) \bmod p) \bmod n \;\middle|\; a \in \{1, 2, \dots, p-1\}, b \in \{0, 1, \dots, p-1\} \right\}

只需存储 aa 和 bb 两个整数(共约 2log⁡p2\log p 比特),即可完全确定一个哈希函数。

全域性质证明

对于任意给定的不同键值 x≠yx \ne y(x,y∈[p]x, y \in [p]): 令 s=(ax+b) mod ps = (ax + b) \bmod p,t=(ay+b) mod pt = (ay + b) \bmod p。

由模素数 pp 的有限域性质,由于 a≠0a \ne 0 且 x≠y(modp)x \ne y \pmod p:

s−t≡a(x−y)≢0(modp)  ⟹  s≠ts - t \equiv a(x - y) \not\equiv 0 \pmod p \implies s \ne t

根据线性同余方程组求解:

a≡(s−t)(x−y)−1(modp),b≡s−ax(modp)a \equiv (s - t)(x - y)^{-1} \pmod p, \quad b \equiv s - ax \pmod p

对于每一对确定的不同 (s,t)(s, t)(s≠t,s,t∈[p]s \ne t, s, t \in [p]),恰好对应唯一确定的参数对 (a,b)(a, b)。由于 (a,b)(a, b) 共有 p(p−1)p(p-1) 种均匀等可能的取值,因此 (s,t)(s, t) 也均匀分布在所有 p(p−1)p(p-1) 个有序非对角点上。

当且仅当 s≡t(modn)s \equiv t \pmod n 时,发生最终碰撞 ha,b(x)=ha,b(y)h_{a,b}(x) = h_{a,b}(y)。 对于任意固定的 s∈[p]s \in [p],满足 t≠st \ne s 且 t≡s(modn)t \equiv s \pmod n 的 tt 的个数至多为 ⌈p/n⌉−1≤(p−1)/n\lceil p/n \rceil - 1 \le (p-1)/n。

因此发生碰撞的选法总数最多为:

p⋅p−1n=p(p−1)np \cdot \frac{p - 1}{n} = \frac{p(p-1)}{n}

碰撞概率严格满足:

Pr⁡[ha,b(x)=ha,b(y)]≤p(p−1)/np(p−1)=1n\Pr[h_{a,b}(x) = h_{a,b}(y)] \le \frac{p(p-1)/n}{p(p-1)} = \frac{1}{n}

这便以构造性方式证明了 2-全域性。

一致性哈希的前瞻关联

在分布式存储与负载均衡场景中,桶数量 nn 会随节点增减动态改变。传统的 h(x) mod nh(x) \bmod n 会导致几乎所有键值重新映射。

一致性哈希(Consistent Hashing,Karger et al. 1997)通过将节点和数据键映射到同一个连续哈希环上,使单个节点的加入或退出仅影响环上相邻区间的极少数键值(数据迁移期望比例为 O(1/n)O(1/n)),成为现代分布式系统与 CDN 缓存路由的基础。

我的理解

我的理解

随机化不是无序,而是剥夺最坏输入的对抗能力: 在确定性哈希中,任何单一哈希函数在巨大的全域空间面前都是透明的,对抗性输入总能击穿平均时间保证。Carter-Wegman 的核心洞见在于:算法的设计者应当掌握随机性的主动权。只要哈希族具备 2-独立性(Pairwise Independence),输入数据即使由恶意对手挑选,映射的期望行为也与独立投掷完全一致。

FKS 两级哈希则展现了极具美感的分层思想:一级粗分降低局部规模,二级平方空间彻底粉碎残余碰撞,通过期望的线性性(E[∑Si2]=O(m)\mathbb{E}[\sum S_i^2] = O(m))巧妙解决了“最坏情况 O(1)O(1) 时间”与“线性空间”看似不可调和的矛盾。