常用的方法有三種:直接取餘法、乘積取整法、平方取中法。下面我們對這三種方法分別進行討論。以下假定我們的關鍵字是,hash表的容量是,hash函式為 。
1.直接取餘法
我們用關鍵字 除以 ,取餘數作為在hash表中的位置。函式表示式可以寫成:
例如,表容量 ,關鍵值 ,那麼 。該方法的好處是實現容易且速度快,是很常用的一種方法。但是如果選擇的不好而偏偏標本又很特殊,那麼資料在hash中很容易扎堆而影響效率。
對於 的選擇,在經驗上,我們一般選擇不太接近的乙個素數;如果關鍵字的值域較小,我們一般在此值域1.1~1.6倍範圍內選擇。例如的值域為,那麼即為乙個不錯的選擇。競賽的時候可以寫乙個素數生成器或乾脆自己寫乙個「比較畫素數」的數。
我用4000個數插入乙個容量為701的hash表,得到的結果是:
測試資料
隨機資料
連續資料
最小單元容量:
最大單元容量:
期望容量:
5.70613
5.70613
標準差:
2.4165
0.455531
可見對於隨機資料,取餘法的最大單元容量達到了期望容量的將近3倍。經測試,在我的機器(pentium iii 866mhz,128mb ram)上,該函式的執行時間大約是39ns,即大約35個時鐘週期。
2.乘積取整法
我們用關鍵字 乘以乙個在 中的實數 (最好是無理數),得到乙個之間的實數;取出其小數部分,乘以,再取整數部分,即得在hash表中的位置。函式表示式可以寫成:
其中 表示 的小數部分,即。例如,表容量,種子(是乙個實際效果很好的選擇),關鍵值,那麼。
同樣用4000個數插入乙個容量為701的hash表(),得到的結果是:
測試資料
隨機資料
連續資料
最小單元容量:
最大單元容量:
期望容量:
5.70613
5.70613
標準差:
2.5069
0.619999
從公式中可以看出,這個方法受 的影響是很小的,在的值比較不適合直接取餘法的時候這個方法的表現很好。但是從上面的測試來看,其表現並不是非常理想,且由於浮點運算較多,執行速度較慢。經反覆優化,在我的機器上仍需892ns才能完成一次計算,即810個時鐘週期,是直接取餘法的23倍。
3.平方取中法
我們把關鍵字 平方,然後取中間的位作為hash函式值返回。由於的每一位都會對其平方中間的若干位產生影響,因此這個方法的效果也是不錯的。但是對於比較小的值效果並不是很理想,實現起來也比較繁瑣。為了充分利用hash表的空間,最好取2的整數次冪。例如,表容量,關鍵值,那麼。
用4000個數插入乙個容量為512的hash表(注意這裡沒有用701,是為了利用hash表的空間),得到的結果是:
測試資料
隨機資料
連續資料
最小單元容量:
最大單元容量:
期望容量:
7.8125
7.8125
標準差:
2.95804
2.64501
效果比我們想象的要差,尤其是對於連續資料。但由於只有乘法和位運算,該函式的速度是最快的。在我的機器上,一次運算只需要23ns,即19個時鐘週期,比直接取餘法還要快一些。
比較一下這三種方法:
實現難度
實際效果
執行速度
其他應用
直接取餘法易好
較快字串
乘積取整法
較易較好
慢浮點數
平方取中法中較好
快無從這個**中我們很容易看出,直接取餘法的價效比是最高的,因此也是我們競賽中用得最多的一種方法。
對於實數的hash函式,我們可以直接利用乘積取整法;而對於標本為其他型別資料的hash函式,我們可以先將其轉換為整數,然後再將其插入hash表。下面我們來研究把其他型別資料轉換成整數的方法。
常用的字串hash函式還有elfhash,aphash等等,都是十分簡單有效的方法。這些函式使用位運算使得每乙個字元都對最後的函式值產生影響。另外還有以md5和sha1為代表的雜湊函式,這些函式幾乎不可能找到碰撞(md5前一段時間才剛剛被破解)。
我從mark twain的一篇**中分別隨機抽取了1000個不同的單詞和1000個不同的句子,作為短字串和長字串的測試資料,然後用不同的hash函式把它們變成整數,再用直接取餘法插入乙個容量為1237的hash表,遇到衝突則用新字串覆蓋舊字串。通過觀察最後「剩下」的字串的個數,我們可以粗略地得出不同的hash函式實際效果。
短字串
長字串
平均編碼難度
直接取餘數
671.5
易p. j. weinberger hash
679.5
難elf hash
679.5
較難sdbm hash
687.0
易bkdr hash
687.5
較易djb hash
688.5
較易ap hash
691.0
較難rs hash
692.0
較難js hash
696.0
較易把1000個隨機數用直接取餘法插入容量為1237的hash表,其覆蓋單元數也只達到了694,可見後面的幾種方法已經達到了極限,隨機性相當優秀。然而我們卻很難選擇,因為不存在完美的、既簡單又實用的解決方案。我一般選擇js hash或sdbm hash作為字串的hash函式。這兩個函式的**如下:
unsigned int jshash(char *str)
return (hash & 0x7fffffff);
}unsigned int sdbmhash(char *str)
return (hash & 0x7fffffff);
}
jshash的運算比較複雜,如果對效果要求不是特別高的話sdbmhash是乙個很好的選擇。
整數的Hash函式
常用的方法有三種 直接取餘法 乘積取整法 平方取中法。下面我們對這三種方法分別進行討論。以下假定我們的關鍵字是,hash表的容量是,hash函式為 1 直接取餘法 我們用關鍵字 除以 取餘數作為在hash表中的位置。函式表示式可以寫成 例如,表容量 關鍵值 那麼 該方法的好處是實現容易且速度快,是很...
整數判重 大整數Hash
總是聽高屆的大佬說,乙個字串hash,能搞出大部分的字串題,hash真的有那麼神嗎?答 是的 近來,參加很多noip模擬賽,其中一場設計判斷大整數是否存在偷懶了一下,開了乙個map原本能a的題t掉了三個點,oj上評測t3個點還跑了1480ms,忍痛改hash後發現a了只跑了356ms 對於整數的判重...
Hash函式和Hash衝突
2.rehash 3.鍊錶法 4.建立公共溢位區 一 簡介 將任意長度的數值以某個對映規則對映為固定長度的數值,這個過程稱為hash,而這個對映規則被稱為hash函式,而對這個key value進行儲存的資料結構被稱為hash表。由於通過key的hash對映直接得到了記憶體位址,所以hash查詢的時...