[整理]完美哈希函數(Perfect Hash Function)


完美 哈希函數(Perfect Hash Function,簡稱PHF)是沒有沖突的哈希函數,也就是,函數 H 將 N 個 KEY 值映射到 M 個整數上,這里 M>=N ,而且,對於任意的 KEY1 ,KEY2 ,H( KEY1 ) != H( KEY2 ) ,並且,如果 M = = N ,則 H 是最小完美哈希函數(Minimal Perfect Hash Function,簡稱MPHF完美 哈希函數是靜態的,就意味着事前必須知道需要哈希哪些數據。同時生成的算法比較復雜,需要很長的時間來建立索引。沒有辦法實時添加更新。給他的應用范圍提了個極大的限制。

在現實情況中,很難構造完美的散列算法。因為通常幾乎不可能提前知道要散列的完整數據集。例如,在我們馬上將探討的一個程序中,散列表用於統計文本中的重復單詞。由於我們事先不知道哪些單詞出現在文本中,就不能編寫一個完美的算法。數據倉庫的查詢索引,還有一些不需要更新且對性能有要求的場景,這個算法是適用的。

什么時 候使用PHFMPHF

通常情況下,PHF或MPHF是針對 靜態集合的。也就是,在使用PHF或MPHF時,所有的 KEY 值是事先已知並且固定的。不過, 這里有 針對動態集合的一個算法。

使用PHFMPHF的一般流程

1.  (准備階 段)將已知的所有的 KEY 值傳給PHF或MPHF生成算法,生成PHF或MPHF以及相應的數據;
【這也是完美hash函數的一大缺點:必須事前必須知道原數據集,並且需要花一定的CPU來生成這個函數】
2.   (使用階 段)調用已有的PHF或MPHF以及相應的數據快速計算哈希值並進行相應的操作。
其實在實際使用中我們只關心步驟2,但步驟1的生成算法卻是PHF或MPHF的關鍵。

PHFMPHF的集中生成程序庫

GNU的完美哈希函數生成程序,可以生成PHF和MPHF,生成MPHF時和輸入數據以及參數設置的關系比較大。使用的應該是比較簡單的算法, 生成的效率不高,當數據量大時,程序就沒什么反應了。生成的結果是代碼(里面包含有數據,直接組織在代碼里),移植性非常好。很多編譯器對保留字的處理都 采用gperf來建立完美哈希函數。Windows版的可執行文件可以從 這里下 載,源代碼可以從 這里下 載,一篇介紹論文在 這里, 說明書在 這 里,說明書中文翻譯在 這里
易用性:5
穩定性:5
移植性:5
效率 : 2
MPHF: 2
巴西的這個哥們 Fabiano C. Botelho發了很多MPHF方面的論文。CMPH應該他和其他幾個人開發的開源的生成MPHF的程序庫。 這里面封裝了4個算法,而且設計了一個程序框架(搞不懂他們為什么要設計這樣一個框架,用MPHF的人不會 像他們做研究那樣會一次使用那么多個算法的,而這樣一個框架明顯增加了使用的難度)。里面有幾個算法是針對大數據量的,但簡單試了試,發現並不像他們論文 里宣稱的那樣高效,而且由於是一個框架,生成的MPHF並不能直接脫離他們的環境來使用。 這里是他們在SourceForge上的鏈接。
易用性:3
穩定性:2
移植性:2
效率   : 2
MPHF:5
mph
又一個牛人寫的生成MPHF的算法,注 意了,他寫這個純粹是為了好玩,考!
簡單試了試,可以直接生成代碼,但不是很好用,針 對大數據量效率也不行。
易用性:3
穩定性:3
移植性:4
效率 : 3
MPHF:5
又又一個牛人寫的生成MPHF的算法,比 較好用,可以處理大數據量的集合,而且比較有特色的是關鍵字不僅僅可以是字符串,還可以是整數等。
易用性:5
穩定性:5
移植性:4
效率 : 5
MPHF:5
以上都是用C/C++來實現的PHF或MPHF生成程序 考,這是一個用Python實現的MPHF生成程序。還是比較好用的,遺憾的是對大數據量效率不行。
易用性:5
穩定性:5
移植性:4
效率 :3
MPHF:5

最小完美哈希函數算法的實現(例子說明)

算法實現

該算法的實現方法是這樣的。先構造兩個普通的哈希函數h1(x)和h2(x),還有一個用數組實現的函數g(x)。使得h(x)=g(h1(x))+g(h2(x)) mod n,其中n是參數的總個數,H(x)就是最終的有序最小完美哈希函數了。

以上是定義,說不清楚,舉個例子就明白了。取一個n為12的數據集。

首先構造這三個函數:
函數h1和h2:

x h1函數 h2函數
jezebel 5 9
jezer 5 7
... ... ...

函數g:

x g函數
5 0
7 1
9 0
... ...
根據上文的公式 h(x)=g(h1(x))+g(h2(x)) mod n,可以得出:
x h計算步驟 h值
jezebel g(5)+g(9) 0
jezer g(5)+g(7) 1
... ... ...

這里的h就可以最小完美哈希函數算出的值了,很神奇,不是嗎?

大致的流程走了一遍,現在最最關鍵的是h1(x),h2(x)和g(x)是怎么得來的。

h1(x)和h2(x)比較簡單,可以使用一個很簡便的方法獲得:先定義一個權重數組w[i],這個數據是一系列隨機的數。h1=(t[1]w[1]+t[2]w[2]+...+t[i]w[i]) mod m。其中t[i]指得的字符串x的第i個字符,m值得的函數的值域。只要更換一個權重數組,就可以重新構造一個新函數。有很多方法可以構造這兩個函數。

g(x)的獲得就比較復雜。可以是湊出來的。就如同上面的例子,因為g(5)+g(9) mod 12=0,g(5)+g(7) mod 12=1。所以我們可以湊出g(5)=0,g(7)=1,g(9)=0這樣就可以滿足上面的兩個條件了。需要一個數組來存儲函數g的結果,當然湊也不能瞎湊,是有方法的,下面專門講湊的步驟。

算法函數生成

首先隨意設定一個數,比如是7,設為g(7)=1,因為我們已知g(5)+g(7) mod 12=1,所以可以推論出g(5)=0。因為g(5)+g(9) mod 12=0,所以可以推出g(9)=0,以此類推就可以了。但要注意的是千萬不能重復設定一個數兩次,這樣就會形成一個環,永遠也推不完。所以遇到已經推算過的數的時候,要檢測環的存在。這樣下去,就可以猜出全部的值了。

現在需要的就是分析這個湊的過程的運行效率問題。這個就要涉及到h1,h2這兩個函數的值域大小,如果越大,越容易湊出一個g函數,但是g函數的參數域就會比較大,存儲這個g函數的數據就需要占用更多的空間。相反如果值域越小,在湊的時候就非常容易出現環,需要更長的時間才能湊出這個g函數。

怎么辦呢?

我們可以使用3個的h函數來降低形成環的可能,就是這樣h(x)=g(h1(x))+g(h2(x))+g(h3(x)) mod n,這樣雖然推理g函數的過程會復雜一些,但是很有效,有實驗分析表明,當h函數的值域大約參數域的1.23倍的時候,這個g函數的創建嘗試次數是常數。

至此,這個算法介紹完了。這個方法是從《Managing Gigabytes》這本書看到的,這里的講述更淺顯一些。

參考:http://www.yankay.com/introduction-to-opmphf/

參考:http://blog.csdn.net/chixinmuzi/article/details/1727195


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM