整數的Hash函數

來源:互聯網
上載者:User
一、整數的Hash函數

常用的方法有三種:直接取餘法、乘積取整法、平方取中法。下面我們對這三種方法分別進行討論。以下假定我們的關鍵字是,Hash表的容量是,Hash函數為
。

1.直接取餘法

我們用關鍵字 除以
,取餘數作為在Hash表中的位置。函數運算式可以寫成:

例如,表容量 ,關索引值
,那麼 。該方法的好處是實現容易且速度快,是很常用的一種方法。但是如果選擇的不好而偏偏標本又很特殊,那麼資料在Hash中很容易紮堆而影響效率。

對於 的選擇,在經驗上,我們一般選擇不太接近的一個素數;如果關鍵字的範圍較小,我們一般在此範圍1.1~1.6倍範圍內選擇。例如的範圍為,那麼即為一個不錯的選擇。競賽的時候可以寫一個素數產生器或乾脆自己寫一個“比較像素數”的數。

我用4000個數插入一個容量為701的Hash表,得到的結果是:

測試資料

隨機資料

連續資料

最小單元容量:

0

5

最大單元容量:

15

6

期望容量:

5.70613

5.70613

標準差:

2.4165

0.455531

可見對於隨機資料,取餘法的最大單元容量達到了期望容量的將近3倍。經測試,在我的機器(Pentium III 866MHz,128MB RAM)上,該函數的已耗用時間大約是39ns,即大約35個刻度。

2.乘積取整法

我們用關鍵字 乘以一個在
中的實數 (最好是無理數),得到一個之間的實數;取出其小數部分,乘以,再取整數部分,即得在Hash表中的位置。函數運算式可以寫成:

其中 表示 的小數部分,即。例如,表容量,種子(是一個實際效果很好的選擇),關索引值,那麼。

同樣用4000個數插入一個容量為701的Hash表(),得到的結果是:

測試資料

隨機資料

連續資料

最小單元容量:

0

4

最大單元容量:

15

7

期望容量:

5.70613

5.70613

標準差:

2.5069

0.619999

從公式中可以看出,這個方法受 的影響是很小的,在的值比較不適合直接取餘法的時候這個方法的表現很好。但是從上面的測試來看,其表現並不是非常理想,且由於浮點運算較多,運行速度較慢。經反覆最佳化,在我的機器上仍需892ns才能完成一次計算,即810個刻度,是直接取餘法的23倍。

3.平方取中法

我們把關鍵字 平方,然後取中間的位作為Hash函數值返回。由於的每一位都會對其平方中間的若干位產生影響,因此這個方法的效果也是不錯的。但是對於比較小的值效果並不是很理想,實現起來也比較繁瑣。為了充分利用Hash表的空間,最好取2的整數次冪。例如,表容量,關索引值,那麼。

用4000個數插入一個容量為512的Hash表(注意這裡沒有用701,是為了利用Hash表的空間),得到的結果是:

測試資料

隨機資料

連續資料

最小單元容量:

0

1

最大單元容量:

17

17

期望容量:

7.8125

7.8125

標準差:

2.95804

2.64501

效果比我們想象的要差,尤其是對於連續資料。但由於只有乘法和位元運算,該函數的速度是最快的。在我的機器上,一次運算只需要23ns,即19個刻度,比直接取餘法還要快一些。

比較一下這三種方法:

實現難度

實際效果

運行速度

其他應用

直接取餘法

易

好

較快

字串

乘積取整法

較易

較好

慢

浮點數

平方取中法

中

較好

快

無

從這個表格中我們很容易看出,直接取餘法的性價比是最高的,因此也是我們競賽中用得最多的一種方法。

對於實數的Hash函數,我們可以直接利用乘積取整法;而對於標本為其他類型資料的Hash函數,我們可以先將其轉換為整數,然後再將其插入Hash表。下面我們來研究把其他類型資料轉換成整數的方法。

二、字串的Hash函數

字串本身就可以看成一個256進位(ANSI字串為128進位)的大整數,因此我們可以利用直接取餘法,線上性時間內直接算出Hash函數值。為了保證效果,仍然不能選擇太接近的數;尤其是當我們把字串看成一個進位數的時候,選擇會使得該字串的任意一個排列的Hash函數值都相同。(想想看,為什嗎?)

常用的字串Hash函數還有ELFHash,APHash等等,都是十分簡單有效方法。這些函數使用位元運算使得每一個字元都對最後的函數值產生影響。另外還有以MD5和SHA1為代表的雜湊函數,這些函數幾乎不可能找到碰撞(MD5前一段時間才剛剛被破解)。

我從Mark Twain的一篇小說中分別隨機抽取了1000個不同的單詞和1000個不同的句子,作為短字串和長字串的測試資料,然後用不同的Hash函數把它們變成整數,再用直接取餘法插入一個容量為1237的Hash表,遇到衝突則用新字串覆蓋舊字串。通過觀察最後“剩下”的字串的個數,我們可以粗略地得出不同的Hash函數實際效果。

短字串

長字串

平均

編碼難度

直接取餘數

667

676

671.5

易

P. J. Weinberger Hash

683

676

679.5

難

ELF Hash

683

676

679.5

較難

SDBM Hash

694

680

687.0

易

BKDR Hash

665

710

687.5

較易

DJB Hash

694

683

688.5

較易

AP Hash

684

698

691.0

較難

RS Hash

691

693

692.0

較難

JS Hash

684

708

696.0

較易

把1000個隨機數用直接取餘法插入容量為1237的Hash表,其覆蓋單元數也只達到了694,可見後面的幾種方法已經達到了極限,隨機性相當優秀。然而我們卻很難選擇,因為不存在完美的、既簡單又實用的解決方案。我一般選擇JS Hash或SDBM
Hash作為字串的Hash函數。這兩個函數的代碼如下:

unsigned int JSHash(char *str){unsigned int hash = 1315423911; // nearly a prime - 1315423911 = 3 * 438474637while (*str){hash ^= ((hash << 5) + (*str++) + (hash >> 2));}return (hash & 0x7FFFFFFF);}unsigned int SDBMHash(char *str){unsigned int hash = 0;while (*str){// equivalent to: hash = 65599*hash + (*str++);hash = (*str++) + (hash << 6) + (hash << 16) - hash;}return (hash & 0x7FFFFFFF);}

JSHash的運算比較複雜,如果對效果要求不是特別高的話SDBMHash是一個很好的選擇。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.