# 📘 哈希函数的定义与作用
# ✅ 什么是哈希函数?
哈希函数是一个 将输入值映射到固定大小的输出空间 的函数。
输入:任意数据(整数、字符串、对象等)
输出:通常是一个固定范围的整数(例如 0 到 m−1m - 1m−1)
记作:
h : \mathcal{U} \rightarrow \
其中 U 是输入的全集
# 🧰 用途 / 作用
哈希函数被用于实现:
应用 | 说明 |
---|---|
哈希表 | 高效的数据结构,实现 dict / map |
快速查找 | 期望时间复杂度 O (1),比线性搜索快得多 |
数据分组 | 把数据打散分配到不同桶中 |
数据去重 | 用哈希值判断是否出现过 |
加密和校验 | 如密码存储、完整性验证(SHA256 等) |
图算法 / 流算法 | 用哈希做随机划分、模拟、抽样(正是作业里的内容) |
空间复杂度:O (logm + m'logm)
# 🔁 举例说明
例如,你要存储学生姓名 "Alice"
,你可以使用:
1 | def hash(name): |
这样
"Alice"
→ 哈希值在 0~99 之间存在哈希表中时,就可以根据哈希值快速定位到对应的位置
# ❓为什么我们需要哈希函数?
因为我们希望:
快速插入、查找、删除数据(比数组快)
分布要尽量 “均匀”,避免太多冲突
在算法中模拟 “随机分组”“颜色划分” 等操作(例如 MaxCut)
# 一些自己的理解
所谓哈希表,就是底层使用哈希函数来实现的表
例如 dic,他的本质就是比如将一个名为 'David' 的数据,通过哈希函数加密,例如 hash ('David') = 17,那么将直接去寻找地址为 17 的位置的值,故哈希表的查询非常快,因为他只需要通过哈希函数就可以直接查找到目标位置,而不需要遍历。
如 set,就是通过哈希值的形式实现查重和去重。
# 🎯 主旨:哈希表遇到 “哈希冲突” 怎么办?
# 所谓哈希冲突是指:
两个不同的输入,通过哈希函数后,得到相同的哈希值(= 同一个位置)
# 🧱 哈希表内部的数据结构
哈希表背后是一个 “数组”,哈希函数输出的是数组的下标。
比如:
1 | "Apple" → hash → 3 |
# 🔨 如何解决冲突(重点)
# 方法一:Separate Chaining(链地址法)
每个位置放一个链表(或列表)
所有落在同一位置的元素,都挂在那一桶后面
就像 “一个抽屉里放多个文件”
🧠 优点:结构简单,易实现
📉 缺点:查找慢时可能退化为链表查找 O (n)
# 方法二:Open Addressing(开放寻址法)
如果位置被占了,就往后找一个空位
所有元素都存在主数组中,不用额外链表
# 🔹 线性探测(Linear Probing)
- 冲突时,往后挨个找空位:
row 1
hash("Apple") = 3 → 位置 3 被占 → 试 4 → 再试 5 ...
🧠 优点:实现简单
📉 缺点:容易形成 “堆积效应”(连续位置都被占)
# 🔹 布谷鸟哈希(Cuckoo Hashing)
使用 多个哈希函数(比如两个)
每个元素可以存在两个位置之一
如果都被占,就踢出原来的值,重新放
🧠 类似布谷鸟把别人蛋踢出去放自己的蛋
📈 性能优秀,查找是常数时间 O (1)
# ✅ 总结对你有用的理解:
方法 | 原理 | 类比 |
---|---|---|
Separate Chaining | 同位置挂链表 | 一个格子放多个书签 |
Open Addressing | 冲突时换地方 | 柜子满了换下一个抽屉 |
Linear Probing | 一格一格往后找 | |
Cuckoo Hashing | 多位置可放,挤出旧的 | 布谷鸟占窝蛋 🐣 |
# 3. 随机性在哈希函数中的作用
在理论上,很多分析都会假设哈希函数是完全随机的
但在实际中,我们不能真地做到这一点(因为完全随机函数是无限大、不可实现的)
所以我们用的是 “哈希函数族”,例如:
2-universal(两两独立的哈希函数族)
k-wise independent(k - 元独立哈希函数族)
✔️ 所以我们希望找到一种 简单、可实现、但表现类似完全随机 的哈希函数。
# ✅ 1. 理论 vs 实践:完全随机 vs 可实现哈希函数
在很多算法分析中(比如概率分析),我们常常假设哈希函数是 “完全随机” 的,也叫做:
理想哈希函数,它对每个输入输出完全独立均匀分布。
但问题是:你没法真正实现一个完美随机函数,因为它需要无限多的随机比特。
# ✅ 2. 所以我们做了什么?—— 构造 “哈希函数族”
我们定义一族函数 ,从中随机挑一个函数来用。
这叫做:
哈希函数族(hash function family)
而且我们希望这个族具备一些 “随机性保证”:
# 🔹 常见的哈希函数族类型
类型 | 含义 | 示例 |
---|---|---|
2-universal (两两独立) | 对任意不同输入 x≠yx \neq yx=y,哈希值 h (x),h (y) h (x), h (y) h (x),h (y) 是独立的 | 你作业中实现的就是这个 |
k-wise independent | 任意 kkk 个输入的哈希值是联合独立的 | 更强,适合更高级算法 |
# 🔸 举个你熟悉的例子(你作业中):
这个就构成了一个 2-universal 的哈希函数族!
# 🔹 Pairwise Independent Hash Functions(两两独立)
哈希族 H 是两两独立的,当对任意 x≠y:
这个公式的意思是:
对于任何两个不同的输入 x 和 y,它们的哈希结果是独立分布的
也就是说:
h (x) 的结果不会影响 h (y) 的结果
就像真的从完全随机函数中取出来一样(虽然我们只是从一个 “函数族” 中选)
# 🔧 为什么叫 “2-universal” 或 “两两独立”?
是指:只要求任意两个输入之间的结果是独立的(不要求 3 个或更多输入联合独立)
比起完全独立(所有输入都独立),更容易实现,但仍有强大理论保证
# ✅ 第三行:
举了经典构造:
这个就是你在作业中要用的哈希函数形式!
p:一个大于输入范围的素数
m:哈希输出范围(比如你要输出 0 或 1 就设 m=2m = 2m=2)
,** 但要求 a≠0
# ✅ 这个构造的厉害之处?
数学证明:这个哈希函数族是两两独立的
即你随便选两个不同的输入 x≠y,它们的哈希值是独立的!
# 🧪 举个简单例子:
设:
p=101(素数)
m=2(你想要一个 0 或 1 的哈希值)
随机选 a=7, b=13
那:
对 x=3,哈希值可能是 1
对 x=5,哈希值可能是 0
并且这两个结果是概率独立的
# ✅ 总结一句话:
你作业中实现的
h(n, r, x)
就是在构造一个两两独立哈希函数族中的成员,用它来保证哈希结果足够随机但可控,用于各种概率算法(比如 MaxCut 随机分组)。
# 5. 应用拓展:布隆过滤器、流处理、数据结构
这是讲哈希函数在实际算法与系统设计中的应用场景。具体包括:
# ✅ 1. 布隆过滤器(Bloom Filters)
是一种节省空间的集合判断结构
可以快速判断一个元素是否 “可能存在” 于集合中
使用 多个哈希函数 映射到一个位数组中
# 🔍 特点:
优点 | 缺点 |
---|---|
空间非常省 | 会有 “假阳性”:可能说存在但其实没有 |
查询非常快 | 不支持删除元素(除非用 Counting Bloom Filter) |
✅ 典型用途:网页缓存、数据库、网络黑名单、区块链去重等
# ✅ 2. 哈希在流处理算法中的应用
比如在处理大规模数据流时(比如网络数据包、日志流):
无法保存全部数据
但你可以用哈希函数来做:
估计不同元素个数(distinct counting)
检测频繁项(heavy hitters)
滑动窗口统计
这些技术都用到了类似 “哈希函数族 + 位数组 / 计数器” 的结构。
# ✅ 3. 哈希函数在其他数据结构中的应用
例如:
Skip lists(跳表):部分使用哈希构造跳跃层
Count-Min Sketch(用于频率估计)
HyperLogLog(用于基数估计)
Cuckoo Hashing(你刚学的!)
# ✅ 4. 引用的论文:Mitzenmacher & Vadhan, 2008
这句话说的是:
虽然我们在理论中用的是 “完全随机” 的哈希函数,
但实践中即使用简单的哈希函数族(比如两两独立),实际效果也很好。
这也是你课程强调的重点之一:
我们不需要 “完美随机”,只要够用的随机性 + 理论保证 → 依然很强!
# ✅ 总结一句话:
这一部分告诉你:哈希函数不仅是基础结构,更是很多现代算法和系统的关键组成部分。从节省空间到流式处理,哈希的 “轻量 + 随机 + 快速” 特性,使它成为构建高效系统的利器。