第 1 页
- 标题:Hashing II:
- Universal hashing functions(通用哈希函数)
- String matching(字符串匹配)
- Table doubling(表扩容/加倍)
- 这表示这次要重点讲三个话题:
- 更多的通用哈希函数的例子与原理;
- 字符串匹配里常见的 Rabin-Karp 算法(它也用到哈希思想);
- 表扩容(又称摊销分析),解释哈希表如何动态扩大以保持良好性能。
第 2 页
- Objective(目标):
- Analyze selected topics in hashing:继续讨论哈希的一些重点话题;
- Universal hashing function examples:给出几个具体的通用哈希函数构造例子;
- String matching: the Rabin-Karp algorithm:用哈希思路做字符串模式匹配;
- Table doubling:进行摊销(amortized)成本分析,看插入/扩容是如何保持平均 \(O(1)\) 的。
通俗理解:本次课程主题是哈希的高级应用和更详细的性能分析,尤其是字符串匹配(Rabin-Karp)和哈希表扩容算法(table doubling)。
第 3 页
- Universal hashing(通用哈希)的回顾:
- 回到定义:若 \(H\) 是一族哈希函数,对任意两个不同键 \(k \neq r\),落到同一个哈希值的函数数量不超过 \(|H|/m\);也就是随机选一个函数 \(h\) 时,碰撞概率 \(\le 1/m\)。
- 也就是:我们能“证明”可以构造这样的函数家族 \(H\) 来保证碰撞概率小。
通俗理解:这页就是重复/回顾之前讲过的通用哈希核心概念——通过随机选函数来降低任意两键碰撞的概率。
第 4 页
- 标题:Universal hashing function examples(通用哈希函数举例)
- 从这一页开始,会展示一些具体的通用哈希函数是如何设计的。
通俗理解:前面都是概念,现在要看真正落地的“数学构造”。
第 5 页
- Example 1: The matrix method
- 假设键(keys)有 \(u\) 位(bit),哈希表大小 \(m = 2^b\),也就是说索引只需要 \(b\) 位。
- 我们随机生成一个 \(b \times u\) 的 0/1 矩阵 \(h\),然后定义 \(h(x) = h \times x\)(矩阵乘法在模 2 下进行,即二进制加法)。
- 声称:对任意不同 \(x \neq y\),碰撞概率 \(\le 1/2^b = 1/m\)。
大白话:
把一个 \(u\)-bit 的输入当作 \(u\)-维向量 \(x\),再乘上一个 \(b \times u\) 的随机 0/1 矩阵(在“模 2”意义下),结果就是一个 \(b\)-bit 的输出值(也就是在 \(m=2^b\) 范围内)。能证明这样做让碰撞概率很小。
第 20 页:总结
通俗解释
- 通用哈希函数:
- 易于构造,能有效降低碰撞概率。
- 表扩容(Table Doubling):
- 解决动态哈希表存储的问题。
- 让哈希表不会一直固定大小,而是按需增长。
- 均摊分析(Amortized Analysis):
- 关键在于分摊高成本操作,让平均操作保持
O(1)
。
- 关键在于分摊高成本操作,让平均操作保持
- Rolling Hash(滚动哈希):
- 主要用于字符串匹配,结合模运算提高搜索效率。
出题
Q4 (English): What is the amortized cost per insert operation when using table doubling?
- (A)
O(n)
- (B)
O(log n)
- (C)
O(1)
- (D)
O(m)
✅ Answer: (C) O(1)
中文额外解释
Q: 为什么表扩容成本能均摊到 O(1)
?
- 因为 虽然扩容操作是
O(n)
级别,但下一次扩容前,我们有n
次O(1)
级别的插入。 - 直觉理解:
- 如果你每次都存钱(多存一点),那么突然有一天要花大钱时,你已经存够了,分摊到过去的存款上,每次存钱的平均花费仍然是小数目。