完美 哈希函數(Perfect Hash Function,簡稱PHF)是沒有沖突的哈希函數,也就是,函數 H 將 N 個 KEY 值映射到 M 個整數上,這里 M>=N ,而且,對於任意的 KEY1 ,KEY2 ,H( KEY1 ) != H( KEY2 ) ,並且,如果 M = = N ,則 H 是最小完美哈希函數(Minimal Perfect Hash Function,簡稱MPHF)。完美 哈希函數是靜態的,就意味着事前必須知道需要哈希哪些數據。同時生成的算法比較復雜,需要很長的時間來建立索引。沒有辦法實時添加更新。給他的應用范圍提了個極大的限制。
在現實情況中,很難構造完美的散列算法。因為通常幾乎不可能提前知道要散列的完整數據集。例如,在我們馬上將探討的一個程序中,散列表用於統計文本中的重復單詞。由於我們事先不知道哪些單詞出現在文本中,就不能編寫一個完美的算法。數據倉庫的查詢索引,還有一些不需要更新且對性能有要求的場景,這個算法是適用的。
什么時 候使用PHF和MPHF
使用PHF和MPHF的一般流程
PHF和MPHF的集中生成程序庫
最小完美哈希函數算法的實現(例子說明)
算法實現
該算法的實現方法是這樣的。先構造兩個普通的哈希函數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 |
... | ... |
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》這本書看到的,這里的講述更淺顯一些。