dww*_*n66 36 worksheet-function hashing microsoft-excel-2010
我正在处理许多以文档名称为键的数据列表。文档名称虽然非常具有描述性,但如果我需要查看它们就非常麻烦(最多 256 个字节是很多空间),我希望能够创建一个更小的键域,如果我需要,它可以很容易地重现VLOOKUP从另一个工作表或工作簿做一个。
我认为来自标题的哈希值对于每个标题都是唯一且可重复的,这将是最合适的。是否有可用的功能,或者我正在考虑开发自己的算法?
关于这个或其他策略的任何想法或想法?
nix*_*xda 39
您不需要编写自己的函数 - 其他人已经为您编写了。
例如,我在这个stackoverflow answer上收集并比较了五个 VBA 哈希函数
我个人使用这个 VBA 函数
=BASE64SHA1(A1)将宏复制到 VBA模块后,它会在 Excel 中调用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)
自定义哈希长度
Const cutoff As Integer = 5还有不需要 .NET 且不使用外部库的哈希函数(所有三个 CRC16 函数)。但是散列更长并且产生更多的冲突。
您也可以只下载此示例工作簿并尝试所有 5 个哈希实现。如您所见,第一张纸上有一个很好的比较
小智 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)的位置是任意的;使用任何东西。
这是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)
对于相当小的列表,您可以使用内置的 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)
所以长度是一个有意义的扰码器。
| 归档时间: |
|
| 查看次数: |
207490 次 |
| 最近记录: |