h = MULTIPLIER * h +
*p; return h % NHASH; }
В вычислениях символы принимаются неотрицательными принудительно (тем, что
используется тип unsigned char), так как ни в С, ни в C++ наличие знака у символов
не регламентировано, а мы хотим, чтобы наша хэш-функция оставалась
положительной.
Хэш-функция возвращает результат по модулю размера массива. Если хэш-функция
распределяет данные равномерно, то точный размер массива
неважен. Трудно,
однако, гарантировать, что хэш-функция независима от размера массива, и даже у
хорошей функции могут быть проблемы с некоторыми наборами входных данных.
Поэтому имеет смысл сделать размер массива простым числом, чтобы слегка
подстраховаться, обеспечив отсутствие общих делителей у размера массива, хэш-
мультипликатора и, возможно, значений данных.
Эксперименты показывают,
что для большого числа разных строк трудно придумать
хэш-функцию, которая работала бы гораздо лучше, чем эта, зато легко придумать
такую, которая работает хуже. В ранних версиях Java была хэш-функция для строк,
работавшая более эффективно, если строка была длинной. Хэш-функция работала
быстрее, анализируя только 8 или 9 символов через равные интервалы в
строках
длиннее, чем 16 символов, начиная с первого символа. К сожалению, хотя хэш-
функция работала быстрее, у нее были очень плохие статистические показатели, что
сводило на нет выигрыш в производительности. Пропуская части строки, она
нередко выкидывала именно различающиеся части строк. Имена файлов
начинаются с длинных идентичных префиксов — имен каталогов — и могут
отличаться только несколькими последними символами (например, .Java и .class).
Адреса в Internet обычно начинаются с http://www. и заканчиваются на . html, поэтому
все различия у них в середине. Хэш-функция частенько проходила только по
неразличающейся части в имени, в результате чего образовывались более длинные
хэш-цепочки, что замедляло поиск. Проблема была решена заменой хэш-функции на
эквивалентную
той, что мы привели выше (с мультипликатором 37), которая
исследует каждый символ в строке.
Хэш-функция, которая неплохо подходит для одного набора данных (например, имен
переменных), может плохо подходить для другого (адреса Internet), так что
потенциальная хэш-функция должна быть протестирована на разных наборах
типичных входных данных. Хорошо ли она перемешивает короткие
строки? Длинные
строки? Строки одинаковой длины с небольшими изменениями?
Хэшировать можно не только строки. Можно "смешать" три координаты частицы при
физическом моделировании, тем самым уменьшив размер хранилища до линейной
таблицы (0(количество точек)) вместо трехмерного массива (0(размер_по_х
хразмер_nojj X размер_no_z)).
Один примечательный случай использования хэширования — программа Джерарда
Холзманна (Gerard Holzmann) для анализа протоколов
и параллельных систем
Supertrace. Supertrace берет полную информацию о каждом возможном состоянии
наблюдаемой системы и хэширует эту информацию для получения адреса
единственного бита в памяти. Если этот бит установлен, то данное состояние
наблюдалось и раньше; если нет, то не наблюдалось. В Supertrace используется
хэш-таблица длиной во много мегабайт, однако в каждой ячейке
хранится только
один бит. Цепочки не строятся; если два разных состояния совпали по своей хэш-
функции, то программа этого не заметит. Supertrace рассчитана на то, что
вероятность коллизии мала (она не обязана быть нулем, поскольку Supertrace