Почему хеш-функции должны использовать модуль простого числа?

Вам понадобится сортированный набор, ограниченный определенным количеством предметов. Возможно, некоторые библиотеки сторонних коллекций предоставляют его, в противном случае вы можете сделать это как-то так: Limited SortedSet . Важно то, что метод add такого отсортированного набора должен возвращать false, если коллекция заполнена, а добавленный элемент выходит за пределы, а true в противном случае.

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

Когда цикл будет завершен, ответом будет результирующий набор.

323
задан volni 13 March 2011 в 11:37
поделиться

8 ответов

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

(first char) + k * (second char) + k^2 * (third char) + ...

Тогда, если будет введена группа строк с одинаковым первым символом, тогда все результаты будут одинаковыми по модулю k, по крайней мере пока целочисленный тип не переполнится.

[Например, строковый hashCode Java устрашающе похож на этот - он выполняет обратный порядок символов с k = 31. Таким образом, вы получаете поразительные отношения по модулю 31 между строками, которые заканчиваются одинаково, и поразительные отношения по модулю 2 ^ 32 между строками, которые одинаковы, за исключением около конца. Это не сильно портит поведение хеш-таблицы.]

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

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

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

Оказывается, что «из-за природы математики», если константа, используемая в хэше, и количество сегментов взаимно просты , то коллизии в некоторых общих случаях сведены к минимуму. Если они не являются взаимно простыми , то между входными данными существуют довольно простые отношения, для которых коллизии не минимизированы. Все хэши получаются равными по модулю общего множителя, что означает, что все они попадут в 1 / n-е ведра, которые имеют это значение по модулю общего множителя. Вы получаете в n раз больше столкновений, где n - общий множитель. Поскольку n не менее 2, я бы сказал, что для довольно простого варианта использования недопустимо генерировать как минимум вдвое больше столкновений, чем обычно. Если какой-то пользователь собирается разбить наш дистрибутив на сегменты, мы хотим, чтобы это было странной случайностью, а не каким-то простым предсказуемым использованием.

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

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

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

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

[Edit: есть еще одна, более специализированная причина использовать простое количество сегментов, то есть если вы обрабатываете столкновения с помощью линейного зондирования. Затем вы вычисляете шаг из хэш-кода, и если этот шаг оказывается фактором количества ведра, вы можете выполнить только (bucket_count / stride) зонды, прежде чем вернуться к тому месту, где вы начали. Разумеется, вам больше всего нужно избегать stride = 0, который должен иметь специальный регистр, но чтобы избежать также bucket_count / stride в специальном регистре, равного небольшому целому числу, вы можете просто сделать bucket_count простым и не заботиться о том, что шаг, если он не равен 0.]

233
ответ дан 23 November 2019 в 00:56
поделиться

"Природа математики" относительно главных модулей питания - то, что они - один стандартный блок конечное поле . Другие два стандартных блока являются дополнением и операцией умножения. Специальное свойство главных модулей - то, что они формируют конечное поле с "регулярными" операциями дополнения и умножения, просто взятыми к модулю. Это значит каждое умножение карты для различного целого числа по модулю начало, каждое дополнение - также.

Главные модули выгодны потому что:

  • Они дают большую часть свободы при выборе вторичного множителя во вторичном хешировании, все множители кроме 0 закончат тем, что посетили все элементы точно однажды
  • , Если все хеши будут меньше, чем модуль то не будет никаких коллизий во всем
  • , Случайные начала смешиваются лучше, чем питание двух модулей и сжимают информацию всех битов не только подмножество

, у Них однако есть большая оборотная сторона, они требуют целочисленного деления, которое берет многих (~ 15-40) циклы, даже на современном ЦП. Приблизительно с половиной вычисления можно удостовериться, что хеш перепутан очень хорошо. Два умножения и xorshift операции смешаются лучше, чем главный модуль. Затем мы можем использовать любой размер хеш-таблицы и хешировать сокращение, является самым быстрым, давая 7 операций всего для питания 2 размеров таблицы и приблизительно 9 операций для произвольных размеров.

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

0
ответ дан 23 November 2019 в 00:56
поделиться

http: // computinglife.wordpress.com/2008/11/20/why-do-hash-functions-use-prime-numbers/

Довольно четкое объяснение, также с изображениями.

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

Лучше спросить, почему именно число 31?

10
ответ дан 23 November 2019 в 00:56
поделиться

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

Допустим, у вас есть уравнение: (x + y * z)% key = x с 0 и 0 . Если ключ является простым числом n * y = ключ истинен для каждого n в N и ложен для всех остальных чисел.

Пример, где ключ не является основным примером: x = 1, z = 2 и ключ = 8 Поскольку key / z = 4 по-прежнему является натуральным числом, 4 становится решением нашего уравнения, и в этом случае (n / 2) * y = key верно для каждого n в N. Количество решений для уравнения практически удвоилось. потому что 8 не является простым числом.

Если наш злоумышленник уже знает, что 8 является возможным решением уравнения, он может изменить файл с производящего 8 на 4 и все равно получить тот же хэш.

0
ответ дан 23 November 2019 в 00:56
поделиться

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

http://www.codexon.com/posts/hash-functions-the-modulo-prime-myth

Который утверждает, что вам следует использовать как можно большее количество сегментов, а не округлять до простого числа сегментов. Это кажется разумной возможностью. Интуитивно я, конечно, вижу, чем было бы лучше большее количество корзин, но я не могу привести это математическое обоснование.

5
ответ дан 23 November 2019 в 00:56
поделиться

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

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

Однако использование простых чисел - старая техника. Ключ здесь, чтобы понять что до тех пор, пока вы можете создать достаточно уникальный ключ, который можно переместить к другим методам хеширования. Идти здесь для получения дополнительной информации по этой теме о http://www.azillionmonkeys.com/qed/hash.html

http://computinglife.wordpress.com/2008/11/20/why-do-hash-functions-use-prime-numbers /

3
ответ дан 23 November 2019 в 00:56
поделиться

Это зависит от выбора хэш-функции.

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

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

С другой стороны, эти множители обычно состоят из нечетных простых чисел, поэтому вы также должны быть в безопасности, используя степени двойки для вашего хэша. table (например, Eclipse использует 31 при создании метода Java hashCode ()).

3
ответ дан 23 November 2019 в 00:56
поделиться

Первое, что вы делаете при вставке / извлечении из хэш-таблицы, - это вычисление хэш-кода для данного ключа, а затем поиск правильного сегмента, обрезая хэш-код до размера хэш-таблицы, выполнив hashCode % table_length. Вот 2 «утверждения», которые вы, скорее всего, где-то читали

  1. Если вы используете степень 2 для table_length, найти (hashCode (key)% 2 ^ n) так же просто и быстро, как (hashCode (key) & ( 2 ^ п -1)). Но если ваша функция для вычисления hashCode для данного ключа не подходит, вы определенно пострадаете от кластеризации многих ключей в нескольких хэш-корзинах.
  2. Но если вы используете простые числа для table_length, вычисленные hashCodes могут отображаться в разные хэш-ведра, даже если у вас есть немного глупая функция hashCode.

И вот доказательство.

Если предположим, что ваша функция hashCode выдает следующие хэш-коды, среди прочих {x, 2x, 3x, 4x, 5x, 6x ...}, то все они будут сгруппированы всего в m сегментов, где m = длина_таблицы / GreatestCommonFactor (длина_таблицы, x). (Это тривиально проверить / вывести). Теперь вы можете сделать одно из следующих действий, чтобы избежать кластеризации

. Убедитесь, что вы не генерируете слишком много хэш-кодов, которые кратны другому хэш-коду, как в {x, 2x, 3x, 4x, 5x, 6x ...}. Но это может быть сложно, если ваша хэш-таблица должна иметь миллионы записей. Или просто сделайте m равным table_length, сделав GreatestCommonFactor (table_length, x) равным 1, то есть сделав table_length равным x. И если x может быть практически любым числом, убедитесь, что table_length - простое число.

From - http://srinvis.blogspot.com/2006/07/hash-table-lengths-and-prime -numbers.html

28
ответ дан 23 November 2019 в 00:56
поделиться
Другие вопросы по тегам:

Похожие вопросы: