不發生沖突的Hash函數,你信喵?
不發生沖突的Hash函數,你信喵?不管你信不信,反正我第一次看到時候是不信的喵~
其實人家有個專門的名字叫作“完美哈希函數”——就是完全不會沖突的哈希函數。
我想大家每次Hash-table的時候一定很討厭沖突的存在,這時候你肯定是會想盡辦法去避免過多沖突的出現。依我個人的經驗,到最后很可能就是直接借用別人使用的hash函數了。
要自己設計一個hash函數容易,但推導或證明其發生沖突的概率滿足要求,這個就比較麻煩了喵~更何況這里是要求得到一個完全不沖突的函數。這簡直是想都不敢想的事情了喵(我第一次看見完美hash函數時就是這種感覺)
喵~說了這么多沒營養的話喵~來點實用的喵~
先來幾個概念
哈希函數(Hash Function)
任意函數h(x)都可以說哈希函數,一般來說,一個良好的哈希函數可以盡量避免重復。x的集合是參數域,h(x)的集合是值域。
完美哈希函數(Perfect Hash Function)
完美哈希函數,就是完全不會沖突的哈希函數,這要求函數的值域不小於參數域
最小完美哈希函數(Minimal Perfect Hash Function)
最小完美哈希函數,就是指函數的值域和參數域的大小完全相等,一個也不多
保序最小完美哈希函數(Order Preserving Minimal Perfect Hash Function)
保序的意思就是指這個哈希之后順序是不變的,同時還能滿足其他兩個條件。可以這樣理解原來在前面的元素,哈希之后它也在前面。就和排序的穩定性是一個意思哦喵~
分析
對於一個給定的數據集,我們可以構造出一個保序最小完美哈希函數(OPMPHF)
我們定義該函數為h(t):(+n:在n的模域下進行加法)
其中函數g(x)是需要我們去構造的,另兩個函數h1(t)與h2(t)就是一般的哈希函數了。
現在我們的目的就是去構造出一個函數g(x)使得h(t)成為一個保序最小完美哈希函數了喵~
一種形像的理解就是:
第0個元素t0經過h(t)后輸出就為0
第1個元素t1經過h(t)后輸出就為1
第x個元素tx經過h(t)后輸出就為x
那么具體怎喵構造這個函數呢?
看上面那個式子,我們對於每個元素tx,我們已經通過兩個哈希函數得到了它的兩個哈希值,然后我們建一個圖:
對於有n個元素的序列,這個圖里有n條邊分別對應每個數據。邊權值對應該元素在原序列中的序號,邊的兩個端點為該元素經過兩個哈希函數之后的值。
即E(h1(tx), h2(tx)) = x
正如大家所看到的一樣,如果我們給每個點標記一個權值g(x),對於邊E(a,b)
我們要求的就是使得g(x)對於所有邊,滿足g(a) +n g(b) == E(a,b)
對於一個一般圖點標號問題來說,確實比較麻煩,但如果這個圖有一個很好性質:無環。那么標號就變得異常的簡單了喵~
所以如果你不幸建個了一個有環圖,那就重新選兩個哈希函數再建一次圖,直到建成無環圖為止喵~這一句話看上去不怎喵靠譜吧喵~要建多少次才會出現無環圖呢?我也覺得不怎喵靠譜的樣子喵,可人家證明這樣建圖是可以接受的。
然后剩下的問題就比較簡單了喵~遍歷圖中所有頂點,當遍歷到未標號頂點時,就以該點為根遍歷該子樹,並標號。(無環圖就可以看作森林了喵~)
算法綜述(生成一個完美哈希函數):
1、隨機選兩個哈希函數
2、生成一個圖G=(V,E)
3、判環與計算映射函數g(x)
4、如果未得成功計算出g(x)(有環存在且破壞了g(x)函數),則重新執行該算法。
生成無環圖的期望次數
參考:
《深入搜索引擎》(Managing Gigabytes即MG)