Генерация k попарно независимых хеш-функций

Я пытаюсь реализовать алгоритм Count-Min Sketch в Scala, поэтому мне нужно для генерации k попарно независимых хеш-функций.

Это более низкий уровень, чем все, что я когда-либо программировал раньше, и я мало что знаю о хэш-функциях, кроме как из классов алгоритмов, поэтому мой вопрос: как мне сгенерировать эти k попарно независимых хеш-функций?

Должен ли я использовать хеш-функцию, такую ​​как MD5 или MurmurHash? Я просто генерирую k хеш-функций вида f(x) = ax + b (mod p), где p — простое число, а a и b — случайные целые числа? (т. е. универсальное семейство хеширования, которое все изучают в алгоритмах 101)

Я ищу больше простоты, чем чистой скорости (например, я возьму что-то в 5 раз медленнее, если это проще реализовать).


person grautur    schedule 25.08.2012    source источник
comment
MD5 является криптографическим. MurmurHash хорош, но не криптостойкий.   -  person Rex Kerr    schedule 26.08.2012


Ответы (2)


В Scala уже реализовано MurmurHash (это scala.util.MurmurHash). Это очень быстро и очень хорошо распределяет значения. Криптографический хеш — это излишество — вам просто потребуется в десятки или сотни раз больше времени, чем нужно. Просто выберите для начала k различных начальных значений, и, поскольку качество почти криптографическое, вы получите k в значительной степени независимых хеш-кодов. (В 2.10 вам, вероятно, следует переключиться на использование scala.util.hashing.MurmurHash3; использование несколько отличается, но вы все еще можете делать то же самое с микшированием.)

Если вам нужны только близкие значения, которые должны быть сопоставлены со случайными дальними значениями, это сработает; если вы хотите избежать коллизий (т.е. если A и B сталкиваются с использованием хэша 1, они, вероятно, также не столкнутся с использованием хэша 2), тогда вам нужно будет сделать хотя бы еще один шаг и хэшировать не весь объект, а его подкомпоненты, поэтому есть возможность, чтобы хэши начинались по-разному.

person Rex Kerr    schedule 25.08.2012
comment
Означает ли ваше замечание об избежании коллизий, что хэш-функции, сгенерированные из MurmurHash с использованием разных начальных значений, не будут (по умолчанию) попарно независимыми? В моем случае я хэширую только целые числа. - person grautur; 25.08.2012
comment
@grautur - О, целые числа подойдут. Я имею в виду, что если объект A хеширует значение x с помощью .hashValue, а объект B также хеширует значение x, то A и B столкнутся независимо от того, какое начальное число вы используете (поскольку вы начинаете с начального значения, а затем смешиваете x). Если вы хешируете целые числа, это не проблема: A и B имеют одно и то же внутреннее хеш-значение тогда и только тогда, когда A == B. - person Rex Kerr; 26.08.2012
comment
А, понял, спасибо! Чтобы выбрать k разных семян, работает ли запуск scala.util.Random.nextInt() k в разное время или мне нужно сделать что-то еще? - person grautur; 26.08.2012
comment
@grautur - Это должно быть хорошо. Если вы хотите, чтобы ваш код был детерминированным (несмотря на то, что он псевдослучайный), чтобы вы каждый раз получали один и тот же ответ, вам нужно создать новый scala.util.Random с выбранным вами начальным числом. В противном случае nextInt по умолчанию является достаточно хорошим генератором случайных чисел. - person Rex Kerr; 26.08.2012
comment
@RexKerr Я не думаю, что можно изменить начальное число в новой реализации MurmurHash3. - person paradigmatic; 28.08.2012
comment
@paradigmatic — многие методы перегружены вариантом, который принимает начальное значение, и в любом случае вы всегда можете просто начать с начального значения и смешать с ним следующее хэш-значение. - person Rex Kerr; 28.08.2012
comment
Можно семена, например. 1,2,3 использовать для разных хэш-функций, или семена должны быть случайными числами? - person Matěj Račinský; 05.06.2021

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

Я бы сделал что-то вроде:

// for each 0 <= i < k generate a sequence of random numbers
val randomSeeds: Array[Array[Byte]] = ... ; // initialize by random sequences

def hash(i: Int, value: Array[Byte]): Array[Byte] = {
    val dg = java.security.MessageDigest.getInstance("SHA-1");
    // "seed" the digest by a random value based on the index
    dg.update(randomSeeds(i));
    return dg.digest(value);
    // if you need integer hash values, just take 4 bytes
    // of the result and convert them to an int
}

Редактировать: я не знаю точных требований эскиза Count-Min, возможно, будет достаточно простой функции has, но это не самое простое решение.

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

С другой стороны, если у вас есть две хэш-функции вида f1(x) = ax + b (mod p) и f2(x) = cx + d (mod p), то вы можете вычислить одну, используя другую (не зная x), используя простую линейную формулу f2(x) = c / a * (f1(x) - b) + d (mod p), которая предполагает, что они не очень независимы. Таким образом, вы можете столкнуться с неожиданными проблемами здесь.

person Petr    schedule 25.08.2012
comment
Есть ли какое-то преимущество в использовании криптографической хеш-функции (в отличие от f(x) = ax + b mod p) в случае создания чего-то вроде фильтра Блума или эскиза Count-Min? AFAICT, криптографическая хеш-функция кажется немного излишней, поскольку мне не нужны криптографические свойства, но я могу что-то упустить. - person grautur; 25.08.2012
comment
@grautur - ax+b mod p может попадать в циклы, которые могут создавать шаблоны в вашей выборке, которые могут быть проблематичными, в зависимости от предположений вашей выборки. И затем, если вам не нужен точно полный диапазон, вы столкнетесь с проблемами битов высокого порядка по сравнению с битами низкого порядка и т. Д. Это хорошо для небольшого случайного скремблирования, но есть довольно быстрые альтернативы, которые работают намного лучше. - person Rex Kerr; 26.08.2012