2012-10-09 62 views
0

我在Excel中使用VBA中的SuperFastHash變體(32位版本,因此沒有LongLong可用)來實現散列字符串。這是SuperFashHash實現計算字符串的正確31位散列嗎?

爲了解決已簽名的32位Long值的侷限性,我使用Double類型進行加法和位移,然後以一種將它截斷爲31位(最大值積極的價值 - 不想處理兩個補碼和標誌)。

我得到的答案和避免溢出到目前爲止,但我有一個懷疑,我在翻譯中犯了一些錯誤,因爲大多數實現使用一個uint的所有32位,也處理單個字節從數組而不是來自AscW()的16位值。

我希望有人能腸道檢查落實的具體部分:

  1. 我如何測試16位字符的話,而不是4個字節的塊。
  2. 無論我的位移操作在技術上是否正確,考慮到我需要以31位截斷Long值的警告。
  3. 鑑於哈希僅使用31位,最終的雪崩件是否仍然合適。

下面是當前的代碼:

Public Function shr(ByVal Value As Long, ByVal Shift As Byte) As Long 
    shr = Value 
    If Shift > 0 Then shr = shr \ (2^Shift) 
End Function 

Public Function shl(ByVal Value As Long, ByVal Shift As Byte) As Long 
    If Shift > 0 Then 
     shl = LimitDouble(CDbl(Value) * (2&^Shift)) 
    Else 
     shl = Value 
    End If 
End Function 

Public Function LimitDouble(ByVal d As Double) As Long 
    '' Prevent overflow by lopping off anything beyond 31 bits 
    Const MaxNumber As Double = 2^31 
    LimitDouble = CLng(d - (Fix(d/MaxNumber) * MaxNumber)) 
End Function 

Public Function SuperFastHash(ByVal dataToHash As String) As Long 
    Dim dataLength As Long 
    dataLength = Len(dataToHash) 
    If (dataLength = 0) Then 
     SuperFastHash = 0 
     Exit Function 
    End If 
    Dim hash As Long 
    hash = dataLength 
    Dim remainingBytes As Integer 
    remainingBytes = dataLength Mod 2 
    Dim numberOfLoops As Integer 
    numberOfLoops = dataLength \ 2 
    Dim currentIndex As Integer 
    currentIndex = 0 
    Dim tmp As Double 
    Do While (numberOfLoops > 0) 
     hash = LimitDouble(CDbl(hash) + AscW(Mid$(dataToHash, currentIndex + 1, 1))) 
     tmp = shl(AscW(Mid$(dataToHash, currentIndex + 2, 1)), 11) Xor hash 
     hash = shl(hash, 16) Xor tmp 
     hash = LimitDouble(CDbl(hash) + shr(hash, 11)) 
     currentIndex = currentIndex + 2 
     numberOfLoops = numberOfLoops - 1 
    Loop 
    If remainingBytes = 1 Then 
     hash = LimitDouble(CDbl(hash) + AscW(Mid$(dataToHash, currentIndex + 1, 1))) 
     hash = hash Xor shl(hash, 10) 
     hash = LimitDouble(CDbl(hash) + shr(hash, 1)) 
    End If 
    '' Final avalanche 
    hash = hash Xor shl(hash, 3) 
    hash = LimitDouble(CDbl(hash) + shr(hash, 5)) 
    hash = hash Xor shl(hash, 4) 
    hash = LimitDouble(CDbl(hash) + shr(hash, 17)) 
    hash = hash Xor shl(hash, 25) 
    hash = LimitDouble(CDbl(hash) + shr(hash, 6)) 
    SuperFastHash = hash 
End Function 
+0

沒有冒犯,但微型優化vba代碼有什麼意義?不會有簡單的散列嗎? –

+0

@AlexandreC。,這*是一個簡單的哈希算法。 VBA不包含內在散列函數,我寧願不重新發明輪子,所以保羅謝的算法看起來很合適。我發現MD5的現有VBA實現速度慢了10倍。 – richardtallent

+0

您可能想在[代碼評論](http://codereview.stackexchange.com/)上發佈此內容。 – Blastfurnace

回答

1

我會建議,而不是雙打亂搞,你可能會更好過分裂32位字爲兩個「16位」部分,其中的每一個在一個符號的32位變量所保持(使用低16位的每個變量的,然後在「正常化」步驟之間的值:

highPart = (highPart + (lowPart \ 65536)) and 65535 
lowPart = lowPart and 65535 

左移16個地方僅僅意味着複製低部分高部分和調零低部分。右移16位意味着將高位部分複製到低位部分並將高位部分調零。向左移動較少的位置就意味着分別移動兩個部分然後正常化。將標準化數字向右移動較少數量的位置意味着將兩個部分左移(16-N),正常化並右移16位。

+0

一個非常好的觀點 - 我之前正在考慮類似的東西,同時創建一個「UInt」類來封裝骯髒的細節。整數加布爾值也可以工作(儘管我同意兩個整數更好)。 我仍然覺得我可能會誤解原始算法,並且我應該分別處理每個Unicode字符的MSB/LSB。我希望這樣的事情可能會嚴重影響散列分佈。 – richardtallent

+0

@richardtallent:我對原始算法沒有足夠的瞭解來評論這個問題,但是會建議通過刪除MSB將32位算法摺疊爲31位算法通常不是一個好方法。在許多情況下,更好的辦法是讓它以某種方式影響其他比特,也許可以通過任意負數與正交於算法固有內容的任意負常數進行比較。作爲「uint」的設計,我認爲一對整數是要走的路,因爲大多數操作都可以在沒有溢出的情況下單獨完成。 – supercat

+0

@richardtallent:唯一不能通過執行'粗糙'然後正常化來執行的操作是乘法(因爲65535 * 65535超過了vba'Integer'的範圍)和長分區(這往往是有點痛苦的事關你如何切片)。通過轉換爲「Double」,進行數學運算並轉換回來,可以做得更好。當X和Y至少爲32768時,可以通過識別X * Y = 65536 * Y +(X-65536)* Y來完成全範圍16位值X和Y的乘法運算。 – supercat