Скажем, у вас есть два хэша H(A)
иH(B)
, и вы хотите , чтобы объединить их. Я читал, что хороший способ объединить два хеша для XOR
них, например XOR( H(A), H(B) )
.
Лучшее объяснение, которое я нашел, кратко затронуто здесь рекомендациям хэш-функции :
XOR двух чисел с примерно случайным распределением приводит к другому числу, все еще с примерно случайным распределением *, но которое теперь зависит от двух значений.
...
* В каждом бите двух объединяемых чисел выводится 0, если два бита равны, иначе - 1. Другими словами, в 50% комбинаций выводится 1. Таким образом, если каждый из двух входных битов имеет примерно 50-50 шанс быть равным 0 или 1, то и выходной бит тоже будет.
Можете ли вы объяснить интуицию и / или математику, почему XOR должен быть операцией по умолчанию для объединения хеш-функций (а не OR или AND и т. Д.)?
cryptography
bit-manipulation
hash
probability
xor
Нейт Мюррей
источник
источник
Ответы:
Принимая во внимание равномерно случайные (1-битные) входы, распределение вероятности выхода функции AND составляет 75%
0
и 25%1
. И наоборот, ИЛИ составляет 25%0
и 75%1
.Функция XOR составляет 50%
0
и 50%1
, поэтому она подходит для объединения равномерных распределений вероятностей.Это можно увидеть написав таблицы истинности:
Упражнение: Сколько логических функций двух 1-битных входов
a
иb
имеют это равномерное распределение выходов? Почему XOR наиболее подходит для цели, указанной в вашем вопросе?источник
(0, a & b, a > b, a, a < b, b, a % b, a | b, !a & !b, a == b, !b, a >= b, !a, a <= b, !a | !b, 1)
следующие имеют 50% -50% распределений 0 и 1, предполагая, что a и b имеют 50% -50% распределений 0 и 1:a, b, !a, !b, a % b, a == b
т. е. противоположное XOR (EQUIV) тоже можно было использовать ...a, b, !a, !b
будет то же распределение, что и у их соответствующих входов, вы потеряете энтропию другого входа. То есть XOR наиболее подходит для объединения хэшей, потому что мы хотим получить энтропию как от a, так и от b.(a,a)
и(b,b)
оба производят ноль, что во многих (большинство?) Случаев значительно увеличивает вероятность столкновений в хэш на основе структуры данных.xor
опасная функция по умолчанию для использования при хешировании Это лучше чемand
иor
, но это мало что говорит.xor
симметричен, поэтому порядок элементов теряется. Так"bad"
что хеш объединит так же как"dab"
.xor
отображает попарно одинаковые значения в ноль, и вам следует избегать отображения «общих» значений в ноль:Таким образом,
(a,a)
отображается на 0, а(b,b)
также отображается на 0. Так как такие пары почти всегда более распространены, чем может предполагать случайность, вы в конечном итоге столкнетесь с большим количеством столкновений в нуле, чем должны.С этими двумя проблемами
xor
получается хеш-сумматор, который выглядит наполовину прилично на поверхности, но не после дальнейшей проверки.На современном оборудовании добавление обычно происходит примерно так же быстро
xor
(вероятно, для этого требуется больше энергии). Таблица истинности добавления похожа наxor
на рассматриваемый бит, но она также отправляет бит на следующий бит, когда оба значения равны 1. Это означает, что она стирает меньше информации.Так
hash(a) + hash(b)
что лучше, чемhash(a) xor hash(b)
в том случаеa==b
, если результатhash(a)<<1
вместо 0 результат.Это остается симметричным; поэтому
"bad"
и"dab"
получение того же результата остается проблемой. Мы можем нарушить эту симметрию за скромную цену:ака
hash(a)*3 + hash(b)
. (Расчетhash(a)
один раз и сохранение рекомендуется, если вы используете сменное решение). Любая нечетная константа вместо "3
биективно" будет отображать "k
-битное" целое число без знака для себя, так как отображение на целые числа без знака является математическим по модулю2^k
для некоторыхk
, и любая нечетная константа относительно проста2^k
.Для еще более изящной версии мы можем изучить
boost::hash_combine
, что эффективно:здесь мы складываем несколько сдвинутых версий
seed
с константой (которая в основном случайная0
s и1
s - в частности, это обратное значение золотого сечения как 32-битной дроби с фиксированной запятой) с некоторым добавлением и xor. Это нарушает симметрию и вводит некоторый «шум», если входящие хэшированные значения плохие (т.е. представьте, что каждый компонент хеширует до 0 - вышеупомянутый обрабатывает это хорошо, генерируя мазок1
и0
s после каждого объединения. Мой наивный3*hash(a)+hash(b)
просто выводит a0
in тот случай).(Для тех, кто не знаком с C / C ++, a
size_t
- это целое число без знака, которое достаточно велико, чтобы описать размер любого объекта в памяти. В 64-битной системе это обычно 64-битное целое число без знака. В 32-битной системе 32-разрядное целое число без знака.)источник
0x9e3779b9
.Несмотря на удобные свойства смешивания битов, XOR не является хорошим способом объединения хэшей из-за его коммутативности. Подумайте, что произойдет, если вы сохранили перестановки {1, 2,…, 10} в хэш-таблице из 10 кортежей.
Гораздо лучший выбор
m * H(A) + H(B)
, где м - большое нечетное число.Кредит: вышеупомянутый объединитель был подсказкой от Боба Дженкинса.
источник
long
а затем вернуть верхнюю часть обратно в нижнюю часть.m = 3
на самом деле хороший выбор и очень быстрый на многих системах. Обратите внимание, что для любого нечетногоm
целочисленного умножения по модулю2^32
или,2^64
и, следовательно, оно обратимо, поэтому вы не теряете биты.Xor может быть способом по умолчанию для объединения хэшей, но ответ Грега Хьюгилла также показывает, почему у него есть свои подводные камни: xor двух идентичных значений хэша равен нулю. В реальной жизни идентичные хэши встречаются чаще, чем можно было ожидать. Затем вы можете обнаружить, что в этих (не очень редких) угловых случаях результирующие комбинированные хэши всегда одинаковы (ноль). Хеш-коллизии будут намного, намного чаще, чем вы ожидаете.
В надуманном примере вы можете комбинировать хешированные пароли пользователей с разных веб-сайтов, которыми вы управляете. К сожалению, большое количество пользователей повторно используют свои пароли, и удивительная доля получаемых хэшей равна нулю!
источник
Есть кое-что, что я хочу явно указать для тех, кто находит эту страницу. И и ИЛИ ограничивают вывод, как BlueRaja - Дэнни Пфлугхо пытается указать, но может быть лучше определен:
Сначала я хочу определить две простые функции, которые я буду использовать для объяснения этого: Min () и Max ().
Min (A, B) вернет меньшее значение между A и B, например: Min (1, 5) возвращает 1.
Max (A, B) вернет значение, большее между A и B, например: Max (1, 5) возвращает 5.
Если вам дают:
C = A AND B
Тогда вы можете найти, что
C <= Min(A, B)
Мы знаем это, потому что вы ничего не можете И С 0 битами А или В сделать их 1. Таким образом, каждый нулевой бит остается нулевым, и каждый бит имеет шанс стать нулевым (и, следовательно, меньшим значением).С участием:
C = A OR B
Верно обратное:
C >= Max(A, B)
с этим мы видим следствие функции AND. Любой бит, который уже равен единице, не может быть преобразован в ноль, поэтому он остается равным единице, но каждый нулевой бит имеет шанс стать единицей и, следовательно, большим числом.Это подразумевает, что состояние ввода накладывает ограничения на вывод. Если вы И что-нибудь с 90, вы знаете, что выход будет равен или меньше 90, независимо от того, что другое значение.
Для XOR нет подразумеваемых ограничений на основе входных данных. Есть особые случаи, когда вы можете обнаружить, что если вы XOR байта с 255, то вы получите обратный, но любой возможный байт может быть выведен из этого. Каждый бит может изменить состояние в зависимости от того же бита в другом операнде.
источник
OR
это побитовое максимум , иAND
это побитовое мин .Если вы
XOR
случайный вход с предвзятым входом, выход является случайным. То же самое не верно дляAND
илиOR
. Пример:Как упоминает @Greg Hewgill, даже если оба входа являются случайными, использование
AND
илиOR
приведет к смещенному выводу.Причина, по которой мы используем
XOR
что-то более сложное, в том, что ну, в этом нет необходимости:XOR
работает отлично, и это чертовски быстро.источник
Покройте 2 левых столбца и попытайтесь выяснить, какие входные данные используют только выходные данные.
Когда вы видели 1-бит, вы должны были понять, что оба входа были 1.
Теперь сделайте то же самое для XOR
XOR ничего не дает по этому поводу.
источник
Исходный код для различных версий
hashCode()
в java.util.Arrays - отличный справочник по надежным, широко используемым алгоритмам хеширования. Они легко понимаются и переводятся на другие языки программирования.Грубо говоря, большинство реализаций с несколькими атрибутами
hashCode()
следуют этому шаблону:Вы можете искать другие вопросы и ответы в StackOverflow для получения дополнительной информации о магии, стоящей за ней
31
, и о том , почему Java-код использует ее так часто. Он несовершенен, но имеет очень хорошие общие характеристики.источник
string
сstring + "AA"
IIRC), и они давно хотели, чтобы они не запеклись в этом алгоритме в спецификации. Тем не менее, использование большего нечетного числа с большим количеством установленных битов и добавление сдвигов или вращений решает эту проблему. MurmurHash3 'mix' делает это.XOR не игнорирует некоторые входные данные, такие как OR и AND .
Если вы возьмете, например, AND (X, Y) и зададите для входа X значение false, то вход Y не имеет значения ... и, возможно, вы захотите, чтобы значение ввода имело значение при объединении хэшей.
Если вы возьмете XOR (X, Y), тогда ОБА входы ВСЕГДА имеют значение. Там не будет никакого значения X, где Y не имеет значения. Если изменяется X или Y, то результат будет отражать это.
источник