Cuckoo Hash

最近在看APSI中,涉及到了一些hash技术(朴素hash,置换hash,布谷鸟hash),Hash 技术是 PSI 协议中优化通信复杂度和计算杂度的重要工具之一,现在系统的认识一下!
更多关于哈希函数的参考:哈希函数

朴素hash(plain hash)

使用\(ℎ𝑎𝑠ℎ_𝑘(·)\)将元素映射到具有 b 个桶的 Hash 表 T 中的 \(k\)个位置, 每个桶最多有\(lb(n)\)个元素(\(n\)为集合的元素个数),其中\(ℎ𝑎𝑠ℎ_𝑘(·)\)表示\(k\)个不同的Hash 函数 \(h_1(·), h_2(·),...,h_k(·)\).
比如:
该表中有四个桶,每个桶中最多可以存4个值。

置换hash(permutation-based hash)

将元素转化为更短的字符串并存储在 Hash 表中, 以此减少存储空间和计算复杂度.
元素插入如下: 元素 \(x\) 表示为bit 的形式并拆分为 2 部分 \(x_1, x_2\). 为元素获取 Hash表的索引: \(𝑥_1 ⊕ 𝐻(𝑥_2)\), \(H\) 为 Hash 函数.
最后桶中存储大小为\(|𝑥_2| = |𝑥| − |𝑥_1|\),其中\(|x|\)表示 \(x\) 的比特长度;\(⊕\)表示按位异或.

布谷鸟hash(cuckoo hash)

思考一个问题:为什么要用布谷鸟哈希?

1、首先是能隐藏数据
2、是一个高性能的哈希方案
3、哈希的本质是从一个较大空间映射到一个较小的空间,因此在插入数据足够多之后,根据鸽巢原理,一定会存在位置冲突。常见的哈希表(Hash Table 或者字典,dictionary)会通过链表、开放地址探测等方式来处理冲突。单桶多函数的布谷鸟哈希,便是开放地址法处理冲突的一种哈希表,只不过有冲突后,不是通过线性寻找新的位置,而是通过额外哈希函数来寻找。

背景

Cuckoo中文名叫布谷鸟,这种鸟有一种即狡猾又贪婪的习性,它不肯自己筑巢, 而是把蛋下到别的鸟巢里,而且它的幼鸟又会比别的鸟早出生,布谷幼鸟天生有一种残忍的动作,幼鸟会拼命把未出生的其它鸟蛋挤出窝巢,今后以便独享“养父 母”的食物。借助生物学上这一典故,cuckoo hashing处理碰撞的方法,就是把原来占用位置的这个元素踢走,不过被踢出去的元素还要比鸟蛋幸运,因为它还有一个备用位置可以安置,如果备用位置上 还有人,再把它踢走,如此往复。直到被踢的次数达到一个上限,才确认哈希表已满,并执行rehash操作。

定义

布谷鸟哈希最早是 Rasmus Pagh 和 Flemming Friche Rodler 在 2001 年一次会议上公开的 。

思想

使用\(ℎ𝑎𝑠ℎ_𝑘(·)\)将元素 \(e\) 映射到具有 \(b\) 个桶的 Hash 表 T 中的某一个位置, 确保每个桶只能有 1 个元素:

  • 计算 \(h_1(e), h_2(e), ..., h_k(e)\), 如果 \(T[h_1(e)], T[h_2(e)], …, T[h_k(e)]\)至少有 1 个桶为空, 则随机插入;如果都不为空, 则随机选择\(T[h_i(e)]\), 替换桶中的元素 \(T[h_i(e’)]\), 再对被替换元素\(e’\)执行上述操作.

当上述替换操作达到一定阈值时, 则将 \(e’\)放置在额外的存储空间 stash 中. 因此, 元素 \(e\)必定在以下容器中找到: \(T[h_1(e)], T[h_2(e)], …, T[h_k(e)]\)\(stash\). 由于 \(stash\) 可能会存在溢出威胁而导致Hash 错误, Pinkas 等人[5]通过实验分析出 Hash 函数个数、\(stash\) 大小和桶数 \(b\) 的最佳关系.

变种

布谷鸟哈希可以有很多变种

使用两个哈希函数和一个哈希桶(表只有一个桶)

  1. 对key值hash,生成两个hash key值,hashk1和 hashk2, 如果对应的两个位置上有一个为空,那么直接把key插入即可。
  2. 否则,任选一个位置,把key值插入,把已经在那个位置的key值踢出来。
  3. 被踢出来的key值,需要选择另外一个hash函数插入,直到没有key被踢出为止。

下面是第一种情况,有空位置,直接插入即可!

下面是出现碰撞的情况,需要被踢出,选择另外一个hash函数重新插入

举例

假设有两个hash函数:

下面依次插入item:

可以看作两张表,但序号是一致的。

当再次插入item:6时,就出现了问题,插入失败:

应用

cmu 大学的 Bin Fan 等人,在 2014 年发表了一篇名为:Cuckoo Filter: Practically Better Than Bloom的论文,基本思想是将布谷鸟哈希(cuckoo hash)的思想应用于集合(set membership)方向,可以替代工程中常用的 Bloom Filter(布隆过滤器),有以下优势:
1、支持删除元素
2、更高的查询效率,尤其在高负载因子时
3、相比其他支持删除的 Filter 更容易实现
4、如果期望误报率在 3% 以下,所用空间比 Bloom Filter 少

为了达到以上效果,Cuckoo Filter 对原 Cuckoo Hash 做了如下改变:
1、为了提高桶的利用率,使用多路哈希桶。
2、为了减少内存的使用,只存储 key 指纹(不懂就问,什么是key指纹?)。

实现

微软有一个开源库:Kuku,下面说一下怎么用。

介绍

Kuku是一个简单的开源(MIT许可)布谷鸟哈希库,由微软的加密和隐私研究小组开发。KuCu是用现代标准C++编写的,没有外部依赖关系,便于在许多不同的环境中编译和运行。

cuckoo hash

布谷鸟哈希是一种哈希技术,可以实现非常高的填充率,尤其能创建高效的哈希表,每个桶(bin)中只有一个项目。这是通过使用多个(通常是2、3或4个)不同的哈希函数实现的,如下所示:
(1)\(H_1, H_2, ..., H_k\)表示hash函数
(2)当\(X\)要插入时,任意选择一个hash函数\(H_j\),判断对应的bin是否为空(\(H_j(X)\)所对应的位置),如果为空,则插入\(X\),返回\(True\);否则,移除bin中存在的值\(Y\),将\(X\)插入到该位置,\(Y\)则重新执行插入操作(选用其他的hash函数)
(3)如果尝试多次后失败,则将item放入预准备好的最大容量的桶(stash)中,然后返回\(True\)
(4)如果stash已经达到装满,将该item储存在"已知位置"并返回\(False\)

不太理解这个已知位置;stash可以看作是另外一张hash表

为了判断一个item:\(Z\)是否在hash表中,需要逐个比较\(H_1(Z), H_2(Z), ..., H_k(Z)\),stash同样也是。如果不需要使用stash,则使长度为0即可。

kuku

Kuku是一个简约的库,它支持布谷鸟哈希的某些变体,如上所述。它使用Tabulation hashing作为hash函数。Kuku中的item的长度正好是128位,不能增加;然而较长的item总是可以使用其他一些接受任意输入长度的哈希函数哈希到128位,并且输出可以随后在Kuku中使用。

Tabulation hashing:一种特殊的hash函数

安装

//下载库
git clone https://github.com/Microsoft/Kuku 

//编译,这样生成的静态文件/build/lib/libkuku-2.1.a,但没有编译example和test下文件
cd kuku-main
cmake -S . -B build
cmake --build build

//编译(exapme和test)
cmake -S . -B build -DKUKU_BUILD_EXAMPLES=ON
cmake -S . -B build -DKUKU_BUILD_TESTS=ON
cmake --build build

最后结果在/build/bin下:

使用

example实例

主要模块

主要是src/kuku下的文件

(1)locfunc

(2)kuku

(3)common

参考

1、Cuckoo Hash 布谷鸟哈希
2、布谷鸟哈希和布谷鸟过滤器
3、CUCKOO FILTER:设计与实现
4、面向隐私保护的集合交集计算综述
5、Scalable private set intersection based on OT extension

posted @ 2022-04-24 11:39  PamShao  阅读(2840)  评论(0编辑  收藏  举报