Hashtable / Словарь столкновений - PullRequest
4 голосов
/ 09 апреля 2009

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

Так что строки вроде:

blur
Blur
b
Blur_The_Shades_Slightly_With_A_Tint_Of_Blue

...

Ответы [ 5 ]

15 голосов
/ 09 апреля 2009

Нет гарантии, что вы не столкнетесь с отдельными буквами.

Вы , вероятно, не сможете, но алгоритм, используемый в string.GetHashCode, не указан и может измениться. (В частности, он изменился между .NET 1.1 и .NET 2.0, что обожгло людей, которые предполагали, что это не изменится.)

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

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

3 голосов
/ 10 апреля 2009

Универсальное хеширование

Для вычисления вероятности коллизий с S строками длины L с W битами на символ для хеша длины H битов, предполагая оптимальный универсальный хэш ( 1 ) Вы можете рассчитать вероятность столкновения на основе хеш-таблицы размера (количества сегментов) 'N`.

Прежде всего, мы можем предположить идеальную реализацию хеш-таблицы ( 2 ), которая идеально разбивает H бит в хэше на доступные сегменты N ( 3 ). Это означает, что H становится бессмысленным, кроме как ограничение для N. W и 'L' являются просто основой для верхней границы S. Для более простой математики предположим, что строки длиной <<code>L просто дополняются до L специальным нулевым символом. Если нас интересовало, нас интересует наихудший случай, это 54 ^ L (26 * 2 + '_' + null), просто это смешное число, фактическое количество записей более полезно, чем набор символов и длина, поэтому мы просто будем работать так, как если бы S была переменной сама по себе.

Мы пытаемся поместить S предметов в N ведра. Тогда это становится очень известной проблемой, парадоксом дня рождения

Решение этой проблемы для различных вероятностей и количества сегментов является поучительным , но если предположить, что у нас есть 1 миллиард блоков (то есть около 4 ГБ памяти в 32-битной системе), тогда нам потребуется только 37 КБ записей, прежде чем мы достигнем 50% -й шанс того, что они будут хотя бы одним столкновением. Учитывая, что пытаться избежать любых столкновений в хеш-таблице становится просто абсурдом.

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

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

Альтернативный подход: идеальное хеширование

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

Напомним ограничение в 54 ^ L строк длиной L. Однако у нас есть только H битов (предположим, 32), что составляет около 4 миллиардов различных чисел. Так что если вы можете иметь действительно любую строку и любое их количество, тогда вы должны удовлетворить:

54 ^ L <= 2 ^ 32

И ее решение:

log2 (54 ^ L) <= 32
L * log2 54 <= 32
L <= 32 / log2 54 <= 5.56

Поскольку длины строк явно не могут быть дробными, максимальная длина у вас остается всего 5. Действительно, очень короткая.

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


  1. Для этого упражнения универсальный хеш является оптимальным, так как мы хотим уменьшить вероятность любого столкновения, т. Е. Для любого входа вероятность его выхода x из набора возможностей R равна 1 /. Р.

  2. Обратите внимание, что выполнить оптимальную работу по хешированию (и внутреннему группированию) довольно сложно, но следует ожидать, что встроенные типы будут разумными, если не всегда идеальными.

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

3 голосов
/ 09 апреля 2009

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


Нет. уникальных хеш-кодов avilable = 2 ^ 32 = 4294967296 (при условии, что для хеш-кодов используется 32-разрядное целое число) Размер набора символов = 2 * 26 + 1 = 53 (26 строчных букв верхнего регистра в латинском алфавите, плюс подчеркивание)

Тогда вы должны учитывать, что строка длиной l (или меньше) имеет всего 54 ^ l представлений. Обратите внимание, что основание равно 54, а не 53, потому что строка может заканчиваться после любого символа, добавляя дополнительную возможность для каждого символа - не то чтобы это сильно влияло на результат.

Принимая нет. уникальных хеш-кодов в качестве максимального числа представлений строк, вы получите следующее простое уравнение:

54 ^ l = 2 ^ 32

И ее решение:

log2 (54 ^ l) = 32
l * log2 54 = 32
l = 32 / log2 54 = 5.56

(где log2 - логарифмическая функция основания 2.)

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


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

1 голос
/ 09 апреля 2009

Алгоритм хеширования не должен гарантировать уникальность. Учитывая, что потенциальных строк гораздо больше (26 ^ n для длины n, даже игнорируя специальные символы, пробелы, заглавные буквы, неанглийские символы и т. Д.), Чем есть места в вашей хеш-таблице, такая гарантия не может быть выполнена , Это только для того, чтобы гарантировать хорошее распространение.

0 голосов
/ 09 апреля 2009

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...