Опубликован: 05.01.2015 | Доступ: свободный | Студентов: 2178 / 0 | Длительность: 63:16:00
Лекция 14:

Хеширование

Перспективы

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

Таблица 14.1. Экспериментальное сравнение реализаций хеш-таблиц
N Создание Неудачный поиск
R H P h P* R H P h P*
1250 1 0 5 3 0 1 1 0 1 0
2500 3 1 3 4 2 1 1 0 0 0
5000 6 1 4 4 3 2 1 0 1 0
12500 14 6 5 5 5 6 1 2 2 1
25000 34 9 7 8 11 16 5 3 4 3
50000 74 18 11 12 22 36 15 8 8 8
100000 182 35 21 23 47 84 45 23 21 15
150000 54 40 36 138 99 89 52 21
160000 58 43 44 147 115 133 66 23
170000 68 55 45 136 121 226 85 25
180000 65 61 50 152 133 449 125 27
190000 79 106 59 155 144 2194 261 30
200000 407 84 159 186 156 33
Обозначения:
R RB-дерево бинарного поиска (программы 12.8 и 13.6)
H Цепочки переполнения (программа 14.3 при размере таблицы 20000)
P Линейное опробование (программа 14.4 при размере таблицы 200000)
D Двойное хеширование (программа 14.6 при размере таблицы 200000)
P Линейное опробование с расширением путем удвоения (программа 14.7)

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

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

Сравнение линейного опробования и двойного хеширования с методом цепочек переполнения выполнить сложнее, поскольку необходимо точно учитывать использование памяти. Цепочки переполнения используют дополнительную память под ссылки; методы с открытой адресацией неявно используют дополнительную память внутри таблицы для завершения последовательностей проб. Следующий конкретный пример иллюстрирует эту ситуацию. Предположим, что имеется таблица М списков, построенная хешированием с цепочками переполнения, что средняя длина списков равна 4, и что каждый элемент и каждая ссылка занимают по одному машинному слову. Предположение, что элементы и ссылки занимают одинаковый объем памяти, оправданно во многих ситуациях, поскольку очень большие элементы обычно заменяются ссылками на них. В этом случае таблица занимает 9М слов памяти (4М для элементов и 5М для ссылок), и требует для выполнения поиска в среднем 2 пробы. Но при линейном опробовании для 4М элементов в таблице размером 9М требуется всего $(1 + 1/(1 — 4/9))/2 = 1,4$ пробы для успешного поиска, что на 30% меньше, чем с цепочками переполнения при том же объеме используемой памяти; а при линейном опробовании для 4М элементов в таблице размером 6М для успешного поиска требуется (в среднем) 2 пробы и, следовательно, используется на 33% меньше памяти, чем с цепочками переполнения при том же времени выполнения. Кроме того, можно использовать динамический метод, наподобие программы 14.7, для сохранения небольшого коэффициента загрузки таблицы с помощью увеличения ее размера.

Приведенные в предыдущем абзаце рассуждения показывают, что обычно выбор цепочек переполнения вместо открытой адресации по соображениям производительности не оправдан. Однако на практике цепочки переполнения с фиксированным значением М часто выбирают по ряду других причин: их легко реализовать (особенно операцию удалить); они требуют небольшого дополнительного объема памяти для элементов с уже выделенными полями ссылок, пригодными для использования таблицей символов и другими АТД, которые могут в них нуждаться. Хотя производительность этого метода снижается с увеличением количества элементов в таблице, это снижение вполне терпимо и происходит так, что вряд ли повредит приложению, поскольку производительность все равно в М раз выше, чем при последовательном поиске.

Существует много других методов хеширования, которые находят применение в особых ситуациях. Мы не можем останавливаться на этом подробно, но все же кратко рассмотрим три примера, иллюстрирующие сущность специальных методов хеширования.

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

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

Таблица символов с быстрым неудачным поиском и несколько более медленным успешным поиском может использоваться для реализации словаря исключений (exception dictionary). Например, система текстовой обработки может содержать алгоритм для разбиения слов на слоги, который успешно работает для большинства слов, но не работает в отдельных случаях (вроде слова " безыскусный " ). Скорее всего, в словаре исключений будет найдено лишь небольшое количество слов очень большого документа, поэтому почти все поиски будут неудачными.

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

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

Например, предположим, что имеется словарь исключений, состоящий из 1000 элементов, и 1 миллион элементов, которые необходимо искать в словаре, поэтому почти все поиски должны быть неудачными. Такая ситуация может возникнуть, если все элементы были бы необычными словами или случайными 32-разрядными целыми числами. Один из подходов — хеширование всех слов, скажем, в 15-разрядные значения (размер таблицы будет около 216). 1000 исключений занимают 1/64 часть таблицы, и большинство из 1 миллиона операций поиска сразу завершатся неудачей, обнаружив пустую позицию таблицы при первой же пробе. Но если таблица содержит 32-разрядные слова, задачу можно выполнить значительно эффективней, преобразовав ее в битовую таблицу исключений и используя 20-разрядные хеш-значения. При неудачном поиске (в большинстве случаев) поиск завершается проверкой одного бита; при удачном поиске требуется выполнение второй проверки в меньшей таблице. Исключения занимают 1/1000 часть таблицы; неудачный поиск — наиболее вероятная операция; и задача выполняется с помощью 1 миллиона битовых проверок с прямой индексацией. Это решение основывается на идее, что хеш-функция создает короткий сертификат, представляющий ключ — эта важная концепция полезна и в приложениях, отличных от реализаций таблиц символов.

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

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

Упражнения

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

14.49. Для 1 миллиона целочисленных ключей вычислите количество сравнений, выполняемых в среднем каждым из трех методов хеширования (цепочки переполнения, линейное опробование и двойное хеширование) при неудачном поиске, если они могут использовать 3 миллиона слов памяти (как было бы в случае BST-деревьев).

14.50. Реализуйте АТД таблицы символов с быстрым неудачным поиском, как описано в тексте, используя для второй проверки цепочки переполнения.

Бактыгуль Асаинова
Бактыгуль Асаинова

Здравствуйте прошла курсы на тему Алгоритмы С++. Но не пришел сертификат и не доступен.Где и как можно его скаачат?

Александра Боброва
Александра Боброва

Я прошла все лекции на 100%.

Но в https://www.intuit.ru/intuituser/study/diplomas ничего нет.

Что делать? Как получить сертификат?