2009-11-30 72 views
9

我正在編寫一個程序,它會生成四個無符號的32位整數作爲某個函數的輸出。我想散列這四個整數,所以我可以將這個函數的輸出與未來的輸出進行比較。四個無符號整數的散列函數(C++)

雖然寫了一個像樣的哈希函數,但我遇到了麻煩。當我最初編寫這段代碼時,我簡單地添加了四個整數中的每一個,我知道這些不足。我嘗試了其他一些技巧,比如移動和添加,但都無濟於事。我得到了一個散列,但質量很差,並且該函數產生了大量的衝突。

散列輸出可以是32位或64位整數。這個函數產生了數十億個哈希,所以碰撞在這裏是一個真正的問題,我願意使用一個更大的變量來確保儘可能少的碰撞。

任何人都可以幫我弄清楚如何編寫一個高質量的散列函數?

+0

「我想散列這四個整數,所以我可以將此函數的輸出與未來的輸出進行比較。」不一定遵循。如果你正在測試一個輸出字符串的函數,你不必爲了做迴歸測試而散列到32或64位。在你的情況下,你爲了節省50%的存儲空間而讓自己頭痛(假設你使用64位而不是128位)。這值得麼?你有嘗試過使用gzip嗎? – 2009-11-30 15:16:00

+16

您是否考慮過使用以下一種或多種通用哈希函數:http://www.partow.net/programming/hashfunctions/index.html – 2009-12-19 07:29:32

回答

8

爲什麼不將四個整數存儲在合適的數據結構中並將它們全部進行比較?在這種情況下散列它們的好處對我來說似乎是可疑的,除非存儲是一個問題。

如果存儲是問題,您可以使用分析的其中一個散列函數here

3

由於哈希可能會產生衝突,因此您必須將密鑰保存在內存中才能發現這些衝突。 HashMap和其他標準數據結構在內部簿記中確實這樣做。由於密鑰太小,只需直接使用密鑰而不是散列。這將更快,並確保不會發生碰撞。

0

爲什麼要散列?它看起來像一個std :: set或std :: multi集合更適合存儲這種輸出。所有你需要做的就是將四個整數包裝在一個結構中,並寫一個簡單的比較函數。

0

嘗試使用CRCFNV。 FNV很好,因爲它速度很快,並且有一個定義的摺疊位方法來獲得「較小」的散列值(即12位/ 24位/等)。

從128位(4 X 32位)數生成64位散列的好處有點有問題,因爲正如其他人所建議的,您可以將原始值用作關鍵字組。你真的希望散列中的位數代表你最初擁有的值的數量。例如,如果您的數據集具有100,000個4X32位值,則可能需要17位或18位散列值,而不是64位散列值。

0

可能有點矯枉過正,但考慮Boost.Hash。生成非常簡單的代碼和很好的值。

1

我完全同意溫科 - 只是比較它們。如果你仍然想要一個好的散列函數,你需要分析你的4個不帶整數的整數的分佈。然後你必須以某種方式來製作你的散列函數,結果將在32位哈希值的整個範圍內均勻分佈。

一個簡單的例子 - 我們姑且認爲大部分的時間,從每個功能的結果是從0到255的範圍內,那麼你可以很容易地從每個功能的低8位地融入你的哈希值。大多數時候,你會直接查找結果,有時(當一個函數返回更大的結果時)會碰撞。總結一下 - 沒有信息如何分配4個函數的結果,我們不能幫助你獲得一個好的散列函數。

4

這裏的一個相當合理的散列函數從4個整數1個整數:

unsigned int hash = in[0]; 
hash *= 37; 
hash += in[1]; 
hash *= 37; 
hash += in[2]; 
hash *= 37; 
hash += in[3]; 

隨着均勻分佈的輸入它給均勻分佈的輸出。輸入的所有位都參與輸出,並且每個輸入值(儘管不是每個輸入位)都會影響每個輸出位。有機會比產生輸出的函數更快,在這種情況下,沒有性能問題。

還有其他散列具有其他特徵,但是除了經過證明,否則累積乘法是一個好的開始。如果你喜歡,你可以嘗試用xor來累積而不是增加。不管怎樣,很容易產生衝突(例如{1,0,a,b}與所有a,b)的{0,37,a,b}碰撞,所以你可能想要選擇一個你認爲具有的素數與您的函數中任何可能的實現錯誤無關。所以如果你的函數中有很多modulo-37算術,可以用1000003代替。