是否有用于创建哈希值的 Excel 函数?

dww*_*n66 36 worksheet-function hashing microsoft-excel-2010

我正在处理许多以文档名称为键的数据列表。文档名称虽然非常具有描述性,但如果我需要查看它们就非常麻烦(最多 256 个字节是很多空间),我希望能够创建一个更小的键域,如果我需要,它可以很容易地重现VLOOKUP从另一个工作表或工作簿做一个。

我认为来自标题的哈希值对于每个标题都是唯一且可重复的这将是最合适的。是否有可用的功能,或者我正在考虑开发自己的算法?

关于这个或其他策略的任何想法或想法?

nix*_*xda 39

您不需要编写自己的函数 - 其他人已经为您编写了。
例如,我在这个stackoverflow answer上收集并比较了五个 VBA 哈希函数

我个人使用这个 VBA 函数

  • =BASE64SHA1(A1)将宏复制到 VBA模块后,它会在 Excel 中调用
  • 需要 .NET,因为它使用库“Microsoft MSXML”(具有后期绑定)

Public Function BASE64SHA1(ByVal sTextToHash As String)

    Dim asc As Object
    Dim enc As Object
    Dim TextToHash() As Byte
    Dim SharedSecretKey() As Byte
    Dim bytes() As Byte
    Const cutoff As Integer = 5

    Set asc = CreateObject("System.Text.UTF8Encoding")
    Set enc = CreateObject("System.Security.Cryptography.HMACSHA1")

    TextToHash = asc.GetBytes_4(sTextToHash)
    SharedSecretKey = asc.GetBytes_4(sTextToHash)
    enc.Key = SharedSecretKey

    bytes = enc.ComputeHash_2((TextToHash))
    BASE64SHA1 = EncodeBase64(bytes)
    BASE64SHA1 = Left(BASE64SHA1, cutoff)

    Set asc = Nothing
    Set enc = Nothing

End Function

Private Function EncodeBase64(ByRef arrData() As Byte) As String

    Dim objXML As Object
    Dim objNode As Object

    Set objXML = CreateObject("MSXML2.DOMDocument")
    Set objNode = objXML.createElement("b64")

    objNode.DataType = "bin.base64"
    objNode.nodeTypedValue = arrData
    EncodeBase64 = objNode.text

    Set objNode = Nothing
    Set objXML = Nothing

End Function
Run Code Online (Sandbox Code Playgroud)

自定义哈希长度

  • 哈希最初是一个 28 个字符长的 unicode 字符串(区分大小写 + 特殊字符)
  • 您可以使用以下行自定义哈希长度: Const cutoff As Integer = 5
  • 4 位哈希 = 6895 行中的 36 次冲突 = 0.5 % 冲突率
  • 5 位哈希 = 6895 行中的 0 次冲突 = 0 % 冲突率

还有不需要 .NET 且不使用外部库的哈希函数(所有三个 CRC16 函数)。但是散列更长并且产生更多的冲突。

您也可以只下载此示例工作簿并尝试所有 5 个哈希实现。如您所见,第一张纸上有一个很好的比较

  • 不久前,但该文件不再可用。谁能再次提供它吗?谢谢。 (2认同)

小智 15

我不太关心冲突,但需要一个基于可变长度字符串字段的弱伪随机化行。这是一个运行良好的疯狂解决方案:

=MOD(MOD(MOD(MOD(MOD(IF(LEN(Z2)>=1,CODE(MID(Z2,1,1))+10,31),1009)*IF(LEN(Z2)>=3,CODE(MID(Z2,3,1))+10,41),1009)*IF(LEN(Z2)>=5,CODE(MID(Z2,5,1))+10,59),1009)*IF(LEN(Z2)>=7,CODE(MID(Z2,7,1))+10,26),1009)*IF(LEN(Z2)>=9,CODE(MID(Z2,9,1))+10,53),1009)
Run Code Online (Sandbox Code Playgroud)

Z2包含要散列的字符串的单元格在哪里。

“MOD”是为了防止溢出到科学记数法。1009是素数,可以使用任何 X 使得 X*255 < max_int_size。10 是任意的;使用任何东西。“其他”值是任意的(这里是 pi 的数字!);使用任何东西。字符(1、3、5、7、9)的位置是任意的;使用任何东西。

  • 老实说,这是最简单的答案,我怀疑冲突是大多数 excel 用例的问题。 (3认同)

Jas*_*ban 8

这是32 位的FNV-1a作为单个 Excel 公式,其中单元格 A1 包含您要散列的字符串:

=LET(
  Y, LAMBDA(G,a,b,m,res,
    IF(0<b,
      G(
        G,
        MOD(a, m) * 2,
        TRUNC(b / 2),
        m,
        IF(MOD(b, 2) = 1,
          MOD(res + MOD(a, m), m),
          res)),
      res)),
  mulmod, LAMBDA(a,b,m, Y(Y,a,b,m,0)),
  p, 16777619    +N("FNV_prime for 32 bits"),
  o, 2166136261  +N("FNV_offset_basis for 32 bits"),
  m, POWER(2,32) +N("modulus for 32 bits"),
  s, A1,
  IF(ISBLANK(s),
    0,
    REDUCE(o,
      ROW(INDIRECT("1:"&LEN(s))),
      LAMBDA(acc,i,mulmod(p, BITXOR(acc, CODE(MID(s,i,1))), m)))))

Run Code Online (Sandbox Code Playgroud)

我防止递归 mulmod中的算术溢出。 a * 2不会溢出,因为m == MAX_UNIT == 4294967295虽然excel 使用双精度数进行数学计算,但它们在9,007,199,254,740,993以内是安全的

REDUCE(...)术语括起来DEC2HEX()以获得典型的十六进制输出。

Examples:

"BAD" => 2775452120 or A56E09D8 (in hex)
"DAB" => 772135228 or 2E05D93C
"ab" => 1294271946 or 4D2505CA
"AB" => 752165258 or 2CD5218A
"" => 0
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." => 2080740573 or 7C0594DD
Run Code Online (Sandbox Code Playgroud)


Ass*_*him 5

对于相当小的列表,您可以使用内置的 Excel 函数创建一个加扰器(穷人的哈希函数)。

例如

 =CODE(A2)*LEN(A2) + CODE(MID(A2,$A$1,$B$1))*LEN(MID(A2,$A$1,$B$1))
Run Code Online (Sandbox Code Playgroud)

这里 A1 和 B1 保存一个随机的起始字母和字符串长度。

稍微摆弄和检查,在大多数情况下,您可以很快获得一个可行的唯一 ID。

工作原理:该公式使用字符串的第一个字母和取自字符串中间的固定字母,并使用 LEN() 作为“扇形函数”以减少冲突的机会。

CAVEAT:这不是散列,但是当您需要快速完成某事并且可以检查结果以查看没有冲突时,它运行得非常好。

编辑: 如果您的字符串应该具有可变长度(例如全名)但从具有固定宽度字段的数据库记录中提取,您将需要这样做:

 =CODE(TRIM(C8))*LEN(TRIM(C8))
       +CODE(MID(TRIM(C8),$A$1,1))*LEN(MID(TRIM(C8),$A$1,$B$1))
Run Code Online (Sandbox Code Playgroud)

所以长度是一个有意义的扰码器。

  • 要*“检查结果以确保没有冲突”*,您可以简单地通过运行“数据”&gt;“删除重复项”来尝试/测试它,看看是否有任何冲突。[显然/大概,如果您确实遇到重复项,您可以迭代地重新运行上述函数,直到不留下重复项] (2认同)