Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Пособие часть 1.doc
Скачиваний:
60
Добавлен:
24.09.2019
Размер:
6.98 Mб
Скачать

Метод середины квадрата

Значение ключа K возводится в квадрат и из полученного квадрата извлекается нескольких средних цифр. Так, при размере хеш-таблицы M=100 достаточно двух цифр (00-99), при M=1000 – трёх и т.д. Эксперименты показали, что такой способ хорошо работает, когда в ключах нет большого количества нолей слева или справа [Кнут].

Метод середины квадрата можно обобщить для случая, когда M уже не обязательно степень числа 10. Предположим, что ключи – целые числа из интервала [0, N]. Найдём такое целое число C, что MC2 примерно равно N2. Тогда используется функция h(K)=[K2/C] mod M, где [] означает целую часть.

Например, если ключи находятся в интервале от 0 до 1000 (N=1000), размер хеш-таблицы M=8, то можно выбрать C=354 (8*3542≈10002). Тогда, например, h(456)=[4562/354] mod 8 = 587 mod 8 = 3.

Хеш-функции для строк переменной длины

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

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

Рассмотрим для примера одну из самых простых хеш-функций для строки S (M — размер хеш-таблицы):

int h(char *S, int M)

{ int i, sum=0;

for (i=1; i<= 10; i++) sum = sum + S[i];

return sum%M;

}

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

Возьмем 100 следующих строк: “A0”, “A1”, “A2”,…,”A99”. Легко проверить, что при обработке этих 100 строк будет получено всего 28 различных хеш-адресов из 100 возможных. Это означает, что на таких специфических входных данных рассмотренная функция работает не очень эффективно, хотя для случайно взятых текстов она вполне приемлема. В [14] можно найти еще несколько вариантов более сложных хеш-функций для строк. Ниже будет разбираться пример реализации хеш-таблицы, в котором используется хеш-функция, взятая из [16], которая, по мнению авторов, гораздо более равномерно распределяет данные из реальных текстовых файлов по хеш-таблице, чем способ простого суммирования кодов символов.

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