专栏文章

Filters

算法·理论参与者 7已保存评论 10

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
10 条
当前快照
1 份
快照标识符
@mj3478ow
此快照首次捕获于
2025/12/13 01:02
2 个月前
此快照最后确认于
2026/02/13 01:26
7 天前
查看原文
前言:
本文中和文末设计了若干思考题,可以稍加思考。

Filter 用于解决如下问题:
  • 插入一个元素 xx
  • 查询一个 xx 是否出现过。
普通的做法是开一个 uset 直接做,但是这样有若干缺陷:
  • 空间消耗大。
  • 无法传输(这里指通信)。
因为如上原因,我们要引入 Bloom Filter(布隆过滤器)。
思考:平常的哈希表使用 vector 来避免冲突,我们现在允许一定的错误率,能否只用 01 字符串存储信息?要求只有假阳性而不会出现假阴性(即可能 0 回答成 1 而不允许 1 回答成 0)。
考虑如下算法:
  • 设计哈希函数 h(x)[1,m]h(x) \rightarrow [1 , m]
  • 对于一个插入操作,ah(x)=1a_{h(x)} = 1
  • 对于一个查询操作,查询 ah(x)a_{h(x)} 的值。
但是这样 FP 率比较高。Bloom Filter 采用了如上思想,但是做了些许改进。
  • 设计哈希函数 h1(x)hk(x)[1,m]h_1(x) \sim h_k(x) \rightarrow [1 , m]
  • 对于一个插入操作,i[1,k],ahi(x)=1\forall i \in [1 , k], a_{h_i(x)} = 1
  • 对于一个查询操作,查询 andi=1kahk(x)\operatorname{and}_{i = 1}^k a_{h_k(x)} 的值。
如果要取得较优的参数,可以查询 https://hur.st/bloomfilter/
参数选择在合理范围内都具有可行性。
Bloom Filter 看似简单,事实上已经逼近了信息论的极限,但是仍然有许多方向可以优化。
  • 增加删除操作(Cuckoo Filter)。
  • 优化空间性能(Xor Filter)。
  • 杜绝错误(4-coloring Filter),但是有额外限制。
Before Cuckoo:计数 Bloom Filter
思考:能否给 Bloom Filter 加入计数器使得其支持删除操作?
解答:显然可以,考虑存至多 kk 次,则需要 kk 倍空间,从赋值变为加减即可。
Cuckoo Filter 基于布谷鸟哈希算法。
布谷鸟哈希是一种特殊的哈希算法。
设立 h1(x),h2(x)[1,m]h_1(x) , h_2(x) \rightarrow [1 , m]
  • 如果 h1(x),h2(x)h_1(x) , h_2(x) 上没有巢穴直接随机放在 h1(x)h_1(x)h2(x)h_2(x) 上。
  • 如果 h1(x),h2(x)h_1(x) , h_2(x) 上只有一个巢穴直接放在空的巢穴上。
  • 如果都不为空,则随机踢出一个元素,踢出的元素再重新计算哈希找到相应的位置。
可以设一个踢出阈值动态扩容。
现在思考如下问题:
  • 直接建立 Cuckoo 哈希表是不可取的,这样做比普通哈希没有优势。思考空间复杂度瓶颈在哪里?
  • 提示:如果使用 kk 个 01 bit,随机哈希可达到 2k2^{-k} 的错误率。
  • 如何在不存储 xx 的情况下计算 Hash 值?
  • 提示:考虑优化哈希函数。
我们考虑不存储 xx 的真实值,但是这样我们就无法在迁巢时计算第二个哈希函数的真实值。
  • 考虑改变:让两个 Hash 函数不再独立!
  • h3(x)[1,m]h_3(x) \rightarrow [1 , m]f(x)[0,2k)f(x) \rightarrow [0, 2^k)
  • 考虑让 h2(x)=h3(f(x))xorh1(x)h_2(x) = h_3(f(x)) \operatorname{xor} h_1(x)
我们将键值变为 f(x)f(x),如果我们仅存储 f(x)f(x),我们也可以通过 xor\operatorname{xor} 的方式找到另一个哈希值。
查询的时候判断 h1(x),h2(x)h_1(x) , h_2(x) 下标是否有一个 =f(x)= f(x) 的即可。
4-Coloring Filter 相对而言知名度较低,下文将介绍其核心思想。
这个 Filter 虽然效率最高但是有一个额外限制:必须提前给定所有输入,也就是你必须知道 SQS \cup Q,其中 QQ 表示所有询问构成的集合,你也可以理解为每个元素有一个 {0,1}\{0 , 1\} 权值然后若干次询问权值。
思考:设计 h1(x),h2(x)[1,m]h_1(x) , h_2(x) \rightarrow [1 , m],考虑根据 {0,1}\{0 , 1\} 权值建图
我们令 S0S_0 表示权值为 00 构成的 (h1(x),h2(x))(h_1(x) , h_2(x)) 集合,S1S_1 表示权值为 11 构成的 (h1(x),h2(x))(h_1(x) , h_2(x)) 集合。
我们钦定 S0S1|S_0| \leq |S_1|,否则可以交换。
考虑给图着色,让 S0S_0 中的所有边代表两个节点同色,S1S_1 代表异色。
如果找到一个合法的图着色方案,可以 O(1)\mathcal O(1) 回答询问,并且必然正确
但是冲突是难免的(当 mm 约等于 nn 时),理由是并查集缩点后异色边链接同一个连通块,我们在启发式合并之后尽可能找到一组冲突数量尽可能少的解,剩余的再使用 hash_table/umap。
Xor Filter 的主要思想是设计三个哈希函数 h1(x),h2(x),h3(x)[1,m]h_1(x) , h_2(x), h_3(x) \rightarrow [1 , m],和指纹 f(x)[0,2k)f(x) \rightarrow [0, 2^k) 并求出一组解 TT 使得对于任意 xSx \in S,都有 Th1(x)xorTh2(x)xorTh3(x)=f(x)T_{h_1(x)} \operatorname{xor} T_{h_2(x)} \operatorname{xor} T_{h_3(x)} = f(x)
  • 思考:如何构造 TT
  • 提示:如果我们让 ch1(x),ch2(x),ch3(x)c_{h_1(x)} , c_{h_2(x)} , c_{h_3(x)} 都增加 11,即 cc 是计数器。如果存在 cc 值恰好为 11jj,无论其他 TiT_i 分布,我们可以仅改变该位的 TT 使得条件成立。
做法已经呼之欲出:类似拓扑排序,将每个 cc 值为 11 的点剔除,找到他的 h1(x),h2(x),h3(x)h_1(x), h_2(x) ,h_3(x),删除标记后我们将 (idx,j)(idx , j) 压入栈中。
最后弹出栈时修改 TidxT_{idx} 使其满足条件即可。
这个算法需要离线处理,即先处理插入再回答询问。
如果仍然认为不够清晰,可以查阅伪代码:
事实上取 m=1.23n+O(1)m = 1.23n + \mathcal O(1) 最优。
Ribbon Filter
Ribbon Filter 在概念上可视为 Xor Filter 的泛化。
下文中的矩阵乘法都是加乘矩阵(在 01 意义下),即 ci,j=xorai,kandbk,jc_{i, j} = \operatorname{xor} a_{i , k} \operatorname{and} b_{k , j}
放个伪代码。
s(x)[0,m],c(x)[0,2k),b(x)[0,2r)s(x) \rightarrow [0 , m] , c(x) \rightarrow [0 ,2^k), b(x) \rightarrow [0, 2^r) 都是 Hash 函数。
c(x)c(x) 是定长二进制整数,这样避免了 xor 时补位。记得低位补 00
我们考虑令 h(x)=0s(x)+c(x)+0ws(x)c(x)h(x) = 0^{s(x)} + c(x) + 0^{w - s(x) - |c(x)|}++ 表示字符串拼接,0x0^{x} 表示 xx00。这样每次相当于看 h(x)h(x) 有多少个前缀 00,记为 pp
如果第 pp 行为空,则直接插入:hp=x,bp=b(x)h_p = x , b_p = b(x)
否则让 h(x)h(x)xorhph(x) \leftarrow h(x) \operatorname{xor} h_pb(x)b(x)xorbpb(x) \leftarrow b(x) \operatorname{xor} b_p
  • tip:如果 h(x)h(x) 归零说明构建失败(除非 b(x)=0b(x) = 0,此时说明了这个限制重复)
这样就完成了插入。同时显然我们可以快速撤销上一次插入,但无法快速删除。
  • 为什么直接 xor 是对的:显然在同一行的元素 s(x)s(x) 相同,又因为 c(x)|c(x)| 定长,因此是对的。
如何查询?考虑构造矩阵 SS 使得 hS=bhS = b,这样查询即可快速判断:只需查询 h(x)S=b(x)h(x)S = b(x) 是否成立即可。
  • 思考:为什么查询是正确的?
  • 解答:插入是一个消元的过程。
  • 思考:如何构造 SS
  • 提示:考察消元后 hh 矩阵的性质。
解答:这就是一个高斯消元。已经消出了一个上三角矩阵,因此做完了。(稍微说一下,就是倒着确定 SS 的每一行即可)。
显然构建出 S S 后可以立即抛弃 h,bh , b 矩阵,因此也只是产生临时空间。
r=4r = 4 效果不错(哈希位数)。
这张图是真实存储的 cc 矩阵(b)。
Homogeneous Ribbon Filters
  • 考虑丢弃指纹函数 b(x)b(x) 会怎么样。
这样 bb 矩阵就是一个全 00 矩阵。
显然有平凡解 SS 也为全零矩阵。
但是如果使用这个 SS,则 FP 率等于 11
如果我们可以随机从解集空间选择一个 SS,这样 FP 率会较低。而且这完全改善了构建失败的问题。
但是这个过滤器在 nn 较小时 FP 率较高,因为解的质量方差很大。所以在 n<104n < 10^4 时不要使用这个过滤器。
  • 思考:给定 01(xor-and)矩阵 xx,如何在所有 xyxy 是全 00 的解 yy 中随机选取一个?
  • 解答:给每个自由变量赋值为 [0,1][0 , 1] 中的随机权值即可。
思考题:
  • 给定 FP 率 pp 和插入元素个数 nn,计算最少使用的空间数量(信息论下界)。
  • 计算 Bloom Filter 的 FP 率,(给定 n,m,kn , m , k)。
  • 解释 Cuckoo Filter 中 h3h_3 的作用。
  • 如何改造 4-Coloring Filter 使其支持在线插入和删除。
  • 如何在 Homogeneous Ribbon Filters 使用 FP 率换时间?
另外,我新增了一道题目供测试。
  • U640209 Filter Test
可用于测试自己的 Filter 算法。

评论

10 条评论,欢迎与作者交流。

正在加载评论...