Сколько хэш-ведер
Если я заметил, что хеш-таблица (или любая другая структура данных, построенная на хэш-таблице) заполняется, в какой момент вы должны построить новую таблицу с большим количеством ковшей. И учитывая, что n позиций в таблице до сих пор, как вы определяете, сколько ведер использовать в новом?
Итак, скажем, у меня есть 100 ведер. Должен ли я реорганизовать его, когда в нем 50 предметов? 500? 5000? Или я должен искать наиболее полное ведро и ключ? Затем, когда я ударил по этой точке, насколько велика моя новая таблица хэшей?
В связи с этим, если вы заранее знаете, сколько элементов будет входить, есть ли способ вычислить количество ковшей для получения хорошей средней производительности?
Я знаю, что реальный ответ зависит от многих других соображений, таких как важность скорости и размера в конкретном примере, но я ищу общие линии гильдии.
Я также знаю, что я не должен оптимизировать такие вещи, если хорошее профилирование не указывает на то, что это узкое место. Я просто думаю о проекте, который использовал бы много хеш-таблиц, и задавался вопросом, как подойти к этому.
Ответы
Ответ 1
Хорошим правилом большого пальца (не всегда идеальным, ну, просто правилом большого пальца) является re-hash, если хэш-таблица заполнена до 80%. Это означает, что если у вас есть 100 ведер и 80 предметов внутри, независимо от того, сколько коллизий у вас было до этого, он получает время для увеличения емкости.
Сколько стоит его увеличить? Ну, также нет идеальной ценности. Самое простое решение - удвоить емкость при каждом увеличении. Так оно идет до 200, 400, 800, и так далее. Если вы считаете, что это слишком много (в конце концов, он будет прыгать с 8 МБ памяти до 16 МБ, когда хеш-таблица становится действительно большой, и вы никогда не сможете заполнить 16 МБ), выберите меньший фактор роста. По крайней мере, 1/3 рекомендуется (рост от 100 до 133), я бы сказал, возможно, пусть он будет расти на 50% каждый раз в качестве компромисса.
Обратите внимание, что все это также зависит от того, как обрабатываются конфликты. Простой способ справиться с ними (мой личный фаворит) - хранить элементы в связанном списке при столкновении. Если на один и тот же ключ помещено 3 предмета, их можно найти только до 3, чтобы найти его. Поскольку связанный список очень неэффективен для поиска, вы можете захотеть увеличить емкость раньше, например. если для сохранения хэш-таблицы используется 60-процентная емкость. OTOH, вы можете сделать что-то более сложное и сохранить статистику о количестве столкновений. До тех пор, пока вы вряд ли столкнетесь с конфликтами (если у вас очень хорошая хеш-функция), нет необходимости перехватывать, даже если используется 99% его возможностей. Также, если вы обрабатываете конфликты сложным способом (например, каждый node снова является отсортированной таблицей, и вы можете выполнять двоичный поиск в них), ваш поиск может быть достаточно быстрым, если таблица загружена до 200% (так что вы имеете в два раза больше многие предметы как емкость). В этом случае вы можете сохранить статистику, насколько велика самая большая отсортированная таблица, и когда она становится больше, чем, допустим, 8 записей, вы считаете, что это становится слишком медленным, а затем вы снова хешируете.
Повторное хеширование происходит очень медленно, поэтому его следует избегать как можно чаще. Таким образом, если вам нужно повторно использовать хэш, не просто слишком сильно увеличивайте производительность, в противном случае вы снова захотите повторно использовать хэш снова при добавлении большего количества предметов. Поэтому, когда вам нужно перехватить хэш, сделайте емкость значительно больше, чем количество элементов, находящихся в данный момент в таблице, все остальное будет слишком мало.
Ответ 2
Как правило, вы смотрите на коэффициент загрузки (в неофициальном плане, вы уже сказали это), который формально определяется как α = n n n n n n n n N N я i я i я i я i я i я i я i я i я i я i я i я i я i я i = Для того, чтобы хэш-таблица функционировала должным образом (или, по крайней мере, чтобы объяснить ее производительность в математических терминах), она должна быть α < 1.
Все остальное действительно зависит от эмпирических тестов. Если вы видите, что ваша хеш-таблица не работает хорошо, начиная с α > 0.5, то обязательно оставайтесь под этим значением. Это значение также зависит от вашего метода разрешения конфликтов. Для хеширования с цепью могут потребоваться другие факторы нагрузки, чем хеширование с открытой адресацией. Еще одним фактором является локальность кеша. Если ваша таблица становится слишком большой, она не будет вписываться в основную память. Поскольку ваш доступ к массиву случайный, загрузка из кеша может стать узким местом.
Ответ 3
Обычно существуют два типа хэш-таблиц: открытые и закрытые.
В открытой хэш-таблице вы найдете правое ведро на основе хеша, а затем создайте список элементов, зависающих от этого ведра.
В закрытой хэш-таблице вы найдете начальное ведро с использованием значения хэша, и если оно занято, вы проследуете для следующего значения. В упрощенном случае вы можете сделать это, ища следующее свободное ведро, или вы можете создать второе хеш-значение из своего элемента и шаг за шагом (хотя вы должны убедиться, что это просто по модулю размер хэш-таблиц, чтобы вы посетили все ведра).
Открытая хеш-таблица обычно не изменяется. Вы устанавливаете начальный размер таким, какой вы считаете разумным для проблемы. Как указывали другие, вы можете изменить размер на открытой хеш-таблице, но рассуждение о производительности этой структуры данных становится очень тяжелым. Если вы измените размер, когда длина данного ведра равна L, вы можете > изменить размер только на L элементов во всей хеш-таблице, что очень неэффективно.
Закрытая хэш-таблица изменяется, когда коэффициент нагрузки (количество элементов в хэш-таблице/количестве ковшей) достигает определенного предопределенного значения. Я склонен использовать 80%, но точное значение вряд ли будет слишком критическим.
Преимущество закрытой хэш-таблицы заключается в том, что стоимость амортизированной вставки элемента всегда равна O (1) (при условии хорошей хэш-функции). Вставка определенного элемента может быть O (N) из-за стоимости изменения размера, но это делается очень редко.
Ответ 4
Зависит от типа создаваемой таблицы хэшей. Если вы используете хэш-таблицу с фиксированным массивом (в отличие от связанных списков для ковшей), вы должны изменить размер массива либо при заполнении таблицы, либо при превышении максимального количества зондов (в зависимости от того, хотите ли вы больше узнать о скорости или Память). Если вы используете связанные списки, память не столько беспокоит вас, сколько и не нужно исследовать пустые пространства, поэтому изменение размера не является чем-то большим.
Ключ с хэш-таблицами - это алгоритм хеширования, а не количество ведер. В идеале вы всегда хотите не более одного элемента в каждом ковше, поэтому в идеале вы должны изменить размер, когда количество элементов в хеш-таблице = количество ковшей. Если ваши данные распределены неравномерно, вам лучше с лучшим алгоритмом хэширования, чем с лучшей стратегией изменения размера.
Ответ 5
Если вы используете Linear Hashing, сама таблица автоматически позаботится об изменении размера, поддерживая коэффициент постоянной нагрузки.