This commit is contained in:
krahets
2026-04-14 18:06:14 +08:00
parent 065a978848
commit e53a7f2498
93 changed files with 565 additions and 570 deletions

View File

@@ -6,7 +6,7 @@ comments: true
В двух предыдущих разделах мы рассмотрели принципы работы хеш-таблицы и способы обработки хеш-коллизий. Однако и открытая адресация, и метод цепочек **лишь позволяют хеш-таблице корректно работать при возникновении коллизий, но не уменьшают вероятность появления самих коллизий**.
Если хеш-коллизии происходят слишком часто, производительность хеш-таблицы резко деградирует. Как показано на рисунке 6-8, для хеш-таблицы с методом цепочек в идеальном случае пары ключ-значение равномерно распределены по всем бакетам, и это дает наилучшую эффективность поиска; в худшем же случае все пары ключ-значение оказываются в одном бакете, и временная сложность вырождается до $O(n)$ .
Если хеш-коллизии происходят слишком часто, производительность хеш-таблицы резко деградирует. Как показано на рисунке 6-8, для хеш-таблицы с методом цепочек в идеальном случае пары ключ-значение равномерно распределены по всем бакетам, и это дает наилучшую эффективность поиска. В худшем же случае все пары ключ-значение оказываются в одном бакете, и временная сложность вырождается до $O(n)$ .
![Лучший и худший случаи хеш-коллизий](hash_algorithm.assets/hash_collision_best_worst_condition.png){ class="animation-figure" }
@@ -33,7 +33,7 @@ index = hash(key) % capacity
На практике хеш-алгоритмы используются не только для реализации хеш-таблиц, но и во многих других областях.
- **Хранение паролей**: чтобы защищать пароли пользователей, система обычно хранит не сами пароли в открытом виде, а их хеш-значения. Когда пользователь вводит пароль, система вычисляет хеш-значение введенного пароля и сравнивает его с сохраненным значением. Если они совпадают, пароль считается правильным.
- **Проверка целостности данных**: отправитель может вычислить хеш-значение данных и отправить его вместе с самими данными; получатель затем вычисляет хеш-значение повторно и сравнивает его с полученным. Если они совпадают, данные считаются целостными.
- **Проверка целостности данных**: отправитель может вычислить хеш-значение данных и отправить его вместе с самими данными. Получатель затем вычисляет хеш-значение повторно и сравнивает его с полученным. Если они совпадают, данные считаются целостными.
Для приложений, связанных с криптографией, чтобы не допустить восстановления исходного пароля по хеш-значению и иных форм обратного анализа, хеш-алгоритм должен обладать более строгими свойствами безопасности.
@@ -41,14 +41,14 @@ index = hash(key) % capacity
- **Устойчивость к коллизиям**: должно быть крайне трудно найти два разных входа, имеющих одинаковое хеш-значение.
- **Эффект лавины**: даже небольшое изменение во входных данных должно приводить к заметному и непредсказуемому изменению результата.
Обрати внимание: **"равномерное распределение" и "устойчивость к коллизиям" - это два независимых понятия** , и выполнение первого не означает автоматического выполнения второго. Например, при случайном распределении входных `key` хеш-функция `key % 100` может выдавать достаточно равномерное распределение. Однако этот хеш-алгоритм слишком прост: все `key` с одинаковыми двумя последними цифрами будут иметь одинаковый результат, а значит, по хеш-значению можно легко подобрать подходящие `key` и, например, взломать пароль.
Обрати внимание: **«равномерное распределение» и «устойчивость к коллизиям» - это два независимых понятия** , и выполнение первого не означает автоматического выполнения второго. Например, при случайном распределении входных `key` хеш-функция `key % 100` может выдавать достаточно равномерное распределение. Однако этот хеш-алгоритм слишком прост: все `key` с одинаковыми двумя последними цифрами будут иметь одинаковый результат, а значит, по хеш-значению можно легко подобрать подходящие `key` и, например, взломать пароль.
## 6.3.2   Проектирование хеш-алгоритма
Разработка хеш-алгоритма - это сложная задача, в которой нужно учитывать множество факторов. Однако для некоторых нетребовательных сценариев мы можем спроектировать и несколько простых хеш-алгоритмов.
- **Аддитивный хеш**: складываем ASCII-коды всех символов входной строки и используем полученную сумму как хеш-значение.
- **Мультипликативный хеш**: используем "некоррелированность" умножения; на каждом шаге умножаем текущее значение на константу и добавляем ASCII-код очередного символа.
- **Мультипликативный хеш**: используем «некоррелированность» умножения. На каждом шаге умножаем текущее значение на константу и добавляем ASCII-код очередного символа.
- **XOR-хеш**: последовательно накапливаем элементы входных данных в одном хеш-значении через операцию XOR.
- **Ротационный хеш**: последовательно накапливаем ASCII-коды символов, причем перед каждым накоплением выполняем циклический сдвиг хеш-значения.
@@ -661,7 +661,7 @@ $$
\end{aligned}
$$
Если входные `key` как раз удовлетворяют такому распределению в виде арифметической прогрессии, то хеш-значения начнут скучиваться, а это усугубит хеш-коллизии. Теперь предположим, что мы заменили `modulus` на простое число $13$ ; поскольку между `key` и `modulus` нет общих делителей, равномерность распределения хеш-значений заметно улучшится.
Если входные `key` как раз удовлетворяют такому распределению в виде арифметической прогрессии, то хеш-значения начнут скучиваться, а это усугубит хеш-коллизии. Теперь предположим, что мы заменили `modulus` на простое число $13$. Поскольку между `key` и `modulus` нет общих делителей, равномерность распределения хеш-значений заметно улучшится.
$$
\begin{aligned}
@@ -684,7 +684,7 @@ $$
На протяжении почти ста лет хеш-алгоритмы непрерывно развивались и оптимизировались. Одни исследователи старались повысить их производительность, а другие исследователи и хакеры сосредоточивались на поиске уязвимостей в их безопасности. В таблице 6-2 приведены распространенные хеш-алгоритмы, которые часто встречаются в реальных приложениях.
- MD5 и SHA-1 уже многократно были успешно атакованы, поэтому они выведены из большинства сценариев, где требуется безопасность.
- SHA-256 из семейства SHA-2 является одним из самых надежных хеш-алгоритмов; на сегодняшний день не известно успешных практических атак, поэтому он широко используется в самых разных протоколах и системах безопасности.
- SHA-256 из семейства SHA-2 является одним из самых надежных хеш-алгоритмов. На сегодняшний день не известно успешных практических атак, поэтому он широко используется в самых разных протоколах и системах безопасности.
- SHA-3 по сравнению с SHA-2 требует меньших затрат на реализацию и обеспечивает более высокую вычислительную эффективность, но на данный момент распространен слабее, чем семейство SHA-2.
<p align="center"> Таблица 6-2 &nbsp; Распространенные хеш-алгоритмы </p>
@@ -706,7 +706,7 @@ $$
Мы знаем, что `key` в хеш-таблице могут быть целыми числами, вещественными числами, строками и другими типами данных. Языки программирования обычно предоставляют встроенные хеш-алгоритмы для этих типов, чтобы вычислять индексы бакетов в хеш-таблице. Возьмем Python: в нем можно вызвать функцию `hash()` , чтобы вычислить хеш-значения для различных типов данных.
- Хеш-значение целого числа и булева значения совпадает с самим значением.
- Вычисление хеш-значений для вещественных чисел и строк устроено сложнее; интересующиеся читатели могут изучить это самостоятельно.
- Вычисление хеш-значений для вещественных чисел и строк устроено сложнее. Интересующиеся читатели могут изучить это самостоятельно.
- Хеш-значение кортежа получается путем хеширования каждого элемента, а затем объединения этих хеш-значений в одно итоговое значение.
- Хеш-значение объекта обычно строится на основе его адреса в памяти. Если переопределить метод хеширования объекта, можно реализовать вычисление хеша по содержимому.