Основное использование CRC и подобных вычислений (таких как Флетчер и Адлер), по-видимому, предназначено для обнаружения ошибок передачи. Таким образом, большинство исследований, которые я видел, похоже, посвящены проблеме вероятности обнаружения небольших различий между двумя наборами данных. Мои потребности немного другие.
Ниже приводится очень приблизительное описание проблемы. Детали намного сложнее, но приведенное ниже описание иллюстрирует функции, которые я ищу. Этот небольшой отказ от ответственности предназначен для защиты от таких ответов, как «Почему вы решаете свою проблему таким образом, если вам легче решить ее другим способом, который я предлагаю?» - Мне нужно решить мою проблему таким образом по множеству причин, которые не имеют отношения к этому вопросу или сообщению, поэтому, пожалуйста, не публикуйте такие ответы.
Я имею дело с коллекциями наборов данных (размером ~ 1 МБ) в распределенной сети. Вычисления выполняются с этими наборами данных, и скорость / производительность имеют решающее значение. Мне нужен механизм, позволяющий избежать повторной передачи наборов данных. То есть мне нужен способ сгенерировать уникальный идентификатор (UID) для каждого набора данных заданного размера. (Затем я передаю размер набора данных и UID с одной машины на другую, и принимающей машине нужно только запросить передачу данных, если у нее их еще нет локально, на основе UID.)
Это похоже на разницу между использованием CRC для проверки изменений файла и использованием CRC в качестве дайджеста для обнаружения дубликатов среди файлов. Я не видел никаких обсуждений последнего использования.
Меня не волнуют вопросы подделки, т.е. мне не нужно хеширование криптографической стойкости.
В настоящее время я использую простую 32-битную CRC сериализованных данных, и до сих пор она мне хорошо служила. Однако я хотел бы знать, может ли кто-нибудь порекомендовать, какой 32-битный алгоритм CRC (т.е. какой полином?) Лучше всего подходит для минимизации вероятности коллизий в этой ситуации?
Другой вопрос, который у меня есть, немного более тонкий. В моей текущей реализации я игнорирую структуру своего набора данных и фактически просто проверяю CRC сериализованной строки, представляющей мои данные. Однако по разным причинам я хочу изменить свою методологию CRC следующим образом. Предположим, мой набор данных верхнего уровня представляет собой набор необработанных данных и нескольких подчиненных наборов данных. Моя текущая схема по существу объединяет необработанные данные и все подчиненные наборы данных, а затем результат CRC. Однако в большинстве случаев у меня уже есть CRC подчиненных наборов данных, и я бы предпочел построить свой UID набора данных верхнего уровня, объединив необработанные данные с CRC подчиненные наборы данных, а затем CRC этой конструкции. Вопрос в том, как использование этой методики влияет на вероятность столкновений?
Чтобы выразить это на языке, который позволит мне обсуждать мои мысли, я определю некоторые обозначения. Назовите мой набор данных верхнего уровня T
и предположим, что он состоит из набора исходных данных R
и подчиненных наборов данных Si, i=1..n
. Я могу написать это как T = (R, S1, S2, ..., Sn)
. Если &
представляет собой объединение наборов данных, мою исходную схему можно представить как:
UID_1(T) = CRC(R & S1 & S2 & ... & Sn)
и мою новую схему можно представить как
UID_2(T) = CRC(R & CRC(S1) & CRC(S2) & ... & CRC(Sn))
Тогда мои вопросы: (1) если T
и T'
очень разные, какой алгоритм CRC минимизирует prob( UID_1(T)=UID_1(T') )
, а какой алгоритм CRC минимизирует prob( UID_2(T)=UID_2(T') )
, и как сравнить эти две вероятности?
Мои (наивные и неосведомленные) мысли по этому поводу таковы. Предположим, что различия между T
и T'
находятся только в одном подчиненном наборе данных, WLOG сообщает S1!=S1'
. Если случится, что CRC(S1)=CRC(S1')
, то явно у нас будет UID_2(T)=UID_2(T')
. С другой стороны, если CRC(S1)!=CRC(S1')
, тогда разница между R & CRC(S1) & CRC(S2) & ... & CRC(Sn)
и R & CRC(S1') & CRC(S2) & ... & CRC(Sn)
небольшая разница только в 4 байта, поэтому способность UID_2 обнаруживать различия фактически такая же, как способность CRC обнаруживать ошибки передачи, то есть его способность обнаруживать ошибки только в нескольких битах, которые не разделены на большие расстояния. Поскольку это то, для чего предназначены CRC, я бы подумал, что UID_2 довольно безопасен, если CRC, который я использую, хорош для обнаружения ошибок передачи. Выражаясь в наших обозначениях,
prob( UID_2(T)=UID_2(T') ) = prob(CRC(S1)=CRC(S1')) + (1-prob(CRC(S1)=CRC(S1'))) * probability of CRC not detecting error a few bits.
Назовем вероятность того, что CRC не обнаружит ошибку в несколько битов P
, и вероятность того, что она не обнаружит больших различий в наборе данных большого размера Q
. Сказанное можно приблизительно записать как
prob( UID_2(T)=UID_2(T') ) ~ Q + (1-Q)*P
Теперь я немного изменю свой UID следующим образом. Для «фундаментальной» части данных, то есть набора данных T=(R)
, где R - это просто двойное, целое число, символ, логическое значение и т. Д., Определите UID_3(T)=(R)
. Затем для набора данных T
, состоящего из вектора подчиненных наборов данных T = (S1, S2, ..., Sn)
, определите
UID_3(T) = CRC(ID_3(S1) & ID_3(S2) & ... & ID_3(Sn))
Предположим, что в конкретном наборе данных T
есть подчиненные наборы данных, вложенные m
на глубину, тогда, в некотором неопределенном смысле, я мог бы подумать, что
prob( UID_3(T)=UID_3(T') ) ~ 1 - (1-Q)(1-P)^m
Учитывая, что эти вероятности в любом случае малы, это можно аппроксимировать как
1 - (1-Q)(1-P)^m = Q + (1-Q)*P*m + (1-Q)*P*P*m*(m-1)/2 + ... ~ Q + m*P
Итак, если я знаю свой максимальный уровень вложенности m
и знаю P
и Q
для различных CRC, я хочу выбрать CRC, который дает мне минимальное значение для Q + m*P
. Если, как я подозреваю, может иметь место P~Q
, вышеизложенное упрощается до этого. Моя вероятность ошибки для UID_1 равна P
. Моя вероятность ошибки для UID_3 равна (m+1)P
, где m
- мой максимальный уровень вложенности (рекурсии).
Все это кажется разумным?