Initial commit
This commit is contained in:
@@ -0,0 +1,139 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Страница 8</title>
|
||||
</head>
|
||||
<body>
|
||||
<p style="text-align: center">
|
||||
<i><b>Страница 8</b></i>
|
||||
</p>
|
||||
<hr />
|
||||
<h1 style="text-align: center">Анализ хеш-таблиц и алгоритмов поиска</h1>
|
||||
<h2 style="text-align: center">Общее описание [2, c. 285-318]</h2>
|
||||
<p style="text-align: justify">
|
||||
Хеш-таблица представляет собой эффективную структуру данных для реализации
|
||||
словарей. Хотя на поиск элемента в хеш-таблице может в наихудшем случае
|
||||
потребоваться столько же времени, сколько на поиск в связанном списке, а
|
||||
именно - <i>Θ(n)</i>, на практике хеширование исключительно
|
||||
эффективно. При вполне обоснованных допущениях среднее время поиска
|
||||
элемента в хеш-таблице составляет <i>O(1)</i>.
|
||||
</p>
|
||||
<h2 style="text-align: center">
|
||||
Таблица с прямой адресацией, как хеш-таблица
|
||||
</h2>
|
||||
<p style="text-align: justify">
|
||||
Хеш-таблица обобщает обычный массив. Возможность прямой индексации
|
||||
элементов обычного массива обеспечивает доступ к произвольной позиции в
|
||||
массиве за время <i>O(1)</i>. Хеширование представляет собой исключительно
|
||||
эффективную и практичную технологию: в среднем все базовые словарные
|
||||
операции выполняются за время <i>O(1)</i><i>.</i>
|
||||
</p>
|
||||
<p style="text-align: justify">
|
||||
Недостаток прямой адресации очевиден: если совокупность ключей
|
||||
<i>U</i> велика, хранение таблицы <i>T</i> размером
|
||||
<i>|U|</i> непрактично, а то и вовсе невозможно - в зависимости от
|
||||
количества доступной памяти и размера совокупности ключей. Кроме того,
|
||||
множество <i>K</i> реально сохранённых ключей может быть мало по сравнению
|
||||
с совокупностью ключей <i>U</i>, а в этом случае память, выделенная для
|
||||
таблицы <i>T</i>, в основном расходуется напрасно.
|
||||
</p>
|
||||
<p style="text-align: justify">
|
||||
Когда множество <i>K</i> хранящихся в словаре ключей гораздо меньше
|
||||
совокупности возможных ключей <i>U</i>, для хеш-таблицы требуется
|
||||
существенно меньше места, чем для таблицы с прямой адресацией<sup>1</sup>.
|
||||
Точнее говоря, требования к памяти могут быть снижены
|
||||
до <i>Θ(/K/)</i>, при этом время поиска элемента в хеш-таблице
|
||||
останется равным <i>O(1)</i>. Нужно только заметить, что это граница
|
||||
времени поиска в среднем случае, в то время как в случае таблицы с прямой
|
||||
адресацией эта граница справедлива для наихудшего случая.
|
||||
</p>
|
||||
<h2 style="text-align: center">Хеш-таблица с прямым связыванием</h2>
|
||||
<p>
|
||||
Время, необходимое для вставки в наихудшем случае, равно <i>O(1)</i>.
|
||||
Процедура вставки выполняется очень быстро, в частности, потому, что
|
||||
предполагается, что вставляемый элемент отсутствует в таблице. Время
|
||||
работы поиска в наихудшем случае пропорционально длине списка. Удаление
|
||||
элемента может быть выполнено за время <i>O(1)</i> при использовании
|
||||
дважды связанных списков.
|
||||
</p>
|
||||
<p>
|
||||
Пусть у нас есть хеш-таблица T с m ячейками, в которых хранятся n
|
||||
элементов. В наихудшем случае хеширование с цепочками ведёт себя крайне
|
||||
неприятно: все n ключей хешированы в одну и ту же ячейку, создав список
|
||||
длиной n. Таким образом, время поиска в наихудшем случае равно <i
|
||||
style="text-align: justify"
|
||||
>Θ(n)</i
|
||||
>
|
||||
плюс время вычисления хеш-функции, что ничуть не лучше, чем в случае
|
||||
использования связного списка для хранения всех n элементов. Понятно, что
|
||||
использование хеш-таблиц в наихудшем случае совершенно бессмысленно.
|
||||
</p>
|
||||
<h3>Теорема 8.1<sup>2</sup></h3>
|
||||
<p>
|
||||
В хеш-таблице с разрешением коллизий методом цепочек время неудачного
|
||||
поиска в среднем случае в предположении простого равномерного хеширования
|
||||
составляет <i style="text-align: justify">Θ(1 + a).</i>
|
||||
</p>
|
||||
<h3>Теорема 8.2</h3>
|
||||
<p>
|
||||
В хеш-таблице с разрешением коллизий методом цепочек время успешного
|
||||
поиска в среднем случае в предположении простого равномерного хеширования
|
||||
в среднем равно <i style="text-align: justify">Θ(1 + a).</i>
|
||||
</p>
|
||||
<p style="text-align: justify">
|
||||
Если количество ячеек в хеш-таблице как минимум пропорционально количеству
|
||||
элементов, хранящихся в ней, то <i>n = O(m)</i> и, следовательно,
|
||||
коэффициент заполнения равен <i>n/m = O(m)/m = O(1).</i> Таким
|
||||
образом, поиск элемента в хеш-таблице в среднем требует постоянного
|
||||
времени. Поскольку в худшем случае вставка элемента в хеш-таблицу занимает
|
||||
O(1) времени (как и удаление элемента при использовании дважды связанных
|
||||
списков), можно сделать вывод, что все словарные операции в хеш-таблице в
|
||||
среднем выполняются за время O(1).
|
||||
</p>
|
||||
<h2 style="text-align: center">Хеш-таблица с открытой адресацией</h2>
|
||||
<p style="text-align: justify">
|
||||
Мы проанализируем математическое ожидание количества исследований для
|
||||
хеширования с открытой адресацией в предположении равномерного хеширования
|
||||
и начнём с анализа количества исследований в случае неудачного поиска.
|
||||
</p>
|
||||
<h3>Теорема 8.3</h3>
|
||||
<p>
|
||||
Математическое ожидание количества исследований при неудачном поиске в хеш
|
||||
таблице с открытой адресацией и коэффициентом заполнения
|
||||
<i>a = n/m < 1</i> в предположении равномерного хеширования не
|
||||
превышает <i>1/(1-a).</i>
|
||||
</p>
|
||||
<h3>Следствие 8.1</h3>
|
||||
<p>
|
||||
Вставка элемента в хеш-таблицу с открытой адресацией и коэффициентом
|
||||
заполнения a в предположении равномерного хеширования требует в среднем не
|
||||
более <i>1/(1-a)</i> исследований.
|
||||
</p>
|
||||
<h3>Теорема 8.4</h3>
|
||||
<p>
|
||||
Математическое ожидание количества исследований при удачном поиске в
|
||||
хеш-таблице с открытой адресацией и коэффициентом заполнения <i>a < 1</i>, в
|
||||
предположении равномерного хеширования и равновероятного поиска любого из
|
||||
ключей не превышает
|
||||
</p>
|
||||
<div style="text-align: center">
|
||||
<img src="8.1.png" width="150" />
|
||||
</div>
|
||||
<p> </p>
|
||||
<hr />
|
||||
<p>
|
||||
<sup>1</sup>В данном контексте автор, упоминая таблицу с прямой
|
||||
адресацией, не применяет термин хеш, так как, предположительно, в примере
|
||||
оригинальной книги не используется хеш-функция для операций поиска
|
||||
элемента или все операции вставки элемента являются инъективными и не
|
||||
нуждаются в решении проблем коллизий.
|
||||
</p>
|
||||
<p>
|
||||
<sup>2</sup>Все доказательства теорем представлены в книге автора с целью
|
||||
описания минимума методического материала
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,149 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Страница 2</title>
|
||||
</head>
|
||||
<body>
|
||||
<p style="text-align: center">
|
||||
<i><b>Страница 2</b></i>
|
||||
</p>
|
||||
<hr />
|
||||
<h1 style="text-align: center">Дискретная математика</h1>
|
||||
<h2 style="text-align: center">
|
||||
Функция, как тип бинарных отношений [4, c. 96-100]
|
||||
</h2>
|
||||
<p>
|
||||
Отношения эффективно применяются для описания связей между парами
|
||||
элементов, выбранных из двух множеств <i>А</i> и <i>В</i>. Функции —
|
||||
это частный случай бинарных отношений, на которые наложены дополнительные
|
||||
ограничения.
|
||||
</p>
|
||||
<p>
|
||||
Функцией из множества <i>А</i> в множество <i>В</i> называется бинарное
|
||||
отношение, при котором <i>каждый</i> элемент множества <i>А</i> связан с
|
||||
единственным элементом множества <i>В</i>. Другими словами, для каждого
|
||||
<i>a ∈ A</i> существует ровно одна пара из отношения
|
||||
вида <i>(а, b)</i>.
|
||||
</p>
|
||||
<p>
|
||||
В графических терминах функция описывается таким графом, у которого из
|
||||
каждой вершины, изображающей элементы множества <i>A</i>, выходит
|
||||
<i>ровно одна</i> стрелка.
|
||||
</p>
|
||||
<p>
|
||||
Например, на рис. 2.1 изображен граф, представляющий функцию из множества
|
||||
<i>{а, b, с}</i> в <i>{1, 2}</i>, состоящую из пар <i>(а, 1),</i>
|
||||
<i>(b, 1)</i> и <i>(с, 2)</i>.
|
||||
<div style="text-align: center">
|
||||
<img src="2.1.png" height="150" />
|
||||
<p style="text-align: center">Рисунок 2.1 - функция из множества</p>
|
||||
</div>
|
||||
</p>
|
||||
<p style="text-align: justify">
|
||||
Пусть <i>f</i> - функция из множества <i>A</i> в множество <i>B</i>.
|
||||
Поскольку для каждого <i>x ∈ A</i> существует единственным
|
||||
образом определённый <i>y∈ B</i>, такой, что
|
||||
<i>(x, y) ∈ f</i>, мы будем писать: <i>y = f(x)</i>, и говорить, что
|
||||
функция <i>f</i> <i>отображает </i>множество <i>A</i> в множество
|
||||
<i>B</i>, а <i>f(x)</i> называть <i>образом x</i> при отображении
|
||||
<i>f</i> или <i>значением</i> <i>f</i>, соответствующим аргументу
|
||||
<i>x</i>.
|
||||
</p>
|
||||
<p style="text-align: justify">
|
||||
Кроме того, можно написать <i>f : A → B</i>, чтобы
|
||||
подчеркнуть, что функция <i>f</i> переводит элементы из <i>A</i> в
|
||||
элементы из <i>B</i>. Множество <i>A </i>принято называть
|
||||
<i>областью определения</i>, а <i>B</i> -
|
||||
<i>областью значений</i> функции<sup>1</sup> <i>f</i>.
|
||||
</p>
|
||||
<p style="text-align: justify">
|
||||
Для уточнения подмножества элементов, в которые переводятся элементы из
|
||||
<i>A</i> функцией <i>f</i>, вводят понятие "образ" или
|
||||
"множество значений функции". А именно,
|
||||
<i>множеством значений</i> функции <i>f</i> называется подмножество в
|
||||
<i>B</i>, состоящее из образов всех
|
||||
элементов <i>x ∈ A</i>. Оно обозначается символом
|
||||
<i>f(A)</i> и формально определяется так:
|
||||
</p>
|
||||
<p style="text-align: center">
|
||||
<i
|
||||
>f(A) = {f(x) : x <i style="text-align: justify">∈ </i
|
||||
>A}</i
|
||||
>
|
||||
</p>
|
||||
<p style="text-align: center"> </p>
|
||||
<h2 style="text-align: center">Свойства функций</h2>
|
||||
<p style="text-align: justify">
|
||||
Пусть <i>f : A → B</i> - функция. Мы будем называть её
|
||||
<i>инъективной </i>или <i>инъекцией</i>, или <i>взаимно однозначной</i>,
|
||||
если
|
||||
</p>
|
||||
<p style="text-align: center">
|
||||
<i
|
||||
>f(a<sub>1</sub>) = f(a<sub>2</sub>) ⇒ a<sub>1 </sub
|
||||
>=<sub> </sub>a<sub>2</sub></i
|
||||
>
|
||||
</p>
|
||||
<p style="text-align: justify">
|
||||
для всех <i>a<sub>1</sub>, a<sub>2 </sub>∈ A</i>.
|
||||
</p>
|
||||
<p style="text-align: justify">
|
||||
Это определение логически эквивалентно тому, что
|
||||
</p>
|
||||
<p style="text-align: center">
|
||||
<i
|
||||
>a<sub>1 </sub>≠ a<sub>2</sub> ⇒ f(a<sub>1</sub>) ≠ f(a<sub>2</sub>),</i
|
||||
>
|
||||
</p>
|
||||
<p style="text-align: justify">
|
||||
т.е. у инъективной функции нет повторяющихся значений. Иными словами,
|
||||
разные входные данные дают различные выходные данные.
|
||||
</p>
|
||||
<p style="text-align: justify">
|
||||
Будем называть функцию <i>f</i> <i>сюръективной </i>или <i>сюръекцией</i>,
|
||||
или <i>функцией "на"</i>, если множество её значений совпадает с
|
||||
областью значений. Это означает, что для каждого
|
||||
<i>b ∈ B</i> найдётся такой <i>a ∈ A</i>,
|
||||
что <i>b = f(a)</i>. Таким образом, каждый элемент области значений
|
||||
является образом какого-то элемента из области определения <i>f</i> .
|
||||
</p>
|
||||
<p style="text-align: justify">
|
||||
Мы называем <i>f</i> <i>биективной </i>функцией или просто
|
||||
<i>биекцией</i>, если она как инъективна, так и сюръективна.
|
||||
</p>
|
||||
<p style="text-align: justify"> </p>
|
||||
<p>На рисунке 2.2 представлены различные виды функций.</p>
|
||||
<div style="text-align: center">
|
||||
<img src="2.2.png" height="350" />
|
||||
<p style="text-align: center">Рисунок 2.2 - различные виды функций</p>
|
||||
</div>
|
||||
<p>Описание:</p>
|
||||
<ul>
|
||||
<li>
|
||||
В случае (a) функция не инъективна, поскольку значение 1 соответствует
|
||||
как a, так и b. Она не является и сюръективной, ввиду того, что в
|
||||
элемент 2 ничего не переходит.
|
||||
</li>
|
||||
<li>
|
||||
В случае (б) функция инъективна, так как не имеет повторяющихся
|
||||
значений. Она же и сюръективна, поскольку множество ее значений
|
||||
совпадает со всей областью значений.
|
||||
</li>
|
||||
<li>
|
||||
Значение 1 эта функция в случае (в) принимает как на а, так и на Ь.
|
||||
Следовательно, она не инъективна. Однако данная функция сюръективна,
|
||||
поскольку в ее множество значений входят все элементы области значений.
|
||||
</li>
|
||||
<li>Последняя функция в случае (г) инъективна, но не сюръективна.</li>
|
||||
<li>Только в случае (б) мы имеем биекцию.</li>
|
||||
</ul>
|
||||
<hr />
|
||||
<p>
|
||||
<sup>1</sup>Довольно часто говорят, что функция f определена или задана на
|
||||
множестве A со значениями в множестве B. - Прим. перев.
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,56 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Страница 6</title>
|
||||
</head>
|
||||
<body>
|
||||
<p style="text-align: center">
|
||||
<i><b>Страница 6</b></i>
|
||||
</p>
|
||||
<hr />
|
||||
<h1 style="text-align: center">Решение проблем коллизий</h1>
|
||||
<h2 style="text-align: center">Метод цепочек или прямое связывание [5, c. 170-171]</h2>
|
||||
<p style="text-align: justify">
|
||||
В хеш таблице с прямым связыванием (рис 6.1) значения ключей (бункеры)
|
||||
хранятся в специальных наборах записей, называемых <i>блоками.</i> Каждый
|
||||
из них является вершиной связного списка, в котором находятся привязанные
|
||||
к блоку элементы.
|
||||
</p>
|
||||
<div style="text-align: center">
|
||||
<img src="6.1.png" height="350" />
|
||||
<p style="text-align: center">Рисунок 6.1 - хеш-таблица с прямым связыванием</p>
|
||||
</div>
|
||||
<p style="text-align: justify">
|
||||
Поиск элементов в хеш-таблице пройдёт быстрее, если связанные списки будут
|
||||
содержать ключи в отсортированном порядке. В этом случае алгоритм сделает
|
||||
вывод, что ключа нет, если дойдёт до значения больше ключевого и не станет
|
||||
просматривать список до конца. Теоретически время его работы составит
|
||||
<i>O(N/B)</i>, но на практике оно будет немного меньше.
|
||||
</p>
|
||||
<p style="text-align: justify">
|
||||
Чтобы найти нужный элемент, программе необходимо хешировать ключ и
|
||||
определить, в каком из блоков он может содержаться, а затем двигаться по
|
||||
связному списку до тех пор, пока не будет достигнут его конец или не
|
||||
обнаружится искомое. Если вы доберётесь до конца списка, значит,
|
||||
запрашиваемого элемента в хеш-таблице нет. Как и в случае с добавлением
|
||||
элемента, предстоит выполнить <i>O(N/B)</i> шагов.
|
||||
</p>
|
||||
<p style="text-align: justify">
|
||||
Хеш-таблица с прямым связыванием может расширяться и сжиматься<sup>1</sup>
|
||||
по мере необходимости, поэтому вам не нужно специально изменять её размер.
|
||||
Однако если связные списки станут слишком длинными, поиск и удаление
|
||||
элементов займут много времени. В этом случае вам понадобится увеличить
|
||||
таблицу, чтобы создать больше блоков. Поскольку при рехешировании таблицы
|
||||
не надо проводить поиск дубликатов до конца связного списка в каждом
|
||||
блоке, полностью справиться с операцией можно за время <i>O(N)</i>.
|
||||
</p>
|
||||
<hr />
|
||||
<p>
|
||||
<sup>1</sup>Удаление элементов в данном методическом материале
|
||||
рассматриваться не будет
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,52 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Страница 3</title>
|
||||
</head>
|
||||
<body>
|
||||
<p style="text-align: center">
|
||||
<i><b>Страница 3</b></i>
|
||||
</p>
|
||||
<hr />
|
||||
<h1 style="text-align: center">Дискретная математика</h1>
|
||||
<h2 style="text-align: center">Принцип Дирихле [4, c. 105]</h2>
|
||||
<p>
|
||||
Пусть <i>f : A <i style="text-align: justify">→ </i>B</i> -
|
||||
функция, причём как <i>A</i>, так и <i>B</i> - конечные множества.
|
||||
Предположим, что <i>A</i> состоит из <i>n</i> элементов:
|
||||
<i>a<sub>1</sub>, a<sub>2</sub>,..., a<sub>n</sub></i
|
||||
>. Принцип Дирихле гласит, что если <i>|A| > |B|</i>, то по крайней
|
||||
мере одно значение <i>f</i> встретится более одного раза<sup>1</sup>.
|
||||
Проще говоря, найдётся пара элементов
|
||||
<i
|
||||
>a<sub>i </sub><i style="text-align: center">≠</i
|
||||
><sub> </sub>a<sub>j</sub></i
|
||||
>, для которых <i>f(a<sub>i</sub>) = f(a<sub>j</sub>)</i>.
|
||||
</p>
|
||||
<p>
|
||||
Чтобы убедиться в истинности принципа, предположим, что для любой пары
|
||||
разных индексов
|
||||
<i>i <i style="text-align: center">≠ </i>j</i> мы имеем:
|
||||
<i>f(a<sub>i</sub>) </i><i style="text-align: center">≠ </i
|
||||
><i>f(a<sub>j</sub>)</i>. Тогда множество <i>B</i> содержит по крайней
|
||||
мере <i>n</i> различных элементов:
|
||||
<i>f(a<sub>1</sub>), f(a<sub>2</sub>),..., f(a<sub>n</sub>)</i>. И уж во
|
||||
всяком случае, <i>|B| ≥ n</i>, что противоречит
|
||||
предположению: <i>n = |A| > |B|</i>. Следовательно, есть хотя бы два
|
||||
разных элемента
|
||||
<i
|
||||
>a<sub>i</sub>, a<sub>j </sub
|
||||
><i style="text-align: justify">∈ </i>A</i
|
||||
>, для которых <i>f(a<sub>i</sub>) = f(a<sub>j</sub>)</i>.
|
||||
</p>
|
||||
<hr />
|
||||
<p>
|
||||
<sup>1</sup>Допуская некоторую вольность, принцип Дирихле можно
|
||||
переформулировать в легко запоминающейся форме: нельзя рассадить 10 зайцев
|
||||
в 9 клеток так, чтобы в каждой клетке сидел один заяц - Прим. перев.
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,102 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Страница 4</title>
|
||||
</head>
|
||||
<body>
|
||||
<p style="text-align: center">
|
||||
<i><b>Страница 4</b></i>
|
||||
</p>
|
||||
<hr />
|
||||
<h1 style="text-align: center">Хеш-фукнция</h1>
|
||||
<h2 style="text-align: center">Свойства хеш-функции</h2>
|
||||
<p style="text-align: justify">
|
||||
Хеш-функция (англ. hash function от hash —
|
||||
«превращать в фарш»), или функция свёртки —
|
||||
функция, осуществляющая преобразование массива входных данных
|
||||
произвольной длины в выходную битовую строку установленной
|
||||
длины, выполняемое определённым алгоритмом. Преобразование,
|
||||
производимое хеш-функцией, называется хешированием. Исходные данные
|
||||
называются входным массивом, «ключом» или
|
||||
«сообщением». Результат преобразования называется
|
||||
«хешем», «хеш-кодом», «хеш-суммой»,
|
||||
«сводкой сообщения».[<a
|
||||
href="https://ru.wikipedia.org/wiki/Хеш-таблица"
|
||||
>7</a
|
||||
>]
|
||||
</p>
|
||||
<p style="text-align: justify">Свойства хеш-функции:</p>
|
||||
<ul>
|
||||
<li style="text-align: justify">Должна обладать свойствами функции.</li>
|
||||
<li style="text-align: justify">
|
||||
Не должна обязательно являться инъективной или сюръективной.
|
||||
</li>
|
||||
</ul>
|
||||
<p style="text-align: justify">Определения :</p>
|
||||
<ul>
|
||||
<li style="text-align: justify">
|
||||
Если известна область определения и можно задать отображение в область
|
||||
значений, мощность которого будет больше или эквивалентна мощности
|
||||
области определения, при котором хеш-функция будет являться
|
||||
<i>инъективной</i>, то такая хеш-функция будет являться<i
|
||||
> идеальной хеш-функцией</i
|
||||
>.
|
||||
</li>
|
||||
<li style="text-align: justify">
|
||||
Если область определения неизвестна и можно задать отображение в
|
||||
область значений, мощность которого будет больше или эквивалентна
|
||||
мощности области определения, при котором хеш-функция будет
|
||||
являться <i>инъективной</i>, то такая хеш-функция будет являться<i
|
||||
> динамической идеальной хеш-функцией</i
|
||||
>.
|
||||
</li>
|
||||
<li style="text-align: justify">
|
||||
Если известна область определения и можно задать отображение в
|
||||
область значений, мощность которого будет эквивалентна мощности
|
||||
области определения, при котором хеш-функция будет
|
||||
являться <i>биективной</i>, то такая хеш-функция будет являться
|
||||
<i>минимальной идеальной хеш-функцией</i>.
|
||||
</li>
|
||||
<li style="text-align: justify">
|
||||
Хеш-функция является <i>k-совершенной хеш-функцией</i>,
|
||||
если область определения неизвестна и можно задать отображение в
|
||||
область значений, любой элемент которого будет обладать не более чем k
|
||||
отношениями.
|
||||
</li>
|
||||
</ul>
|
||||
<div style="text-align: justify">
|
||||
Существует несколько терминов, связанных с хеш-функцией:
|
||||
</div>
|
||||
<dl>
|
||||
<dt style="text-align: justify"><i>Хеш</i></dt>
|
||||
<dd style="text-align: justify">
|
||||
элемент области значений, соотнесённый с заранее известным элементом из
|
||||
области определения.
|
||||
</dd>
|
||||
<dt style="text-align: justify"><i>Хеширование</i></dt>
|
||||
<dd style="text-align: justify">это процесс получения хеша.</dd>
|
||||
<dt style="text-align: justify"><i>Коллизия</i></dt>
|
||||
<dd style="text-align: justify">
|
||||
ситуация, при которой нарушается условие инъективности хеш-функции.
|
||||
</dd>
|
||||
</dl>
|
||||
<p style="text-align: justify">
|
||||
Производительность хеширования в среднем случае зависит от того, насколько
|
||||
хорошо хеш-функция приближена к биективной k-совершенной хеш-функции, где
|
||||
разность количества отношейний любых двух элементов области значений
|
||||
стремиться к нулю. Другими словами, каждый элемент области значений
|
||||
обладает минимально возможным количеством отношений. Данное хеширование
|
||||
автор книги [2, с. 291] Томас Кормен называет
|
||||
<i>простым равномерным хешированием</i>.
|
||||
</p>
|
||||
<div style="text-align: justify">
|
||||
Существует множество разнообразных хеш-функций, алгоритмы которых
|
||||
применяются в различных ситуациях, например, когда требуется скорость
|
||||
вычисления хеш-значения, но с повышенным шансом появления коллизии или
|
||||
наоборот.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,120 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Страница 5</title>
|
||||
</head>
|
||||
<body>
|
||||
<p style="text-align: center">
|
||||
<i><b>Страница 5</b></i>
|
||||
</p>
|
||||
<hr />
|
||||
<h1 style="text-align: center">Хеш-таблица</h1>
|
||||
<h2 style="text-align: center">Свойства хеш-таблицы</h2>
|
||||
<p style="text-align: justify">
|
||||
Хеш-таблицы являются одним из способов хранения данных при помощи
|
||||
ассоциативного массива [<a
|
||||
href="https://ru.wikipedia.org/wiki/Ассоциативный_массив"
|
||||
target="_blank"
|
||||
>12</a
|
||||
>].
|
||||
</p>
|
||||
<p style="text-align: justify">
|
||||
Хеш-таблицы решают несколько главных проблем при оперировании с данными:
|
||||
</p>
|
||||
<ul>
|
||||
<li style="text-align: justify">
|
||||
<i
|
||||
>Создание отношения (связи) между ключом и абсолютно любым
|
||||
значением.</i
|
||||
>
|
||||
</li>
|
||||
<li style="text-align: justify"><i>Решение проблем коллизий.</i></li>
|
||||
</ul>
|
||||
<p style="text-align: justify">
|
||||
Для большего понимания материала требуется ознакомиться с новыми терминами
|
||||
хеш-таблицы:
|
||||
</p>
|
||||
<dl>
|
||||
<dt><i>Ключ или сообщение</i></dt>
|
||||
<dd>использующийся хеш-таблицей произвольный элемент из области определения хеш-функции.</dd>
|
||||
<dt><i>Бункер или корзина</i></dt>
|
||||
<dd>произвольный элемент, который хранит данные хеш-таблицы.</dd>
|
||||
<dt><i>Коэффициент заполнения</i></dt>
|
||||
<dd>
|
||||
отношение количества заполненных бункеров на количество возможных ключей.
|
||||
</dd>
|
||||
</dl>
|
||||
<p style="text-align: justify">
|
||||
Исходя из определений, бункер - это условная единица хранимой информации в
|
||||
хеш-таблице (ассоциативном массиве данных).
|
||||
</p>
|
||||
<p>
|
||||
<i
|
||||
><span style="text-align: justify"
|
||||
>Решение первой проблемы. </span
|
||||
></i
|
||||
>Хеш-функции в хеш-таблице, нужны по причине получения хеша в процессе
|
||||
хеширования ключа, однако хеш - значение абстрактное, так как невозможно
|
||||
создать такой алгоритм, который бы выдавал хеш, как набор данных,
|
||||
используемый в процессе работы программы или это очень сложно.<br />
|
||||
Для большего понимания нужно вспомнить пример о <span
|
||||
style="text-align: justify"
|
||||
>"продавце в маленьком магазинчике", где "помощница"
|
||||
абстрагирует понятие хеш-функции, которая выдавала для каждого товара
|
||||
конкретную цену, что возможно, но трудозатратно.</span
|
||||
><br />
|
||||
<span style="text-align: justify"
|
||||
>Для этого требуется хеш-функция, где хеш является универсальным
|
||||
идентификатором конкретного бункера, в котором располагается необходимый
|
||||
пользователю набор данных.</span
|
||||
>
|
||||
</p>
|
||||
<p>
|
||||
Томас Кормен говорит в книге [2, c.288], что цель хеш-функции состоит в
|
||||
том, чтобы уменьшить рабочий диапазон индексов массива - размер массива
|
||||
может быть равен соотносительно намного меньшему значению, чем мощность
|
||||
множества всех существующих значений ключей, которые могут потенциально
|
||||
использоваться в процессе работы программы.
|
||||
</p>
|
||||
<p style="text-align: justify">
|
||||
<span style="text-align: justify"
|
||||
>Из этого можно выделить несколько особенностей хеш-таблиц:</span
|
||||
>
|
||||
</p>
|
||||
<ul>
|
||||
<li style="text-align: justify">
|
||||
Каждый бункер имеет в качестве уникального идентификатора
|
||||
хеш-значение
|
||||
</li>
|
||||
<li style="text-align: justify">
|
||||
Каждый ключ гарантированно имеет связь только с одним бункером.
|
||||
</li>
|
||||
<li style="text-align: justify">
|
||||
Количество бункеров относительно количества ключей сильно ограниченно.
|
||||
</li>
|
||||
<li style="text-align: justify">
|
||||
От хеш-функции зависит коэффициент заполнения.
|
||||
</li>
|
||||
<li style="text-align: justify">
|
||||
По принципу Дирихле коллизия неизбежна в ситуации, когда количество ключей больше, чем количество бункеров.
|
||||
</li>
|
||||
</ul>
|
||||
<p style="text-align: justify">
|
||||
<i>Решение второй проблемы</i>. Хеш-таблицы используют в качестве
|
||||
алгоритма поиска элемента контейнера (бункера) хеш-функцию, которая по
|
||||
принципу Дирихле гарантированно создаст коллизии в ситуации, когда
|
||||
количество ключей больше, чем количество бункеров, а обычно так бывает
|
||||
всегда.
|
||||
</p>
|
||||
<p style="text-align: justify">
|
||||
Хеш-таблицы решают данную проблему двумя способами:
|
||||
</p>
|
||||
<ul>
|
||||
<li style="text-align: justify">Метод цепочек или прямым связыванием.</li>
|
||||
<li style="text-align: justify">Открытой адресацией.</li>
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,116 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Страница 1</title>
|
||||
</head>
|
||||
<body>
|
||||
<p style="text-align: center">
|
||||
<i><b>Страница 1</b></i>
|
||||
</p>
|
||||
<hr />
|
||||
<h1 style="text-align: center">Введение</h1>
|
||||
<h2 style="text-align: center">Гардероб [3, с. 393-394]</h2>
|
||||
<p style="text-align: justify">
|
||||
Когда вы сдаете в гардероб пальто или сумку, вам выдают номерок. Когда же
|
||||
вы собираетесь покинуть помещение, вы отдаете номерок, и вам возвращают
|
||||
ваше пальто или сумку.
|
||||
</p>
|
||||
<p style="text-align: justify">
|
||||
Если вдуматься, какую задачку решает данный процесс, можно увидеть, что
|
||||
это задача о поиске и нахождении объекта. Ваше пальто — это объект,
|
||||
который вы передаете гардеробщику; пальто сначала привязывают к
|
||||
определенному номерку, а затем возвращают вам. Метод работает за счет
|
||||
того, что в гардеробе находятся ряды пронумерованных вешалок для одежды и
|
||||
пронумерованные отделения для сумок. Вашему номерку соответствует
|
||||
определенная вешалка или определенное отделение. Гардеробщик же отвечает
|
||||
за то, чтобы разместить ваш объект в нужном месте и чтобы забрать его с
|
||||
помощью отданного вами номерка.
|
||||
</p>
|
||||
<p style="text-align: justify">
|
||||
Когда речь идет о поиске, люди чаще всего представляют себе, что нужно
|
||||
перебирать вещи до тех пор, пока не найдется нужная; процесс становится
|
||||
проще, если вещи отсортированы так, что мы можем искать среди них более
|
||||
слаженно. Гардероб же показывает, что есть и другой способ: присвоить вещи
|
||||
адрес, где она будет располагаться, и затем сразу достать ее из указанного
|
||||
места. В гардеробе адрес указан на номерках, и гардеробщик на самом деле
|
||||
ищет не ваши вещи: он смотрит на номерок, на адрес и идет к указанной
|
||||
вешалке или отделению с вашими вещами.
|
||||
</p>
|
||||
<p style="text-align: justify">
|
||||
Весь метод в целом можно назвать определением местонахождения
|
||||
<i>без поиска</i>: мы не ищем объект, а получаем адрес места, где он будет
|
||||
храниться. Соотносим объект с адресом, а затем, когда хотим найти объект,
|
||||
мы идем прямиком к нужному месту.
|
||||
</p>
|
||||
<p style="text-align: justify">
|
||||
Теперь рассмотрим данный метод с точки зрения компьютерного мира, где наши
|
||||
объекты представлены в виде записей, содержащих атрибуты. Мы хотим уметь
|
||||
переходить напрямую от записи к адресу, по которому она размещена;
|
||||
адресом будет число, соответствующее области в компьютерной памяти.
|
||||
Главная загвоздка в такой задачке — то, как соотнести адрес с
|
||||
элементом. Нам нужен быстрый и надежный способ, и мы не можем положиться
|
||||
на какого-либо посредника. Когда мы ищем запись, мы ищем ее по одному или
|
||||
большему числу атрибутов, которые формируют ключ записи, поэтому нам нужен
|
||||
метод получения адреса из ключа записи. Так как адрес — это число,
|
||||
нам нужен способ, который перенесет нас от ключей к числам. Иными словами,
|
||||
мы нуждаемся в функции, скажем, <i>f(K)</i>, которая возьмет ключ K записи
|
||||
R и вернет значение <i>a = f(K)</i>. Значение a представляет собой адрес,
|
||||
где будет храниться запись, мы идем по нему и оставляем там нашу запись.
|
||||
Каждый раз, когда нам требуется достать нашу запись с помощью ключа, мы
|
||||
снова прибегаем к функции <i>f(K)</i>, которая вернет то же адресное
|
||||
значение a, откуда мы и заберем нашу запись. Функция должна быть быстрой и
|
||||
достаточно скоро находить нужный адрес: как минимум, не дольше, чем поиск
|
||||
по записям до тех пор, пока не найдется та, которую мы ищем. В противном
|
||||
случае толку от такой функции никакого. Если же мы в самом деле
|
||||
обзаведемся такой функцией, то разрешим нашу задачку с местоположением
|
||||
новым способом: не мы будем искать элемент, а сам элемент расскажет нам,
|
||||
где его найти: расскажет с помощью нашей функции.
|
||||
</p>
|
||||
<h2 style="text-align: center">Продавец</h2>
|
||||
<p style="text-align: justify">
|
||||
Так же стоит привести введение в теоретический материал, описанное автором
|
||||
книги [1, c. 100-101], где описывается пример с "продавцом в
|
||||
маленьком магазинчике".
|
||||
</p>
|
||||
<p style="text-align: justify">
|
||||
В ситуации, когда клиент покупает
|
||||
у продавца товар необходимо определить ценность предмета.
|
||||
Изначально стоит отметить, что цены в магазинах на один и тот же товар
|
||||
всегда одинаковый в один момент времени, поэтому необходимо хранить
|
||||
информацию о ценах, например, в книге, однако данный метод оперирования
|
||||
информацией будет весьма утомителен и ужасно неэффективен. Для
|
||||
решения этой проблемы продавец нанимает помощницу, которая
|
||||
безошибочно определяет цену на товар, если посмотрит на него или узнает
|
||||
его название.
|
||||
</p>
|
||||
<p style="text-align: justify">
|
||||
Автор абстрактно описывает проблему с упорядоченным контейнером данных,
|
||||
где может потребоваться простой поиск, алгоритм которого обладает линейной
|
||||
сложностью. В данном примере контейнер является хранилищем элементов,
|
||||
каждый из которых может повторяться, но обязан иметь только один
|
||||
уникальный идентификатор. Можно с уверенностью выделить две проблемы, от
|
||||
которых можно избавиться в <i>идеальных </i>условиях — затраты на
|
||||
хранение элементов и поиск каждого из них при помощи уникального
|
||||
идентификатора. В приоритете необходимо выполнять успешный поиск элемента,
|
||||
алгоритм которого будет выполняться за константное время.
|
||||
</p>
|
||||
<p style="text-align: justify">
|
||||
Решением является использование функции [4, c. 96] из множества <i>A</i> в
|
||||
множество <i>B.</i> Данная функция описывает бинарное отношение, при
|
||||
котором <i>каждый</i> элемент множества <i>А</i> связан с
|
||||
единственным элементом множества <i>B</i>. Данная функция в
|
||||
программировании имеет название хеш-функция. Избавиться от необходимости в
|
||||
хранении множеств элементов, чтобы сохранялась возможность определять
|
||||
новые бинарные отношения, не изменяя другие очень сложно, однако это
|
||||
возможно. Обычно, когда известны все элементы множества <i>А</i> и все
|
||||
элементы множества <i>B</i>, реализуют функцию, при которой любой элемент
|
||||
множества <i>А</i> имеет одно отношение с любым элементом из множества
|
||||
<i>B</i>. Например, данную функцию можно применить в примере с продавцом и
|
||||
помощницей, если каталог товаров не изменяется, но цены на разные товары
|
||||
могут совпадать.
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,170 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Теоретический материал</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1 style="text-align: center">Теоретический материал</h1>
|
||||
|
||||
<h2 style="text-align: center">
|
||||
Необходимый минимум теории по теме<br />
|
||||
"Алгоритм вставки в хеш-таблицу"
|
||||
</h2>
|
||||
|
||||
<h3>Описание страницы:</h3>
|
||||
|
||||
<p>
|
||||
Главная страница, предоставляющая пользователю быстрый и удобный поиск
|
||||
нужного разбитого по темам материала.<br />
|
||||
Каждый элемент списка оглавления является гиперссылкой при нажатии на
|
||||
которую можно мгновенно перейти к необходимому разделу информации.<br />
|
||||
Предоставленный пользователю теоретический материал был взят из списка
|
||||
использованной литературы в конце данной страницы.
|
||||
</p>
|
||||
|
||||
<p>Оглавление:</p>
|
||||
|
||||
<ol>
|
||||
<li>
|
||||
<a href="introduction.html">Введение.</a>
|
||||
</li>
|
||||
<li>Дискретная математика:
|
||||
<ul>
|
||||
<li>
|
||||
<a href="binaryRelationship.html">Функция.</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="dirichletPrinciple.html">Принцип Дирихле.</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<a href="hashFunction.html">Хеш-функция.</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="hashTable.html">Хеш-таблица.</a>
|
||||
</li>
|
||||
<li>Решение проблем коллизий:
|
||||
<ul>
|
||||
<li>
|
||||
<a href="chainMethod.html">Метод цепочек или прямое связывание.</a>
|
||||
</li>
|
||||
<li><a href = "openAddressing.html">Открытая и прямая адресации.</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href = "analysis.html">Анализ хеш-таблиц и алгоритмов поиска.</a></li>
|
||||
</ol>
|
||||
|
||||
<h3 style="text-align: center">Список использованной литературы</h3>
|
||||
|
||||
<ol>
|
||||
<li>
|
||||
<em
|
||||
>Бхаргава, А. Грокаем алгоритмы. Иллюстрированное пособие для
|
||||
программистов и любопытствующих / Адитья Бхаргава. — СПб. :
|
||||
Питер, 2017. - 288 с. : ил. — (Библиотека программиста).</em
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<em
|
||||
>Кормен, Т. Х. Алгоритмы: построение и анализ, 3-е изд / Томас Кормен ; пер. с англ. — Москва :
|
||||
ООО "И. Д. Вильямс", 2013. — 1328 с. : ил. — Парал. тит. англ.</em
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<em
|
||||
>Луридас, П. Алгоритмы для начинающих : теория и практика для
|
||||
разработчика / Панос Луридас ; пер. с англ. Е.М. Егоровой. — Москва :
|
||||
Эксмо, 2020. — 608 с. — (Мировой компьютерный бестселлер).</em
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<em
|
||||
>Хаггарти, Р. Дискретная математика для программистов, 2-е издание, исправленное / Род
|
||||
Стивенс. — Москва : ТЕХНОСФЕРА, 2019. — 400 c.</em
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<em
|
||||
>Стивенс, Р. Алгоритмы. Теория и практическое применение / Род
|
||||
Стивенс. — Москва : Издательство "Э", 2016. — 544 c. — (Мировой
|
||||
компьютерный бестселлер).</em
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<em
|
||||
>Хеш-таблица [Электронный ресурс]: Википедия. Свободная энциклопедия.
|
||||
— URL: <a
|
||||
href="https://ru.wikipedia.org/wiki/Хеш-таблица"
|
||||
target="_blank"
|
||||
>https://ru.wikipedia.org/wiki/Хеш-таблица</a
|
||||
> (дата обращения: 03.11.2021).</em
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<em
|
||||
>Хеш-функция [Электронный ресурс]: Википедия. Свободная энциклопедия.
|
||||
— URL: <a
|
||||
href="https://ru.wikipedia.org/wiki/Хеш-функция"
|
||||
target="_blank"
|
||||
>https://ru.wikipedia.org/wiki/Хеш-функция</a
|
||||
> (дата обращения: 03.11.2021).</em
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<em
|
||||
>Хеш-функция, что это такое? [Электронный ресурс]: Хабр. Сообщество
|
||||
IT-специалистов. — URL: <a
|
||||
href="https://habr.com/ru/post/534596/"
|
||||
target="_blank"
|
||||
>https://habr.com/ru/post/534596/</a
|
||||
>
|
||||
(дата обращения: 03.11.2021).</em
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<em
|
||||
>Хеш-таблицы [Электронный ресурс]: Хабр. Сообщество IT-специалистов. —
|
||||
URL: <a href="https://habr.com/ru/post/509220/" target="_blank"
|
||||
>https://habr.com/ru/post/509220/</a
|
||||
>
|
||||
(дата обращения: 03.11.2021).</em
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<em
|
||||
>Хеш-таблицы [Электронный ресурс]: CodeChick. Задачи по
|
||||
программированию с проверкой. — URL: <a
|
||||
href="https://codechick.io/tutorials/dsa/dsa-hash-table"
|
||||
target="_blank"
|
||||
>https://codechick.io/tutorials/dsa/dsa-hash-table</a
|
||||
> (дата обращения: 03.11.2021).</em
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<em
|
||||
>Fowler–Noll–Vo hash function [Электронный ресурс]: Википедия.
|
||||
Свободная энциклопедия. — URL: <a
|
||||
href="https://en.wikipedia.org/wiki/Fowler–Noll–Vo_hash_function"
|
||||
target="_blank"
|
||||
>https://en.wikipedia.org/wiki/Fowler–Noll–Vo_hash_function</a
|
||||
> (дата обращения: 03.11.2021).</em
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<em
|
||||
>Ассоциативный массив [Электронный ресурс]: Википедия.
|
||||
Свободная энциклопедия. — URL: <a
|
||||
href="https://ru.wikipedia.org/wiki/Ассоциативный_массив"
|
||||
target="_blank"
|
||||
>https://ru.wikipedia.org/wiki/Ассоциативный_массив</a
|
||||
> (дата обращения: 06.11.2021).</em
|
||||
>
|
||||
</li>
|
||||
</ol>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,274 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Страница 7</title>
|
||||
</head>
|
||||
<body>
|
||||
<p style="text-align: center">
|
||||
<i><b>Страница 7</b></i>
|
||||
</p>
|
||||
<hr />
|
||||
<h1 style="text-align: center">Решение проблем коллизий</h1>
|
||||
<h2 style="text-align: center">
|
||||
Открытая и прямая адресации [5, c. 170-179]
|
||||
</h2>
|
||||
<p>Оглавление:</p>
|
||||
<ul>
|
||||
<li><a href="#description">Описание.</a></li>
|
||||
<li><a href="#linear">Линейное пробирование.</a></li>
|
||||
<li><a href="#quadratic">Квадратичное пробирование.</a></li>
|
||||
<li><a href="#pseudorandom">Псевдослучайное пробирование.</a></li>
|
||||
<li><a href="#double">Двойное хеширование.</a></li>
|
||||
</ul>
|
||||
<div>
|
||||
<h3 style="text-align: center"><a name="description">Описание</a></h3>
|
||||
<p style="text-align: justify">
|
||||
Безусловно, прямое связывание имеет свои преимущества. Основное из них
|
||||
заключается в том, что общее количество значений не зависит от
|
||||
количества блоков. Однако есть у таких хеш-таблиц и некоторые
|
||||
недостатки. Например, если элементов в блоках накопится слишком много,
|
||||
поиск нужного займет продолжительное время. Чтобы сократить временные
|
||||
затраты, число блоков можно увеличить, но тогда вы рискуете получить ряд
|
||||
пустых, которые потребуют места в памяти и не будут использоваться
|
||||
таблицей.
|
||||
</p>
|
||||
<p style="text-align: justify">
|
||||
Еще один способ реализации хеш-таблиц — <i>открытая адресация</i>.
|
||||
В этом случае значения хранятся в массиве, а функция хеширования
|
||||
представляет собой некоторые расчеты.
|
||||
</p>
|
||||
<p style="text-align: justify">
|
||||
Хеш-таблица с <i>прямой адресацией</i> отличается от хеш-таблицы с
|
||||
открытой адресацией тем, что в первом случае гарантировано должна
|
||||
использоваться идеальная хеш-функция. Это обусловлено тем, что в данной
|
||||
хеш-таблице должны отсутствовать коллизии, соответственно, как и методы
|
||||
их разрешения. Стоит отметить, что под данное описание подходит
|
||||
<i>идеальное хеширование</i> хеш-таблицы<sup>1</sup>.
|
||||
</p>
|
||||
<p style="text-align: justify">
|
||||
В разных видах открытой адресации используются различные функции
|
||||
хеширования. Неодинакова и политика разрешения коллизий, но в общем
|
||||
случае она выглядит так: для каждого значения в массиве подбирается
|
||||
несколько ячеек, и если первая уже занята, алгоритм пробует использовать
|
||||
вторую, затем третью и так до тех пор, пока не найдет свободную или не
|
||||
придет к выводу, что таковой нет.
|
||||
</p>
|
||||
<p style="text-align: justify">
|
||||
Серия ячеек, которую алгоритм подбирает для значения, называется пробной
|
||||
последовательностью. По ее средней длине хорошо оценивать наполненность
|
||||
хештаблицы. В идеале пробная последовательность должна равняться 1 или
|
||||
2, большие цифры говорят о полной таблице.
|
||||
</p>
|
||||
<p style="text-align: justify">
|
||||
Иногда политика разрешения коллизий такова, что для элемента может не
|
||||
найтись свободной ячейки, даже когда она есть. Если пробная
|
||||
последовательность повторяет саму себя перед тем, как проверить
|
||||
очередную запись, некоторые записи могут остаться неиспользованными.
|
||||
</p>
|
||||
<p style="text-align: justify">
|
||||
Чтобы найти элемент в хеш-таблице, алгоритм следует за пробной
|
||||
последовательностью, пока не произойдет одно из трех событий.
|
||||
</p>
|
||||
<ol>
|
||||
<li style="text-align: justify">
|
||||
Если пробная последовательность сумела отыскать элемент, задача
|
||||
выполнена.
|
||||
</li>
|
||||
<li style="text-align: justify">
|
||||
Если пробная последовательность находит пустую запись в массиве,
|
||||
элемента нет.
|
||||
</li>
|
||||
<li style="text-align: justify">
|
||||
Пробная последовательность проверяет <i>M</i> записей (по размеру
|
||||
массива) — и алгоритм приходит к выводу, что значение
|
||||
отсутствует. Последовательность может перебрать не все элементы, но
|
||||
если пройдет по всем, вы будете знать, что они точно пересмотрены или
|
||||
что целевой элемент не найден. Она также может проверить в цикле одну
|
||||
и ту же позицию несколько раз. В любом случае значение не должно
|
||||
присутствовать, поскольку иначе оно бы добавлялось к массиву с
|
||||
использованием той же пробной последовательности.
|
||||
</li>
|
||||
</ol>
|
||||
<p style="text-align: justify">
|
||||
При разумном заполнении хеш-таблицы открытая адресация работает очень
|
||||
быстро. Если длина пробной последовательности равна 1 или 2, добавление
|
||||
и нахождение элементов выполняются за время <i>O(1)</i>. Но если массив
|
||||
из N элементов существенно переполнен, производительность снижается. В
|
||||
наихудшем случае алгоритм придет к выводу, что элемента в массиве нет,
|
||||
за время <i>O(N)</i>. Поиск присутствующих элементов также будет
|
||||
выполняться крайне медленно.
|
||||
</p>
|
||||
<p style="text-align: justify">
|
||||
Вы можете увеличить размер массива, чтобы уменьшить коэффициент
|
||||
наполненности хеш-таблицы. Для этого создайте новый массив и рехешируйте
|
||||
элементы в нем. Для каждого из них операция займет <i>O(1)</i> времени,
|
||||
а общая производительность алгоритма составит <i>O(N)</i>.
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<h3 style="text-align: center">
|
||||
<a name="linear">Линейное пробирование</a>
|
||||
</h3>
|
||||
<p style="text-align: justify">
|
||||
В линейном пробировании политика разрешения коллизий добавляет к каждой
|
||||
ячейке постоянное число (чаще всего 1), называемое шагом по индексу,
|
||||
которое генерирует пробную последовательность. При каждом очередном
|
||||
добавлении берется размер массива по модулю, стало быть, при
|
||||
необходимости последовательность возвращается к началу массива.
|
||||
</p>
|
||||
<p style="text-align: justify">
|
||||
Предположим, в хеш-таблице 100 элементов, а правило хеширования звучит
|
||||
следующим образом: N связано с ячейкой N mod 100. Тогда пробная
|
||||
последовательность для значения 2197 проверяет ячейки 97, 98, 99, 0, 1,
|
||||
2 и т. д.
|
||||
</p>
|
||||
<p style="text-align: justify">
|
||||
На рисунке 7.1 представлен массив из 10 записей, который уже содержит
|
||||
несколько значений. Чтобы добавить в него новое значение 71, используя
|
||||
линейную пробную последовательность, нужно связать его с ячейкой 71 mod
|
||||
10 = 1. Но эта ячейка уже занята значением 61, поэтому алгоритм
|
||||
переходит к ячейке 2, которая тоже заполнена. Следующей должна быть
|
||||
ячейка 3 — она свободна, и алгоритм размещает там 71.
|
||||
</p>
|
||||
<div style="text-align: center">
|
||||
<img src="7.1.png" width="800" />
|
||||
<p style="text-align: center">
|
||||
Рисунок 7.1 - линейная пробная последовательность
|
||||
</p>
|
||||
</div>
|
||||
<p style="text-align: justify">
|
||||
Преимущество данного метода — в его простоте. Если необходимо,
|
||||
пробная последовательность пройдет по каждой ячейке массива и вставит
|
||||
элемент в свободное место, если оно еще осталось. Но есть и
|
||||
сопутствующий недостаток — так называемая
|
||||
<i>первичная кластеризация</i>, которая проявляется в образовании
|
||||
больших блоков смежных записей и приводит к длинным пробным
|
||||
последовательностям. В результате при добавлении нового элемента и его
|
||||
хешировании к какой-либо записи в кластере пробная последовательность
|
||||
вынуждена пройти через весь кластер, чтобы найти свободную ячейку.
|
||||
</p>
|
||||
<p style="text-align: justify">
|
||||
Чтобы понять, как формируются кластеры, рассмотрим пример пустой
|
||||
хеш-таблицы. Предположим, в ней содержится <i>N</i> записей и существует
|
||||
вероятность <i>1/N</i>, что она закончится в любой данной позиции при
|
||||
добавлении случайного числа. А теперь представим, что <i>K</i> —
|
||||
конечная позиция таблицы. Существует вероятность <i>1/N</i>, что новое
|
||||
случайное число попадет в позицию K и линейное пробирование попытается
|
||||
расположить элемент в позиции <i>K + 1</i>. Но та же вероятность
|
||||
характерна и для случая, при котором новый элемент будет связан с
|
||||
позицией <i>K + 1</i> напрямую. Значит, существует вероятность
|
||||
<i>2/N</i>, что элемент займет конечную позицию <i>K + 1</i> и
|
||||
сформируется малый кластер.
|
||||
</p>
|
||||
<p style="text-align: justify">
|
||||
Через какое-то время таких кластеров станет много. Чем больше они будут,
|
||||
тем больше вероятность того, что новый элемент добавится в конец одного
|
||||
из них. В итоге более мелкие кластеры сольются в более крупные, массив
|
||||
заполнится ими — и возникнут длинные пробные последовательности.
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<h3 style="text-align: center">
|
||||
<a name="quadratic">Квадратичное пробирование</a>
|
||||
</h3>
|
||||
<p style="text-align: justify">
|
||||
Возникновение больших кластеров при линейном пробировании связано с тем,
|
||||
что новые элементы связываются с ячейками, стоящими в конце группы, и
|
||||
постепенно увеличивают ее. Предотвратить подобную ситуацию помогает
|
||||
<i>квадратичное пробирование</i>. Для создания пробной
|
||||
последовательности в качестве шага по индексу берется квадрат количества
|
||||
ячеек. Другими словами, если в линейном пробировании существует
|
||||
последовательность <i>K, K + 1, K + 2, K + 3,</i> ... то в квадратичном
|
||||
варианте она будет выглядеть так:
|
||||
<i>K, K + 12, K + 22, K + 32, ...</i> В этом случае, если два
|
||||
элемента окажутся связанными с разными позициями в одном и том же
|
||||
кластере, они не обязательно будут придерживаться одной пробной
|
||||
последовательности и попадут в конец кластера.
|
||||
</p>
|
||||
<p style="text-align: justify">
|
||||
На рисунке 7.2 показана хеш-таблица, в начале которой есть группа из
|
||||
пяти элементов. Новое значение 71 получает пробную последовательность
|
||||
<i>1, 1 + 12 = 2, 1 + 22 = 5, 1 + 32 = 10</i> и не добавляется к
|
||||
существующему кластеру. Значение <i>93 </i>поначалу связано с тем же
|
||||
кластером, но согласно собственной пробной последовательности
|
||||
<i>3, 3 + 12 = 4, 3 + 22 = 7</i> также не попадает в него.
|
||||
</p>
|
||||
<div style="text-align: center">
|
||||
<img src="7.2.png" width="800" />
|
||||
<p style="text-align: center">Рисунок 7.2 - Квадратичное пробирование</p>
|
||||
</div>
|
||||
<p style="text-align: justify">
|
||||
Квадратичное пробирование предотвращает первичную, но не
|
||||
<i>вторичную кластеризацию</i>, при которой значения, связанные с
|
||||
одинаковой начальной позицией в массиве, получают одну и ту же пробную
|
||||
последовательность, иногда очень длинную. В результате образуется точно
|
||||
такая же группа элементов, но уже не собранных вместе, а распределенных
|
||||
по всему массиву.
|
||||
</p>
|
||||
<p style="text-align: justify">
|
||||
Еще один недостаток квадратичного пробирования связан с тем, что оно
|
||||
может не найти свободную позицию, даже если в хеш-таблице их несколько.
|
||||
Дело в том, что с каждым разом перемещение по массиву происходит все
|
||||
дальше и дальше, и незаполненная ячейка попросту пропускается.
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<h3 style="text-align: center">
|
||||
<a name="pseudorandom">Псевдослучайное пробирование</a>
|
||||
</h3>
|
||||
<p style="text-align: justify">
|
||||
Это пробирование подобно линейному, за исключением того, что шаг по
|
||||
индексу формирует псевдослучайная функция изначально связанной ячейки.
|
||||
Предположим, что это ячейка <i>K</i>, тогда пробная последовательность
|
||||
будет выглядеть следующим образом: <i>K, K + p, K + 2p, ...</i> где p
|
||||
определяется псевдослучайной функцией.
|
||||
</p>
|
||||
<p style="text-align: justify">
|
||||
Подобно квадратичному пробированию, псевдослучайное предотвращает только
|
||||
первичную кластеризацию, но страдает от вторичной: значения, связанные с
|
||||
одной и той же начальной позицией, размещаются в хеш-таблице согласно
|
||||
одной и той же пробной последовательности. Точно так же псевдослучайное
|
||||
пробирование может пропускать некоторые неиспользуемые записи.
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<h3 style="text-align: center">
|
||||
<a name="double">Двойное хеширование</a>
|
||||
</h3>
|
||||
<p style="text-align: justify">
|
||||
Чтобы избавиться от вторичной кластеризации значения, связанные с одной
|
||||
и той же начальной ячейкой, должны получать разные пробные
|
||||
последовательности. И здесь пригодится двойное хеширование. Оно похоже
|
||||
на псевдослучайное пробирование, только шаг для индекса задается не
|
||||
псевдослучайной функцией начальной ячейки, а второй функцией
|
||||
хеширования.
|
||||
</p>
|
||||
<p style="text-align: justify">
|
||||
Допустим, значения <i>A</i> и <i>B</i> связаны с позицией <i>K</i>. В
|
||||
псевдослучайном пробировании шаг по индексу <i>p</i> генерируется
|
||||
функцией <i>F<sub>1</sub> (K)</i>, затем оба значения используют пробную
|
||||
последовательность <i>K, K + p, K + 2p, K + 3p, ...</i> В двойном
|
||||
хешировании для связывания начальных значений <i>А</i> и
|
||||
<i>В</i> применяется функция псевдослучайного хеширования
|
||||
<i>F<sub>2</sub></i
|
||||
>. В итоге при одном и том же начальном <i>K</i> образуются две пробные
|
||||
последовательности с различными шагами по индексу:
|
||||
<i>p<sub>A</sub> = F<sub>2</sub> (A)</i> и
|
||||
<i>p<sub>B</sub> = F<sub>2</sub> (B)</i> соответственно.
|
||||
</p>
|
||||
<p style="text-align: justify">
|
||||
Несмотря на то, что двойное хеширование хорошо справляется с первичной и
|
||||
вторичной кластеризациями, оно точно так же, как и псевдослучайное
|
||||
пробирование, может пропускать неиспользуемые записи.
|
||||
</p>
|
||||
</div>
|
||||
<hr />
|
||||
<p>
|
||||
<sup>1</sup>Данный метод хеширования хорошо описан в книге Томаса Кормена
|
||||
[2]
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user