Хэш-строка кода VBA

Как получить короткий хэш длинной строки с помощью Excel VBA

учитывая, что

  • входная строка не длиннее 80 символов
  • допустимы символы: [0..9] [A_Z] . _ /
  • допустимые символы вывода [0..9] [A_Z] [a_z] (нижний и верхний регистр можно использовать)
  • выходной хэш не должен быть длиннее ~12 символов (короче даже лучше)
  • не нужно быть уникальным вообще, так как это приведет к слишком длинному хэшу

что я сделал до сих пор

Я думал это так ответ является хорошим началом, так как он генерирует 4-значный шестнадцатеричный код (CRC16).

но 4 цифры были маленькие. В моем тесте с 400 строками 20% получили дубликат где-то еще.
Вероятность столкновения слишком высока.

Sub tester()
    For i = 2 To 433
        Cells(i, 2) = CRC16(Cells(i, 1))
    Next i
End Sub


Function CRC16(txt As String)
Dim x As Long
Dim mask, i, j, nC, Crc As Integer
Dim c As String

Crc = &HFFFF

For nC = 1 To Len(txt)
    j = Val("&H" + Mid(txt, nC, 2))
    Crc = Crc Xor j
    For j = 1 To 8
        mask = 0
        If Crc / 2 <> Int(Crc / 2) Then mask = &HA001
        Crc = Int(Crc / 2) And &H7FFF: Crc = Crc Xor mask
    Next j
Next nC

CRC16 = Hex$(Crc)
End Function

как воспроизвести

вы можете скопировать эти 400 тестовые строки из pastebin.
Вставьте их в столбец A в новой книге Excel и выполните приведенный выше код.

Q: как получить строковый хэш, который достаточно короткий (12 символов) и достаточно длинный, чтобы получить небольшой процент дубликатов.

4 ответов


разделите строку на три более короткие строки (если не делится на три, последний будет длиннее, чем два других). Запустите свой" короткий " алгоритм на каждом и объедините результаты.

я мог бы написать код, но судя по качеству вопроса, я думаю, вы можете взять его отсюда!

EDIT: оказывается, этого совета недостаточно. В вашем оригинальном коде CRC16 есть серьезный недостаток-а именно строка, которая говорит:

j = Val("&H" + Mid(txt, nC, 2))

это обрабатывает только текст, который можно интерпретировать как шестнадцатеричные значения: строчные и прописные буквы одинаковы, и все, что после Ф в алфавите игнорируется (насколько я могу судить). То, что все хорошее выходит наружу-это чудо. Если вы замените строку с

j = asc(mid(txt, nC, 1))

вещи работать лучше - каждый код ASCII, по крайней мере, начинает жизнь как свою собственную ценность.

комбинируя это изменение с предложением, которое я сделал ранее, вы получаете следующий код:

Function hash12(s As String)
' create a 12 character hash from string s

Dim l As Integer, l3 As Integer
Dim s1 As String, s2 As String, s3 As String

l = Len(s)
l3 = Int(l / 3)
s1 = Mid(s, 1, l3)      ' first part
s2 = Mid(s, l3 + 1, l3) ' middle part
s3 = Mid(s, 2 * l3 + 1) ' the rest of the string...

hash12 = hash4(s1) + hash4(s2) + hash4(s3)

End Function

Function hash4(txt)
' copied from the example
Dim x As Long
Dim mask, i, j, nC, crc As Integer
Dim c As String

crc = &HFFFF

For nC = 1 To Len(txt)
    j = Asc(Mid(txt, nC)) ' <<<<<<< new line of code - makes all the difference
    ' instead of j = Val("&H" + Mid(txt, nC, 2))
    crc = crc Xor j
    For j = 1 To 8
        mask = 0
        If crc / 2 <> Int(crc / 2) Then mask = &HA001
        crc = Int(crc / 2) And &H7FFF: crc = crc Xor mask
    Next j
Next nC

c = Hex$(crc)

' <<<<< new section: make sure returned string is always 4 characters long >>>>>
' pad to always have length 4:
While Len(c) < 4
  c = "0" & c
Wend

hash4 = c

End Function

вы можете поместить этот код в электронную таблицу как =hash12("A2") etc. Для удовольствия вы также можете использовать" новый, улучшенный " алгоритм hash4 и посмотреть, как они сравниваются. Я создал сводную таблицу для подсчета столкновений-для hash12 алгоритм, и только 3 для hash4. Я уверен, что вы можете понять, как создать hash8, ... из этого. "Не нужно быть уникальным" из вашего вопроса предполагает, что возможно "улучшить" hash4 все, что вам необходимость.

в принципе, четырехсимвольный hex должен иметь 64K уникальных значений-так что вероятность двух случайных строк с одинаковым хэшем будет 1 в 64k. Когда у вас есть 400 строк, есть 400 x 399 / 2 "возможные пары столкновений" ~ 80k возможностей (предполагая, что у вас были очень случайные строки). Таким образом, наблюдение трех столкновений в образце набора данных не является необоснованным. По мере увеличения количества строк N вероятность столкновений увеличивается как квадрат N. с дополнительные 32 бита информации в hash12, вы ожидаете увидеть столкновения, когда N > 20 м или около того (размахивание руками, в моей голове математика).

вы можете сделать код hash12 немного более компактным, очевидно, - и должно быть легко увидеть, как расширить его на любую длину.

О - И последнее. Если у вас включена RC-адресация, используйте =CRC16("string") как электронная таблица формула дает трудно отслеживать #REF ошибка... вот почему я переименовал его hash4


возможно, другие найдут это полезным.

я собрал несколько разных функций для генерации короткого хэша строки в VBA.
Я не беру на себя ответственность за код, и все источники ссылаются.

enter image description here

  1. контрольные суммы CRC16
    • функция: =CRC16HASH(A1) с код
    • хэш-это 4-символьная шестнадцатеричная строка
    • 19 строк кода
    • 4 цифр хэш = 624 столкновения в 6895 линиях = 9% скорость столкновения
  2. контрольные суммы CRC16 числовые
    • функция: =CRC16NUMERIC(A1) С код
    • хэш-это 5 цифр номер
    • 92 строк кода
    • 5 цифр длинный хэш = 616 столкновений в 6895 строк = 8,9% скорость столкновения
  3. контрольные суммы CRC16 два раза
    • функция: =CRC16TWICE(A1) С этим код
    • хэш-это 8-символьная шестнадцатеричная строка
    • хэш может быть расширен до 12/16/20 и т. д. символы, чтобы уменьшить скорость столкновения еще больше
    • 39 строк кода
    • 8 цифр длинный хэш = 18 столкновений в 6895 строк = 0,23% скорость столкновения
  4. в SHA1
    • функция: =SHA1TRUNC(A1) С код
    • хэш - это 40 символов длиной HEX строка
    • 142 код строки
    • можно усечь
    • 4 цифры хэш = 726 столкновений в 6895 строк = 10,5% скорость столкновения
    • 5 цифр хэш = 51 столкновение в 6895 строк = 0.73 % скорость столкновения
    • 6 цифр хэш = 0 столкновений в 6895 строк = 0% скорость столкновения
  5. в SHA1 + в base64
    • функция: =BASE64SHA1(A1) С код
    • хэш это 28 символов Unicode строку (регистр + спецсимволы)
    • 41 строк кода
    • требуется .NET, так как он использует библиотеку "Microsoft MSXML"
    • можно усечь
    • 4 цифры хэш = 36 столкновений в 6895 строк = 0.5 % скорость столкновения
    • 5 цифр хэш = 0 столкновений в 6895 строк = 0% скорость столкновения

здесь моя тестовая книга со всеми функциями примера и большое количество тестовых строк.

не стесняйтесь добавлять собственные функции.


для сведения, эта быстро генерирует 32-битный хэш с низким уровнем столкновения:

Public Function HashFNV(txt As String) As Long
  Const max# = 2 ^ 31
  Dim hash#, upper&, i&
  If txt = Empty Then Exit Function
  hash = &H11C9DC5
  For i = 1 To Len(txt)
    hash = 31# * (hash - upper * max Xor AscW(Mid$(txt, i, 1)))
    upper = hash / max
  Next
  HashFNV = hash - upper * max Or &H80000000 * (upper And 1&)
End Function

хотя ниже не хэш-функция, я использовал ее как быстрый способ генерации числовых идентификаторов, которые имеют низкую частоту столкновений над небольшим списком (достаточно маленьким, чтобы проверить инспекцией).

как это работает: столбец A содержит строки из строки 2 и далее. В строке 1 A1 и B1 занимают произвольное начальное и конечное положение посередине строки. Формула использует первую букву строки и фиксированную букву, взятую из середины строки, и использует LEN () в качестве "функции веера" чтобы уменьшить вероятность столкновений.

 =CODE(A2)*LEN(A2) + CODE(MID(A2,$A,$B))*LEN(MID(A2,$A,$B))

Если строки извлекаются из таблицы базы данных с полями фиксированной ширины, вам может потребоваться обрезать длины:

 =CODE(TRIM(C8))*LEN(TRIM(C8))
       +CODE(MID(TRIM(C8),$A,1))*LEN(MID(TRIM(C8),$A,$B))