哈希取餘法、哈希表大小取質數的問題

原文地址php


哈希取餘法、哈希表大小取質數的問題

  1. hashing | planetmath.org http://planetmath.org/node/33326
  2. good hash table primes | planetmath.org http://planetmath.org/goodhashtableprimes
  3. 哈希函數取餘法除數爲什麼要取質數? - SegmentFault http://segmentfault.com/q/1010000000593741
  4. 爲什麼哈希函數取餘法要避免2的冪? - SegmentFault http://segmentfault.com/q/1010000000593556
  5. 哈希表詳解 - 承續緣的信仰 - 博客頻道 - CSDN.NET http://blog.csdn.net/liangbopirates/article/details/9753599

  6. 以前一直好奇,爲何哈希表的大小須要取質數,並且還要取最好遠離2的冪次方的質數?
    任意一個數x mod m的值,與m的值應該沒有什麼關係吧。
    看了上面一些講的,本身總結一下。

    第一個連接planetmath講的hashing裏面,我以爲說的挺對的,哈希函數的實質,就是把一個鍵值空間爲K的映射到hashtable的空間T中。
    hash函數的設置有兩個主要的要求:
    1.hash的計算要快,效率要高,即hash的計算公式要比較簡單,不要太複雜
    2.hash的衝突率要低
    這裏它說了兩種hash函數的構造方法,一個是除,一個是乘:h(k)=f(k)(modn);h(k)=⌊n⋅((f(k)⋅r)(mod1))⌋,r是0到1的數。
    雖然都想要那種可以one to one的完美hash函數,不過通常很難辦到,因此就有hash衝突,hash衝突就是兩個不一樣的鍵值k1,k2,
    可是h(k1)=h(k2),這就形成衝突。
    這裏咱們定義一個負載因子:l=t/h,其中t是表明hashtable中已經被填好的槽,h是hashtable的總槽數。
    很明顯,當l接近於1的時候,衝突是不可避免的。
    爲了不衝突的話,有三種解決方案:
    1.把hashtable的大小開的足夠大,比鍵值空間K的範圍大得多
    2.採用拉鍊法
    3.選擇一個完美hash函數,是one to one的。
    考慮option 1,設hash表中實際被填滿的槽爲A,A是K的子集,則有|A|<c|T|,實驗效果發現,c取1/2到3/4
    之間比較好。
    考慮option 2,拉鍊法就沒有衝突不衝突了,若是衝突了,直接再申請一塊內存空間存下來就行了。
    考慮option 3,所謂的完美one to one的hash函數,其實主要是由目標數據和可用內存空間兩個一塊兒決定的
    舉個簡單例子,h(k)=k確定是一個完美hash,不過你的內存是否是可以把它們都保存下來就不必定了。
    而後提了兩種hash:
    1.接近完美hash的函數,能夠把n個映射到n個,沒有衝突,這個須要好好構造。
    2.可以直接進行排序的,若是k1<k2,就有h(k1)<h(k2),這樣至關於節約了排序的時間消耗o(nlog(n)),當K
    小於T的時候是可行的,不過因爲這樣確定會在hash函數中增長額外的計算,這樣無形中對hash的效率是有
    必定影響的。

    第二個連接主要是對hash表的大小取質數進行了一個簡單的介紹。
    它提供了一個hash表大小取值list,list裏面的書都有3個特色:
    1.都是質數
    2.都是前一個質數的2倍小一點點
    3.都是遠離2的冪次方的數
    至於爲何這麼選的緣由
    第一個質數,從第三個和第四個連接裏面看的話,我的以爲滕亦飛的答案比較正確。其實若是鍵值的取值是均勻的,沒有什麼特色,當K<T的時候,其實無論T的取值是多少,都是最優的,機率都是平均的。不過若是當K>T了,那麼就是他分析的那樣了,只要是K,T的公因子的那個就會比較吃香,映射過去的就比較多。因此天然是選擇質數,機率範圍最大了。
    第二個要是2倍小一點點,主要是考慮到數據量的增大,那麼,這麼選取比較方便
    第三個就純粹說的是allegedly,這樣的實驗效果比較好的緣故了。

    第三個連接裏面說,單純取餘的哈希運算很糟糕。「遠離2^i的質數」、「接近2^i的質數」、「剛好等於2^i」都只不過是「沒有那麼差」、「確實不好」和「差到不能再差」的區別。
    我的以爲說得不對,仍是根據以前的分析,若是說由於電腦是由二進制來表明數字,就說2的冪次方不適合作哈希表的大小,或者說效果不好,緣由不是徹底不是由於是二進制,2的冪次方,而是由於他是合數,而且數據自己分佈不均勻致使的。
    若是數據分佈均勻,其實不論是2的冪次方仍是3的冪次方都會是同樣的結果。
    還有就是第四個連接裏面說的,感受一部分說的挺對的。
    不過裏面也說,首先不能選擇2的倍數,緣由是這樣至關於把數字的高位截斷了,若是數字恰巧只有高位部分,那就悲劇了。從某種程度上說,感受說得挺對的,不過和他本身以前的理論就有點兒矛盾。原本他本身就說選啥都OK,結果如今又說選2的冪次方有問題,這個我不能接受。
    我以爲根本性的問題仍是在於他自己是一個質數上。
    由於所謂的高位截斷,其實就是說若是選擇的是m大小的,那麼k*m的都會被映射到同一個位中,這是很正常的一件事,由於你不能說計算機選擇什麼進制就不能以該數的冪次方來做爲哈希表大小。
    固然我認可,單純取餘的哈希運算很糟糕,可是做爲映射到哈希表中的最後一步填槽運算的時候,其實影響不大了,可能就和連接1,2裏面說的那樣,只要是個質數就行,只不過實驗效果來看,原理2的冪次方的質數效果稍微好點兒。
    其實可能還有一個理由,有些哈希函數對字符串處理的時候,採用的就是ANSI編碼或者什麼編碼方式,這種編碼方式的話,可能弄出來的字符串轉成整數時,出現2的冪次方的可能性比較大,數據分佈很不均勻致使的。

    綜上所述:
    1.單純取餘哈希算法效果很差的緣由主要是由於數據自己分佈不均勻致使的,在數據分佈均勻的狀況下,無論你取的是質數仍是合數,對於最後的結果都沒有影響,只要你的數據是平均分佈在0-k*m的區間範圍之間的。
    2.不過一個好的哈希函數,就是須要在各類不一樣的數據分佈狀況下,其效果是最優的。考慮到這一點,哈希取餘算法天然最好是一個質數,由於根據連接四滕亦飛裏面他推導的公式,能夠知道,做爲質數,最後求出來的值的值域最大,在0-m-1之間
    3.由2可知,2的冪次方就首先被排除了,不只是2的冪次方、3的冪次方,4的冪次方……都被排除了。可是,爲何不能取距離2的冪次方的質數、遠離2的冪次方的質數,這一點,我認爲是沒有理論依據的,只能說,根據實驗效果來看,遠離2的冪次方的質數效果比較好,因此選擇這個。
    4.上面3點說的都是做爲哈希函數的取餘算法的效果,然而做爲使用哈希表的,無論你以前作了什麼哈希函數的映射,到最後一步,存到哈希表中的時候,都會對哈希表的大小進行一個取餘的運算,使數據儘可能均勻分佈在槽裏面。由上面說的,若是你以前的哈希函數足夠好,把數據量都均勻分佈了,那選啥都沒問題;但爲了防止以前的哈希不夠好,天然哈希表的大小開具爲一個質數比較OK,並且根據實驗效果來看,開具成一個儘可能遠離2的冪次方的哈希表大小比較OK。

若是有說錯的地方歡迎你們批評指正!html

相關文章
相關標籤/搜索