这个SuperFashHash实现是否正在计算字符串的正确31位哈希值?

时间:2012-10-09 19:09:27

标签: algorithm vba hash excel-vba excel

我正在VBA中实现SuperFastHash的变体,以便在Excel(32位版本,因此没有LongLong可用)中使用哈希字符串。

为了解决有符号32位Long值的限制,我正在使用Double类型进行加法和位移,然后以一种截断31位的方式从Double转换为Long(最大值为正)价值 - 不想处理两个补码和符号)。

到目前为止,我得到的答案和避免溢出,但我怀疑我在翻译中犯了一些错误,因为大多数实现使用uint的所有32位并且还处理来自数组的单个字节而不是16 -bit值来自AscW()。

实施的具体部分我希望有人可以直接检查:

  1. 我如何测试16位字符而不是4字节块。
  2. 我的位移操作在技术上是否正确,但需要注意的是我需要截断31位的Long值。
  3. 鉴于散列仅使用31位,最终的雪崩片是否仍然合适。
  4. 这是当前的代码:

    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
    

1 个答案:

答案 0 :(得分:1)

我建议不要乱用双打,你可能最好将32位字分成两个“16位”部分,每个部分都保存在一个带符号的32位变量中(使用每个变量的低16位,然后“规范化”步骤之间的值:

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

向左移动16个位置只是意味着将低部分复制到高部分并将低部分归零。向右移动16个位置只是意味着将高部分复制到低部分并将高部分归零。向左移动较少数量的地方只是意味着将两个部件分开移动然后进行标准化。将标准化数字移位到较小数量的位置意味着将两个部分向左移位(16-N),标准化并向右移位16位。