最近在看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\) 的最佳關系.
變種
布谷鳥哈希可以有很多變種
使用兩個哈希函數和一個哈希桶(表只有一個桶)
- 對key值hash,生成兩個hash key值,hashk1和 hashk2, 如果對應的兩個位置上有一個為空,那么直接把key插入即可。
- 否則,任選一個位置,把key值插入,把已經在那個位置的key值踢出來。
- 被踢出來的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