<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ru">
		<id>http://neerc.ifmo.ru/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Kozichuk</id>
		<title>Викиконспекты - Вклад участника [ru]</title>
		<link rel="self" type="application/atom+xml" href="http://neerc.ifmo.ru/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Kozichuk"/>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A1%D0%BB%D1%83%D0%B6%D0%B5%D0%B1%D0%BD%D0%B0%D1%8F:%D0%92%D0%BA%D0%BB%D0%B0%D0%B4/Kozichuk"/>
		<updated>2026-06-08T19:36:31Z</updated>
		<subtitle>Вклад участника</subtitle>
		<generator>MediaWiki 1.30.0</generator>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%93%D1%80%D0%B0%D1%84%D1%8B-%D1%8D%D0%BA%D1%81%D0%BF%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%8B&amp;diff=50938</id>
		<title>Графы-экспандеры</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%93%D1%80%D0%B0%D1%84%D1%8B-%D1%8D%D0%BA%D1%81%D0%BF%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%8B&amp;diff=50938"/>
				<updated>2016-01-09T02:28:33Z</updated>
		
		<summary type="html">&lt;p&gt;Kozichuk: /* Граф Рамануджана */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Граф-экспандер''' (или расширяющийся граф, англ. ''expander graph'') - в комбинаторике сильно разреженный граф, при этом связность определяется по вершинам, дугам или спектру.&lt;br /&gt;
&lt;br /&gt;
==Определение==&lt;br /&gt;
''Граф-экспандер'' — это конечный ненаправленный ''мультиграф'', в котором любое подмножество вершин, не являясь «слишком большим», имеет «сильную» связность. Различные формализации этих понятий дают различные типы экспандеров: ''рёберный расширитель'', ''вершинный расширитель'', и ''спектральный расширитель''.&lt;br /&gt;
&lt;br /&gt;
Несвязный граф не является экспандером. Любой связный граф является экспандером, однако различные связные графы имеют различные параметры расширителя. Полный граф имеет лучшие параметры расширителя, но имеет наибольшую возможную степень. Неформально говоря, граф является хорошим экспандером, если он имеет низкую степень и высокий параметр расширителя.&lt;br /&gt;
&lt;br /&gt;
===Реберное расширение===&lt;br /&gt;
''Рёберное расширение'' (также ''изопериметрическое число'' или константа Чигера) &amp;lt;tex&amp;gt;h(G)&amp;lt;/tex&amp;gt; графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; для &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; вершин определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;h(G) = \min\limits_{0 &amp;lt; |S|\leqslant \frac{n}{2}} \frac{|\partial_{\text{out}}(S)|}{|S|}&amp;lt;/tex&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где минимум берётся по всем непустым множествам &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; не более чем &amp;lt;tex&amp;gt;n/2&amp;lt;/tex&amp;gt; вершин и &amp;lt;tex&amp;gt;\partial(S)&amp;lt;/tex&amp;gt; — ''граничные дуги'' множества &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, то есть, множество дуг с единственной вершиной в &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Вершинное расширение===&lt;br /&gt;
''Вершинное изопериметрическое число'' &amp;lt;tex&amp;gt;h_{out}(G)&amp;lt;/tex&amp;gt; (также ''вершинное раширение'') графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;h_{out}(G) = \min\limits_{0 &amp;lt; |S|\leqslant \frac{n}{2}} \frac{|\partial_{\text{out}}(S)|}{|S|}&amp;lt;/tex&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;tex&amp;gt;\partial_{\text{out}}(S)&amp;lt;/tex&amp;gt; — ''внешняя граница'' &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, то есть множество вершин из &amp;lt;tex&amp;gt;V(G)\setminus S&amp;lt;/tex&amp;gt;, имеющих как минимум одного соседа в &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;. В варианте этого определения (называемом ''уникальным соседним расширением'') &amp;lt;tex&amp;gt;\partial_{\text{out}}(S)&amp;lt;/tex&amp;gt; заменяется на множество вершин из &amp;lt;tex&amp;gt;V&amp;lt;/tex&amp;gt; с ''точностью одним'' соседом из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
''Вершинное изопериметрическое число'' &amp;lt;tex&amp;gt;h_{in}(G)&amp;lt;/tex&amp;gt; графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;h_{in}(G) = \min\limits_{0 &amp;lt; |S|\leqslant \frac{n}{2}} \frac{|\partial_{\text{in}}(S)|}{|S|}&amp;lt;/tex&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;tex&amp;gt;\partial_{in}(S)&amp;lt;/tex&amp;gt; — ''внутренняя граница'' &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, то есть множество вершин &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, имеющих как минимум одного соседа в &amp;lt;tex&amp;gt;V(G)\setminus S&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Спектральное расширение===&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; является d-регулярным, возможно определение в терминах линейной алгебры на основе собственных значений матрицы смежности &amp;lt;tex&amp;gt;A = A(G)&amp;lt;/tex&amp;gt; графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;A_{ij}&amp;lt;/tex&amp;gt; — число дуг между вершинами &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;. Поскольку &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; является симметричной, согласно спектральной теореме, &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; имеет &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; действительных собственных значений &amp;lt;tex&amp;gt;\lambda_1 \ge \lambda_2 \ge \cdots \ge \lambda_{n}&amp;lt;/tex&amp;gt;. Известно, что эти значения лежат в промежутке &amp;lt;tex&amp;gt;[−d, d]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
Граф регулярен тогда и только тогда, когда вектор &amp;lt;tex&amp;gt;u\in \mathbb {R} ^{n} с u_{i}=1&amp;lt;/tex&amp;gt; для всех &amp;lt;tex&amp;gt;i = 1, …, n&amp;lt;/tex&amp;gt; является собственным вектором матрицы &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt;, а его собственное число будет постоянной степенью графа. Таким образом, мы имеем &amp;lt;tex&amp;gt;Au = du&amp;lt;/tex&amp;gt;, и &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt; — собственный вектор матрицы &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; с собственными значениями &amp;lt;tex&amp;gt;λ1 = d&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt; — степень вершин графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt;. Спектральный зазор графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; определяется как &amp;lt;tex&amp;gt;d−λ2&amp;lt;/tex&amp;gt; и является мерилом спектрального расширения графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Известно, что &amp;lt;tex&amp;gt;\lambda_n = −d&amp;lt;/tex&amp;gt; тогда и только тогда, когда &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; — двудольный. Во многих случаях, например в лемме о перемешивании, необходимо ограничить снизу не только зазор между &amp;lt;tex&amp;gt;\lambda_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\lambda_2&amp;lt;/tex&amp;gt;, но и зазор между &amp;lt;tex&amp;gt;\lambda_1&amp;lt;/tex&amp;gt; и вторым максимальным по модулю собственным значением:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\lambda=\max\{|\lambda_2|, |\lambda_{n}|\}&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Поскольку это собственное значение соответствует некоторому собственному вектору, ортогональному &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;, его можно определить, используя отношение Рэлея:&lt;br /&gt;
&amp;lt;tex&amp;gt;\lambda=\max_{0\neq v\perp u} \frac{\|Av\|_2}{\|v\|_2},&amp;lt;/tex&amp;gt;&lt;br /&gt;
gde&lt;br /&gt;
&amp;lt;tex&amp;gt;\|v\|_2=\left(\sum_{i=1}^n v_i^2\right)^{1/2}&amp;lt;/tex&amp;gt;&lt;br /&gt;
— евклидова норма вектора &amp;lt;tex&amp;gt;v\in \mathbb {R} ^{n}&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Нормализованная версия этого определения также широко используется и более удобна для получения определённых результатов. В таком случае рассматривается матрица &amp;lt;tex&amp;gt;{\tfrac {1}{d}}A&amp;lt;/tex&amp;gt;, являющаяся матрицей переходов графа G. Все её собственные значения лежат между &amp;lt;tex&amp;gt;−1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;. Если граф не регулярен, спектр графа может быть определён аналогичным образом, используя собственные значения матрицы Кирхгофа. Для направленного графа используются сингулярные значения матрицы сопряжения A, которые равны квадратным корням из собственных значений симметричной матрицы &amp;lt;tex&amp;gt;A^TA&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Конструирование==&lt;br /&gt;
Существуют три основные стратегии создания семейств графов расширений. Первая стратегия — алгебраическая и теоретико-групповая, вторая — аналитическая, использующая аддитивную комбинаторику, и третья — комбинаторная, использующая зигзаг-произведение и связанные комбинаторные произведения.&lt;br /&gt;
===Маргулис-Габбер-Галил===&lt;br /&gt;
Алгебраическое конструирование, основанное на графах Кэли, известно для различных вариантов экспандеров. Следующее конструирование принадлежит Маргулису и было проанализировано Габбером (Gabber) и Галилом (Galil). Для любого натурального &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; строим граф, &amp;lt;tex&amp;gt;G_{n}&amp;lt;/tex&amp;gt; со множеством вершин &amp;lt;tex&amp;gt;\mathbb Z _{n}\times \mathbb {Z} _{n}&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;\mathbb {Z} _{n}=\mathbb Z /n \mathbb Z&amp;lt;/tex&amp;gt; . Для любой вершины &amp;lt;tex&amp;gt;(x,y)\in \mathbb {Z} _{n}\times \mathbb {Z} _{n}&amp;lt;/tex&amp;gt;, её восемь соседей будут&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;(x \pm 2y,y), (x \pm (2y+1),y), (x,y \pm 2x), (x,y \pm (2x+1)).&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Выполняется следующая теорема:&lt;br /&gt;
{{Теорема&lt;br /&gt;
|statement=&lt;br /&gt;
Для всех &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; граф &amp;lt;tex&amp;gt;Gn&amp;lt;/tex&amp;gt; второе по величине собственное число&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;tex&amp;gt;\lambda(G)\leq 5 \sqrt{2}&amp;lt;/tex&amp;gt;.}}&lt;br /&gt;
&lt;br /&gt;
===Граф Рамануджана===&lt;br /&gt;
&amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt;-регулярный граф называется графом Рамануджана, если его второе по модулю собственное число не превосходит &amp;lt;tex&amp;gt;2{\sqrt {d-1}}&amp;lt;/tex&amp;gt;. Любоцкий, Сарнак, Филлипс и Маргулис указали явную конструкцию графов Кэли, являющихся графами Рамануджана. Опишем эту конструкцию.&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt;p&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; простые числа, &amp;lt;tex&amp;gt;p = 1 \bmod 4&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;q = 1 \bmod 4&amp;lt;/tex&amp;gt;. В качестве группы &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; возьмём &amp;lt;tex&amp;gt;PGL(2, \mathbb Z/q{\mathbb Z})&amp;lt;/tex&amp;gt;, т.е. невырожденные матрицы 2 × 2 над полем вычетов по модулю &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;, профакторизованные по отношению пропорциональности (с обычной операцией матричного умножения).&lt;br /&gt;
Далее мы зададим в этой группе симметричное множество &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;. Выберем такое целое &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;i^2 = −1 \bmod q&amp;lt;/tex&amp;gt;. Можно доказать, что тогда имеется ровно &amp;lt;tex&amp;gt;(p + 1)&amp;lt;/tex&amp;gt; целочисленное решение уравнения&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;a_0^2 + a_1^2 + a_3^2 = p&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
такое, что &amp;lt;tex&amp;gt;a_0&amp;lt;/tex&amp;gt; положительно и нечётно, а &amp;lt;tex&amp;gt;a_1&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;a_2&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;a_3&amp;lt;/tex&amp;gt; чётны. Каждой такой четвёрке сопоставим матрицу&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;A = \begin{pmatrix} a_0 + ia_1 &amp;amp; a_2 + ia_3\\ -a_2 + ia_3 &amp;amp; a_0 - ia_1 \end{pmatrix}&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Эти матрицы образуют множество S.&lt;br /&gt;
Нетрудно понять, что граф Кэли &amp;lt;tex&amp;gt;(G, S)&amp;lt;/tex&amp;gt; состоит из &amp;lt;tex&amp;gt;\Theta(q^3)&amp;lt;/tex&amp;gt; вершин, и степень каждой вершины равна &amp;lt;tex&amp;gt;(p + 1)&amp;lt;/tex&amp;gt;. Свойства данного графа зависят от соотношения &amp;lt;tex&amp;gt;p&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;. Рассмотрим случай, когда p является квадратичным вычетом по модулю &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;. Тогда полученный граф Кэли состоит из двух связных компонент (поскольку все матрицы из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; лежат в подгруппе &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; индекса два — подгруппе матриц, определитель которых является квадратичным вычетом). Обозначим &amp;lt;tex&amp;gt;X^{p,q}&amp;lt;/tex&amp;gt; связную компоненту полученного графа. Можно доказать, что у &amp;lt;tex&amp;gt;X^{p,q}&amp;lt;/tex&amp;gt; второе по абсолютной величине собственное число не превосходит &amp;lt;tex&amp;gt;2{\sqrt{p}}&amp;lt;/tex&amp;gt;, т.е. мы получили граф Рамануджана.&lt;br /&gt;
&lt;br /&gt;
==Примеры применения экспандеров==&lt;br /&gt;
===Коды, исправляющие ошибки===&lt;br /&gt;
С помощью расширяющего графа можно посторить линейный код, позволяющий исправлять ошибки в доле &amp;lt;tex&amp;gt;\delta = 1/(2000d)&amp;lt;/tex&amp;gt; битов. Чтобы задать линейный код с длиной кодового слова n, достаточно описать его проверочную матрицу &amp;lt;tex&amp;gt;H&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;(&amp;lt;/tex&amp;gt;слово &amp;lt;tex&amp;gt;x \in \{0, 1\}^n&amp;lt;/tex&amp;gt; является кодовым словом если и только если &amp;lt;tex&amp;gt;Hx^t = 0)&amp;lt;/tex&amp;gt;. Другими словами, нужно задать систему линейных уравнений для переменных &amp;lt;tex&amp;gt;x_1, . . . , x_n;&amp;lt;/tex&amp;gt; решения этой системы и будут кодовыми словами.&lt;br /&gt;
===Увеличение вероятности успеха в алгоритмах с датчиком случайных чисел===&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=Язык &amp;lt;tex&amp;gt;L&amp;lt;/tex&amp;gt; принадлежит сложностному классу &amp;lt;tex&amp;gt;RP&amp;lt;/tex&amp;gt;, если существует полиномиальный алгоритм &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; такой что&lt;br /&gt;
1. для &amp;lt;tex&amp;gt;x \in L&amp;lt;/tex&amp;gt; для всех &amp;lt;tex&amp;gt;r \in \{0, 1\}^{poly(n)}&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;A(x, r) = 1&amp;lt;/tex&amp;gt;&lt;br /&gt;
2. для &amp;lt;tex&amp;gt;x \notin L&amp;lt;/tex&amp;gt; не более чем для 1/2000 всех &amp;lt;tex&amp;gt;r \in \{0, 1\}^{poly(n)}&amp;lt;/tex&amp;gt; может выполняться &amp;lt;tex&amp;gt;A(x, r) = 1&amp;lt;/tex&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Покажем, что для любого &amp;lt;tex&amp;gt;\epsilon &amp;gt; 0&amp;lt;/tex&amp;gt; полиномиальный вероятностный алгоритм A можно модифицировать таким образом, чтобы вероятность ошибки уменьшилась до &amp;lt;tex&amp;gt;\epsilon&amp;lt;/tex&amp;gt;, а число используемых случайных битов не изменится.&lt;br /&gt;
&lt;br /&gt;
Пусть исходный алгоритм использует &amp;lt;tex&amp;gt;k = k(n)&amp;lt;/tex&amp;gt; случайных битов для вычислений на входах длины &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;. Зафиксируем &amp;lt;tex&amp;gt;(2^k, 2^k, d)&amp;lt;/tex&amp;gt; - экспандер &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;d &amp;gt; 1/\epsilon&amp;lt;/tex&amp;gt;. Новый алгоритм действует следующим образом: выбирается случайная вершина &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; из левой доли графа (для этого требуется &amp;lt;tex&amp;gt;k&amp;lt;/tex&amp;gt; случайных битов); затем исходный алгоритм &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; последовательно запускается на всех &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt; наборах случайных битов, соответствующих соседям вершины &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;. Если все полученные ответы равны &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;, новый алгоритм также возвращает единицу; в противном случае возвращается ноль.&lt;br /&gt;
Покажем, что у нового алгоритма вероятность ошибки не превосходит &amp;lt;tex&amp;gt;1/d&amp;lt;/tex&amp;gt;. В самом деле, обозначим &amp;lt;tex&amp;gt;B = B(x)&amp;lt;/tex&amp;gt; множество таких вершин &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt; из правой доли графа, которые соответствуют неверному ответу старого алгоритма на входе &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;; аналогично, обозначим &amp;lt;tex&amp;gt;S = S(x)&amp;lt;/tex&amp;gt; множество таких вершин v из левой доли графа, которые для которых новый алгоритм даёт неверный ответ на входе &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;. Очевидно, &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; состоит из вершин, все соседи которых лежат в B. Предположим, что &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; содержит не менее &amp;lt;tex&amp;gt;n/(1000d)&amp;lt;/tex&amp;gt; вершин. Выберем среди них ровно &amp;lt;tex&amp;gt;n/(1000d)&amp;lt;/tex&amp;gt; вершин и назовём это множество &amp;lt;tex&amp;gt;S'&amp;lt;/tex&amp;gt;. По свойству экспандера, имеем&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;|\Gamma(S')| \geqslant  \frac{7}{8}d\frac{n}{1000d}=7n/8000 &amp;gt; n/2000&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Это противоречит тому, что все соседи &amp;lt;tex&amp;gt;S'&amp;lt;/tex&amp;gt; лежат в &amp;lt;tex&amp;gt;B&amp;lt;/tex&amp;gt;. В данном случае нам нужна явная в более сильном (чем в первом примере) смысле конструкция экспандера. Размер графа экспоненциално растёт с увеличенем &amp;lt;tex&amp;gt;k&amp;lt;/tex&amp;gt;, и нам необходим алгоритм, который по заданному номеру вершины &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; (из левой доли графа) за время &amp;lt;tex&amp;gt;poly(k)&amp;lt;/tex&amp;gt; находит список номеров всех соседей этой вершины (в правой доле графа).&lt;br /&gt;
&lt;br /&gt;
===Хранение множества со сверхбыстрым запросом элементов===&lt;br /&gt;
Мы организуем хранение m-элементного множества &amp;lt;tex&amp;gt;S \subset \{1, . . . , n\}&amp;lt;/tex&amp;gt; в виде описания &amp;lt;tex&amp;gt;X&amp;lt;/tex&amp;gt;, состоящего из &amp;lt;tex&amp;gt;O(m log n)&amp;lt;/tex&amp;gt; битов. При этом проверка принадлежности &amp;lt;tex&amp;gt;a \in S&amp;lt;/tex&amp;gt; будет производиться чрезвычайно быстро. А именно, мы построим такой вероятностный алгоритм, который по любому входу &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; запрашивает из &amp;lt;tex&amp;gt;X&amp;lt;/tex&amp;gt; один бит; если этот бит оказывается равным единице, то алгоритм отвечает, что &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; является элементом &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;; в противном случае алгоритм говорит, что &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; множеству не принадлежит. При этом для каждого &amp;lt;tex&amp;gt;a \in \{1, . . . , n\}&amp;lt;/tex&amp;gt; алгоритм ошибается с вероятностью не более &amp;lt;tex&amp;gt;1/3&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Чтобы построить нужное нам хранилище &amp;lt;tex&amp;gt;X&amp;lt;/tex&amp;gt;, мы сначала зафиксируем некоторый экспандер, у которого левая доля &amp;lt;tex&amp;gt;L&amp;lt;/tex&amp;gt; состоит из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; вершин, правая &amp;lt;tex&amp;gt;R&amp;lt;/tex&amp;gt; из &amp;lt;tex&amp;gt;k = O(m log n)&amp;lt;/tex&amp;gt; вершин, степень всех вершин левой доли одинакова и равна некоторому &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt;, и для каждого &amp;lt;tex&amp;gt;A \subset L&amp;lt;/tex&amp;gt; размера не более &amp;lt;tex&amp;gt;2m&amp;lt;/tex&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;|\Gamma(A)| \geqslant \frac{7}{8}d|A|&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;X&amp;lt;/tex&amp;gt; будет состоять в разметке вершин правой доли нулями и единицами. Эту разметку нужно выбрать таким образом, чтобы у каждой вершины из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; не менее &amp;lt;tex&amp;gt;2/3&amp;lt;/tex&amp;gt; соседей были помечены единицей, а у каждой вершины не из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; не менее &amp;lt;tex&amp;gt;2/3&amp;lt;/tex&amp;gt; соседей были помечены нулями.&amp;lt;tex&amp;gt;X&amp;lt;/tex&amp;gt; будет состоять в разметке вершин правой доли нулями и единицами. Эту разметку нужно выбрать таким образом, чтобы у каждой вершины из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; не менее &amp;lt;tex&amp;gt;2/3&amp;lt;/tex&amp;gt; соседей были помечены единицей, а у каждой вершины не из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; не менее &amp;lt;tex&amp;gt;2/3&amp;lt;/tex&amp;gt; соседей были помечены нулями.&lt;br /&gt;
&lt;br /&gt;
Остаётся объяснить, как построить нужную нам разметку правой доли графа. Будем строить её последовательными приближениями. Сначала пометим всех соседей всех вершин из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; единицами, а все остальные вершины – нулями. На данной разметке наш алгоритм с вероятностью &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt; возвращает правильный ответ для всех &amp;lt;tex&amp;gt;a \in S&amp;lt;/tex&amp;gt;. Однако для &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; не из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; проверочный алгоритм может работать неверно. Обозначим T множество всех таких вершин вне &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, у которых более &amp;lt;tex&amp;gt;d/3&amp;lt;/tex&amp;gt; соседей помечены единицей. Поменяем разметку: пометим всех соседей &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; нулём. Теперь разметка может стать плохой для части вершин из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;. Обозначим &amp;lt;tex&amp;gt;S'&amp;lt;/tex&amp;gt; множество всех таких вершин из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, у которых более &amp;lt;tex&amp;gt;d/3&amp;lt;/tex&amp;gt; соседей помечены нулями. Далее, поменяем разметку у&lt;br /&gt;
всех соседей &amp;lt;tex&amp;gt;S'&amp;lt;/tex&amp;gt; на единицы. После этого может вновь возникнуть множество ‘неправильных’ вершин вне &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, и т.д.&lt;br /&gt;
&lt;br /&gt;
Чтобы доказать, что данный процесс в конце концов сойдётся, нужно показать, что на каждом шаге число ‘проблемных’ вершин уменьшается в константу раз. Поскольку все шаги аналогичны, достаточно разобрать самый первый: докажем, что &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; в константу раз меньше, чем &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;. Мы воспользуемся тем, что для &amp;lt;tex&amp;gt;S \cup T&amp;lt;/tex&amp;gt; выполнено свойство расширения:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;(7/8)d(|S|+|T|) \leqslant |\Gamma(S \cup T)| \leqslant d|S| + (2/3)d|T|&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Откуда получаем &amp;lt;tex&amp;gt;|T| \leqslant 3/5|S|&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Приложения и полезные свойства==&lt;br /&gt;
Первоначально интерес к экспандерам возник с целью построения устойчивой сети (телефонов или компьютеров) — число дуг графов расширения с ограниченным значением регулярности растет линейно по отношению к числу вершин.&lt;br /&gt;
&lt;br /&gt;
Экспандеры нашли широкое применение в теории вычислительных машин и систем, для построения алгоритмов, в корректирующих кодах, экстракторах, генераторах псевдослучайных чисел, сетях сортировки и компьютерных сетях. Они также используются для доказательства многих важных результатов в теории вычислительной сложности, таких как &amp;lt;tex&amp;gt;SL=L&amp;lt;/tex&amp;gt; и Теорема PCP. В криптографии экспандеры используются для создания хеш-функций.&lt;br /&gt;
&lt;br /&gt;
Ниже приведены некоторые свойства экспандеров, считающиеся полезными во многих областях.&lt;br /&gt;
===Лемма о перемешивании===&lt;br /&gt;
Лемма о перемешивании утверждает, что для любых двух подмножеств вершин &amp;lt;tex&amp;gt;S,T\subseteq V&amp;lt;/tex&amp;gt; число рёбер между &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; примерно равно числу рёбер в случайном &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt;-регулярном графе. Приближение тем лучше, чем меньше &amp;lt;tex&amp;gt;\lambda =\max\{|\lambda _{2}|&amp;lt;/tex&amp;gt;,&amp;lt;tex&amp;gt;|\lambda _{n}|\}&amp;lt;/tex&amp;gt;. В случайном &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt;-регулярном графе, также как и в случайном графе Эрдёша — Реньи с вероятностью &amp;lt;tex&amp;gt;{\tfrac {d}{n}}&amp;lt;/tex&amp;gt; выбора ребра, ожидается &amp;lt;tex&amp;gt;{\tfrac {d}{n}}\cdot |S|\cdot |T|&amp;lt;/tex&amp;gt; рёбер между &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Более формально, пусть &amp;lt;tex&amp;gt;E(S, T)&amp;lt;/tex&amp;gt; обозначает число рёбер между &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;. Если эти два множества пересекаются, дуги в пересечении считаются дважды, так что&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;E(S,T)=2|E(G[S\cap T])|+E(S\setminus T,T)+E(S,T\setminus S)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
Лемма о перемешивании утверждает, что&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\left|E(S,T)-{\frac {d\cdot |S|\cdot |T|}{n}}\right|\leq d\lambda {\sqrt {|S|\cdot |T|}}&amp;lt;/tex&amp;gt;,&lt;br /&gt;
где &amp;lt;tex&amp;gt;\lambda &amp;lt;/tex&amp;gt; — абсолютное значение нормализованного второго по величине собственного значения.&lt;br /&gt;
&lt;br /&gt;
Недавно Билу (Bilu) и Линайл (Linial) показали, что обратное тоже верно, то есть, при условии выполнения неравенства из леммы второе по величине собственное значение равно &amp;lt;tex&amp;gt;O(d\lambda \cdot (1+\log(1/\lambda )))&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Блуждания по экспандеру===&lt;br /&gt;
Согласно границе Чернова, если выбирать много независимых случайных значений из интервала &amp;lt;tex&amp;gt;[−1, 1]&amp;lt;/tex&amp;gt;, с большой степенью вероятности среднее выбранных значений будет близко к математическому ожиданию случайной переменной. Лемма о блуждании по экспандеру, согласно статьям Аджтари, Комлоша и Семереди и Гилмана, утверждает, что то же самое верно и для блужданий по экспандеру. Это полезно в теории дерандомизации, поскольку блуждание по экспандеру использует много меньше случайных бит, чем случайная независимая выборка.&lt;br /&gt;
&lt;br /&gt;
==Источники информации==&lt;br /&gt;
*[https://ru.wikipedia.org/wiki/%D0%AD%D0%BA%D1%81%D0%BF%D0%B0%D0%BD%D0%B4%D0%B5%D1%80_(%D1%82%D0%B5%D0%BE%D1%80%D0%B8%D1%8F_%D0%B3%D1%80%D0%B0%D1%84%D0%BE%D0%B2) Экспандер (теория графов)]&lt;br /&gt;
*[S. Hoory, N. Linial, A. Wigderson. Expander graphs and their applications. Bulletin of the AMS, vol. 43, Number 4, Oct. 2006, pp.439 561.]&lt;br /&gt;
*[ H. Buhrman, P.B. Miltersen, J. Radhakrishnan, S. Venkatesh. Are Bitvectors optimal? SIAM J. Comput., 31(6):1723–1744, 2002.]&lt;/div&gt;</summary>
		<author><name>Kozichuk</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%93%D1%80%D0%B0%D1%84%D1%8B-%D1%8D%D0%BA%D1%81%D0%BF%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%8B&amp;diff=50937</id>
		<title>Графы-экспандеры</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%93%D1%80%D0%B0%D1%84%D1%8B-%D1%8D%D0%BA%D1%81%D0%BF%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%8B&amp;diff=50937"/>
				<updated>2016-01-09T02:19:28Z</updated>
		
		<summary type="html">&lt;p&gt;Kozichuk: /* Конструирование */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Граф-экспандер''' (или расширяющийся граф, англ. ''expander graph'') - в комбинаторике сильно разреженный граф, при этом связность определяется по вершинам, дугам или спектру.&lt;br /&gt;
&lt;br /&gt;
==Определение==&lt;br /&gt;
''Граф-экспандер'' — это конечный ненаправленный ''мультиграф'', в котором любое подмножество вершин, не являясь «слишком большим», имеет «сильную» связность. Различные формализации этих понятий дают различные типы экспандеров: ''рёберный расширитель'', ''вершинный расширитель'', и ''спектральный расширитель''.&lt;br /&gt;
&lt;br /&gt;
Несвязный граф не является экспандером. Любой связный граф является экспандером, однако различные связные графы имеют различные параметры расширителя. Полный граф имеет лучшие параметры расширителя, но имеет наибольшую возможную степень. Неформально говоря, граф является хорошим экспандером, если он имеет низкую степень и высокий параметр расширителя.&lt;br /&gt;
&lt;br /&gt;
===Реберное расширение===&lt;br /&gt;
''Рёберное расширение'' (также ''изопериметрическое число'' или константа Чигера) &amp;lt;tex&amp;gt;h(G)&amp;lt;/tex&amp;gt; графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; для &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; вершин определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;h(G) = \min\limits_{0 &amp;lt; |S|\leqslant \frac{n}{2}} \frac{|\partial_{\text{out}}(S)|}{|S|}&amp;lt;/tex&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где минимум берётся по всем непустым множествам &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; не более чем &amp;lt;tex&amp;gt;n/2&amp;lt;/tex&amp;gt; вершин и &amp;lt;tex&amp;gt;\partial(S)&amp;lt;/tex&amp;gt; — ''граничные дуги'' множества &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, то есть, множество дуг с единственной вершиной в &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Вершинное расширение===&lt;br /&gt;
''Вершинное изопериметрическое число'' &amp;lt;tex&amp;gt;h_{out}(G)&amp;lt;/tex&amp;gt; (также ''вершинное раширение'') графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;h_{out}(G) = \min\limits_{0 &amp;lt; |S|\leqslant \frac{n}{2}} \frac{|\partial_{\text{out}}(S)|}{|S|}&amp;lt;/tex&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;tex&amp;gt;\partial_{\text{out}}(S)&amp;lt;/tex&amp;gt; — ''внешняя граница'' &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, то есть множество вершин из &amp;lt;tex&amp;gt;V(G)\setminus S&amp;lt;/tex&amp;gt;, имеющих как минимум одного соседа в &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;. В варианте этого определения (называемом ''уникальным соседним расширением'') &amp;lt;tex&amp;gt;\partial_{\text{out}}(S)&amp;lt;/tex&amp;gt; заменяется на множество вершин из &amp;lt;tex&amp;gt;V&amp;lt;/tex&amp;gt; с ''точностью одним'' соседом из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
''Вершинное изопериметрическое число'' &amp;lt;tex&amp;gt;h_{in}(G)&amp;lt;/tex&amp;gt; графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;h_{in}(G) = \min\limits_{0 &amp;lt; |S|\leqslant \frac{n}{2}} \frac{|\partial_{\text{in}}(S)|}{|S|}&amp;lt;/tex&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;tex&amp;gt;\partial_{in}(S)&amp;lt;/tex&amp;gt; — ''внутренняя граница'' &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, то есть множество вершин &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, имеющих как минимум одного соседа в &amp;lt;tex&amp;gt;V(G)\setminus S&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Спектральное расширение===&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; является d-регулярным, возможно определение в терминах линейной алгебры на основе собственных значений матрицы смежности &amp;lt;tex&amp;gt;A = A(G)&amp;lt;/tex&amp;gt; графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;A_{ij}&amp;lt;/tex&amp;gt; — число дуг между вершинами &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;. Поскольку &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; является симметричной, согласно спектральной теореме, &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; имеет &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; действительных собственных значений &amp;lt;tex&amp;gt;\lambda_1 \ge \lambda_2 \ge \cdots \ge \lambda_{n}&amp;lt;/tex&amp;gt;. Известно, что эти значения лежат в промежутке &amp;lt;tex&amp;gt;[−d, d]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
Граф регулярен тогда и только тогда, когда вектор &amp;lt;tex&amp;gt;u\in \mathbb {R} ^{n} с u_{i}=1&amp;lt;/tex&amp;gt; для всех &amp;lt;tex&amp;gt;i = 1, …, n&amp;lt;/tex&amp;gt; является собственным вектором матрицы &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt;, а его собственное число будет постоянной степенью графа. Таким образом, мы имеем &amp;lt;tex&amp;gt;Au = du&amp;lt;/tex&amp;gt;, и &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt; — собственный вектор матрицы &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; с собственными значениями &amp;lt;tex&amp;gt;λ1 = d&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt; — степень вершин графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt;. Спектральный зазор графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; определяется как &amp;lt;tex&amp;gt;d−λ2&amp;lt;/tex&amp;gt; и является мерилом спектрального расширения графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Известно, что &amp;lt;tex&amp;gt;\lambda_n = −d&amp;lt;/tex&amp;gt; тогда и только тогда, когда &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; — двудольный. Во многих случаях, например в лемме о перемешивании, необходимо ограничить снизу не только зазор между &amp;lt;tex&amp;gt;\lambda_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\lambda_2&amp;lt;/tex&amp;gt;, но и зазор между &amp;lt;tex&amp;gt;\lambda_1&amp;lt;/tex&amp;gt; и вторым максимальным по модулю собственным значением:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\lambda=\max\{|\lambda_2|, |\lambda_{n}|\}&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Поскольку это собственное значение соответствует некоторому собственному вектору, ортогональному &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;, его можно определить, используя отношение Рэлея:&lt;br /&gt;
&amp;lt;tex&amp;gt;\lambda=\max_{0\neq v\perp u} \frac{\|Av\|_2}{\|v\|_2},&amp;lt;/tex&amp;gt;&lt;br /&gt;
gde&lt;br /&gt;
&amp;lt;tex&amp;gt;\|v\|_2=\left(\sum_{i=1}^n v_i^2\right)^{1/2}&amp;lt;/tex&amp;gt;&lt;br /&gt;
— евклидова норма вектора &amp;lt;tex&amp;gt;v\in \mathbb {R} ^{n}&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Нормализованная версия этого определения также широко используется и более удобна для получения определённых результатов. В таком случае рассматривается матрица &amp;lt;tex&amp;gt;{\tfrac {1}{d}}A&amp;lt;/tex&amp;gt;, являющаяся матрицей переходов графа G. Все её собственные значения лежат между &amp;lt;tex&amp;gt;−1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;. Если граф не регулярен, спектр графа может быть определён аналогичным образом, используя собственные значения матрицы Кирхгофа. Для направленного графа используются сингулярные значения матрицы сопряжения A, которые равны квадратным корням из собственных значений симметричной матрицы &amp;lt;tex&amp;gt;A^TA&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Конструирование==&lt;br /&gt;
Существуют три основные стратегии создания семейств графов расширений. Первая стратегия — алгебраическая и теоретико-групповая, вторая — аналитическая, использующая аддитивную комбинаторику, и третья — комбинаторная, использующая зигзаг-произведение и связанные комбинаторные произведения.&lt;br /&gt;
===Маргулис-Габбер-Галил===&lt;br /&gt;
Алгебраическое конструирование, основанное на графах Кэли, известно для различных вариантов экспандеров. Следующее конструирование принадлежит Маргулису и было проанализировано Габбером (Gabber) и Галилом (Galil). Для любого натурального &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; строим граф, &amp;lt;tex&amp;gt;G_{n}&amp;lt;/tex&amp;gt; со множеством вершин &amp;lt;tex&amp;gt;\mathbb Z _{n}\times \mathbb {Z} _{n}&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;\mathbb {Z} _{n}=\mathbb Z /n \mathbb Z&amp;lt;/tex&amp;gt; . Для любой вершины &amp;lt;tex&amp;gt;(x,y)\in \mathbb {Z} _{n}\times \mathbb {Z} _{n}&amp;lt;/tex&amp;gt;, её восемь соседей будут&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;(x \pm 2y,y), (x \pm (2y+1),y), (x,y \pm 2x), (x,y \pm (2x+1)).&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Выполняется следующая теорема:&lt;br /&gt;
{{Теорема&lt;br /&gt;
|statement=&lt;br /&gt;
Для всех &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; граф &amp;lt;tex&amp;gt;Gn&amp;lt;/tex&amp;gt; второе по величине собственное число&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;tex&amp;gt;\lambda(G)\leq 5 \sqrt{2}&amp;lt;/tex&amp;gt;.}}&lt;br /&gt;
&lt;br /&gt;
===Граф Рамануджана===&lt;br /&gt;
&amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt;-регулярный граф называется графом Рамануджана, если его второе по модулю собственное число не превосходит &amp;lt;tex&amp;gt;2{\sqrt {d-1}}&amp;lt;/tex&amp;gt;. Любоцкий, Сарнак, Филлипс и Маргулис указали явную конструкцию графов Кэли, являющихся графами Рамануджана. Опишем эту конструкцию.&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt;p&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; простые числа, &amp;lt;tex&amp;gt;p = 1&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;mod&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;4&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;q = 1&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;mod&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;4&amp;lt;/tex&amp;gt;. В качестве группы &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; возьмём &amp;lt;tex&amp;gt;PGL(2, \mathbb Z/q{\mathbb Z})&amp;lt;/tex&amp;gt;, т.е. невырожденные матрицы 2 × 2 над полем вычетов по модулю &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;, профакторизованные по отношению пропорциональности (с обычной операцией матричного умножения).&lt;br /&gt;
Далее мы зададим в этой группе симметричное множество &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;. Выберем такое целое &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;i^2 = −1&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;mod&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;. Можно доказать, что тогда имеется ровно &amp;lt;tex&amp;gt;(p + 1)&amp;lt;/tex&amp;gt; целочисленное решение уравнения&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;a_0^2 + a_1^2 + a_3^2 = p&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
такое, что &amp;lt;tex&amp;gt;a_0&amp;lt;/tex&amp;gt; положительно и нечётно, а &amp;lt;tex&amp;gt;a_1&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;a_2&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;a_3&amp;lt;/tex&amp;gt; чётны. Каждой такой четвёрке сопоставим матрицу&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;A = \begin{pmatrix} a_0 + ia_1 &amp;amp; a_2 + ia_3\\ -a_2 + ia_3 &amp;amp; a_0 - ia_1 \end{pmatrix}&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Эти матрицы образуют множество S.&lt;br /&gt;
Нетрудно понять, что граф Кэли &amp;lt;tex&amp;gt;(G, S)&amp;lt;/tex&amp;gt; состоит из &amp;lt;tex&amp;gt;\Theta(q^3)&amp;lt;/tex&amp;gt; вершин, и степень каждой вершины равна &amp;lt;tex&amp;gt;(p + 1)&amp;lt;/tex&amp;gt;. Свойства данного графа зависят от соотношения &amp;lt;tex&amp;gt;p&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;. Рассмотрим случай, когда p является квадратичным вычетом по модулю &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;. Тогда полученный граф Кэли состоит из двух связных компонент (поскольку все матрицы из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; лежат в подгруппе &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; индекса два — подгруппе матриц, определитель которых является квадратичным вычетом). Обозначим &amp;lt;tex&amp;gt;X^{p,q}&amp;lt;/tex&amp;gt; связную компоненту полученного графа. Можно доказать, что у &amp;lt;tex&amp;gt;X^{p,q}&amp;lt;/tex&amp;gt; второе по абсолютной величине собственное число не превосходит &amp;lt;tex&amp;gt;2{\sqrt{p}}&amp;lt;/tex&amp;gt;, т.е. мы получили граф Рамануджана.&lt;br /&gt;
&lt;br /&gt;
==Примеры применения экспандеров==&lt;br /&gt;
===Коды, исправляющие ошибки===&lt;br /&gt;
С помощью расширяющего графа можно посторить линейный код, позволяющий исправлять ошибки в доле &amp;lt;tex&amp;gt;\delta = 1/(2000d)&amp;lt;/tex&amp;gt; битов. Чтобы задать линейный код с длиной кодового слова n, достаточно описать его проверочную матрицу &amp;lt;tex&amp;gt;H&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;(&amp;lt;/tex&amp;gt;слово &amp;lt;tex&amp;gt;x \in \{0, 1\}^n&amp;lt;/tex&amp;gt; является кодовым словом если и только если &amp;lt;tex&amp;gt;Hx^t = 0)&amp;lt;/tex&amp;gt;. Другими словами, нужно задать систему линейных уравнений для переменных &amp;lt;tex&amp;gt;x_1, . . . , x_n;&amp;lt;/tex&amp;gt; решения этой системы и будут кодовыми словами.&lt;br /&gt;
===Увеличение вероятности успеха в алгоритмах с датчиком случайных чисел===&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=Язык &amp;lt;tex&amp;gt;L&amp;lt;/tex&amp;gt; принадлежит сложностному классу &amp;lt;tex&amp;gt;RP&amp;lt;/tex&amp;gt;, если существует полиномиальный алгоритм &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; такой что&lt;br /&gt;
1. для &amp;lt;tex&amp;gt;x \in L&amp;lt;/tex&amp;gt; для всех &amp;lt;tex&amp;gt;r \in \{0, 1\}^{poly(n)}&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;A(x, r) = 1&amp;lt;/tex&amp;gt;&lt;br /&gt;
2. для &amp;lt;tex&amp;gt;x \notin L&amp;lt;/tex&amp;gt; не более чем для 1/2000 всех &amp;lt;tex&amp;gt;r \in \{0, 1\}^{poly(n)}&amp;lt;/tex&amp;gt; может выполняться &amp;lt;tex&amp;gt;A(x, r) = 1&amp;lt;/tex&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Покажем, что для любого &amp;lt;tex&amp;gt;\epsilon &amp;gt; 0&amp;lt;/tex&amp;gt; полиномиальный вероятностный алгоритм A можно модифицировать таким образом, чтобы вероятность ошибки уменьшилась до &amp;lt;tex&amp;gt;\epsilon&amp;lt;/tex&amp;gt;, а число используемых случайных битов не изменится.&lt;br /&gt;
&lt;br /&gt;
Пусть исходный алгоритм использует &amp;lt;tex&amp;gt;k = k(n)&amp;lt;/tex&amp;gt; случайных битов для вычислений на входах длины &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;. Зафиксируем &amp;lt;tex&amp;gt;(2^k, 2^k, d)&amp;lt;/tex&amp;gt; - экспандер &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;d &amp;gt; 1/\epsilon&amp;lt;/tex&amp;gt;. Новый алгоритм действует следующим образом: выбирается случайная вершина &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; из левой доли графа (для этого требуется &amp;lt;tex&amp;gt;k&amp;lt;/tex&amp;gt; случайных битов); затем исходный алгоритм &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; последовательно запускается на всех &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt; наборах случайных битов, соответствующих соседям вершины &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;. Если все полученные ответы равны &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;, новый алгоритм также возвращает единицу; в противном случае возвращается ноль.&lt;br /&gt;
Покажем, что у нового алгоритма вероятность ошибки не превосходит &amp;lt;tex&amp;gt;1/d&amp;lt;/tex&amp;gt;. В самом деле, обозначим &amp;lt;tex&amp;gt;B = B(x)&amp;lt;/tex&amp;gt; множество таких вершин &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt; из правой доли графа, которые соответствуют неверному ответу старого алгоритма на входе &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;; аналогично, обозначим &amp;lt;tex&amp;gt;S = S(x)&amp;lt;/tex&amp;gt; множество таких вершин v из левой доли графа, которые для которых новый алгоритм даёт неверный ответ на входе &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;. Очевидно, &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; состоит из вершин, все соседи которых лежат в B. Предположим, что &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; содержит не менее &amp;lt;tex&amp;gt;n/(1000d)&amp;lt;/tex&amp;gt; вершин. Выберем среди них ровно &amp;lt;tex&amp;gt;n/(1000d)&amp;lt;/tex&amp;gt; вершин и назовём это множество &amp;lt;tex&amp;gt;S'&amp;lt;/tex&amp;gt;. По свойству экспандера, имеем&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;|\Gamma(S')| \geqslant  \frac{7}{8}d\frac{n}{1000d}=7n/8000 &amp;gt; n/2000&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Это противоречит тому, что все соседи &amp;lt;tex&amp;gt;S'&amp;lt;/tex&amp;gt; лежат в &amp;lt;tex&amp;gt;B&amp;lt;/tex&amp;gt;. В данном случае нам нужна явная в более сильном (чем в первом примере) смысле конструкция экспандера. Размер графа экспоненциално растёт с увеличенем &amp;lt;tex&amp;gt;k&amp;lt;/tex&amp;gt;, и нам необходим алгоритм, который по заданному номеру вершины &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; (из левой доли графа) за время &amp;lt;tex&amp;gt;poly(k)&amp;lt;/tex&amp;gt; находит список номеров всех соседей этой вершины (в правой доле графа).&lt;br /&gt;
&lt;br /&gt;
===Хранение множества со сверхбыстрым запросом элементов===&lt;br /&gt;
Мы организуем хранение m-элементного множества &amp;lt;tex&amp;gt;S \subset \{1, . . . , n\}&amp;lt;/tex&amp;gt; в виде описания &amp;lt;tex&amp;gt;X&amp;lt;/tex&amp;gt;, состоящего из &amp;lt;tex&amp;gt;O(m log n)&amp;lt;/tex&amp;gt; битов. При этом проверка принадлежности &amp;lt;tex&amp;gt;a \in S&amp;lt;/tex&amp;gt; будет производиться чрезвычайно быстро. А именно, мы построим такой вероятностный алгоритм, который по любому входу &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; запрашивает из &amp;lt;tex&amp;gt;X&amp;lt;/tex&amp;gt; один бит; если этот бит оказывается равным единице, то алгоритм отвечает, что &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; является элементом &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;; в противном случае алгоритм говорит, что &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; множеству не принадлежит. При этом для каждого &amp;lt;tex&amp;gt;a \in \{1, . . . , n\}&amp;lt;/tex&amp;gt; алгоритм ошибается с вероятностью не более &amp;lt;tex&amp;gt;1/3&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Чтобы построить нужное нам хранилище &amp;lt;tex&amp;gt;X&amp;lt;/tex&amp;gt;, мы сначала зафиксируем некоторый экспандер, у которого левая доля &amp;lt;tex&amp;gt;L&amp;lt;/tex&amp;gt; состоит из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; вершин, правая &amp;lt;tex&amp;gt;R&amp;lt;/tex&amp;gt; из &amp;lt;tex&amp;gt;k = O(m log n)&amp;lt;/tex&amp;gt; вершин, степень всех вершин левой доли одинакова и равна некоторому &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt;, и для каждого &amp;lt;tex&amp;gt;A \subset L&amp;lt;/tex&amp;gt; размера не более &amp;lt;tex&amp;gt;2m&amp;lt;/tex&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;|\Gamma(A)| \geqslant \frac{7}{8}d|A|&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;X&amp;lt;/tex&amp;gt; будет состоять в разметке вершин правой доли нулями и единицами. Эту разметку нужно выбрать таким образом, чтобы у каждой вершины из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; не менее &amp;lt;tex&amp;gt;2/3&amp;lt;/tex&amp;gt; соседей были помечены единицей, а у каждой вершины не из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; не менее &amp;lt;tex&amp;gt;2/3&amp;lt;/tex&amp;gt; соседей были помечены нулями.&amp;lt;tex&amp;gt;X&amp;lt;/tex&amp;gt; будет состоять в разметке вершин правой доли нулями и единицами. Эту разметку нужно выбрать таким образом, чтобы у каждой вершины из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; не менее &amp;lt;tex&amp;gt;2/3&amp;lt;/tex&amp;gt; соседей были помечены единицей, а у каждой вершины не из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; не менее &amp;lt;tex&amp;gt;2/3&amp;lt;/tex&amp;gt; соседей были помечены нулями.&lt;br /&gt;
&lt;br /&gt;
Остаётся объяснить, как построить нужную нам разметку правой доли графа. Будем строить её последовательными приближениями. Сначала пометим всех соседей всех вершин из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; единицами, а все остальные вершины – нулями. На данной разметке наш алгоритм с вероятностью &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt; возвращает правильный ответ для всех &amp;lt;tex&amp;gt;a \in S&amp;lt;/tex&amp;gt;. Однако для &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; не из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; проверочный алгоритм может работать неверно. Обозначим T множество всех таких вершин вне &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, у которых более &amp;lt;tex&amp;gt;d/3&amp;lt;/tex&amp;gt; соседей помечены единицей. Поменяем разметку: пометим всех соседей &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; нулём. Теперь разметка может стать плохой для части вершин из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;. Обозначим &amp;lt;tex&amp;gt;S'&amp;lt;/tex&amp;gt; множество всех таких вершин из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, у которых более &amp;lt;tex&amp;gt;d/3&amp;lt;/tex&amp;gt; соседей помечены нулями. Далее, поменяем разметку у&lt;br /&gt;
всех соседей &amp;lt;tex&amp;gt;S'&amp;lt;/tex&amp;gt; на единицы. После этого может вновь возникнуть множество ‘неправильных’ вершин вне &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, и т.д.&lt;br /&gt;
&lt;br /&gt;
Чтобы доказать, что данный процесс в конце концов сойдётся, нужно показать, что на каждом шаге число ‘проблемных’ вершин уменьшается в константу раз. Поскольку все шаги аналогичны, достаточно разобрать самый первый: докажем, что &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; в константу раз меньше, чем &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;. Мы воспользуемся тем, что для &amp;lt;tex&amp;gt;S \cup T&amp;lt;/tex&amp;gt; выполнено свойство расширения:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;(7/8)d(|S|+|T|) \leqslant |\Gamma(S \cup T)| \leqslant d|S| + (2/3)d|T|&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Откуда получаем &amp;lt;tex&amp;gt;|T| \leqslant 3/5|S|&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Приложения и полезные свойства==&lt;br /&gt;
Первоначально интерес к экспандерам возник с целью построения устойчивой сети (телефонов или компьютеров) — число дуг графов расширения с ограниченным значением регулярности растет линейно по отношению к числу вершин.&lt;br /&gt;
&lt;br /&gt;
Экспандеры нашли широкое применение в теории вычислительных машин и систем, для построения алгоритмов, в корректирующих кодах, экстракторах, генераторах псевдослучайных чисел, сетях сортировки и компьютерных сетях. Они также используются для доказательства многих важных результатов в теории вычислительной сложности, таких как &amp;lt;tex&amp;gt;SL=L&amp;lt;/tex&amp;gt; и Теорема PCP. В криптографии экспандеры используются для создания хеш-функций.&lt;br /&gt;
&lt;br /&gt;
Ниже приведены некоторые свойства экспандеров, считающиеся полезными во многих областях.&lt;br /&gt;
===Лемма о перемешивании===&lt;br /&gt;
Лемма о перемешивании утверждает, что для любых двух подмножеств вершин &amp;lt;tex&amp;gt;S,T\subseteq V&amp;lt;/tex&amp;gt; число рёбер между &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; примерно равно числу рёбер в случайном &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt;-регулярном графе. Приближение тем лучше, чем меньше &amp;lt;tex&amp;gt;\lambda =\max\{|\lambda _{2}|&amp;lt;/tex&amp;gt;,&amp;lt;tex&amp;gt;|\lambda _{n}|\}&amp;lt;/tex&amp;gt;. В случайном &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt;-регулярном графе, также как и в случайном графе Эрдёша — Реньи с вероятностью &amp;lt;tex&amp;gt;{\tfrac {d}{n}}&amp;lt;/tex&amp;gt; выбора ребра, ожидается &amp;lt;tex&amp;gt;{\tfrac {d}{n}}\cdot |S|\cdot |T|&amp;lt;/tex&amp;gt; рёбер между &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Более формально, пусть &amp;lt;tex&amp;gt;E(S, T)&amp;lt;/tex&amp;gt; обозначает число рёбер между &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;. Если эти два множества пересекаются, дуги в пересечении считаются дважды, так что&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;E(S,T)=2|E(G[S\cap T])|+E(S\setminus T,T)+E(S,T\setminus S)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
Лемма о перемешивании утверждает, что&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\left|E(S,T)-{\frac {d\cdot |S|\cdot |T|}{n}}\right|\leq d\lambda {\sqrt {|S|\cdot |T|}}&amp;lt;/tex&amp;gt;,&lt;br /&gt;
где &amp;lt;tex&amp;gt;\lambda &amp;lt;/tex&amp;gt; — абсолютное значение нормализованного второго по величине собственного значения.&lt;br /&gt;
&lt;br /&gt;
Недавно Билу (Bilu) и Линайл (Linial) показали, что обратное тоже верно, то есть, при условии выполнения неравенства из леммы второе по величине собственное значение равно &amp;lt;tex&amp;gt;O(d\lambda \cdot (1+\log(1/\lambda )))&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Блуждания по экспандеру===&lt;br /&gt;
Согласно границе Чернова, если выбирать много независимых случайных значений из интервала &amp;lt;tex&amp;gt;[−1, 1]&amp;lt;/tex&amp;gt;, с большой степенью вероятности среднее выбранных значений будет близко к математическому ожиданию случайной переменной. Лемма о блуждании по экспандеру, согласно статьям Аджтари, Комлоша и Семереди и Гилмана, утверждает, что то же самое верно и для блужданий по экспандеру. Это полезно в теории дерандомизации, поскольку блуждание по экспандеру использует много меньше случайных бит, чем случайная независимая выборка.&lt;br /&gt;
&lt;br /&gt;
==Источники информации==&lt;br /&gt;
*[https://ru.wikipedia.org/wiki/%D0%AD%D0%BA%D1%81%D0%BF%D0%B0%D0%BD%D0%B4%D0%B5%D1%80_(%D1%82%D0%B5%D0%BE%D1%80%D0%B8%D1%8F_%D0%B3%D1%80%D0%B0%D1%84%D0%BE%D0%B2) Экспандер (теория графов)]&lt;br /&gt;
*[S. Hoory, N. Linial, A. Wigderson. Expander graphs and their applications. Bulletin of the AMS, vol. 43, Number 4, Oct. 2006, pp.439 561.]&lt;br /&gt;
*[ H. Buhrman, P.B. Miltersen, J. Radhakrishnan, S. Venkatesh. Are Bitvectors optimal? SIAM J. Comput., 31(6):1723–1744, 2002.]&lt;/div&gt;</summary>
		<author><name>Kozichuk</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%93%D1%80%D0%B0%D1%84%D1%8B-%D1%8D%D0%BA%D1%81%D0%BF%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%8B&amp;diff=50936</id>
		<title>Графы-экспандеры</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%93%D1%80%D0%B0%D1%84%D1%8B-%D1%8D%D0%BA%D1%81%D0%BF%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%8B&amp;diff=50936"/>
				<updated>2016-01-09T02:17:32Z</updated>
		
		<summary type="html">&lt;p&gt;Kozichuk: /* Источники информации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Граф-экспандер''' (или расширяющийся граф, англ. ''expander graph'') - в комбинаторике сильно разреженный граф, при этом связность определяется по вершинам, дугам или спектру.&lt;br /&gt;
&lt;br /&gt;
==Определение==&lt;br /&gt;
''Граф-экспандер'' — это конечный ненаправленный ''мультиграф'', в котором любое подмножество вершин, не являясь «слишком большим», имеет «сильную» связность. Различные формализации этих понятий дают различные типы экспандеров: ''рёберный расширитель'', ''вершинный расширитель'', и ''спектральный расширитель''.&lt;br /&gt;
&lt;br /&gt;
Несвязный граф не является экспандером. Любой связный граф является экспандером, однако различные связные графы имеют различные параметры расширителя. Полный граф имеет лучшие параметры расширителя, но имеет наибольшую возможную степень. Неформально говоря, граф является хорошим экспандером, если он имеет низкую степень и высокий параметр расширителя.&lt;br /&gt;
&lt;br /&gt;
===Реберное расширение===&lt;br /&gt;
''Рёберное расширение'' (также ''изопериметрическое число'' или константа Чигера) &amp;lt;tex&amp;gt;h(G)&amp;lt;/tex&amp;gt; графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; для &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; вершин определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;h(G) = \min\limits_{0 &amp;lt; |S|\leqslant \frac{n}{2}} \frac{|\partial_{\text{out}}(S)|}{|S|}&amp;lt;/tex&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где минимум берётся по всем непустым множествам &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; не более чем &amp;lt;tex&amp;gt;n/2&amp;lt;/tex&amp;gt; вершин и &amp;lt;tex&amp;gt;\partial(S)&amp;lt;/tex&amp;gt; — ''граничные дуги'' множества &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, то есть, множество дуг с единственной вершиной в &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Вершинное расширение===&lt;br /&gt;
''Вершинное изопериметрическое число'' &amp;lt;tex&amp;gt;h_{out}(G)&amp;lt;/tex&amp;gt; (также ''вершинное раширение'') графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;h_{out}(G) = \min\limits_{0 &amp;lt; |S|\leqslant \frac{n}{2}} \frac{|\partial_{\text{out}}(S)|}{|S|}&amp;lt;/tex&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;tex&amp;gt;\partial_{\text{out}}(S)&amp;lt;/tex&amp;gt; — ''внешняя граница'' &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, то есть множество вершин из &amp;lt;tex&amp;gt;V(G)\setminus S&amp;lt;/tex&amp;gt;, имеющих как минимум одного соседа в &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;. В варианте этого определения (называемом ''уникальным соседним расширением'') &amp;lt;tex&amp;gt;\partial_{\text{out}}(S)&amp;lt;/tex&amp;gt; заменяется на множество вершин из &amp;lt;tex&amp;gt;V&amp;lt;/tex&amp;gt; с ''точностью одним'' соседом из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
''Вершинное изопериметрическое число'' &amp;lt;tex&amp;gt;h_{in}(G)&amp;lt;/tex&amp;gt; графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;h_{in}(G) = \min\limits_{0 &amp;lt; |S|\leqslant \frac{n}{2}} \frac{|\partial_{\text{in}}(S)|}{|S|}&amp;lt;/tex&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;tex&amp;gt;\partial_{in}(S)&amp;lt;/tex&amp;gt; — ''внутренняя граница'' &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, то есть множество вершин &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, имеющих как минимум одного соседа в &amp;lt;tex&amp;gt;V(G)\setminus S&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Спектральное расширение===&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; является d-регулярным, возможно определение в терминах линейной алгебры на основе собственных значений матрицы смежности &amp;lt;tex&amp;gt;A = A(G)&amp;lt;/tex&amp;gt; графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;A_{ij}&amp;lt;/tex&amp;gt; — число дуг между вершинами &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;. Поскольку &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; является симметричной, согласно спектральной теореме, &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; имеет &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; действительных собственных значений &amp;lt;tex&amp;gt;\lambda_1 \ge \lambda_2 \ge \cdots \ge \lambda_{n}&amp;lt;/tex&amp;gt;. Известно, что эти значения лежат в промежутке &amp;lt;tex&amp;gt;[−d, d]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
Граф регулярен тогда и только тогда, когда вектор &amp;lt;tex&amp;gt;u\in \mathbb {R} ^{n} с u_{i}=1&amp;lt;/tex&amp;gt; для всех &amp;lt;tex&amp;gt;i = 1, …, n&amp;lt;/tex&amp;gt; является собственным вектором матрицы &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt;, а его собственное число будет постоянной степенью графа. Таким образом, мы имеем &amp;lt;tex&amp;gt;Au = du&amp;lt;/tex&amp;gt;, и &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt; — собственный вектор матрицы &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; с собственными значениями &amp;lt;tex&amp;gt;λ1 = d&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt; — степень вершин графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt;. Спектральный зазор графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; определяется как &amp;lt;tex&amp;gt;d−λ2&amp;lt;/tex&amp;gt; и является мерилом спектрального расширения графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Известно, что &amp;lt;tex&amp;gt;\lambda_n = −d&amp;lt;/tex&amp;gt; тогда и только тогда, когда &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; — двудольный. Во многих случаях, например в лемме о перемешивании, необходимо ограничить снизу не только зазор между &amp;lt;tex&amp;gt;\lambda_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\lambda_2&amp;lt;/tex&amp;gt;, но и зазор между &amp;lt;tex&amp;gt;\lambda_1&amp;lt;/tex&amp;gt; и вторым максимальным по модулю собственным значением:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\lambda=\max\{|\lambda_2|, |\lambda_{n}|\}&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Поскольку это собственное значение соответствует некоторому собственному вектору, ортогональному &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;, его можно определить, используя отношение Рэлея:&lt;br /&gt;
&amp;lt;tex&amp;gt;\lambda=\max_{0\neq v\perp u} \frac{\|Av\|_2}{\|v\|_2},&amp;lt;/tex&amp;gt;&lt;br /&gt;
gde&lt;br /&gt;
&amp;lt;tex&amp;gt;\|v\|_2=\left(\sum_{i=1}^n v_i^2\right)^{1/2}&amp;lt;/tex&amp;gt;&lt;br /&gt;
— евклидова норма вектора &amp;lt;tex&amp;gt;v\in \mathbb {R} ^{n}&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Нормализованная версия этого определения также широко используется и более удобна для получения определённых результатов. В таком случае рассматривается матрица &amp;lt;tex&amp;gt;{\tfrac {1}{d}}A&amp;lt;/tex&amp;gt;, являющаяся матрицей переходов графа G. Все её собственные значения лежат между &amp;lt;tex&amp;gt;−1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;. Если граф не регулярен, спектр графа может быть определён аналогичным образом, используя собственные значения матрицы Кирхгофа. Для направленного графа используются сингулярные значения матрицы сопряжения A, которые равны квадратным корням из собственных значений симметричной матрицы &amp;lt;tex&amp;gt;A^TA&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Конструирование==&lt;br /&gt;
Существуют три основные стратегии создания семейств графов расширений[6]. Первая стратегия — алгебраическая и теоретико-групповая, вторая — аналитическая, использующая аддитивную комбинаторику, и третья — комбинаторная, использующая зигзаг-произведение и связанные комбинаторные произведения.&lt;br /&gt;
===Маргулис-Габбер-Галил===&lt;br /&gt;
Алгебраическое конструирование, основанное на графах Кэли, известно для различных вариантов экспандеров. Следующее конструирование принадлежит Маргулису и было проанализировано Габбером (Gabber) и Галилом (Galil). Для любого натурального &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; строим граф, &amp;lt;tex&amp;gt;G_{n}&amp;lt;/tex&amp;gt; со множеством вершин &amp;lt;tex&amp;gt;\mathbb Z _{n}\times \mathbb {Z} _{n}&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;\mathbb {Z} _{n}=\mathbb Z /n \mathbb Z&amp;lt;/tex&amp;gt; . Для любой вершины &amp;lt;tex&amp;gt;(x,y)\in \mathbb {Z} _{n}\times \mathbb {Z} _{n}&amp;lt;/tex&amp;gt;, её восемь соседей будут&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;(x \pm 2y,y), (x \pm (2y+1),y), (x,y \pm 2x), (x,y \pm (2x+1)).&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Выполняется следующая теорема:&lt;br /&gt;
{{Теорема&lt;br /&gt;
|statement=&lt;br /&gt;
Для всех &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; граф &amp;lt;tex&amp;gt;Gn&amp;lt;/tex&amp;gt; второе по величине собственное число&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;tex&amp;gt;\lambda(G)\leq 5 \sqrt{2}&amp;lt;/tex&amp;gt;.}}&lt;br /&gt;
&lt;br /&gt;
===Граф Рамануджана===&lt;br /&gt;
&amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt;-регулярный граф называется графом Рамануджана, если его второе по модулю собственное число не превосходит &amp;lt;tex&amp;gt;2{\sqrt {d-1}}&amp;lt;/tex&amp;gt;. Любоцкий, Сарнак, Филлипс и Маргулис указали явную конструкцию графов Кэли, являющихся графами Рамануджана. Опишем эту конструкцию.&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt;p&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; простые числа, &amp;lt;tex&amp;gt;p = 1&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;mod&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;4&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;q = 1&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;mod&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;4&amp;lt;/tex&amp;gt;. В качестве группы &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; возьмём &amp;lt;tex&amp;gt;PGL(2, \mathbb Z/q{\mathbb Z})&amp;lt;/tex&amp;gt;, т.е. невырожденные матрицы 2 × 2 над полем вычетов по модулю &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;, профакторизованные по отношению пропорциональности (с обычной операцией матричного умножения).&lt;br /&gt;
Далее мы зададим в этой группе симметричное множество &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;. Выберем такое целое &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;i^2 = −1&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;mod&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;. Можно доказать, что тогда имеется ровно &amp;lt;tex&amp;gt;(p + 1)&amp;lt;/tex&amp;gt; целочисленное решение уравнения&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;a_0^2 + a_1^2 + a_3^2 = p&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
такое, что &amp;lt;tex&amp;gt;a_0&amp;lt;/tex&amp;gt; положительно и нечётно, а &amp;lt;tex&amp;gt;a_1&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;a_2&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;a_3&amp;lt;/tex&amp;gt; чётны. Каждой такой четвёрке сопоставим матрицу&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;A = \begin{pmatrix} a_0 + ia_1 &amp;amp; a_2 + ia_3\\ -a_2 + ia_3 &amp;amp; a_0 - ia_1 \end{pmatrix}&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Эти матрицы образуют множество S.&lt;br /&gt;
Нетрудно понять, что граф Кэли &amp;lt;tex&amp;gt;(G, S)&amp;lt;/tex&amp;gt; состоит из &amp;lt;tex&amp;gt;\Theta(q^3)&amp;lt;/tex&amp;gt; вершин, и степень каждой вершины равна &amp;lt;tex&amp;gt;(p + 1)&amp;lt;/tex&amp;gt;. Свойства данного графа зависят от соотношения &amp;lt;tex&amp;gt;p&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;. Рассмотрим случай, когда p является квадратичным вычетом по модулю &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;. Тогда полученный граф Кэли состоит из двух связных компонент (поскольку все матрицы из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; лежат в подгруппе &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; индекса два — подгруппе матриц, определитель которых является квадратичным вычетом). Обозначим &amp;lt;tex&amp;gt;X^{p,q}&amp;lt;/tex&amp;gt; связную компоненту полученного графа. Можно доказать, что у &amp;lt;tex&amp;gt;X^{p,q}&amp;lt;/tex&amp;gt; второе по абсолютной величине собственное число не превосходит &amp;lt;tex&amp;gt;2{\sqrt{p}}&amp;lt;/tex&amp;gt;, т.е. мы получили граф Рамануджана.&lt;br /&gt;
&lt;br /&gt;
==Примеры применения экспандеров==&lt;br /&gt;
===Коды, исправляющие ошибки===&lt;br /&gt;
С помощью расширяющего графа можно посторить линейный код, позволяющий исправлять ошибки в доле &amp;lt;tex&amp;gt;\delta = 1/(2000d)&amp;lt;/tex&amp;gt; битов. Чтобы задать линейный код с длиной кодового слова n, достаточно описать его проверочную матрицу &amp;lt;tex&amp;gt;H&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;(&amp;lt;/tex&amp;gt;слово &amp;lt;tex&amp;gt;x \in \{0, 1\}^n&amp;lt;/tex&amp;gt; является кодовым словом если и только если &amp;lt;tex&amp;gt;Hx^t = 0)&amp;lt;/tex&amp;gt;. Другими словами, нужно задать систему линейных уравнений для переменных &amp;lt;tex&amp;gt;x_1, . . . , x_n;&amp;lt;/tex&amp;gt; решения этой системы и будут кодовыми словами.&lt;br /&gt;
===Увеличение вероятности успеха в алгоритмах с датчиком случайных чисел===&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=Язык &amp;lt;tex&amp;gt;L&amp;lt;/tex&amp;gt; принадлежит сложностному классу &amp;lt;tex&amp;gt;RP&amp;lt;/tex&amp;gt;, если существует полиномиальный алгоритм &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; такой что&lt;br /&gt;
1. для &amp;lt;tex&amp;gt;x \in L&amp;lt;/tex&amp;gt; для всех &amp;lt;tex&amp;gt;r \in \{0, 1\}^{poly(n)}&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;A(x, r) = 1&amp;lt;/tex&amp;gt;&lt;br /&gt;
2. для &amp;lt;tex&amp;gt;x \notin L&amp;lt;/tex&amp;gt; не более чем для 1/2000 всех &amp;lt;tex&amp;gt;r \in \{0, 1\}^{poly(n)}&amp;lt;/tex&amp;gt; может выполняться &amp;lt;tex&amp;gt;A(x, r) = 1&amp;lt;/tex&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Покажем, что для любого &amp;lt;tex&amp;gt;\epsilon &amp;gt; 0&amp;lt;/tex&amp;gt; полиномиальный вероятностный алгоритм A можно модифицировать таким образом, чтобы вероятность ошибки уменьшилась до &amp;lt;tex&amp;gt;\epsilon&amp;lt;/tex&amp;gt;, а число используемых случайных битов не изменится.&lt;br /&gt;
&lt;br /&gt;
Пусть исходный алгоритм использует &amp;lt;tex&amp;gt;k = k(n)&amp;lt;/tex&amp;gt; случайных битов для вычислений на входах длины &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;. Зафиксируем &amp;lt;tex&amp;gt;(2^k, 2^k, d)&amp;lt;/tex&amp;gt; - экспандер &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;d &amp;gt; 1/\epsilon&amp;lt;/tex&amp;gt;. Новый алгоритм действует следующим образом: выбирается случайная вершина &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; из левой доли графа (для этого требуется &amp;lt;tex&amp;gt;k&amp;lt;/tex&amp;gt; случайных битов); затем исходный алгоритм &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; последовательно запускается на всех &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt; наборах случайных битов, соответствующих соседям вершины &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;. Если все полученные ответы равны &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;, новый алгоритм также возвращает единицу; в противном случае возвращается ноль.&lt;br /&gt;
Покажем, что у нового алгоритма вероятность ошибки не превосходит &amp;lt;tex&amp;gt;1/d&amp;lt;/tex&amp;gt;. В самом деле, обозначим &amp;lt;tex&amp;gt;B = B(x)&amp;lt;/tex&amp;gt; множество таких вершин &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt; из правой доли графа, которые соответствуют неверному ответу старого алгоритма на входе &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;; аналогично, обозначим &amp;lt;tex&amp;gt;S = S(x)&amp;lt;/tex&amp;gt; множество таких вершин v из левой доли графа, которые для которых новый алгоритм даёт неверный ответ на входе &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;. Очевидно, &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; состоит из вершин, все соседи которых лежат в B. Предположим, что &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; содержит не менее &amp;lt;tex&amp;gt;n/(1000d)&amp;lt;/tex&amp;gt; вершин. Выберем среди них ровно &amp;lt;tex&amp;gt;n/(1000d)&amp;lt;/tex&amp;gt; вершин и назовём это множество &amp;lt;tex&amp;gt;S'&amp;lt;/tex&amp;gt;. По свойству экспандера, имеем&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;|\Gamma(S')| \geqslant  \frac{7}{8}d\frac{n}{1000d}=7n/8000 &amp;gt; n/2000&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Это противоречит тому, что все соседи &amp;lt;tex&amp;gt;S'&amp;lt;/tex&amp;gt; лежат в &amp;lt;tex&amp;gt;B&amp;lt;/tex&amp;gt;. В данном случае нам нужна явная в более сильном (чем в первом примере) смысле конструкция экспандера. Размер графа экспоненциално растёт с увеличенем &amp;lt;tex&amp;gt;k&amp;lt;/tex&amp;gt;, и нам необходим алгоритм, который по заданному номеру вершины &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; (из левой доли графа) за время &amp;lt;tex&amp;gt;poly(k)&amp;lt;/tex&amp;gt; находит список номеров всех соседей этой вершины (в правой доле графа).&lt;br /&gt;
&lt;br /&gt;
===Хранение множества со сверхбыстрым запросом элементов===&lt;br /&gt;
Мы организуем хранение m-элементного множества &amp;lt;tex&amp;gt;S \subset \{1, . . . , n\}&amp;lt;/tex&amp;gt; в виде описания &amp;lt;tex&amp;gt;X&amp;lt;/tex&amp;gt;, состоящего из &amp;lt;tex&amp;gt;O(m log n)&amp;lt;/tex&amp;gt; битов. При этом проверка принадлежности &amp;lt;tex&amp;gt;a \in S&amp;lt;/tex&amp;gt; будет производиться чрезвычайно быстро. А именно, мы построим такой вероятностный алгоритм, который по любому входу &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; запрашивает из &amp;lt;tex&amp;gt;X&amp;lt;/tex&amp;gt; один бит; если этот бит оказывается равным единице, то алгоритм отвечает, что &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; является элементом &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;; в противном случае алгоритм говорит, что &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; множеству не принадлежит. При этом для каждого &amp;lt;tex&amp;gt;a \in \{1, . . . , n\}&amp;lt;/tex&amp;gt; алгоритм ошибается с вероятностью не более &amp;lt;tex&amp;gt;1/3&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Чтобы построить нужное нам хранилище &amp;lt;tex&amp;gt;X&amp;lt;/tex&amp;gt;, мы сначала зафиксируем некоторый экспандер, у которого левая доля &amp;lt;tex&amp;gt;L&amp;lt;/tex&amp;gt; состоит из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; вершин, правая &amp;lt;tex&amp;gt;R&amp;lt;/tex&amp;gt; из &amp;lt;tex&amp;gt;k = O(m log n)&amp;lt;/tex&amp;gt; вершин, степень всех вершин левой доли одинакова и равна некоторому &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt;, и для каждого &amp;lt;tex&amp;gt;A \subset L&amp;lt;/tex&amp;gt; размера не более &amp;lt;tex&amp;gt;2m&amp;lt;/tex&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;|\Gamma(A)| \geqslant \frac{7}{8}d|A|&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;X&amp;lt;/tex&amp;gt; будет состоять в разметке вершин правой доли нулями и единицами. Эту разметку нужно выбрать таким образом, чтобы у каждой вершины из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; не менее &amp;lt;tex&amp;gt;2/3&amp;lt;/tex&amp;gt; соседей были помечены единицей, а у каждой вершины не из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; не менее &amp;lt;tex&amp;gt;2/3&amp;lt;/tex&amp;gt; соседей были помечены нулями.&amp;lt;tex&amp;gt;X&amp;lt;/tex&amp;gt; будет состоять в разметке вершин правой доли нулями и единицами. Эту разметку нужно выбрать таким образом, чтобы у каждой вершины из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; не менее &amp;lt;tex&amp;gt;2/3&amp;lt;/tex&amp;gt; соседей были помечены единицей, а у каждой вершины не из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; не менее &amp;lt;tex&amp;gt;2/3&amp;lt;/tex&amp;gt; соседей были помечены нулями.&lt;br /&gt;
&lt;br /&gt;
Остаётся объяснить, как построить нужную нам разметку правой доли графа. Будем строить её последовательными приближениями. Сначала пометим всех соседей всех вершин из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; единицами, а все остальные вершины – нулями. На данной разметке наш алгоритм с вероятностью &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt; возвращает правильный ответ для всех &amp;lt;tex&amp;gt;a \in S&amp;lt;/tex&amp;gt;. Однако для &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; не из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; проверочный алгоритм может работать неверно. Обозначим T множество всех таких вершин вне &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, у которых более &amp;lt;tex&amp;gt;d/3&amp;lt;/tex&amp;gt; соседей помечены единицей. Поменяем разметку: пометим всех соседей &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; нулём. Теперь разметка может стать плохой для части вершин из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;. Обозначим &amp;lt;tex&amp;gt;S'&amp;lt;/tex&amp;gt; множество всех таких вершин из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, у которых более &amp;lt;tex&amp;gt;d/3&amp;lt;/tex&amp;gt; соседей помечены нулями. Далее, поменяем разметку у&lt;br /&gt;
всех соседей &amp;lt;tex&amp;gt;S'&amp;lt;/tex&amp;gt; на единицы. После этого может вновь возникнуть множество ‘неправильных’ вершин вне &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, и т.д.&lt;br /&gt;
&lt;br /&gt;
Чтобы доказать, что данный процесс в конце концов сойдётся, нужно показать, что на каждом шаге число ‘проблемных’ вершин уменьшается в константу раз. Поскольку все шаги аналогичны, достаточно разобрать самый первый: докажем, что &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; в константу раз меньше, чем &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;. Мы воспользуемся тем, что для &amp;lt;tex&amp;gt;S \cup T&amp;lt;/tex&amp;gt; выполнено свойство расширения:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;(7/8)d(|S|+|T|) \leqslant |\Gamma(S \cup T)| \leqslant d|S| + (2/3)d|T|&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Откуда получаем &amp;lt;tex&amp;gt;|T| \leqslant 3/5|S|&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Приложения и полезные свойства==&lt;br /&gt;
Первоначально интерес к экспандерам возник с целью построения устойчивой сети (телефонов или компьютеров) — число дуг графов расширения с ограниченным значением регулярности растет линейно по отношению к числу вершин.&lt;br /&gt;
&lt;br /&gt;
Экспандеры нашли широкое применение в теории вычислительных машин и систем, для построения алгоритмов, в корректирующих кодах, экстракторах, генераторах псевдослучайных чисел, сетях сортировки и компьютерных сетях. Они также используются для доказательства многих важных результатов в теории вычислительной сложности, таких как &amp;lt;tex&amp;gt;SL=L&amp;lt;/tex&amp;gt; и Теорема PCP. В криптографии экспандеры используются для создания хеш-функций.&lt;br /&gt;
&lt;br /&gt;
Ниже приведены некоторые свойства экспандеров, считающиеся полезными во многих областях.&lt;br /&gt;
===Лемма о перемешивании===&lt;br /&gt;
Лемма о перемешивании утверждает, что для любых двух подмножеств вершин &amp;lt;tex&amp;gt;S,T\subseteq V&amp;lt;/tex&amp;gt; число рёбер между &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; примерно равно числу рёбер в случайном &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt;-регулярном графе. Приближение тем лучше, чем меньше &amp;lt;tex&amp;gt;\lambda =\max\{|\lambda _{2}|&amp;lt;/tex&amp;gt;,&amp;lt;tex&amp;gt;|\lambda _{n}|\}&amp;lt;/tex&amp;gt;. В случайном &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt;-регулярном графе, также как и в случайном графе Эрдёша — Реньи с вероятностью &amp;lt;tex&amp;gt;{\tfrac {d}{n}}&amp;lt;/tex&amp;gt; выбора ребра, ожидается &amp;lt;tex&amp;gt;{\tfrac {d}{n}}\cdot |S|\cdot |T|&amp;lt;/tex&amp;gt; рёбер между &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Более формально, пусть &amp;lt;tex&amp;gt;E(S, T)&amp;lt;/tex&amp;gt; обозначает число рёбер между &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;. Если эти два множества пересекаются, дуги в пересечении считаются дважды, так что&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;E(S,T)=2|E(G[S\cap T])|+E(S\setminus T,T)+E(S,T\setminus S)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
Лемма о перемешивании утверждает, что&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\left|E(S,T)-{\frac {d\cdot |S|\cdot |T|}{n}}\right|\leq d\lambda {\sqrt {|S|\cdot |T|}}&amp;lt;/tex&amp;gt;,&lt;br /&gt;
где &amp;lt;tex&amp;gt;\lambda &amp;lt;/tex&amp;gt; — абсолютное значение нормализованного второго по величине собственного значения.&lt;br /&gt;
&lt;br /&gt;
Недавно Билу (Bilu) и Линайл (Linial) показали, что обратное тоже верно, то есть, при условии выполнения неравенства из леммы второе по величине собственное значение равно &amp;lt;tex&amp;gt;O(d\lambda \cdot (1+\log(1/\lambda )))&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Блуждания по экспандеру===&lt;br /&gt;
Согласно границе Чернова, если выбирать много независимых случайных значений из интервала &amp;lt;tex&amp;gt;[−1, 1]&amp;lt;/tex&amp;gt;, с большой степенью вероятности среднее выбранных значений будет близко к математическому ожиданию случайной переменной. Лемма о блуждании по экспандеру, согласно статьям Аджтари, Комлоша и Семереди и Гилмана, утверждает, что то же самое верно и для блужданий по экспандеру. Это полезно в теории дерандомизации, поскольку блуждание по экспандеру использует много меньше случайных бит, чем случайная независимая выборка.&lt;br /&gt;
&lt;br /&gt;
==Источники информации==&lt;br /&gt;
*[https://ru.wikipedia.org/wiki/%D0%AD%D0%BA%D1%81%D0%BF%D0%B0%D0%BD%D0%B4%D0%B5%D1%80_(%D1%82%D0%B5%D0%BE%D1%80%D0%B8%D1%8F_%D0%B3%D1%80%D0%B0%D1%84%D0%BE%D0%B2) Экспандер (теория графов)]&lt;br /&gt;
*[S. Hoory, N. Linial, A. Wigderson. Expander graphs and their applications. Bulletin of the AMS, vol. 43, Number 4, Oct. 2006, pp.439 561.]&lt;br /&gt;
*[ H. Buhrman, P.B. Miltersen, J. Radhakrishnan, S. Venkatesh. Are Bitvectors optimal? SIAM J. Comput., 31(6):1723–1744, 2002.]&lt;/div&gt;</summary>
		<author><name>Kozichuk</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%93%D1%80%D0%B0%D1%84%D1%8B-%D1%8D%D0%BA%D1%81%D0%BF%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%8B&amp;diff=50935</id>
		<title>Графы-экспандеры</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%93%D1%80%D0%B0%D1%84%D1%8B-%D1%8D%D0%BA%D1%81%D0%BF%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%8B&amp;diff=50935"/>
				<updated>2016-01-09T02:10:20Z</updated>
		
		<summary type="html">&lt;p&gt;Kozichuk: /* Лемма о перемешивании */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Граф-экспандер''' (или расширяющийся граф, англ. ''expander graph'') - в комбинаторике сильно разреженный граф, при этом связность определяется по вершинам, дугам или спектру.&lt;br /&gt;
&lt;br /&gt;
==Определение==&lt;br /&gt;
''Граф-экспандер'' — это конечный ненаправленный ''мультиграф'', в котором любое подмножество вершин, не являясь «слишком большим», имеет «сильную» связность. Различные формализации этих понятий дают различные типы экспандеров: ''рёберный расширитель'', ''вершинный расширитель'', и ''спектральный расширитель''.&lt;br /&gt;
&lt;br /&gt;
Несвязный граф не является экспандером. Любой связный граф является экспандером, однако различные связные графы имеют различные параметры расширителя. Полный граф имеет лучшие параметры расширителя, но имеет наибольшую возможную степень. Неформально говоря, граф является хорошим экспандером, если он имеет низкую степень и высокий параметр расширителя.&lt;br /&gt;
&lt;br /&gt;
===Реберное расширение===&lt;br /&gt;
''Рёберное расширение'' (также ''изопериметрическое число'' или константа Чигера) &amp;lt;tex&amp;gt;h(G)&amp;lt;/tex&amp;gt; графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; для &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; вершин определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;h(G) = \min\limits_{0 &amp;lt; |S|\leqslant \frac{n}{2}} \frac{|\partial_{\text{out}}(S)|}{|S|}&amp;lt;/tex&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где минимум берётся по всем непустым множествам &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; не более чем &amp;lt;tex&amp;gt;n/2&amp;lt;/tex&amp;gt; вершин и &amp;lt;tex&amp;gt;\partial(S)&amp;lt;/tex&amp;gt; — ''граничные дуги'' множества &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, то есть, множество дуг с единственной вершиной в &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Вершинное расширение===&lt;br /&gt;
''Вершинное изопериметрическое число'' &amp;lt;tex&amp;gt;h_{out}(G)&amp;lt;/tex&amp;gt; (также ''вершинное раширение'') графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;h_{out}(G) = \min\limits_{0 &amp;lt; |S|\leqslant \frac{n}{2}} \frac{|\partial_{\text{out}}(S)|}{|S|}&amp;lt;/tex&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;tex&amp;gt;\partial_{\text{out}}(S)&amp;lt;/tex&amp;gt; — ''внешняя граница'' &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, то есть множество вершин из &amp;lt;tex&amp;gt;V(G)\setminus S&amp;lt;/tex&amp;gt;, имеющих как минимум одного соседа в &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;. В варианте этого определения (называемом ''уникальным соседним расширением'') &amp;lt;tex&amp;gt;\partial_{\text{out}}(S)&amp;lt;/tex&amp;gt; заменяется на множество вершин из &amp;lt;tex&amp;gt;V&amp;lt;/tex&amp;gt; с ''точностью одним'' соседом из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
''Вершинное изопериметрическое число'' &amp;lt;tex&amp;gt;h_{in}(G)&amp;lt;/tex&amp;gt; графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;h_{in}(G) = \min\limits_{0 &amp;lt; |S|\leqslant \frac{n}{2}} \frac{|\partial_{\text{in}}(S)|}{|S|}&amp;lt;/tex&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;tex&amp;gt;\partial_{in}(S)&amp;lt;/tex&amp;gt; — ''внутренняя граница'' &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, то есть множество вершин &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, имеющих как минимум одного соседа в &amp;lt;tex&amp;gt;V(G)\setminus S&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Спектральное расширение===&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; является d-регулярным, возможно определение в терминах линейной алгебры на основе собственных значений матрицы смежности &amp;lt;tex&amp;gt;A = A(G)&amp;lt;/tex&amp;gt; графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;A_{ij}&amp;lt;/tex&amp;gt; — число дуг между вершинами &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;. Поскольку &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; является симметричной, согласно спектральной теореме, &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; имеет &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; действительных собственных значений &amp;lt;tex&amp;gt;\lambda_1 \ge \lambda_2 \ge \cdots \ge \lambda_{n}&amp;lt;/tex&amp;gt;. Известно, что эти значения лежат в промежутке &amp;lt;tex&amp;gt;[−d, d]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
Граф регулярен тогда и только тогда, когда вектор &amp;lt;tex&amp;gt;u\in \mathbb {R} ^{n} с u_{i}=1&amp;lt;/tex&amp;gt; для всех &amp;lt;tex&amp;gt;i = 1, …, n&amp;lt;/tex&amp;gt; является собственным вектором матрицы &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt;, а его собственное число будет постоянной степенью графа. Таким образом, мы имеем &amp;lt;tex&amp;gt;Au = du&amp;lt;/tex&amp;gt;, и &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt; — собственный вектор матрицы &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; с собственными значениями &amp;lt;tex&amp;gt;λ1 = d&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt; — степень вершин графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt;. Спектральный зазор графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; определяется как &amp;lt;tex&amp;gt;d−λ2&amp;lt;/tex&amp;gt; и является мерилом спектрального расширения графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Известно, что &amp;lt;tex&amp;gt;\lambda_n = −d&amp;lt;/tex&amp;gt; тогда и только тогда, когда &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; — двудольный. Во многих случаях, например в лемме о перемешивании, необходимо ограничить снизу не только зазор между &amp;lt;tex&amp;gt;\lambda_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\lambda_2&amp;lt;/tex&amp;gt;, но и зазор между &amp;lt;tex&amp;gt;\lambda_1&amp;lt;/tex&amp;gt; и вторым максимальным по модулю собственным значением:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\lambda=\max\{|\lambda_2|, |\lambda_{n}|\}&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Поскольку это собственное значение соответствует некоторому собственному вектору, ортогональному &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;, его можно определить, используя отношение Рэлея:&lt;br /&gt;
&amp;lt;tex&amp;gt;\lambda=\max_{0\neq v\perp u} \frac{\|Av\|_2}{\|v\|_2},&amp;lt;/tex&amp;gt;&lt;br /&gt;
gde&lt;br /&gt;
&amp;lt;tex&amp;gt;\|v\|_2=\left(\sum_{i=1}^n v_i^2\right)^{1/2}&amp;lt;/tex&amp;gt;&lt;br /&gt;
— евклидова норма вектора &amp;lt;tex&amp;gt;v\in \mathbb {R} ^{n}&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Нормализованная версия этого определения также широко используется и более удобна для получения определённых результатов. В таком случае рассматривается матрица &amp;lt;tex&amp;gt;{\tfrac {1}{d}}A&amp;lt;/tex&amp;gt;, являющаяся матрицей переходов графа G. Все её собственные значения лежат между &amp;lt;tex&amp;gt;−1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;. Если граф не регулярен, спектр графа может быть определён аналогичным образом, используя собственные значения матрицы Кирхгофа. Для направленного графа используются сингулярные значения матрицы сопряжения A, которые равны квадратным корням из собственных значений симметричной матрицы &amp;lt;tex&amp;gt;A^TA&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Конструирование==&lt;br /&gt;
Существуют три основные стратегии создания семейств графов расширений[6]. Первая стратегия — алгебраическая и теоретико-групповая, вторая — аналитическая, использующая аддитивную комбинаторику, и третья — комбинаторная, использующая зигзаг-произведение и связанные комбинаторные произведения.&lt;br /&gt;
===Маргулис-Габбер-Галил===&lt;br /&gt;
Алгебраическое конструирование, основанное на графах Кэли, известно для различных вариантов экспандеров. Следующее конструирование принадлежит Маргулису и было проанализировано Габбером (Gabber) и Галилом (Galil). Для любого натурального &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; строим граф, &amp;lt;tex&amp;gt;G_{n}&amp;lt;/tex&amp;gt; со множеством вершин &amp;lt;tex&amp;gt;\mathbb Z _{n}\times \mathbb {Z} _{n}&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;\mathbb {Z} _{n}=\mathbb Z /n \mathbb Z&amp;lt;/tex&amp;gt; . Для любой вершины &amp;lt;tex&amp;gt;(x,y)\in \mathbb {Z} _{n}\times \mathbb {Z} _{n}&amp;lt;/tex&amp;gt;, её восемь соседей будут&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;(x \pm 2y,y), (x \pm (2y+1),y), (x,y \pm 2x), (x,y \pm (2x+1)).&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Выполняется следующая теорема:&lt;br /&gt;
{{Теорема&lt;br /&gt;
|statement=&lt;br /&gt;
Для всех &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; граф &amp;lt;tex&amp;gt;Gn&amp;lt;/tex&amp;gt; второе по величине собственное число&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;tex&amp;gt;\lambda(G)\leq 5 \sqrt{2}&amp;lt;/tex&amp;gt;.}}&lt;br /&gt;
&lt;br /&gt;
===Граф Рамануджана===&lt;br /&gt;
&amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt;-регулярный граф называется графом Рамануджана, если его второе по модулю собственное число не превосходит &amp;lt;tex&amp;gt;2{\sqrt {d-1}}&amp;lt;/tex&amp;gt;. Любоцкий, Сарнак, Филлипс и Маргулис указали явную конструкцию графов Кэли, являющихся графами Рамануджана. Опишем эту конструкцию.&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt;p&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; простые числа, &amp;lt;tex&amp;gt;p = 1&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;mod&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;4&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;q = 1&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;mod&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;4&amp;lt;/tex&amp;gt;. В качестве группы &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; возьмём &amp;lt;tex&amp;gt;PGL(2, \mathbb Z/q{\mathbb Z})&amp;lt;/tex&amp;gt;, т.е. невырожденные матрицы 2 × 2 над полем вычетов по модулю &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;, профакторизованные по отношению пропорциональности (с обычной операцией матричного умножения).&lt;br /&gt;
Далее мы зададим в этой группе симметричное множество &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;. Выберем такое целое &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;i^2 = −1&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;mod&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;. Можно доказать, что тогда имеется ровно &amp;lt;tex&amp;gt;(p + 1)&amp;lt;/tex&amp;gt; целочисленное решение уравнения&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;a_0^2 + a_1^2 + a_3^2 = p&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
такое, что &amp;lt;tex&amp;gt;a_0&amp;lt;/tex&amp;gt; положительно и нечётно, а &amp;lt;tex&amp;gt;a_1&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;a_2&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;a_3&amp;lt;/tex&amp;gt; чётны. Каждой такой четвёрке сопоставим матрицу&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;A = \begin{pmatrix} a_0 + ia_1 &amp;amp; a_2 + ia_3\\ -a_2 + ia_3 &amp;amp; a_0 - ia_1 \end{pmatrix}&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Эти матрицы образуют множество S.&lt;br /&gt;
Нетрудно понять, что граф Кэли &amp;lt;tex&amp;gt;(G, S)&amp;lt;/tex&amp;gt; состоит из &amp;lt;tex&amp;gt;\Theta(q^3)&amp;lt;/tex&amp;gt; вершин, и степень каждой вершины равна &amp;lt;tex&amp;gt;(p + 1)&amp;lt;/tex&amp;gt;. Свойства данного графа зависят от соотношения &amp;lt;tex&amp;gt;p&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;. Рассмотрим случай, когда p является квадратичным вычетом по модулю &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;. Тогда полученный граф Кэли состоит из двух связных компонент (поскольку все матрицы из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; лежат в подгруппе &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; индекса два — подгруппе матриц, определитель которых является квадратичным вычетом). Обозначим &amp;lt;tex&amp;gt;X^{p,q}&amp;lt;/tex&amp;gt; связную компоненту полученного графа. Можно доказать, что у &amp;lt;tex&amp;gt;X^{p,q}&amp;lt;/tex&amp;gt; второе по абсолютной величине собственное число не превосходит &amp;lt;tex&amp;gt;2{\sqrt{p}}&amp;lt;/tex&amp;gt;, т.е. мы получили граф Рамануджана.&lt;br /&gt;
&lt;br /&gt;
==Примеры применения экспандеров==&lt;br /&gt;
===Коды, исправляющие ошибки===&lt;br /&gt;
С помощью расширяющего графа можно посторить линейный код, позволяющий исправлять ошибки в доле &amp;lt;tex&amp;gt;\delta = 1/(2000d)&amp;lt;/tex&amp;gt; битов. Чтобы задать линейный код с длиной кодового слова n, достаточно описать его проверочную матрицу &amp;lt;tex&amp;gt;H&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;(&amp;lt;/tex&amp;gt;слово &amp;lt;tex&amp;gt;x \in \{0, 1\}^n&amp;lt;/tex&amp;gt; является кодовым словом если и только если &amp;lt;tex&amp;gt;Hx^t = 0)&amp;lt;/tex&amp;gt;. Другими словами, нужно задать систему линейных уравнений для переменных &amp;lt;tex&amp;gt;x_1, . . . , x_n;&amp;lt;/tex&amp;gt; решения этой системы и будут кодовыми словами.&lt;br /&gt;
===Увеличение вероятности успеха в алгоритмах с датчиком случайных чисел===&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=Язык &amp;lt;tex&amp;gt;L&amp;lt;/tex&amp;gt; принадлежит сложностному классу &amp;lt;tex&amp;gt;RP&amp;lt;/tex&amp;gt;, если существует полиномиальный алгоритм &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; такой что&lt;br /&gt;
1. для &amp;lt;tex&amp;gt;x \in L&amp;lt;/tex&amp;gt; для всех &amp;lt;tex&amp;gt;r \in \{0, 1\}^{poly(n)}&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;A(x, r) = 1&amp;lt;/tex&amp;gt;&lt;br /&gt;
2. для &amp;lt;tex&amp;gt;x \notin L&amp;lt;/tex&amp;gt; не более чем для 1/2000 всех &amp;lt;tex&amp;gt;r \in \{0, 1\}^{poly(n)}&amp;lt;/tex&amp;gt; может выполняться &amp;lt;tex&amp;gt;A(x, r) = 1&amp;lt;/tex&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Покажем, что для любого &amp;lt;tex&amp;gt;\epsilon &amp;gt; 0&amp;lt;/tex&amp;gt; полиномиальный вероятностный алгоритм A можно модифицировать таким образом, чтобы вероятность ошибки уменьшилась до &amp;lt;tex&amp;gt;\epsilon&amp;lt;/tex&amp;gt;, а число используемых случайных битов не изменится.&lt;br /&gt;
&lt;br /&gt;
Пусть исходный алгоритм использует &amp;lt;tex&amp;gt;k = k(n)&amp;lt;/tex&amp;gt; случайных битов для вычислений на входах длины &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;. Зафиксируем &amp;lt;tex&amp;gt;(2^k, 2^k, d)&amp;lt;/tex&amp;gt; - экспандер &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;d &amp;gt; 1/\epsilon&amp;lt;/tex&amp;gt;. Новый алгоритм действует следующим образом: выбирается случайная вершина &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; из левой доли графа (для этого требуется &amp;lt;tex&amp;gt;k&amp;lt;/tex&amp;gt; случайных битов); затем исходный алгоритм &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; последовательно запускается на всех &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt; наборах случайных битов, соответствующих соседям вершины &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;. Если все полученные ответы равны &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;, новый алгоритм также возвращает единицу; в противном случае возвращается ноль.&lt;br /&gt;
Покажем, что у нового алгоритма вероятность ошибки не превосходит &amp;lt;tex&amp;gt;1/d&amp;lt;/tex&amp;gt;. В самом деле, обозначим &amp;lt;tex&amp;gt;B = B(x)&amp;lt;/tex&amp;gt; множество таких вершин &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt; из правой доли графа, которые соответствуют неверному ответу старого алгоритма на входе &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;; аналогично, обозначим &amp;lt;tex&amp;gt;S = S(x)&amp;lt;/tex&amp;gt; множество таких вершин v из левой доли графа, которые для которых новый алгоритм даёт неверный ответ на входе &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;. Очевидно, &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; состоит из вершин, все соседи которых лежат в B. Предположим, что &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; содержит не менее &amp;lt;tex&amp;gt;n/(1000d)&amp;lt;/tex&amp;gt; вершин. Выберем среди них ровно &amp;lt;tex&amp;gt;n/(1000d)&amp;lt;/tex&amp;gt; вершин и назовём это множество &amp;lt;tex&amp;gt;S'&amp;lt;/tex&amp;gt;. По свойству экспандера, имеем&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;|\Gamma(S')| \geqslant  \frac{7}{8}d\frac{n}{1000d}=7n/8000 &amp;gt; n/2000&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Это противоречит тому, что все соседи &amp;lt;tex&amp;gt;S'&amp;lt;/tex&amp;gt; лежат в &amp;lt;tex&amp;gt;B&amp;lt;/tex&amp;gt;. В данном случае нам нужна явная в более сильном (чем в первом примере) смысле конструкция экспандера. Размер графа экспоненциално растёт с увеличенем &amp;lt;tex&amp;gt;k&amp;lt;/tex&amp;gt;, и нам необходим алгоритм, который по заданному номеру вершины &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; (из левой доли графа) за время &amp;lt;tex&amp;gt;poly(k)&amp;lt;/tex&amp;gt; находит список номеров всех соседей этой вершины (в правой доле графа).&lt;br /&gt;
&lt;br /&gt;
===Хранение множества со сверхбыстрым запросом элементов===&lt;br /&gt;
Мы организуем хранение m-элементного множества &amp;lt;tex&amp;gt;S \subset \{1, . . . , n\}&amp;lt;/tex&amp;gt; в виде описания &amp;lt;tex&amp;gt;X&amp;lt;/tex&amp;gt;, состоящего из &amp;lt;tex&amp;gt;O(m log n)&amp;lt;/tex&amp;gt; битов. При этом проверка принадлежности &amp;lt;tex&amp;gt;a \in S&amp;lt;/tex&amp;gt; будет производиться чрезвычайно быстро. А именно, мы построим такой вероятностный алгоритм, который по любому входу &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; запрашивает из &amp;lt;tex&amp;gt;X&amp;lt;/tex&amp;gt; один бит; если этот бит оказывается равным единице, то алгоритм отвечает, что &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; является элементом &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;; в противном случае алгоритм говорит, что &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; множеству не принадлежит. При этом для каждого &amp;lt;tex&amp;gt;a \in \{1, . . . , n\}&amp;lt;/tex&amp;gt; алгоритм ошибается с вероятностью не более &amp;lt;tex&amp;gt;1/3&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Чтобы построить нужное нам хранилище &amp;lt;tex&amp;gt;X&amp;lt;/tex&amp;gt;, мы сначала зафиксируем некоторый экспандер, у которого левая доля &amp;lt;tex&amp;gt;L&amp;lt;/tex&amp;gt; состоит из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; вершин, правая &amp;lt;tex&amp;gt;R&amp;lt;/tex&amp;gt; из &amp;lt;tex&amp;gt;k = O(m log n)&amp;lt;/tex&amp;gt; вершин, степень всех вершин левой доли одинакова и равна некоторому &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt;, и для каждого &amp;lt;tex&amp;gt;A \subset L&amp;lt;/tex&amp;gt; размера не более &amp;lt;tex&amp;gt;2m&amp;lt;/tex&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;|\Gamma(A)| \geqslant \frac{7}{8}d|A|&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;X&amp;lt;/tex&amp;gt; будет состоять в разметке вершин правой доли нулями и единицами. Эту разметку нужно выбрать таким образом, чтобы у каждой вершины из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; не менее &amp;lt;tex&amp;gt;2/3&amp;lt;/tex&amp;gt; соседей были помечены единицей, а у каждой вершины не из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; не менее &amp;lt;tex&amp;gt;2/3&amp;lt;/tex&amp;gt; соседей были помечены нулями.&amp;lt;tex&amp;gt;X&amp;lt;/tex&amp;gt; будет состоять в разметке вершин правой доли нулями и единицами. Эту разметку нужно выбрать таким образом, чтобы у каждой вершины из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; не менее &amp;lt;tex&amp;gt;2/3&amp;lt;/tex&amp;gt; соседей были помечены единицей, а у каждой вершины не из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; не менее &amp;lt;tex&amp;gt;2/3&amp;lt;/tex&amp;gt; соседей были помечены нулями.&lt;br /&gt;
&lt;br /&gt;
Остаётся объяснить, как построить нужную нам разметку правой доли графа. Будем строить её последовательными приближениями. Сначала пометим всех соседей всех вершин из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; единицами, а все остальные вершины – нулями. На данной разметке наш алгоритм с вероятностью &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt; возвращает правильный ответ для всех &amp;lt;tex&amp;gt;a \in S&amp;lt;/tex&amp;gt;. Однако для &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; не из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; проверочный алгоритм может работать неверно. Обозначим T множество всех таких вершин вне &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, у которых более &amp;lt;tex&amp;gt;d/3&amp;lt;/tex&amp;gt; соседей помечены единицей. Поменяем разметку: пометим всех соседей &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; нулём. Теперь разметка может стать плохой для части вершин из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;. Обозначим &amp;lt;tex&amp;gt;S'&amp;lt;/tex&amp;gt; множество всех таких вершин из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, у которых более &amp;lt;tex&amp;gt;d/3&amp;lt;/tex&amp;gt; соседей помечены нулями. Далее, поменяем разметку у&lt;br /&gt;
всех соседей &amp;lt;tex&amp;gt;S'&amp;lt;/tex&amp;gt; на единицы. После этого может вновь возникнуть множество ‘неправильных’ вершин вне &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, и т.д.&lt;br /&gt;
&lt;br /&gt;
Чтобы доказать, что данный процесс в конце концов сойдётся, нужно показать, что на каждом шаге число ‘проблемных’ вершин уменьшается в константу раз. Поскольку все шаги аналогичны, достаточно разобрать самый первый: докажем, что &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; в константу раз меньше, чем &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;. Мы воспользуемся тем, что для &amp;lt;tex&amp;gt;S \cup T&amp;lt;/tex&amp;gt; выполнено свойство расширения:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;(7/8)d(|S|+|T|) \leqslant |\Gamma(S \cup T)| \leqslant d|S| + (2/3)d|T|&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Откуда получаем &amp;lt;tex&amp;gt;|T| \leqslant 3/5|S|&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Приложения и полезные свойства==&lt;br /&gt;
Первоначально интерес к экспандерам возник с целью построения устойчивой сети (телефонов или компьютеров) — число дуг графов расширения с ограниченным значением регулярности растет линейно по отношению к числу вершин.&lt;br /&gt;
&lt;br /&gt;
Экспандеры нашли широкое применение в теории вычислительных машин и систем, для построения алгоритмов, в корректирующих кодах, экстракторах, генераторах псевдослучайных чисел, сетях сортировки и компьютерных сетях. Они также используются для доказательства многих важных результатов в теории вычислительной сложности, таких как &amp;lt;tex&amp;gt;SL=L&amp;lt;/tex&amp;gt; и Теорема PCP. В криптографии экспандеры используются для создания хеш-функций.&lt;br /&gt;
&lt;br /&gt;
Ниже приведены некоторые свойства экспандеров, считающиеся полезными во многих областях.&lt;br /&gt;
===Лемма о перемешивании===&lt;br /&gt;
Лемма о перемешивании утверждает, что для любых двух подмножеств вершин &amp;lt;tex&amp;gt;S,T\subseteq V&amp;lt;/tex&amp;gt; число рёбер между &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; примерно равно числу рёбер в случайном &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt;-регулярном графе. Приближение тем лучше, чем меньше &amp;lt;tex&amp;gt;\lambda =\max\{|\lambda _{2}|&amp;lt;/tex&amp;gt;,&amp;lt;tex&amp;gt;|\lambda _{n}|\}&amp;lt;/tex&amp;gt;. В случайном &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt;-регулярном графе, также как и в случайном графе Эрдёша — Реньи с вероятностью &amp;lt;tex&amp;gt;{\tfrac {d}{n}}&amp;lt;/tex&amp;gt; выбора ребра, ожидается &amp;lt;tex&amp;gt;{\tfrac {d}{n}}\cdot |S|\cdot |T|&amp;lt;/tex&amp;gt; рёбер между &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Более формально, пусть &amp;lt;tex&amp;gt;E(S, T)&amp;lt;/tex&amp;gt; обозначает число рёбер между &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;. Если эти два множества пересекаются, дуги в пересечении считаются дважды, так что&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;E(S,T)=2|E(G[S\cap T])|+E(S\setminus T,T)+E(S,T\setminus S)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
Лемма о перемешивании утверждает, что&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\left|E(S,T)-{\frac {d\cdot |S|\cdot |T|}{n}}\right|\leq d\lambda {\sqrt {|S|\cdot |T|}}&amp;lt;/tex&amp;gt;,&lt;br /&gt;
где &amp;lt;tex&amp;gt;\lambda &amp;lt;/tex&amp;gt; — абсолютное значение нормализованного второго по величине собственного значения.&lt;br /&gt;
&lt;br /&gt;
Недавно Билу (Bilu) и Линайл (Linial) показали, что обратное тоже верно, то есть, при условии выполнения неравенства из леммы второе по величине собственное значение равно &amp;lt;tex&amp;gt;O(d\lambda \cdot (1+\log(1/\lambda )))&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Блуждания по экспандеру===&lt;br /&gt;
Согласно границе Чернова, если выбирать много независимых случайных значений из интервала &amp;lt;tex&amp;gt;[−1, 1]&amp;lt;/tex&amp;gt;, с большой степенью вероятности среднее выбранных значений будет близко к математическому ожиданию случайной переменной. Лемма о блуждании по экспандеру, согласно статьям Аджтари, Комлоша и Семереди и Гилмана, утверждает, что то же самое верно и для блужданий по экспандеру. Это полезно в теории дерандомизации, поскольку блуждание по экспандеру использует много меньше случайных бит, чем случайная независимая выборка.&lt;br /&gt;
&lt;br /&gt;
==Источники информации==&lt;br /&gt;
*[https://ru.wikipedia.org/wiki/%D0%AD%D0%BA%D1%81%D0%BF%D0%B0%D0%BD%D0%B4%D0%B5%D1%80_(%D1%82%D0%B5%D0%BE%D1%80%D0%B8%D1%8F_%D0%B3%D1%80%D0%B0%D1%84%D0%BE%D0%B2) Экспандер (теория графов)]&lt;br /&gt;
*&lt;br /&gt;
[S. Hoory, N. Linial, A. Wigderson. Expander graphs and their&lt;br /&gt;
applications. Bulletin of the AMS, vol. 43, Number 4, Oct. 2006, pp.439–&lt;br /&gt;
561.]&lt;br /&gt;
*&lt;br /&gt;
[ H. Buhrman, P.B. Miltersen, J. Radhakrishnan, S. Venkatesh. Are&lt;br /&gt;
Bitvectors optimal? SIAM J. Comput., 31(6):1723–1744, 2002.]&lt;/div&gt;</summary>
		<author><name>Kozichuk</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%93%D1%80%D0%B0%D1%84%D1%8B-%D1%8D%D0%BA%D1%81%D0%BF%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%8B&amp;diff=50934</id>
		<title>Графы-экспандеры</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%93%D1%80%D0%B0%D1%84%D1%8B-%D1%8D%D0%BA%D1%81%D0%BF%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%8B&amp;diff=50934"/>
				<updated>2016-01-09T02:09:24Z</updated>
		
		<summary type="html">&lt;p&gt;Kozichuk: /* Источники информации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Граф-экспандер''' (или расширяющийся граф, англ. ''expander graph'') - в комбинаторике сильно разреженный граф, при этом связность определяется по вершинам, дугам или спектру.&lt;br /&gt;
&lt;br /&gt;
==Определение==&lt;br /&gt;
''Граф-экспандер'' — это конечный ненаправленный ''мультиграф'', в котором любое подмножество вершин, не являясь «слишком большим», имеет «сильную» связность. Различные формализации этих понятий дают различные типы экспандеров: ''рёберный расширитель'', ''вершинный расширитель'', и ''спектральный расширитель''.&lt;br /&gt;
&lt;br /&gt;
Несвязный граф не является экспандером. Любой связный граф является экспандером, однако различные связные графы имеют различные параметры расширителя. Полный граф имеет лучшие параметры расширителя, но имеет наибольшую возможную степень. Неформально говоря, граф является хорошим экспандером, если он имеет низкую степень и высокий параметр расширителя.&lt;br /&gt;
&lt;br /&gt;
===Реберное расширение===&lt;br /&gt;
''Рёберное расширение'' (также ''изопериметрическое число'' или константа Чигера) &amp;lt;tex&amp;gt;h(G)&amp;lt;/tex&amp;gt; графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; для &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; вершин определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;h(G) = \min\limits_{0 &amp;lt; |S|\leqslant \frac{n}{2}} \frac{|\partial_{\text{out}}(S)|}{|S|}&amp;lt;/tex&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где минимум берётся по всем непустым множествам &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; не более чем &amp;lt;tex&amp;gt;n/2&amp;lt;/tex&amp;gt; вершин и &amp;lt;tex&amp;gt;\partial(S)&amp;lt;/tex&amp;gt; — ''граничные дуги'' множества &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, то есть, множество дуг с единственной вершиной в &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Вершинное расширение===&lt;br /&gt;
''Вершинное изопериметрическое число'' &amp;lt;tex&amp;gt;h_{out}(G)&amp;lt;/tex&amp;gt; (также ''вершинное раширение'') графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;h_{out}(G) = \min\limits_{0 &amp;lt; |S|\leqslant \frac{n}{2}} \frac{|\partial_{\text{out}}(S)|}{|S|}&amp;lt;/tex&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;tex&amp;gt;\partial_{\text{out}}(S)&amp;lt;/tex&amp;gt; — ''внешняя граница'' &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, то есть множество вершин из &amp;lt;tex&amp;gt;V(G)\setminus S&amp;lt;/tex&amp;gt;, имеющих как минимум одного соседа в &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;. В варианте этого определения (называемом ''уникальным соседним расширением'') &amp;lt;tex&amp;gt;\partial_{\text{out}}(S)&amp;lt;/tex&amp;gt; заменяется на множество вершин из &amp;lt;tex&amp;gt;V&amp;lt;/tex&amp;gt; с ''точностью одним'' соседом из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
''Вершинное изопериметрическое число'' &amp;lt;tex&amp;gt;h_{in}(G)&amp;lt;/tex&amp;gt; графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;h_{in}(G) = \min\limits_{0 &amp;lt; |S|\leqslant \frac{n}{2}} \frac{|\partial_{\text{in}}(S)|}{|S|}&amp;lt;/tex&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;tex&amp;gt;\partial_{in}(S)&amp;lt;/tex&amp;gt; — ''внутренняя граница'' &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, то есть множество вершин &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, имеющих как минимум одного соседа в &amp;lt;tex&amp;gt;V(G)\setminus S&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Спектральное расширение===&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; является d-регулярным, возможно определение в терминах линейной алгебры на основе собственных значений матрицы смежности &amp;lt;tex&amp;gt;A = A(G)&amp;lt;/tex&amp;gt; графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;A_{ij}&amp;lt;/tex&amp;gt; — число дуг между вершинами &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;. Поскольку &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; является симметричной, согласно спектральной теореме, &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; имеет &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; действительных собственных значений &amp;lt;tex&amp;gt;\lambda_1 \ge \lambda_2 \ge \cdots \ge \lambda_{n}&amp;lt;/tex&amp;gt;. Известно, что эти значения лежат в промежутке &amp;lt;tex&amp;gt;[−d, d]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
Граф регулярен тогда и только тогда, когда вектор &amp;lt;tex&amp;gt;u\in \mathbb {R} ^{n} с u_{i}=1&amp;lt;/tex&amp;gt; для всех &amp;lt;tex&amp;gt;i = 1, …, n&amp;lt;/tex&amp;gt; является собственным вектором матрицы &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt;, а его собственное число будет постоянной степенью графа. Таким образом, мы имеем &amp;lt;tex&amp;gt;Au = du&amp;lt;/tex&amp;gt;, и &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt; — собственный вектор матрицы &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; с собственными значениями &amp;lt;tex&amp;gt;λ1 = d&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt; — степень вершин графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt;. Спектральный зазор графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; определяется как &amp;lt;tex&amp;gt;d−λ2&amp;lt;/tex&amp;gt; и является мерилом спектрального расширения графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Известно, что &amp;lt;tex&amp;gt;\lambda_n = −d&amp;lt;/tex&amp;gt; тогда и только тогда, когда &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; — двудольный. Во многих случаях, например в лемме о перемешивании, необходимо ограничить снизу не только зазор между &amp;lt;tex&amp;gt;\lambda_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\lambda_2&amp;lt;/tex&amp;gt;, но и зазор между &amp;lt;tex&amp;gt;\lambda_1&amp;lt;/tex&amp;gt; и вторым максимальным по модулю собственным значением:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\lambda=\max\{|\lambda_2|, |\lambda_{n}|\}&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Поскольку это собственное значение соответствует некоторому собственному вектору, ортогональному &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;, его можно определить, используя отношение Рэлея:&lt;br /&gt;
&amp;lt;tex&amp;gt;\lambda=\max_{0\neq v\perp u} \frac{\|Av\|_2}{\|v\|_2},&amp;lt;/tex&amp;gt;&lt;br /&gt;
gde&lt;br /&gt;
&amp;lt;tex&amp;gt;\|v\|_2=\left(\sum_{i=1}^n v_i^2\right)^{1/2}&amp;lt;/tex&amp;gt;&lt;br /&gt;
— евклидова норма вектора &amp;lt;tex&amp;gt;v\in \mathbb {R} ^{n}&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Нормализованная версия этого определения также широко используется и более удобна для получения определённых результатов. В таком случае рассматривается матрица &amp;lt;tex&amp;gt;{\tfrac {1}{d}}A&amp;lt;/tex&amp;gt;, являющаяся матрицей переходов графа G. Все её собственные значения лежат между &amp;lt;tex&amp;gt;−1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;. Если граф не регулярен, спектр графа может быть определён аналогичным образом, используя собственные значения матрицы Кирхгофа. Для направленного графа используются сингулярные значения матрицы сопряжения A, которые равны квадратным корням из собственных значений симметричной матрицы &amp;lt;tex&amp;gt;A^TA&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Конструирование==&lt;br /&gt;
Существуют три основные стратегии создания семейств графов расширений[6]. Первая стратегия — алгебраическая и теоретико-групповая, вторая — аналитическая, использующая аддитивную комбинаторику, и третья — комбинаторная, использующая зигзаг-произведение и связанные комбинаторные произведения.&lt;br /&gt;
===Маргулис-Габбер-Галил===&lt;br /&gt;
Алгебраическое конструирование, основанное на графах Кэли, известно для различных вариантов экспандеров. Следующее конструирование принадлежит Маргулису и было проанализировано Габбером (Gabber) и Галилом (Galil). Для любого натурального &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; строим граф, &amp;lt;tex&amp;gt;G_{n}&amp;lt;/tex&amp;gt; со множеством вершин &amp;lt;tex&amp;gt;\mathbb Z _{n}\times \mathbb {Z} _{n}&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;\mathbb {Z} _{n}=\mathbb Z /n \mathbb Z&amp;lt;/tex&amp;gt; . Для любой вершины &amp;lt;tex&amp;gt;(x,y)\in \mathbb {Z} _{n}\times \mathbb {Z} _{n}&amp;lt;/tex&amp;gt;, её восемь соседей будут&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;(x \pm 2y,y), (x \pm (2y+1),y), (x,y \pm 2x), (x,y \pm (2x+1)).&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Выполняется следующая теорема:&lt;br /&gt;
{{Теорема&lt;br /&gt;
|statement=&lt;br /&gt;
Для всех &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; граф &amp;lt;tex&amp;gt;Gn&amp;lt;/tex&amp;gt; второе по величине собственное число&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;tex&amp;gt;\lambda(G)\leq 5 \sqrt{2}&amp;lt;/tex&amp;gt;.}}&lt;br /&gt;
&lt;br /&gt;
===Граф Рамануджана===&lt;br /&gt;
&amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt;-регулярный граф называется графом Рамануджана, если его второе по модулю собственное число не превосходит &amp;lt;tex&amp;gt;2{\sqrt {d-1}}&amp;lt;/tex&amp;gt;. Любоцкий, Сарнак, Филлипс и Маргулис указали явную конструкцию графов Кэли, являющихся графами Рамануджана. Опишем эту конструкцию.&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt;p&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; простые числа, &amp;lt;tex&amp;gt;p = 1&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;mod&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;4&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;q = 1&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;mod&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;4&amp;lt;/tex&amp;gt;. В качестве группы &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; возьмём &amp;lt;tex&amp;gt;PGL(2, \mathbb Z/q{\mathbb Z})&amp;lt;/tex&amp;gt;, т.е. невырожденные матрицы 2 × 2 над полем вычетов по модулю &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;, профакторизованные по отношению пропорциональности (с обычной операцией матричного умножения).&lt;br /&gt;
Далее мы зададим в этой группе симметричное множество &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;. Выберем такое целое &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;i^2 = −1&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;mod&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;. Можно доказать, что тогда имеется ровно &amp;lt;tex&amp;gt;(p + 1)&amp;lt;/tex&amp;gt; целочисленное решение уравнения&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;a_0^2 + a_1^2 + a_3^2 = p&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
такое, что &amp;lt;tex&amp;gt;a_0&amp;lt;/tex&amp;gt; положительно и нечётно, а &amp;lt;tex&amp;gt;a_1&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;a_2&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;a_3&amp;lt;/tex&amp;gt; чётны. Каждой такой четвёрке сопоставим матрицу&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;A = \begin{pmatrix} a_0 + ia_1 &amp;amp; a_2 + ia_3\\ -a_2 + ia_3 &amp;amp; a_0 - ia_1 \end{pmatrix}&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Эти матрицы образуют множество S.&lt;br /&gt;
Нетрудно понять, что граф Кэли &amp;lt;tex&amp;gt;(G, S)&amp;lt;/tex&amp;gt; состоит из &amp;lt;tex&amp;gt;\Theta(q^3)&amp;lt;/tex&amp;gt; вершин, и степень каждой вершины равна &amp;lt;tex&amp;gt;(p + 1)&amp;lt;/tex&amp;gt;. Свойства данного графа зависят от соотношения &amp;lt;tex&amp;gt;p&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;. Рассмотрим случай, когда p является квадратичным вычетом по модулю &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;. Тогда полученный граф Кэли состоит из двух связных компонент (поскольку все матрицы из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; лежат в подгруппе &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; индекса два — подгруппе матриц, определитель которых является квадратичным вычетом). Обозначим &amp;lt;tex&amp;gt;X^{p,q}&amp;lt;/tex&amp;gt; связную компоненту полученного графа. Можно доказать, что у &amp;lt;tex&amp;gt;X^{p,q}&amp;lt;/tex&amp;gt; второе по абсолютной величине собственное число не превосходит &amp;lt;tex&amp;gt;2{\sqrt{p}}&amp;lt;/tex&amp;gt;, т.е. мы получили граф Рамануджана.&lt;br /&gt;
&lt;br /&gt;
==Примеры применения экспандеров==&lt;br /&gt;
===Коды, исправляющие ошибки===&lt;br /&gt;
С помощью расширяющего графа можно посторить линейный код, позволяющий исправлять ошибки в доле &amp;lt;tex&amp;gt;\delta = 1/(2000d)&amp;lt;/tex&amp;gt; битов. Чтобы задать линейный код с длиной кодового слова n, достаточно описать его проверочную матрицу &amp;lt;tex&amp;gt;H&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;(&amp;lt;/tex&amp;gt;слово &amp;lt;tex&amp;gt;x \in \{0, 1\}^n&amp;lt;/tex&amp;gt; является кодовым словом если и только если &amp;lt;tex&amp;gt;Hx^t = 0)&amp;lt;/tex&amp;gt;. Другими словами, нужно задать систему линейных уравнений для переменных &amp;lt;tex&amp;gt;x_1, . . . , x_n;&amp;lt;/tex&amp;gt; решения этой системы и будут кодовыми словами.&lt;br /&gt;
===Увеличение вероятности успеха в алгоритмах с датчиком случайных чисел===&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=Язык &amp;lt;tex&amp;gt;L&amp;lt;/tex&amp;gt; принадлежит сложностному классу &amp;lt;tex&amp;gt;RP&amp;lt;/tex&amp;gt;, если существует полиномиальный алгоритм &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; такой что&lt;br /&gt;
1. для &amp;lt;tex&amp;gt;x \in L&amp;lt;/tex&amp;gt; для всех &amp;lt;tex&amp;gt;r \in \{0, 1\}^{poly(n)}&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;A(x, r) = 1&amp;lt;/tex&amp;gt;&lt;br /&gt;
2. для &amp;lt;tex&amp;gt;x \notin L&amp;lt;/tex&amp;gt; не более чем для 1/2000 всех &amp;lt;tex&amp;gt;r \in \{0, 1\}^{poly(n)}&amp;lt;/tex&amp;gt; может выполняться &amp;lt;tex&amp;gt;A(x, r) = 1&amp;lt;/tex&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Покажем, что для любого &amp;lt;tex&amp;gt;\epsilon &amp;gt; 0&amp;lt;/tex&amp;gt; полиномиальный вероятностный алгоритм A можно модифицировать таким образом, чтобы вероятность ошибки уменьшилась до &amp;lt;tex&amp;gt;\epsilon&amp;lt;/tex&amp;gt;, а число используемых случайных битов не изменится.&lt;br /&gt;
&lt;br /&gt;
Пусть исходный алгоритм использует &amp;lt;tex&amp;gt;k = k(n)&amp;lt;/tex&amp;gt; случайных битов для вычислений на входах длины &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;. Зафиксируем &amp;lt;tex&amp;gt;(2^k, 2^k, d)&amp;lt;/tex&amp;gt; - экспандер &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;d &amp;gt; 1/\epsilon&amp;lt;/tex&amp;gt;. Новый алгоритм действует следующим образом: выбирается случайная вершина &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; из левой доли графа (для этого требуется &amp;lt;tex&amp;gt;k&amp;lt;/tex&amp;gt; случайных битов); затем исходный алгоритм &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; последовательно запускается на всех &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt; наборах случайных битов, соответствующих соседям вершины &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;. Если все полученные ответы равны &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;, новый алгоритм также возвращает единицу; в противном случае возвращается ноль.&lt;br /&gt;
Покажем, что у нового алгоритма вероятность ошибки не превосходит &amp;lt;tex&amp;gt;1/d&amp;lt;/tex&amp;gt;. В самом деле, обозначим &amp;lt;tex&amp;gt;B = B(x)&amp;lt;/tex&amp;gt; множество таких вершин &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt; из правой доли графа, которые соответствуют неверному ответу старого алгоритма на входе &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;; аналогично, обозначим &amp;lt;tex&amp;gt;S = S(x)&amp;lt;/tex&amp;gt; множество таких вершин v из левой доли графа, которые для которых новый алгоритм даёт неверный ответ на входе &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;. Очевидно, &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; состоит из вершин, все соседи которых лежат в B. Предположим, что &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; содержит не менее &amp;lt;tex&amp;gt;n/(1000d)&amp;lt;/tex&amp;gt; вершин. Выберем среди них ровно &amp;lt;tex&amp;gt;n/(1000d)&amp;lt;/tex&amp;gt; вершин и назовём это множество &amp;lt;tex&amp;gt;S'&amp;lt;/tex&amp;gt;. По свойству экспандера, имеем&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;|\Gamma(S')| \geqslant  \frac{7}{8}d\frac{n}{1000d}=7n/8000 &amp;gt; n/2000&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Это противоречит тому, что все соседи &amp;lt;tex&amp;gt;S'&amp;lt;/tex&amp;gt; лежат в &amp;lt;tex&amp;gt;B&amp;lt;/tex&amp;gt;. В данном случае нам нужна явная в более сильном (чем в первом примере) смысле конструкция экспандера. Размер графа экспоненциално растёт с увеличенем &amp;lt;tex&amp;gt;k&amp;lt;/tex&amp;gt;, и нам необходим алгоритм, который по заданному номеру вершины &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; (из левой доли графа) за время &amp;lt;tex&amp;gt;poly(k)&amp;lt;/tex&amp;gt; находит список номеров всех соседей этой вершины (в правой доле графа).&lt;br /&gt;
&lt;br /&gt;
===Хранение множества со сверхбыстрым запросом элементов===&lt;br /&gt;
Мы организуем хранение m-элементного множества &amp;lt;tex&amp;gt;S \subset \{1, . . . , n\}&amp;lt;/tex&amp;gt; в виде описания &amp;lt;tex&amp;gt;X&amp;lt;/tex&amp;gt;, состоящего из &amp;lt;tex&amp;gt;O(m log n)&amp;lt;/tex&amp;gt; битов. При этом проверка принадлежности &amp;lt;tex&amp;gt;a \in S&amp;lt;/tex&amp;gt; будет производиться чрезвычайно быстро. А именно, мы построим такой вероятностный алгоритм, который по любому входу &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; запрашивает из &amp;lt;tex&amp;gt;X&amp;lt;/tex&amp;gt; один бит; если этот бит оказывается равным единице, то алгоритм отвечает, что &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; является элементом &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;; в противном случае алгоритм говорит, что &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; множеству не принадлежит. При этом для каждого &amp;lt;tex&amp;gt;a \in \{1, . . . , n\}&amp;lt;/tex&amp;gt; алгоритм ошибается с вероятностью не более &amp;lt;tex&amp;gt;1/3&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Чтобы построить нужное нам хранилище &amp;lt;tex&amp;gt;X&amp;lt;/tex&amp;gt;, мы сначала зафиксируем некоторый экспандер, у которого левая доля &amp;lt;tex&amp;gt;L&amp;lt;/tex&amp;gt; состоит из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; вершин, правая &amp;lt;tex&amp;gt;R&amp;lt;/tex&amp;gt; из &amp;lt;tex&amp;gt;k = O(m log n)&amp;lt;/tex&amp;gt; вершин, степень всех вершин левой доли одинакова и равна некоторому &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt;, и для каждого &amp;lt;tex&amp;gt;A \subset L&amp;lt;/tex&amp;gt; размера не более &amp;lt;tex&amp;gt;2m&amp;lt;/tex&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;|\Gamma(A)| \geqslant \frac{7}{8}d|A|&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;X&amp;lt;/tex&amp;gt; будет состоять в разметке вершин правой доли нулями и единицами. Эту разметку нужно выбрать таким образом, чтобы у каждой вершины из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; не менее &amp;lt;tex&amp;gt;2/3&amp;lt;/tex&amp;gt; соседей были помечены единицей, а у каждой вершины не из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; не менее &amp;lt;tex&amp;gt;2/3&amp;lt;/tex&amp;gt; соседей были помечены нулями.&amp;lt;tex&amp;gt;X&amp;lt;/tex&amp;gt; будет состоять в разметке вершин правой доли нулями и единицами. Эту разметку нужно выбрать таким образом, чтобы у каждой вершины из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; не менее &amp;lt;tex&amp;gt;2/3&amp;lt;/tex&amp;gt; соседей были помечены единицей, а у каждой вершины не из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; не менее &amp;lt;tex&amp;gt;2/3&amp;lt;/tex&amp;gt; соседей были помечены нулями.&lt;br /&gt;
&lt;br /&gt;
Остаётся объяснить, как построить нужную нам разметку правой доли графа. Будем строить её последовательными приближениями. Сначала пометим всех соседей всех вершин из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; единицами, а все остальные вершины – нулями. На данной разметке наш алгоритм с вероятностью &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt; возвращает правильный ответ для всех &amp;lt;tex&amp;gt;a \in S&amp;lt;/tex&amp;gt;. Однако для &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; не из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; проверочный алгоритм может работать неверно. Обозначим T множество всех таких вершин вне &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, у которых более &amp;lt;tex&amp;gt;d/3&amp;lt;/tex&amp;gt; соседей помечены единицей. Поменяем разметку: пометим всех соседей &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; нулём. Теперь разметка может стать плохой для части вершин из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;. Обозначим &amp;lt;tex&amp;gt;S'&amp;lt;/tex&amp;gt; множество всех таких вершин из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, у которых более &amp;lt;tex&amp;gt;d/3&amp;lt;/tex&amp;gt; соседей помечены нулями. Далее, поменяем разметку у&lt;br /&gt;
всех соседей &amp;lt;tex&amp;gt;S'&amp;lt;/tex&amp;gt; на единицы. После этого может вновь возникнуть множество ‘неправильных’ вершин вне &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, и т.д.&lt;br /&gt;
&lt;br /&gt;
Чтобы доказать, что данный процесс в конце концов сойдётся, нужно показать, что на каждом шаге число ‘проблемных’ вершин уменьшается в константу раз. Поскольку все шаги аналогичны, достаточно разобрать самый первый: докажем, что &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; в константу раз меньше, чем &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;. Мы воспользуемся тем, что для &amp;lt;tex&amp;gt;S \cup T&amp;lt;/tex&amp;gt; выполнено свойство расширения:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;(7/8)d(|S|+|T|) \leqslant |\Gamma(S \cup T)| \leqslant d|S| + (2/3)d|T|&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Откуда получаем &amp;lt;tex&amp;gt;|T| \leqslant 3/5|S|&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Приложения и полезные свойства==&lt;br /&gt;
Первоначально интерес к экспандерам возник с целью построения устойчивой сети (телефонов или компьютеров) — число дуг графов расширения с ограниченным значением регулярности растет линейно по отношению к числу вершин.&lt;br /&gt;
&lt;br /&gt;
Экспандеры нашли широкое применение в теории вычислительных машин и систем, для построения алгоритмов, в корректирующих кодах, экстракторах, генераторах псевдослучайных чисел, сетях сортировки и компьютерных сетях. Они также используются для доказательства многих важных результатов в теории вычислительной сложности, таких как &amp;lt;tex&amp;gt;SL=L&amp;lt;/tex&amp;gt; и Теорема PCP. В криптографии экспандеры используются для создания хеш-функций.&lt;br /&gt;
&lt;br /&gt;
Ниже приведены некоторые свойства экспандеров, считающиеся полезными во многих областях.&lt;br /&gt;
===Лемма о перемешивании===&lt;br /&gt;
Лемма о перемешивании утверждает, что для любых двух подмножеств вершин &amp;lt;tex&amp;gt;S,T\subseteq V&amp;lt;/tex&amp;gt; число рёбер между &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; примерно равно числу рёбер в случайном &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt;-регулярном графе. Приближение тем лучше, чем меньше &amp;lt;tex&amp;gt;\lambda =\max\{|\lambda _{2}|&amp;lt;/tex&amp;gt;,&amp;lt;tex&amp;gt;|\lambda _{n}|\}&amp;lt;/tex&amp;gt;. В случайном &amp;lt;tex&amp;gt;d&amp;lt;tex/&amp;gt;-регулярном графе, также как и в случайном графе Эрдёша — Реньи с вероятностью &amp;lt;tex&amp;gt;{\tfrac {d}{n}}&amp;lt;/tex&amp;gt; выбора ребра, ожидается &amp;lt;tex&amp;gt;{\tfrac {d}{n}}\cdot |S|\cdot |T|&amp;lt;/tex&amp;gt; рёбер между S и T.&lt;br /&gt;
&lt;br /&gt;
Более формально, пусть &amp;lt;tex&amp;gt;E(S, T)&amp;lt;/tex&amp;gt; обозначает число рёбер между &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;. Если эти два множества пересекаются, дуги в пересечении считаются дважды, так что&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;E(S,T)=2|E(G[S\cap T])|+E(S\setminus T,T)+E(S,T\setminus S)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
Лемма о перемешивании утверждает, что&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\left|E(S,T)-{\frac {d\cdot |S|\cdot |T|}{n}}\right|\leq d\lambda {\sqrt {|S|\cdot |T|}}&amp;lt;/tex&amp;gt;,&lt;br /&gt;
где &amp;lt;tex&amp;gt;\lambda &amp;lt;/tex&amp;gt; — абсолютное значение нормализованного второго по величине собственного значения.&lt;br /&gt;
&lt;br /&gt;
Недавно Билу (Bilu) и Линайл (Linial) показали, что обратное тоже верно, то есть, при условии выполнения неравенства из леммы второе по величине собственное значение равно &amp;lt;tex&amp;gt;O(d\lambda \cdot (1+\log(1/\lambda )))&amp;lt;/tex&amp;gt;.&lt;br /&gt;
===Блуждания по экспандеру===&lt;br /&gt;
Согласно границе Чернова, если выбирать много независимых случайных значений из интервала &amp;lt;tex&amp;gt;[−1, 1]&amp;lt;/tex&amp;gt;, с большой степенью вероятности среднее выбранных значений будет близко к математическому ожиданию случайной переменной. Лемма о блуждании по экспандеру, согласно статьям Аджтари, Комлоша и Семереди и Гилмана, утверждает, что то же самое верно и для блужданий по экспандеру. Это полезно в теории дерандомизации, поскольку блуждание по экспандеру использует много меньше случайных бит, чем случайная независимая выборка.&lt;br /&gt;
&lt;br /&gt;
==Источники информации==&lt;br /&gt;
*[https://ru.wikipedia.org/wiki/%D0%AD%D0%BA%D1%81%D0%BF%D0%B0%D0%BD%D0%B4%D0%B5%D1%80_(%D1%82%D0%B5%D0%BE%D1%80%D0%B8%D1%8F_%D0%B3%D1%80%D0%B0%D1%84%D0%BE%D0%B2) Экспандер (теория графов)]&lt;br /&gt;
*&lt;br /&gt;
[S. Hoory, N. Linial, A. Wigderson. Expander graphs and their&lt;br /&gt;
applications. Bulletin of the AMS, vol. 43, Number 4, Oct. 2006, pp.439–&lt;br /&gt;
561.]&lt;br /&gt;
*&lt;br /&gt;
[ H. Buhrman, P.B. Miltersen, J. Radhakrishnan, S. Venkatesh. Are&lt;br /&gt;
Bitvectors optimal? SIAM J. Comput., 31(6):1723–1744, 2002.]&lt;/div&gt;</summary>
		<author><name>Kozichuk</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%93%D1%80%D0%B0%D1%84%D1%8B-%D1%8D%D0%BA%D1%81%D0%BF%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%8B&amp;diff=50933</id>
		<title>Графы-экспандеры</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%93%D1%80%D0%B0%D1%84%D1%8B-%D1%8D%D0%BA%D1%81%D0%BF%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%8B&amp;diff=50933"/>
				<updated>2016-01-09T02:08:08Z</updated>
		
		<summary type="html">&lt;p&gt;Kozichuk: /* Определение */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Граф-экспандер''' (или расширяющийся граф, англ. ''expander graph'') - в комбинаторике сильно разреженный граф, при этом связность определяется по вершинам, дугам или спектру.&lt;br /&gt;
&lt;br /&gt;
==Определение==&lt;br /&gt;
''Граф-экспандер'' — это конечный ненаправленный ''мультиграф'', в котором любое подмножество вершин, не являясь «слишком большим», имеет «сильную» связность. Различные формализации этих понятий дают различные типы экспандеров: ''рёберный расширитель'', ''вершинный расширитель'', и ''спектральный расширитель''.&lt;br /&gt;
&lt;br /&gt;
Несвязный граф не является экспандером. Любой связный граф является экспандером, однако различные связные графы имеют различные параметры расширителя. Полный граф имеет лучшие параметры расширителя, но имеет наибольшую возможную степень. Неформально говоря, граф является хорошим экспандером, если он имеет низкую степень и высокий параметр расширителя.&lt;br /&gt;
&lt;br /&gt;
===Реберное расширение===&lt;br /&gt;
''Рёберное расширение'' (также ''изопериметрическое число'' или константа Чигера) &amp;lt;tex&amp;gt;h(G)&amp;lt;/tex&amp;gt; графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; для &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; вершин определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;h(G) = \min\limits_{0 &amp;lt; |S|\leqslant \frac{n}{2}} \frac{|\partial_{\text{out}}(S)|}{|S|}&amp;lt;/tex&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где минимум берётся по всем непустым множествам &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; не более чем &amp;lt;tex&amp;gt;n/2&amp;lt;/tex&amp;gt; вершин и &amp;lt;tex&amp;gt;\partial(S)&amp;lt;/tex&amp;gt; — ''граничные дуги'' множества &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, то есть, множество дуг с единственной вершиной в &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Вершинное расширение===&lt;br /&gt;
''Вершинное изопериметрическое число'' &amp;lt;tex&amp;gt;h_{out}(G)&amp;lt;/tex&amp;gt; (также ''вершинное раширение'') графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;h_{out}(G) = \min\limits_{0 &amp;lt; |S|\leqslant \frac{n}{2}} \frac{|\partial_{\text{out}}(S)|}{|S|}&amp;lt;/tex&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;tex&amp;gt;\partial_{\text{out}}(S)&amp;lt;/tex&amp;gt; — ''внешняя граница'' &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, то есть множество вершин из &amp;lt;tex&amp;gt;V(G)\setminus S&amp;lt;/tex&amp;gt;, имеющих как минимум одного соседа в &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;. В варианте этого определения (называемом ''уникальным соседним расширением'') &amp;lt;tex&amp;gt;\partial_{\text{out}}(S)&amp;lt;/tex&amp;gt; заменяется на множество вершин из &amp;lt;tex&amp;gt;V&amp;lt;/tex&amp;gt; с ''точностью одним'' соседом из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
''Вершинное изопериметрическое число'' &amp;lt;tex&amp;gt;h_{in}(G)&amp;lt;/tex&amp;gt; графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;h_{in}(G) = \min\limits_{0 &amp;lt; |S|\leqslant \frac{n}{2}} \frac{|\partial_{\text{in}}(S)|}{|S|}&amp;lt;/tex&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;tex&amp;gt;\partial_{in}(S)&amp;lt;/tex&amp;gt; — ''внутренняя граница'' &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, то есть множество вершин &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, имеющих как минимум одного соседа в &amp;lt;tex&amp;gt;V(G)\setminus S&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Спектральное расширение===&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; является d-регулярным, возможно определение в терминах линейной алгебры на основе собственных значений матрицы смежности &amp;lt;tex&amp;gt;A = A(G)&amp;lt;/tex&amp;gt; графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;A_{ij}&amp;lt;/tex&amp;gt; — число дуг между вершинами &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;. Поскольку &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; является симметричной, согласно спектральной теореме, &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; имеет &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; действительных собственных значений &amp;lt;tex&amp;gt;\lambda_1 \ge \lambda_2 \ge \cdots \ge \lambda_{n}&amp;lt;/tex&amp;gt;. Известно, что эти значения лежат в промежутке &amp;lt;tex&amp;gt;[−d, d]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
Граф регулярен тогда и только тогда, когда вектор &amp;lt;tex&amp;gt;u\in \mathbb {R} ^{n} с u_{i}=1&amp;lt;/tex&amp;gt; для всех &amp;lt;tex&amp;gt;i = 1, …, n&amp;lt;/tex&amp;gt; является собственным вектором матрицы &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt;, а его собственное число будет постоянной степенью графа. Таким образом, мы имеем &amp;lt;tex&amp;gt;Au = du&amp;lt;/tex&amp;gt;, и &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt; — собственный вектор матрицы &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; с собственными значениями &amp;lt;tex&amp;gt;λ1 = d&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt; — степень вершин графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt;. Спектральный зазор графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; определяется как &amp;lt;tex&amp;gt;d−λ2&amp;lt;/tex&amp;gt; и является мерилом спектрального расширения графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Известно, что &amp;lt;tex&amp;gt;\lambda_n = −d&amp;lt;/tex&amp;gt; тогда и только тогда, когда &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; — двудольный. Во многих случаях, например в лемме о перемешивании, необходимо ограничить снизу не только зазор между &amp;lt;tex&amp;gt;\lambda_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\lambda_2&amp;lt;/tex&amp;gt;, но и зазор между &amp;lt;tex&amp;gt;\lambda_1&amp;lt;/tex&amp;gt; и вторым максимальным по модулю собственным значением:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\lambda=\max\{|\lambda_2|, |\lambda_{n}|\}&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Поскольку это собственное значение соответствует некоторому собственному вектору, ортогональному &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;, его можно определить, используя отношение Рэлея:&lt;br /&gt;
&amp;lt;tex&amp;gt;\lambda=\max_{0\neq v\perp u} \frac{\|Av\|_2}{\|v\|_2},&amp;lt;/tex&amp;gt;&lt;br /&gt;
gde&lt;br /&gt;
&amp;lt;tex&amp;gt;\|v\|_2=\left(\sum_{i=1}^n v_i^2\right)^{1/2}&amp;lt;/tex&amp;gt;&lt;br /&gt;
— евклидова норма вектора &amp;lt;tex&amp;gt;v\in \mathbb {R} ^{n}&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Нормализованная версия этого определения также широко используется и более удобна для получения определённых результатов. В таком случае рассматривается матрица &amp;lt;tex&amp;gt;{\tfrac {1}{d}}A&amp;lt;/tex&amp;gt;, являющаяся матрицей переходов графа G. Все её собственные значения лежат между &amp;lt;tex&amp;gt;−1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;. Если граф не регулярен, спектр графа может быть определён аналогичным образом, используя собственные значения матрицы Кирхгофа. Для направленного графа используются сингулярные значения матрицы сопряжения A, которые равны квадратным корням из собственных значений симметричной матрицы &amp;lt;tex&amp;gt;A^TA&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Конструирование==&lt;br /&gt;
Существуют три основные стратегии создания семейств графов расширений[6]. Первая стратегия — алгебраическая и теоретико-групповая, вторая — аналитическая, использующая аддитивную комбинаторику, и третья — комбинаторная, использующая зигзаг-произведение и связанные комбинаторные произведения.&lt;br /&gt;
===Маргулис-Габбер-Галил===&lt;br /&gt;
Алгебраическое конструирование, основанное на графах Кэли, известно для различных вариантов экспандеров. Следующее конструирование принадлежит Маргулису и было проанализировано Габбером (Gabber) и Галилом (Galil). Для любого натурального &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; строим граф, &amp;lt;tex&amp;gt;G_{n}&amp;lt;/tex&amp;gt; со множеством вершин &amp;lt;tex&amp;gt;\mathbb Z _{n}\times \mathbb {Z} _{n}&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;\mathbb {Z} _{n}=\mathbb Z /n \mathbb Z&amp;lt;/tex&amp;gt; . Для любой вершины &amp;lt;tex&amp;gt;(x,y)\in \mathbb {Z} _{n}\times \mathbb {Z} _{n}&amp;lt;/tex&amp;gt;, её восемь соседей будут&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;(x \pm 2y,y), (x \pm (2y+1),y), (x,y \pm 2x), (x,y \pm (2x+1)).&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Выполняется следующая теорема:&lt;br /&gt;
{{Теорема&lt;br /&gt;
|statement=&lt;br /&gt;
Для всех &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; граф &amp;lt;tex&amp;gt;Gn&amp;lt;/tex&amp;gt; второе по величине собственное число&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;tex&amp;gt;\lambda(G)\leq 5 \sqrt{2}&amp;lt;/tex&amp;gt;.}}&lt;br /&gt;
&lt;br /&gt;
===Граф Рамануджана===&lt;br /&gt;
&amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt;-регулярный граф называется графом Рамануджана, если его второе по модулю собственное число не превосходит &amp;lt;tex&amp;gt;2{\sqrt {d-1}}&amp;lt;/tex&amp;gt;. Любоцкий, Сарнак, Филлипс и Маргулис указали явную конструкцию графов Кэли, являющихся графами Рамануджана. Опишем эту конструкцию.&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt;p&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; простые числа, &amp;lt;tex&amp;gt;p = 1&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;mod&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;4&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;q = 1&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;mod&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;4&amp;lt;/tex&amp;gt;. В качестве группы &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; возьмём &amp;lt;tex&amp;gt;PGL(2, \mathbb Z/q{\mathbb Z})&amp;lt;/tex&amp;gt;, т.е. невырожденные матрицы 2 × 2 над полем вычетов по модулю &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;, профакторизованные по отношению пропорциональности (с обычной операцией матричного умножения).&lt;br /&gt;
Далее мы зададим в этой группе симметричное множество &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;. Выберем такое целое &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;i^2 = −1&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;mod&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;. Можно доказать, что тогда имеется ровно &amp;lt;tex&amp;gt;(p + 1)&amp;lt;/tex&amp;gt; целочисленное решение уравнения&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;a_0^2 + a_1^2 + a_3^2 = p&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
такое, что &amp;lt;tex&amp;gt;a_0&amp;lt;/tex&amp;gt; положительно и нечётно, а &amp;lt;tex&amp;gt;a_1&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;a_2&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;a_3&amp;lt;/tex&amp;gt; чётны. Каждой такой четвёрке сопоставим матрицу&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;A = \begin{pmatrix} a_0 + ia_1 &amp;amp; a_2 + ia_3\\ -a_2 + ia_3 &amp;amp; a_0 - ia_1 \end{pmatrix}&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Эти матрицы образуют множество S.&lt;br /&gt;
Нетрудно понять, что граф Кэли &amp;lt;tex&amp;gt;(G, S)&amp;lt;/tex&amp;gt; состоит из &amp;lt;tex&amp;gt;\Theta(q^3)&amp;lt;/tex&amp;gt; вершин, и степень каждой вершины равна &amp;lt;tex&amp;gt;(p + 1)&amp;lt;/tex&amp;gt;. Свойства данного графа зависят от соотношения &amp;lt;tex&amp;gt;p&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;. Рассмотрим случай, когда p является квадратичным вычетом по модулю &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;. Тогда полученный граф Кэли состоит из двух связных компонент (поскольку все матрицы из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; лежат в подгруппе &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; индекса два — подгруппе матриц, определитель которых является квадратичным вычетом). Обозначим &amp;lt;tex&amp;gt;X^{p,q}&amp;lt;/tex&amp;gt; связную компоненту полученного графа. Можно доказать, что у &amp;lt;tex&amp;gt;X^{p,q}&amp;lt;/tex&amp;gt; второе по абсолютной величине собственное число не превосходит &amp;lt;tex&amp;gt;2{\sqrt{p}}&amp;lt;/tex&amp;gt;, т.е. мы получили граф Рамануджана.&lt;br /&gt;
&lt;br /&gt;
==Примеры применения экспандеров==&lt;br /&gt;
===Коды, исправляющие ошибки===&lt;br /&gt;
С помощью расширяющего графа можно посторить линейный код, позволяющий исправлять ошибки в доле &amp;lt;tex&amp;gt;\delta = 1/(2000d)&amp;lt;/tex&amp;gt; битов. Чтобы задать линейный код с длиной кодового слова n, достаточно описать его проверочную матрицу &amp;lt;tex&amp;gt;H&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;(&amp;lt;/tex&amp;gt;слово &amp;lt;tex&amp;gt;x \in \{0, 1\}^n&amp;lt;/tex&amp;gt; является кодовым словом если и только если &amp;lt;tex&amp;gt;Hx^t = 0)&amp;lt;/tex&amp;gt;. Другими словами, нужно задать систему линейных уравнений для переменных &amp;lt;tex&amp;gt;x_1, . . . , x_n;&amp;lt;/tex&amp;gt; решения этой системы и будут кодовыми словами.&lt;br /&gt;
===Увеличение вероятности успеха в алгоритмах с датчиком случайных чисел===&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=Язык &amp;lt;tex&amp;gt;L&amp;lt;/tex&amp;gt; принадлежит сложностному классу &amp;lt;tex&amp;gt;RP&amp;lt;/tex&amp;gt;, если существует полиномиальный алгоритм &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; такой что&lt;br /&gt;
1. для &amp;lt;tex&amp;gt;x \in L&amp;lt;/tex&amp;gt; для всех &amp;lt;tex&amp;gt;r \in \{0, 1\}^{poly(n)}&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;A(x, r) = 1&amp;lt;/tex&amp;gt;&lt;br /&gt;
2. для &amp;lt;tex&amp;gt;x \notin L&amp;lt;/tex&amp;gt; не более чем для 1/2000 всех &amp;lt;tex&amp;gt;r \in \{0, 1\}^{poly(n)}&amp;lt;/tex&amp;gt; может выполняться &amp;lt;tex&amp;gt;A(x, r) = 1&amp;lt;/tex&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Покажем, что для любого &amp;lt;tex&amp;gt;\epsilon &amp;gt; 0&amp;lt;/tex&amp;gt; полиномиальный вероятностный алгоритм A можно модифицировать таким образом, чтобы вероятность ошибки уменьшилась до &amp;lt;tex&amp;gt;\epsilon&amp;lt;/tex&amp;gt;, а число используемых случайных битов не изменится.&lt;br /&gt;
&lt;br /&gt;
Пусть исходный алгоритм использует &amp;lt;tex&amp;gt;k = k(n)&amp;lt;/tex&amp;gt; случайных битов для вычислений на входах длины &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;. Зафиксируем &amp;lt;tex&amp;gt;(2^k, 2^k, d)&amp;lt;/tex&amp;gt; - экспандер &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;d &amp;gt; 1/\epsilon&amp;lt;/tex&amp;gt;. Новый алгоритм действует следующим образом: выбирается случайная вершина &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; из левой доли графа (для этого требуется &amp;lt;tex&amp;gt;k&amp;lt;/tex&amp;gt; случайных битов); затем исходный алгоритм &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; последовательно запускается на всех &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt; наборах случайных битов, соответствующих соседям вершины &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;. Если все полученные ответы равны &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;, новый алгоритм также возвращает единицу; в противном случае возвращается ноль.&lt;br /&gt;
Покажем, что у нового алгоритма вероятность ошибки не превосходит &amp;lt;tex&amp;gt;1/d&amp;lt;/tex&amp;gt;. В самом деле, обозначим &amp;lt;tex&amp;gt;B = B(x)&amp;lt;/tex&amp;gt; множество таких вершин &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt; из правой доли графа, которые соответствуют неверному ответу старого алгоритма на входе &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;; аналогично, обозначим &amp;lt;tex&amp;gt;S = S(x)&amp;lt;/tex&amp;gt; множество таких вершин v из левой доли графа, которые для которых новый алгоритм даёт неверный ответ на входе &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;. Очевидно, &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; состоит из вершин, все соседи которых лежат в B. Предположим, что &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; содержит не менее &amp;lt;tex&amp;gt;n/(1000d)&amp;lt;/tex&amp;gt; вершин. Выберем среди них ровно &amp;lt;tex&amp;gt;n/(1000d)&amp;lt;/tex&amp;gt; вершин и назовём это множество &amp;lt;tex&amp;gt;S'&amp;lt;/tex&amp;gt;. По свойству экспандера, имеем&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;|\Gamma(S')| \geqslant  \frac{7}{8}d\frac{n}{1000d}=7n/8000 &amp;gt; n/2000&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Это противоречит тому, что все соседи &amp;lt;tex&amp;gt;S'&amp;lt;/tex&amp;gt; лежат в &amp;lt;tex&amp;gt;B&amp;lt;/tex&amp;gt;. В данном случае нам нужна явная в более сильном (чем в первом примере) смысле конструкция экспандера. Размер графа экспоненциално растёт с увеличенем &amp;lt;tex&amp;gt;k&amp;lt;/tex&amp;gt;, и нам необходим алгоритм, который по заданному номеру вершины &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; (из левой доли графа) за время &amp;lt;tex&amp;gt;poly(k)&amp;lt;/tex&amp;gt; находит список номеров всех соседей этой вершины (в правой доле графа).&lt;br /&gt;
&lt;br /&gt;
===Хранение множества со сверхбыстрым запросом элементов===&lt;br /&gt;
Мы организуем хранение m-элементного множества &amp;lt;tex&amp;gt;S \subset \{1, . . . , n\}&amp;lt;/tex&amp;gt; в виде описания &amp;lt;tex&amp;gt;X&amp;lt;/tex&amp;gt;, состоящего из &amp;lt;tex&amp;gt;O(m log n)&amp;lt;/tex&amp;gt; битов. При этом проверка принадлежности &amp;lt;tex&amp;gt;a \in S&amp;lt;/tex&amp;gt; будет производиться чрезвычайно быстро. А именно, мы построим такой вероятностный алгоритм, который по любому входу &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; запрашивает из &amp;lt;tex&amp;gt;X&amp;lt;/tex&amp;gt; один бит; если этот бит оказывается равным единице, то алгоритм отвечает, что &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; является элементом &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;; в противном случае алгоритм говорит, что &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; множеству не принадлежит. При этом для каждого &amp;lt;tex&amp;gt;a \in \{1, . . . , n\}&amp;lt;/tex&amp;gt; алгоритм ошибается с вероятностью не более &amp;lt;tex&amp;gt;1/3&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Чтобы построить нужное нам хранилище &amp;lt;tex&amp;gt;X&amp;lt;/tex&amp;gt;, мы сначала зафиксируем некоторый экспандер, у которого левая доля &amp;lt;tex&amp;gt;L&amp;lt;/tex&amp;gt; состоит из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; вершин, правая &amp;lt;tex&amp;gt;R&amp;lt;/tex&amp;gt; из &amp;lt;tex&amp;gt;k = O(m log n)&amp;lt;/tex&amp;gt; вершин, степень всех вершин левой доли одинакова и равна некоторому &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt;, и для каждого &amp;lt;tex&amp;gt;A \subset L&amp;lt;/tex&amp;gt; размера не более &amp;lt;tex&amp;gt;2m&amp;lt;/tex&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;|\Gamma(A)| \geqslant \frac{7}{8}d|A|&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;X&amp;lt;/tex&amp;gt; будет состоять в разметке вершин правой доли нулями и единицами. Эту разметку нужно выбрать таким образом, чтобы у каждой вершины из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; не менее &amp;lt;tex&amp;gt;2/3&amp;lt;/tex&amp;gt; соседей были помечены единицей, а у каждой вершины не из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; не менее &amp;lt;tex&amp;gt;2/3&amp;lt;/tex&amp;gt; соседей были помечены нулями.&amp;lt;tex&amp;gt;X&amp;lt;/tex&amp;gt; будет состоять в разметке вершин правой доли нулями и единицами. Эту разметку нужно выбрать таким образом, чтобы у каждой вершины из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; не менее &amp;lt;tex&amp;gt;2/3&amp;lt;/tex&amp;gt; соседей были помечены единицей, а у каждой вершины не из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; не менее &amp;lt;tex&amp;gt;2/3&amp;lt;/tex&amp;gt; соседей были помечены нулями.&lt;br /&gt;
&lt;br /&gt;
Остаётся объяснить, как построить нужную нам разметку правой доли графа. Будем строить её последовательными приближениями. Сначала пометим всех соседей всех вершин из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; единицами, а все остальные вершины – нулями. На данной разметке наш алгоритм с вероятностью &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt; возвращает правильный ответ для всех &amp;lt;tex&amp;gt;a \in S&amp;lt;/tex&amp;gt;. Однако для &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; не из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; проверочный алгоритм может работать неверно. Обозначим T множество всех таких вершин вне &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, у которых более &amp;lt;tex&amp;gt;d/3&amp;lt;/tex&amp;gt; соседей помечены единицей. Поменяем разметку: пометим всех соседей &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; нулём. Теперь разметка может стать плохой для части вершин из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;. Обозначим &amp;lt;tex&amp;gt;S'&amp;lt;/tex&amp;gt; множество всех таких вершин из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, у которых более &amp;lt;tex&amp;gt;d/3&amp;lt;/tex&amp;gt; соседей помечены нулями. Далее, поменяем разметку у&lt;br /&gt;
всех соседей &amp;lt;tex&amp;gt;S'&amp;lt;/tex&amp;gt; на единицы. После этого может вновь возникнуть множество ‘неправильных’ вершин вне &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, и т.д.&lt;br /&gt;
&lt;br /&gt;
Чтобы доказать, что данный процесс в конце концов сойдётся, нужно показать, что на каждом шаге число ‘проблемных’ вершин уменьшается в константу раз. Поскольку все шаги аналогичны, достаточно разобрать самый первый: докажем, что &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; в константу раз меньше, чем &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;. Мы воспользуемся тем, что для &amp;lt;tex&amp;gt;S \cup T&amp;lt;/tex&amp;gt; выполнено свойство расширения:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;(7/8)d(|S|+|T|) \leqslant |\Gamma(S \cup T)| \leqslant d|S| + (2/3)d|T|&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Откуда получаем &amp;lt;tex&amp;gt;|T| \leqslant 3/5|S|&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Приложения и полезные свойства==&lt;br /&gt;
Первоначально интерес к экспандерам возник с целью построения устойчивой сети (телефонов или компьютеров) — число дуг графов расширения с ограниченным значением регулярности растет линейно по отношению к числу вершин.&lt;br /&gt;
&lt;br /&gt;
Экспандеры нашли широкое применение в теории вычислительных машин и систем, для построения алгоритмов, в корректирующих кодах, экстракторах, генераторах псевдослучайных чисел, сетях сортировки и компьютерных сетях. Они также используются для доказательства многих важных результатов в теории вычислительной сложности, таких как &amp;lt;tex&amp;gt;SL=L&amp;lt;/tex&amp;gt; и Теорема PCP. В криптографии экспандеры используются для создания хеш-функций.&lt;br /&gt;
&lt;br /&gt;
Ниже приведены некоторые свойства экспандеров, считающиеся полезными во многих областях.&lt;br /&gt;
===Лемма о перемешивании===&lt;br /&gt;
Лемма о перемешивании утверждает, что для любых двух подмножеств вершин &amp;lt;tex&amp;gt;S,T\subseteq V&amp;lt;/tex&amp;gt; число рёбер между &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; примерно равно числу рёбер в случайном &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt;-регулярном графе. Приближение тем лучше, чем меньше &amp;lt;tex&amp;gt;\lambda =\max\{|\lambda _{2}|&amp;lt;/tex&amp;gt;,&amp;lt;tex&amp;gt;|\lambda _{n}|\}&amp;lt;/tex&amp;gt;. В случайном &amp;lt;tex&amp;gt;d&amp;lt;tex/&amp;gt;-регулярном графе, также как и в случайном графе Эрдёша — Реньи с вероятностью &amp;lt;tex&amp;gt;{\tfrac {d}{n}}&amp;lt;/tex&amp;gt; выбора ребра, ожидается &amp;lt;tex&amp;gt;{\tfrac {d}{n}}\cdot |S|\cdot |T|&amp;lt;/tex&amp;gt; рёбер между S и T.&lt;br /&gt;
&lt;br /&gt;
Более формально, пусть &amp;lt;tex&amp;gt;E(S, T)&amp;lt;/tex&amp;gt; обозначает число рёбер между &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;. Если эти два множества пересекаются, дуги в пересечении считаются дважды, так что&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;E(S,T)=2|E(G[S\cap T])|+E(S\setminus T,T)+E(S,T\setminus S)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
Лемма о перемешивании утверждает, что&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\left|E(S,T)-{\frac {d\cdot |S|\cdot |T|}{n}}\right|\leq d\lambda {\sqrt {|S|\cdot |T|}}&amp;lt;/tex&amp;gt;,&lt;br /&gt;
где &amp;lt;tex&amp;gt;\lambda &amp;lt;/tex&amp;gt; — абсолютное значение нормализованного второго по величине собственного значения.&lt;br /&gt;
&lt;br /&gt;
Недавно Билу (Bilu) и Линайл (Linial) показали, что обратное тоже верно, то есть, при условии выполнения неравенства из леммы второе по величине собственное значение равно &amp;lt;tex&amp;gt;O(d\lambda \cdot (1+\log(1/\lambda )))&amp;lt;/tex&amp;gt;.&lt;br /&gt;
===Блуждания по экспандеру===&lt;br /&gt;
Согласно границе Чернова, если выбирать много независимых случайных значений из интервала &amp;lt;tex&amp;gt;[−1, 1]&amp;lt;/tex&amp;gt;, с большой степенью вероятности среднее выбранных значений будет близко к математическому ожиданию случайной переменной. Лемма о блуждании по экспандеру, согласно статьям Аджтари, Комлоша и Семереди и Гилмана, утверждает, что то же самое верно и для блужданий по экспандеру. Это полезно в теории дерандомизации, поскольку блуждание по экспандеру использует много меньше случайных бит, чем случайная независимая выборка.&lt;br /&gt;
&lt;br /&gt;
==Источники информации==&lt;br /&gt;
*[https://ru.wikipedia.org/wiki/%D0%AD%D0%BA%D1%81%D0%BF%D0%B0%D0%BD%D0%B4%D0%B5%D1%80_(%D1%82%D0%B5%D0%BE%D1%80%D0%B8%D1%8F_%D0%B3%D1%80%D0%B0%D1%84%D0%BE%D0%B2) Экспандер (теория графов)]&lt;/div&gt;</summary>
		<author><name>Kozichuk</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%93%D1%80%D0%B0%D1%84%D1%8B-%D1%8D%D0%BA%D1%81%D0%BF%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%8B&amp;diff=50932</id>
		<title>Графы-экспандеры</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%93%D1%80%D0%B0%D1%84%D1%8B-%D1%8D%D0%BA%D1%81%D0%BF%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%8B&amp;diff=50932"/>
				<updated>2016-01-09T01:55:40Z</updated>
		
		<summary type="html">&lt;p&gt;Kozichuk: /* Применение экспандеров */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Граф-экспандер''' (или расширяющийся граф, англ. ''expander graph'') - в комбинаторике сильно разреженный граф, при этом связность определяется по вершинам, дугам или спектру.&lt;br /&gt;
&lt;br /&gt;
==Определение==&lt;br /&gt;
''Граф-экспандер'' — это конечный ненаправленный ''мультиграф'', в котором любое подмножество вершин, не являясь «слишком большим», имеет «сильную» связность. Различные формализации этих понятий дают различные типы экспандеров: ''рёберный расширитель'', ''вершинный расширитель'', и ''спектральный расширитель''.&lt;br /&gt;
&lt;br /&gt;
Несвязный граф не является экспандером. Любой связный граф является экспандером, однако различные связные графы имеют различные параметры расширителя. Полный граф имеет лучшие параметры расширителя, но имеет наибольшую возможную степень. Неформально говоря, граф является хорошим экспандером, если он имеет низкую степень и высокий параметр расширителя.&lt;br /&gt;
&lt;br /&gt;
===Реберное расширение===&lt;br /&gt;
''Рёберное расширение'' (также ''изопериметрическое число'' или константа Чигера) &amp;lt;tex&amp;gt;h(G)&amp;lt;/tex&amp;gt; графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; для &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; вершин определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;h(G) = \min_{0 &amp;lt; |S|\le \frac{n}{2}} \frac{|\partial_{\text{out}}(S)|}{|S|}&amp;lt;/tex&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где минимум берётся по всем непустым множествам &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; не более чем &amp;lt;tex&amp;gt;n/2&amp;lt;/tex&amp;gt; вершин и &amp;lt;tex&amp;gt;\partial(S)&amp;lt;/tex&amp;gt; — ''граничные дуги'' множества &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, то есть, множество дуг с единственной вершиной в &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Вершинное расширение===&lt;br /&gt;
''Вершинное изопериметрическое число'' &amp;lt;tex&amp;gt;h_{out}(G)&amp;lt;/tex&amp;gt; (также ''вершинное раширение'') графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;h_{out}(G) = \min_{0 &amp;lt; |S|\le \frac{n}{2}} \frac{|\partial_{\text{out}}(S)|}{|S|},&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;tex&amp;gt;\partial_{\text{out}}(S)&amp;lt;/tex&amp;gt; — ''внешняя граница'' &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, то есть множество вершин из &amp;lt;tex&amp;gt;V(G)\setminus S&amp;lt;/tex&amp;gt;, имеющих как минимум одного соседа в S. В варианте этого определения (называемом ''уникальным соседним расширением'') &amp;lt;tex&amp;gt;\partial_{\text{out}}(S)&amp;lt;/tex&amp;gt; заменяется на множество вершин из &amp;lt;tex&amp;gt;V&amp;lt;/tex&amp;gt; с ''точностью одним'' соседом из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
''Вершинное изопериметрическое число'' &amp;lt;tex&amp;gt;h_{in}(G)&amp;lt;/tex&amp;gt; графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;h_{in}(G) = \min_{0 &amp;lt; |S|\le \frac{n}{2}} \frac{|\partial_{\text{in}}(S)|}{|S|},&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;tex&amp;gt;∂_{in}(S)&amp;lt;/tex&amp;gt; — ''внутренняя граница'' &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, то есть множество вершин &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, имеющих как минимум одного соседа в &amp;lt;tex&amp;gt;V(G)\setminus S&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Спектральное расширение===&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; является d-регулярным, возможно определение в терминах линейной алгебры на основе собственных значений матрицы смежности &amp;lt;tex&amp;gt;A = A(G)&amp;lt;/tex&amp;gt; графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;A_{ij}&amp;lt;/tex&amp;gt; — число дуг между вершинами &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;. Поскольку &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; является симметричной, согласно спектральной теореме, &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; имеет &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; действительных собственных значений &amp;lt;tex&amp;gt;\lambda_1 \ge \lambda_2 \ge \cdots \ge \lambda_{n}&amp;lt;/tex&amp;gt;. Известно, что эти значения лежат в промежутке &amp;lt;tex&amp;gt;[−d, d]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
Граф регулярен тогда и только тогда, когда вектор &amp;lt;tex&amp;gt;u\in \mathbb {R} ^{n} с u_{i}=1&amp;lt;/tex&amp;gt; для всех &amp;lt;tex&amp;gt;i = 1, …, n&amp;lt;/tex&amp;gt; является собственным вектором матрицы &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt;, а его собственное число будет постоянной степенью графа. Таким образом, мы имеем &amp;lt;tex&amp;gt;Au = du&amp;lt;/tex&amp;gt;, и &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt; — собственный вектор матрицы &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; с собственными значениями &amp;lt;tex&amp;gt;λ1 = d&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt; — степень вершин графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt;. Спектральный зазор графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; определяется как &amp;lt;tex&amp;gt;d−λ2&amp;lt;/tex&amp;gt; и является мерилом спектрального расширения графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Известно, что &amp;lt;tex&amp;gt;\lambda_n = −d&amp;lt;/tex&amp;gt; тогда и только тогда, когда &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; — двудольный. Во многих случаях, например в лемме о перемешивании, необходимо ограничить снизу не только зазор между &amp;lt;tex&amp;gt;\lambda_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\lambda_2&amp;lt;/tex&amp;gt;, но и зазор между &amp;lt;tex&amp;gt;\lambda_1&amp;lt;/tex&amp;gt; и вторым максимальным по модулю собственным значением:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\lambda=\max\{|\lambda_2|, |\lambda_{n}|\}&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Поскольку это собственное значение соответствует некоторому собственному вектору, ортогональному &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;, его можно определить, используя отношение Рэлея:&lt;br /&gt;
&amp;lt;tex&amp;gt;\lambda=\max_{0\neq v\perp u} \frac{\|Av\|_2}{\|v\|_2},&amp;lt;/tex&amp;gt;&lt;br /&gt;
gde&lt;br /&gt;
&amp;lt;tex&amp;gt;\|v\|_2=\left(\sum_{i=1}^n v_i^2\right)^{1/2}&amp;lt;/tex&amp;gt;&lt;br /&gt;
— евклидова норма вектора &amp;lt;tex&amp;gt;v\in \mathbb {R} ^{n}&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Нормализованная версия этого определения также широко используется и более удобна для получения определённых результатов. В таком случае рассматривается матрица &amp;lt;tex&amp;gt;{\tfrac {1}{d}}A&amp;lt;/tex&amp;gt;, являющаяся матрицей переходов графа G. Все её собственные значения лежат между &amp;lt;tex&amp;gt;−1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;. Если граф не регулярен, спектр графа может быть определён аналогичным образом, используя собственные значения матрицы Кирхгофа. Для направленного графа используются сингулярные значения матрицы сопряжения A, которые равны квадратным корням из собственных значений симметричной матрицы &amp;lt;tex&amp;gt;A^TA&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Конструирование==&lt;br /&gt;
Существуют три основные стратегии создания семейств графов расширений[6]. Первая стратегия — алгебраическая и теоретико-групповая, вторая — аналитическая, использующая аддитивную комбинаторику, и третья — комбинаторная, использующая зигзаг-произведение и связанные комбинаторные произведения.&lt;br /&gt;
===Маргулис-Габбер-Галил===&lt;br /&gt;
Алгебраическое конструирование, основанное на графах Кэли, известно для различных вариантов экспандеров. Следующее конструирование принадлежит Маргулису и было проанализировано Габбером (Gabber) и Галилом (Galil). Для любого натурального &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; строим граф, &amp;lt;tex&amp;gt;G_{n}&amp;lt;/tex&amp;gt; со множеством вершин &amp;lt;tex&amp;gt;\mathbb Z _{n}\times \mathbb {Z} _{n}&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;\mathbb {Z} _{n}=\mathbb Z /n \mathbb Z&amp;lt;/tex&amp;gt; . Для любой вершины &amp;lt;tex&amp;gt;(x,y)\in \mathbb {Z} _{n}\times \mathbb {Z} _{n}&amp;lt;/tex&amp;gt;, её восемь соседей будут&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;(x \pm 2y,y), (x \pm (2y+1),y), (x,y \pm 2x), (x,y \pm (2x+1)).&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Выполняется следующая теорема:&lt;br /&gt;
{{Теорема&lt;br /&gt;
|statement=&lt;br /&gt;
Для всех &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; граф &amp;lt;tex&amp;gt;Gn&amp;lt;/tex&amp;gt; второе по величине собственное число&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;tex&amp;gt;\lambda(G)\leq 5 \sqrt{2}&amp;lt;/tex&amp;gt;.}}&lt;br /&gt;
&lt;br /&gt;
===Граф Рамануджана===&lt;br /&gt;
&amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt;-регулярный граф называется графом Рамануджана, если его второе по модулю собственное число не превосходит &amp;lt;tex&amp;gt;2{\sqrt {d-1}}&amp;lt;/tex&amp;gt;. Любоцкий, Сарнак, Филлипс и Маргулис указали явную конструкцию графов Кэли, являющихся графами Рамануджана. Опишем эту конструкцию.&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt;p&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; простые числа, &amp;lt;tex&amp;gt;p = 1&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;mod&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;4&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;q = 1&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;mod&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;4&amp;lt;/tex&amp;gt;. В качестве группы &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; возьмём &amp;lt;tex&amp;gt;PGL(2, \mathbb Z/q{\mathbb Z})&amp;lt;/tex&amp;gt;, т.е. невырожденные матрицы 2 × 2 над полем вычетов по модулю &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;, профакторизованные по отношению пропорциональности (с обычной операцией матричного умножения).&lt;br /&gt;
Далее мы зададим в этой группе симметричное множество &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;. Выберем такое целое &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;i^2 = −1&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;mod&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;. Можно доказать, что тогда имеется ровно &amp;lt;tex&amp;gt;(p + 1)&amp;lt;/tex&amp;gt; целочисленное решение уравнения&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;a_0^2 + a_1^2 + a_3^2 = p&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
такое, что &amp;lt;tex&amp;gt;a_0&amp;lt;/tex&amp;gt; положительно и нечётно, а &amp;lt;tex&amp;gt;a_1&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;a_2&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;a_3&amp;lt;/tex&amp;gt; чётны. Каждой такой четвёрке сопоставим матрицу&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;A = \begin{pmatrix} a_0 + ia_1 &amp;amp; a_2 + ia_3\\ -a_2 + ia_3 &amp;amp; a_0 - ia_1 \end{pmatrix}&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Эти матрицы образуют множество S.&lt;br /&gt;
Нетрудно понять, что граф Кэли &amp;lt;tex&amp;gt;(G, S)&amp;lt;/tex&amp;gt; состоит из &amp;lt;tex&amp;gt;\Theta(q^3)&amp;lt;/tex&amp;gt; вершин, и степень каждой вершины равна &amp;lt;tex&amp;gt;(p + 1)&amp;lt;/tex&amp;gt;. Свойства данного графа зависят от соотношения &amp;lt;tex&amp;gt;p&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;. Рассмотрим случай, когда p является квадратичным вычетом по модулю &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;. Тогда полученный граф Кэли состоит из двух связных компонент (поскольку все матрицы из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; лежат в подгруппе &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; индекса два — подгруппе матриц, определитель которых является квадратичным вычетом). Обозначим &amp;lt;tex&amp;gt;X^{p,q}&amp;lt;/tex&amp;gt; связную компоненту полученного графа. Можно доказать, что у &amp;lt;tex&amp;gt;X^{p,q}&amp;lt;/tex&amp;gt; второе по абсолютной величине собственное число не превосходит &amp;lt;tex&amp;gt;2{\sqrt{p}}&amp;lt;/tex&amp;gt;, т.е. мы получили граф Рамануджана.&lt;br /&gt;
&lt;br /&gt;
==Примеры применения экспандеров==&lt;br /&gt;
===Коды, исправляющие ошибки===&lt;br /&gt;
С помощью расширяющего графа можно посторить линейный код, позволяющий исправлять ошибки в доле &amp;lt;tex&amp;gt;\delta = 1/(2000d)&amp;lt;/tex&amp;gt; битов. Чтобы задать линейный код с длиной кодового слова n, достаточно описать его проверочную матрицу &amp;lt;tex&amp;gt;H&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;(&amp;lt;/tex&amp;gt;слово &amp;lt;tex&amp;gt;x \in \{0, 1\}^n&amp;lt;/tex&amp;gt; является кодовым словом если и только если &amp;lt;tex&amp;gt;Hx^t = 0)&amp;lt;/tex&amp;gt;. Другими словами, нужно задать систему линейных уравнений для переменных &amp;lt;tex&amp;gt;x_1, . . . , x_n;&amp;lt;/tex&amp;gt; решения этой системы и будут кодовыми словами.&lt;br /&gt;
===Увеличение вероятности успеха в алгоритмах с датчиком случайных чисел===&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=Язык &amp;lt;tex&amp;gt;L&amp;lt;/tex&amp;gt; принадлежит сложностному классу &amp;lt;tex&amp;gt;RP&amp;lt;/tex&amp;gt;, если существует полиномиальный алгоритм &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; такой что&lt;br /&gt;
1. для &amp;lt;tex&amp;gt;x \in L&amp;lt;/tex&amp;gt; для всех &amp;lt;tex&amp;gt;r \in \{0, 1\}^{poly(n)}&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;A(x, r) = 1&amp;lt;/tex&amp;gt;&lt;br /&gt;
2. для &amp;lt;tex&amp;gt;x \notin L&amp;lt;/tex&amp;gt; не более чем для 1/2000 всех &amp;lt;tex&amp;gt;r \in \{0, 1\}^{poly(n)}&amp;lt;/tex&amp;gt; может выполняться &amp;lt;tex&amp;gt;A(x, r) = 1&amp;lt;/tex&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Покажем, что для любого &amp;lt;tex&amp;gt;\epsilon &amp;gt; 0&amp;lt;/tex&amp;gt; полиномиальный вероятностный алгоритм A можно модифицировать таким образом, чтобы вероятность ошибки уменьшилась до &amp;lt;tex&amp;gt;\epsilon&amp;lt;/tex&amp;gt;, а число используемых случайных битов не изменится.&lt;br /&gt;
&lt;br /&gt;
Пусть исходный алгоритм использует &amp;lt;tex&amp;gt;k = k(n)&amp;lt;/tex&amp;gt; случайных битов для вычислений на входах длины &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;. Зафиксируем &amp;lt;tex&amp;gt;(2^k, 2^k, d)&amp;lt;/tex&amp;gt; - экспандер &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;d &amp;gt; 1/\epsilon&amp;lt;/tex&amp;gt;. Новый алгоритм действует следующим образом: выбирается случайная вершина &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; из левой доли графа (для этого требуется &amp;lt;tex&amp;gt;k&amp;lt;/tex&amp;gt; случайных битов); затем исходный алгоритм &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; последовательно запускается на всех &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt; наборах случайных битов, соответствующих соседям вершины &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;. Если все полученные ответы равны &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;, новый алгоритм также возвращает единицу; в противном случае возвращается ноль.&lt;br /&gt;
Покажем, что у нового алгоритма вероятность ошибки не превосходит &amp;lt;tex&amp;gt;1/d&amp;lt;/tex&amp;gt;. В самом деле, обозначим &amp;lt;tex&amp;gt;B = B(x)&amp;lt;/tex&amp;gt; множество таких вершин &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt; из правой доли графа, которые соответствуют неверному ответу старого алгоритма на входе &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;; аналогично, обозначим &amp;lt;tex&amp;gt;S = S(x)&amp;lt;/tex&amp;gt; множество таких вершин v из левой доли графа, которые для которых новый алгоритм даёт неверный ответ на входе &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;. Очевидно, &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; состоит из вершин, все соседи которых лежат в B. Предположим, что &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; содержит не менее &amp;lt;tex&amp;gt;n/(1000d)&amp;lt;/tex&amp;gt; вершин. Выберем среди них ровно &amp;lt;tex&amp;gt;n/(1000d)&amp;lt;/tex&amp;gt; вершин и назовём это множество &amp;lt;tex&amp;gt;S'&amp;lt;/tex&amp;gt;. По свойству экспандера, имеем&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;|\Gamma(S')| \geqslant  \frac{7}{8}d\frac{n}{1000d}=7n/8000 &amp;gt; n/2000&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Это противоречит тому, что все соседи &amp;lt;tex&amp;gt;S'&amp;lt;/tex&amp;gt; лежат в &amp;lt;tex&amp;gt;B&amp;lt;/tex&amp;gt;. В данном случае нам нужна явная в более сильном (чем в первом примере) смысле конструкция экспандера. Размер графа экспоненциално растёт с увеличенем &amp;lt;tex&amp;gt;k&amp;lt;/tex&amp;gt;, и нам необходим алгоритм, который по заданному номеру вершины &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; (из левой доли графа) за время &amp;lt;tex&amp;gt;poly(k)&amp;lt;/tex&amp;gt; находит список номеров всех соседей этой вершины (в правой доле графа).&lt;br /&gt;
&lt;br /&gt;
===Хранение множества со сверхбыстрым запросом элементов===&lt;br /&gt;
Мы организуем хранение m-элементного множества &amp;lt;tex&amp;gt;S \subset \{1, . . . , n\}&amp;lt;/tex&amp;gt; в виде описания &amp;lt;tex&amp;gt;X&amp;lt;/tex&amp;gt;, состоящего из &amp;lt;tex&amp;gt;O(m log n)&amp;lt;/tex&amp;gt; битов. При этом проверка принадлежности &amp;lt;tex&amp;gt;a \in S&amp;lt;/tex&amp;gt; будет производиться чрезвычайно быстро. А именно, мы построим такой вероятностный алгоритм, который по любому входу &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; запрашивает из &amp;lt;tex&amp;gt;X&amp;lt;/tex&amp;gt; один бит; если этот бит оказывается равным единице, то алгоритм отвечает, что &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; является элементом &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;; в противном случае алгоритм говорит, что &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; множеству не принадлежит. При этом для каждого &amp;lt;tex&amp;gt;a \in \{1, . . . , n\}&amp;lt;/tex&amp;gt; алгоритм ошибается с вероятностью не более &amp;lt;tex&amp;gt;1/3&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Чтобы построить нужное нам хранилище &amp;lt;tex&amp;gt;X&amp;lt;/tex&amp;gt;, мы сначала зафиксируем некоторый экспандер, у которого левая доля &amp;lt;tex&amp;gt;L&amp;lt;/tex&amp;gt; состоит из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; вершин, правая &amp;lt;tex&amp;gt;R&amp;lt;/tex&amp;gt; из &amp;lt;tex&amp;gt;k = O(m log n)&amp;lt;/tex&amp;gt; вершин, степень всех вершин левой доли одинакова и равна некоторому &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt;, и для каждого &amp;lt;tex&amp;gt;A \subset L&amp;lt;/tex&amp;gt; размера не более &amp;lt;tex&amp;gt;2m&amp;lt;/tex&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;|\Gamma(A)| \geqslant \frac{7}{8}d|A|&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;X&amp;lt;/tex&amp;gt; будет состоять в разметке вершин правой доли нулями и единицами. Эту разметку нужно выбрать таким образом, чтобы у каждой вершины из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; не менее &amp;lt;tex&amp;gt;2/3&amp;lt;/tex&amp;gt; соседей были помечены единицей, а у каждой вершины не из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; не менее &amp;lt;tex&amp;gt;2/3&amp;lt;/tex&amp;gt; соседей были помечены нулями.&amp;lt;tex&amp;gt;X&amp;lt;/tex&amp;gt; будет состоять в разметке вершин правой доли нулями и единицами. Эту разметку нужно выбрать таким образом, чтобы у каждой вершины из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; не менее &amp;lt;tex&amp;gt;2/3&amp;lt;/tex&amp;gt; соседей были помечены единицей, а у каждой вершины не из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; не менее &amp;lt;tex&amp;gt;2/3&amp;lt;/tex&amp;gt; соседей были помечены нулями.&lt;br /&gt;
&lt;br /&gt;
Остаётся объяснить, как построить нужную нам разметку правой доли графа. Будем строить её последовательными приближениями. Сначала пометим всех соседей всех вершин из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; единицами, а все остальные вершины – нулями. На данной разметке наш алгоритм с вероятностью &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt; возвращает правильный ответ для всех &amp;lt;tex&amp;gt;a \in S&amp;lt;/tex&amp;gt;. Однако для &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; не из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; проверочный алгоритм может работать неверно. Обозначим T множество всех таких вершин вне &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, у которых более &amp;lt;tex&amp;gt;d/3&amp;lt;/tex&amp;gt; соседей помечены единицей. Поменяем разметку: пометим всех соседей &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; нулём. Теперь разметка может стать плохой для части вершин из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;. Обозначим &amp;lt;tex&amp;gt;S'&amp;lt;/tex&amp;gt; множество всех таких вершин из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, у которых более &amp;lt;tex&amp;gt;d/3&amp;lt;/tex&amp;gt; соседей помечены нулями. Далее, поменяем разметку у&lt;br /&gt;
всех соседей &amp;lt;tex&amp;gt;S'&amp;lt;/tex&amp;gt; на единицы. После этого может вновь возникнуть множество ‘неправильных’ вершин вне &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, и т.д.&lt;br /&gt;
&lt;br /&gt;
Чтобы доказать, что данный процесс в конце концов сойдётся, нужно показать, что на каждом шаге число ‘проблемных’ вершин уменьшается в константу раз. Поскольку все шаги аналогичны, достаточно разобрать самый первый: докажем, что &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; в константу раз меньше, чем &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;. Мы воспользуемся тем, что для &amp;lt;tex&amp;gt;S \cup T&amp;lt;/tex&amp;gt; выполнено свойство расширения:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;(7/8)d(|S|+|T|) \leqslant |\Gamma(S \cup T)| \leqslant d|S| + (2/3)d|T|&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Откуда получаем &amp;lt;tex&amp;gt;|T| \leqslant 3/5|S|&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Приложения и полезные свойства==&lt;br /&gt;
Первоначально интерес к экспандерам возник с целью построения устойчивой сети (телефонов или компьютеров) — число дуг графов расширения с ограниченным значением регулярности растет линейно по отношению к числу вершин.&lt;br /&gt;
&lt;br /&gt;
Экспандеры нашли широкое применение в теории вычислительных машин и систем, для построения алгоритмов, в корректирующих кодах, экстракторах, генераторах псевдослучайных чисел, сетях сортировки и компьютерных сетях. Они также используются для доказательства многих важных результатов в теории вычислительной сложности, таких как &amp;lt;tex&amp;gt;SL=L&amp;lt;/tex&amp;gt; и Теорема PCP. В криптографии экспандеры используются для создания хеш-функций.&lt;br /&gt;
&lt;br /&gt;
Ниже приведены некоторые свойства экспандеров, считающиеся полезными во многих областях.&lt;br /&gt;
===Лемма о перемешивании===&lt;br /&gt;
Лемма о перемешивании утверждает, что для любых двух подмножеств вершин &amp;lt;tex&amp;gt;S,T\subseteq V&amp;lt;/tex&amp;gt; число рёбер между &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; примерно равно числу рёбер в случайном &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt;-регулярном графе. Приближение тем лучше, чем меньше &amp;lt;tex&amp;gt;\lambda =\max\{|\lambda _{2}|&amp;lt;/tex&amp;gt;,&amp;lt;tex&amp;gt;|\lambda _{n}|\}&amp;lt;/tex&amp;gt;. В случайном &amp;lt;tex&amp;gt;d&amp;lt;tex/&amp;gt;-регулярном графе, также как и в случайном графе Эрдёша — Реньи с вероятностью &amp;lt;tex&amp;gt;{\tfrac {d}{n}}&amp;lt;/tex&amp;gt; выбора ребра, ожидается &amp;lt;tex&amp;gt;{\tfrac {d}{n}}\cdot |S|\cdot |T|&amp;lt;/tex&amp;gt; рёбер между S и T.&lt;br /&gt;
&lt;br /&gt;
Более формально, пусть &amp;lt;tex&amp;gt;E(S, T)&amp;lt;/tex&amp;gt; обозначает число рёбер между &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;. Если эти два множества пересекаются, дуги в пересечении считаются дважды, так что&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;E(S,T)=2|E(G[S\cap T])|+E(S\setminus T,T)+E(S,T\setminus S)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
Лемма о перемешивании утверждает, что&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\left|E(S,T)-{\frac {d\cdot |S|\cdot |T|}{n}}\right|\leq d\lambda {\sqrt {|S|\cdot |T|}}&amp;lt;/tex&amp;gt;,&lt;br /&gt;
где &amp;lt;tex&amp;gt;\lambda &amp;lt;/tex&amp;gt; — абсолютное значение нормализованного второго по величине собственного значения.&lt;br /&gt;
&lt;br /&gt;
Недавно Билу (Bilu) и Линайл (Linial) показали, что обратное тоже верно, то есть, при условии выполнения неравенства из леммы второе по величине собственное значение равно &amp;lt;tex&amp;gt;O(d\lambda \cdot (1+\log(1/\lambda )))&amp;lt;/tex&amp;gt;.&lt;br /&gt;
===Блуждания по экспандеру===&lt;br /&gt;
Согласно границе Чернова, если выбирать много независимых случайных значений из интервала &amp;lt;tex&amp;gt;[−1, 1]&amp;lt;/tex&amp;gt;, с большой степенью вероятности среднее выбранных значений будет близко к математическому ожиданию случайной переменной. Лемма о блуждании по экспандеру, согласно статьям Аджтари, Комлоша и Семереди и Гилмана, утверждает, что то же самое верно и для блужданий по экспандеру. Это полезно в теории дерандомизации, поскольку блуждание по экспандеру использует много меньше случайных бит, чем случайная независимая выборка.&lt;br /&gt;
&lt;br /&gt;
==Источники информации==&lt;br /&gt;
*[https://ru.wikipedia.org/wiki/%D0%AD%D0%BA%D1%81%D0%BF%D0%B0%D0%BD%D0%B4%D0%B5%D1%80_(%D1%82%D0%B5%D0%BE%D1%80%D0%B8%D1%8F_%D0%B3%D1%80%D0%B0%D1%84%D0%BE%D0%B2) Экспандер (теория графов)]&lt;/div&gt;</summary>
		<author><name>Kozichuk</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%93%D1%80%D0%B0%D1%84%D1%8B-%D1%8D%D0%BA%D1%81%D0%BF%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%8B&amp;diff=50931</id>
		<title>Графы-экспандеры</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%93%D1%80%D0%B0%D1%84%D1%8B-%D1%8D%D0%BA%D1%81%D0%BF%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%8B&amp;diff=50931"/>
				<updated>2016-01-09T01:55:12Z</updated>
		
		<summary type="html">&lt;p&gt;Kozichuk: /* Хранение множества со сверхбыстрым запросом элементов */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Граф-экспандер''' (или расширяющийся граф, англ. ''expander graph'') - в комбинаторике сильно разреженный граф, при этом связность определяется по вершинам, дугам или спектру.&lt;br /&gt;
&lt;br /&gt;
==Определение==&lt;br /&gt;
''Граф-экспандер'' — это конечный ненаправленный ''мультиграф'', в котором любое подмножество вершин, не являясь «слишком большим», имеет «сильную» связность. Различные формализации этих понятий дают различные типы экспандеров: ''рёберный расширитель'', ''вершинный расширитель'', и ''спектральный расширитель''.&lt;br /&gt;
&lt;br /&gt;
Несвязный граф не является экспандером. Любой связный граф является экспандером, однако различные связные графы имеют различные параметры расширителя. Полный граф имеет лучшие параметры расширителя, но имеет наибольшую возможную степень. Неформально говоря, граф является хорошим экспандером, если он имеет низкую степень и высокий параметр расширителя.&lt;br /&gt;
&lt;br /&gt;
===Реберное расширение===&lt;br /&gt;
''Рёберное расширение'' (также ''изопериметрическое число'' или константа Чигера) &amp;lt;tex&amp;gt;h(G)&amp;lt;/tex&amp;gt; графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; для &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; вершин определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;h(G) = \min_{0 &amp;lt; |S|\le \frac{n}{2}} \frac{|\partial_{\text{out}}(S)|}{|S|}&amp;lt;/tex&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где минимум берётся по всем непустым множествам &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; не более чем &amp;lt;tex&amp;gt;n/2&amp;lt;/tex&amp;gt; вершин и &amp;lt;tex&amp;gt;\partial(S)&amp;lt;/tex&amp;gt; — ''граничные дуги'' множества &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, то есть, множество дуг с единственной вершиной в &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Вершинное расширение===&lt;br /&gt;
''Вершинное изопериметрическое число'' &amp;lt;tex&amp;gt;h_{out}(G)&amp;lt;/tex&amp;gt; (также ''вершинное раширение'') графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;h_{out}(G) = \min_{0 &amp;lt; |S|\le \frac{n}{2}} \frac{|\partial_{\text{out}}(S)|}{|S|},&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;tex&amp;gt;\partial_{\text{out}}(S)&amp;lt;/tex&amp;gt; — ''внешняя граница'' &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, то есть множество вершин из &amp;lt;tex&amp;gt;V(G)\setminus S&amp;lt;/tex&amp;gt;, имеющих как минимум одного соседа в S. В варианте этого определения (называемом ''уникальным соседним расширением'') &amp;lt;tex&amp;gt;\partial_{\text{out}}(S)&amp;lt;/tex&amp;gt; заменяется на множество вершин из &amp;lt;tex&amp;gt;V&amp;lt;/tex&amp;gt; с ''точностью одним'' соседом из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
''Вершинное изопериметрическое число'' &amp;lt;tex&amp;gt;h_{in}(G)&amp;lt;/tex&amp;gt; графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;h_{in}(G) = \min_{0 &amp;lt; |S|\le \frac{n}{2}} \frac{|\partial_{\text{in}}(S)|}{|S|},&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;tex&amp;gt;∂_{in}(S)&amp;lt;/tex&amp;gt; — ''внутренняя граница'' &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, то есть множество вершин &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, имеющих как минимум одного соседа в &amp;lt;tex&amp;gt;V(G)\setminus S&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Спектральное расширение===&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; является d-регулярным, возможно определение в терминах линейной алгебры на основе собственных значений матрицы смежности &amp;lt;tex&amp;gt;A = A(G)&amp;lt;/tex&amp;gt; графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;A_{ij}&amp;lt;/tex&amp;gt; — число дуг между вершинами &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;. Поскольку &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; является симметричной, согласно спектральной теореме, &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; имеет &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; действительных собственных значений &amp;lt;tex&amp;gt;\lambda_1 \ge \lambda_2 \ge \cdots \ge \lambda_{n}&amp;lt;/tex&amp;gt;. Известно, что эти значения лежат в промежутке &amp;lt;tex&amp;gt;[−d, d]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
Граф регулярен тогда и только тогда, когда вектор &amp;lt;tex&amp;gt;u\in \mathbb {R} ^{n} с u_{i}=1&amp;lt;/tex&amp;gt; для всех &amp;lt;tex&amp;gt;i = 1, …, n&amp;lt;/tex&amp;gt; является собственным вектором матрицы &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt;, а его собственное число будет постоянной степенью графа. Таким образом, мы имеем &amp;lt;tex&amp;gt;Au = du&amp;lt;/tex&amp;gt;, и &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt; — собственный вектор матрицы &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; с собственными значениями &amp;lt;tex&amp;gt;λ1 = d&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt; — степень вершин графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt;. Спектральный зазор графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; определяется как &amp;lt;tex&amp;gt;d−λ2&amp;lt;/tex&amp;gt; и является мерилом спектрального расширения графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Известно, что &amp;lt;tex&amp;gt;\lambda_n = −d&amp;lt;/tex&amp;gt; тогда и только тогда, когда &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; — двудольный. Во многих случаях, например в лемме о перемешивании, необходимо ограничить снизу не только зазор между &amp;lt;tex&amp;gt;\lambda_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\lambda_2&amp;lt;/tex&amp;gt;, но и зазор между &amp;lt;tex&amp;gt;\lambda_1&amp;lt;/tex&amp;gt; и вторым максимальным по модулю собственным значением:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\lambda=\max\{|\lambda_2|, |\lambda_{n}|\}&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Поскольку это собственное значение соответствует некоторому собственному вектору, ортогональному &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;, его можно определить, используя отношение Рэлея:&lt;br /&gt;
&amp;lt;tex&amp;gt;\lambda=\max_{0\neq v\perp u} \frac{\|Av\|_2}{\|v\|_2},&amp;lt;/tex&amp;gt;&lt;br /&gt;
gde&lt;br /&gt;
&amp;lt;tex&amp;gt;\|v\|_2=\left(\sum_{i=1}^n v_i^2\right)^{1/2}&amp;lt;/tex&amp;gt;&lt;br /&gt;
— евклидова норма вектора &amp;lt;tex&amp;gt;v\in \mathbb {R} ^{n}&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Нормализованная версия этого определения также широко используется и более удобна для получения определённых результатов. В таком случае рассматривается матрица &amp;lt;tex&amp;gt;{\tfrac {1}{d}}A&amp;lt;/tex&amp;gt;, являющаяся матрицей переходов графа G. Все её собственные значения лежат между &amp;lt;tex&amp;gt;−1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;. Если граф не регулярен, спектр графа может быть определён аналогичным образом, используя собственные значения матрицы Кирхгофа. Для направленного графа используются сингулярные значения матрицы сопряжения A, которые равны квадратным корням из собственных значений симметричной матрицы &amp;lt;tex&amp;gt;A^TA&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Конструирование==&lt;br /&gt;
Существуют три основные стратегии создания семейств графов расширений[6]. Первая стратегия — алгебраическая и теоретико-групповая, вторая — аналитическая, использующая аддитивную комбинаторику, и третья — комбинаторная, использующая зигзаг-произведение и связанные комбинаторные произведения.&lt;br /&gt;
===Маргулис-Габбер-Галил===&lt;br /&gt;
Алгебраическое конструирование, основанное на графах Кэли, известно для различных вариантов экспандеров. Следующее конструирование принадлежит Маргулису и было проанализировано Габбером (Gabber) и Галилом (Galil). Для любого натурального &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; строим граф, &amp;lt;tex&amp;gt;G_{n}&amp;lt;/tex&amp;gt; со множеством вершин &amp;lt;tex&amp;gt;\mathbb Z _{n}\times \mathbb {Z} _{n}&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;\mathbb {Z} _{n}=\mathbb Z /n \mathbb Z&amp;lt;/tex&amp;gt; . Для любой вершины &amp;lt;tex&amp;gt;(x,y)\in \mathbb {Z} _{n}\times \mathbb {Z} _{n}&amp;lt;/tex&amp;gt;, её восемь соседей будут&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;(x \pm 2y,y), (x \pm (2y+1),y), (x,y \pm 2x), (x,y \pm (2x+1)).&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Выполняется следующая теорема:&lt;br /&gt;
{{Теорема&lt;br /&gt;
|statement=&lt;br /&gt;
Для всех &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; граф &amp;lt;tex&amp;gt;Gn&amp;lt;/tex&amp;gt; второе по величине собственное число&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;tex&amp;gt;\lambda(G)\leq 5 \sqrt{2}&amp;lt;/tex&amp;gt;.}}&lt;br /&gt;
&lt;br /&gt;
===Граф Рамануджана===&lt;br /&gt;
&amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt;-регулярный граф называется графом Рамануджана, если его второе по модулю собственное число не превосходит &amp;lt;tex&amp;gt;2{\sqrt {d-1}}&amp;lt;/tex&amp;gt;. Любоцкий, Сарнак, Филлипс и Маргулис указали явную конструкцию графов Кэли, являющихся графами Рамануджана. Опишем эту конструкцию.&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt;p&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; простые числа, &amp;lt;tex&amp;gt;p = 1&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;mod&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;4&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;q = 1&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;mod&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;4&amp;lt;/tex&amp;gt;. В качестве группы &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; возьмём &amp;lt;tex&amp;gt;PGL(2, \mathbb Z/q{\mathbb Z})&amp;lt;/tex&amp;gt;, т.е. невырожденные матрицы 2 × 2 над полем вычетов по модулю &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;, профакторизованные по отношению пропорциональности (с обычной операцией матричного умножения).&lt;br /&gt;
Далее мы зададим в этой группе симметричное множество &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;. Выберем такое целое &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;i^2 = −1&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;mod&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;. Можно доказать, что тогда имеется ровно &amp;lt;tex&amp;gt;(p + 1)&amp;lt;/tex&amp;gt; целочисленное решение уравнения&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;a_0^2 + a_1^2 + a_3^2 = p&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
такое, что &amp;lt;tex&amp;gt;a_0&amp;lt;/tex&amp;gt; положительно и нечётно, а &amp;lt;tex&amp;gt;a_1&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;a_2&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;a_3&amp;lt;/tex&amp;gt; чётны. Каждой такой четвёрке сопоставим матрицу&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;A = \begin{pmatrix} a_0 + ia_1 &amp;amp; a_2 + ia_3\\ -a_2 + ia_3 &amp;amp; a_0 - ia_1 \end{pmatrix}&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Эти матрицы образуют множество S.&lt;br /&gt;
Нетрудно понять, что граф Кэли &amp;lt;tex&amp;gt;(G, S)&amp;lt;/tex&amp;gt; состоит из &amp;lt;tex&amp;gt;\Theta(q^3)&amp;lt;/tex&amp;gt; вершин, и степень каждой вершины равна &amp;lt;tex&amp;gt;(p + 1)&amp;lt;/tex&amp;gt;. Свойства данного графа зависят от соотношения &amp;lt;tex&amp;gt;p&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;. Рассмотрим случай, когда p является квадратичным вычетом по модулю &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;. Тогда полученный граф Кэли состоит из двух связных компонент (поскольку все матрицы из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; лежат в подгруппе &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; индекса два — подгруппе матриц, определитель которых является квадратичным вычетом). Обозначим &amp;lt;tex&amp;gt;X^{p,q}&amp;lt;/tex&amp;gt; связную компоненту полученного графа. Можно доказать, что у &amp;lt;tex&amp;gt;X^{p,q}&amp;lt;/tex&amp;gt; второе по абсолютной величине собственное число не превосходит &amp;lt;tex&amp;gt;2{\sqrt{p}}&amp;lt;/tex&amp;gt;, т.е. мы получили граф Рамануджана.&lt;br /&gt;
&lt;br /&gt;
==Применение экспандеров==&lt;br /&gt;
===Коды, исправляющие ошибки===&lt;br /&gt;
С помощью расширяющего графа можно посторить линейный код, позволяющий исправлять ошибки в доле &amp;lt;tex&amp;gt;\delta = 1/(2000d)&amp;lt;/tex&amp;gt; битов. Чтобы задать линейный код с длиной кодового слова n, достаточно описать его проверочную матрицу &amp;lt;tex&amp;gt;H&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;(&amp;lt;/tex&amp;gt;слово &amp;lt;tex&amp;gt;x \in \{0, 1\}^n&amp;lt;/tex&amp;gt; является кодовым словом если и только если &amp;lt;tex&amp;gt;Hx^t = 0)&amp;lt;/tex&amp;gt;. Другими словами, нужно задать систему линейных уравнений для переменных &amp;lt;tex&amp;gt;x_1, . . . , x_n;&amp;lt;/tex&amp;gt; решения этой системы и будут кодовыми словами.&lt;br /&gt;
===Увеличение вероятности успеха в алгоритмах с датчиком случайных чисел===&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=Язык &amp;lt;tex&amp;gt;L&amp;lt;/tex&amp;gt; принадлежит сложностному классу &amp;lt;tex&amp;gt;RP&amp;lt;/tex&amp;gt;, если существует полиномиальный алгоритм &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; такой что&lt;br /&gt;
1. для &amp;lt;tex&amp;gt;x \in L&amp;lt;/tex&amp;gt; для всех &amp;lt;tex&amp;gt;r \in \{0, 1\}^{poly(n)}&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;A(x, r) = 1&amp;lt;/tex&amp;gt;&lt;br /&gt;
2. для &amp;lt;tex&amp;gt;x \notin L&amp;lt;/tex&amp;gt; не более чем для 1/2000 всех &amp;lt;tex&amp;gt;r \in \{0, 1\}^{poly(n)}&amp;lt;/tex&amp;gt; может выполняться &amp;lt;tex&amp;gt;A(x, r) = 1&amp;lt;/tex&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Покажем, что для любого &amp;lt;tex&amp;gt;\epsilon &amp;gt; 0&amp;lt;/tex&amp;gt; полиномиальный вероятностный алгоритм A можно модифицировать таким образом, чтобы вероятность ошибки уменьшилась до &amp;lt;tex&amp;gt;\epsilon&amp;lt;/tex&amp;gt;, а число используемых случайных битов не изменится.&lt;br /&gt;
&lt;br /&gt;
Пусть исходный алгоритм использует &amp;lt;tex&amp;gt;k = k(n)&amp;lt;/tex&amp;gt; случайных битов для вычислений на входах длины &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;. Зафиксируем &amp;lt;tex&amp;gt;(2^k, 2^k, d)&amp;lt;/tex&amp;gt; - экспандер &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;d &amp;gt; 1/\epsilon&amp;lt;/tex&amp;gt;. Новый алгоритм действует следующим образом: выбирается случайная вершина &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; из левой доли графа (для этого требуется &amp;lt;tex&amp;gt;k&amp;lt;/tex&amp;gt; случайных битов); затем исходный алгоритм &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; последовательно запускается на всех &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt; наборах случайных битов, соответствующих соседям вершины &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;. Если все полученные ответы равны &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;, новый алгоритм также возвращает единицу; в противном случае возвращается ноль.&lt;br /&gt;
Покажем, что у нового алгоритма вероятность ошибки не превосходит &amp;lt;tex&amp;gt;1/d&amp;lt;/tex&amp;gt;. В самом деле, обозначим &amp;lt;tex&amp;gt;B = B(x)&amp;lt;/tex&amp;gt; множество таких вершин &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt; из правой доли графа, которые соответствуют неверному ответу старого алгоритма на входе &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;; аналогично, обозначим &amp;lt;tex&amp;gt;S = S(x)&amp;lt;/tex&amp;gt; множество таких вершин v из левой доли графа, которые для которых новый алгоритм даёт неверный ответ на входе &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;. Очевидно, &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; состоит из вершин, все соседи которых лежат в B. Предположим, что &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; содержит не менее &amp;lt;tex&amp;gt;n/(1000d)&amp;lt;/tex&amp;gt; вершин. Выберем среди них ровно &amp;lt;tex&amp;gt;n/(1000d)&amp;lt;/tex&amp;gt; вершин и назовём это множество &amp;lt;tex&amp;gt;S'&amp;lt;/tex&amp;gt;. По свойству экспандера, имеем&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;|\Gamma(S')| \geqslant  \frac{7}{8}d\frac{n}{1000d}=7n/8000 &amp;gt; n/2000&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Это противоречит тому, что все соседи &amp;lt;tex&amp;gt;S'&amp;lt;/tex&amp;gt; лежат в &amp;lt;tex&amp;gt;B&amp;lt;/tex&amp;gt;. В данном случае нам нужна явная в более сильном (чем в первом примере) смысле конструкция экспандера. Размер графа экспоненциално растёт с увеличенем &amp;lt;tex&amp;gt;k&amp;lt;/tex&amp;gt;, и нам необходим алгоритм, который по заданному номеру вершины &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; (из левой доли графа) за время &amp;lt;tex&amp;gt;poly(k)&amp;lt;/tex&amp;gt; находит список номеров всех соседей этой вершины (в правой доле графа).&lt;br /&gt;
&lt;br /&gt;
===Хранение множества со сверхбыстрым запросом элементов===&lt;br /&gt;
Мы организуем хранение m-элементного множества &amp;lt;tex&amp;gt;S \subset \{1, . . . , n\}&amp;lt;/tex&amp;gt; в виде описания &amp;lt;tex&amp;gt;X&amp;lt;/tex&amp;gt;, состоящего из &amp;lt;tex&amp;gt;O(m log n)&amp;lt;/tex&amp;gt; битов. При этом проверка принадлежности &amp;lt;tex&amp;gt;a \in S&amp;lt;/tex&amp;gt; будет производиться чрезвычайно быстро. А именно, мы построим такой вероятностный алгоритм, который по любому входу &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; запрашивает из &amp;lt;tex&amp;gt;X&amp;lt;/tex&amp;gt; один бит; если этот бит оказывается равным единице, то алгоритм отвечает, что &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; является элементом &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;; в противном случае алгоритм говорит, что &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; множеству не принадлежит. При этом для каждого &amp;lt;tex&amp;gt;a \in \{1, . . . , n\}&amp;lt;/tex&amp;gt; алгоритм ошибается с вероятностью не более &amp;lt;tex&amp;gt;1/3&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Чтобы построить нужное нам хранилище &amp;lt;tex&amp;gt;X&amp;lt;/tex&amp;gt;, мы сначала зафиксируем некоторый экспандер, у которого левая доля &amp;lt;tex&amp;gt;L&amp;lt;/tex&amp;gt; состоит из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; вершин, правая &amp;lt;tex&amp;gt;R&amp;lt;/tex&amp;gt; из &amp;lt;tex&amp;gt;k = O(m log n)&amp;lt;/tex&amp;gt; вершин, степень всех вершин левой доли одинакова и равна некоторому &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt;, и для каждого &amp;lt;tex&amp;gt;A \subset L&amp;lt;/tex&amp;gt; размера не более &amp;lt;tex&amp;gt;2m&amp;lt;/tex&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;|\Gamma(A)| \geqslant \frac{7}{8}d|A|&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;X&amp;lt;/tex&amp;gt; будет состоять в разметке вершин правой доли нулями и единицами. Эту разметку нужно выбрать таким образом, чтобы у каждой вершины из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; не менее &amp;lt;tex&amp;gt;2/3&amp;lt;/tex&amp;gt; соседей были помечены единицей, а у каждой вершины не из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; не менее &amp;lt;tex&amp;gt;2/3&amp;lt;/tex&amp;gt; соседей были помечены нулями.&amp;lt;tex&amp;gt;X&amp;lt;/tex&amp;gt; будет состоять в разметке вершин правой доли нулями и единицами. Эту разметку нужно выбрать таким образом, чтобы у каждой вершины из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; не менее &amp;lt;tex&amp;gt;2/3&amp;lt;/tex&amp;gt; соседей были помечены единицей, а у каждой вершины не из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; не менее &amp;lt;tex&amp;gt;2/3&amp;lt;/tex&amp;gt; соседей были помечены нулями.&lt;br /&gt;
&lt;br /&gt;
Остаётся объяснить, как построить нужную нам разметку правой доли графа. Будем строить её последовательными приближениями. Сначала пометим всех соседей всех вершин из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; единицами, а все остальные вершины – нулями. На данной разметке наш алгоритм с вероятностью &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt; возвращает правильный ответ для всех &amp;lt;tex&amp;gt;a \in S&amp;lt;/tex&amp;gt;. Однако для &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; не из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; проверочный алгоритм может работать неверно. Обозначим T множество всех таких вершин вне &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, у которых более &amp;lt;tex&amp;gt;d/3&amp;lt;/tex&amp;gt; соседей помечены единицей. Поменяем разметку: пометим всех соседей &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; нулём. Теперь разметка может стать плохой для части вершин из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;. Обозначим &amp;lt;tex&amp;gt;S'&amp;lt;/tex&amp;gt; множество всех таких вершин из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, у которых более &amp;lt;tex&amp;gt;d/3&amp;lt;/tex&amp;gt; соседей помечены нулями. Далее, поменяем разметку у&lt;br /&gt;
всех соседей &amp;lt;tex&amp;gt;S'&amp;lt;/tex&amp;gt; на единицы. После этого может вновь возникнуть множество ‘неправильных’ вершин вне &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, и т.д.&lt;br /&gt;
&lt;br /&gt;
Чтобы доказать, что данный процесс в конце концов сойдётся, нужно показать, что на каждом шаге число ‘проблемных’ вершин уменьшается в константу раз. Поскольку все шаги аналогичны, достаточно разобрать самый первый: докажем, что &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; в константу раз меньше, чем &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;. Мы воспользуемся тем, что для &amp;lt;tex&amp;gt;S \cup T&amp;lt;/tex&amp;gt; выполнено свойство расширения:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;(7/8)d(|S|+|T|) \leqslant |\Gamma(S \cup T)| \leqslant d|S| + (2/3)d|T|&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Откуда получаем &amp;lt;tex&amp;gt;|T| \leqslant 3/5|S|&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Приложения и полезные свойства==&lt;br /&gt;
Первоначально интерес к экспандерам возник с целью построения устойчивой сети (телефонов или компьютеров) — число дуг графов расширения с ограниченным значением регулярности растет линейно по отношению к числу вершин.&lt;br /&gt;
&lt;br /&gt;
Экспандеры нашли широкое применение в теории вычислительных машин и систем, для построения алгоритмов, в корректирующих кодах, экстракторах, генераторах псевдослучайных чисел, сетях сортировки и компьютерных сетях. Они также используются для доказательства многих важных результатов в теории вычислительной сложности, таких как &amp;lt;tex&amp;gt;SL=L&amp;lt;/tex&amp;gt; и Теорема PCP. В криптографии экспандеры используются для создания хеш-функций.&lt;br /&gt;
&lt;br /&gt;
Ниже приведены некоторые свойства экспандеров, считающиеся полезными во многих областях.&lt;br /&gt;
===Лемма о перемешивании===&lt;br /&gt;
Лемма о перемешивании утверждает, что для любых двух подмножеств вершин &amp;lt;tex&amp;gt;S,T\subseteq V&amp;lt;/tex&amp;gt; число рёбер между &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; примерно равно числу рёбер в случайном &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt;-регулярном графе. Приближение тем лучше, чем меньше &amp;lt;tex&amp;gt;\lambda =\max\{|\lambda _{2}|&amp;lt;/tex&amp;gt;,&amp;lt;tex&amp;gt;|\lambda _{n}|\}&amp;lt;/tex&amp;gt;. В случайном &amp;lt;tex&amp;gt;d&amp;lt;tex/&amp;gt;-регулярном графе, также как и в случайном графе Эрдёша — Реньи с вероятностью &amp;lt;tex&amp;gt;{\tfrac {d}{n}}&amp;lt;/tex&amp;gt; выбора ребра, ожидается &amp;lt;tex&amp;gt;{\tfrac {d}{n}}\cdot |S|\cdot |T|&amp;lt;/tex&amp;gt; рёбер между S и T.&lt;br /&gt;
&lt;br /&gt;
Более формально, пусть &amp;lt;tex&amp;gt;E(S, T)&amp;lt;/tex&amp;gt; обозначает число рёбер между &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;. Если эти два множества пересекаются, дуги в пересечении считаются дважды, так что&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;E(S,T)=2|E(G[S\cap T])|+E(S\setminus T,T)+E(S,T\setminus S)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
Лемма о перемешивании утверждает, что&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\left|E(S,T)-{\frac {d\cdot |S|\cdot |T|}{n}}\right|\leq d\lambda {\sqrt {|S|\cdot |T|}}&amp;lt;/tex&amp;gt;,&lt;br /&gt;
где &amp;lt;tex&amp;gt;\lambda &amp;lt;/tex&amp;gt; — абсолютное значение нормализованного второго по величине собственного значения.&lt;br /&gt;
&lt;br /&gt;
Недавно Билу (Bilu) и Линайл (Linial) показали, что обратное тоже верно, то есть, при условии выполнения неравенства из леммы второе по величине собственное значение равно &amp;lt;tex&amp;gt;O(d\lambda \cdot (1+\log(1/\lambda )))&amp;lt;/tex&amp;gt;.&lt;br /&gt;
===Блуждания по экспандеру===&lt;br /&gt;
Согласно границе Чернова, если выбирать много независимых случайных значений из интервала &amp;lt;tex&amp;gt;[−1, 1]&amp;lt;/tex&amp;gt;, с большой степенью вероятности среднее выбранных значений будет близко к математическому ожиданию случайной переменной. Лемма о блуждании по экспандеру, согласно статьям Аджтари, Комлоша и Семереди и Гилмана, утверждает, что то же самое верно и для блужданий по экспандеру. Это полезно в теории дерандомизации, поскольку блуждание по экспандеру использует много меньше случайных бит, чем случайная независимая выборка.&lt;br /&gt;
&lt;br /&gt;
==Источники информации==&lt;br /&gt;
*[https://ru.wikipedia.org/wiki/%D0%AD%D0%BA%D1%81%D0%BF%D0%B0%D0%BD%D0%B4%D0%B5%D1%80_(%D1%82%D0%B5%D0%BE%D1%80%D0%B8%D1%8F_%D0%B3%D1%80%D0%B0%D1%84%D0%BE%D0%B2) Экспандер (теория графов)]&lt;/div&gt;</summary>
		<author><name>Kozichuk</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%93%D1%80%D0%B0%D1%84%D1%8B-%D1%8D%D0%BA%D1%81%D0%BF%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%8B&amp;diff=50930</id>
		<title>Графы-экспандеры</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%93%D1%80%D0%B0%D1%84%D1%8B-%D1%8D%D0%BA%D1%81%D0%BF%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%8B&amp;diff=50930"/>
				<updated>2016-01-09T01:38:45Z</updated>
		
		<summary type="html">&lt;p&gt;Kozichuk: /* Увеличение вероятности успеха в алгоритмах с датчиком случайных чисел */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Граф-экспандер''' (или расширяющийся граф, англ. ''expander graph'') - в комбинаторике сильно разреженный граф, при этом связность определяется по вершинам, дугам или спектру.&lt;br /&gt;
&lt;br /&gt;
==Определение==&lt;br /&gt;
''Граф-экспандер'' — это конечный ненаправленный ''мультиграф'', в котором любое подмножество вершин, не являясь «слишком большим», имеет «сильную» связность. Различные формализации этих понятий дают различные типы экспандеров: ''рёберный расширитель'', ''вершинный расширитель'', и ''спектральный расширитель''.&lt;br /&gt;
&lt;br /&gt;
Несвязный граф не является экспандером. Любой связный граф является экспандером, однако различные связные графы имеют различные параметры расширителя. Полный граф имеет лучшие параметры расширителя, но имеет наибольшую возможную степень. Неформально говоря, граф является хорошим экспандером, если он имеет низкую степень и высокий параметр расширителя.&lt;br /&gt;
&lt;br /&gt;
===Реберное расширение===&lt;br /&gt;
''Рёберное расширение'' (также ''изопериметрическое число'' или константа Чигера) &amp;lt;tex&amp;gt;h(G)&amp;lt;/tex&amp;gt; графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; для &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; вершин определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;h(G) = \min_{0 &amp;lt; |S|\le \frac{n}{2}} \frac{|\partial_{\text{out}}(S)|}{|S|}&amp;lt;/tex&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где минимум берётся по всем непустым множествам &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; не более чем &amp;lt;tex&amp;gt;n/2&amp;lt;/tex&amp;gt; вершин и &amp;lt;tex&amp;gt;\partial(S)&amp;lt;/tex&amp;gt; — ''граничные дуги'' множества &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, то есть, множество дуг с единственной вершиной в &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Вершинное расширение===&lt;br /&gt;
''Вершинное изопериметрическое число'' &amp;lt;tex&amp;gt;h_{out}(G)&amp;lt;/tex&amp;gt; (также ''вершинное раширение'') графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;h_{out}(G) = \min_{0 &amp;lt; |S|\le \frac{n}{2}} \frac{|\partial_{\text{out}}(S)|}{|S|},&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;tex&amp;gt;\partial_{\text{out}}(S)&amp;lt;/tex&amp;gt; — ''внешняя граница'' &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, то есть множество вершин из &amp;lt;tex&amp;gt;V(G)\setminus S&amp;lt;/tex&amp;gt;, имеющих как минимум одного соседа в S. В варианте этого определения (называемом ''уникальным соседним расширением'') &amp;lt;tex&amp;gt;\partial_{\text{out}}(S)&amp;lt;/tex&amp;gt; заменяется на множество вершин из &amp;lt;tex&amp;gt;V&amp;lt;/tex&amp;gt; с ''точностью одним'' соседом из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
''Вершинное изопериметрическое число'' &amp;lt;tex&amp;gt;h_{in}(G)&amp;lt;/tex&amp;gt; графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;h_{in}(G) = \min_{0 &amp;lt; |S|\le \frac{n}{2}} \frac{|\partial_{\text{in}}(S)|}{|S|},&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;tex&amp;gt;∂_{in}(S)&amp;lt;/tex&amp;gt; — ''внутренняя граница'' &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, то есть множество вершин &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, имеющих как минимум одного соседа в &amp;lt;tex&amp;gt;V(G)\setminus S&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Спектральное расширение===&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; является d-регулярным, возможно определение в терминах линейной алгебры на основе собственных значений матрицы смежности &amp;lt;tex&amp;gt;A = A(G)&amp;lt;/tex&amp;gt; графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;A_{ij}&amp;lt;/tex&amp;gt; — число дуг между вершинами &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;. Поскольку &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; является симметричной, согласно спектральной теореме, &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; имеет &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; действительных собственных значений &amp;lt;tex&amp;gt;\lambda_1 \ge \lambda_2 \ge \cdots \ge \lambda_{n}&amp;lt;/tex&amp;gt;. Известно, что эти значения лежат в промежутке &amp;lt;tex&amp;gt;[−d, d]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
Граф регулярен тогда и только тогда, когда вектор &amp;lt;tex&amp;gt;u\in \mathbb {R} ^{n} с u_{i}=1&amp;lt;/tex&amp;gt; для всех &amp;lt;tex&amp;gt;i = 1, …, n&amp;lt;/tex&amp;gt; является собственным вектором матрицы &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt;, а его собственное число будет постоянной степенью графа. Таким образом, мы имеем &amp;lt;tex&amp;gt;Au = du&amp;lt;/tex&amp;gt;, и &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt; — собственный вектор матрицы &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; с собственными значениями &amp;lt;tex&amp;gt;λ1 = d&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt; — степень вершин графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt;. Спектральный зазор графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; определяется как &amp;lt;tex&amp;gt;d−λ2&amp;lt;/tex&amp;gt; и является мерилом спектрального расширения графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Известно, что &amp;lt;tex&amp;gt;\lambda_n = −d&amp;lt;/tex&amp;gt; тогда и только тогда, когда &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; — двудольный. Во многих случаях, например в лемме о перемешивании, необходимо ограничить снизу не только зазор между &amp;lt;tex&amp;gt;\lambda_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\lambda_2&amp;lt;/tex&amp;gt;, но и зазор между &amp;lt;tex&amp;gt;\lambda_1&amp;lt;/tex&amp;gt; и вторым максимальным по модулю собственным значением:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\lambda=\max\{|\lambda_2|, |\lambda_{n}|\}&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Поскольку это собственное значение соответствует некоторому собственному вектору, ортогональному &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;, его можно определить, используя отношение Рэлея:&lt;br /&gt;
&amp;lt;tex&amp;gt;\lambda=\max_{0\neq v\perp u} \frac{\|Av\|_2}{\|v\|_2},&amp;lt;/tex&amp;gt;&lt;br /&gt;
gde&lt;br /&gt;
&amp;lt;tex&amp;gt;\|v\|_2=\left(\sum_{i=1}^n v_i^2\right)^{1/2}&amp;lt;/tex&amp;gt;&lt;br /&gt;
— евклидова норма вектора &amp;lt;tex&amp;gt;v\in \mathbb {R} ^{n}&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Нормализованная версия этого определения также широко используется и более удобна для получения определённых результатов. В таком случае рассматривается матрица &amp;lt;tex&amp;gt;{\tfrac {1}{d}}A&amp;lt;/tex&amp;gt;, являющаяся матрицей переходов графа G. Все её собственные значения лежат между &amp;lt;tex&amp;gt;−1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;. Если граф не регулярен, спектр графа может быть определён аналогичным образом, используя собственные значения матрицы Кирхгофа. Для направленного графа используются сингулярные значения матрицы сопряжения A, которые равны квадратным корням из собственных значений симметричной матрицы &amp;lt;tex&amp;gt;A^TA&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Конструирование==&lt;br /&gt;
Существуют три основные стратегии создания семейств графов расширений[6]. Первая стратегия — алгебраическая и теоретико-групповая, вторая — аналитическая, использующая аддитивную комбинаторику, и третья — комбинаторная, использующая зигзаг-произведение и связанные комбинаторные произведения.&lt;br /&gt;
===Маргулис-Габбер-Галил===&lt;br /&gt;
Алгебраическое конструирование, основанное на графах Кэли, известно для различных вариантов экспандеров. Следующее конструирование принадлежит Маргулису и было проанализировано Габбером (Gabber) и Галилом (Galil). Для любого натурального &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; строим граф, &amp;lt;tex&amp;gt;G_{n}&amp;lt;/tex&amp;gt; со множеством вершин &amp;lt;tex&amp;gt;\mathbb Z _{n}\times \mathbb {Z} _{n}&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;\mathbb {Z} _{n}=\mathbb Z /n \mathbb Z&amp;lt;/tex&amp;gt; . Для любой вершины &amp;lt;tex&amp;gt;(x,y)\in \mathbb {Z} _{n}\times \mathbb {Z} _{n}&amp;lt;/tex&amp;gt;, её восемь соседей будут&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;(x \pm 2y,y), (x \pm (2y+1),y), (x,y \pm 2x), (x,y \pm (2x+1)).&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Выполняется следующая теорема:&lt;br /&gt;
{{Теорема&lt;br /&gt;
|statement=&lt;br /&gt;
Для всех &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; граф &amp;lt;tex&amp;gt;Gn&amp;lt;/tex&amp;gt; второе по величине собственное число&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;tex&amp;gt;\lambda(G)\leq 5 \sqrt{2}&amp;lt;/tex&amp;gt;.}}&lt;br /&gt;
&lt;br /&gt;
===Граф Рамануджана===&lt;br /&gt;
&amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt;-регулярный граф называется графом Рамануджана, если его второе по модулю собственное число не превосходит &amp;lt;tex&amp;gt;2{\sqrt {d-1}}&amp;lt;/tex&amp;gt;. Любоцкий, Сарнак, Филлипс и Маргулис указали явную конструкцию графов Кэли, являющихся графами Рамануджана. Опишем эту конструкцию.&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt;p&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; простые числа, &amp;lt;tex&amp;gt;p = 1&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;mod&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;4&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;q = 1&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;mod&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;4&amp;lt;/tex&amp;gt;. В качестве группы &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; возьмём &amp;lt;tex&amp;gt;PGL(2, \mathbb Z/q{\mathbb Z})&amp;lt;/tex&amp;gt;, т.е. невырожденные матрицы 2 × 2 над полем вычетов по модулю &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;, профакторизованные по отношению пропорциональности (с обычной операцией матричного умножения).&lt;br /&gt;
Далее мы зададим в этой группе симметричное множество &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;. Выберем такое целое &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;i^2 = −1&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;mod&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;. Можно доказать, что тогда имеется ровно &amp;lt;tex&amp;gt;(p + 1)&amp;lt;/tex&amp;gt; целочисленное решение уравнения&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;a_0^2 + a_1^2 + a_3^2 = p&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
такое, что &amp;lt;tex&amp;gt;a_0&amp;lt;/tex&amp;gt; положительно и нечётно, а &amp;lt;tex&amp;gt;a_1&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;a_2&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;a_3&amp;lt;/tex&amp;gt; чётны. Каждой такой четвёрке сопоставим матрицу&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;A = \begin{pmatrix} a_0 + ia_1 &amp;amp; a_2 + ia_3\\ -a_2 + ia_3 &amp;amp; a_0 - ia_1 \end{pmatrix}&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Эти матрицы образуют множество S.&lt;br /&gt;
Нетрудно понять, что граф Кэли &amp;lt;tex&amp;gt;(G, S)&amp;lt;/tex&amp;gt; состоит из &amp;lt;tex&amp;gt;\Theta(q^3)&amp;lt;/tex&amp;gt; вершин, и степень каждой вершины равна &amp;lt;tex&amp;gt;(p + 1)&amp;lt;/tex&amp;gt;. Свойства данного графа зависят от соотношения &amp;lt;tex&amp;gt;p&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;. Рассмотрим случай, когда p является квадратичным вычетом по модулю &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;. Тогда полученный граф Кэли состоит из двух связных компонент (поскольку все матрицы из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; лежат в подгруппе &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; индекса два — подгруппе матриц, определитель которых является квадратичным вычетом). Обозначим &amp;lt;tex&amp;gt;X^{p,q}&amp;lt;/tex&amp;gt; связную компоненту полученного графа. Можно доказать, что у &amp;lt;tex&amp;gt;X^{p,q}&amp;lt;/tex&amp;gt; второе по абсолютной величине собственное число не превосходит &amp;lt;tex&amp;gt;2{\sqrt{p}}&amp;lt;/tex&amp;gt;, т.е. мы получили граф Рамануджана.&lt;br /&gt;
&lt;br /&gt;
==Применение экспандеров==&lt;br /&gt;
===Коды, исправляющие ошибки===&lt;br /&gt;
С помощью расширяющего графа можно посторить линейный код, позволяющий исправлять ошибки в доле &amp;lt;tex&amp;gt;\delta = 1/(2000d)&amp;lt;/tex&amp;gt; битов. Чтобы задать линейный код с длиной кодового слова n, достаточно описать его проверочную матрицу &amp;lt;tex&amp;gt;H&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;(&amp;lt;/tex&amp;gt;слово &amp;lt;tex&amp;gt;x \in \{0, 1\}^n&amp;lt;/tex&amp;gt; является кодовым словом если и только если &amp;lt;tex&amp;gt;Hx^t = 0)&amp;lt;/tex&amp;gt;. Другими словами, нужно задать систему линейных уравнений для переменных &amp;lt;tex&amp;gt;x_1, . . . , x_n;&amp;lt;/tex&amp;gt; решения этой системы и будут кодовыми словами.&lt;br /&gt;
===Увеличение вероятности успеха в алгоритмах с датчиком случайных чисел===&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=Язык &amp;lt;tex&amp;gt;L&amp;lt;/tex&amp;gt; принадлежит сложностному классу &amp;lt;tex&amp;gt;RP&amp;lt;/tex&amp;gt;, если существует полиномиальный алгоритм &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; такой что&lt;br /&gt;
1. для &amp;lt;tex&amp;gt;x \in L&amp;lt;/tex&amp;gt; для всех &amp;lt;tex&amp;gt;r \in \{0, 1\}^{poly(n)}&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;A(x, r) = 1&amp;lt;/tex&amp;gt;&lt;br /&gt;
2. для &amp;lt;tex&amp;gt;x \notin L&amp;lt;/tex&amp;gt; не более чем для 1/2000 всех &amp;lt;tex&amp;gt;r \in \{0, 1\}^{poly(n)}&amp;lt;/tex&amp;gt; может выполняться &amp;lt;tex&amp;gt;A(x, r) = 1&amp;lt;/tex&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Покажем, что для любого &amp;lt;tex&amp;gt;\epsilon &amp;gt; 0&amp;lt;/tex&amp;gt; полиномиальный вероятностный алгоритм A можно модифицировать таким образом, чтобы вероятность ошибки уменьшилась до &amp;lt;tex&amp;gt;\epsilon&amp;lt;/tex&amp;gt;, а число используемых случайных битов не изменится.&lt;br /&gt;
&lt;br /&gt;
Пусть исходный алгоритм использует &amp;lt;tex&amp;gt;k = k(n)&amp;lt;/tex&amp;gt; случайных битов для вычислений на входах длины &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;. Зафиксируем &amp;lt;tex&amp;gt;(2^k, 2^k, d)&amp;lt;/tex&amp;gt; - экспандер &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;d &amp;gt; 1/\epsilon&amp;lt;/tex&amp;gt;. Новый алгоритм действует следующим образом: выбирается случайная вершина &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; из левой доли графа (для этого требуется &amp;lt;tex&amp;gt;k&amp;lt;/tex&amp;gt; случайных битов); затем исходный алгоритм &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; последовательно запускается на всех &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt; наборах случайных битов, соответствующих соседям вершины &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;. Если все полученные ответы равны &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;, новый алгоритм также возвращает единицу; в противном случае возвращается ноль.&lt;br /&gt;
Покажем, что у нового алгоритма вероятность ошибки не превосходит &amp;lt;tex&amp;gt;1/d&amp;lt;/tex&amp;gt;. В самом деле, обозначим &amp;lt;tex&amp;gt;B = B(x)&amp;lt;/tex&amp;gt; множество таких вершин &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt; из правой доли графа, которые соответствуют неверному ответу старого алгоритма на входе &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;; аналогично, обозначим &amp;lt;tex&amp;gt;S = S(x)&amp;lt;/tex&amp;gt; множество таких вершин v из левой доли графа, которые для которых новый алгоритм даёт неверный ответ на входе &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;. Очевидно, &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; состоит из вершин, все соседи которых лежат в B. Предположим, что &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; содержит не менее &amp;lt;tex&amp;gt;n/(1000d)&amp;lt;/tex&amp;gt; вершин. Выберем среди них ровно &amp;lt;tex&amp;gt;n/(1000d)&amp;lt;/tex&amp;gt; вершин и назовём это множество &amp;lt;tex&amp;gt;S'&amp;lt;/tex&amp;gt;. По свойству экспандера, имеем&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;|\Gamma(S')| \geqslant  \frac{7}{8}d\frac{n}{1000d}=7n/8000 &amp;gt; n/2000&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Это противоречит тому, что все соседи &amp;lt;tex&amp;gt;S'&amp;lt;/tex&amp;gt; лежат в &amp;lt;tex&amp;gt;B&amp;lt;/tex&amp;gt;. В данном случае нам нужна явная в более сильном (чем в первом примере) смысле конструкция экспандера. Размер графа экспоненциално растёт с увеличенем &amp;lt;tex&amp;gt;k&amp;lt;/tex&amp;gt;, и нам необходим алгоритм, который по заданному номеру вершины &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; (из левой доли графа) за время &amp;lt;tex&amp;gt;poly(k)&amp;lt;/tex&amp;gt; находит список номеров всех соседей этой вершины (в правой доле графа).&lt;br /&gt;
&lt;br /&gt;
===Хранение множества со сверхбыстрым запросом элементов===&lt;br /&gt;
&lt;br /&gt;
==Приложения и полезные свойства==&lt;br /&gt;
Первоначально интерес к экспандерам возник с целью построения устойчивой сети (телефонов или компьютеров) — число дуг графов расширения с ограниченным значением регулярности растет линейно по отношению к числу вершин.&lt;br /&gt;
&lt;br /&gt;
Экспандеры нашли широкое применение в теории вычислительных машин и систем, для построения алгоритмов, в корректирующих кодах, экстракторах, генераторах псевдослучайных чисел, сетях сортировки и компьютерных сетях. Они также используются для доказательства многих важных результатов в теории вычислительной сложности, таких как &amp;lt;tex&amp;gt;SL=L&amp;lt;/tex&amp;gt; и Теорема PCP. В криптографии экспандеры используются для создания хеш-функций.&lt;br /&gt;
&lt;br /&gt;
Ниже приведены некоторые свойства экспандеров, считающиеся полезными во многих областях.&lt;br /&gt;
===Лемма о перемешивании===&lt;br /&gt;
Лемма о перемешивании утверждает, что для любых двух подмножеств вершин &amp;lt;tex&amp;gt;S,T\subseteq V&amp;lt;/tex&amp;gt; число рёбер между &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; примерно равно числу рёбер в случайном &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt;-регулярном графе. Приближение тем лучше, чем меньше &amp;lt;tex&amp;gt;\lambda =\max\{|\lambda _{2}|&amp;lt;/tex&amp;gt;,&amp;lt;tex&amp;gt;|\lambda _{n}|\}&amp;lt;/tex&amp;gt;. В случайном &amp;lt;tex&amp;gt;d&amp;lt;tex/&amp;gt;-регулярном графе, также как и в случайном графе Эрдёша — Реньи с вероятностью &amp;lt;tex&amp;gt;{\tfrac {d}{n}}&amp;lt;/tex&amp;gt; выбора ребра, ожидается &amp;lt;tex&amp;gt;{\tfrac {d}{n}}\cdot |S|\cdot |T|&amp;lt;/tex&amp;gt; рёбер между S и T.&lt;br /&gt;
&lt;br /&gt;
Более формально, пусть &amp;lt;tex&amp;gt;E(S, T)&amp;lt;/tex&amp;gt; обозначает число рёбер между &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;. Если эти два множества пересекаются, дуги в пересечении считаются дважды, так что&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;E(S,T)=2|E(G[S\cap T])|+E(S\setminus T,T)+E(S,T\setminus S)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
Лемма о перемешивании утверждает, что&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\left|E(S,T)-{\frac {d\cdot |S|\cdot |T|}{n}}\right|\leq d\lambda {\sqrt {|S|\cdot |T|}}&amp;lt;/tex&amp;gt;,&lt;br /&gt;
где &amp;lt;tex&amp;gt;\lambda &amp;lt;/tex&amp;gt; — абсолютное значение нормализованного второго по величине собственного значения.&lt;br /&gt;
&lt;br /&gt;
Недавно Билу (Bilu) и Линайл (Linial) показали, что обратное тоже верно, то есть, при условии выполнения неравенства из леммы второе по величине собственное значение равно &amp;lt;tex&amp;gt;O(d\lambda \cdot (1+\log(1/\lambda )))&amp;lt;/tex&amp;gt;.&lt;br /&gt;
===Блуждания по экспандеру===&lt;br /&gt;
Согласно границе Чернова, если выбирать много независимых случайных значений из интервала &amp;lt;tex&amp;gt;[−1, 1]&amp;lt;/tex&amp;gt;, с большой степенью вероятности среднее выбранных значений будет близко к математическому ожиданию случайной переменной. Лемма о блуждании по экспандеру, согласно статьям Аджтари, Комлоша и Семереди и Гилмана, утверждает, что то же самое верно и для блужданий по экспандеру. Это полезно в теории дерандомизации, поскольку блуждание по экспандеру использует много меньше случайных бит, чем случайная независимая выборка.&lt;br /&gt;
&lt;br /&gt;
==Источники информации==&lt;br /&gt;
*[https://ru.wikipedia.org/wiki/%D0%AD%D0%BA%D1%81%D0%BF%D0%B0%D0%BD%D0%B4%D0%B5%D1%80_(%D1%82%D0%B5%D0%BE%D1%80%D0%B8%D1%8F_%D0%B3%D1%80%D0%B0%D1%84%D0%BE%D0%B2) Экспандер (теория графов)]&lt;/div&gt;</summary>
		<author><name>Kozichuk</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%93%D1%80%D0%B0%D1%84%D1%8B-%D1%8D%D0%BA%D1%81%D0%BF%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%8B&amp;diff=50929</id>
		<title>Графы-экспандеры</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%93%D1%80%D0%B0%D1%84%D1%8B-%D1%8D%D0%BA%D1%81%D0%BF%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%8B&amp;diff=50929"/>
				<updated>2016-01-09T01:08:00Z</updated>
		
		<summary type="html">&lt;p&gt;Kozichuk: /* Применение экспандеров */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Граф-экспандер''' (или расширяющийся граф, англ. ''expander graph'') - в комбинаторике сильно разреженный граф, при этом связность определяется по вершинам, дугам или спектру.&lt;br /&gt;
&lt;br /&gt;
==Определение==&lt;br /&gt;
''Граф-экспандер'' — это конечный ненаправленный ''мультиграф'', в котором любое подмножество вершин, не являясь «слишком большим», имеет «сильную» связность. Различные формализации этих понятий дают различные типы экспандеров: ''рёберный расширитель'', ''вершинный расширитель'', и ''спектральный расширитель''.&lt;br /&gt;
&lt;br /&gt;
Несвязный граф не является экспандером. Любой связный граф является экспандером, однако различные связные графы имеют различные параметры расширителя. Полный граф имеет лучшие параметры расширителя, но имеет наибольшую возможную степень. Неформально говоря, граф является хорошим экспандером, если он имеет низкую степень и высокий параметр расширителя.&lt;br /&gt;
&lt;br /&gt;
===Реберное расширение===&lt;br /&gt;
''Рёберное расширение'' (также ''изопериметрическое число'' или константа Чигера) &amp;lt;tex&amp;gt;h(G)&amp;lt;/tex&amp;gt; графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; для &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; вершин определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;h(G) = \min_{0 &amp;lt; |S|\le \frac{n}{2}} \frac{|\partial_{\text{out}}(S)|}{|S|}&amp;lt;/tex&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где минимум берётся по всем непустым множествам &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; не более чем &amp;lt;tex&amp;gt;n/2&amp;lt;/tex&amp;gt; вершин и &amp;lt;tex&amp;gt;\partial(S)&amp;lt;/tex&amp;gt; — ''граничные дуги'' множества &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, то есть, множество дуг с единственной вершиной в &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Вершинное расширение===&lt;br /&gt;
''Вершинное изопериметрическое число'' &amp;lt;tex&amp;gt;h_{out}(G)&amp;lt;/tex&amp;gt; (также ''вершинное раширение'') графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;h_{out}(G) = \min_{0 &amp;lt; |S|\le \frac{n}{2}} \frac{|\partial_{\text{out}}(S)|}{|S|},&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;tex&amp;gt;\partial_{\text{out}}(S)&amp;lt;/tex&amp;gt; — ''внешняя граница'' &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, то есть множество вершин из &amp;lt;tex&amp;gt;V(G)\setminus S&amp;lt;/tex&amp;gt;, имеющих как минимум одного соседа в S. В варианте этого определения (называемом ''уникальным соседним расширением'') &amp;lt;tex&amp;gt;\partial_{\text{out}}(S)&amp;lt;/tex&amp;gt; заменяется на множество вершин из &amp;lt;tex&amp;gt;V&amp;lt;/tex&amp;gt; с ''точностью одним'' соседом из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
''Вершинное изопериметрическое число'' &amp;lt;tex&amp;gt;h_{in}(G)&amp;lt;/tex&amp;gt; графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;h_{in}(G) = \min_{0 &amp;lt; |S|\le \frac{n}{2}} \frac{|\partial_{\text{in}}(S)|}{|S|},&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;tex&amp;gt;∂_{in}(S)&amp;lt;/tex&amp;gt; — ''внутренняя граница'' &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, то есть множество вершин &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, имеющих как минимум одного соседа в &amp;lt;tex&amp;gt;V(G)\setminus S&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Спектральное расширение===&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; является d-регулярным, возможно определение в терминах линейной алгебры на основе собственных значений матрицы смежности &amp;lt;tex&amp;gt;A = A(G)&amp;lt;/tex&amp;gt; графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;A_{ij}&amp;lt;/tex&amp;gt; — число дуг между вершинами &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;. Поскольку &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; является симметричной, согласно спектральной теореме, &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; имеет &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; действительных собственных значений &amp;lt;tex&amp;gt;\lambda_1 \ge \lambda_2 \ge \cdots \ge \lambda_{n}&amp;lt;/tex&amp;gt;. Известно, что эти значения лежат в промежутке &amp;lt;tex&amp;gt;[−d, d]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
Граф регулярен тогда и только тогда, когда вектор &amp;lt;tex&amp;gt;u\in \mathbb {R} ^{n} с u_{i}=1&amp;lt;/tex&amp;gt; для всех &amp;lt;tex&amp;gt;i = 1, …, n&amp;lt;/tex&amp;gt; является собственным вектором матрицы &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt;, а его собственное число будет постоянной степенью графа. Таким образом, мы имеем &amp;lt;tex&amp;gt;Au = du&amp;lt;/tex&amp;gt;, и &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt; — собственный вектор матрицы &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; с собственными значениями &amp;lt;tex&amp;gt;λ1 = d&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt; — степень вершин графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt;. Спектральный зазор графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; определяется как &amp;lt;tex&amp;gt;d−λ2&amp;lt;/tex&amp;gt; и является мерилом спектрального расширения графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Известно, что &amp;lt;tex&amp;gt;\lambda_n = −d&amp;lt;/tex&amp;gt; тогда и только тогда, когда &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; — двудольный. Во многих случаях, например в лемме о перемешивании, необходимо ограничить снизу не только зазор между &amp;lt;tex&amp;gt;\lambda_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\lambda_2&amp;lt;/tex&amp;gt;, но и зазор между &amp;lt;tex&amp;gt;\lambda_1&amp;lt;/tex&amp;gt; и вторым максимальным по модулю собственным значением:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\lambda=\max\{|\lambda_2|, |\lambda_{n}|\}&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Поскольку это собственное значение соответствует некоторому собственному вектору, ортогональному &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;, его можно определить, используя отношение Рэлея:&lt;br /&gt;
&amp;lt;tex&amp;gt;\lambda=\max_{0\neq v\perp u} \frac{\|Av\|_2}{\|v\|_2},&amp;lt;/tex&amp;gt;&lt;br /&gt;
gde&lt;br /&gt;
&amp;lt;tex&amp;gt;\|v\|_2=\left(\sum_{i=1}^n v_i^2\right)^{1/2}&amp;lt;/tex&amp;gt;&lt;br /&gt;
— евклидова норма вектора &amp;lt;tex&amp;gt;v\in \mathbb {R} ^{n}&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Нормализованная версия этого определения также широко используется и более удобна для получения определённых результатов. В таком случае рассматривается матрица &amp;lt;tex&amp;gt;{\tfrac {1}{d}}A&amp;lt;/tex&amp;gt;, являющаяся матрицей переходов графа G. Все её собственные значения лежат между &amp;lt;tex&amp;gt;−1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;. Если граф не регулярен, спектр графа может быть определён аналогичным образом, используя собственные значения матрицы Кирхгофа. Для направленного графа используются сингулярные значения матрицы сопряжения A, которые равны квадратным корням из собственных значений симметричной матрицы &amp;lt;tex&amp;gt;A^TA&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Конструирование==&lt;br /&gt;
Существуют три основные стратегии создания семейств графов расширений[6]. Первая стратегия — алгебраическая и теоретико-групповая, вторая — аналитическая, использующая аддитивную комбинаторику, и третья — комбинаторная, использующая зигзаг-произведение и связанные комбинаторные произведения.&lt;br /&gt;
===Маргулис-Габбер-Галил===&lt;br /&gt;
Алгебраическое конструирование, основанное на графах Кэли, известно для различных вариантов экспандеров. Следующее конструирование принадлежит Маргулису и было проанализировано Габбером (Gabber) и Галилом (Galil). Для любого натурального &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; строим граф, &amp;lt;tex&amp;gt;G_{n}&amp;lt;/tex&amp;gt; со множеством вершин &amp;lt;tex&amp;gt;\mathbb Z _{n}\times \mathbb {Z} _{n}&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;\mathbb {Z} _{n}=\mathbb Z /n \mathbb Z&amp;lt;/tex&amp;gt; . Для любой вершины &amp;lt;tex&amp;gt;(x,y)\in \mathbb {Z} _{n}\times \mathbb {Z} _{n}&amp;lt;/tex&amp;gt;, её восемь соседей будут&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;(x \pm 2y,y), (x \pm (2y+1),y), (x,y \pm 2x), (x,y \pm (2x+1)).&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Выполняется следующая теорема:&lt;br /&gt;
{{Теорема&lt;br /&gt;
|statement=&lt;br /&gt;
Для всех &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; граф &amp;lt;tex&amp;gt;Gn&amp;lt;/tex&amp;gt; второе по величине собственное число&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;tex&amp;gt;\lambda(G)\leq 5 \sqrt{2}&amp;lt;/tex&amp;gt;.}}&lt;br /&gt;
&lt;br /&gt;
===Граф Рамануджана===&lt;br /&gt;
&amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt;-регулярный граф называется графом Рамануджана, если его второе по модулю собственное число не превосходит &amp;lt;tex&amp;gt;2{\sqrt {d-1}}&amp;lt;/tex&amp;gt;. Любоцкий, Сарнак, Филлипс и Маргулис указали явную конструкцию графов Кэли, являющихся графами Рамануджана. Опишем эту конструкцию.&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt;p&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; простые числа, &amp;lt;tex&amp;gt;p = 1&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;mod&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;4&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;q = 1&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;mod&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;4&amp;lt;/tex&amp;gt;. В качестве группы &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; возьмём &amp;lt;tex&amp;gt;PGL(2, \mathbb Z/q{\mathbb Z})&amp;lt;/tex&amp;gt;, т.е. невырожденные матрицы 2 × 2 над полем вычетов по модулю &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;, профакторизованные по отношению пропорциональности (с обычной операцией матричного умножения).&lt;br /&gt;
Далее мы зададим в этой группе симметричное множество &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;. Выберем такое целое &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;i^2 = −1&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;mod&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;. Можно доказать, что тогда имеется ровно &amp;lt;tex&amp;gt;(p + 1)&amp;lt;/tex&amp;gt; целочисленное решение уравнения&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;a_0^2 + a_1^2 + a_3^2 = p&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
такое, что &amp;lt;tex&amp;gt;a_0&amp;lt;/tex&amp;gt; положительно и нечётно, а &amp;lt;tex&amp;gt;a_1&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;a_2&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;a_3&amp;lt;/tex&amp;gt; чётны. Каждой такой четвёрке сопоставим матрицу&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;A = \begin{pmatrix} a_0 + ia_1 &amp;amp; a_2 + ia_3\\ -a_2 + ia_3 &amp;amp; a_0 - ia_1 \end{pmatrix}&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Эти матрицы образуют множество S.&lt;br /&gt;
Нетрудно понять, что граф Кэли &amp;lt;tex&amp;gt;(G, S)&amp;lt;/tex&amp;gt; состоит из &amp;lt;tex&amp;gt;\Theta(q^3)&amp;lt;/tex&amp;gt; вершин, и степень каждой вершины равна &amp;lt;tex&amp;gt;(p + 1)&amp;lt;/tex&amp;gt;. Свойства данного графа зависят от соотношения &amp;lt;tex&amp;gt;p&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;. Рассмотрим случай, когда p является квадратичным вычетом по модулю &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;. Тогда полученный граф Кэли состоит из двух связных компонент (поскольку все матрицы из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; лежат в подгруппе &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; индекса два — подгруппе матриц, определитель которых является квадратичным вычетом). Обозначим &amp;lt;tex&amp;gt;X^{p,q}&amp;lt;/tex&amp;gt; связную компоненту полученного графа. Можно доказать, что у &amp;lt;tex&amp;gt;X^{p,q}&amp;lt;/tex&amp;gt; второе по абсолютной величине собственное число не превосходит &amp;lt;tex&amp;gt;2{\sqrt{p}}&amp;lt;/tex&amp;gt;, т.е. мы получили граф Рамануджана.&lt;br /&gt;
&lt;br /&gt;
==Применение экспандеров==&lt;br /&gt;
===Коды, исправляющие ошибки===&lt;br /&gt;
С помощью расширяющего графа можно посторить линейный код, позволяющий исправлять ошибки в доле &amp;lt;tex&amp;gt;\delta = 1/(2000d)&amp;lt;/tex&amp;gt; битов. Чтобы задать линейный код с длиной кодового слова n, достаточно описать его проверочную матрицу &amp;lt;tex&amp;gt;H&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;(&amp;lt;/tex&amp;gt;слово &amp;lt;tex&amp;gt;x \in \{0, 1\}^n&amp;lt;/tex&amp;gt; является кодовым словом если и только если &amp;lt;tex&amp;gt;Hx^t = 0)&amp;lt;/tex&amp;gt;. Другими словами, нужно задать систему линейных уравнений для переменных &amp;lt;tex&amp;gt;x_1, . . . , x_n;&amp;lt;/tex&amp;gt; решения этой системы и будут кодовыми словами.&lt;br /&gt;
===Увеличение вероятности успеха в алгоритмах с датчиком случайных чисел===&lt;br /&gt;
&lt;br /&gt;
===Хранение множества со сверхбыстрым запросом элементов===&lt;br /&gt;
&lt;br /&gt;
==Приложения и полезные свойства==&lt;br /&gt;
Первоначально интерес к экспандерам возник с целью построения устойчивой сети (телефонов или компьютеров) — число дуг графов расширения с ограниченным значением регулярности растет линейно по отношению к числу вершин.&lt;br /&gt;
&lt;br /&gt;
Экспандеры нашли широкое применение в теории вычислительных машин и систем, для построения алгоритмов, в корректирующих кодах, экстракторах, генераторах псевдослучайных чисел, сетях сортировки и компьютерных сетях. Они также используются для доказательства многих важных результатов в теории вычислительной сложности, таких как &amp;lt;tex&amp;gt;SL=L&amp;lt;/tex&amp;gt; и Теорема PCP. В криптографии экспандеры используются для создания хеш-функций.&lt;br /&gt;
&lt;br /&gt;
Ниже приведены некоторые свойства экспандеров, считающиеся полезными во многих областях.&lt;br /&gt;
===Лемма о перемешивании===&lt;br /&gt;
Лемма о перемешивании утверждает, что для любых двух подмножеств вершин &amp;lt;tex&amp;gt;S,T\subseteq V&amp;lt;/tex&amp;gt; число рёбер между &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; примерно равно числу рёбер в случайном &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt;-регулярном графе. Приближение тем лучше, чем меньше &amp;lt;tex&amp;gt;\lambda =\max\{|\lambda _{2}|&amp;lt;/tex&amp;gt;,&amp;lt;tex&amp;gt;|\lambda _{n}|\}&amp;lt;/tex&amp;gt;. В случайном &amp;lt;tex&amp;gt;d&amp;lt;tex/&amp;gt;-регулярном графе, также как и в случайном графе Эрдёша — Реньи с вероятностью &amp;lt;tex&amp;gt;{\tfrac {d}{n}}&amp;lt;/tex&amp;gt; выбора ребра, ожидается &amp;lt;tex&amp;gt;{\tfrac {d}{n}}\cdot |S|\cdot |T|&amp;lt;/tex&amp;gt; рёбер между S и T.&lt;br /&gt;
&lt;br /&gt;
Более формально, пусть &amp;lt;tex&amp;gt;E(S, T)&amp;lt;/tex&amp;gt; обозначает число рёбер между &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;. Если эти два множества пересекаются, дуги в пересечении считаются дважды, так что&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;E(S,T)=2|E(G[S\cap T])|+E(S\setminus T,T)+E(S,T\setminus S)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
Лемма о перемешивании утверждает, что&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\left|E(S,T)-{\frac {d\cdot |S|\cdot |T|}{n}}\right|\leq d\lambda {\sqrt {|S|\cdot |T|}}&amp;lt;/tex&amp;gt;,&lt;br /&gt;
где &amp;lt;tex&amp;gt;\lambda &amp;lt;/tex&amp;gt; — абсолютное значение нормализованного второго по величине собственного значения.&lt;br /&gt;
&lt;br /&gt;
Недавно Билу (Bilu) и Линайл (Linial) показали, что обратное тоже верно, то есть, при условии выполнения неравенства из леммы второе по величине собственное значение равно &amp;lt;tex&amp;gt;O(d\lambda \cdot (1+\log(1/\lambda )))&amp;lt;/tex&amp;gt;.&lt;br /&gt;
===Блуждания по экспандеру===&lt;br /&gt;
Согласно границе Чернова, если выбирать много независимых случайных значений из интервала &amp;lt;tex&amp;gt;[−1, 1]&amp;lt;/tex&amp;gt;, с большой степенью вероятности среднее выбранных значений будет близко к математическому ожиданию случайной переменной. Лемма о блуждании по экспандеру, согласно статьям Аджтари, Комлоша и Семереди и Гилмана, утверждает, что то же самое верно и для блужданий по экспандеру. Это полезно в теории дерандомизации, поскольку блуждание по экспандеру использует много меньше случайных бит, чем случайная независимая выборка.&lt;br /&gt;
&lt;br /&gt;
==Источники информации==&lt;br /&gt;
*[https://ru.wikipedia.org/wiki/%D0%AD%D0%BA%D1%81%D0%BF%D0%B0%D0%BD%D0%B4%D0%B5%D1%80_(%D1%82%D0%B5%D0%BE%D1%80%D0%B8%D1%8F_%D0%B3%D1%80%D0%B0%D1%84%D0%BE%D0%B2) Экспандер (теория графов)]&lt;/div&gt;</summary>
		<author><name>Kozichuk</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%93%D1%80%D0%B0%D1%84%D1%8B-%D1%8D%D0%BA%D1%81%D0%BF%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%8B&amp;diff=50928</id>
		<title>Графы-экспандеры</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%93%D1%80%D0%B0%D1%84%D1%8B-%D1%8D%D0%BA%D1%81%D0%BF%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%8B&amp;diff=50928"/>
				<updated>2016-01-09T01:06:49Z</updated>
		
		<summary type="html">&lt;p&gt;Kozichuk: /* Применение экспандеров */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Граф-экспандер''' (или расширяющийся граф, англ. ''expander graph'') - в комбинаторике сильно разреженный граф, при этом связность определяется по вершинам, дугам или спектру.&lt;br /&gt;
&lt;br /&gt;
==Определение==&lt;br /&gt;
''Граф-экспандер'' — это конечный ненаправленный ''мультиграф'', в котором любое подмножество вершин, не являясь «слишком большим», имеет «сильную» связность. Различные формализации этих понятий дают различные типы экспандеров: ''рёберный расширитель'', ''вершинный расширитель'', и ''спектральный расширитель''.&lt;br /&gt;
&lt;br /&gt;
Несвязный граф не является экспандером. Любой связный граф является экспандером, однако различные связные графы имеют различные параметры расширителя. Полный граф имеет лучшие параметры расширителя, но имеет наибольшую возможную степень. Неформально говоря, граф является хорошим экспандером, если он имеет низкую степень и высокий параметр расширителя.&lt;br /&gt;
&lt;br /&gt;
===Реберное расширение===&lt;br /&gt;
''Рёберное расширение'' (также ''изопериметрическое число'' или константа Чигера) &amp;lt;tex&amp;gt;h(G)&amp;lt;/tex&amp;gt; графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; для &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; вершин определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;h(G) = \min_{0 &amp;lt; |S|\le \frac{n}{2}} \frac{|\partial_{\text{out}}(S)|}{|S|}&amp;lt;/tex&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где минимум берётся по всем непустым множествам &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; не более чем &amp;lt;tex&amp;gt;n/2&amp;lt;/tex&amp;gt; вершин и &amp;lt;tex&amp;gt;\partial(S)&amp;lt;/tex&amp;gt; — ''граничные дуги'' множества &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, то есть, множество дуг с единственной вершиной в &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Вершинное расширение===&lt;br /&gt;
''Вершинное изопериметрическое число'' &amp;lt;tex&amp;gt;h_{out}(G)&amp;lt;/tex&amp;gt; (также ''вершинное раширение'') графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;h_{out}(G) = \min_{0 &amp;lt; |S|\le \frac{n}{2}} \frac{|\partial_{\text{out}}(S)|}{|S|},&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;tex&amp;gt;\partial_{\text{out}}(S)&amp;lt;/tex&amp;gt; — ''внешняя граница'' &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, то есть множество вершин из &amp;lt;tex&amp;gt;V(G)\setminus S&amp;lt;/tex&amp;gt;, имеющих как минимум одного соседа в S. В варианте этого определения (называемом ''уникальным соседним расширением'') &amp;lt;tex&amp;gt;\partial_{\text{out}}(S)&amp;lt;/tex&amp;gt; заменяется на множество вершин из &amp;lt;tex&amp;gt;V&amp;lt;/tex&amp;gt; с ''точностью одним'' соседом из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
''Вершинное изопериметрическое число'' &amp;lt;tex&amp;gt;h_{in}(G)&amp;lt;/tex&amp;gt; графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;h_{in}(G) = \min_{0 &amp;lt; |S|\le \frac{n}{2}} \frac{|\partial_{\text{in}}(S)|}{|S|},&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;tex&amp;gt;∂_{in}(S)&amp;lt;/tex&amp;gt; — ''внутренняя граница'' &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, то есть множество вершин &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, имеющих как минимум одного соседа в &amp;lt;tex&amp;gt;V(G)\setminus S&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Спектральное расширение===&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; является d-регулярным, возможно определение в терминах линейной алгебры на основе собственных значений матрицы смежности &amp;lt;tex&amp;gt;A = A(G)&amp;lt;/tex&amp;gt; графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;A_{ij}&amp;lt;/tex&amp;gt; — число дуг между вершинами &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;. Поскольку &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; является симметричной, согласно спектральной теореме, &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; имеет &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; действительных собственных значений &amp;lt;tex&amp;gt;\lambda_1 \ge \lambda_2 \ge \cdots \ge \lambda_{n}&amp;lt;/tex&amp;gt;. Известно, что эти значения лежат в промежутке &amp;lt;tex&amp;gt;[−d, d]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
Граф регулярен тогда и только тогда, когда вектор &amp;lt;tex&amp;gt;u\in \mathbb {R} ^{n} с u_{i}=1&amp;lt;/tex&amp;gt; для всех &amp;lt;tex&amp;gt;i = 1, …, n&amp;lt;/tex&amp;gt; является собственным вектором матрицы &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt;, а его собственное число будет постоянной степенью графа. Таким образом, мы имеем &amp;lt;tex&amp;gt;Au = du&amp;lt;/tex&amp;gt;, и &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt; — собственный вектор матрицы &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; с собственными значениями &amp;lt;tex&amp;gt;λ1 = d&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt; — степень вершин графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt;. Спектральный зазор графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; определяется как &amp;lt;tex&amp;gt;d−λ2&amp;lt;/tex&amp;gt; и является мерилом спектрального расширения графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Известно, что &amp;lt;tex&amp;gt;\lambda_n = −d&amp;lt;/tex&amp;gt; тогда и только тогда, когда &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; — двудольный. Во многих случаях, например в лемме о перемешивании, необходимо ограничить снизу не только зазор между &amp;lt;tex&amp;gt;\lambda_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\lambda_2&amp;lt;/tex&amp;gt;, но и зазор между &amp;lt;tex&amp;gt;\lambda_1&amp;lt;/tex&amp;gt; и вторым максимальным по модулю собственным значением:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\lambda=\max\{|\lambda_2|, |\lambda_{n}|\}&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Поскольку это собственное значение соответствует некоторому собственному вектору, ортогональному &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;, его можно определить, используя отношение Рэлея:&lt;br /&gt;
&amp;lt;tex&amp;gt;\lambda=\max_{0\neq v\perp u} \frac{\|Av\|_2}{\|v\|_2},&amp;lt;/tex&amp;gt;&lt;br /&gt;
gde&lt;br /&gt;
&amp;lt;tex&amp;gt;\|v\|_2=\left(\sum_{i=1}^n v_i^2\right)^{1/2}&amp;lt;/tex&amp;gt;&lt;br /&gt;
— евклидова норма вектора &amp;lt;tex&amp;gt;v\in \mathbb {R} ^{n}&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Нормализованная версия этого определения также широко используется и более удобна для получения определённых результатов. В таком случае рассматривается матрица &amp;lt;tex&amp;gt;{\tfrac {1}{d}}A&amp;lt;/tex&amp;gt;, являющаяся матрицей переходов графа G. Все её собственные значения лежат между &amp;lt;tex&amp;gt;−1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;. Если граф не регулярен, спектр графа может быть определён аналогичным образом, используя собственные значения матрицы Кирхгофа. Для направленного графа используются сингулярные значения матрицы сопряжения A, которые равны квадратным корням из собственных значений симметричной матрицы &amp;lt;tex&amp;gt;A^TA&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Конструирование==&lt;br /&gt;
Существуют три основные стратегии создания семейств графов расширений[6]. Первая стратегия — алгебраическая и теоретико-групповая, вторая — аналитическая, использующая аддитивную комбинаторику, и третья — комбинаторная, использующая зигзаг-произведение и связанные комбинаторные произведения.&lt;br /&gt;
===Маргулис-Габбер-Галил===&lt;br /&gt;
Алгебраическое конструирование, основанное на графах Кэли, известно для различных вариантов экспандеров. Следующее конструирование принадлежит Маргулису и было проанализировано Габбером (Gabber) и Галилом (Galil). Для любого натурального &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; строим граф, &amp;lt;tex&amp;gt;G_{n}&amp;lt;/tex&amp;gt; со множеством вершин &amp;lt;tex&amp;gt;\mathbb Z _{n}\times \mathbb {Z} _{n}&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;\mathbb {Z} _{n}=\mathbb Z /n \mathbb Z&amp;lt;/tex&amp;gt; . Для любой вершины &amp;lt;tex&amp;gt;(x,y)\in \mathbb {Z} _{n}\times \mathbb {Z} _{n}&amp;lt;/tex&amp;gt;, её восемь соседей будут&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;(x \pm 2y,y), (x \pm (2y+1),y), (x,y \pm 2x), (x,y \pm (2x+1)).&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Выполняется следующая теорема:&lt;br /&gt;
{{Теорема&lt;br /&gt;
|statement=&lt;br /&gt;
Для всех &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; граф &amp;lt;tex&amp;gt;Gn&amp;lt;/tex&amp;gt; второе по величине собственное число&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;tex&amp;gt;\lambda(G)\leq 5 \sqrt{2}&amp;lt;/tex&amp;gt;.}}&lt;br /&gt;
&lt;br /&gt;
===Граф Рамануджана===&lt;br /&gt;
&amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt;-регулярный граф называется графом Рамануджана, если его второе по модулю собственное число не превосходит &amp;lt;tex&amp;gt;2{\sqrt {d-1}}&amp;lt;/tex&amp;gt;. Любоцкий, Сарнак, Филлипс и Маргулис указали явную конструкцию графов Кэли, являющихся графами Рамануджана. Опишем эту конструкцию.&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt;p&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; простые числа, &amp;lt;tex&amp;gt;p = 1&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;mod&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;4&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;q = 1&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;mod&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;4&amp;lt;/tex&amp;gt;. В качестве группы &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; возьмём &amp;lt;tex&amp;gt;PGL(2, \mathbb Z/q{\mathbb Z})&amp;lt;/tex&amp;gt;, т.е. невырожденные матрицы 2 × 2 над полем вычетов по модулю &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;, профакторизованные по отношению пропорциональности (с обычной операцией матричного умножения).&lt;br /&gt;
Далее мы зададим в этой группе симметричное множество &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;. Выберем такое целое &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;i^2 = −1&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;mod&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;. Можно доказать, что тогда имеется ровно &amp;lt;tex&amp;gt;(p + 1)&amp;lt;/tex&amp;gt; целочисленное решение уравнения&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;a_0^2 + a_1^2 + a_3^2 = p&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
такое, что &amp;lt;tex&amp;gt;a_0&amp;lt;/tex&amp;gt; положительно и нечётно, а &amp;lt;tex&amp;gt;a_1&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;a_2&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;a_3&amp;lt;/tex&amp;gt; чётны. Каждой такой четвёрке сопоставим матрицу&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;A = \begin{pmatrix} a_0 + ia_1 &amp;amp; a_2 + ia_3\\ -a_2 + ia_3 &amp;amp; a_0 - ia_1 \end{pmatrix}&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Эти матрицы образуют множество S.&lt;br /&gt;
Нетрудно понять, что граф Кэли &amp;lt;tex&amp;gt;(G, S)&amp;lt;/tex&amp;gt; состоит из &amp;lt;tex&amp;gt;\Theta(q^3)&amp;lt;/tex&amp;gt; вершин, и степень каждой вершины равна &amp;lt;tex&amp;gt;(p + 1)&amp;lt;/tex&amp;gt;. Свойства данного графа зависят от соотношения &amp;lt;tex&amp;gt;p&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;. Рассмотрим случай, когда p является квадратичным вычетом по модулю &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;. Тогда полученный граф Кэли состоит из двух связных компонент (поскольку все матрицы из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; лежат в подгруппе &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; индекса два — подгруппе матриц, определитель которых является квадратичным вычетом). Обозначим &amp;lt;tex&amp;gt;X^{p,q}&amp;lt;/tex&amp;gt; связную компоненту полученного графа. Можно доказать, что у &amp;lt;tex&amp;gt;X^{p,q}&amp;lt;/tex&amp;gt; второе по абсолютной величине собственное число не превосходит &amp;lt;tex&amp;gt;2{\sqrt{p}}&amp;lt;/tex&amp;gt;, т.е. мы получили граф Рамануджана.&lt;br /&gt;
&lt;br /&gt;
==Применение экспандеров==&lt;br /&gt;
===Коды, исправляющие ошибки===&lt;br /&gt;
С помощью расширяющего графа можно посторить линейный код, позволяющий исправлять ошибки в доле &amp;lt;tex&amp;gt;\delta = 1/(2000d)&amp;lt;/tex&amp;gt; битов. Чтобы задать линейный код с длиной кодового слова n, достаточно описать его проверочную матрицу &amp;lt;tex&amp;gt;H&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;(&amp;lt;/tex&amp;gt;слово &amp;lt;tex&amp;gt;x \in \{0, 1\}^n&amp;lt;/tex&amp;gt; является кодовым словом если и только если &amp;lt;tex&amp;gt;Hx^t = 0)&amp;lt;/tex&amp;gt;. Другими словами, нужно задать систему линейных уравнений для переменных &amp;lt;tex&amp;gt;x_1, . . . , x_n;&amp;lt;/tex&amp;gt; решения этой системы и будут кодовыми словами.&lt;br /&gt;
&lt;br /&gt;
==Приложения и полезные свойства==&lt;br /&gt;
Первоначально интерес к экспандерам возник с целью построения устойчивой сети (телефонов или компьютеров) — число дуг графов расширения с ограниченным значением регулярности растет линейно по отношению к числу вершин.&lt;br /&gt;
&lt;br /&gt;
Экспандеры нашли широкое применение в теории вычислительных машин и систем, для построения алгоритмов, в корректирующих кодах, экстракторах, генераторах псевдослучайных чисел, сетях сортировки и компьютерных сетях. Они также используются для доказательства многих важных результатов в теории вычислительной сложности, таких как &amp;lt;tex&amp;gt;SL=L&amp;lt;/tex&amp;gt; и Теорема PCP. В криптографии экспандеры используются для создания хеш-функций.&lt;br /&gt;
&lt;br /&gt;
Ниже приведены некоторые свойства экспандеров, считающиеся полезными во многих областях.&lt;br /&gt;
===Лемма о перемешивании===&lt;br /&gt;
Лемма о перемешивании утверждает, что для любых двух подмножеств вершин &amp;lt;tex&amp;gt;S,T\subseteq V&amp;lt;/tex&amp;gt; число рёбер между &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; примерно равно числу рёбер в случайном &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt;-регулярном графе. Приближение тем лучше, чем меньше &amp;lt;tex&amp;gt;\lambda =\max\{|\lambda _{2}|&amp;lt;/tex&amp;gt;,&amp;lt;tex&amp;gt;|\lambda _{n}|\}&amp;lt;/tex&amp;gt;. В случайном &amp;lt;tex&amp;gt;d&amp;lt;tex/&amp;gt;-регулярном графе, также как и в случайном графе Эрдёша — Реньи с вероятностью &amp;lt;tex&amp;gt;{\tfrac {d}{n}}&amp;lt;/tex&amp;gt; выбора ребра, ожидается &amp;lt;tex&amp;gt;{\tfrac {d}{n}}\cdot |S|\cdot |T|&amp;lt;/tex&amp;gt; рёбер между S и T.&lt;br /&gt;
&lt;br /&gt;
Более формально, пусть &amp;lt;tex&amp;gt;E(S, T)&amp;lt;/tex&amp;gt; обозначает число рёбер между &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;. Если эти два множества пересекаются, дуги в пересечении считаются дважды, так что&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;E(S,T)=2|E(G[S\cap T])|+E(S\setminus T,T)+E(S,T\setminus S)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
Лемма о перемешивании утверждает, что&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\left|E(S,T)-{\frac {d\cdot |S|\cdot |T|}{n}}\right|\leq d\lambda {\sqrt {|S|\cdot |T|}}&amp;lt;/tex&amp;gt;,&lt;br /&gt;
где &amp;lt;tex&amp;gt;\lambda &amp;lt;/tex&amp;gt; — абсолютное значение нормализованного второго по величине собственного значения.&lt;br /&gt;
&lt;br /&gt;
Недавно Билу (Bilu) и Линайл (Linial) показали, что обратное тоже верно, то есть, при условии выполнения неравенства из леммы второе по величине собственное значение равно &amp;lt;tex&amp;gt;O(d\lambda \cdot (1+\log(1/\lambda )))&amp;lt;/tex&amp;gt;.&lt;br /&gt;
===Блуждания по экспандеру===&lt;br /&gt;
Согласно границе Чернова, если выбирать много независимых случайных значений из интервала &amp;lt;tex&amp;gt;[−1, 1]&amp;lt;/tex&amp;gt;, с большой степенью вероятности среднее выбранных значений будет близко к математическому ожиданию случайной переменной. Лемма о блуждании по экспандеру, согласно статьям Аджтари, Комлоша и Семереди и Гилмана, утверждает, что то же самое верно и для блужданий по экспандеру. Это полезно в теории дерандомизации, поскольку блуждание по экспандеру использует много меньше случайных бит, чем случайная независимая выборка.&lt;br /&gt;
&lt;br /&gt;
==Источники информации==&lt;br /&gt;
*[https://ru.wikipedia.org/wiki/%D0%AD%D0%BA%D1%81%D0%BF%D0%B0%D0%BD%D0%B4%D0%B5%D1%80_(%D1%82%D0%B5%D0%BE%D1%80%D0%B8%D1%8F_%D0%B3%D1%80%D0%B0%D1%84%D0%BE%D0%B2) Экспандер (теория графов)]&lt;/div&gt;</summary>
		<author><name>Kozichuk</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%93%D1%80%D0%B0%D1%84%D1%8B-%D1%8D%D0%BA%D1%81%D0%BF%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%8B&amp;diff=50927</id>
		<title>Графы-экспандеры</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%93%D1%80%D0%B0%D1%84%D1%8B-%D1%8D%D0%BA%D1%81%D0%BF%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%8B&amp;diff=50927"/>
				<updated>2016-01-09T00:57:39Z</updated>
		
		<summary type="html">&lt;p&gt;Kozichuk: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Граф-экспандер''' (или расширяющийся граф, англ. ''expander graph'') - в комбинаторике сильно разреженный граф, при этом связность определяется по вершинам, дугам или спектру.&lt;br /&gt;
&lt;br /&gt;
==Определение==&lt;br /&gt;
''Граф-экспандер'' — это конечный ненаправленный ''мультиграф'', в котором любое подмножество вершин, не являясь «слишком большим», имеет «сильную» связность. Различные формализации этих понятий дают различные типы экспандеров: ''рёберный расширитель'', ''вершинный расширитель'', и ''спектральный расширитель''.&lt;br /&gt;
&lt;br /&gt;
Несвязный граф не является экспандером. Любой связный граф является экспандером, однако различные связные графы имеют различные параметры расширителя. Полный граф имеет лучшие параметры расширителя, но имеет наибольшую возможную степень. Неформально говоря, граф является хорошим экспандером, если он имеет низкую степень и высокий параметр расширителя.&lt;br /&gt;
&lt;br /&gt;
===Реберное расширение===&lt;br /&gt;
''Рёберное расширение'' (также ''изопериметрическое число'' или константа Чигера) &amp;lt;tex&amp;gt;h(G)&amp;lt;/tex&amp;gt; графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; для &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; вершин определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;h(G) = \min_{0 &amp;lt; |S|\le \frac{n}{2}} \frac{|\partial_{\text{out}}(S)|}{|S|}&amp;lt;/tex&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где минимум берётся по всем непустым множествам &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; не более чем &amp;lt;tex&amp;gt;n/2&amp;lt;/tex&amp;gt; вершин и &amp;lt;tex&amp;gt;\partial(S)&amp;lt;/tex&amp;gt; — ''граничные дуги'' множества &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, то есть, множество дуг с единственной вершиной в &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Вершинное расширение===&lt;br /&gt;
''Вершинное изопериметрическое число'' &amp;lt;tex&amp;gt;h_{out}(G)&amp;lt;/tex&amp;gt; (также ''вершинное раширение'') графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;h_{out}(G) = \min_{0 &amp;lt; |S|\le \frac{n}{2}} \frac{|\partial_{\text{out}}(S)|}{|S|},&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;tex&amp;gt;\partial_{\text{out}}(S)&amp;lt;/tex&amp;gt; — ''внешняя граница'' &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, то есть множество вершин из &amp;lt;tex&amp;gt;V(G)\setminus S&amp;lt;/tex&amp;gt;, имеющих как минимум одного соседа в S. В варианте этого определения (называемом ''уникальным соседним расширением'') &amp;lt;tex&amp;gt;\partial_{\text{out}}(S)&amp;lt;/tex&amp;gt; заменяется на множество вершин из &amp;lt;tex&amp;gt;V&amp;lt;/tex&amp;gt; с ''точностью одним'' соседом из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
''Вершинное изопериметрическое число'' &amp;lt;tex&amp;gt;h_{in}(G)&amp;lt;/tex&amp;gt; графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;h_{in}(G) = \min_{0 &amp;lt; |S|\le \frac{n}{2}} \frac{|\partial_{\text{in}}(S)|}{|S|},&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;tex&amp;gt;∂_{in}(S)&amp;lt;/tex&amp;gt; — ''внутренняя граница'' &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, то есть множество вершин &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, имеющих как минимум одного соседа в &amp;lt;tex&amp;gt;V(G)\setminus S&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Спектральное расширение===&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; является d-регулярным, возможно определение в терминах линейной алгебры на основе собственных значений матрицы смежности &amp;lt;tex&amp;gt;A = A(G)&amp;lt;/tex&amp;gt; графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;A_{ij}&amp;lt;/tex&amp;gt; — число дуг между вершинами &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;. Поскольку &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; является симметричной, согласно спектральной теореме, &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; имеет &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; действительных собственных значений &amp;lt;tex&amp;gt;\lambda_1 \ge \lambda_2 \ge \cdots \ge \lambda_{n}&amp;lt;/tex&amp;gt;. Известно, что эти значения лежат в промежутке &amp;lt;tex&amp;gt;[−d, d]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
Граф регулярен тогда и только тогда, когда вектор &amp;lt;tex&amp;gt;u\in \mathbb {R} ^{n} с u_{i}=1&amp;lt;/tex&amp;gt; для всех &amp;lt;tex&amp;gt;i = 1, …, n&amp;lt;/tex&amp;gt; является собственным вектором матрицы &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt;, а его собственное число будет постоянной степенью графа. Таким образом, мы имеем &amp;lt;tex&amp;gt;Au = du&amp;lt;/tex&amp;gt;, и &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt; — собственный вектор матрицы &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; с собственными значениями &amp;lt;tex&amp;gt;λ1 = d&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt; — степень вершин графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt;. Спектральный зазор графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; определяется как &amp;lt;tex&amp;gt;d−λ2&amp;lt;/tex&amp;gt; и является мерилом спектрального расширения графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Известно, что &amp;lt;tex&amp;gt;\lambda_n = −d&amp;lt;/tex&amp;gt; тогда и только тогда, когда &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; — двудольный. Во многих случаях, например в лемме о перемешивании, необходимо ограничить снизу не только зазор между &amp;lt;tex&amp;gt;\lambda_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\lambda_2&amp;lt;/tex&amp;gt;, но и зазор между &amp;lt;tex&amp;gt;\lambda_1&amp;lt;/tex&amp;gt; и вторым максимальным по модулю собственным значением:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\lambda=\max\{|\lambda_2|, |\lambda_{n}|\}&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Поскольку это собственное значение соответствует некоторому собственному вектору, ортогональному &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;, его можно определить, используя отношение Рэлея:&lt;br /&gt;
&amp;lt;tex&amp;gt;\lambda=\max_{0\neq v\perp u} \frac{\|Av\|_2}{\|v\|_2},&amp;lt;/tex&amp;gt;&lt;br /&gt;
gde&lt;br /&gt;
&amp;lt;tex&amp;gt;\|v\|_2=\left(\sum_{i=1}^n v_i^2\right)^{1/2}&amp;lt;/tex&amp;gt;&lt;br /&gt;
— евклидова норма вектора &amp;lt;tex&amp;gt;v\in \mathbb {R} ^{n}&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Нормализованная версия этого определения также широко используется и более удобна для получения определённых результатов. В таком случае рассматривается матрица &amp;lt;tex&amp;gt;{\tfrac {1}{d}}A&amp;lt;/tex&amp;gt;, являющаяся матрицей переходов графа G. Все её собственные значения лежат между &amp;lt;tex&amp;gt;−1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;. Если граф не регулярен, спектр графа может быть определён аналогичным образом, используя собственные значения матрицы Кирхгофа. Для направленного графа используются сингулярные значения матрицы сопряжения A, которые равны квадратным корням из собственных значений симметричной матрицы &amp;lt;tex&amp;gt;A^TA&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Конструирование==&lt;br /&gt;
Существуют три основные стратегии создания семейств графов расширений[6]. Первая стратегия — алгебраическая и теоретико-групповая, вторая — аналитическая, использующая аддитивную комбинаторику, и третья — комбинаторная, использующая зигзаг-произведение и связанные комбинаторные произведения.&lt;br /&gt;
===Маргулис-Габбер-Галил===&lt;br /&gt;
Алгебраическое конструирование, основанное на графах Кэли, известно для различных вариантов экспандеров. Следующее конструирование принадлежит Маргулису и было проанализировано Габбером (Gabber) и Галилом (Galil). Для любого натурального &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; строим граф, &amp;lt;tex&amp;gt;G_{n}&amp;lt;/tex&amp;gt; со множеством вершин &amp;lt;tex&amp;gt;\mathbb Z _{n}\times \mathbb {Z} _{n}&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;\mathbb {Z} _{n}=\mathbb Z /n \mathbb Z&amp;lt;/tex&amp;gt; . Для любой вершины &amp;lt;tex&amp;gt;(x,y)\in \mathbb {Z} _{n}\times \mathbb {Z} _{n}&amp;lt;/tex&amp;gt;, её восемь соседей будут&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;(x \pm 2y,y), (x \pm (2y+1),y), (x,y \pm 2x), (x,y \pm (2x+1)).&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Выполняется следующая теорема:&lt;br /&gt;
{{Теорема&lt;br /&gt;
|statement=&lt;br /&gt;
Для всех &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; граф &amp;lt;tex&amp;gt;Gn&amp;lt;/tex&amp;gt; второе по величине собственное число&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;tex&amp;gt;\lambda(G)\leq 5 \sqrt{2}&amp;lt;/tex&amp;gt;.}}&lt;br /&gt;
&lt;br /&gt;
===Граф Рамануджана===&lt;br /&gt;
&amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt;-регулярный граф называется графом Рамануджана, если его второе по модулю собственное число не превосходит &amp;lt;tex&amp;gt;2{\sqrt {d-1}}&amp;lt;/tex&amp;gt;. Любоцкий, Сарнак, Филлипс и Маргулис указали явную конструкцию графов Кэли, являющихся графами Рамануджана. Опишем эту конструкцию.&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt;p&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; простые числа, &amp;lt;tex&amp;gt;p = 1&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;mod&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;4&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;q = 1&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;mod&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;4&amp;lt;/tex&amp;gt;. В качестве группы &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; возьмём &amp;lt;tex&amp;gt;PGL(2, \mathbb Z/q{\mathbb Z})&amp;lt;/tex&amp;gt;, т.е. невырожденные матрицы 2 × 2 над полем вычетов по модулю &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;, профакторизованные по отношению пропорциональности (с обычной операцией матричного умножения).&lt;br /&gt;
Далее мы зададим в этой группе симметричное множество &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;. Выберем такое целое &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;i^2 = −1&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;mod&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;. Можно доказать, что тогда имеется ровно &amp;lt;tex&amp;gt;(p + 1)&amp;lt;/tex&amp;gt; целочисленное решение уравнения&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;a_0^2 + a_1^2 + a_3^2 = p&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
такое, что &amp;lt;tex&amp;gt;a_0&amp;lt;/tex&amp;gt; положительно и нечётно, а &amp;lt;tex&amp;gt;a_1&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;a_2&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;a_3&amp;lt;/tex&amp;gt; чётны. Каждой такой четвёрке сопоставим матрицу&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;A = \begin{pmatrix} a_0 + ia_1 &amp;amp; a_2 + ia_3\\ -a_2 + ia_3 &amp;amp; a_0 - ia_1 \end{pmatrix}&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Эти матрицы образуют множество S.&lt;br /&gt;
Нетрудно понять, что граф Кэли &amp;lt;tex&amp;gt;(G, S)&amp;lt;/tex&amp;gt; состоит из &amp;lt;tex&amp;gt;\Theta(q^3)&amp;lt;/tex&amp;gt; вершин, и степень каждой вершины равна &amp;lt;tex&amp;gt;(p + 1)&amp;lt;/tex&amp;gt;. Свойства данного графа зависят от соотношения &amp;lt;tex&amp;gt;p&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;. Рассмотрим случай, когда p является квадратичным вычетом по модулю &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;. Тогда полученный граф Кэли состоит из двух связных компонент (поскольку все матрицы из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; лежат в подгруппе &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; индекса два — подгруппе матриц, определитель которых является квадратичным вычетом). Обозначим &amp;lt;tex&amp;gt;X^{p,q}&amp;lt;/tex&amp;gt; связную компоненту полученного графа. Можно доказать, что у &amp;lt;tex&amp;gt;X^{p,q}&amp;lt;/tex&amp;gt; второе по абсолютной величине собственное число не превосходит &amp;lt;tex&amp;gt;2{\sqrt{p}}&amp;lt;/tex&amp;gt;, т.е. мы получили граф Рамануджана.&lt;br /&gt;
&lt;br /&gt;
==Применение экспандеров==&lt;br /&gt;
&lt;br /&gt;
==Приложения и полезные свойства==&lt;br /&gt;
Первоначально интерес к экспандерам возник с целью построения устойчивой сети (телефонов или компьютеров) — число дуг графов расширения с ограниченным значением регулярности растет линейно по отношению к числу вершин.&lt;br /&gt;
&lt;br /&gt;
Экспандеры нашли широкое применение в теории вычислительных машин и систем, для построения алгоритмов, в корректирующих кодах, экстракторах, генераторах псевдослучайных чисел, сетях сортировки и компьютерных сетях. Они также используются для доказательства многих важных результатов в теории вычислительной сложности, таких как &amp;lt;tex&amp;gt;SL=L&amp;lt;/tex&amp;gt; и Теорема PCP. В криптографии экспандеры используются для создания хеш-функций.&lt;br /&gt;
&lt;br /&gt;
Ниже приведены некоторые свойства экспандеров, считающиеся полезными во многих областях.&lt;br /&gt;
===Лемма о перемешивании===&lt;br /&gt;
Лемма о перемешивании утверждает, что для любых двух подмножеств вершин &amp;lt;tex&amp;gt;S,T\subseteq V&amp;lt;/tex&amp;gt; число рёбер между &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; примерно равно числу рёбер в случайном &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt;-регулярном графе. Приближение тем лучше, чем меньше &amp;lt;tex&amp;gt;\lambda =\max\{|\lambda _{2}|&amp;lt;/tex&amp;gt;,&amp;lt;tex&amp;gt;|\lambda _{n}|\}&amp;lt;/tex&amp;gt;. В случайном &amp;lt;tex&amp;gt;d&amp;lt;tex/&amp;gt;-регулярном графе, также как и в случайном графе Эрдёша — Реньи с вероятностью &amp;lt;tex&amp;gt;{\tfrac {d}{n}}&amp;lt;/tex&amp;gt; выбора ребра, ожидается &amp;lt;tex&amp;gt;{\tfrac {d}{n}}\cdot |S|\cdot |T|&amp;lt;/tex&amp;gt; рёбер между S и T.&lt;br /&gt;
&lt;br /&gt;
Более формально, пусть &amp;lt;tex&amp;gt;E(S, T)&amp;lt;/tex&amp;gt; обозначает число рёбер между &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;. Если эти два множества пересекаются, дуги в пересечении считаются дважды, так что&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;E(S,T)=2|E(G[S\cap T])|+E(S\setminus T,T)+E(S,T\setminus S)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
Лемма о перемешивании утверждает, что&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\left|E(S,T)-{\frac {d\cdot |S|\cdot |T|}{n}}\right|\leq d\lambda {\sqrt {|S|\cdot |T|}}&amp;lt;/tex&amp;gt;,&lt;br /&gt;
где &amp;lt;tex&amp;gt;\lambda &amp;lt;/tex&amp;gt; — абсолютное значение нормализованного второго по величине собственного значения.&lt;br /&gt;
&lt;br /&gt;
Недавно Билу (Bilu) и Линайл (Linial) показали, что обратное тоже верно, то есть, при условии выполнения неравенства из леммы второе по величине собственное значение равно &amp;lt;tex&amp;gt;O(d\lambda \cdot (1+\log(1/\lambda )))&amp;lt;/tex&amp;gt;.&lt;br /&gt;
===Блуждания по экспандеру===&lt;br /&gt;
Согласно границе Чернова, если выбирать много независимых случайных значений из интервала &amp;lt;tex&amp;gt;[−1, 1]&amp;lt;/tex&amp;gt;, с большой степенью вероятности среднее выбранных значений будет близко к математическому ожиданию случайной переменной. Лемма о блуждании по экспандеру, согласно статьям Аджтари, Комлоша и Семереди и Гилмана, утверждает, что то же самое верно и для блужданий по экспандеру. Это полезно в теории дерандомизации, поскольку блуждание по экспандеру использует много меньше случайных бит, чем случайная независимая выборка.&lt;br /&gt;
&lt;br /&gt;
==Источники информации==&lt;br /&gt;
*[https://ru.wikipedia.org/wiki/%D0%AD%D0%BA%D1%81%D0%BF%D0%B0%D0%BD%D0%B4%D0%B5%D1%80_(%D1%82%D0%B5%D0%BE%D1%80%D0%B8%D1%8F_%D0%B3%D1%80%D0%B0%D1%84%D0%BE%D0%B2) Экспандер (теория графов)]&lt;/div&gt;</summary>
		<author><name>Kozichuk</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%93%D1%80%D0%B0%D1%84%D1%8B-%D1%8D%D0%BA%D1%81%D0%BF%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%8B&amp;diff=50926</id>
		<title>Графы-экспандеры</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%93%D1%80%D0%B0%D1%84%D1%8B-%D1%8D%D0%BA%D1%81%D0%BF%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%8B&amp;diff=50926"/>
				<updated>2016-01-09T00:55:11Z</updated>
		
		<summary type="html">&lt;p&gt;Kozichuk: /* Граф Рамануджана */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Граф-экспандер''' (или расширяющийся граф, англ. ''expander graph'') - в комбинаторике сильно разреженный граф, при этом связность определяется по вершинам, дугам или спектру.&lt;br /&gt;
&lt;br /&gt;
==Определение==&lt;br /&gt;
''Граф-экспандер'' — это конечный ненаправленный ''мультиграф'', в котором любое подмножество вершин, не являясь «слишком большим», имеет «сильную» связность. Различные формализации этих понятий дают различные типы экспандеров: ''рёберный расширитель'', ''вершинный расширитель'', и ''спектральный расширитель''.&lt;br /&gt;
&lt;br /&gt;
Несвязный граф не является экспандером. Любой связный граф является экспандером, однако различные связные графы имеют различные параметры расширителя. Полный граф имеет лучшие параметры расширителя, но имеет наибольшую возможную степень. Неформально говоря, граф является хорошим экспандером, если он имеет низкую степень и высокий параметр расширителя.&lt;br /&gt;
&lt;br /&gt;
===Реберное расширение===&lt;br /&gt;
''Рёберное расширение'' (также ''изопериметрическое число'' или константа Чигера) &amp;lt;tex&amp;gt;h(G)&amp;lt;/tex&amp;gt; графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; для &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; вершин определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;h(G) = \min_{0 &amp;lt; |S|\le \frac{n}{2}} \frac{|\partial_{\text{out}}(S)|}{|S|}&amp;lt;/tex&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где минимум берётся по всем непустым множествам &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; не более чем &amp;lt;tex&amp;gt;n/2&amp;lt;/tex&amp;gt; вершин и &amp;lt;tex&amp;gt;\partial(S)&amp;lt;/tex&amp;gt; — ''граничные дуги'' множества &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, то есть, множество дуг с единственной вершиной в &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Вершинное расширение===&lt;br /&gt;
''Вершинное изопериметрическое число'' &amp;lt;tex&amp;gt;h_{out}(G)&amp;lt;/tex&amp;gt; (также ''вершинное раширение'') графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;h_{out}(G) = \min_{0 &amp;lt; |S|\le \frac{n}{2}} \frac{|\partial_{\text{out}}(S)|}{|S|},&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;tex&amp;gt;\partial_{\text{out}}(S)&amp;lt;/tex&amp;gt; — ''внешняя граница'' &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, то есть множество вершин из &amp;lt;tex&amp;gt;V(G)\setminus S&amp;lt;/tex&amp;gt;, имеющих как минимум одного соседа в S. В варианте этого определения (называемом ''уникальным соседним расширением'') &amp;lt;tex&amp;gt;\partial_{\text{out}}(S)&amp;lt;/tex&amp;gt; заменяется на множество вершин из &amp;lt;tex&amp;gt;V&amp;lt;/tex&amp;gt; с ''точностью одним'' соседом из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
''Вершинное изопериметрическое число'' &amp;lt;tex&amp;gt;h_{in}(G)&amp;lt;/tex&amp;gt; графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;h_{in}(G) = \min_{0 &amp;lt; |S|\le \frac{n}{2}} \frac{|\partial_{\text{in}}(S)|}{|S|},&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;tex&amp;gt;∂_{in}(S)&amp;lt;/tex&amp;gt; — ''внутренняя граница'' &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, то есть множество вершин &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, имеющих как минимум одного соседа в &amp;lt;tex&amp;gt;V(G)\setminus S&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Спектральное расширение===&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; является d-регулярным, возможно определение в терминах линейной алгебры на основе собственных значений матрицы смежности &amp;lt;tex&amp;gt;A = A(G)&amp;lt;/tex&amp;gt; графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;A_{ij}&amp;lt;/tex&amp;gt; — число дуг между вершинами &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;. Поскольку &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; является симметричной, согласно спектральной теореме, &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; имеет &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; действительных собственных значений &amp;lt;tex&amp;gt;\lambda_1 \ge \lambda_2 \ge \cdots \ge \lambda_{n}&amp;lt;/tex&amp;gt;. Известно, что эти значения лежат в промежутке &amp;lt;tex&amp;gt;[−d, d]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
Граф регулярен тогда и только тогда, когда вектор &amp;lt;tex&amp;gt;u\in \mathbb {R} ^{n} с u_{i}=1&amp;lt;/tex&amp;gt; для всех &amp;lt;tex&amp;gt;i = 1, …, n&amp;lt;/tex&amp;gt; является собственным вектором матрицы &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt;, а его собственное число будет постоянной степенью графа. Таким образом, мы имеем &amp;lt;tex&amp;gt;Au = du&amp;lt;/tex&amp;gt;, и &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt; — собственный вектор матрицы &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; с собственными значениями &amp;lt;tex&amp;gt;λ1 = d&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt; — степень вершин графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt;. Спектральный зазор графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; определяется как &amp;lt;tex&amp;gt;d−λ2&amp;lt;/tex&amp;gt; и является мерилом спектрального расширения графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Известно, что &amp;lt;tex&amp;gt;\lambda_n = −d&amp;lt;/tex&amp;gt; тогда и только тогда, когда &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; — двудольный. Во многих случаях, например в лемме о перемешивании, необходимо ограничить снизу не только зазор между &amp;lt;tex&amp;gt;\lambda_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\lambda_2&amp;lt;/tex&amp;gt;, но и зазор между &amp;lt;tex&amp;gt;\lambda_1&amp;lt;/tex&amp;gt; и вторым максимальным по модулю собственным значением:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\lambda=\max\{|\lambda_2|, |\lambda_{n}|\}&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Поскольку это собственное значение соответствует некоторому собственному вектору, ортогональному &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;, его можно определить, используя отношение Рэлея:&lt;br /&gt;
&amp;lt;tex&amp;gt;\lambda=\max_{0\neq v\perp u} \frac{\|Av\|_2}{\|v\|_2},&amp;lt;/tex&amp;gt;&lt;br /&gt;
gde&lt;br /&gt;
&amp;lt;tex&amp;gt;\|v\|_2=\left(\sum_{i=1}^n v_i^2\right)^{1/2}&amp;lt;/tex&amp;gt;&lt;br /&gt;
— евклидова норма вектора &amp;lt;tex&amp;gt;v\in \mathbb {R} ^{n}&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Нормализованная версия этого определения также широко используется и более удобна для получения определённых результатов. В таком случае рассматривается матрица &amp;lt;tex&amp;gt;{\tfrac {1}{d}}A&amp;lt;/tex&amp;gt;, являющаяся матрицей переходов графа G. Все её собственные значения лежат между &amp;lt;tex&amp;gt;−1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;. Если граф не регулярен, спектр графа может быть определён аналогичным образом, используя собственные значения матрицы Кирхгофа. Для направленного графа используются сингулярные значения матрицы сопряжения A, которые равны квадратным корням из собственных значений симметричной матрицы &amp;lt;tex&amp;gt;A^TA&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Конструирование==&lt;br /&gt;
Существуют три основные стратегии создания семейств графов расширений[6]. Первая стратегия — алгебраическая и теоретико-групповая, вторая — аналитическая, использующая аддитивную комбинаторику, и третья — комбинаторная, использующая зигзаг-произведение и связанные комбинаторные произведения.&lt;br /&gt;
===Маргулис-Габбер-Галил===&lt;br /&gt;
Алгебраическое конструирование, основанное на графах Кэли, известно для различных вариантов экспандеров. Следующее конструирование принадлежит Маргулису и было проанализировано Габбером (Gabber) и Галилом (Galil). Для любого натурального &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; строим граф, &amp;lt;tex&amp;gt;G_{n}&amp;lt;/tex&amp;gt; со множеством вершин &amp;lt;tex&amp;gt;\mathbb Z _{n}\times \mathbb {Z} _{n}&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;\mathbb {Z} _{n}=\mathbb Z /n \mathbb Z&amp;lt;/tex&amp;gt; . Для любой вершины &amp;lt;tex&amp;gt;(x,y)\in \mathbb {Z} _{n}\times \mathbb {Z} _{n}&amp;lt;/tex&amp;gt;, её восемь соседей будут&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;(x \pm 2y,y), (x \pm (2y+1),y), (x,y \pm 2x), (x,y \pm (2x+1)).&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Выполняется следующая теорема:&lt;br /&gt;
{{Теорема&lt;br /&gt;
|statement=&lt;br /&gt;
Для всех &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; граф &amp;lt;tex&amp;gt;Gn&amp;lt;/tex&amp;gt; второе по величине собственное число&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;tex&amp;gt;\lambda(G)\leq 5 \sqrt{2}&amp;lt;/tex&amp;gt;.}}&lt;br /&gt;
&lt;br /&gt;
===Граф Рамануджана===&lt;br /&gt;
&amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt;-регулярный граф называется графом Рамануджана, если его второе по модулю собственное число не превосходит &amp;lt;tex&amp;gt;2{\sqrt {d-1}}&amp;lt;/tex&amp;gt;. Любоцкий, Сарнак, Филлипс и Маргулис указали явную конструкцию графов Кэли, являющихся графами Рамануджана. Опишем эту конструкцию.&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt;p&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; простые числа, &amp;lt;tex&amp;gt;p = 1&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;mod&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;4&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;q = 1&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;mod&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;4&amp;lt;/tex&amp;gt;. В качестве группы &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; возьмём &amp;lt;tex&amp;gt;PGL(2, \mathbb Z/q{\mathbb Z})&amp;lt;/tex&amp;gt;, т.е. невырожденные матрицы 2 × 2 над полем вычетов по модулю &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;, профакторизованные по отношению пропорциональности (с обычной операцией матричного умножения).&lt;br /&gt;
Далее мы зададим в этой группе симметричное множество &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;. Выберем такое целое &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;i^2 = −1&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;mod&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;. Можно доказать, что тогда имеется ровно &amp;lt;tex&amp;gt;(p + 1)&amp;lt;/tex&amp;gt; целочисленное решение уравнения&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;a_0^2 + a_1^2 + a_3^2 = p&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
такое, что &amp;lt;tex&amp;gt;a_0&amp;lt;/tex&amp;gt; положительно и нечётно, а &amp;lt;tex&amp;gt;a_1&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;a_2&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;a_3&amp;lt;/tex&amp;gt; чётны. Каждой такой четвёрке сопоставим матрицу&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;A = \begin{pmatrix} a_0 + ia_1 &amp;amp; a_2 + ia_3\\ -a_2 + ia_3 &amp;amp; a_0 - ia_1 \end{pmatrix}&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Эти матрицы образуют множество S.&lt;br /&gt;
Нетрудно понять, что граф Кэли &amp;lt;tex&amp;gt;(G, S)&amp;lt;/tex&amp;gt; состоит из &amp;lt;tex&amp;gt;\Theta(q^3)&amp;lt;/tex&amp;gt; вершин, и степень каждой вершины равна &amp;lt;tex&amp;gt;(p + 1)&amp;lt;/tex&amp;gt;. Свойства данного графа зависят от соотношения &amp;lt;tex&amp;gt;p&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;. Рассмотрим случай, когда p является квадратичным вычетом по модулю &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;. Тогда полученный граф Кэли состоит из двух связных компонент (поскольку все матрицы из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; лежат в подгруппе &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; индекса два — подгруппе матриц, определитель которых является квадратичным вычетом). Обозначим &amp;lt;tex&amp;gt;X^{p,q}&amp;lt;/tex&amp;gt; связную компоненту полученного графа. Можно доказать, что у &amp;lt;tex&amp;gt;X^{p,q}&amp;lt;/tex&amp;gt; второе по абсолютной величине собственное число не превосходит &amp;lt;tex&amp;gt;2{\sqrt{p}}&amp;lt;/tex&amp;gt;, т.е. мы получили граф Рамануджана.&lt;br /&gt;
&lt;br /&gt;
==Приложения и полезные свойства==&lt;br /&gt;
Первоначально интерес к экспандерам возник с целью построения устойчивой сети (телефонов или компьютеров) — число дуг графов расширения с ограниченным значением регулярности растет линейно по отношению к числу вершин.&lt;br /&gt;
&lt;br /&gt;
Экспандеры нашли широкое применение в теории вычислительных машин и систем, для построения алгоритмов, в корректирующих кодах, экстракторах, генераторах псевдослучайных чисел, сетях сортировки и компьютерных сетях. Они также используются для доказательства многих важных результатов в теории вычислительной сложности, таких как &amp;lt;tex&amp;gt;SL=L&amp;lt;/tex&amp;gt; и Теорема PCP. В криптографии экспандеры используются для создания хеш-функций.&lt;br /&gt;
&lt;br /&gt;
Ниже приведены некоторые свойства экспандеров, считающиеся полезными во многих областях.&lt;br /&gt;
===Лемма о перемешивании===&lt;br /&gt;
Лемма о перемешивании утверждает, что для любых двух подмножеств вершин &amp;lt;tex&amp;gt;S,T\subseteq V&amp;lt;/tex&amp;gt; число рёбер между &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; примерно равно числу рёбер в случайном &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt;-регулярном графе. Приближение тем лучше, чем меньше &amp;lt;tex&amp;gt;\lambda =\max\{|\lambda _{2}|&amp;lt;/tex&amp;gt;,&amp;lt;tex&amp;gt;|\lambda _{n}|\}&amp;lt;/tex&amp;gt;. В случайном &amp;lt;tex&amp;gt;d&amp;lt;tex/&amp;gt;-регулярном графе, также как и в случайном графе Эрдёша — Реньи с вероятностью &amp;lt;tex&amp;gt;{\tfrac {d}{n}}&amp;lt;/tex&amp;gt; выбора ребра, ожидается &amp;lt;tex&amp;gt;{\tfrac {d}{n}}\cdot |S|\cdot |T|&amp;lt;/tex&amp;gt; рёбер между S и T.&lt;br /&gt;
&lt;br /&gt;
Более формально, пусть &amp;lt;tex&amp;gt;E(S, T)&amp;lt;/tex&amp;gt; обозначает число рёбер между &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;. Если эти два множества пересекаются, дуги в пересечении считаются дважды, так что&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;E(S,T)=2|E(G[S\cap T])|+E(S\setminus T,T)+E(S,T\setminus S)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
Лемма о перемешивании утверждает, что&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\left|E(S,T)-{\frac {d\cdot |S|\cdot |T|}{n}}\right|\leq d\lambda {\sqrt {|S|\cdot |T|}}&amp;lt;/tex&amp;gt;,&lt;br /&gt;
где &amp;lt;tex&amp;gt;\lambda &amp;lt;/tex&amp;gt; — абсолютное значение нормализованного второго по величине собственного значения.&lt;br /&gt;
&lt;br /&gt;
Недавно Билу (Bilu) и Линайл (Linial) показали, что обратное тоже верно, то есть, при условии выполнения неравенства из леммы второе по величине собственное значение равно &amp;lt;tex&amp;gt;O(d\lambda \cdot (1+\log(1/\lambda )))&amp;lt;/tex&amp;gt;.&lt;br /&gt;
===Блуждания по экспандеру===&lt;br /&gt;
Согласно границе Чернова, если выбирать много независимых случайных значений из интервала &amp;lt;tex&amp;gt;[−1, 1]&amp;lt;/tex&amp;gt;, с большой степенью вероятности среднее выбранных значений будет близко к математическому ожиданию случайной переменной. Лемма о блуждании по экспандеру, согласно статьям Аджтари, Комлоша и Семереди и Гилмана, утверждает, что то же самое верно и для блужданий по экспандеру. Это полезно в теории дерандомизации, поскольку блуждание по экспандеру использует много меньше случайных бит, чем случайная независимая выборка.&lt;br /&gt;
&lt;br /&gt;
==Источники информации==&lt;br /&gt;
*[https://ru.wikipedia.org/wiki/%D0%AD%D0%BA%D1%81%D0%BF%D0%B0%D0%BD%D0%B4%D0%B5%D1%80_(%D1%82%D0%B5%D0%BE%D1%80%D0%B8%D1%8F_%D0%B3%D1%80%D0%B0%D1%84%D0%BE%D0%B2) Экспандер (теория графов)]&lt;/div&gt;</summary>
		<author><name>Kozichuk</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%93%D1%80%D0%B0%D1%84%D1%8B-%D1%8D%D0%BA%D1%81%D0%BF%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%8B&amp;diff=50258</id>
		<title>Графы-экспандеры</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%93%D1%80%D0%B0%D1%84%D1%8B-%D1%8D%D0%BA%D1%81%D0%BF%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%8B&amp;diff=50258"/>
				<updated>2015-12-17T07:59:01Z</updated>
		
		<summary type="html">&lt;p&gt;Kozichuk: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Граф-экспандер''' (или расширяющийся граф, англ. ''expander graph'') - в комбинаторике сильно разреженный граф, при этом связность определяется по вершинам, дугам или спектру.&lt;br /&gt;
&lt;br /&gt;
==Определение==&lt;br /&gt;
''Граф-экспандер'' — это конечный ненаправленный ''мультиграф'', в котором любое подмножество вершин, не являясь «слишком большим», имеет «сильную» связность. Различные формализации этих понятий дают различные типы экспандеров: ''рёберный расширитель'', ''вершинный расширитель'', и ''спектральный расширитель''.&lt;br /&gt;
&lt;br /&gt;
Несвязный граф не является экспандером. Любой связный граф является экспандером, однако различные связные графы имеют различные параметры расширителя. Полный граф имеет лучшие параметры расширителя, но имеет наибольшую возможную степень. Неформально говоря, граф является хорошим экспандером, если он имеет низкую степень и высокий параметр расширителя.&lt;br /&gt;
&lt;br /&gt;
===Реберное расширение===&lt;br /&gt;
''Рёберное расширение'' (также ''изопериметрическое число'' или константа Чигера) &amp;lt;tex&amp;gt;h(G)&amp;lt;/tex&amp;gt; графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; для &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; вершин определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;h(G) = \min_{0 &amp;lt; |S|\le \frac{n}{2}} \frac{|\partial_{\text{out}}(S)|}{|S|}&amp;lt;/tex&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где минимум берётся по всем непустым множествам &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; не более чем &amp;lt;tex&amp;gt;n/2&amp;lt;/tex&amp;gt; вершин и &amp;lt;tex&amp;gt;\partial(S)&amp;lt;/tex&amp;gt; — ''граничные дуги'' множества &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, то есть, множество дуг с единственной вершиной в &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Вершинное расширение===&lt;br /&gt;
''Вершинное изопериметрическое число'' &amp;lt;tex&amp;gt;h_{out}(G)&amp;lt;/tex&amp;gt; (также ''вершинное раширение'') графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;h_{out}(G) = \min_{0 &amp;lt; |S|\le \frac{n}{2}} \frac{|\partial_{\text{out}}(S)|}{|S|},&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;tex&amp;gt;\partial_{\text{out}}(S)&amp;lt;/tex&amp;gt; — ''внешняя граница'' &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, то есть множество вершин из &amp;lt;tex&amp;gt;V(G)\setminus S&amp;lt;/tex&amp;gt;, имеющих как минимум одного соседа в S. В варианте этого определения (называемом ''уникальным соседним расширением'') &amp;lt;tex&amp;gt;\partial_{\text{out}}(S)&amp;lt;/tex&amp;gt; заменяется на множество вершин из &amp;lt;tex&amp;gt;V&amp;lt;/tex&amp;gt; с ''точностью одним'' соседом из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
''Вершинное изопериметрическое число'' &amp;lt;tex&amp;gt;h_{in}(G)&amp;lt;/tex&amp;gt; графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;h_{in}(G) = \min_{0 &amp;lt; |S|\le \frac{n}{2}} \frac{|\partial_{\text{in}}(S)|}{|S|},&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;tex&amp;gt;∂_{in}(S)&amp;lt;/tex&amp;gt; — ''внутренняя граница'' &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, то есть множество вершин &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, имеющих как минимум одного соседа в &amp;lt;tex&amp;gt;V(G)\setminus S&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Спектральное расширение===&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; является d-регулярным, возможно определение в терминах линейной алгебры на основе собственных значений матрицы смежности &amp;lt;tex&amp;gt;A = A(G)&amp;lt;/tex&amp;gt; графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;A_{ij}&amp;lt;/tex&amp;gt; — число дуг между вершинами &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;. Поскольку &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; является симметричной, согласно спектральной теореме, &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; имеет &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; действительных собственных значений &amp;lt;tex&amp;gt;\lambda_1 \ge \lambda_2 \ge \cdots \ge \lambda_{n}&amp;lt;/tex&amp;gt;. Известно, что эти значения лежат в промежутке &amp;lt;tex&amp;gt;[−d, d]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
Граф регулярен тогда и только тогда, когда вектор &amp;lt;tex&amp;gt;u\in \mathbb {R} ^{n} с u_{i}=1&amp;lt;/tex&amp;gt; для всех &amp;lt;tex&amp;gt;i = 1, …, n&amp;lt;/tex&amp;gt; является собственным вектором матрицы &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt;, а его собственное число будет постоянной степенью графа. Таким образом, мы имеем &amp;lt;tex&amp;gt;Au = du&amp;lt;/tex&amp;gt;, и &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt; — собственный вектор матрицы &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; с собственными значениями &amp;lt;tex&amp;gt;λ1 = d&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt; — степень вершин графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt;. Спектральный зазор графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; определяется как &amp;lt;tex&amp;gt;d−λ2&amp;lt;/tex&amp;gt; и является мерилом спектрального расширения графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Известно, что &amp;lt;tex&amp;gt;\lambda_n = −d&amp;lt;/tex&amp;gt; тогда и только тогда, когда &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; — двудольный. Во многих случаях, например в лемме о перемешивании, необходимо ограничить снизу не только зазор между &amp;lt;tex&amp;gt;\lambda_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\lambda_2&amp;lt;/tex&amp;gt;, но и зазор между &amp;lt;tex&amp;gt;\lambda_1&amp;lt;/tex&amp;gt; и вторым максимальным по модулю собственным значением:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\lambda=\max\{|\lambda_2|, |\lambda_{n}|\}&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Поскольку это собственное значение соответствует некоторому собственному вектору, ортогональному &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;, его можно определить, используя отношение Рэлея:&lt;br /&gt;
&amp;lt;tex&amp;gt;\lambda=\max_{0\neq v\perp u} \frac{\|Av\|_2}{\|v\|_2},&amp;lt;/tex&amp;gt;&lt;br /&gt;
gde&lt;br /&gt;
&amp;lt;tex&amp;gt;\|v\|_2=\left(\sum_{i=1}^n v_i^2\right)^{1/2}&amp;lt;/tex&amp;gt;&lt;br /&gt;
— евклидова норма вектора &amp;lt;tex&amp;gt;v\in \mathbb {R} ^{n}&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Нормализованная версия этого определения также широко используется и более удобна для получения определённых результатов. В таком случае рассматривается матрица &amp;lt;tex&amp;gt;{\tfrac {1}{d}}A&amp;lt;/tex&amp;gt;, являющаяся матрицей переходов графа G. Все её собственные значения лежат между &amp;lt;tex&amp;gt;−1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;. Если граф не регулярен, спектр графа может быть определён аналогичным образом, используя собственные значения матрицы Кирхгофа. Для направленного графа используются сингулярные значения матрицы сопряжения A, которые равны квадратным корням из собственных значений симметричной матрицы &amp;lt;tex&amp;gt;A^TA&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Конструирование==&lt;br /&gt;
Существуют три основные стратегии создания семейств графов расширений[6]. Первая стратегия — алгебраическая и теоретико-групповая, вторая — аналитическая, использующая аддитивную комбинаторику, и третья — комбинаторная, использующая зигзаг-произведение и связанные комбинаторные произведения.&lt;br /&gt;
===Маргулис-Габбер-Галил===&lt;br /&gt;
Алгебраическое конструирование, основанное на графах Кэли, известно для различных вариантов экспандеров. Следующее конструирование принадлежит Маргулису и было проанализировано Габбером (Gabber) и Галилом (Galil). Для любого натурального &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; строим граф, &amp;lt;tex&amp;gt;G_{n}&amp;lt;/tex&amp;gt; со множеством вершин &amp;lt;tex&amp;gt;\mathbb Z _{n}\times \mathbb {Z} _{n}&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;\mathbb {Z} _{n}=\mathbb Z /n \mathbb Z&amp;lt;/tex&amp;gt; . Для любой вершины &amp;lt;tex&amp;gt;(x,y)\in \mathbb {Z} _{n}\times \mathbb {Z} _{n}&amp;lt;/tex&amp;gt;, её восемь соседей будут&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;(x \pm 2y,y), (x \pm (2y+1),y), (x,y \pm 2x), (x,y \pm (2x+1)).&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Выполняется следующая теорема:&lt;br /&gt;
{{Теорема&lt;br /&gt;
|statement=&lt;br /&gt;
Для всех &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; граф &amp;lt;tex&amp;gt;Gn&amp;lt;/tex&amp;gt; второе по величине собственное число&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;tex&amp;gt;\lambda(G)\leq 5 \sqrt{2}&amp;lt;/tex&amp;gt;.}}&lt;br /&gt;
&lt;br /&gt;
===Граф Рамануджана===&lt;br /&gt;
По теореме Алона (Alon) и Боппана (Boppana) для всех достаточно больших d-регулярных графов выполняется неравенство &amp;lt;tex&amp;gt;\lambda \geqslant 2{\sqrt {d-1}}-o(1)&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;\lambda &amp;lt;/tex&amp;gt; — второе по абсолютной величине собственное число. Для графов Рамануджана &amp;lt;tex&amp;gt;\lambda \leqslant 2{\sqrt {d-1}}&amp;lt;/tex&amp;gt;. Таким образом, графы Рамануджана имеют асимптотически наименьшее возможное значение λ и являются превосходными спектральными расширителями.&lt;br /&gt;
&lt;br /&gt;
Александр Любоцкий, Филипс и Сарнак (1988), Маргулис (1988) и Моргенштерн (1994) показали как можно явно сконструировать граф Рамануджана. По теореме Фридмана (Friedman,2003) случайный d-регулярный граф с &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; вершинами является почти графом Рамануджана, поскольку выполняется&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt; \lambda \leqslant 2\sqrt{d-1}+\epsilon&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
с вероятностью &amp;lt;tex&amp;gt;1 - o(1)&amp;lt;/tex&amp;gt; при &amp;lt;tex&amp;gt;n \to \infty&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Приложения и полезные свойства==&lt;br /&gt;
Первоначально интерес к экспандерам возник с целью построения устойчивой сети (телефонов или компьютеров) — число дуг графов расширения с ограниченным значением регулярности растет линейно по отношению к числу вершин.&lt;br /&gt;
&lt;br /&gt;
Экспандеры нашли широкое применение в теории вычислительных машин и систем, для построения алгоритмов, в корректирующих кодах, экстракторах, генераторах псевдослучайных чисел, сетях сортировки и компьютерных сетях. Они также используются для доказательства многих важных результатов в теории вычислительной сложности, таких как &amp;lt;tex&amp;gt;SL=L&amp;lt;/tex&amp;gt; и Теорема PCP. В криптографии экспандеры используются для создания хеш-функций.&lt;br /&gt;
&lt;br /&gt;
Ниже приведены некоторые свойства экспандеров, считающиеся полезными во многих областях.&lt;br /&gt;
===Лемма о перемешивании===&lt;br /&gt;
Лемма о перемешивании утверждает, что для любых двух подмножеств вершин &amp;lt;tex&amp;gt;S,T\subseteq V&amp;lt;/tex&amp;gt; число рёбер между &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; примерно равно числу рёбер в случайном &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt;-регулярном графе. Приближение тем лучше, чем меньше &amp;lt;tex&amp;gt;\lambda =\max\{|\lambda _{2}|&amp;lt;/tex&amp;gt;,&amp;lt;tex&amp;gt;|\lambda _{n}|\}&amp;lt;/tex&amp;gt;. В случайном &amp;lt;tex&amp;gt;d&amp;lt;tex/&amp;gt;-регулярном графе, также как и в случайном графе Эрдёша — Реньи с вероятностью &amp;lt;tex&amp;gt;{\tfrac {d}{n}}&amp;lt;/tex&amp;gt; выбора ребра, ожидается &amp;lt;tex&amp;gt;{\tfrac {d}{n}}\cdot |S|\cdot |T|&amp;lt;/tex&amp;gt; рёбер между S и T.&lt;br /&gt;
&lt;br /&gt;
Более формально, пусть &amp;lt;tex&amp;gt;E(S, T)&amp;lt;/tex&amp;gt; обозначает число рёбер между &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;. Если эти два множества пересекаются, дуги в пересечении считаются дважды, так что&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;E(S,T)=2|E(G[S\cap T])|+E(S\setminus T,T)+E(S,T\setminus S)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
Лемма о перемешивании утверждает, что&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\left|E(S,T)-{\frac {d\cdot |S|\cdot |T|}{n}}\right|\leq d\lambda {\sqrt {|S|\cdot |T|}}&amp;lt;/tex&amp;gt;,&lt;br /&gt;
где &amp;lt;tex&amp;gt;\lambda &amp;lt;/tex&amp;gt; — абсолютное значение нормализованного второго по величине собственного значения.&lt;br /&gt;
&lt;br /&gt;
Недавно Билу (Bilu) и Линайл (Linial) показали, что обратное тоже верно, то есть, при условии выполнения неравенства из леммы второе по величине собственное значение равно &amp;lt;tex&amp;gt;O(d\lambda \cdot (1+\log(1/\lambda )))&amp;lt;/tex&amp;gt;.&lt;br /&gt;
===Блуждания по экспандеру===&lt;br /&gt;
Согласно границе Чернова, если выбирать много независимых случайных значений из интервала &amp;lt;tex&amp;gt;[−1, 1]&amp;lt;/tex&amp;gt;, с большой степенью вероятности среднее выбранных значений будет близко к математическому ожиданию случайной переменной. Лемма о блуждании по экспандеру, согласно статьям Аджтари, Комлоша и Семереди и Гилмана, утверждает, что то же самое верно и для блужданий по экспандеру. Это полезно в теории дерандомизации, поскольку блуждание по экспандеру использует много меньше случайных бит, чем случайная независимая выборка.&lt;br /&gt;
&lt;br /&gt;
==Источники информации==&lt;br /&gt;
*[https://ru.wikipedia.org/wiki/%D0%AD%D0%BA%D1%81%D0%BF%D0%B0%D0%BD%D0%B4%D0%B5%D1%80_(%D1%82%D0%B5%D0%BE%D1%80%D0%B8%D1%8F_%D0%B3%D1%80%D0%B0%D1%84%D0%BE%D0%B2) Экспандер (теория графов)]&lt;/div&gt;</summary>
		<author><name>Kozichuk</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%93%D1%80%D0%B0%D1%84%D1%8B-%D1%8D%D0%BA%D1%81%D0%BF%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%8B&amp;diff=50257</id>
		<title>Графы-экспандеры</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%93%D1%80%D0%B0%D1%84%D1%8B-%D1%8D%D0%BA%D1%81%D0%BF%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%8B&amp;diff=50257"/>
				<updated>2015-12-17T07:56:28Z</updated>
		
		<summary type="html">&lt;p&gt;Kozichuk: /* Определение */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Граф-экспандер''' (англ. ''expander graph'') - в комбинаторике сильно разреженный граф, при этом связность определяется по вершинам, дугам или спектру.&lt;br /&gt;
&lt;br /&gt;
==Определение==&lt;br /&gt;
''Граф-экспандер'' — это конечный ненаправленный ''мультиграф'', в котором любое подмножество вершин, не являясь «слишком большим», имеет «сильную» связность. Различные формализации этих понятий дают различные типы экспандеров: ''рёберный расширитель'', ''вершинный расширитель'', и ''спектральный расширитель''.&lt;br /&gt;
&lt;br /&gt;
Несвязный граф не является экспандером. Любой связный граф является экспандером, однако различные связные графы имеют различные параметры расширителя. Полный граф имеет лучшие параметры расширителя, но имеет наибольшую возможную степень. Неформально говоря, граф является хорошим экспандером, если он имеет низкую степень и высокий параметр расширителя.&lt;br /&gt;
&lt;br /&gt;
===Реберное расширение===&lt;br /&gt;
''Рёберное расширение'' (также ''изопериметрическое число'' или константа Чигера) &amp;lt;tex&amp;gt;h(G)&amp;lt;/tex&amp;gt; графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; для &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; вершин определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;h(G) = \min_{0 &amp;lt; |S|\le \frac{n}{2}} \frac{|\partial_{\text{out}}(S)|}{|S|}&amp;lt;/tex&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где минимум берётся по всем непустым множествам &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; не более чем &amp;lt;tex&amp;gt;n/2&amp;lt;/tex&amp;gt; вершин и &amp;lt;tex&amp;gt;\partial(S)&amp;lt;/tex&amp;gt; — ''граничные дуги'' множества &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, то есть, множество дуг с единственной вершиной в &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Вершинное расширение===&lt;br /&gt;
''Вершинное изопериметрическое число'' &amp;lt;tex&amp;gt;h_{out}(G)&amp;lt;/tex&amp;gt; (также ''вершинное раширение'') графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;h_{out}(G) = \min_{0 &amp;lt; |S|\le \frac{n}{2}} \frac{|\partial_{\text{out}}(S)|}{|S|},&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;tex&amp;gt;\partial_{\text{out}}(S)&amp;lt;/tex&amp;gt; — ''внешняя граница'' &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, то есть множество вершин из &amp;lt;tex&amp;gt;V(G)\setminus S&amp;lt;/tex&amp;gt;, имеющих как минимум одного соседа в S. В варианте этого определения (называемом ''уникальным соседним расширением'') &amp;lt;tex&amp;gt;\partial_{\text{out}}(S)&amp;lt;/tex&amp;gt; заменяется на множество вершин из &amp;lt;tex&amp;gt;V&amp;lt;/tex&amp;gt; с ''точностью одним'' соседом из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
''Вершинное изопериметрическое число'' &amp;lt;tex&amp;gt;h_{in}(G)&amp;lt;/tex&amp;gt; графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;h_{in}(G) = \min_{0 &amp;lt; |S|\le \frac{n}{2}} \frac{|\partial_{\text{in}}(S)|}{|S|},&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;tex&amp;gt;∂_{in}(S)&amp;lt;/tex&amp;gt; — ''внутренняя граница'' &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, то есть множество вершин &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, имеющих как минимум одного соседа в &amp;lt;tex&amp;gt;V(G)\setminus S&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Спектральное расширение===&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; является d-регулярным, возможно определение в терминах линейной алгебры на основе собственных значений матрицы смежности &amp;lt;tex&amp;gt;A = A(G)&amp;lt;/tex&amp;gt; графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;A_{ij}&amp;lt;/tex&amp;gt; — число дуг между вершинами &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;. Поскольку &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; является симметричной, согласно спектральной теореме, &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; имеет &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; действительных собственных значений &amp;lt;tex&amp;gt;\lambda_1 \ge \lambda_2 \ge \cdots \ge \lambda_{n}&amp;lt;/tex&amp;gt;. Известно, что эти значения лежат в промежутке &amp;lt;tex&amp;gt;[−d, d]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
Граф регулярен тогда и только тогда, когда вектор &amp;lt;tex&amp;gt;u\in \mathbb {R} ^{n} с u_{i}=1&amp;lt;/tex&amp;gt; для всех &amp;lt;tex&amp;gt;i = 1, …, n&amp;lt;/tex&amp;gt; является собственным вектором матрицы &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt;, а его собственное число будет постоянной степенью графа. Таким образом, мы имеем &amp;lt;tex&amp;gt;Au = du&amp;lt;/tex&amp;gt;, и &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt; — собственный вектор матрицы &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; с собственными значениями &amp;lt;tex&amp;gt;λ1 = d&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt; — степень вершин графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt;. Спектральный зазор графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; определяется как &amp;lt;tex&amp;gt;d−λ2&amp;lt;/tex&amp;gt; и является мерилом спектрального расширения графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Известно, что &amp;lt;tex&amp;gt;\lambda_n = −d&amp;lt;/tex&amp;gt; тогда и только тогда, когда &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; — двудольный. Во многих случаях, например в лемме о перемешивании, необходимо ограничить снизу не только зазор между &amp;lt;tex&amp;gt;\lambda_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\lambda_2&amp;lt;/tex&amp;gt;, но и зазор между &amp;lt;tex&amp;gt;\lambda_1&amp;lt;/tex&amp;gt; и вторым максимальным по модулю собственным значением:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\lambda=\max\{|\lambda_2|, |\lambda_{n}|\}&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Поскольку это собственное значение соответствует некоторому собственному вектору, ортогональному &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;, его можно определить, используя отношение Рэлея:&lt;br /&gt;
&amp;lt;tex&amp;gt;\lambda=\max_{0\neq v\perp u} \frac{\|Av\|_2}{\|v\|_2},&amp;lt;/tex&amp;gt;&lt;br /&gt;
gde&lt;br /&gt;
&amp;lt;tex&amp;gt;\|v\|_2=\left(\sum_{i=1}^n v_i^2\right)^{1/2}&amp;lt;/tex&amp;gt;&lt;br /&gt;
— евклидова норма вектора &amp;lt;tex&amp;gt;v\in \mathbb {R} ^{n}&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Нормализованная версия этого определения также широко используется и более удобна для получения определённых результатов. В таком случае рассматривается матрица &amp;lt;tex&amp;gt;{\tfrac {1}{d}}A&amp;lt;/tex&amp;gt;, являющаяся матрицей переходов графа G. Все её собственные значения лежат между &amp;lt;tex&amp;gt;−1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;. Если граф не регулярен, спектр графа может быть определён аналогичным образом, используя собственные значения матрицы Кирхгофа. Для направленного графа используются сингулярные значения матрицы сопряжения A, которые равны квадратным корням из собственных значений симметричной матрицы &amp;lt;tex&amp;gt;A^TA&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Конструирование==&lt;br /&gt;
Существуют три основные стратегии создания семейств графов расширений[6]. Первая стратегия — алгебраическая и теоретико-групповая, вторая — аналитическая, использующая аддитивную комбинаторику, и третья — комбинаторная, использующая зигзаг-произведение и связанные комбинаторные произведения.&lt;br /&gt;
===Маргулис-Габбер-Галил===&lt;br /&gt;
Алгебраическое конструирование, основанное на графах Кэли, известно для различных вариантов экспандеров. Следующее конструирование принадлежит Маргулису и было проанализировано Габбером (Gabber) и Галилом (Galil). Для любого натурального &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; строим граф, &amp;lt;tex&amp;gt;G_{n}&amp;lt;/tex&amp;gt; со множеством вершин &amp;lt;tex&amp;gt;\mathbb Z _{n}\times \mathbb {Z} _{n}&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;\mathbb {Z} _{n}=\mathbb Z /n \mathbb Z&amp;lt;/tex&amp;gt; . Для любой вершины &amp;lt;tex&amp;gt;(x,y)\in \mathbb {Z} _{n}\times \mathbb {Z} _{n}&amp;lt;/tex&amp;gt;, её восемь соседей будут&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;(x \pm 2y,y), (x \pm (2y+1),y), (x,y \pm 2x), (x,y \pm (2x+1)).&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Выполняется следующая теорема:&lt;br /&gt;
{{Теорема&lt;br /&gt;
|statement=&lt;br /&gt;
Для всех &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; граф &amp;lt;tex&amp;gt;Gn&amp;lt;/tex&amp;gt; второе по величине собственное число&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;tex&amp;gt;\lambda(G)\leq 5 \sqrt{2}&amp;lt;/tex&amp;gt;.}}&lt;br /&gt;
&lt;br /&gt;
===Граф Рамануджана===&lt;br /&gt;
По теореме Алона (Alon) и Боппана (Boppana) для всех достаточно больших d-регулярных графов выполняется неравенство &amp;lt;tex&amp;gt;\lambda \geqslant 2{\sqrt {d-1}}-o(1)&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;\lambda &amp;lt;/tex&amp;gt; — второе по абсолютной величине собственное число. Для графов Рамануджана &amp;lt;tex&amp;gt;\lambda \leqslant 2{\sqrt {d-1}}&amp;lt;/tex&amp;gt;. Таким образом, графы Рамануджана имеют асимптотически наименьшее возможное значение λ и являются превосходными спектральными расширителями.&lt;br /&gt;
&lt;br /&gt;
Александр Любоцкий, Филипс и Сарнак (1988), Маргулис (1988) и Моргенштерн (1994) показали как можно явно сконструировать граф Рамануджана. По теореме Фридмана (Friedman,2003) случайный d-регулярный граф с &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; вершинами является почти графом Рамануджана, поскольку выполняется&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt; \lambda \leqslant 2\sqrt{d-1}+\epsilon&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
с вероятностью &amp;lt;tex&amp;gt;1 - o(1)&amp;lt;/tex&amp;gt; при &amp;lt;tex&amp;gt;n \to \infty&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Приложения и полезные свойства==&lt;br /&gt;
Первоначально интерес к экспандерам возник с целью построения устойчивой сети (телефонов или компьютеров) — число дуг графов расширения с ограниченным значением регулярности растет линейно по отношению к числу вершин.&lt;br /&gt;
&lt;br /&gt;
Экспандеры нашли широкое применение в теории вычислительных машин и систем, для построения алгоритмов, в корректирующих кодах, экстракторах, генераторах псевдослучайных чисел, сетях сортировки и компьютерных сетях. Они также используются для доказательства многих важных результатов в теории вычислительной сложности, таких как &amp;lt;tex&amp;gt;SL=L&amp;lt;/tex&amp;gt; и Теорема PCP. В криптографии экспандеры используются для создания хеш-функций.&lt;br /&gt;
&lt;br /&gt;
Ниже приведены некоторые свойства экспандеров, считающиеся полезными во многих областях.&lt;br /&gt;
===Лемма о перемешивании===&lt;br /&gt;
Лемма о перемешивании утверждает, что для любых двух подмножеств вершин &amp;lt;tex&amp;gt;S,T\subseteq V&amp;lt;/tex&amp;gt; число рёбер между &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; примерно равно числу рёбер в случайном &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt;-регулярном графе. Приближение тем лучше, чем меньше &amp;lt;tex&amp;gt;\lambda =\max\{|\lambda _{2}|&amp;lt;/tex&amp;gt;,&amp;lt;tex&amp;gt;|\lambda _{n}|\}&amp;lt;/tex&amp;gt;. В случайном &amp;lt;tex&amp;gt;d&amp;lt;tex/&amp;gt;-регулярном графе, также как и в случайном графе Эрдёша — Реньи с вероятностью &amp;lt;tex&amp;gt;{\tfrac {d}{n}}&amp;lt;/tex&amp;gt; выбора ребра, ожидается &amp;lt;tex&amp;gt;{\tfrac {d}{n}}\cdot |S|\cdot |T|&amp;lt;/tex&amp;gt; рёбер между S и T.&lt;br /&gt;
&lt;br /&gt;
Более формально, пусть &amp;lt;tex&amp;gt;E(S, T)&amp;lt;/tex&amp;gt; обозначает число рёбер между &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;. Если эти два множества пересекаются, дуги в пересечении считаются дважды, так что&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;E(S,T)=2|E(G[S\cap T])|+E(S\setminus T,T)+E(S,T\setminus S)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
Лемма о перемешивании утверждает, что&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\left|E(S,T)-{\frac {d\cdot |S|\cdot |T|}{n}}\right|\leq d\lambda {\sqrt {|S|\cdot |T|}}&amp;lt;/tex&amp;gt;,&lt;br /&gt;
где &amp;lt;tex&amp;gt;\lambda &amp;lt;/tex&amp;gt; — абсолютное значение нормализованного второго по величине собственного значения.&lt;br /&gt;
&lt;br /&gt;
Недавно Билу (Bilu) и Линайл (Linial) показали, что обратное тоже верно, то есть, при условии выполнения неравенства из леммы второе по величине собственное значение равно &amp;lt;tex&amp;gt;O(d\lambda \cdot (1+\log(1/\lambda )))&amp;lt;/tex&amp;gt;.&lt;br /&gt;
===Блуждания по экспандеру===&lt;br /&gt;
Согласно границе Чернова, если выбирать много независимых случайных значений из интервала &amp;lt;tex&amp;gt;[−1, 1]&amp;lt;/tex&amp;gt;, с большой степенью вероятности среднее выбранных значений будет близко к математическому ожиданию случайной переменной. Лемма о блуждании по экспандеру, согласно статьям Аджтари, Комлоша и Семереди и Гилмана, утверждает, что то же самое верно и для блужданий по экспандеру. Это полезно в теории дерандомизации, поскольку блуждание по экспандеру использует много меньше случайных бит, чем случайная независимая выборка.&lt;br /&gt;
&lt;br /&gt;
==Источники информации==&lt;br /&gt;
*[https://ru.wikipedia.org/wiki/%D0%AD%D0%BA%D1%81%D0%BF%D0%B0%D0%BD%D0%B4%D0%B5%D1%80_(%D1%82%D0%B5%D0%BE%D1%80%D0%B8%D1%8F_%D0%B3%D1%80%D0%B0%D1%84%D0%BE%D0%B2) Экспандер (теория графов)]&lt;/div&gt;</summary>
		<author><name>Kozichuk</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%93%D1%80%D0%B0%D1%84%D1%8B-%D1%8D%D0%BA%D1%81%D0%BF%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%8B&amp;diff=50256</id>
		<title>Графы-экспандеры</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%93%D1%80%D0%B0%D1%84%D1%8B-%D1%8D%D0%BA%D1%81%D0%BF%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%8B&amp;diff=50256"/>
				<updated>2015-12-17T07:50:29Z</updated>
		
		<summary type="html">&lt;p&gt;Kozichuk: /* Определение */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Граф-экспандер''' (англ. ''expander graph'') - в комбинаторике сильно разреженный граф, при этом связность определяется по вершинам, дугам или спектру.&lt;br /&gt;
&lt;br /&gt;
==Определение==&lt;br /&gt;
''Граф-экспандер'' — это конечный ненаправленный ''мультиграф'', в котором любое подмножество вершин, не являясь «слишком большим», имеет «сильную» связность. Различные формализации этих понятий дают различные типы экспандеров: ''рёберный расширитель'', ''вершинный расширитель'', и ''спектральный расширитель''.&lt;br /&gt;
&lt;br /&gt;
Несвязный граф не является экспандером. Любой связный граф является экспандером, однако различные связные графы имеют различные параметры расширителя. Полный граф имеет лучшие параметры расширителя, но имеет наибольшую возможную степень. Неформально говоря, граф является хорошим экспандером, если он имеет низкую степень и высокий параметр расширителя.&lt;br /&gt;
&lt;br /&gt;
===Реберное расширение===&lt;br /&gt;
''Рёберное расширение'' (также ''изопериметрическое число'' или константа Чигера) &amp;lt;tex&amp;gt;h(G)&amp;lt;/tex&amp;gt; графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; для &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; вершин определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;h(G) = \min_{0 &amp;lt; |S|\le \frac{n}{2}} \frac{|\partial_{\text{out}}(S)|}{|S|}&amp;lt;/tex&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где минимум берётся по всем непустым множествам &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; не более чем &amp;lt;tex&amp;gt;n/2&amp;lt;/tex&amp;gt; вершин и &amp;lt;tex&amp;gt;∂(S)&amp;lt;/tex&amp;gt; — ''граничные дуги'' множества &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, то есть, множество дуг с единственной вершиной в &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Вершинное расширение===&lt;br /&gt;
''Вершинное изопериметрическое число'' &amp;lt;tex&amp;gt;h_{out}(G)&amp;lt;/tex&amp;gt; (также ''вершинное раширение'') графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;h_{out}(G) = \min_{0 &amp;lt; |S|\le \frac{n}{2}} \frac{|\partial_{\text{out}}(S)|}{|S|},&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;tex&amp;gt;∂_{out}(S)&amp;lt;/tex&amp;gt; — ''внешняя граница'' &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, то есть множество вершин из &amp;lt;tex&amp;gt;V(G)\setminus S&amp;lt;/tex&amp;gt;, имеющих как минимум одного соседа в S. В варианте этого определения (называемом ''уникальным соседним расширением'') &amp;lt;tex&amp;gt;∂_{out}(S)&amp;lt;/tex&amp;gt; заменяется на множество вершин из &amp;lt;tex&amp;gt;V&amp;lt;/tex&amp;gt; с ''точностью одним'' соседом из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
''Вершинное изопериметрическое число'' &amp;lt;tex&amp;gt;h_{in}(G)&amp;lt;/tex&amp;gt; графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;h_{in}(G) = \min_{0 &amp;lt; |S|\le \frac{n}{2}} \frac{|\partial_{\text{in}}(S)|}{|S|},&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;tex&amp;gt;∂_{in}(S)&amp;lt;/tex&amp;gt; — ''внутренняя граница'' &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, то есть множество вершин &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, имеющих как минимум одного соседа в &amp;lt;tex&amp;gt;V(G)\setminus S&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Спектральное расширение===&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; является d-регулярным, возможно определение в терминах линейной алгебры на основе собственных значений матрицы смежности &amp;lt;tex&amp;gt;A = A(G)&amp;lt;/tex&amp;gt; графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;A_{ij}&amp;lt;/tex&amp;gt; — число дуг между вершинами &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;. Поскольку &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; является симметричной, согласно спектральной теореме, &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; имеет &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; действительных собственных значений &amp;lt;tex&amp;gt;\lambda_1 \ge \lambda_2 \ge \cdots \ge \lambda_{n}&amp;lt;/tex&amp;gt;. Известно, что эти значения лежат в промежутке &amp;lt;tex&amp;gt;[−d, d]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
Граф регулярен тогда и только тогда, когда вектор &amp;lt;tex&amp;gt;u\in \mathbb {R} ^{n} с u_{i}=1&amp;lt;/tex&amp;gt; для всех &amp;lt;tex&amp;gt;i = 1, …, n&amp;lt;/tex&amp;gt; является собственным вектором матрицы &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt;, а его собственное число будет постоянной степенью графа. Таким образом, мы имеем &amp;lt;tex&amp;gt;Au = du&amp;lt;/tex&amp;gt;, и &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt; — собственный вектор матрицы &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; с собственными значениями &amp;lt;tex&amp;gt;λ1 = d&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt; — степень вершин графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt;. Спектральный зазор графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; определяется как &amp;lt;tex&amp;gt;d−λ2&amp;lt;/tex&amp;gt; и является мерилом спектрального расширения графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Известно, что &amp;lt;tex&amp;gt;\lambda_n = −d&amp;lt;/tex&amp;gt; тогда и только тогда, когда &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; — двудольный. Во многих случаях, например в лемме о перемешивании, необходимо ограничить снизу не только зазор между &amp;lt;tex&amp;gt;\lambda_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\lambda_2&amp;lt;/tex&amp;gt;, но и зазор между &amp;lt;tex&amp;gt;\lambda_1&amp;lt;/tex&amp;gt; и вторым максимальным по модулю собственным значением:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\lambda=\max\{|\lambda_2|, |\lambda_{n}|\}&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Поскольку это собственное значение соответствует некоторому собственному вектору, ортогональному &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;, его можно определить, используя отношение Рэлея:&lt;br /&gt;
&amp;lt;tex&amp;gt;\lambda=\max_{0\neq v\perp u} \frac{\|Av\|_2}{\|v\|_2},&amp;lt;/tex&amp;gt;&lt;br /&gt;
gde&lt;br /&gt;
&amp;lt;tex&amp;gt;\|v\|_2=\left(\sum_{i=1}^n v_i^2\right)^{1/2}&amp;lt;/tex&amp;gt;&lt;br /&gt;
— евклидова норма вектора &amp;lt;tex&amp;gt;v\in \mathbb {R} ^{n}&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Нормализованная версия этого определения также широко используется и более удобна для получения определённых результатов. В таком случае рассматривается матрица &amp;lt;tex&amp;gt;{\tfrac {1}{d}}A&amp;lt;/tex&amp;gt;, являющаяся матрицей переходов графа G. Все её собственные значения лежат между &amp;lt;tex&amp;gt;−1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;. Если граф не регулярен, спектр графа может быть определён аналогичным образом, используя собственные значения матрицы Кирхгофа. Для направленного графа используются сингулярные значения матрицы сопряжения A, которые равны квадратным корням из собственных значений симметричной матрицы &amp;lt;tex&amp;gt;A^TA&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Конструирование==&lt;br /&gt;
Существуют три основные стратегии создания семейств графов расширений[6]. Первая стратегия — алгебраическая и теоретико-групповая, вторая — аналитическая, использующая аддитивную комбинаторику, и третья — комбинаторная, использующая зигзаг-произведение и связанные комбинаторные произведения.&lt;br /&gt;
===Маргулис-Габбер-Галил===&lt;br /&gt;
Алгебраическое конструирование, основанное на графах Кэли, известно для различных вариантов экспандеров. Следующее конструирование принадлежит Маргулису и было проанализировано Габбером (Gabber) и Галилом (Galil). Для любого натурального &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; строим граф, &amp;lt;tex&amp;gt;G_{n}&amp;lt;/tex&amp;gt; со множеством вершин &amp;lt;tex&amp;gt;\mathbb Z _{n}\times \mathbb {Z} _{n}&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;\mathbb {Z} _{n}=\mathbb Z /n \mathbb Z&amp;lt;/tex&amp;gt; . Для любой вершины &amp;lt;tex&amp;gt;(x,y)\in \mathbb {Z} _{n}\times \mathbb {Z} _{n}&amp;lt;/tex&amp;gt;, её восемь соседей будут&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;(x \pm 2y,y), (x \pm (2y+1),y), (x,y \pm 2x), (x,y \pm (2x+1)).&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Выполняется следующая теорема:&lt;br /&gt;
{{Теорема&lt;br /&gt;
|statement=&lt;br /&gt;
Для всех &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; граф &amp;lt;tex&amp;gt;Gn&amp;lt;/tex&amp;gt; второе по величине собственное число&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;tex&amp;gt;\lambda(G)\leq 5 \sqrt{2}&amp;lt;/tex&amp;gt;.}}&lt;br /&gt;
&lt;br /&gt;
===Граф Рамануджана===&lt;br /&gt;
По теореме Алона (Alon) и Боппана (Boppana) для всех достаточно больших d-регулярных графов выполняется неравенство &amp;lt;tex&amp;gt;\lambda \geqslant 2{\sqrt {d-1}}-o(1)&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;\lambda &amp;lt;/tex&amp;gt; — второе по абсолютной величине собственное число. Для графов Рамануджана &amp;lt;tex&amp;gt;\lambda \leqslant 2{\sqrt {d-1}}&amp;lt;/tex&amp;gt;. Таким образом, графы Рамануджана имеют асимптотически наименьшее возможное значение λ и являются превосходными спектральными расширителями.&lt;br /&gt;
&lt;br /&gt;
Александр Любоцкий, Филипс и Сарнак (1988), Маргулис (1988) и Моргенштерн (1994) показали как можно явно сконструировать граф Рамануджана. По теореме Фридмана (Friedman,2003) случайный d-регулярный граф с &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; вершинами является почти графом Рамануджана, поскольку выполняется&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt; \lambda \leqslant 2\sqrt{d-1}+\epsilon&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
с вероятностью &amp;lt;tex&amp;gt;1 - o(1)&amp;lt;/tex&amp;gt; при &amp;lt;tex&amp;gt;n \to \infty&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Приложения и полезные свойства==&lt;br /&gt;
Первоначально интерес к экспандерам возник с целью построения устойчивой сети (телефонов или компьютеров) — число дуг графов расширения с ограниченным значением регулярности растет линейно по отношению к числу вершин.&lt;br /&gt;
&lt;br /&gt;
Экспандеры нашли широкое применение в теории вычислительных машин и систем, для построения алгоритмов, в корректирующих кодах, экстракторах, генераторах псевдослучайных чисел, сетях сортировки и компьютерных сетях. Они также используются для доказательства многих важных результатов в теории вычислительной сложности, таких как &amp;lt;tex&amp;gt;SL=L&amp;lt;/tex&amp;gt; и Теорема PCP. В криптографии экспандеры используются для создания хеш-функций.&lt;br /&gt;
&lt;br /&gt;
Ниже приведены некоторые свойства экспандеров, считающиеся полезными во многих областях.&lt;br /&gt;
===Лемма о перемешивании===&lt;br /&gt;
Лемма о перемешивании утверждает, что для любых двух подмножеств вершин &amp;lt;tex&amp;gt;S,T\subseteq V&amp;lt;/tex&amp;gt; число рёбер между &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; примерно равно числу рёбер в случайном &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt;-регулярном графе. Приближение тем лучше, чем меньше &amp;lt;tex&amp;gt;\lambda =\max\{|\lambda _{2}|&amp;lt;/tex&amp;gt;,&amp;lt;tex&amp;gt;|\lambda _{n}|\}&amp;lt;/tex&amp;gt;. В случайном &amp;lt;tex&amp;gt;d&amp;lt;tex/&amp;gt;-регулярном графе, также как и в случайном графе Эрдёша — Реньи с вероятностью &amp;lt;tex&amp;gt;{\tfrac {d}{n}}&amp;lt;/tex&amp;gt; выбора ребра, ожидается &amp;lt;tex&amp;gt;{\tfrac {d}{n}}\cdot |S|\cdot |T|&amp;lt;/tex&amp;gt; рёбер между S и T.&lt;br /&gt;
&lt;br /&gt;
Более формально, пусть &amp;lt;tex&amp;gt;E(S, T)&amp;lt;/tex&amp;gt; обозначает число рёбер между &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;. Если эти два множества пересекаются, дуги в пересечении считаются дважды, так что&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;E(S,T)=2|E(G[S\cap T])|+E(S\setminus T,T)+E(S,T\setminus S)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
Лемма о перемешивании утверждает, что&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\left|E(S,T)-{\frac {d\cdot |S|\cdot |T|}{n}}\right|\leq d\lambda {\sqrt {|S|\cdot |T|}}&amp;lt;/tex&amp;gt;,&lt;br /&gt;
где &amp;lt;tex&amp;gt;\lambda &amp;lt;/tex&amp;gt; — абсолютное значение нормализованного второго по величине собственного значения.&lt;br /&gt;
&lt;br /&gt;
Недавно Билу (Bilu) и Линайл (Linial) показали, что обратное тоже верно, то есть, при условии выполнения неравенства из леммы второе по величине собственное значение равно &amp;lt;tex&amp;gt;O(d\lambda \cdot (1+\log(1/\lambda )))&amp;lt;/tex&amp;gt;.&lt;br /&gt;
===Блуждания по экспандеру===&lt;br /&gt;
Согласно границе Чернова, если выбирать много независимых случайных значений из интервала &amp;lt;tex&amp;gt;[−1, 1]&amp;lt;/tex&amp;gt;, с большой степенью вероятности среднее выбранных значений будет близко к математическому ожиданию случайной переменной. Лемма о блуждании по экспандеру, согласно статьям Аджтари, Комлоша и Семереди и Гилмана, утверждает, что то же самое верно и для блужданий по экспандеру. Это полезно в теории дерандомизации, поскольку блуждание по экспандеру использует много меньше случайных бит, чем случайная независимая выборка.&lt;br /&gt;
&lt;br /&gt;
==Источники информации==&lt;br /&gt;
*[https://ru.wikipedia.org/wiki/%D0%AD%D0%BA%D1%81%D0%BF%D0%B0%D0%BD%D0%B4%D0%B5%D1%80_(%D1%82%D0%B5%D0%BE%D1%80%D0%B8%D1%8F_%D0%B3%D1%80%D0%B0%D1%84%D0%BE%D0%B2) Экспандер (теория графов)]&lt;/div&gt;</summary>
		<author><name>Kozichuk</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%93%D1%80%D0%B0%D1%84%D1%8B-%D1%8D%D0%BA%D1%81%D0%BF%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%8B&amp;diff=50250</id>
		<title>Графы-экспандеры</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%93%D1%80%D0%B0%D1%84%D1%8B-%D1%8D%D0%BA%D1%81%D0%BF%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%8B&amp;diff=50250"/>
				<updated>2015-12-17T00:38:09Z</updated>
		
		<summary type="html">&lt;p&gt;Kozichuk: /* Спектральное расширение */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Граф-экспандер''' (англ. ''expander graph'') - в комбинаторике сильно разреженный граф, при этом связность определяется по вершинам, дугам или спектру.&lt;br /&gt;
&lt;br /&gt;
==Определение==&lt;br /&gt;
''Экспандер'' — это конечный ненаправленный ''мультиграф'', в котором любое подмножество вершин, не являясь «слишком большим», имеет «сильную» связность. Различные формализации этих понятий дают различные типы экспандеров: ''рёберный расширитель'', ''вершинный расширитель'', и ''спектральный расширитель''.&lt;br /&gt;
&lt;br /&gt;
Несвязный граф не является экспандером. Любой связный граф является экспандером, однако различные связные графы имеют различные параметры расширителя. Полный граф имеет лучшие параметры расширителя, но имеет наибольшую возможную степень. Неформально говоря, граф является хорошим экспандером, если он имеет низкую степень и высокий параметр расширителя.&lt;br /&gt;
&lt;br /&gt;
===Реберное расширение===&lt;br /&gt;
''Рёберное расширение'' (также ''изопериметрическое число'' или константа Чигера) &amp;lt;tex&amp;gt;h(G)&amp;lt;/tex&amp;gt; графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; для &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; вершин определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;h(G) = \min_{0 &amp;lt; |S|\le \frac{n}{2}} \frac{|\partial_{\text{out}}(S)|}{|S|}&amp;lt;/tex&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где минимум берётся по всем непустым множествам &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; не более чем &amp;lt;tex&amp;gt;n/2&amp;lt;/tex&amp;gt; вершин и &amp;lt;tex&amp;gt;∂(S)&amp;lt;/tex&amp;gt; — ''граничные дуги'' множества &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, то есть, множество дуг с единственной вершиной в &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Вершинное расширение===&lt;br /&gt;
''Вершинное изопериметрическое число'' &amp;lt;tex&amp;gt;h_{out}(G)&amp;lt;/tex&amp;gt; (также ''вершинное раширение'') графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;h_{out}(G) = \min_{0 &amp;lt; |S|\le \frac{n}{2}} \frac{|\partial_{\text{out}}(S)|}{|S|},&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;tex&amp;gt;∂_{out}(S)&amp;lt;/tex&amp;gt; — ''внешняя граница'' &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, то есть множество вершин из &amp;lt;tex&amp;gt;V(G)\setminus S&amp;lt;/tex&amp;gt;, имеющих как минимум одного соседа в S. В варианте этого определения (называемом ''уникальным соседним расширением'') &amp;lt;tex&amp;gt;∂_{out}(S)&amp;lt;/tex&amp;gt; заменяется на множество вершин из &amp;lt;tex&amp;gt;V&amp;lt;/tex&amp;gt; с ''точностью одним'' соседом из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
''Вершинное изопериметрическое число'' &amp;lt;tex&amp;gt;h_{in}(G)&amp;lt;/tex&amp;gt; графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;h_{in}(G) = \min_{0 &amp;lt; |S|\le \frac{n}{2}} \frac{|\partial_{\text{in}}(S)|}{|S|},&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;tex&amp;gt;∂_{in}(S)&amp;lt;/tex&amp;gt; — ''внутренняя граница'' &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, то есть множество вершин &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, имеющих как минимум одного соседа в &amp;lt;tex&amp;gt;V(G)\setminus S&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Спектральное расширение===&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; является d-регулярным, возможно определение в терминах линейной алгебры на основе собственных значений матрицы смежности &amp;lt;tex&amp;gt;A = A(G)&amp;lt;/tex&amp;gt; графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;A_{ij}&amp;lt;/tex&amp;gt; — число дуг между вершинами &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;. Поскольку &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; является симметричной, согласно спектральной теореме, &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; имеет &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; действительных собственных значений &amp;lt;tex&amp;gt;\lambda_1 \ge \lambda_2 \ge \cdots \ge \lambda_{n}&amp;lt;/tex&amp;gt;. Известно, что эти значения лежат в промежутке &amp;lt;tex&amp;gt;[−d, d]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
Граф регулярен тогда и только тогда, когда вектор &amp;lt;tex&amp;gt;u\in \mathbb {R} ^{n} с u_{i}=1&amp;lt;/tex&amp;gt; для всех &amp;lt;tex&amp;gt;i = 1, …, n&amp;lt;/tex&amp;gt; является собственным вектором матрицы &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt;, а его собственное число будет постоянной степенью графа. Таким образом, мы имеем &amp;lt;tex&amp;gt;Au = du&amp;lt;/tex&amp;gt;, и &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt; — собственный вектор матрицы &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; с собственными значениями &amp;lt;tex&amp;gt;λ1 = d&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt; — степень вершин графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt;. Спектральный зазор графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; определяется как &amp;lt;tex&amp;gt;d−λ2&amp;lt;/tex&amp;gt; и является мерилом спектрального расширения графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Известно, что &amp;lt;tex&amp;gt;\lambda_n = −d&amp;lt;/tex&amp;gt; тогда и только тогда, когда &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; — двудольный. Во многих случаях, например в лемме о перемешивании, необходимо ограничить снизу не только зазор между &amp;lt;tex&amp;gt;\lambda_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\lambda_2&amp;lt;/tex&amp;gt;, но и зазор между &amp;lt;tex&amp;gt;\lambda_1&amp;lt;/tex&amp;gt; и вторым максимальным по модулю собственным значением:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\lambda=\max\{|\lambda_2|, |\lambda_{n}|\}&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Поскольку это собственное значение соответствует некоторому собственному вектору, ортогональному &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;, его можно определить, используя отношение Рэлея:&lt;br /&gt;
&amp;lt;tex&amp;gt;\lambda=\max_{0\neq v\perp u} \frac{\|Av\|_2}{\|v\|_2},&amp;lt;/tex&amp;gt;&lt;br /&gt;
gde&lt;br /&gt;
&amp;lt;tex&amp;gt;\|v\|_2=\left(\sum_{i=1}^n v_i^2\right)^{1/2}&amp;lt;/tex&amp;gt;&lt;br /&gt;
— евклидова норма вектора &amp;lt;tex&amp;gt;v\in \mathbb {R} ^{n}&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Нормализованная версия этого определения также широко используется и более удобна для получения определённых результатов. В таком случае рассматривается матрица &amp;lt;tex&amp;gt;{\tfrac {1}{d}}A&amp;lt;/tex&amp;gt;, являющаяся матрицей переходов графа G. Все её собственные значения лежат между &amp;lt;tex&amp;gt;−1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;. Если граф не регулярен, спектр графа может быть определён аналогичным образом, используя собственные значения матрицы Кирхгофа. Для направленного графа используются сингулярные значения матрицы сопряжения A, которые равны квадратным корням из собственных значений симметричной матрицы &amp;lt;tex&amp;gt;A^TA&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Конструирование==&lt;br /&gt;
Существуют три основные стратегии создания семейств графов расширений[6]. Первая стратегия — алгебраическая и теоретико-групповая, вторая — аналитическая, использующая аддитивную комбинаторику, и третья — комбинаторная, использующая зигзаг-произведение и связанные комбинаторные произведения.&lt;br /&gt;
===Маргулис-Габбер-Галил===&lt;br /&gt;
Алгебраическое конструирование, основанное на графах Кэли, известно для различных вариантов экспандеров. Следующее конструирование принадлежит Маргулису и было проанализировано Габбером (Gabber) и Галилом (Galil)[7]. Для любого натурального n строим граф, Gn со множеством вершин \mathbb {Z} _{n}\times \mathbb {Z} _{n}, где \mathbb {Z} _{n}=\mathbb {Z} /n\mathbb {Z} . Для любой вершины (x,y)\in \mathbb {Z} _{n}\times \mathbb {Z} _{n}, её восемь соседей будут&lt;br /&gt;
// formulas&lt;br /&gt;
Выполняется следующая теорема:&lt;br /&gt;
//theorem&lt;br /&gt;
===Граф Рамануджана===&lt;br /&gt;
По теореме Алона (Alon) и Боппана (Boppana) для всех достаточно больших d-регулярных графов выполняется неравенство \lambda \geq 2{\sqrt {d-1}}-o(1), где λ — второе по абсолютной величине собственное число[1]. Для графов Рамануджана[en]\lambda \leq 2{\sqrt {d-1}} [1]. Таким образом, графы Рамануджана имеют асимптотически наименьшее возможное значение λ и являются превосходными спектральными расширителями.&lt;br /&gt;
&lt;br /&gt;
Александр Любоцкий, Филипс и Сарнак (1988), Маргулис (1988) и Моргенштерн (1994) показали как можно явно сконструировать граф Рамануджана[1]. По теореме Фридмана (Friedman,2003) случайный d-регулярный граф[en] с n вершинами является почти графом Рамануджана, поскольку выполняется&lt;br /&gt;
//formulas&lt;br /&gt;
==Приложения и полезные свойства==&lt;br /&gt;
Первоначально интерес к экспандерам возник с целью построения устойчивой сети (телефонов или компьютеров) — число дуг графов расширения с ограниченным значением регулярности растет линейно по отношению к числу вершин.&lt;br /&gt;
&lt;br /&gt;
Экспандеры нашли широкое применение в теории вычислительных машин и систем, для построения алгоритмов, в корректирующих кодах[en], экстракторах[en], генераторах псевдослучайных чисел, сетях сортировки[8] и компьютерных сетях. Они также используются для доказательства многих важных результатов в теории вычислительной сложности, таких как SL[en]=L[en][9] и Теорема PCP[10]. В rриптографии экспандеры используются для создания хеш-функций.&lt;br /&gt;
&lt;br /&gt;
Ниже приведены некоторые свойства экспандеров, считающиеся полезными во многих областях.&lt;br /&gt;
===Лемма о перемешивании===&lt;br /&gt;
Лемма о перемешивании утверждает, что для любых двух подмножеств вершин S,T\subseteq V число рёбер между S и T примерно равно числу рёбер в случайном d-регулярном графе. Приближение тем лучше, чем меньше \lambda =\max\{|\lambda _{2}|,|\lambda _{n}|\}. В случайном d-регулярном графе, также как и в случайном графе Эрдёша — Реньи[en]с вероятностью {\tfrac {d}{n}} выбора ребра, ожидается {\tfrac {d}{n}}\cdot |S|\cdot |T| рёбер между S и T.&lt;br /&gt;
&lt;br /&gt;
Более формально, пусть E(S, T) обозначает число рёбер между S и T. Если эти два множества пересекаются, дуги в пересечении считаются дважды, так что&lt;br /&gt;
&lt;br /&gt;
E(S,T)=2|E(G[S\cap T])|+E(S\setminus T,T)+E(S,T\setminus S).&lt;br /&gt;
Лемма о перемешивании утверждает, что[11]&lt;br /&gt;
&lt;br /&gt;
\left|E(S,T)-{\frac {d\cdot |S|\cdot |T|}{n}}\right|\leq d\lambda {\sqrt {|S|\cdot |T|}},&lt;br /&gt;
где λ — абсолютное значение нормализованного второго по величине собственного значения.&lt;br /&gt;
&lt;br /&gt;
Недавно Билу (Bilu) и Линайл (Linial) показали, что обратное тоже верно, то есть, при условии выполнения неравенства из леммы второе по величине собственное значение равно O(d\lambda \cdot (1+\log(1/\lambda )))[12].&lt;br /&gt;
===Блуждания по экспандеру===&lt;br /&gt;
Согласно границе Чернова[en], если выбирать много независимых случайных значений из интервала [−1, 1], с большой степенью вероятности среднее выбранных значений будет близко к математическому ожиданию случайной переменной. Лемма о блуждании по экспандеру, согласно статьям Аджтари, Комлоша и Семереди[13] и Гилмана[14], утверждает, что то же самое верно и для блужданий по экспандеру. Это полезно в теории дерандомизации[en], поскольку блуждание по экспандеру использует много меньше случайных бит, чем случайная независимая выборка.&lt;br /&gt;
==Источники==&lt;br /&gt;
// на потом&lt;/div&gt;</summary>
		<author><name>Kozichuk</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%93%D1%80%D0%B0%D1%84%D1%8B-%D1%8D%D0%BA%D1%81%D0%BF%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%8B&amp;diff=50249</id>
		<title>Графы-экспандеры</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%93%D1%80%D0%B0%D1%84%D1%8B-%D1%8D%D0%BA%D1%81%D0%BF%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%8B&amp;diff=50249"/>
				<updated>2015-12-17T00:08:54Z</updated>
		
		<summary type="html">&lt;p&gt;Kozichuk: /* Определение */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Граф-экспандер''' (англ. ''expander graph'') - в комбинаторике сильно разреженный граф, при этом связность определяется по вершинам, дугам или спектру.&lt;br /&gt;
&lt;br /&gt;
==Определение==&lt;br /&gt;
''Экспандер'' — это конечный ненаправленный ''мультиграф'', в котором любое подмножество вершин, не являясь «слишком большим», имеет «сильную» связность. Различные формализации этих понятий дают различные типы экспандеров: ''рёберный расширитель'', ''вершинный расширитель'', и ''спектральный расширитель''.&lt;br /&gt;
&lt;br /&gt;
Несвязный граф не является экспандером. Любой связный граф является экспандером, однако различные связные графы имеют различные параметры расширителя. Полный граф имеет лучшие параметры расширителя, но имеет наибольшую возможную степень. Неформально говоря, граф является хорошим экспандером, если он имеет низкую степень и высокий параметр расширителя.&lt;br /&gt;
&lt;br /&gt;
===Реберное расширение===&lt;br /&gt;
''Рёберное расширение'' (также ''изопериметрическое число'' или константа Чигера) &amp;lt;tex&amp;gt;h(G)&amp;lt;/tex&amp;gt; графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; для &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; вершин определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;h(G) = \min_{0 &amp;lt; |S|\le \frac{n}{2}} \frac{|\partial_{\text{out}}(S)|}{|S|}&amp;lt;/tex&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где минимум берётся по всем непустым множествам &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; не более чем &amp;lt;tex&amp;gt;n/2&amp;lt;/tex&amp;gt; вершин и &amp;lt;tex&amp;gt;∂(S)&amp;lt;/tex&amp;gt; — ''граничные дуги'' множества &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, то есть, множество дуг с единственной вершиной в &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Вершинное расширение===&lt;br /&gt;
''Вершинное изопериметрическое число'' &amp;lt;tex&amp;gt;h_{out}(G)&amp;lt;/tex&amp;gt; (также ''вершинное раширение'') графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;h_{out}(G) = \min_{0 &amp;lt; |S|\le \frac{n}{2}} \frac{|\partial_{\text{out}}(S)|}{|S|},&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;tex&amp;gt;∂_{out}(S)&amp;lt;/tex&amp;gt; — ''внешняя граница'' &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, то есть множество вершин из &amp;lt;tex&amp;gt;V(G)\setminus S&amp;lt;/tex&amp;gt;, имеющих как минимум одного соседа в S. В варианте этого определения (называемом ''уникальным соседним расширением'') &amp;lt;tex&amp;gt;∂_{out}(S)&amp;lt;/tex&amp;gt; заменяется на множество вершин из &amp;lt;tex&amp;gt;V&amp;lt;/tex&amp;gt; с ''точностью одним'' соседом из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
''Вершинное изопериметрическое число'' &amp;lt;tex&amp;gt;h_{in}(G)&amp;lt;/tex&amp;gt; графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;h_{in}(G) = \min_{0 &amp;lt; |S|\le \frac{n}{2}} \frac{|\partial_{\text{in}}(S)|}{|S|},&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;tex&amp;gt;∂_{in}(S)&amp;lt;/tex&amp;gt; — ''внутренняя граница'' &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, то есть множество вершин &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, имеющих как минимум одного соседа в &amp;lt;tex&amp;gt;V(G)\setminus S&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Спектральное расширение===&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; является d-регулярным, возможно определение в терминах линейной алгебры на основе собственных значений матрицы смежности &amp;lt;tex&amp;gt;A = A(G)&amp;lt;/tex&amp;gt; графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;A_{ij}&amp;lt;/tex&amp;gt; — число дуг между вершинами &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;. Поскольку &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; является симметричной, согласно спектральной теореме, &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; имеет &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; действительных собственных значений &amp;lt;tex&amp;gt;\lambda_1 \ge \lambda_2 \ge \cdots \ge \lambda_{n}&amp;lt;/tex&amp;gt;. Известно, что эти значения лежат в промежутке &amp;lt;tex&amp;gt;[−d, d]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
Граф регулярен тогда и только тогда, когда вектор &amp;lt;tex&amp;gt;u\in \mathbb {R} ^{n} с u_{i}=1&amp;lt;/tex&amp;gt; для всех &amp;lt;tex&amp;gt;i = 1, …, n&amp;lt;/tex&amp;gt; является собственным вектором матрицы &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt;, а его собственное число будет постоянной степенью графа. Таким образом, мы имеем &amp;lt;tex&amp;gt;Au = du&amp;lt;/tex&amp;gt;, и &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt; — собственный вектор матрицы &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; с собственными значениями &amp;lt;tex&amp;gt;λ1 = d&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt; — степень вершин графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt;. Спектральный зазор графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; определяется как &amp;lt;tex&amp;gt;d−λ2&amp;lt;t/ex&amp;gt; и является мерилом спектрального расширения графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Известно, что &amp;lt;tex&amp;gt;λ_n = −d&amp;lt;/tex&amp;gt; тогда и только тогда, когда &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; — двудольный. Во многих случаях, например в лемме о перемешивании, необходимо ограничить снизу не только зазор между &amp;lt;tex&amp;gt;λ1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;λ2&amp;lt;/tex&amp;gt;, но и зазор между &amp;lt;tex&amp;gt;λ1&amp;lt;/tex&amp;gt; и вторым максимальным по модулю собственным значением:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;// formuli&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Поскольку это собственное значение соответствует некоторому собственному вектору, ортогональному &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;, его можно определить, используя отношение Рэлея:&lt;br /&gt;
&amp;lt;tex&amp;gt;//formulas&amp;lt;/tex&amp;gt;&lt;br /&gt;
gde&lt;br /&gt;
&amp;lt;tex&amp;gt;//formulas&amp;lt;/tex&amp;gt;&lt;br /&gt;
— евклидова норма вектора &amp;lt;tex&amp;gt;v\in \mathbb {R} ^{n}&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Нормализованная версия этого определения также широко используется и более удобна для получения определённых результатов. В таком случае рассматривается матрица &amp;lt;tex&amp;gt;{\tfrac {1}{d}}A&amp;lt;/tex&amp;gt;, являющаяся матрицей переходов графа G. Все её собственные значения лежат между &amp;lt;tex&amp;gt;−1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;. Если граф не регулярен, спектр графа может быть определён аналогичным образом, используя собственные значения матрицы Кирхгофа. Для направленного графа используются сингулярные значения матрицы сопряжения A, которые равны квадратным корням из собственных значений симметричной матрицы &amp;lt;tex&amp;gt;A^TA&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Конструирование==&lt;br /&gt;
Существуют три основные стратегии создания семейств графов расширений[6]. Первая стратегия — алгебраическая и теоретико-групповая, вторая — аналитическая, использующая аддитивную комбинаторику, и третья — комбинаторная, использующая зигзаг-произведение и связанные комбинаторные произведения.&lt;br /&gt;
===Маргулис-Габбер-Галил===&lt;br /&gt;
Алгебраическое конструирование, основанное на графах Кэли, известно для различных вариантов экспандеров. Следующее конструирование принадлежит Маргулису и было проанализировано Габбером (Gabber) и Галилом (Galil)[7]. Для любого натурального n строим граф, Gn со множеством вершин \mathbb {Z} _{n}\times \mathbb {Z} _{n}, где \mathbb {Z} _{n}=\mathbb {Z} /n\mathbb {Z} . Для любой вершины (x,y)\in \mathbb {Z} _{n}\times \mathbb {Z} _{n}, её восемь соседей будут&lt;br /&gt;
// formulas&lt;br /&gt;
Выполняется следующая теорема:&lt;br /&gt;
//theorem&lt;br /&gt;
===Граф Рамануджана===&lt;br /&gt;
По теореме Алона (Alon) и Боппана (Boppana) для всех достаточно больших d-регулярных графов выполняется неравенство \lambda \geq 2{\sqrt {d-1}}-o(1), где λ — второе по абсолютной величине собственное число[1]. Для графов Рамануджана[en]\lambda \leq 2{\sqrt {d-1}} [1]. Таким образом, графы Рамануджана имеют асимптотически наименьшее возможное значение λ и являются превосходными спектральными расширителями.&lt;br /&gt;
&lt;br /&gt;
Александр Любоцкий, Филипс и Сарнак (1988), Маргулис (1988) и Моргенштерн (1994) показали как можно явно сконструировать граф Рамануджана[1]. По теореме Фридмана (Friedman,2003) случайный d-регулярный граф[en] с n вершинами является почти графом Рамануджана, поскольку выполняется&lt;br /&gt;
//formulas&lt;br /&gt;
==Приложения и полезные свойства==&lt;br /&gt;
Первоначально интерес к экспандерам возник с целью построения устойчивой сети (телефонов или компьютеров) — число дуг графов расширения с ограниченным значением регулярности растет линейно по отношению к числу вершин.&lt;br /&gt;
&lt;br /&gt;
Экспандеры нашли широкое применение в теории вычислительных машин и систем, для построения алгоритмов, в корректирующих кодах[en], экстракторах[en], генераторах псевдослучайных чисел, сетях сортировки[8] и компьютерных сетях. Они также используются для доказательства многих важных результатов в теории вычислительной сложности, таких как SL[en]=L[en][9] и Теорема PCP[10]. В rриптографии экспандеры используются для создания хеш-функций.&lt;br /&gt;
&lt;br /&gt;
Ниже приведены некоторые свойства экспандеров, считающиеся полезными во многих областях.&lt;br /&gt;
===Лемма о перемешивании===&lt;br /&gt;
Лемма о перемешивании утверждает, что для любых двух подмножеств вершин S,T\subseteq V число рёбер между S и T примерно равно числу рёбер в случайном d-регулярном графе. Приближение тем лучше, чем меньше \lambda =\max\{|\lambda _{2}|,|\lambda _{n}|\}. В случайном d-регулярном графе, также как и в случайном графе Эрдёша — Реньи[en]с вероятностью {\tfrac {d}{n}} выбора ребра, ожидается {\tfrac {d}{n}}\cdot |S|\cdot |T| рёбер между S и T.&lt;br /&gt;
&lt;br /&gt;
Более формально, пусть E(S, T) обозначает число рёбер между S и T. Если эти два множества пересекаются, дуги в пересечении считаются дважды, так что&lt;br /&gt;
&lt;br /&gt;
E(S,T)=2|E(G[S\cap T])|+E(S\setminus T,T)+E(S,T\setminus S).&lt;br /&gt;
Лемма о перемешивании утверждает, что[11]&lt;br /&gt;
&lt;br /&gt;
\left|E(S,T)-{\frac {d\cdot |S|\cdot |T|}{n}}\right|\leq d\lambda {\sqrt {|S|\cdot |T|}},&lt;br /&gt;
где λ — абсолютное значение нормализованного второго по величине собственного значения.&lt;br /&gt;
&lt;br /&gt;
Недавно Билу (Bilu) и Линайл (Linial) показали, что обратное тоже верно, то есть, при условии выполнения неравенства из леммы второе по величине собственное значение равно O(d\lambda \cdot (1+\log(1/\lambda )))[12].&lt;br /&gt;
===Блуждания по экспандеру===&lt;br /&gt;
Согласно границе Чернова[en], если выбирать много независимых случайных значений из интервала [−1, 1], с большой степенью вероятности среднее выбранных значений будет близко к математическому ожиданию случайной переменной. Лемма о блуждании по экспандеру, согласно статьям Аджтари, Комлоша и Семереди[13] и Гилмана[14], утверждает, что то же самое верно и для блужданий по экспандеру. Это полезно в теории дерандомизации[en], поскольку блуждание по экспандеру использует много меньше случайных бит, чем случайная независимая выборка.&lt;br /&gt;
==Источники==&lt;br /&gt;
// на потом&lt;/div&gt;</summary>
		<author><name>Kozichuk</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%93%D1%80%D0%B0%D1%84%D1%8B-%D1%8D%D0%BA%D1%81%D0%BF%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%8B&amp;diff=50248</id>
		<title>Графы-экспандеры</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%93%D1%80%D0%B0%D1%84%D1%8B-%D1%8D%D0%BA%D1%81%D0%BF%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%8B&amp;diff=50248"/>
				<updated>2015-12-16T23:58:37Z</updated>
		
		<summary type="html">&lt;p&gt;Kozichuk: /* Вершинное расширение */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Граф-экспандер''' (англ. ''expander graph'') - в комбинаторике сильно разреженный граф, при этом связность определяется по вершинам, дугам или спектру.&lt;br /&gt;
&lt;br /&gt;
==Определение==&lt;br /&gt;
Экспандер — это конечный ненаправленный мультиграф, в котором любое подмножество вершин, не являясь «слишком большим», имеет «сильную» связность. Различные формализации этих понятий дают различные типы экспандеров: рёберный расширитель, вершинный расширитель, и спектральный расширитель.&lt;br /&gt;
&lt;br /&gt;
Несвязный граф не является экспандером. Любой связный граф является экспандером, однако различные связные графы имеют различные параметры расширителя. Полный граф имеет лучшие параметры расширителя, но имеет наибольшую возможную степень. Неформально говоря, граф является хорошим экспандером, если он имеет низкую степень и высокий параметр расширителя.&lt;br /&gt;
&lt;br /&gt;
===Реберное расширение===&lt;br /&gt;
Рёберное расширение (также изопериметрическое число или константа Чигера) &amp;lt;tex&amp;gt;h(G)&amp;lt;/tex&amp;gt; графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; для &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; вершин определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;h(G) = \min_{0 &amp;lt; |S|\le \frac{n}{2}} \frac{|\partial_{\text{out}}(S)|}{|S|}&amp;lt;/tex&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где минимум берётся по всем непустым множествам &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; не более чем &amp;lt;tex&amp;gt;n/2&amp;lt;/tex&amp;gt; вершин и &amp;lt;tex&amp;gt;∂(S)&amp;lt;/tex&amp;gt; — граничные дуги множества &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, то есть, множество дуг с единственной вершиной в &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Вершинное расширение===&lt;br /&gt;
Вершинное изопериметрическое число &amp;lt;tex&amp;gt;h_{out}(G)&amp;lt;/tex&amp;gt; (также вершинное раширение) графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;h_{out}(G) = \min_{0 &amp;lt; |S|\le \frac{n}{2}} \frac{|\partial_{\text{out}}(S)|}{|S|},&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;tex&amp;gt;∂_{out}(S)&amp;lt;/tex&amp;gt; — внешняя граница &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, то есть множество вершин из &amp;lt;tex&amp;gt;V(G)\setminus S&amp;lt;/tex&amp;gt;, имеющих как минимум одного соседа в S. В варианте этого определения (называемом уникальным соседним расширением) &amp;lt;tex&amp;gt;∂_{out}(S)&amp;lt;/tex&amp;gt; заменяется на множество вершин из &amp;lt;tex&amp;gt;V&amp;lt;/tex&amp;gt; с точностью одним соседом из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Вершинное изопериметрическое число &amp;lt;tex&amp;gt;h_{in}(G)&amp;lt;/tex&amp;gt; графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;h_{in}(G) = \min_{0 &amp;lt; |S|\le \frac{n}{2}} \frac{|\partial_{\text{in}}(S)|}{|S|},&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;tex&amp;gt;∂_{in}(S)&amp;lt;/tex&amp;gt; — внутренняя граница &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, то есть множество вершин &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, имеющих как минимум одного соседа в &amp;lt;tex&amp;gt;V(G)\setminus S&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Спектральное расширение===&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; является d-регулярным, возможно определение в терминах линейной алгебры на основе собственных значений матрицы смежности &amp;lt;tex&amp;gt;A = A(G)&amp;lt;/tex&amp;gt; графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;A_{ij}&amp;lt;/tex&amp;gt; — число дуг между вершинами &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;. Поскольку &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; является симметричной, согласно спектральной теореме, &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; имеет &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; действительных собственных значений &amp;lt;tex&amp;gt;\lambda_1 \ge \lambda_2 \ge \cdots \ge \lambda_{n}&amp;lt;/tex&amp;gt;. Известно, что эти значения лежат в промежутке &amp;lt;tex&amp;gt;[−d, d]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
Граф регулярен тогда и только тогда, когда вектор &amp;lt;tex&amp;gt;u\in \mathbb {R} ^{n} с u_{i}=1&amp;lt;/tex&amp;gt; для всех &amp;lt;tex&amp;gt;i = 1, …, n&amp;lt;/tex&amp;gt; является собственным вектором матрицы &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt;, а его собственное число будет постоянной степенью графа. Таким образом, мы имеем &amp;lt;tex&amp;gt;Au = du&amp;lt;/tex&amp;gt;, и &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt; — собственный вектор матрицы &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; с собственными значениями &amp;lt;tex&amp;gt;λ1 = d&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt; — степень вершин графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt;. Спектральный зазор графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; определяется как &amp;lt;tex&amp;gt;d−λ2&amp;lt;t/ex&amp;gt; и является мерилом спектрального расширения графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Известно, что &amp;lt;tex&amp;gt;λ_n = −d&amp;lt;/tex&amp;gt; тогда и только тогда, когда &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; — двудольный. Во многих случаях, например в лемме о перемешивании, необходимо ограничить снизу не только зазор между &amp;lt;tex&amp;gt;λ1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;λ2&amp;lt;/tex&amp;gt;, но и зазор между &amp;lt;tex&amp;gt;λ1&amp;lt;/tex&amp;gt; и вторым максимальным по модулю собственным значением:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;// formuli&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Поскольку это собственное значение соответствует некоторому собственному вектору, ортогональному &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;, его можно определить, используя отношение Рэлея:&lt;br /&gt;
&amp;lt;tex&amp;gt;//formulas&amp;lt;/tex&amp;gt;&lt;br /&gt;
gde&lt;br /&gt;
&amp;lt;tex&amp;gt;//formulas&amp;lt;/tex&amp;gt;&lt;br /&gt;
— евклидова норма вектора &amp;lt;tex&amp;gt;v\in \mathbb {R} ^{n}&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Нормализованная версия этого определения также широко используется и более удобна для получения определённых результатов. В таком случае рассматривается матрица &amp;lt;tex&amp;gt;{\tfrac {1}{d}}A&amp;lt;/tex&amp;gt;, являющаяся матрицей переходов графа G. Все её собственные значения лежат между &amp;lt;tex&amp;gt;−1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;. Если граф не регулярен, спектр графа может быть определён аналогичным образом, используя собственные значения матрицы Кирхгофа. Для направленного графа используются сингулярные значения матрицы сопряжения A, которые равны квадратным корням из собственных значений симметричной матрицы &amp;lt;tex&amp;gt;A^TA&amp;lt;/tex&amp;gt;.&lt;br /&gt;
==Конструирование==&lt;br /&gt;
Существуют три основные стратегии создания семейств графов расширений[6]. Первая стратегия — алгебраическая и теоретико-групповая, вторая — аналитическая, использующая аддитивную комбинаторику, и третья — комбинаторная, использующая зигзаг-произведение и связанные комбинаторные произведения.&lt;br /&gt;
===Маргулис-Габбер-Галил===&lt;br /&gt;
Алгебраическое конструирование, основанное на графах Кэли, известно для различных вариантов экспандеров. Следующее конструирование принадлежит Маргулису и было проанализировано Габбером (Gabber) и Галилом (Galil)[7]. Для любого натурального n строим граф, Gn со множеством вершин \mathbb {Z} _{n}\times \mathbb {Z} _{n}, где \mathbb {Z} _{n}=\mathbb {Z} /n\mathbb {Z} . Для любой вершины (x,y)\in \mathbb {Z} _{n}\times \mathbb {Z} _{n}, её восемь соседей будут&lt;br /&gt;
// formulas&lt;br /&gt;
Выполняется следующая теорема:&lt;br /&gt;
//theorem&lt;br /&gt;
===Граф Рамануджана===&lt;br /&gt;
По теореме Алона (Alon) и Боппана (Boppana) для всех достаточно больших d-регулярных графов выполняется неравенство \lambda \geq 2{\sqrt {d-1}}-o(1), где λ — второе по абсолютной величине собственное число[1]. Для графов Рамануджана[en]\lambda \leq 2{\sqrt {d-1}} [1]. Таким образом, графы Рамануджана имеют асимптотически наименьшее возможное значение λ и являются превосходными спектральными расширителями.&lt;br /&gt;
&lt;br /&gt;
Александр Любоцкий, Филипс и Сарнак (1988), Маргулис (1988) и Моргенштерн (1994) показали как можно явно сконструировать граф Рамануджана[1]. По теореме Фридмана (Friedman,2003) случайный d-регулярный граф[en] с n вершинами является почти графом Рамануджана, поскольку выполняется&lt;br /&gt;
//formulas&lt;br /&gt;
==Приложения и полезные свойства==&lt;br /&gt;
Первоначально интерес к экспандерам возник с целью построения устойчивой сети (телефонов или компьютеров) — число дуг графов расширения с ограниченным значением регулярности растет линейно по отношению к числу вершин.&lt;br /&gt;
&lt;br /&gt;
Экспандеры нашли широкое применение в теории вычислительных машин и систем, для построения алгоритмов, в корректирующих кодах[en], экстракторах[en], генераторах псевдослучайных чисел, сетях сортировки[8] и компьютерных сетях. Они также используются для доказательства многих важных результатов в теории вычислительной сложности, таких как SL[en]=L[en][9] и Теорема PCP[10]. В rриптографии экспандеры используются для создания хеш-функций.&lt;br /&gt;
&lt;br /&gt;
Ниже приведены некоторые свойства экспандеров, считающиеся полезными во многих областях.&lt;br /&gt;
===Лемма о перемешивании===&lt;br /&gt;
Лемма о перемешивании утверждает, что для любых двух подмножеств вершин S,T\subseteq V число рёбер между S и T примерно равно числу рёбер в случайном d-регулярном графе. Приближение тем лучше, чем меньше \lambda =\max\{|\lambda _{2}|,|\lambda _{n}|\}. В случайном d-регулярном графе, также как и в случайном графе Эрдёша — Реньи[en]с вероятностью {\tfrac {d}{n}} выбора ребра, ожидается {\tfrac {d}{n}}\cdot |S|\cdot |T| рёбер между S и T.&lt;br /&gt;
&lt;br /&gt;
Более формально, пусть E(S, T) обозначает число рёбер между S и T. Если эти два множества пересекаются, дуги в пересечении считаются дважды, так что&lt;br /&gt;
&lt;br /&gt;
E(S,T)=2|E(G[S\cap T])|+E(S\setminus T,T)+E(S,T\setminus S).&lt;br /&gt;
Лемма о перемешивании утверждает, что[11]&lt;br /&gt;
&lt;br /&gt;
\left|E(S,T)-{\frac {d\cdot |S|\cdot |T|}{n}}\right|\leq d\lambda {\sqrt {|S|\cdot |T|}},&lt;br /&gt;
где λ — абсолютное значение нормализованного второго по величине собственного значения.&lt;br /&gt;
&lt;br /&gt;
Недавно Билу (Bilu) и Линайл (Linial) показали, что обратное тоже верно, то есть, при условии выполнения неравенства из леммы второе по величине собственное значение равно O(d\lambda \cdot (1+\log(1/\lambda )))[12].&lt;br /&gt;
===Блуждания по экспандеру===&lt;br /&gt;
Согласно границе Чернова[en], если выбирать много независимых случайных значений из интервала [−1, 1], с большой степенью вероятности среднее выбранных значений будет близко к математическому ожиданию случайной переменной. Лемма о блуждании по экспандеру, согласно статьям Аджтари, Комлоша и Семереди[13] и Гилмана[14], утверждает, что то же самое верно и для блужданий по экспандеру. Это полезно в теории дерандомизации[en], поскольку блуждание по экспандеру использует много меньше случайных бит, чем случайная независимая выборка.&lt;br /&gt;
==Источники==&lt;br /&gt;
// на потом&lt;/div&gt;</summary>
		<author><name>Kozichuk</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%93%D1%80%D0%B0%D1%84%D1%8B-%D1%8D%D0%BA%D1%81%D0%BF%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%8B&amp;diff=50247</id>
		<title>Графы-экспандеры</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%93%D1%80%D0%B0%D1%84%D1%8B-%D1%8D%D0%BA%D1%81%D0%BF%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%8B&amp;diff=50247"/>
				<updated>2015-12-16T23:55:34Z</updated>
		
		<summary type="html">&lt;p&gt;Kozichuk: Новая страница: «'''Граф-экспандер''' (англ. ''expander graph'') - в комбинаторике сильно разреженный граф, при этом с...»&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Граф-экспандер''' (англ. ''expander graph'') - в комбинаторике сильно разреженный граф, при этом связность определяется по вершинам, дугам или спектру.&lt;br /&gt;
&lt;br /&gt;
==Определение==&lt;br /&gt;
Экспандер — это конечный ненаправленный мультиграф, в котором любое подмножество вершин, не являясь «слишком большим», имеет «сильную» связность. Различные формализации этих понятий дают различные типы экспандеров: рёберный расширитель, вершинный расширитель, и спектральный расширитель.&lt;br /&gt;
&lt;br /&gt;
Несвязный граф не является экспандером. Любой связный граф является экспандером, однако различные связные графы имеют различные параметры расширителя. Полный граф имеет лучшие параметры расширителя, но имеет наибольшую возможную степень. Неформально говоря, граф является хорошим экспандером, если он имеет низкую степень и высокий параметр расширителя.&lt;br /&gt;
&lt;br /&gt;
===Реберное расширение===&lt;br /&gt;
Рёберное расширение (также изопериметрическое число или константа Чигера) &amp;lt;tex&amp;gt;h(G)&amp;lt;/tex&amp;gt; графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; для &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; вершин определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;h(G) = \min_{0 &amp;lt; |S|\le \frac{n}{2}} \frac{|\partial_{\text{out}}(S)|}{|S|}&amp;lt;/tex&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где минимум берётся по всем непустым множествам &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; не более чем &amp;lt;tex&amp;gt;n/2&amp;lt;/tex&amp;gt; вершин и &amp;lt;tex&amp;gt;∂(S)&amp;lt;/tex&amp;gt; — граничные дуги множества &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, то есть, множество дуг с единственной вершиной в &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Вершинное расширение===&lt;br /&gt;
Вершинное изопериметрическое число &amp;lt;tex&amp;gt;h_out(G)&amp;lt;/tex&amp;gt; (также вершинное раширение) графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;h_out(G) = \min_{0 &amp;lt; |S|\le \frac{n}{2}} \frac{|\partial_{\text{out}}(S)|}{|S|},&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;tex&amp;gt;∂_out(S)&amp;lt;/tex&amp;gt; — внешняя граница &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, то есть множество вершин из &amp;lt;tex&amp;gt;V(G)\S&amp;lt;/tex&amp;gt;, имеющих как минимум одного соседа в S. В варианте этого определения (называемом уникальным соседним расширением) &amp;lt;tex&amp;gt;∂_out(S)&amp;lt;/tex&amp;gt; заменяется на множество вершин из &amp;lt;tex&amp;gt;V&amp;lt;/tex&amp;gt; с точностью одним соседом из &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Вершинное изопериметрическое число &amp;lt;tex&amp;gt;h_in(G)&amp;lt;/tex&amp;gt; графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; определяется как&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;h_in(G) = \min_{0 &amp;lt; |S|\le \frac{n}{2}} \frac{|\partial_{\text{in}}(S)|}{|S|},&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;tex&amp;gt;∂_in(S)(S)&amp;lt;/tex&amp;gt; — внутренняя граница &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, то есть множество вершин &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, имеющих как минимум одного соседа в &amp;lt;tex&amp;gt;V(G)\setminus S&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Спектральное расширение===&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; является d-регулярным, возможно определение в терминах линейной алгебры на основе собственных значений матрицы смежности &amp;lt;tex&amp;gt;A = A(G)&amp;lt;/tex&amp;gt; графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;A_{ij}&amp;lt;/tex&amp;gt; — число дуг между вершинами &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;. Поскольку &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; является симметричной, согласно спектральной теореме, &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; имеет &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; действительных собственных значений &amp;lt;tex&amp;gt;\lambda_1 \ge \lambda_2 \ge \cdots \ge \lambda_{n}&amp;lt;/tex&amp;gt;. Известно, что эти значения лежат в промежутке &amp;lt;tex&amp;gt;[−d, d]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
Граф регулярен тогда и только тогда, когда вектор &amp;lt;tex&amp;gt;u\in \mathbb {R} ^{n} с u_{i}=1&amp;lt;/tex&amp;gt; для всех &amp;lt;tex&amp;gt;i = 1, …, n&amp;lt;/tex&amp;gt; является собственным вектором матрицы &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt;, а его собственное число будет постоянной степенью графа. Таким образом, мы имеем &amp;lt;tex&amp;gt;Au = du&amp;lt;/tex&amp;gt;, и &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt; — собственный вектор матрицы &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; с собственными значениями &amp;lt;tex&amp;gt;λ1 = d&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt; — степень вершин графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt;. Спектральный зазор графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; определяется как &amp;lt;tex&amp;gt;d−λ2&amp;lt;t/ex&amp;gt; и является мерилом спектрального расширения графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Известно, что &amp;lt;tex&amp;gt;λ_n = −d&amp;lt;/tex&amp;gt; тогда и только тогда, когда &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; — двудольный. Во многих случаях, например в лемме о перемешивании, необходимо ограничить снизу не только зазор между &amp;lt;tex&amp;gt;λ1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;λ2&amp;lt;/tex&amp;gt;, но и зазор между &amp;lt;tex&amp;gt;λ1&amp;lt;/tex&amp;gt; и вторым максимальным по модулю собственным значением:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;// formuli&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Поскольку это собственное значение соответствует некоторому собственному вектору, ортогональному &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;, его можно определить, используя отношение Рэлея:&lt;br /&gt;
&amp;lt;tex&amp;gt;//formulas&amp;lt;/tex&amp;gt;&lt;br /&gt;
gde&lt;br /&gt;
&amp;lt;tex&amp;gt;//formulas&amp;lt;/tex&amp;gt;&lt;br /&gt;
— евклидова норма вектора &amp;lt;tex&amp;gt;v\in \mathbb {R} ^{n}&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Нормализованная версия этого определения также широко используется и более удобна для получения определённых результатов. В таком случае рассматривается матрица &amp;lt;tex&amp;gt;{\tfrac {1}{d}}A&amp;lt;/tex&amp;gt;, являющаяся матрицей переходов графа G. Все её собственные значения лежат между &amp;lt;tex&amp;gt;−1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;. Если граф не регулярен, спектр графа может быть определён аналогичным образом, используя собственные значения матрицы Кирхгофа. Для направленного графа используются сингулярные значения матрицы сопряжения A, которые равны квадратным корням из собственных значений симметричной матрицы &amp;lt;tex&amp;gt;A^TA&amp;lt;/tex&amp;gt;.&lt;br /&gt;
==Конструирование==&lt;br /&gt;
Существуют три основные стратегии создания семейств графов расширений[6]. Первая стратегия — алгебраическая и теоретико-групповая, вторая — аналитическая, использующая аддитивную комбинаторику, и третья — комбинаторная, использующая зигзаг-произведение и связанные комбинаторные произведения.&lt;br /&gt;
===Маргулис-Габбер-Галил===&lt;br /&gt;
Алгебраическое конструирование, основанное на графах Кэли, известно для различных вариантов экспандеров. Следующее конструирование принадлежит Маргулису и было проанализировано Габбером (Gabber) и Галилом (Galil)[7]. Для любого натурального n строим граф, Gn со множеством вершин \mathbb {Z} _{n}\times \mathbb {Z} _{n}, где \mathbb {Z} _{n}=\mathbb {Z} /n\mathbb {Z} . Для любой вершины (x,y)\in \mathbb {Z} _{n}\times \mathbb {Z} _{n}, её восемь соседей будут&lt;br /&gt;
// formulas&lt;br /&gt;
Выполняется следующая теорема:&lt;br /&gt;
//theorem&lt;br /&gt;
===Граф Рамануджана===&lt;br /&gt;
По теореме Алона (Alon) и Боппана (Boppana) для всех достаточно больших d-регулярных графов выполняется неравенство \lambda \geq 2{\sqrt {d-1}}-o(1), где λ — второе по абсолютной величине собственное число[1]. Для графов Рамануджана[en]\lambda \leq 2{\sqrt {d-1}} [1]. Таким образом, графы Рамануджана имеют асимптотически наименьшее возможное значение λ и являются превосходными спектральными расширителями.&lt;br /&gt;
&lt;br /&gt;
Александр Любоцкий, Филипс и Сарнак (1988), Маргулис (1988) и Моргенштерн (1994) показали как можно явно сконструировать граф Рамануджана[1]. По теореме Фридмана (Friedman,2003) случайный d-регулярный граф[en] с n вершинами является почти графом Рамануджана, поскольку выполняется&lt;br /&gt;
//formulas&lt;br /&gt;
==Приложения и полезные свойства==&lt;br /&gt;
Первоначально интерес к экспандерам возник с целью построения устойчивой сети (телефонов или компьютеров) — число дуг графов расширения с ограниченным значением регулярности растет линейно по отношению к числу вершин.&lt;br /&gt;
&lt;br /&gt;
Экспандеры нашли широкое применение в теории вычислительных машин и систем, для построения алгоритмов, в корректирующих кодах[en], экстракторах[en], генераторах псевдослучайных чисел, сетях сортировки[8] и компьютерных сетях. Они также используются для доказательства многих важных результатов в теории вычислительной сложности, таких как SL[en]=L[en][9] и Теорема PCP[10]. В rриптографии экспандеры используются для создания хеш-функций.&lt;br /&gt;
&lt;br /&gt;
Ниже приведены некоторые свойства экспандеров, считающиеся полезными во многих областях.&lt;br /&gt;
===Лемма о перемешивании===&lt;br /&gt;
Лемма о перемешивании утверждает, что для любых двух подмножеств вершин S,T\subseteq V число рёбер между S и T примерно равно числу рёбер в случайном d-регулярном графе. Приближение тем лучше, чем меньше \lambda =\max\{|\lambda _{2}|,|\lambda _{n}|\}. В случайном d-регулярном графе, также как и в случайном графе Эрдёша — Реньи[en]с вероятностью {\tfrac {d}{n}} выбора ребра, ожидается {\tfrac {d}{n}}\cdot |S|\cdot |T| рёбер между S и T.&lt;br /&gt;
&lt;br /&gt;
Более формально, пусть E(S, T) обозначает число рёбер между S и T. Если эти два множества пересекаются, дуги в пересечении считаются дважды, так что&lt;br /&gt;
&lt;br /&gt;
E(S,T)=2|E(G[S\cap T])|+E(S\setminus T,T)+E(S,T\setminus S).&lt;br /&gt;
Лемма о перемешивании утверждает, что[11]&lt;br /&gt;
&lt;br /&gt;
\left|E(S,T)-{\frac {d\cdot |S|\cdot |T|}{n}}\right|\leq d\lambda {\sqrt {|S|\cdot |T|}},&lt;br /&gt;
где λ — абсолютное значение нормализованного второго по величине собственного значения.&lt;br /&gt;
&lt;br /&gt;
Недавно Билу (Bilu) и Линайл (Linial) показали, что обратное тоже верно, то есть, при условии выполнения неравенства из леммы второе по величине собственное значение равно O(d\lambda \cdot (1+\log(1/\lambda )))[12].&lt;br /&gt;
===Блуждания по экспандеру===&lt;br /&gt;
Согласно границе Чернова[en], если выбирать много независимых случайных значений из интервала [−1, 1], с большой степенью вероятности среднее выбранных значений будет близко к математическому ожиданию случайной переменной. Лемма о блуждании по экспандеру, согласно статьям Аджтари, Комлоша и Семереди[13] и Гилмана[14], утверждает, что то же самое верно и для блужданий по экспандеру. Это полезно в теории дерандомизации[en], поскольку блуждание по экспандеру использует много меньше случайных бит, чем случайная независимая выборка.&lt;br /&gt;
==Источники==&lt;br /&gt;
// на потом&lt;/div&gt;</summary>
		<author><name>Kozichuk</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%A0%D0%B0%D0%B7%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BA%D0%BE%D0%BB%D0%BB%D0%B8%D0%B7%D0%B8%D0%B9&amp;diff=47806</id>
		<title>Разрешение коллизий</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A0%D0%B0%D0%B7%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BA%D0%BE%D0%BB%D0%BB%D0%B8%D0%B7%D0%B8%D0%B9&amp;diff=47806"/>
				<updated>2015-06-06T16:33:51Z</updated>
		
		<summary type="html">&lt;p&gt;Kozichuk: /* Реализация метода цепочек на практике */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Разрешение [[Хеш-таблица|коллизий]]''' (англ. collision resolution) в [[Хеш-таблица|хеш-таблице]], задача, решаемая несколькими способами: метод цепочек, открытая адресация и т.д. Очень важно сводить количество коллизий к минимуму, так как это увеличивает время работы с хеш-таблицами. &lt;br /&gt;
&lt;br /&gt;
== Разрешение коллизий с помощью цепочек ==&lt;br /&gt;
[[Файл:open_hash.png|thumb|380px|right|Разрешение коллизий при помощи цепочек.]]&lt;br /&gt;
Каждая ячейка &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; массива &amp;lt;tex&amp;gt;H&amp;lt;/tex&amp;gt; содержит указатель на начало [[Список|списка]] всех элементов, хеш-код которых равен &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, либо указывает на их отсутствие. Коллизии приводят к тому, что появляются списки размером больше одного элемента.&lt;br /&gt;
&lt;br /&gt;
В зависимости от того нужна ли нам уникальность значений операции вставки у нас будет работать за разное время. Если не важна, то мы используем список, время вставки в который будет в худшем случае равна &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;. Иначе мы проверяем есть ли в списке данный элемент, а потом в случае его отсутствия мы его добавляем. В таком случае вставка элемента в худшем случае будет выполнена за &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Время работы поиска в наихудшем случае пропорционально длине списка, а если все &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; ключей захешировались в одну и ту же ячейку (создав список длиной &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;) время поиска будет равно &amp;lt;tex&amp;gt;\Theta(n)&amp;lt;/tex&amp;gt; плюс время вычисления хеш-функции, что ничуть не лучше, чем использование связного списка для хранения всех &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; элементов.&lt;br /&gt;
&lt;br /&gt;
Удаления элемента может быть выполнено за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;, как и вставка, при использовании двухсвязного списка.&lt;br /&gt;
&lt;br /&gt;
== Линейное разрешение коллизий ==&lt;br /&gt;
[[Файл:close_hash.png|thumb|380px|right|Пример хеш-таблицы с открытой адресацией и линейным пробированием.]]&lt;br /&gt;
Все элементы хранятся непосредственно в хеш-таблице, без использования связных списков. В отличии от хеширования с цепочками, при использовании этого метода может возникнуть ситуация, когда хеш-таблица окажется полностью заполненной, следовательно будет невозможно добавлять в неё новые элементы. Так что при возникновении такой ситуации решением может быть динамическое увеличение размера хеш-таблицы, с одновременной её перестройкой.&lt;br /&gt;
&lt;br /&gt;
=== Стратегии поиска ===&lt;br /&gt;
&lt;br /&gt;
''' Последовательный поиск '''&lt;br /&gt;
&lt;br /&gt;
При попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt;i+1, i+2, i+3&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку. В неё и запишем элемент.&lt;br /&gt;
&lt;br /&gt;
[[Файл:hashtables1.png|400px|Последовательный поиск, частный случай линейного поиска.]]&lt;br /&gt;
&lt;br /&gt;
''' Линейный поиск '''&lt;br /&gt;
&lt;br /&gt;
Выбираем шаг &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;. При попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt;i+(1 \cdot q), i+(2 \cdot q), i+(3 \cdot q)&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку. В неё и запишем элемент.&lt;br /&gt;
По сути последовательный поиск - частный случай линейного, где &amp;lt;tex&amp;gt;q=1&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Hashtables56.PNG|400px|Линейный поиск с шагом q.]]&lt;br /&gt;
&lt;br /&gt;
''' Квадратичный поиск '''&lt;br /&gt;
&lt;br /&gt;
Шаг &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; не фиксирован, а изменяется квадратично: &amp;lt;tex&amp;gt;q = 1,4,9,16...&amp;lt;/tex&amp;gt;. Соответственно при попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt; i+1, i+4, i+9&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку.&lt;br /&gt;
&lt;br /&gt;
[[Файл:hashtables3.png|400px|Квадратичный поиск.]]&lt;br /&gt;
&lt;br /&gt;
=== Проверка наличия элемента в таблице===&lt;br /&gt;
&lt;br /&gt;
Проверка осуществляется аналогично добавлению: мы проверяем ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; и другие, в соответствии с выбранной стратегией, пока не найдём искомый элемент или свободную ячейку.&lt;br /&gt;
&lt;br /&gt;
При поиске элемента может получится так, что мы дойдём до конца таблицы. Обычно поиск продолжается, начиная с другого конца, пока мы не придём в ту ячейку, откуда начинался поиск.&lt;br /&gt;
&lt;br /&gt;
=== Проблемы данных стратегий ===&lt;br /&gt;
&lt;br /&gt;
Проблем две — крайне нетривиальное удаление элемента из таблицы и образование кластеров  — последовательностей занятых ячеек.&lt;br /&gt;
&lt;br /&gt;
Кластеризация замедляет все операции с хеш-таблицей: при добавлении требуется перебирать всё больше элементов, при проверке тоже. Чем больше в таблице элементов, тем больше в ней кластеры и тем выше вероятность того, что добавляемый элемент попадёт в кластер.&lt;br /&gt;
Для защиты от кластеризации используется двойное хеширование и [[Хеширование кукушки|хеширование кукушки]].&lt;br /&gt;
&lt;br /&gt;
=== Удаление элемента без пометок ===&lt;br /&gt;
&lt;br /&gt;
Рассуждение будет описывать случай с линейным поиском хеша. Будем при удалении элемента сдвигать всё последующие на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; позиций назад. При этом:&lt;br /&gt;
* если в цепочке встречается элемент с другим хешем, то он должен остаться на своём месте (такая ситуация может возникнуть если оставшаяся часть цепочки была добавлена позже этого элемента)&lt;br /&gt;
* в цепочке не должно оставаться &amp;quot;дырок&amp;quot;, тогда любой элемент с данным хешем будет доступен из начала цепи&lt;br /&gt;
&lt;br /&gt;
Учитывая это будем действовать следующим образом: при поиске следующего элемента цепочки будем пропускать все ячейки с другим значением хеша, первый найденный элемент копировать в текущую ячейку, и затем рекурсивно его удалять. Если такой следующей ячейки нет, то текущий элемент можно просто удалить, сторонние цепочки при этом не разрушатся (чего нельзя сказать про случай квадратичного поиска).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
''' Псевдокод '''&lt;br /&gt;
&lt;br /&gt;
 '''function''' delete('''Item''' i):&lt;br /&gt;
      j = i + q&lt;br /&gt;
      '''while''' table[j] == ''null'' '''or''' table[j].key != table[i].key&lt;br /&gt;
         '''if''' table[j] == ''null''&lt;br /&gt;
            table[i] = ''null''&lt;br /&gt;
            '''return'''&lt;br /&gt;
         j += q&lt;br /&gt;
      table[i] = table[j]&lt;br /&gt;
      delete(j)    &lt;br /&gt;
&lt;br /&gt;
Хеш-таблицу считаем зацикленной&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Утверждение&lt;br /&gt;
|about=о времени работы&lt;br /&gt;
|statement=Асимптотически время работы &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\mathrm{find}&amp;lt;/tex&amp;gt; совпадают&lt;br /&gt;
|proof=&lt;br /&gt;
Заметим что указатель &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; в каждой итерации перемещается вперёд на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; (с учётом рекурсивных вызовов &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt;). То есть этот алгоритм последовательно пройдёт по цепочке от удаляемого элемента до последнего {{---}} с учётом вызова &amp;lt;tex&amp;gt;\mathrm{find}&amp;lt;/tex&amp;gt; собственно для нахождения удаляемого элемента, мы посетим все ячейки цепи.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Вариант с зацикливанием мы не рассматриваем, поскольку если &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; взаимнопросто с размером хеш-таблицы, то для зацикливания в ней вообще не должно быть свободных позиций&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Теперь докажем почему этот алгоритм работает. Собственно нам требуется сохранение трёх условий.&lt;br /&gt;
* В редактируемой цепи не остаётся дырок&lt;br /&gt;
Докажем по индукции. Если на данной итерации мы просто удаляем элемент (база), то после него ничего нет, всё верно. Если же нет, то вызванный в конце &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt; (см. псевдокод) заметёт созданную дыру (скопированный элемент), и сам, по предположению, новых не создаст.&lt;br /&gt;
* Элементы, которые уже на своих местах, не должны быть сдвинуты.&lt;br /&gt;
Это учтено.&lt;br /&gt;
* В других цепочках не появятся дыры&lt;br /&gt;
Противное возможно только в том случае, если какой-то элемент был действительно удалён. Удаляем мы только последнюю ячейку в цепи, и если бы на её месте возникла дыра для сторонней цепочки, это бы означало что элемент, стоящий на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; позиций назад, одновременно принадлежал нашей и другой цепочкам, что невозможно.&lt;br /&gt;
&lt;br /&gt;
==Двойное хеширование==&lt;br /&gt;
'''Двойное хеширование''' (англ. double hashing) {{---}} метод борьбы с коллизиями, возникающими при открытой адресации, основанный на использовании двух хеш-функций для построения различных последовательностей исследования хеш-таблицы.&lt;br /&gt;
&lt;br /&gt;
===Принцип двойного хеширования===&lt;br /&gt;
При двойном хешировании используются две независимые хеш-функции &amp;lt;tex&amp;gt; h_1(k) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; h_2(k) &amp;lt;/tex&amp;gt;. Пусть &amp;lt;tex&amp;gt; k &amp;lt;/tex&amp;gt; {{---}} это наш ключ, &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt; {{---}} размер нашей таблицы, &amp;lt;tex&amp;gt;n \bmod m &amp;lt;/tex&amp;gt; {{---}} остаток от деления &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt; на &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, тогда сначала исследуется ячейка с адресом &amp;lt;tex&amp;gt; h_1(k) &amp;lt;/tex&amp;gt;, если она уже занята, то рассматривается &amp;lt;tex&amp;gt; (h_1(k) +  h_2(k)) \bmod m &amp;lt;/tex&amp;gt;, затем &amp;lt;tex&amp;gt; (h_1(k) +  2 \cdot h_2(k)) \bmod m &amp;lt;/tex&amp;gt; и так далее. В общем случае идёт проверка последовательности ячеек &amp;lt;tex&amp;gt; (h_1(k) +  i \cdot h_2(k)) \bmod m &amp;lt;/tex&amp;gt; где &amp;lt;tex&amp;gt;  i = (0, 1, \; ... \;,  m - 1) &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, операции вставки, удаления и поиска в лучшем случае выполняются за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;, в худшем {{---}} за &amp;lt;tex&amp;gt;O(m)&amp;lt;/tex&amp;gt;, что не отличается от обычного [[Открытое_и_закрытое_хеширование#Линейное разрешение коллизий|линейного разрешения коллизий]].&lt;br /&gt;
Однако в среднем, при грамотном выборе хеш-функций, двойное хеширование будет выдавать лучшие результаты, за счёт того, что вероятность совпадения значений сразу двух независимых хеш-функций ниже, чем одной.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt;\forall x \neq y \; \exists h_1,h_2 : p(h_1(x)=h_1(y))&amp;gt; p((h_1(x)=h_1(y)) \land (h_2(x)=h_2(y)))&amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Выбор хеш-функций===&lt;br /&gt;
&amp;lt;tex&amp;gt; h_1 &amp;lt;/tex&amp;gt; может быть обычной хеш-функцией. Однако чтобы последовательность исследования могла охватить всю таблицу, &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; должна возвращать значения:&lt;br /&gt;
*не равные &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
*независимые  от &amp;lt;tex&amp;gt; h_1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
*взаимно простые с величиной хеш-таблицы&lt;br /&gt;
&lt;br /&gt;
Есть два удобных способа это сделать. Первый состоит в том, что в качестве размера таблицы используется простое число, а &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; возвращает натуральные числа, меньшие &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;. Второй {{---}} размер таблицы является степенью двойки, а &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; возвращает нечетные значения.&lt;br /&gt;
&lt;br /&gt;
Например, если размер таблицы равен &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, то в качестве &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; можно использовать функцию вида &amp;lt;tex&amp;gt; h_2(k) = k \bmod (m-1) + 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Файл: Вставка при двойном хэшировании.svg.jpeg|thumb|right|Вставка при двойном хешировании]]&lt;br /&gt;
&lt;br /&gt;
===Пример===&lt;br /&gt;
&lt;br /&gt;
Показана хеш-таблица размером 13 ячеек, в которой используются вспомогательные функции:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h(k,i) = (h_1(k) + i \cdot h_2(k)) \bmod 13 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h_1(k) = k \bmod 13 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h_2(k) = 1 + k \bmod 11 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Мы хотим вставить ключ 14. Изначально &amp;lt;tex&amp;gt; i = 0 &amp;lt;/tex&amp;gt;. Тогда &amp;lt;tex&amp;gt; h(14,0) = (h_1(14) + 0\cdot h_2(14)) \bmod 13 = 1 &amp;lt;/tex&amp;gt;. Но ячейка с индексом 1 занята, поэтому увеличиваем &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; на 1 и пересчитываем значение хеш-функции. Делаем так, пока не дойдем до пустой ячейки. При &amp;lt;tex&amp;gt; i = 2 &amp;lt;/tex&amp;gt; получаем &amp;lt;tex&amp;gt; h(14,2) = (h_1(14) + 2\cdot h_2(14)) \bmod 13 = 9 &amp;lt;/tex&amp;gt;. Ячейка с номером 9 свободна, значит записываем туда наш ключ.&lt;br /&gt;
&lt;br /&gt;
Таким образом, основная особенность двойного хеширования состоит в том, что при различных &amp;lt;tex&amp;gt; k &amp;lt;/tex&amp;gt; пара &amp;lt;tex&amp;gt; (h_1(k),h_2(k)) &amp;lt;/tex&amp;gt; дает различные последовательности ячеек для исследования.&lt;br /&gt;
&lt;br /&gt;
===Простая реализация===&lt;br /&gt;
Пусть у нас есть некоторый объект &amp;lt;tex&amp;gt; item &amp;lt;/tex&amp;gt;, в котором определено поле &amp;lt;tex&amp;gt; key &amp;lt;/tex&amp;gt;, от которого можно вычислить хеш-функции &amp;lt;tex&amp;gt; h_1(key)&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; h_2(key) &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Так же у нас есть таблица &amp;lt;tex&amp;gt; table &amp;lt;/tex&amp;gt; величиной &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, состоящая из объектов типа &amp;lt;tex&amp;gt; item &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
'''Вставка'''&lt;br /&gt;
 '''function''' add('''Item''' item):&lt;br /&gt;
      x = h1(item.key)&lt;br /&gt;
      y = h2(item.key)&lt;br /&gt;
         '''for''' (i = 0..m)    	&lt;br /&gt;
            '''if''' table[x] == ''null''&lt;br /&gt;
               table[x] = item&lt;br /&gt;
            '''return'''      &lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      table.resize()&amp;lt;span style=&amp;quot;color:Green&amp;quot;&amp;gt;// ошибка, требуется увеличить размер таблицы&lt;br /&gt;
&lt;br /&gt;
'''Поиск'''&lt;br /&gt;
 '''Item''' search('''Item''' key):&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m)&lt;br /&gt;
         '''if''' table[x] != ''null''&lt;br /&gt;
            '''if''' table[x].key == key&lt;br /&gt;
               '''return''' table[x]&lt;br /&gt;
            '''else'''&lt;br /&gt;
               '''return''' ''null''&lt;br /&gt;
      x = (x + y) '''mod''' m   &lt;br /&gt;
      '''return''' ''null''&lt;br /&gt;
&lt;br /&gt;
===Реализация с удалением===&lt;br /&gt;
Что бы наша хеш-таблица поддерживала удаление, требуется добавить массив &amp;lt;tex&amp;gt;deleted&amp;lt;/tex&amp;gt; типов &amp;lt;tex&amp;gt;bool&amp;lt;/tex&amp;gt;, равный по величине массиву &amp;lt;tex&amp;gt;table&amp;lt;/tex&amp;gt;. Теперь при удалении мы просто будем помечать наш объект ''как удалённый'', а при добавлении как ''не удалённый'' и замещать новым добавляемым объектом. При поиске, помимо равенства ключей, мы смотрим, удалён ли элемент, если да, то идём дальше.&lt;br /&gt;
&lt;br /&gt;
'''Вставка'''&lt;br /&gt;
 '''function''' add('''Item''' item):&lt;br /&gt;
      x = h1(item.key)&lt;br /&gt;
      y = h2(item.key)&lt;br /&gt;
      '''for''' (i = 0..m)   	&lt;br /&gt;
         '''if''' table[x] == '''null''' '''or''' deleted[x]&lt;br /&gt;
            table[x] = item&lt;br /&gt;
            deleted[x] = '''false'''&lt;br /&gt;
            '''return'''      &lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      table.resize()&amp;lt;span style=&amp;quot;color:Green&amp;quot;&amp;gt;// ошибка, требуется увеличить размер таблицы&lt;br /&gt;
'''Поиск'''&lt;br /&gt;
 '''Item''' search('''Item''' key):&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m) &lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key '''and''' !deleted[x]&lt;br /&gt;
               '''return''' table[x]&lt;br /&gt;
         '''else'''&lt;br /&gt;
            '''return''' '''null'''&lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      '''return''' '''null'''&lt;br /&gt;
&lt;br /&gt;
'''Удаление'''&lt;br /&gt;
 '''function''' remove('''Item''' key):&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m)&lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key&lt;br /&gt;
               deleted[x] = '''true'''&lt;br /&gt;
         '''else''' &lt;br /&gt;
            '''return'''&lt;br /&gt;
      x = (x + y) '''mod''' m&lt;br /&gt;
&lt;br /&gt;
==Альтернативная реализация метода цепочек==&lt;br /&gt;
В Java 8 для разрешения коллизий используется модифицированный метод цепочек. Суть его заключается в том, что когда количество элементов в корзине превышает определенное значение, данный корзина переходит от использования связного списка к использованию [[АВЛ-дерево|сбалансированного дерева]]. Но данный метод имеет смысл лишь тогда, когда на элементах хеш-таблицы задан [[Отношение порядка|линейный порядок]]. То есть при использовании данный типа &amp;lt;tex&amp;gt;\mathbf{int}&amp;lt;/tex&amp;gt; или &amp;lt;tex&amp;gt;\mathbf{double}&amp;lt;/tex&amp;gt; имеет смысл переходить к дереву поиска, а при использовании каких-нибудь ссылок на объекты не имеет, так как они не реализуют нужный интерфейс. Такой подход позволяет улучшить производительность с &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt;. Данный способ используется в таких коллекциях как HashMap, LinkedHashMap и ConcurrentHashMap.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Hashing_in_Java8.png|500px|Хеширование в Java 8.]]&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
* [[Хеширование]]&lt;br /&gt;
* [[Хеширование_кукушки|Хеширование кукушки]]&lt;br /&gt;
* [[Идеальное_хеширование|Идеальное хеширование]]&lt;br /&gt;
&lt;br /&gt;
== Источники информации ==&lt;br /&gt;
* Бакнелл Дж. М. «Фундаментальные алгоритмы и структуры данных в Delphi», 2003&lt;br /&gt;
* Кормен, Томас Х., Лейзерсон, Чарльз И., Ривест, Рональд Л., Штайн Клиффорд «Алгоритмы: построение и анализ», 2-е издание. Пер. с англ. — М.:Издательский дом &amp;quot;Вильямс&amp;quot;, 2010.— Парал. тит. англ. — ISBN 978-5-8459-0857-5 (рус.)&lt;br /&gt;
* Дональд Кнут. «Искусство программирования, том 3. Сортировка и поиск» {{---}} «Вильямс», 2007 г.{{---}} ISBN 0-201-89685-0&lt;br /&gt;
* Седжвик Р. «Фундаментальные алгоритмы на C. Части 1-4. Анализ. Структуры данных. Сортировка. Поиск», 2003&lt;br /&gt;
* [http://openjdk.java.net/jeps/180 Handle Frequent HashMap Collisions with Balanced Trees]&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Double_hashing Wikipedia {{---}} Double_hashing]&lt;br /&gt;
* [http://ru.wikipedia.org/wiki/%D0%A5%D0%B5%D1%88-%D1%82%D0%B0%D0%B1%D0%BB%D0%B8%D1%86%D0%B0 Разрешение коллизий]&lt;br /&gt;
* [http://rain.ifmo.ru/cat/view.php/vis/hashtables/hash-2001-2 Пример хеш таблицы]&lt;br /&gt;
* [http://research.cs.vt.edu/AVresearch/hashing/double.php Пример хеш таблицы с двойным хешированием]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория: Хеширование]]&lt;br /&gt;
[[Категория: Структуры данных]]&lt;/div&gt;</summary>
		<author><name>Kozichuk</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%A0%D0%B0%D0%B7%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BA%D0%BE%D0%BB%D0%BB%D0%B8%D0%B7%D0%B8%D0%B9&amp;diff=47805</id>
		<title>Разрешение коллизий</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A0%D0%B0%D0%B7%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BA%D0%BE%D0%BB%D0%BB%D0%B8%D0%B7%D0%B8%D0%B9&amp;diff=47805"/>
				<updated>2015-06-06T16:32:29Z</updated>
		
		<summary type="html">&lt;p&gt;Kozichuk: /* Разрешение коллизий в Java */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Разрешение [[Хеш-таблица|коллизий]]''' (англ. collision resolution) в [[Хеш-таблица|хеш-таблице]], задача, решаемая несколькими способами: метод цепочек, открытая адресация и т.д. Очень важно сводить количество коллизий к минимуму, так как это увеличивает время работы с хеш-таблицами. &lt;br /&gt;
&lt;br /&gt;
== Разрешение коллизий с помощью цепочек ==&lt;br /&gt;
[[Файл:open_hash.png|thumb|380px|right|Разрешение коллизий при помощи цепочек.]]&lt;br /&gt;
Каждая ячейка &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; массива &amp;lt;tex&amp;gt;H&amp;lt;/tex&amp;gt; содержит указатель на начало [[Список|списка]] всех элементов, хеш-код которых равен &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, либо указывает на их отсутствие. Коллизии приводят к тому, что появляются списки размером больше одного элемента.&lt;br /&gt;
&lt;br /&gt;
В зависимости от того нужна ли нам уникальность значений операции вставки у нас будет работать за разное время. Если не важна, то мы используем список, время вставки в который будет в худшем случае равна &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;. Иначе мы проверяем есть ли в списке данный элемент, а потом в случае его отсутствия мы его добавляем. В таком случае вставка элемента в худшем случае будет выполнена за &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Время работы поиска в наихудшем случае пропорционально длине списка, а если все &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; ключей захешировались в одну и ту же ячейку (создав список длиной &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;) время поиска будет равно &amp;lt;tex&amp;gt;\Theta(n)&amp;lt;/tex&amp;gt; плюс время вычисления хеш-функции, что ничуть не лучше, чем использование связного списка для хранения всех &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; элементов.&lt;br /&gt;
&lt;br /&gt;
Удаления элемента может быть выполнено за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;, как и вставка, при использовании двухсвязного списка.&lt;br /&gt;
&lt;br /&gt;
== Линейное разрешение коллизий ==&lt;br /&gt;
[[Файл:close_hash.png|thumb|380px|right|Пример хеш-таблицы с открытой адресацией и линейным пробированием.]]&lt;br /&gt;
Все элементы хранятся непосредственно в хеш-таблице, без использования связных списков. В отличии от хеширования с цепочками, при использовании этого метода может возникнуть ситуация, когда хеш-таблица окажется полностью заполненной, следовательно будет невозможно добавлять в неё новые элементы. Так что при возникновении такой ситуации решением может быть динамическое увеличение размера хеш-таблицы, с одновременной её перестройкой.&lt;br /&gt;
&lt;br /&gt;
=== Стратегии поиска ===&lt;br /&gt;
&lt;br /&gt;
''' Последовательный поиск '''&lt;br /&gt;
&lt;br /&gt;
При попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt;i+1, i+2, i+3&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку. В неё и запишем элемент.&lt;br /&gt;
&lt;br /&gt;
[[Файл:hashtables1.png|400px|Последовательный поиск, частный случай линейного поиска.]]&lt;br /&gt;
&lt;br /&gt;
''' Линейный поиск '''&lt;br /&gt;
&lt;br /&gt;
Выбираем шаг &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;. При попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt;i+(1 \cdot q), i+(2 \cdot q), i+(3 \cdot q)&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку. В неё и запишем элемент.&lt;br /&gt;
По сути последовательный поиск - частный случай линейного, где &amp;lt;tex&amp;gt;q=1&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Hashtables56.PNG|400px|Линейный поиск с шагом q.]]&lt;br /&gt;
&lt;br /&gt;
''' Квадратичный поиск '''&lt;br /&gt;
&lt;br /&gt;
Шаг &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; не фиксирован, а изменяется квадратично: &amp;lt;tex&amp;gt;q = 1,4,9,16...&amp;lt;/tex&amp;gt;. Соответственно при попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt; i+1, i+4, i+9&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку.&lt;br /&gt;
&lt;br /&gt;
[[Файл:hashtables3.png|400px|Квадратичный поиск.]]&lt;br /&gt;
&lt;br /&gt;
=== Проверка наличия элемента в таблице===&lt;br /&gt;
&lt;br /&gt;
Проверка осуществляется аналогично добавлению: мы проверяем ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; и другие, в соответствии с выбранной стратегией, пока не найдём искомый элемент или свободную ячейку.&lt;br /&gt;
&lt;br /&gt;
При поиске элемента может получится так, что мы дойдём до конца таблицы. Обычно поиск продолжается, начиная с другого конца, пока мы не придём в ту ячейку, откуда начинался поиск.&lt;br /&gt;
&lt;br /&gt;
=== Проблемы данных стратегий ===&lt;br /&gt;
&lt;br /&gt;
Проблем две — крайне нетривиальное удаление элемента из таблицы и образование кластеров  — последовательностей занятых ячеек.&lt;br /&gt;
&lt;br /&gt;
Кластеризация замедляет все операции с хеш-таблицей: при добавлении требуется перебирать всё больше элементов, при проверке тоже. Чем больше в таблице элементов, тем больше в ней кластеры и тем выше вероятность того, что добавляемый элемент попадёт в кластер.&lt;br /&gt;
Для защиты от кластеризации используется двойное хеширование и [[Хеширование кукушки|хеширование кукушки]].&lt;br /&gt;
&lt;br /&gt;
=== Удаление элемента без пометок ===&lt;br /&gt;
&lt;br /&gt;
Рассуждение будет описывать случай с линейным поиском хеша. Будем при удалении элемента сдвигать всё последующие на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; позиций назад. При этом:&lt;br /&gt;
* если в цепочке встречается элемент с другим хешем, то он должен остаться на своём месте (такая ситуация может возникнуть если оставшаяся часть цепочки была добавлена позже этого элемента)&lt;br /&gt;
* в цепочке не должно оставаться &amp;quot;дырок&amp;quot;, тогда любой элемент с данным хешем будет доступен из начала цепи&lt;br /&gt;
&lt;br /&gt;
Учитывая это будем действовать следующим образом: при поиске следующего элемента цепочки будем пропускать все ячейки с другим значением хеша, первый найденный элемент копировать в текущую ячейку, и затем рекурсивно его удалять. Если такой следующей ячейки нет, то текущий элемент можно просто удалить, сторонние цепочки при этом не разрушатся (чего нельзя сказать про случай квадратичного поиска).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
''' Псевдокод '''&lt;br /&gt;
&lt;br /&gt;
 '''function''' delete('''Item''' i):&lt;br /&gt;
      j = i + q&lt;br /&gt;
      '''while''' table[j] == ''null'' '''or''' table[j].key != table[i].key&lt;br /&gt;
         '''if''' table[j] == ''null''&lt;br /&gt;
            table[i] = ''null''&lt;br /&gt;
            '''return'''&lt;br /&gt;
         j += q&lt;br /&gt;
      table[i] = table[j]&lt;br /&gt;
      delete(j)    &lt;br /&gt;
&lt;br /&gt;
Хеш-таблицу считаем зацикленной&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Утверждение&lt;br /&gt;
|about=о времени работы&lt;br /&gt;
|statement=Асимптотически время работы &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\mathrm{find}&amp;lt;/tex&amp;gt; совпадают&lt;br /&gt;
|proof=&lt;br /&gt;
Заметим что указатель &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; в каждой итерации перемещается вперёд на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; (с учётом рекурсивных вызовов &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt;). То есть этот алгоритм последовательно пройдёт по цепочке от удаляемого элемента до последнего {{---}} с учётом вызова &amp;lt;tex&amp;gt;\mathrm{find}&amp;lt;/tex&amp;gt; собственно для нахождения удаляемого элемента, мы посетим все ячейки цепи.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Вариант с зацикливанием мы не рассматриваем, поскольку если &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; взаимнопросто с размером хеш-таблицы, то для зацикливания в ней вообще не должно быть свободных позиций&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Теперь докажем почему этот алгоритм работает. Собственно нам требуется сохранение трёх условий.&lt;br /&gt;
* В редактируемой цепи не остаётся дырок&lt;br /&gt;
Докажем по индукции. Если на данной итерации мы просто удаляем элемент (база), то после него ничего нет, всё верно. Если же нет, то вызванный в конце &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt; (см. псевдокод) заметёт созданную дыру (скопированный элемент), и сам, по предположению, новых не создаст.&lt;br /&gt;
* Элементы, которые уже на своих местах, не должны быть сдвинуты.&lt;br /&gt;
Это учтено.&lt;br /&gt;
* В других цепочках не появятся дыры&lt;br /&gt;
Противное возможно только в том случае, если какой-то элемент был действительно удалён. Удаляем мы только последнюю ячейку в цепи, и если бы на её месте возникла дыра для сторонней цепочки, это бы означало что элемент, стоящий на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; позиций назад, одновременно принадлежал нашей и другой цепочкам, что невозможно.&lt;br /&gt;
&lt;br /&gt;
==Двойное хеширование==&lt;br /&gt;
'''Двойное хеширование''' (англ. double hashing) {{---}} метод борьбы с коллизиями, возникающими при открытой адресации, основанный на использовании двух хеш-функций для построения различных последовательностей исследования хеш-таблицы.&lt;br /&gt;
&lt;br /&gt;
===Принцип двойного хеширования===&lt;br /&gt;
При двойном хешировании используются две независимые хеш-функции &amp;lt;tex&amp;gt; h_1(k) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; h_2(k) &amp;lt;/tex&amp;gt;. Пусть &amp;lt;tex&amp;gt; k &amp;lt;/tex&amp;gt; {{---}} это наш ключ, &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt; {{---}} размер нашей таблицы, &amp;lt;tex&amp;gt;n \bmod m &amp;lt;/tex&amp;gt; {{---}} остаток от деления &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt; на &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, тогда сначала исследуется ячейка с адресом &amp;lt;tex&amp;gt; h_1(k) &amp;lt;/tex&amp;gt;, если она уже занята, то рассматривается &amp;lt;tex&amp;gt; (h_1(k) +  h_2(k)) \bmod m &amp;lt;/tex&amp;gt;, затем &amp;lt;tex&amp;gt; (h_1(k) +  2 \cdot h_2(k)) \bmod m &amp;lt;/tex&amp;gt; и так далее. В общем случае идёт проверка последовательности ячеек &amp;lt;tex&amp;gt; (h_1(k) +  i \cdot h_2(k)) \bmod m &amp;lt;/tex&amp;gt; где &amp;lt;tex&amp;gt;  i = (0, 1, \; ... \;,  m - 1) &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, операции вставки, удаления и поиска в лучшем случае выполняются за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;, в худшем {{---}} за &amp;lt;tex&amp;gt;O(m)&amp;lt;/tex&amp;gt;, что не отличается от обычного [[Открытое_и_закрытое_хеширование#Линейное разрешение коллизий|линейного разрешения коллизий]].&lt;br /&gt;
Однако в среднем, при грамотном выборе хеш-функций, двойное хеширование будет выдавать лучшие результаты, за счёт того, что вероятность совпадения значений сразу двух независимых хеш-функций ниже, чем одной.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt;\forall x \neq y \; \exists h_1,h_2 : p(h_1(x)=h_1(y))&amp;gt; p((h_1(x)=h_1(y)) \land (h_2(x)=h_2(y)))&amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Выбор хеш-функций===&lt;br /&gt;
&amp;lt;tex&amp;gt; h_1 &amp;lt;/tex&amp;gt; может быть обычной хеш-функцией. Однако чтобы последовательность исследования могла охватить всю таблицу, &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; должна возвращать значения:&lt;br /&gt;
*не равные &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
*независимые  от &amp;lt;tex&amp;gt; h_1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
*взаимно простые с величиной хеш-таблицы&lt;br /&gt;
&lt;br /&gt;
Есть два удобных способа это сделать. Первый состоит в том, что в качестве размера таблицы используется простое число, а &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; возвращает натуральные числа, меньшие &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;. Второй {{---}} размер таблицы является степенью двойки, а &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; возвращает нечетные значения.&lt;br /&gt;
&lt;br /&gt;
Например, если размер таблицы равен &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, то в качестве &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; можно использовать функцию вида &amp;lt;tex&amp;gt; h_2(k) = k \bmod (m-1) + 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Файл: Вставка при двойном хэшировании.svg.jpeg|thumb|right|Вставка при двойном хешировании]]&lt;br /&gt;
&lt;br /&gt;
===Пример===&lt;br /&gt;
&lt;br /&gt;
Показана хеш-таблица размером 13 ячеек, в которой используются вспомогательные функции:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h(k,i) = (h_1(k) + i \cdot h_2(k)) \bmod 13 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h_1(k) = k \bmod 13 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h_2(k) = 1 + k \bmod 11 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Мы хотим вставить ключ 14. Изначально &amp;lt;tex&amp;gt; i = 0 &amp;lt;/tex&amp;gt;. Тогда &amp;lt;tex&amp;gt; h(14,0) = (h_1(14) + 0\cdot h_2(14)) \bmod 13 = 1 &amp;lt;/tex&amp;gt;. Но ячейка с индексом 1 занята, поэтому увеличиваем &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; на 1 и пересчитываем значение хеш-функции. Делаем так, пока не дойдем до пустой ячейки. При &amp;lt;tex&amp;gt; i = 2 &amp;lt;/tex&amp;gt; получаем &amp;lt;tex&amp;gt; h(14,2) = (h_1(14) + 2\cdot h_2(14)) \bmod 13 = 9 &amp;lt;/tex&amp;gt;. Ячейка с номером 9 свободна, значит записываем туда наш ключ.&lt;br /&gt;
&lt;br /&gt;
Таким образом, основная особенность двойного хеширования состоит в том, что при различных &amp;lt;tex&amp;gt; k &amp;lt;/tex&amp;gt; пара &amp;lt;tex&amp;gt; (h_1(k),h_2(k)) &amp;lt;/tex&amp;gt; дает различные последовательности ячеек для исследования.&lt;br /&gt;
&lt;br /&gt;
===Простая реализация===&lt;br /&gt;
Пусть у нас есть некоторый объект &amp;lt;tex&amp;gt; item &amp;lt;/tex&amp;gt;, в котором определено поле &amp;lt;tex&amp;gt; key &amp;lt;/tex&amp;gt;, от которого можно вычислить хеш-функции &amp;lt;tex&amp;gt; h_1(key)&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; h_2(key) &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Так же у нас есть таблица &amp;lt;tex&amp;gt; table &amp;lt;/tex&amp;gt; величиной &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, состоящая из объектов типа &amp;lt;tex&amp;gt; item &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
'''Вставка'''&lt;br /&gt;
 '''function''' add('''Item''' item):&lt;br /&gt;
      x = h1(item.key)&lt;br /&gt;
      y = h2(item.key)&lt;br /&gt;
         '''for''' (i = 0..m)    	&lt;br /&gt;
            '''if''' table[x] == ''null''&lt;br /&gt;
               table[x] = item&lt;br /&gt;
            '''return'''      &lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      table.resize()&amp;lt;span style=&amp;quot;color:Green&amp;quot;&amp;gt;// ошибка, требуется увеличить размер таблицы&lt;br /&gt;
&lt;br /&gt;
'''Поиск'''&lt;br /&gt;
 '''Item''' search('''Item''' key):&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m)&lt;br /&gt;
         '''if''' table[x] != ''null''&lt;br /&gt;
            '''if''' table[x].key == key&lt;br /&gt;
               '''return''' table[x]&lt;br /&gt;
            '''else'''&lt;br /&gt;
               '''return''' ''null''&lt;br /&gt;
      x = (x + y) '''mod''' m   &lt;br /&gt;
      '''return''' ''null''&lt;br /&gt;
&lt;br /&gt;
===Реализация с удалением===&lt;br /&gt;
Что бы наша хеш-таблица поддерживала удаление, требуется добавить массив &amp;lt;tex&amp;gt;deleted&amp;lt;/tex&amp;gt; типов &amp;lt;tex&amp;gt;bool&amp;lt;/tex&amp;gt;, равный по величине массиву &amp;lt;tex&amp;gt;table&amp;lt;/tex&amp;gt;. Теперь при удалении мы просто будем помечать наш объект ''как удалённый'', а при добавлении как ''не удалённый'' и замещать новым добавляемым объектом. При поиске, помимо равенства ключей, мы смотрим, удалён ли элемент, если да, то идём дальше.&lt;br /&gt;
&lt;br /&gt;
'''Вставка'''&lt;br /&gt;
 '''function''' add('''Item''' item):&lt;br /&gt;
      x = h1(item.key)&lt;br /&gt;
      y = h2(item.key)&lt;br /&gt;
      '''for''' (i = 0..m)   	&lt;br /&gt;
         '''if''' table[x] == '''null''' '''or''' deleted[x]&lt;br /&gt;
            table[x] = item&lt;br /&gt;
            deleted[x] = '''false'''&lt;br /&gt;
            '''return'''      &lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      table.resize()&amp;lt;span style=&amp;quot;color:Green&amp;quot;&amp;gt;// ошибка, требуется увеличить размер таблицы&lt;br /&gt;
'''Поиск'''&lt;br /&gt;
 '''Item''' search('''Item''' key):&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m) &lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key '''and''' !deleted[x]&lt;br /&gt;
               '''return''' table[x]&lt;br /&gt;
         '''else'''&lt;br /&gt;
            '''return''' '''null'''&lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      '''return''' '''null'''&lt;br /&gt;
&lt;br /&gt;
'''Удаление'''&lt;br /&gt;
 '''function''' remove('''Item''' key):&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m)&lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key&lt;br /&gt;
               deleted[x] = '''true'''&lt;br /&gt;
         '''else''' &lt;br /&gt;
            '''return'''&lt;br /&gt;
      x = (x + y) '''mod''' m&lt;br /&gt;
&lt;br /&gt;
==Реализация метода цепочек на практике==&lt;br /&gt;
В Java 8 для разрешения коллизий используется модифицированный метод цепочек. Суть его заключается в том, что когда количество элементов в корзине превышает определенное значение, данный корзина переходит от использования связного списка к использованию [[АВЛ-дерево|сбалансированного дерева]]. Но данный метод имеет смысл лишь тогда, когда на элементах хеш-таблицы задан [[Отношение порядка|линейный порядок]]. То есть при использовании данный типа &amp;lt;tex&amp;gt;\mathbf{int}&amp;lt;/tex&amp;gt; или &amp;lt;tex&amp;gt;\mathbf{double}&amp;lt;/tex&amp;gt; имеет смысл переходить к дереву поиска, а при использовании каких-нибудь ссылок на объекты не имеет, так как они не реализуют нужный интерфейс. Такой подход позволяет улучшить производительность с &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt;. Данный способ используется в таких коллекциях как HashMap, LinkedHashMap и ConcurrentHashMap.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Hashing_in_Java8.png|500px|Хеширование в Java 8.]]&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
* [[Хеширование]]&lt;br /&gt;
* [[Хеширование_кукушки|Хеширование кукушки]]&lt;br /&gt;
* [[Идеальное_хеширование|Идеальное хеширование]]&lt;br /&gt;
&lt;br /&gt;
== Источники информации ==&lt;br /&gt;
* Бакнелл Дж. М. «Фундаментальные алгоритмы и структуры данных в Delphi», 2003&lt;br /&gt;
* Кормен, Томас Х., Лейзерсон, Чарльз И., Ривест, Рональд Л., Штайн Клиффорд «Алгоритмы: построение и анализ», 2-е издание. Пер. с англ. — М.:Издательский дом &amp;quot;Вильямс&amp;quot;, 2010.— Парал. тит. англ. — ISBN 978-5-8459-0857-5 (рус.)&lt;br /&gt;
* Дональд Кнут. «Искусство программирования, том 3. Сортировка и поиск» {{---}} «Вильямс», 2007 г.{{---}} ISBN 0-201-89685-0&lt;br /&gt;
* Седжвик Р. «Фундаментальные алгоритмы на C. Части 1-4. Анализ. Структуры данных. Сортировка. Поиск», 2003&lt;br /&gt;
* [http://openjdk.java.net/jeps/180 Handle Frequent HashMap Collisions with Balanced Trees]&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Double_hashing Wikipedia {{---}} Double_hashing]&lt;br /&gt;
* [http://ru.wikipedia.org/wiki/%D0%A5%D0%B5%D1%88-%D1%82%D0%B0%D0%B1%D0%BB%D0%B8%D1%86%D0%B0 Разрешение коллизий]&lt;br /&gt;
* [http://rain.ifmo.ru/cat/view.php/vis/hashtables/hash-2001-2 Пример хеш таблицы]&lt;br /&gt;
* [http://research.cs.vt.edu/AVresearch/hashing/double.php Пример хеш таблицы с двойным хешированием]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория: Хеширование]]&lt;br /&gt;
[[Категория: Структуры данных]]&lt;/div&gt;</summary>
		<author><name>Kozichuk</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%A0%D0%B0%D0%B7%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BA%D0%BE%D0%BB%D0%BB%D0%B8%D0%B7%D0%B8%D0%B9&amp;diff=47804</id>
		<title>Разрешение коллизий</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A0%D0%B0%D0%B7%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BA%D0%BE%D0%BB%D0%BB%D0%B8%D0%B7%D0%B8%D0%B9&amp;diff=47804"/>
				<updated>2015-06-06T16:26:49Z</updated>
		
		<summary type="html">&lt;p&gt;Kozichuk: /* Разрешение коллизий в Java */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Разрешение [[Хеш-таблица|коллизий]]''' (англ. collision resolution) в [[Хеш-таблица|хеш-таблице]], задача, решаемая несколькими способами: метод цепочек, открытая адресация и т.д. Очень важно сводить количество коллизий к минимуму, так как это увеличивает время работы с хеш-таблицами. &lt;br /&gt;
&lt;br /&gt;
== Разрешение коллизий с помощью цепочек ==&lt;br /&gt;
[[Файл:open_hash.png|thumb|380px|right|Разрешение коллизий при помощи цепочек.]]&lt;br /&gt;
Каждая ячейка &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; массива &amp;lt;tex&amp;gt;H&amp;lt;/tex&amp;gt; содержит указатель на начало [[Список|списка]] всех элементов, хеш-код которых равен &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, либо указывает на их отсутствие. Коллизии приводят к тому, что появляются списки размером больше одного элемента.&lt;br /&gt;
&lt;br /&gt;
В зависимости от того нужна ли нам уникальность значений операции вставки у нас будет работать за разное время. Если не важна, то мы используем список, время вставки в который будет в худшем случае равна &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;. Иначе мы проверяем есть ли в списке данный элемент, а потом в случае его отсутствия мы его добавляем. В таком случае вставка элемента в худшем случае будет выполнена за &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Время работы поиска в наихудшем случае пропорционально длине списка, а если все &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; ключей захешировались в одну и ту же ячейку (создав список длиной &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;) время поиска будет равно &amp;lt;tex&amp;gt;\Theta(n)&amp;lt;/tex&amp;gt; плюс время вычисления хеш-функции, что ничуть не лучше, чем использование связного списка для хранения всех &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; элементов.&lt;br /&gt;
&lt;br /&gt;
Удаления элемента может быть выполнено за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;, как и вставка, при использовании двухсвязного списка.&lt;br /&gt;
&lt;br /&gt;
== Линейное разрешение коллизий ==&lt;br /&gt;
[[Файл:close_hash.png|thumb|380px|right|Пример хеш-таблицы с открытой адресацией и линейным пробированием.]]&lt;br /&gt;
Все элементы хранятся непосредственно в хеш-таблице, без использования связных списков. В отличии от хеширования с цепочками, при использовании этого метода может возникнуть ситуация, когда хеш-таблица окажется полностью заполненной, следовательно будет невозможно добавлять в неё новые элементы. Так что при возникновении такой ситуации решением может быть динамическое увеличение размера хеш-таблицы, с одновременной её перестройкой.&lt;br /&gt;
&lt;br /&gt;
=== Стратегии поиска ===&lt;br /&gt;
&lt;br /&gt;
''' Последовательный поиск '''&lt;br /&gt;
&lt;br /&gt;
При попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt;i+1, i+2, i+3&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку. В неё и запишем элемент.&lt;br /&gt;
&lt;br /&gt;
[[Файл:hashtables1.png|400px|Последовательный поиск, частный случай линейного поиска.]]&lt;br /&gt;
&lt;br /&gt;
''' Линейный поиск '''&lt;br /&gt;
&lt;br /&gt;
Выбираем шаг &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;. При попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt;i+(1 \cdot q), i+(2 \cdot q), i+(3 \cdot q)&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку. В неё и запишем элемент.&lt;br /&gt;
По сути последовательный поиск - частный случай линейного, где &amp;lt;tex&amp;gt;q=1&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Hashtables56.PNG|400px|Линейный поиск с шагом q.]]&lt;br /&gt;
&lt;br /&gt;
''' Квадратичный поиск '''&lt;br /&gt;
&lt;br /&gt;
Шаг &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; не фиксирован, а изменяется квадратично: &amp;lt;tex&amp;gt;q = 1,4,9,16...&amp;lt;/tex&amp;gt;. Соответственно при попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt; i+1, i+4, i+9&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку.&lt;br /&gt;
&lt;br /&gt;
[[Файл:hashtables3.png|400px|Квадратичный поиск.]]&lt;br /&gt;
&lt;br /&gt;
=== Проверка наличия элемента в таблице===&lt;br /&gt;
&lt;br /&gt;
Проверка осуществляется аналогично добавлению: мы проверяем ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; и другие, в соответствии с выбранной стратегией, пока не найдём искомый элемент или свободную ячейку.&lt;br /&gt;
&lt;br /&gt;
При поиске элемента может получится так, что мы дойдём до конца таблицы. Обычно поиск продолжается, начиная с другого конца, пока мы не придём в ту ячейку, откуда начинался поиск.&lt;br /&gt;
&lt;br /&gt;
=== Проблемы данных стратегий ===&lt;br /&gt;
&lt;br /&gt;
Проблем две — крайне нетривиальное удаление элемента из таблицы и образование кластеров  — последовательностей занятых ячеек.&lt;br /&gt;
&lt;br /&gt;
Кластеризация замедляет все операции с хеш-таблицей: при добавлении требуется перебирать всё больше элементов, при проверке тоже. Чем больше в таблице элементов, тем больше в ней кластеры и тем выше вероятность того, что добавляемый элемент попадёт в кластер.&lt;br /&gt;
Для защиты от кластеризации используется двойное хеширование и [[Хеширование кукушки|хеширование кукушки]].&lt;br /&gt;
&lt;br /&gt;
=== Удаление элемента без пометок ===&lt;br /&gt;
&lt;br /&gt;
Рассуждение будет описывать случай с линейным поиском хеша. Будем при удалении элемента сдвигать всё последующие на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; позиций назад. При этом:&lt;br /&gt;
* если в цепочке встречается элемент с другим хешем, то он должен остаться на своём месте (такая ситуация может возникнуть если оставшаяся часть цепочки была добавлена позже этого элемента)&lt;br /&gt;
* в цепочке не должно оставаться &amp;quot;дырок&amp;quot;, тогда любой элемент с данным хешем будет доступен из начала цепи&lt;br /&gt;
&lt;br /&gt;
Учитывая это будем действовать следующим образом: при поиске следующего элемента цепочки будем пропускать все ячейки с другим значением хеша, первый найденный элемент копировать в текущую ячейку, и затем рекурсивно его удалять. Если такой следующей ячейки нет, то текущий элемент можно просто удалить, сторонние цепочки при этом не разрушатся (чего нельзя сказать про случай квадратичного поиска).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
''' Псевдокод '''&lt;br /&gt;
&lt;br /&gt;
 '''function''' delete('''Item''' i):&lt;br /&gt;
      j = i + q&lt;br /&gt;
      '''while''' table[j] == ''null'' '''or''' table[j].key != table[i].key&lt;br /&gt;
         '''if''' table[j] == ''null''&lt;br /&gt;
            table[i] = ''null''&lt;br /&gt;
            '''return'''&lt;br /&gt;
         j += q&lt;br /&gt;
      table[i] = table[j]&lt;br /&gt;
      delete(j)    &lt;br /&gt;
&lt;br /&gt;
Хеш-таблицу считаем зацикленной&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Утверждение&lt;br /&gt;
|about=о времени работы&lt;br /&gt;
|statement=Асимптотически время работы &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\mathrm{find}&amp;lt;/tex&amp;gt; совпадают&lt;br /&gt;
|proof=&lt;br /&gt;
Заметим что указатель &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; в каждой итерации перемещается вперёд на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; (с учётом рекурсивных вызовов &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt;). То есть этот алгоритм последовательно пройдёт по цепочке от удаляемого элемента до последнего {{---}} с учётом вызова &amp;lt;tex&amp;gt;\mathrm{find}&amp;lt;/tex&amp;gt; собственно для нахождения удаляемого элемента, мы посетим все ячейки цепи.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Вариант с зацикливанием мы не рассматриваем, поскольку если &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; взаимнопросто с размером хеш-таблицы, то для зацикливания в ней вообще не должно быть свободных позиций&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Теперь докажем почему этот алгоритм работает. Собственно нам требуется сохранение трёх условий.&lt;br /&gt;
* В редактируемой цепи не остаётся дырок&lt;br /&gt;
Докажем по индукции. Если на данной итерации мы просто удаляем элемент (база), то после него ничего нет, всё верно. Если же нет, то вызванный в конце &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt; (см. псевдокод) заметёт созданную дыру (скопированный элемент), и сам, по предположению, новых не создаст.&lt;br /&gt;
* Элементы, которые уже на своих местах, не должны быть сдвинуты.&lt;br /&gt;
Это учтено.&lt;br /&gt;
* В других цепочках не появятся дыры&lt;br /&gt;
Противное возможно только в том случае, если какой-то элемент был действительно удалён. Удаляем мы только последнюю ячейку в цепи, и если бы на её месте возникла дыра для сторонней цепочки, это бы означало что элемент, стоящий на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; позиций назад, одновременно принадлежал нашей и другой цепочкам, что невозможно.&lt;br /&gt;
&lt;br /&gt;
==Двойное хеширование==&lt;br /&gt;
'''Двойное хеширование''' (англ. double hashing) {{---}} метод борьбы с коллизиями, возникающими при открытой адресации, основанный на использовании двух хеш-функций для построения различных последовательностей исследования хеш-таблицы.&lt;br /&gt;
&lt;br /&gt;
===Принцип двойного хеширования===&lt;br /&gt;
При двойном хешировании используются две независимые хеш-функции &amp;lt;tex&amp;gt; h_1(k) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; h_2(k) &amp;lt;/tex&amp;gt;. Пусть &amp;lt;tex&amp;gt; k &amp;lt;/tex&amp;gt; {{---}} это наш ключ, &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt; {{---}} размер нашей таблицы, &amp;lt;tex&amp;gt;n \bmod m &amp;lt;/tex&amp;gt; {{---}} остаток от деления &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt; на &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, тогда сначала исследуется ячейка с адресом &amp;lt;tex&amp;gt; h_1(k) &amp;lt;/tex&amp;gt;, если она уже занята, то рассматривается &amp;lt;tex&amp;gt; (h_1(k) +  h_2(k)) \bmod m &amp;lt;/tex&amp;gt;, затем &amp;lt;tex&amp;gt; (h_1(k) +  2 \cdot h_2(k)) \bmod m &amp;lt;/tex&amp;gt; и так далее. В общем случае идёт проверка последовательности ячеек &amp;lt;tex&amp;gt; (h_1(k) +  i \cdot h_2(k)) \bmod m &amp;lt;/tex&amp;gt; где &amp;lt;tex&amp;gt;  i = (0, 1, \; ... \;,  m - 1) &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, операции вставки, удаления и поиска в лучшем случае выполняются за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;, в худшем {{---}} за &amp;lt;tex&amp;gt;O(m)&amp;lt;/tex&amp;gt;, что не отличается от обычного [[Открытое_и_закрытое_хеширование#Линейное разрешение коллизий|линейного разрешения коллизий]].&lt;br /&gt;
Однако в среднем, при грамотном выборе хеш-функций, двойное хеширование будет выдавать лучшие результаты, за счёт того, что вероятность совпадения значений сразу двух независимых хеш-функций ниже, чем одной.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt;\forall x \neq y \; \exists h_1,h_2 : p(h_1(x)=h_1(y))&amp;gt; p((h_1(x)=h_1(y)) \land (h_2(x)=h_2(y)))&amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Выбор хеш-функций===&lt;br /&gt;
&amp;lt;tex&amp;gt; h_1 &amp;lt;/tex&amp;gt; может быть обычной хеш-функцией. Однако чтобы последовательность исследования могла охватить всю таблицу, &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; должна возвращать значения:&lt;br /&gt;
*не равные &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
*независимые  от &amp;lt;tex&amp;gt; h_1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
*взаимно простые с величиной хеш-таблицы&lt;br /&gt;
&lt;br /&gt;
Есть два удобных способа это сделать. Первый состоит в том, что в качестве размера таблицы используется простое число, а &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; возвращает натуральные числа, меньшие &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;. Второй {{---}} размер таблицы является степенью двойки, а &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; возвращает нечетные значения.&lt;br /&gt;
&lt;br /&gt;
Например, если размер таблицы равен &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, то в качестве &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; можно использовать функцию вида &amp;lt;tex&amp;gt; h_2(k) = k \bmod (m-1) + 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Файл: Вставка при двойном хэшировании.svg.jpeg|thumb|right|Вставка при двойном хешировании]]&lt;br /&gt;
&lt;br /&gt;
===Пример===&lt;br /&gt;
&lt;br /&gt;
Показана хеш-таблица размером 13 ячеек, в которой используются вспомогательные функции:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h(k,i) = (h_1(k) + i \cdot h_2(k)) \bmod 13 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h_1(k) = k \bmod 13 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h_2(k) = 1 + k \bmod 11 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Мы хотим вставить ключ 14. Изначально &amp;lt;tex&amp;gt; i = 0 &amp;lt;/tex&amp;gt;. Тогда &amp;lt;tex&amp;gt; h(14,0) = (h_1(14) + 0\cdot h_2(14)) \bmod 13 = 1 &amp;lt;/tex&amp;gt;. Но ячейка с индексом 1 занята, поэтому увеличиваем &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; на 1 и пересчитываем значение хеш-функции. Делаем так, пока не дойдем до пустой ячейки. При &amp;lt;tex&amp;gt; i = 2 &amp;lt;/tex&amp;gt; получаем &amp;lt;tex&amp;gt; h(14,2) = (h_1(14) + 2\cdot h_2(14)) \bmod 13 = 9 &amp;lt;/tex&amp;gt;. Ячейка с номером 9 свободна, значит записываем туда наш ключ.&lt;br /&gt;
&lt;br /&gt;
Таким образом, основная особенность двойного хеширования состоит в том, что при различных &amp;lt;tex&amp;gt; k &amp;lt;/tex&amp;gt; пара &amp;lt;tex&amp;gt; (h_1(k),h_2(k)) &amp;lt;/tex&amp;gt; дает различные последовательности ячеек для исследования.&lt;br /&gt;
&lt;br /&gt;
===Простая реализация===&lt;br /&gt;
Пусть у нас есть некоторый объект &amp;lt;tex&amp;gt; item &amp;lt;/tex&amp;gt;, в котором определено поле &amp;lt;tex&amp;gt; key &amp;lt;/tex&amp;gt;, от которого можно вычислить хеш-функции &amp;lt;tex&amp;gt; h_1(key)&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; h_2(key) &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Так же у нас есть таблица &amp;lt;tex&amp;gt; table &amp;lt;/tex&amp;gt; величиной &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, состоящая из объектов типа &amp;lt;tex&amp;gt; item &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
'''Вставка'''&lt;br /&gt;
 '''function''' add('''Item''' item):&lt;br /&gt;
      x = h1(item.key)&lt;br /&gt;
      y = h2(item.key)&lt;br /&gt;
         '''for''' (i = 0..m)    	&lt;br /&gt;
            '''if''' table[x] == ''null''&lt;br /&gt;
               table[x] = item&lt;br /&gt;
            '''return'''      &lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      table.resize()&amp;lt;span style=&amp;quot;color:Green&amp;quot;&amp;gt;// ошибка, требуется увеличить размер таблицы&lt;br /&gt;
&lt;br /&gt;
'''Поиск'''&lt;br /&gt;
 '''Item''' search('''Item''' key):&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m)&lt;br /&gt;
         '''if''' table[x] != ''null''&lt;br /&gt;
            '''if''' table[x].key == key&lt;br /&gt;
               '''return''' table[x]&lt;br /&gt;
            '''else'''&lt;br /&gt;
               '''return''' ''null''&lt;br /&gt;
      x = (x + y) '''mod''' m   &lt;br /&gt;
      '''return''' ''null''&lt;br /&gt;
&lt;br /&gt;
===Реализация с удалением===&lt;br /&gt;
Что бы наша хеш-таблица поддерживала удаление, требуется добавить массив &amp;lt;tex&amp;gt;deleted&amp;lt;/tex&amp;gt; типов &amp;lt;tex&amp;gt;bool&amp;lt;/tex&amp;gt;, равный по величине массиву &amp;lt;tex&amp;gt;table&amp;lt;/tex&amp;gt;. Теперь при удалении мы просто будем помечать наш объект ''как удалённый'', а при добавлении как ''не удалённый'' и замещать новым добавляемым объектом. При поиске, помимо равенства ключей, мы смотрим, удалён ли элемент, если да, то идём дальше.&lt;br /&gt;
&lt;br /&gt;
'''Вставка'''&lt;br /&gt;
 '''function''' add('''Item''' item):&lt;br /&gt;
      x = h1(item.key)&lt;br /&gt;
      y = h2(item.key)&lt;br /&gt;
      '''for''' (i = 0..m)   	&lt;br /&gt;
         '''if''' table[x] == '''null''' '''or''' deleted[x]&lt;br /&gt;
            table[x] = item&lt;br /&gt;
            deleted[x] = '''false'''&lt;br /&gt;
            '''return'''      &lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      table.resize()&amp;lt;span style=&amp;quot;color:Green&amp;quot;&amp;gt;// ошибка, требуется увеличить размер таблицы&lt;br /&gt;
'''Поиск'''&lt;br /&gt;
 '''Item''' search('''Item''' key):&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m) &lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key '''and''' !deleted[x]&lt;br /&gt;
               '''return''' table[x]&lt;br /&gt;
         '''else'''&lt;br /&gt;
            '''return''' '''null'''&lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      '''return''' '''null'''&lt;br /&gt;
&lt;br /&gt;
'''Удаление'''&lt;br /&gt;
 '''function''' remove('''Item''' key):&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m)&lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key&lt;br /&gt;
               deleted[x] = '''true'''&lt;br /&gt;
         '''else''' &lt;br /&gt;
            '''return'''&lt;br /&gt;
      x = (x + y) '''mod''' m&lt;br /&gt;
&lt;br /&gt;
== Разрешение коллизий в Java==&lt;br /&gt;
В Java 8 для разрешения коллизий используется модифицированный метод цепочек. Суть его заключается в том, что когда количество элементов в корзине превышает определенное значение, данный корзина переходит от использования связного списка к использованию [[АВЛ-дерево|сбалансированного дерева]]. Но данный метод имеет смысл лишь тогда, когда на элементах хеш-таблицы задан [[Отношение порядка|линейный порядок]]. То есть при использовании данный типа &amp;lt;tex&amp;gt;\mathbf{int}&amp;lt;/tex&amp;gt; или &amp;lt;tex&amp;gt;\mathbf{double}&amp;lt;/tex&amp;gt; имеет смысл переходить к дереву поиска, а при использовании каких-нибудь ссылок на объекты не имеет, так как они не реализуют нужный интерфейс. Такой подход позволяет улучшить производительность с &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt;. Данный способ используется в таких коллекциях как HashMap, LinkedHashMap и ConcurrentHashMap.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Hashing_in_Java8.png|500px|Хеширование в Java 8.]]&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
* [[Хеширование]]&lt;br /&gt;
* [[Хеширование_кукушки|Хеширование кукушки]]&lt;br /&gt;
* [[Идеальное_хеширование|Идеальное хеширование]]&lt;br /&gt;
&lt;br /&gt;
== Источники информации ==&lt;br /&gt;
* Бакнелл Дж. М. «Фундаментальные алгоритмы и структуры данных в Delphi», 2003&lt;br /&gt;
* Кормен, Томас Х., Лейзерсон, Чарльз И., Ривест, Рональд Л., Штайн Клиффорд «Алгоритмы: построение и анализ», 2-е издание. Пер. с англ. — М.:Издательский дом &amp;quot;Вильямс&amp;quot;, 2010.— Парал. тит. англ. — ISBN 978-5-8459-0857-5 (рус.)&lt;br /&gt;
* Дональд Кнут. «Искусство программирования, том 3. Сортировка и поиск» {{---}} «Вильямс», 2007 г.{{---}} ISBN 0-201-89685-0&lt;br /&gt;
* Седжвик Р. «Фундаментальные алгоритмы на C. Части 1-4. Анализ. Структуры данных. Сортировка. Поиск», 2003&lt;br /&gt;
* [http://openjdk.java.net/jeps/180 Handle Frequent HashMap Collisions with Balanced Trees]&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Double_hashing Wikipedia {{---}} Double_hashing]&lt;br /&gt;
* [http://ru.wikipedia.org/wiki/%D0%A5%D0%B5%D1%88-%D1%82%D0%B0%D0%B1%D0%BB%D0%B8%D1%86%D0%B0 Разрешение коллизий]&lt;br /&gt;
* [http://rain.ifmo.ru/cat/view.php/vis/hashtables/hash-2001-2 Пример хеш таблицы]&lt;br /&gt;
* [http://research.cs.vt.edu/AVresearch/hashing/double.php Пример хеш таблицы с двойным хешированием]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория: Хеширование]]&lt;br /&gt;
[[Категория: Структуры данных]]&lt;/div&gt;</summary>
		<author><name>Kozichuk</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%A0%D0%B0%D0%B7%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BA%D0%BE%D0%BB%D0%BB%D0%B8%D0%B7%D0%B8%D0%B9&amp;diff=47803</id>
		<title>Разрешение коллизий</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A0%D0%B0%D0%B7%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BA%D0%BE%D0%BB%D0%BB%D0%B8%D0%B7%D0%B8%D0%B9&amp;diff=47803"/>
				<updated>2015-06-06T16:24:15Z</updated>
		
		<summary type="html">&lt;p&gt;Kozichuk: /* Разрешение коллизий в Java */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Разрешение [[Хеш-таблица|коллизий]]''' (англ. collision resolution) в [[Хеш-таблица|хеш-таблице]], задача, решаемая несколькими способами: метод цепочек, открытая адресация и т.д. Очень важно сводить количество коллизий к минимуму, так как это увеличивает время работы с хеш-таблицами. &lt;br /&gt;
&lt;br /&gt;
== Разрешение коллизий с помощью цепочек ==&lt;br /&gt;
[[Файл:open_hash.png|thumb|380px|right|Разрешение коллизий при помощи цепочек.]]&lt;br /&gt;
Каждая ячейка &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; массива &amp;lt;tex&amp;gt;H&amp;lt;/tex&amp;gt; содержит указатель на начало [[Список|списка]] всех элементов, хеш-код которых равен &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, либо указывает на их отсутствие. Коллизии приводят к тому, что появляются списки размером больше одного элемента.&lt;br /&gt;
&lt;br /&gt;
В зависимости от того нужна ли нам уникальность значений операции вставки у нас будет работать за разное время. Если не важна, то мы используем список, время вставки в который будет в худшем случае равна &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;. Иначе мы проверяем есть ли в списке данный элемент, а потом в случае его отсутствия мы его добавляем. В таком случае вставка элемента в худшем случае будет выполнена за &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Время работы поиска в наихудшем случае пропорционально длине списка, а если все &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; ключей захешировались в одну и ту же ячейку (создав список длиной &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;) время поиска будет равно &amp;lt;tex&amp;gt;\Theta(n)&amp;lt;/tex&amp;gt; плюс время вычисления хеш-функции, что ничуть не лучше, чем использование связного списка для хранения всех &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; элементов.&lt;br /&gt;
&lt;br /&gt;
Удаления элемента может быть выполнено за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;, как и вставка, при использовании двухсвязного списка.&lt;br /&gt;
&lt;br /&gt;
== Линейное разрешение коллизий ==&lt;br /&gt;
[[Файл:close_hash.png|thumb|380px|right|Пример хеш-таблицы с открытой адресацией и линейным пробированием.]]&lt;br /&gt;
Все элементы хранятся непосредственно в хеш-таблице, без использования связных списков. В отличии от хеширования с цепочками, при использовании этого метода может возникнуть ситуация, когда хеш-таблица окажется полностью заполненной, следовательно будет невозможно добавлять в неё новые элементы. Так что при возникновении такой ситуации решением может быть динамическое увеличение размера хеш-таблицы, с одновременной её перестройкой.&lt;br /&gt;
&lt;br /&gt;
=== Стратегии поиска ===&lt;br /&gt;
&lt;br /&gt;
''' Последовательный поиск '''&lt;br /&gt;
&lt;br /&gt;
При попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt;i+1, i+2, i+3&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку. В неё и запишем элемент.&lt;br /&gt;
&lt;br /&gt;
[[Файл:hashtables1.png|400px|Последовательный поиск, частный случай линейного поиска.]]&lt;br /&gt;
&lt;br /&gt;
''' Линейный поиск '''&lt;br /&gt;
&lt;br /&gt;
Выбираем шаг &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;. При попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt;i+(1 \cdot q), i+(2 \cdot q), i+(3 \cdot q)&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку. В неё и запишем элемент.&lt;br /&gt;
По сути последовательный поиск - частный случай линейного, где &amp;lt;tex&amp;gt;q=1&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Hashtables56.PNG|400px|Линейный поиск с шагом q.]]&lt;br /&gt;
&lt;br /&gt;
''' Квадратичный поиск '''&lt;br /&gt;
&lt;br /&gt;
Шаг &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; не фиксирован, а изменяется квадратично: &amp;lt;tex&amp;gt;q = 1,4,9,16...&amp;lt;/tex&amp;gt;. Соответственно при попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt; i+1, i+4, i+9&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку.&lt;br /&gt;
&lt;br /&gt;
[[Файл:hashtables3.png|400px|Квадратичный поиск.]]&lt;br /&gt;
&lt;br /&gt;
=== Проверка наличия элемента в таблице===&lt;br /&gt;
&lt;br /&gt;
Проверка осуществляется аналогично добавлению: мы проверяем ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; и другие, в соответствии с выбранной стратегией, пока не найдём искомый элемент или свободную ячейку.&lt;br /&gt;
&lt;br /&gt;
При поиске элемента может получится так, что мы дойдём до конца таблицы. Обычно поиск продолжается, начиная с другого конца, пока мы не придём в ту ячейку, откуда начинался поиск.&lt;br /&gt;
&lt;br /&gt;
=== Проблемы данных стратегий ===&lt;br /&gt;
&lt;br /&gt;
Проблем две — крайне нетривиальное удаление элемента из таблицы и образование кластеров  — последовательностей занятых ячеек.&lt;br /&gt;
&lt;br /&gt;
Кластеризация замедляет все операции с хеш-таблицей: при добавлении требуется перебирать всё больше элементов, при проверке тоже. Чем больше в таблице элементов, тем больше в ней кластеры и тем выше вероятность того, что добавляемый элемент попадёт в кластер.&lt;br /&gt;
Для защиты от кластеризации используется двойное хеширование и [[Хеширование кукушки|хеширование кукушки]].&lt;br /&gt;
&lt;br /&gt;
=== Удаление элемента без пометок ===&lt;br /&gt;
&lt;br /&gt;
Рассуждение будет описывать случай с линейным поиском хеша. Будем при удалении элемента сдвигать всё последующие на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; позиций назад. При этом:&lt;br /&gt;
* если в цепочке встречается элемент с другим хешем, то он должен остаться на своём месте (такая ситуация может возникнуть если оставшаяся часть цепочки была добавлена позже этого элемента)&lt;br /&gt;
* в цепочке не должно оставаться &amp;quot;дырок&amp;quot;, тогда любой элемент с данным хешем будет доступен из начала цепи&lt;br /&gt;
&lt;br /&gt;
Учитывая это будем действовать следующим образом: при поиске следующего элемента цепочки будем пропускать все ячейки с другим значением хеша, первый найденный элемент копировать в текущую ячейку, и затем рекурсивно его удалять. Если такой следующей ячейки нет, то текущий элемент можно просто удалить, сторонние цепочки при этом не разрушатся (чего нельзя сказать про случай квадратичного поиска).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
''' Псевдокод '''&lt;br /&gt;
&lt;br /&gt;
 '''function''' delete('''Item''' i):&lt;br /&gt;
      j = i + q&lt;br /&gt;
      '''while''' table[j] == ''null'' '''or''' table[j].key != table[i].key&lt;br /&gt;
         '''if''' table[j] == ''null''&lt;br /&gt;
            table[i] = ''null''&lt;br /&gt;
            '''return'''&lt;br /&gt;
         j += q&lt;br /&gt;
      table[i] = table[j]&lt;br /&gt;
      delete(j)    &lt;br /&gt;
&lt;br /&gt;
Хеш-таблицу считаем зацикленной&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Утверждение&lt;br /&gt;
|about=о времени работы&lt;br /&gt;
|statement=Асимптотически время работы &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\mathrm{find}&amp;lt;/tex&amp;gt; совпадают&lt;br /&gt;
|proof=&lt;br /&gt;
Заметим что указатель &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; в каждой итерации перемещается вперёд на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; (с учётом рекурсивных вызовов &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt;). То есть этот алгоритм последовательно пройдёт по цепочке от удаляемого элемента до последнего {{---}} с учётом вызова &amp;lt;tex&amp;gt;\mathrm{find}&amp;lt;/tex&amp;gt; собственно для нахождения удаляемого элемента, мы посетим все ячейки цепи.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Вариант с зацикливанием мы не рассматриваем, поскольку если &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; взаимнопросто с размером хеш-таблицы, то для зацикливания в ней вообще не должно быть свободных позиций&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Теперь докажем почему этот алгоритм работает. Собственно нам требуется сохранение трёх условий.&lt;br /&gt;
* В редактируемой цепи не остаётся дырок&lt;br /&gt;
Докажем по индукции. Если на данной итерации мы просто удаляем элемент (база), то после него ничего нет, всё верно. Если же нет, то вызванный в конце &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt; (см. псевдокод) заметёт созданную дыру (скопированный элемент), и сам, по предположению, новых не создаст.&lt;br /&gt;
* Элементы, которые уже на своих местах, не должны быть сдвинуты.&lt;br /&gt;
Это учтено.&lt;br /&gt;
* В других цепочках не появятся дыры&lt;br /&gt;
Противное возможно только в том случае, если какой-то элемент был действительно удалён. Удаляем мы только последнюю ячейку в цепи, и если бы на её месте возникла дыра для сторонней цепочки, это бы означало что элемент, стоящий на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; позиций назад, одновременно принадлежал нашей и другой цепочкам, что невозможно.&lt;br /&gt;
&lt;br /&gt;
==Двойное хеширование==&lt;br /&gt;
'''Двойное хеширование''' (англ. double hashing) {{---}} метод борьбы с коллизиями, возникающими при открытой адресации, основанный на использовании двух хеш-функций для построения различных последовательностей исследования хеш-таблицы.&lt;br /&gt;
&lt;br /&gt;
===Принцип двойного хеширования===&lt;br /&gt;
При двойном хешировании используются две независимые хеш-функции &amp;lt;tex&amp;gt; h_1(k) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; h_2(k) &amp;lt;/tex&amp;gt;. Пусть &amp;lt;tex&amp;gt; k &amp;lt;/tex&amp;gt; {{---}} это наш ключ, &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt; {{---}} размер нашей таблицы, &amp;lt;tex&amp;gt;n \bmod m &amp;lt;/tex&amp;gt; {{---}} остаток от деления &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt; на &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, тогда сначала исследуется ячейка с адресом &amp;lt;tex&amp;gt; h_1(k) &amp;lt;/tex&amp;gt;, если она уже занята, то рассматривается &amp;lt;tex&amp;gt; (h_1(k) +  h_2(k)) \bmod m &amp;lt;/tex&amp;gt;, затем &amp;lt;tex&amp;gt; (h_1(k) +  2 \cdot h_2(k)) \bmod m &amp;lt;/tex&amp;gt; и так далее. В общем случае идёт проверка последовательности ячеек &amp;lt;tex&amp;gt; (h_1(k) +  i \cdot h_2(k)) \bmod m &amp;lt;/tex&amp;gt; где &amp;lt;tex&amp;gt;  i = (0, 1, \; ... \;,  m - 1) &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, операции вставки, удаления и поиска в лучшем случае выполняются за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;, в худшем {{---}} за &amp;lt;tex&amp;gt;O(m)&amp;lt;/tex&amp;gt;, что не отличается от обычного [[Открытое_и_закрытое_хеширование#Линейное разрешение коллизий|линейного разрешения коллизий]].&lt;br /&gt;
Однако в среднем, при грамотном выборе хеш-функций, двойное хеширование будет выдавать лучшие результаты, за счёт того, что вероятность совпадения значений сразу двух независимых хеш-функций ниже, чем одной.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt;\forall x \neq y \; \exists h_1,h_2 : p(h_1(x)=h_1(y))&amp;gt; p((h_1(x)=h_1(y)) \land (h_2(x)=h_2(y)))&amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Выбор хеш-функций===&lt;br /&gt;
&amp;lt;tex&amp;gt; h_1 &amp;lt;/tex&amp;gt; может быть обычной хеш-функцией. Однако чтобы последовательность исследования могла охватить всю таблицу, &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; должна возвращать значения:&lt;br /&gt;
*не равные &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
*независимые  от &amp;lt;tex&amp;gt; h_1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
*взаимно простые с величиной хеш-таблицы&lt;br /&gt;
&lt;br /&gt;
Есть два удобных способа это сделать. Первый состоит в том, что в качестве размера таблицы используется простое число, а &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; возвращает натуральные числа, меньшие &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;. Второй {{---}} размер таблицы является степенью двойки, а &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; возвращает нечетные значения.&lt;br /&gt;
&lt;br /&gt;
Например, если размер таблицы равен &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, то в качестве &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; можно использовать функцию вида &amp;lt;tex&amp;gt; h_2(k) = k \bmod (m-1) + 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Файл: Вставка при двойном хэшировании.svg.jpeg|thumb|right|Вставка при двойном хешировании]]&lt;br /&gt;
&lt;br /&gt;
===Пример===&lt;br /&gt;
&lt;br /&gt;
Показана хеш-таблица размером 13 ячеек, в которой используются вспомогательные функции:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h(k,i) = (h_1(k) + i \cdot h_2(k)) \bmod 13 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h_1(k) = k \bmod 13 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h_2(k) = 1 + k \bmod 11 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Мы хотим вставить ключ 14. Изначально &amp;lt;tex&amp;gt; i = 0 &amp;lt;/tex&amp;gt;. Тогда &amp;lt;tex&amp;gt; h(14,0) = (h_1(14) + 0\cdot h_2(14)) \bmod 13 = 1 &amp;lt;/tex&amp;gt;. Но ячейка с индексом 1 занята, поэтому увеличиваем &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; на 1 и пересчитываем значение хеш-функции. Делаем так, пока не дойдем до пустой ячейки. При &amp;lt;tex&amp;gt; i = 2 &amp;lt;/tex&amp;gt; получаем &amp;lt;tex&amp;gt; h(14,2) = (h_1(14) + 2\cdot h_2(14)) \bmod 13 = 9 &amp;lt;/tex&amp;gt;. Ячейка с номером 9 свободна, значит записываем туда наш ключ.&lt;br /&gt;
&lt;br /&gt;
Таким образом, основная особенность двойного хеширования состоит в том, что при различных &amp;lt;tex&amp;gt; k &amp;lt;/tex&amp;gt; пара &amp;lt;tex&amp;gt; (h_1(k),h_2(k)) &amp;lt;/tex&amp;gt; дает различные последовательности ячеек для исследования.&lt;br /&gt;
&lt;br /&gt;
===Простая реализация===&lt;br /&gt;
Пусть у нас есть некоторый объект &amp;lt;tex&amp;gt; item &amp;lt;/tex&amp;gt;, в котором определено поле &amp;lt;tex&amp;gt; key &amp;lt;/tex&amp;gt;, от которого можно вычислить хеш-функции &amp;lt;tex&amp;gt; h_1(key)&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; h_2(key) &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Так же у нас есть таблица &amp;lt;tex&amp;gt; table &amp;lt;/tex&amp;gt; величиной &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, состоящая из объектов типа &amp;lt;tex&amp;gt; item &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
'''Вставка'''&lt;br /&gt;
 '''function''' add('''Item''' item):&lt;br /&gt;
      x = h1(item.key)&lt;br /&gt;
      y = h2(item.key)&lt;br /&gt;
         '''for''' (i = 0..m)    	&lt;br /&gt;
            '''if''' table[x] == ''null''&lt;br /&gt;
               table[x] = item&lt;br /&gt;
            '''return'''      &lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      table.resize()&amp;lt;span style=&amp;quot;color:Green&amp;quot;&amp;gt;// ошибка, требуется увеличить размер таблицы&lt;br /&gt;
&lt;br /&gt;
'''Поиск'''&lt;br /&gt;
 '''Item''' search('''Item''' key):&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m)&lt;br /&gt;
         '''if''' table[x] != ''null''&lt;br /&gt;
            '''if''' table[x].key == key&lt;br /&gt;
               '''return''' table[x]&lt;br /&gt;
            '''else'''&lt;br /&gt;
               '''return''' ''null''&lt;br /&gt;
      x = (x + y) '''mod''' m   &lt;br /&gt;
      '''return''' ''null''&lt;br /&gt;
&lt;br /&gt;
===Реализация с удалением===&lt;br /&gt;
Что бы наша хеш-таблица поддерживала удаление, требуется добавить массив &amp;lt;tex&amp;gt;deleted&amp;lt;/tex&amp;gt; типов &amp;lt;tex&amp;gt;bool&amp;lt;/tex&amp;gt;, равный по величине массиву &amp;lt;tex&amp;gt;table&amp;lt;/tex&amp;gt;. Теперь при удалении мы просто будем помечать наш объект ''как удалённый'', а при добавлении как ''не удалённый'' и замещать новым добавляемым объектом. При поиске, помимо равенства ключей, мы смотрим, удалён ли элемент, если да, то идём дальше.&lt;br /&gt;
&lt;br /&gt;
'''Вставка'''&lt;br /&gt;
 '''function''' add('''Item''' item):&lt;br /&gt;
      x = h1(item.key)&lt;br /&gt;
      y = h2(item.key)&lt;br /&gt;
      '''for''' (i = 0..m)   	&lt;br /&gt;
         '''if''' table[x] == '''null''' '''or''' deleted[x]&lt;br /&gt;
            table[x] = item&lt;br /&gt;
            deleted[x] = '''false'''&lt;br /&gt;
            '''return'''      &lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      table.resize()&amp;lt;span style=&amp;quot;color:Green&amp;quot;&amp;gt;// ошибка, требуется увеличить размер таблицы&lt;br /&gt;
'''Поиск'''&lt;br /&gt;
 '''Item''' search('''Item''' key):&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m) &lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key '''and''' !deleted[x]&lt;br /&gt;
               '''return''' table[x]&lt;br /&gt;
         '''else'''&lt;br /&gt;
            '''return''' '''null'''&lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      '''return''' '''null'''&lt;br /&gt;
&lt;br /&gt;
'''Удаление'''&lt;br /&gt;
 '''function''' remove('''Item''' key):&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m)&lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key&lt;br /&gt;
               deleted[x] = '''true'''&lt;br /&gt;
         '''else''' &lt;br /&gt;
            '''return'''&lt;br /&gt;
      x = (x + y) '''mod''' m&lt;br /&gt;
&lt;br /&gt;
== Разрешение коллизий в Java==&lt;br /&gt;
В Java 8 для разрешения коллизий используется модифицированный метод цепочек. Суть его заключается в том, что когда количество элементов в корзине превышает определенное значение, данный корзина переходит от использования связного списка к использованию [[АВЛ-дерево|сбалансированного дерева]]. Но данный метод имеет смысл лишь тогда, когда на элементах хеш-таблицы задан линейный порядок. То есть при использовании данный типа &amp;lt;tex&amp;gt;\mathbf{int}&amp;lt;/tex&amp;gt; или &amp;lt;tex&amp;gt;\mathbf{double}&amp;lt;/tex&amp;gt; имеет смысл переходить к дереву поиска, а при использовании каких-нибудь ссылок на объекты не имеет, так как они не реализуют нужный интерфейс. Такой подход позволяет улучшить производительность с &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt;. Данный способ используется в таких коллекциях как HashMap, LinkedHashMap и ConcurrentHashMap.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Hashing_in_Java8.png|500px|Хеширование в Java 8.]]&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
* [[Хеширование]]&lt;br /&gt;
* [[Хеширование_кукушки|Хеширование кукушки]]&lt;br /&gt;
* [[Идеальное_хеширование|Идеальное хеширование]]&lt;br /&gt;
&lt;br /&gt;
== Источники информации ==&lt;br /&gt;
* Бакнелл Дж. М. «Фундаментальные алгоритмы и структуры данных в Delphi», 2003&lt;br /&gt;
* Кормен, Томас Х., Лейзерсон, Чарльз И., Ривест, Рональд Л., Штайн Клиффорд «Алгоритмы: построение и анализ», 2-е издание. Пер. с англ. — М.:Издательский дом &amp;quot;Вильямс&amp;quot;, 2010.— Парал. тит. англ. — ISBN 978-5-8459-0857-5 (рус.)&lt;br /&gt;
* Дональд Кнут. «Искусство программирования, том 3. Сортировка и поиск» {{---}} «Вильямс», 2007 г.{{---}} ISBN 0-201-89685-0&lt;br /&gt;
* Седжвик Р. «Фундаментальные алгоритмы на C. Части 1-4. Анализ. Структуры данных. Сортировка. Поиск», 2003&lt;br /&gt;
* [http://openjdk.java.net/jeps/180 Handle Frequent HashMap Collisions with Balanced Trees]&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Double_hashing Wikipedia {{---}} Double_hashing]&lt;br /&gt;
* [http://ru.wikipedia.org/wiki/%D0%A5%D0%B5%D1%88-%D1%82%D0%B0%D0%B1%D0%BB%D0%B8%D1%86%D0%B0 Разрешение коллизий]&lt;br /&gt;
* [http://rain.ifmo.ru/cat/view.php/vis/hashtables/hash-2001-2 Пример хеш таблицы]&lt;br /&gt;
* [http://research.cs.vt.edu/AVresearch/hashing/double.php Пример хеш таблицы с двойным хешированием]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория: Хеширование]]&lt;br /&gt;
[[Категория: Структуры данных]]&lt;/div&gt;</summary>
		<author><name>Kozichuk</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%A0%D0%B0%D0%B7%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BA%D0%BE%D0%BB%D0%BB%D0%B8%D0%B7%D0%B8%D0%B9&amp;diff=47800</id>
		<title>Разрешение коллизий</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A0%D0%B0%D0%B7%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BA%D0%BE%D0%BB%D0%BB%D0%B8%D0%B7%D0%B8%D0%B9&amp;diff=47800"/>
				<updated>2015-06-06T16:17:51Z</updated>
		
		<summary type="html">&lt;p&gt;Kozichuk: /* Разрешение коллизий в Java 8 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Разрешение [[Хеш-таблица|коллизий]]''' (англ. collision resolution) в [[Хеш-таблица|хеш-таблице]], задача, решаемая несколькими способами: метод цепочек, открытая адресация и т.д. Очень важно сводить количество коллизий к минимуму, так как это увеличивает время работы с хеш-таблицами. &lt;br /&gt;
&lt;br /&gt;
== Разрешение коллизий с помощью цепочек ==&lt;br /&gt;
[[Файл:open_hash.png|thumb|380px|right|Разрешение коллизий при помощи цепочек.]]&lt;br /&gt;
Каждая ячейка &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; массива &amp;lt;tex&amp;gt;H&amp;lt;/tex&amp;gt; содержит указатель на начало [[Список|списка]] всех элементов, хеш-код которых равен &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, либо указывает на их отсутствие. Коллизии приводят к тому, что появляются списки размером больше одного элемента.&lt;br /&gt;
&lt;br /&gt;
В зависимости от того нужна ли нам уникальность значений операции вставки у нас будет работать за разное время. Если не важна, то мы используем список, время вставки в который будет в худшем случае равна &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;. Иначе мы проверяем есть ли в списке данный элемент, а потом в случае его отсутствия мы его добавляем. В таком случае вставка элемента в худшем случае будет выполнена за &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Время работы поиска в наихудшем случае пропорционально длине списка, а если все &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; ключей захешировались в одну и ту же ячейку (создав список длиной &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;) время поиска будет равно &amp;lt;tex&amp;gt;\Theta(n)&amp;lt;/tex&amp;gt; плюс время вычисления хеш-функции, что ничуть не лучше, чем использование связного списка для хранения всех &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; элементов.&lt;br /&gt;
&lt;br /&gt;
Удаления элемента может быть выполнено за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;, как и вставка, при использовании двухсвязного списка.&lt;br /&gt;
&lt;br /&gt;
== Линейное разрешение коллизий ==&lt;br /&gt;
[[Файл:close_hash.png|thumb|380px|right|Пример хеш-таблицы с открытой адресацией и линейным пробированием.]]&lt;br /&gt;
Все элементы хранятся непосредственно в хеш-таблице, без использования связных списков. В отличии от хеширования с цепочками, при использовании этого метода может возникнуть ситуация, когда хеш-таблица окажется полностью заполненной, следовательно будет невозможно добавлять в неё новые элементы. Так что при возникновении такой ситуации решением может быть динамическое увеличение размера хеш-таблицы, с одновременной её перестройкой.&lt;br /&gt;
&lt;br /&gt;
=== Стратегии поиска ===&lt;br /&gt;
&lt;br /&gt;
''' Последовательный поиск '''&lt;br /&gt;
&lt;br /&gt;
При попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt;i+1, i+2, i+3&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку. В неё и запишем элемент.&lt;br /&gt;
&lt;br /&gt;
[[Файл:hashtables1.png|400px|Последовательный поиск, частный случай линейного поиска.]]&lt;br /&gt;
&lt;br /&gt;
''' Линейный поиск '''&lt;br /&gt;
&lt;br /&gt;
Выбираем шаг &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;. При попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt;i+(1 \cdot q), i+(2 \cdot q), i+(3 \cdot q)&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку. В неё и запишем элемент.&lt;br /&gt;
По сути последовательный поиск - частный случай линейного, где &amp;lt;tex&amp;gt;q=1&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Hashtables56.PNG|400px|Линейный поиск с шагом q.]]&lt;br /&gt;
&lt;br /&gt;
''' Квадратичный поиск '''&lt;br /&gt;
&lt;br /&gt;
Шаг &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; не фиксирован, а изменяется квадратично: &amp;lt;tex&amp;gt;q = 1,4,9,16...&amp;lt;/tex&amp;gt;. Соответственно при попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt; i+1, i+4, i+9&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку.&lt;br /&gt;
&lt;br /&gt;
[[Файл:hashtables3.png|400px|Квадратичный поиск.]]&lt;br /&gt;
&lt;br /&gt;
=== Проверка наличия элемента в таблице===&lt;br /&gt;
&lt;br /&gt;
Проверка осуществляется аналогично добавлению: мы проверяем ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; и другие, в соответствии с выбранной стратегией, пока не найдём искомый элемент или свободную ячейку.&lt;br /&gt;
&lt;br /&gt;
При поиске элемента может получится так, что мы дойдём до конца таблицы. Обычно поиск продолжается, начиная с другого конца, пока мы не придём в ту ячейку, откуда начинался поиск.&lt;br /&gt;
&lt;br /&gt;
=== Проблемы данных стратегий ===&lt;br /&gt;
&lt;br /&gt;
Проблем две — крайне нетривиальное удаление элемента из таблицы и образование кластеров  — последовательностей занятых ячеек.&lt;br /&gt;
&lt;br /&gt;
Кластеризация замедляет все операции с хеш-таблицей: при добавлении требуется перебирать всё больше элементов, при проверке тоже. Чем больше в таблице элементов, тем больше в ней кластеры и тем выше вероятность того, что добавляемый элемент попадёт в кластер.&lt;br /&gt;
Для защиты от кластеризации используется двойное хеширование и [[Хеширование кукушки|хеширование кукушки]].&lt;br /&gt;
&lt;br /&gt;
=== Удаление элемента без пометок ===&lt;br /&gt;
&lt;br /&gt;
Рассуждение будет описывать случай с линейным поиском хеша. Будем при удалении элемента сдвигать всё последующие на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; позиций назад. При этом:&lt;br /&gt;
* если в цепочке встречается элемент с другим хешем, то он должен остаться на своём месте (такая ситуация может возникнуть если оставшаяся часть цепочки была добавлена позже этого элемента)&lt;br /&gt;
* в цепочке не должно оставаться &amp;quot;дырок&amp;quot;, тогда любой элемент с данным хешем будет доступен из начала цепи&lt;br /&gt;
&lt;br /&gt;
Учитывая это будем действовать следующим образом: при поиске следующего элемента цепочки будем пропускать все ячейки с другим значением хеша, первый найденный элемент копировать в текущую ячейку, и затем рекурсивно его удалять. Если такой следующей ячейки нет, то текущий элемент можно просто удалить, сторонние цепочки при этом не разрушатся (чего нельзя сказать про случай квадратичного поиска).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
''' Псевдокод '''&lt;br /&gt;
&lt;br /&gt;
 '''function''' delete('''Item''' i):&lt;br /&gt;
      j = i + q&lt;br /&gt;
      '''while''' table[j] == ''null'' '''or''' table[j].key != table[i].key&lt;br /&gt;
         '''if''' table[j] == ''null''&lt;br /&gt;
            table[i] = ''null''&lt;br /&gt;
            '''return'''&lt;br /&gt;
         j += q&lt;br /&gt;
      table[i] = table[j]&lt;br /&gt;
      delete(j)    &lt;br /&gt;
&lt;br /&gt;
Хеш-таблицу считаем зацикленной&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Утверждение&lt;br /&gt;
|about=о времени работы&lt;br /&gt;
|statement=Асимптотически время работы &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\mathrm{find}&amp;lt;/tex&amp;gt; совпадают&lt;br /&gt;
|proof=&lt;br /&gt;
Заметим что указатель &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; в каждой итерации перемещается вперёд на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; (с учётом рекурсивных вызовов &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt;). То есть этот алгоритм последовательно пройдёт по цепочке от удаляемого элемента до последнего {{---}} с учётом вызова &amp;lt;tex&amp;gt;\mathrm{find}&amp;lt;/tex&amp;gt; собственно для нахождения удаляемого элемента, мы посетим все ячейки цепи.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Вариант с зацикливанием мы не рассматриваем, поскольку если &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; взаимнопросто с размером хеш-таблицы, то для зацикливания в ней вообще не должно быть свободных позиций&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Теперь докажем почему этот алгоритм работает. Собственно нам требуется сохранение трёх условий.&lt;br /&gt;
* В редактируемой цепи не остаётся дырок&lt;br /&gt;
Докажем по индукции. Если на данной итерации мы просто удаляем элемент (база), то после него ничего нет, всё верно. Если же нет, то вызванный в конце &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt; (см. псевдокод) заметёт созданную дыру (скопированный элемент), и сам, по предположению, новых не создаст.&lt;br /&gt;
* Элементы, которые уже на своих местах, не должны быть сдвинуты.&lt;br /&gt;
Это учтено.&lt;br /&gt;
* В других цепочках не появятся дыры&lt;br /&gt;
Противное возможно только в том случае, если какой-то элемент был действительно удалён. Удаляем мы только последнюю ячейку в цепи, и если бы на её месте возникла дыра для сторонней цепочки, это бы означало что элемент, стоящий на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; позиций назад, одновременно принадлежал нашей и другой цепочкам, что невозможно.&lt;br /&gt;
&lt;br /&gt;
==Двойное хеширование==&lt;br /&gt;
'''Двойное хеширование''' (англ. double hashing) {{---}} метод борьбы с коллизиями, возникающими при открытой адресации, основанный на использовании двух хеш-функций для построения различных последовательностей исследования хеш-таблицы.&lt;br /&gt;
&lt;br /&gt;
===Принцип двойного хеширования===&lt;br /&gt;
При двойном хешировании используются две независимые хеш-функции &amp;lt;tex&amp;gt; h_1(k) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; h_2(k) &amp;lt;/tex&amp;gt;. Пусть &amp;lt;tex&amp;gt; k &amp;lt;/tex&amp;gt; {{---}} это наш ключ, &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt; {{---}} размер нашей таблицы, &amp;lt;tex&amp;gt;n \bmod m &amp;lt;/tex&amp;gt; {{---}} остаток от деления &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt; на &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, тогда сначала исследуется ячейка с адресом &amp;lt;tex&amp;gt; h_1(k) &amp;lt;/tex&amp;gt;, если она уже занята, то рассматривается &amp;lt;tex&amp;gt; (h_1(k) +  h_2(k)) \bmod m &amp;lt;/tex&amp;gt;, затем &amp;lt;tex&amp;gt; (h_1(k) +  2 \cdot h_2(k)) \bmod m &amp;lt;/tex&amp;gt; и так далее. В общем случае идёт проверка последовательности ячеек &amp;lt;tex&amp;gt; (h_1(k) +  i \cdot h_2(k)) \bmod m &amp;lt;/tex&amp;gt; где &amp;lt;tex&amp;gt;  i = (0, 1, \; ... \;,  m - 1) &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, операции вставки, удаления и поиска в лучшем случае выполняются за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;, в худшем {{---}} за &amp;lt;tex&amp;gt;O(m)&amp;lt;/tex&amp;gt;, что не отличается от обычного [[Открытое_и_закрытое_хеширование#Линейное разрешение коллизий|линейного разрешения коллизий]].&lt;br /&gt;
Однако в среднем, при грамотном выборе хеш-функций, двойное хеширование будет выдавать лучшие результаты, за счёт того, что вероятность совпадения значений сразу двух независимых хеш-функций ниже, чем одной.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt;\forall x \neq y \; \exists h_1,h_2 : p(h_1(x)=h_1(y))&amp;gt; p((h_1(x)=h_1(y)) \land (h_2(x)=h_2(y)))&amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Выбор хеш-функций===&lt;br /&gt;
&amp;lt;tex&amp;gt; h_1 &amp;lt;/tex&amp;gt; может быть обычной хеш-функцией. Однако чтобы последовательность исследования могла охватить всю таблицу, &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; должна возвращать значения:&lt;br /&gt;
*не равные &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
*независимые  от &amp;lt;tex&amp;gt; h_1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
*взаимно простые с величиной хеш-таблицы&lt;br /&gt;
&lt;br /&gt;
Есть два удобных способа это сделать. Первый состоит в том, что в качестве размера таблицы используется простое число, а &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; возвращает натуральные числа, меньшие &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;. Второй {{---}} размер таблицы является степенью двойки, а &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; возвращает нечетные значения.&lt;br /&gt;
&lt;br /&gt;
Например, если размер таблицы равен &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, то в качестве &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; можно использовать функцию вида &amp;lt;tex&amp;gt; h_2(k) = k \bmod (m-1) + 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Файл: Вставка при двойном хэшировании.svg.jpeg|thumb|right|Вставка при двойном хешировании]]&lt;br /&gt;
&lt;br /&gt;
===Пример===&lt;br /&gt;
&lt;br /&gt;
Показана хеш-таблица размером 13 ячеек, в которой используются вспомогательные функции:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h(k,i) = (h_1(k) + i \cdot h_2(k)) \bmod 13 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h_1(k) = k \bmod 13 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h_2(k) = 1 + k \bmod 11 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Мы хотим вставить ключ 14. Изначально &amp;lt;tex&amp;gt; i = 0 &amp;lt;/tex&amp;gt;. Тогда &amp;lt;tex&amp;gt; h(14,0) = (h_1(14) + 0\cdot h_2(14)) \bmod 13 = 1 &amp;lt;/tex&amp;gt;. Но ячейка с индексом 1 занята, поэтому увеличиваем &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; на 1 и пересчитываем значение хеш-функции. Делаем так, пока не дойдем до пустой ячейки. При &amp;lt;tex&amp;gt; i = 2 &amp;lt;/tex&amp;gt; получаем &amp;lt;tex&amp;gt; h(14,2) = (h_1(14) + 2\cdot h_2(14)) \bmod 13 = 9 &amp;lt;/tex&amp;gt;. Ячейка с номером 9 свободна, значит записываем туда наш ключ.&lt;br /&gt;
&lt;br /&gt;
Таким образом, основная особенность двойного хеширования состоит в том, что при различных &amp;lt;tex&amp;gt; k &amp;lt;/tex&amp;gt; пара &amp;lt;tex&amp;gt; (h_1(k),h_2(k)) &amp;lt;/tex&amp;gt; дает различные последовательности ячеек для исследования.&lt;br /&gt;
&lt;br /&gt;
===Простая реализация===&lt;br /&gt;
Пусть у нас есть некоторый объект &amp;lt;tex&amp;gt; item &amp;lt;/tex&amp;gt;, в котором определено поле &amp;lt;tex&amp;gt; key &amp;lt;/tex&amp;gt;, от которого можно вычислить хеш-функции &amp;lt;tex&amp;gt; h_1(key)&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; h_2(key) &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Так же у нас есть таблица &amp;lt;tex&amp;gt; table &amp;lt;/tex&amp;gt; величиной &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, состоящая из объектов типа &amp;lt;tex&amp;gt; item &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
'''Вставка'''&lt;br /&gt;
 '''function''' add('''Item''' item):&lt;br /&gt;
      x = h1(item.key)&lt;br /&gt;
      y = h2(item.key)&lt;br /&gt;
         '''for''' (i = 0..m)    	&lt;br /&gt;
            '''if''' table[x] == ''null''&lt;br /&gt;
               table[x] = item&lt;br /&gt;
            '''return'''      &lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      table.resize()&amp;lt;span style=&amp;quot;color:Green&amp;quot;&amp;gt;// ошибка, требуется увеличить размер таблицы&lt;br /&gt;
&lt;br /&gt;
'''Поиск'''&lt;br /&gt;
 '''Item''' search('''Item''' key):&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m)&lt;br /&gt;
         '''if''' table[x] != ''null''&lt;br /&gt;
            '''if''' table[x].key == key&lt;br /&gt;
               '''return''' table[x]&lt;br /&gt;
            '''else'''&lt;br /&gt;
               '''return''' ''null''&lt;br /&gt;
      x = (x + y) '''mod''' m   &lt;br /&gt;
      '''return''' ''null''&lt;br /&gt;
&lt;br /&gt;
===Реализация с удалением===&lt;br /&gt;
Что бы наша хеш-таблица поддерживала удаление, требуется добавить массив &amp;lt;tex&amp;gt;deleted&amp;lt;/tex&amp;gt; типов &amp;lt;tex&amp;gt;bool&amp;lt;/tex&amp;gt;, равный по величине массиву &amp;lt;tex&amp;gt;table&amp;lt;/tex&amp;gt;. Теперь при удалении мы просто будем помечать наш объект ''как удалённый'', а при добавлении как ''не удалённый'' и замещать новым добавляемым объектом. При поиске, помимо равенства ключей, мы смотрим, удалён ли элемент, если да, то идём дальше.&lt;br /&gt;
&lt;br /&gt;
'''Вставка'''&lt;br /&gt;
 '''function''' add('''Item''' item):&lt;br /&gt;
      x = h1(item.key)&lt;br /&gt;
      y = h2(item.key)&lt;br /&gt;
      '''for''' (i = 0..m)   	&lt;br /&gt;
         '''if''' table[x] == '''null''' '''or''' deleted[x]&lt;br /&gt;
            table[x] = item&lt;br /&gt;
            deleted[x] = '''false'''&lt;br /&gt;
            '''return'''      &lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      table.resize()&amp;lt;span style=&amp;quot;color:Green&amp;quot;&amp;gt;// ошибка, требуется увеличить размер таблицы&lt;br /&gt;
'''Поиск'''&lt;br /&gt;
 '''Item''' search('''Item''' key):&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m) &lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key '''and''' !deleted[x]&lt;br /&gt;
               '''return''' table[x]&lt;br /&gt;
         '''else'''&lt;br /&gt;
            '''return''' '''null'''&lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      '''return''' '''null'''&lt;br /&gt;
&lt;br /&gt;
'''Удаление'''&lt;br /&gt;
 '''function''' remove('''Item''' key):&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m)&lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key&lt;br /&gt;
               deleted[x] = '''true'''&lt;br /&gt;
         '''else''' &lt;br /&gt;
            '''return'''&lt;br /&gt;
      x = (x + y) '''mod''' m&lt;br /&gt;
&lt;br /&gt;
== Разрешение коллизий в Java==&lt;br /&gt;
В Java 8 для разрешения коллизий используется модифицированный метод цепочек. Суть его заключается в том, что когда количество элементов в корзине превышает определенное значение, данный корзина переходит от использования связного списка к использованию [[АВЛ-дерево|сбалансированного дерева]]. Но данный метод имеет смысл лишь тогда, когда на элементах хеш-таблицы задан линейный порядок. То есть при использовании данный типа &amp;lt;tex&amp;gt;int&amp;lt;/tex&amp;gt; или &amp;lt;tex&amp;gt;double&amp;lt;/tex&amp;gt; имеет смысл переходить к дереву поиска, а при использовании каких-нибудь ссылок на объекты не имеет, так как они не реализуют нужный интерфейс. Такой подход позволяет улучшить производительность с &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt;. Данный способ используется в таких коллекциях как HashMap, LinkedHashMap и ConcurrentHashMap.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Hashing_in_Java8.png|500px|Хеширование в Java 8.]]&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
* [[Хеширование]]&lt;br /&gt;
* [[Хеширование_кукушки|Хеширование кукушки]]&lt;br /&gt;
* [[Идеальное_хеширование|Идеальное хеширование]]&lt;br /&gt;
&lt;br /&gt;
== Источники информации ==&lt;br /&gt;
* Бакнелл Дж. М. «Фундаментальные алгоритмы и структуры данных в Delphi», 2003&lt;br /&gt;
* Кормен, Томас Х., Лейзерсон, Чарльз И., Ривест, Рональд Л., Штайн Клиффорд «Алгоритмы: построение и анализ», 2-е издание. Пер. с англ. — М.:Издательский дом &amp;quot;Вильямс&amp;quot;, 2010.— Парал. тит. англ. — ISBN 978-5-8459-0857-5 (рус.)&lt;br /&gt;
* Дональд Кнут. «Искусство программирования, том 3. Сортировка и поиск» {{---}} «Вильямс», 2007 г.{{---}} ISBN 0-201-89685-0&lt;br /&gt;
* Седжвик Р. «Фундаментальные алгоритмы на C. Части 1-4. Анализ. Структуры данных. Сортировка. Поиск», 2003&lt;br /&gt;
* [http://openjdk.java.net/jeps/180 Handle Frequent HashMap Collisions with Balanced Trees]&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Double_hashing Wikipedia {{---}} Double_hashing]&lt;br /&gt;
* [http://ru.wikipedia.org/wiki/%D0%A5%D0%B5%D1%88-%D1%82%D0%B0%D0%B1%D0%BB%D0%B8%D1%86%D0%B0 Разрешение коллизий]&lt;br /&gt;
* [http://rain.ifmo.ru/cat/view.php/vis/hashtables/hash-2001-2 Пример хеш таблицы]&lt;br /&gt;
* [http://research.cs.vt.edu/AVresearch/hashing/double.php Пример хеш таблицы с двойным хешированием]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория: Хеширование]]&lt;br /&gt;
[[Категория: Структуры данных]]&lt;/div&gt;</summary>
		<author><name>Kozichuk</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%A0%D0%B0%D0%B7%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BA%D0%BE%D0%BB%D0%BB%D0%B8%D0%B7%D0%B8%D0%B9&amp;diff=47799</id>
		<title>Разрешение коллизий</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A0%D0%B0%D0%B7%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BA%D0%BE%D0%BB%D0%BB%D0%B8%D0%B7%D0%B8%D0%B9&amp;diff=47799"/>
				<updated>2015-06-06T16:17:21Z</updated>
		
		<summary type="html">&lt;p&gt;Kozichuk: /* Реализация с удалением */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Разрешение [[Хеш-таблица|коллизий]]''' (англ. collision resolution) в [[Хеш-таблица|хеш-таблице]], задача, решаемая несколькими способами: метод цепочек, открытая адресация и т.д. Очень важно сводить количество коллизий к минимуму, так как это увеличивает время работы с хеш-таблицами. &lt;br /&gt;
&lt;br /&gt;
== Разрешение коллизий с помощью цепочек ==&lt;br /&gt;
[[Файл:open_hash.png|thumb|380px|right|Разрешение коллизий при помощи цепочек.]]&lt;br /&gt;
Каждая ячейка &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; массива &amp;lt;tex&amp;gt;H&amp;lt;/tex&amp;gt; содержит указатель на начало [[Список|списка]] всех элементов, хеш-код которых равен &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, либо указывает на их отсутствие. Коллизии приводят к тому, что появляются списки размером больше одного элемента.&lt;br /&gt;
&lt;br /&gt;
В зависимости от того нужна ли нам уникальность значений операции вставки у нас будет работать за разное время. Если не важна, то мы используем список, время вставки в который будет в худшем случае равна &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;. Иначе мы проверяем есть ли в списке данный элемент, а потом в случае его отсутствия мы его добавляем. В таком случае вставка элемента в худшем случае будет выполнена за &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Время работы поиска в наихудшем случае пропорционально длине списка, а если все &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; ключей захешировались в одну и ту же ячейку (создав список длиной &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;) время поиска будет равно &amp;lt;tex&amp;gt;\Theta(n)&amp;lt;/tex&amp;gt; плюс время вычисления хеш-функции, что ничуть не лучше, чем использование связного списка для хранения всех &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; элементов.&lt;br /&gt;
&lt;br /&gt;
Удаления элемента может быть выполнено за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;, как и вставка, при использовании двухсвязного списка.&lt;br /&gt;
&lt;br /&gt;
== Линейное разрешение коллизий ==&lt;br /&gt;
[[Файл:close_hash.png|thumb|380px|right|Пример хеш-таблицы с открытой адресацией и линейным пробированием.]]&lt;br /&gt;
Все элементы хранятся непосредственно в хеш-таблице, без использования связных списков. В отличии от хеширования с цепочками, при использовании этого метода может возникнуть ситуация, когда хеш-таблица окажется полностью заполненной, следовательно будет невозможно добавлять в неё новые элементы. Так что при возникновении такой ситуации решением может быть динамическое увеличение размера хеш-таблицы, с одновременной её перестройкой.&lt;br /&gt;
&lt;br /&gt;
=== Стратегии поиска ===&lt;br /&gt;
&lt;br /&gt;
''' Последовательный поиск '''&lt;br /&gt;
&lt;br /&gt;
При попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt;i+1, i+2, i+3&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку. В неё и запишем элемент.&lt;br /&gt;
&lt;br /&gt;
[[Файл:hashtables1.png|400px|Последовательный поиск, частный случай линейного поиска.]]&lt;br /&gt;
&lt;br /&gt;
''' Линейный поиск '''&lt;br /&gt;
&lt;br /&gt;
Выбираем шаг &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;. При попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt;i+(1 \cdot q), i+(2 \cdot q), i+(3 \cdot q)&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку. В неё и запишем элемент.&lt;br /&gt;
По сути последовательный поиск - частный случай линейного, где &amp;lt;tex&amp;gt;q=1&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Hashtables56.PNG|400px|Линейный поиск с шагом q.]]&lt;br /&gt;
&lt;br /&gt;
''' Квадратичный поиск '''&lt;br /&gt;
&lt;br /&gt;
Шаг &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; не фиксирован, а изменяется квадратично: &amp;lt;tex&amp;gt;q = 1,4,9,16...&amp;lt;/tex&amp;gt;. Соответственно при попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt; i+1, i+4, i+9&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку.&lt;br /&gt;
&lt;br /&gt;
[[Файл:hashtables3.png|400px|Квадратичный поиск.]]&lt;br /&gt;
&lt;br /&gt;
=== Проверка наличия элемента в таблице===&lt;br /&gt;
&lt;br /&gt;
Проверка осуществляется аналогично добавлению: мы проверяем ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; и другие, в соответствии с выбранной стратегией, пока не найдём искомый элемент или свободную ячейку.&lt;br /&gt;
&lt;br /&gt;
При поиске элемента может получится так, что мы дойдём до конца таблицы. Обычно поиск продолжается, начиная с другого конца, пока мы не придём в ту ячейку, откуда начинался поиск.&lt;br /&gt;
&lt;br /&gt;
=== Проблемы данных стратегий ===&lt;br /&gt;
&lt;br /&gt;
Проблем две — крайне нетривиальное удаление элемента из таблицы и образование кластеров  — последовательностей занятых ячеек.&lt;br /&gt;
&lt;br /&gt;
Кластеризация замедляет все операции с хеш-таблицей: при добавлении требуется перебирать всё больше элементов, при проверке тоже. Чем больше в таблице элементов, тем больше в ней кластеры и тем выше вероятность того, что добавляемый элемент попадёт в кластер.&lt;br /&gt;
Для защиты от кластеризации используется двойное хеширование и [[Хеширование кукушки|хеширование кукушки]].&lt;br /&gt;
&lt;br /&gt;
=== Удаление элемента без пометок ===&lt;br /&gt;
&lt;br /&gt;
Рассуждение будет описывать случай с линейным поиском хеша. Будем при удалении элемента сдвигать всё последующие на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; позиций назад. При этом:&lt;br /&gt;
* если в цепочке встречается элемент с другим хешем, то он должен остаться на своём месте (такая ситуация может возникнуть если оставшаяся часть цепочки была добавлена позже этого элемента)&lt;br /&gt;
* в цепочке не должно оставаться &amp;quot;дырок&amp;quot;, тогда любой элемент с данным хешем будет доступен из начала цепи&lt;br /&gt;
&lt;br /&gt;
Учитывая это будем действовать следующим образом: при поиске следующего элемента цепочки будем пропускать все ячейки с другим значением хеша, первый найденный элемент копировать в текущую ячейку, и затем рекурсивно его удалять. Если такой следующей ячейки нет, то текущий элемент можно просто удалить, сторонние цепочки при этом не разрушатся (чего нельзя сказать про случай квадратичного поиска).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
''' Псевдокод '''&lt;br /&gt;
&lt;br /&gt;
 '''function''' delete('''Item''' i):&lt;br /&gt;
      j = i + q&lt;br /&gt;
      '''while''' table[j] == ''null'' '''or''' table[j].key != table[i].key&lt;br /&gt;
         '''if''' table[j] == ''null''&lt;br /&gt;
            table[i] = ''null''&lt;br /&gt;
            '''return'''&lt;br /&gt;
         j += q&lt;br /&gt;
      table[i] = table[j]&lt;br /&gt;
      delete(j)    &lt;br /&gt;
&lt;br /&gt;
Хеш-таблицу считаем зацикленной&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Утверждение&lt;br /&gt;
|about=о времени работы&lt;br /&gt;
|statement=Асимптотически время работы &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\mathrm{find}&amp;lt;/tex&amp;gt; совпадают&lt;br /&gt;
|proof=&lt;br /&gt;
Заметим что указатель &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; в каждой итерации перемещается вперёд на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; (с учётом рекурсивных вызовов &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt;). То есть этот алгоритм последовательно пройдёт по цепочке от удаляемого элемента до последнего {{---}} с учётом вызова &amp;lt;tex&amp;gt;\mathrm{find}&amp;lt;/tex&amp;gt; собственно для нахождения удаляемого элемента, мы посетим все ячейки цепи.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Вариант с зацикливанием мы не рассматриваем, поскольку если &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; взаимнопросто с размером хеш-таблицы, то для зацикливания в ней вообще не должно быть свободных позиций&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Теперь докажем почему этот алгоритм работает. Собственно нам требуется сохранение трёх условий.&lt;br /&gt;
* В редактируемой цепи не остаётся дырок&lt;br /&gt;
Докажем по индукции. Если на данной итерации мы просто удаляем элемент (база), то после него ничего нет, всё верно. Если же нет, то вызванный в конце &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt; (см. псевдокод) заметёт созданную дыру (скопированный элемент), и сам, по предположению, новых не создаст.&lt;br /&gt;
* Элементы, которые уже на своих местах, не должны быть сдвинуты.&lt;br /&gt;
Это учтено.&lt;br /&gt;
* В других цепочках не появятся дыры&lt;br /&gt;
Противное возможно только в том случае, если какой-то элемент был действительно удалён. Удаляем мы только последнюю ячейку в цепи, и если бы на её месте возникла дыра для сторонней цепочки, это бы означало что элемент, стоящий на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; позиций назад, одновременно принадлежал нашей и другой цепочкам, что невозможно.&lt;br /&gt;
&lt;br /&gt;
==Двойное хеширование==&lt;br /&gt;
'''Двойное хеширование''' (англ. double hashing) {{---}} метод борьбы с коллизиями, возникающими при открытой адресации, основанный на использовании двух хеш-функций для построения различных последовательностей исследования хеш-таблицы.&lt;br /&gt;
&lt;br /&gt;
===Принцип двойного хеширования===&lt;br /&gt;
При двойном хешировании используются две независимые хеш-функции &amp;lt;tex&amp;gt; h_1(k) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; h_2(k) &amp;lt;/tex&amp;gt;. Пусть &amp;lt;tex&amp;gt; k &amp;lt;/tex&amp;gt; {{---}} это наш ключ, &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt; {{---}} размер нашей таблицы, &amp;lt;tex&amp;gt;n \bmod m &amp;lt;/tex&amp;gt; {{---}} остаток от деления &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt; на &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, тогда сначала исследуется ячейка с адресом &amp;lt;tex&amp;gt; h_1(k) &amp;lt;/tex&amp;gt;, если она уже занята, то рассматривается &amp;lt;tex&amp;gt; (h_1(k) +  h_2(k)) \bmod m &amp;lt;/tex&amp;gt;, затем &amp;lt;tex&amp;gt; (h_1(k) +  2 \cdot h_2(k)) \bmod m &amp;lt;/tex&amp;gt; и так далее. В общем случае идёт проверка последовательности ячеек &amp;lt;tex&amp;gt; (h_1(k) +  i \cdot h_2(k)) \bmod m &amp;lt;/tex&amp;gt; где &amp;lt;tex&amp;gt;  i = (0, 1, \; ... \;,  m - 1) &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, операции вставки, удаления и поиска в лучшем случае выполняются за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;, в худшем {{---}} за &amp;lt;tex&amp;gt;O(m)&amp;lt;/tex&amp;gt;, что не отличается от обычного [[Открытое_и_закрытое_хеширование#Линейное разрешение коллизий|линейного разрешения коллизий]].&lt;br /&gt;
Однако в среднем, при грамотном выборе хеш-функций, двойное хеширование будет выдавать лучшие результаты, за счёт того, что вероятность совпадения значений сразу двух независимых хеш-функций ниже, чем одной.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt;\forall x \neq y \; \exists h_1,h_2 : p(h_1(x)=h_1(y))&amp;gt; p((h_1(x)=h_1(y)) \land (h_2(x)=h_2(y)))&amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Выбор хеш-функций===&lt;br /&gt;
&amp;lt;tex&amp;gt; h_1 &amp;lt;/tex&amp;gt; может быть обычной хеш-функцией. Однако чтобы последовательность исследования могла охватить всю таблицу, &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; должна возвращать значения:&lt;br /&gt;
*не равные &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
*независимые  от &amp;lt;tex&amp;gt; h_1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
*взаимно простые с величиной хеш-таблицы&lt;br /&gt;
&lt;br /&gt;
Есть два удобных способа это сделать. Первый состоит в том, что в качестве размера таблицы используется простое число, а &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; возвращает натуральные числа, меньшие &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;. Второй {{---}} размер таблицы является степенью двойки, а &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; возвращает нечетные значения.&lt;br /&gt;
&lt;br /&gt;
Например, если размер таблицы равен &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, то в качестве &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; можно использовать функцию вида &amp;lt;tex&amp;gt; h_2(k) = k \bmod (m-1) + 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Файл: Вставка при двойном хэшировании.svg.jpeg|thumb|right|Вставка при двойном хешировании]]&lt;br /&gt;
&lt;br /&gt;
===Пример===&lt;br /&gt;
&lt;br /&gt;
Показана хеш-таблица размером 13 ячеек, в которой используются вспомогательные функции:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h(k,i) = (h_1(k) + i \cdot h_2(k)) \bmod 13 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h_1(k) = k \bmod 13 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h_2(k) = 1 + k \bmod 11 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Мы хотим вставить ключ 14. Изначально &amp;lt;tex&amp;gt; i = 0 &amp;lt;/tex&amp;gt;. Тогда &amp;lt;tex&amp;gt; h(14,0) = (h_1(14) + 0\cdot h_2(14)) \bmod 13 = 1 &amp;lt;/tex&amp;gt;. Но ячейка с индексом 1 занята, поэтому увеличиваем &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; на 1 и пересчитываем значение хеш-функции. Делаем так, пока не дойдем до пустой ячейки. При &amp;lt;tex&amp;gt; i = 2 &amp;lt;/tex&amp;gt; получаем &amp;lt;tex&amp;gt; h(14,2) = (h_1(14) + 2\cdot h_2(14)) \bmod 13 = 9 &amp;lt;/tex&amp;gt;. Ячейка с номером 9 свободна, значит записываем туда наш ключ.&lt;br /&gt;
&lt;br /&gt;
Таким образом, основная особенность двойного хеширования состоит в том, что при различных &amp;lt;tex&amp;gt; k &amp;lt;/tex&amp;gt; пара &amp;lt;tex&amp;gt; (h_1(k),h_2(k)) &amp;lt;/tex&amp;gt; дает различные последовательности ячеек для исследования.&lt;br /&gt;
&lt;br /&gt;
===Простая реализация===&lt;br /&gt;
Пусть у нас есть некоторый объект &amp;lt;tex&amp;gt; item &amp;lt;/tex&amp;gt;, в котором определено поле &amp;lt;tex&amp;gt; key &amp;lt;/tex&amp;gt;, от которого можно вычислить хеш-функции &amp;lt;tex&amp;gt; h_1(key)&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; h_2(key) &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Так же у нас есть таблица &amp;lt;tex&amp;gt; table &amp;lt;/tex&amp;gt; величиной &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, состоящая из объектов типа &amp;lt;tex&amp;gt; item &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
'''Вставка'''&lt;br /&gt;
 '''function''' add('''Item''' item):&lt;br /&gt;
      x = h1(item.key)&lt;br /&gt;
      y = h2(item.key)&lt;br /&gt;
         '''for''' (i = 0..m)    	&lt;br /&gt;
            '''if''' table[x] == ''null''&lt;br /&gt;
               table[x] = item&lt;br /&gt;
            '''return'''      &lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      table.resize()&amp;lt;span style=&amp;quot;color:Green&amp;quot;&amp;gt;// ошибка, требуется увеличить размер таблицы&lt;br /&gt;
&lt;br /&gt;
'''Поиск'''&lt;br /&gt;
 '''Item''' search('''Item''' key):&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m)&lt;br /&gt;
         '''if''' table[x] != ''null''&lt;br /&gt;
            '''if''' table[x].key == key&lt;br /&gt;
               '''return''' table[x]&lt;br /&gt;
            '''else'''&lt;br /&gt;
               '''return''' ''null''&lt;br /&gt;
      x = (x + y) '''mod''' m   &lt;br /&gt;
      '''return''' ''null''&lt;br /&gt;
&lt;br /&gt;
===Реализация с удалением===&lt;br /&gt;
Что бы наша хеш-таблица поддерживала удаление, требуется добавить массив &amp;lt;tex&amp;gt;deleted&amp;lt;/tex&amp;gt; типов &amp;lt;tex&amp;gt;bool&amp;lt;/tex&amp;gt;, равный по величине массиву &amp;lt;tex&amp;gt;table&amp;lt;/tex&amp;gt;. Теперь при удалении мы просто будем помечать наш объект ''как удалённый'', а при добавлении как ''не удалённый'' и замещать новым добавляемым объектом. При поиске, помимо равенства ключей, мы смотрим, удалён ли элемент, если да, то идём дальше.&lt;br /&gt;
&lt;br /&gt;
'''Вставка'''&lt;br /&gt;
 '''function''' add('''Item''' item):&lt;br /&gt;
      x = h1(item.key)&lt;br /&gt;
      y = h2(item.key)&lt;br /&gt;
      '''for''' (i = 0..m)   	&lt;br /&gt;
         '''if''' table[x] == '''null''' '''or''' deleted[x]&lt;br /&gt;
            table[x] = item&lt;br /&gt;
            deleted[x] = '''false'''&lt;br /&gt;
            '''return'''      &lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      table.resize()&amp;lt;span style=&amp;quot;color:Green&amp;quot;&amp;gt;// ошибка, требуется увеличить размер таблицы&lt;br /&gt;
'''Поиск'''&lt;br /&gt;
 '''Item''' search('''Item''' key):&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m) &lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key '''and''' !deleted[x]&lt;br /&gt;
               '''return''' table[x]&lt;br /&gt;
         '''else'''&lt;br /&gt;
            '''return''' '''null'''&lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      '''return''' '''null'''&lt;br /&gt;
&lt;br /&gt;
'''Удаление'''&lt;br /&gt;
 '''function''' remove('''Item''' key):&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m)&lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key&lt;br /&gt;
               deleted[x] = '''true'''&lt;br /&gt;
         '''else''' &lt;br /&gt;
            '''return'''&lt;br /&gt;
      x = (x + y) '''mod''' m&lt;br /&gt;
&lt;br /&gt;
== Разрешение коллизий в Java 8==&lt;br /&gt;
В Java 8 для разрешения коллизий используется модифицированный метод цепочек. Суть его заключается в том, что когда количество элементов в корзине превышает определенное значение, данный корзина переходит от использования связного списка к использованию [[АВЛ-дерево|сбалансированного дерева]]. Но данный метод имеет смысл лишь тогда, когда на элементах хеш-таблицы задан линейный порядок. То есть при использовании данный типа &amp;lt;tex&amp;gt;int&amp;lt;/tex&amp;gt; или &amp;lt;tex&amp;gt;double&amp;lt;/tex&amp;gt; имеет смысл переходить к дереву поиска, а при использовании каких-нибудь ссылок на объекты не имеет, так как они не реализуют нужный интерфейс. Такой подход позволяет улучшить производительность с &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt;. Данный способ используется в таких коллекциях как HashMap, LinkedHashMap и ConcurrentHashMap.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Hashing_in_Java8.png|500px|Хеширование в Java 8.]]&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
* [[Хеширование]]&lt;br /&gt;
* [[Хеширование_кукушки|Хеширование кукушки]]&lt;br /&gt;
* [[Идеальное_хеширование|Идеальное хеширование]]&lt;br /&gt;
&lt;br /&gt;
== Источники информации ==&lt;br /&gt;
* Бакнелл Дж. М. «Фундаментальные алгоритмы и структуры данных в Delphi», 2003&lt;br /&gt;
* Кормен, Томас Х., Лейзерсон, Чарльз И., Ривест, Рональд Л., Штайн Клиффорд «Алгоритмы: построение и анализ», 2-е издание. Пер. с англ. — М.:Издательский дом &amp;quot;Вильямс&amp;quot;, 2010.— Парал. тит. англ. — ISBN 978-5-8459-0857-5 (рус.)&lt;br /&gt;
* Дональд Кнут. «Искусство программирования, том 3. Сортировка и поиск» {{---}} «Вильямс», 2007 г.{{---}} ISBN 0-201-89685-0&lt;br /&gt;
* Седжвик Р. «Фундаментальные алгоритмы на C. Части 1-4. Анализ. Структуры данных. Сортировка. Поиск», 2003&lt;br /&gt;
* [http://openjdk.java.net/jeps/180 Handle Frequent HashMap Collisions with Balanced Trees]&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Double_hashing Wikipedia {{---}} Double_hashing]&lt;br /&gt;
* [http://ru.wikipedia.org/wiki/%D0%A5%D0%B5%D1%88-%D1%82%D0%B0%D0%B1%D0%BB%D0%B8%D1%86%D0%B0 Разрешение коллизий]&lt;br /&gt;
* [http://rain.ifmo.ru/cat/view.php/vis/hashtables/hash-2001-2 Пример хеш таблицы]&lt;br /&gt;
* [http://research.cs.vt.edu/AVresearch/hashing/double.php Пример хеш таблицы с двойным хешированием]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория: Хеширование]]&lt;br /&gt;
[[Категория: Структуры данных]]&lt;/div&gt;</summary>
		<author><name>Kozichuk</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%A0%D0%B0%D0%B7%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BA%D0%BE%D0%BB%D0%BB%D0%B8%D0%B7%D0%B8%D0%B9&amp;diff=47798</id>
		<title>Разрешение коллизий</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A0%D0%B0%D0%B7%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BA%D0%BE%D0%BB%D0%BB%D0%B8%D0%B7%D0%B8%D0%B9&amp;diff=47798"/>
				<updated>2015-06-06T16:17:03Z</updated>
		
		<summary type="html">&lt;p&gt;Kozichuk: /* Простая реализация */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Разрешение [[Хеш-таблица|коллизий]]''' (англ. collision resolution) в [[Хеш-таблица|хеш-таблице]], задача, решаемая несколькими способами: метод цепочек, открытая адресация и т.д. Очень важно сводить количество коллизий к минимуму, так как это увеличивает время работы с хеш-таблицами. &lt;br /&gt;
&lt;br /&gt;
== Разрешение коллизий с помощью цепочек ==&lt;br /&gt;
[[Файл:open_hash.png|thumb|380px|right|Разрешение коллизий при помощи цепочек.]]&lt;br /&gt;
Каждая ячейка &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; массива &amp;lt;tex&amp;gt;H&amp;lt;/tex&amp;gt; содержит указатель на начало [[Список|списка]] всех элементов, хеш-код которых равен &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, либо указывает на их отсутствие. Коллизии приводят к тому, что появляются списки размером больше одного элемента.&lt;br /&gt;
&lt;br /&gt;
В зависимости от того нужна ли нам уникальность значений операции вставки у нас будет работать за разное время. Если не важна, то мы используем список, время вставки в который будет в худшем случае равна &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;. Иначе мы проверяем есть ли в списке данный элемент, а потом в случае его отсутствия мы его добавляем. В таком случае вставка элемента в худшем случае будет выполнена за &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Время работы поиска в наихудшем случае пропорционально длине списка, а если все &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; ключей захешировались в одну и ту же ячейку (создав список длиной &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;) время поиска будет равно &amp;lt;tex&amp;gt;\Theta(n)&amp;lt;/tex&amp;gt; плюс время вычисления хеш-функции, что ничуть не лучше, чем использование связного списка для хранения всех &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; элементов.&lt;br /&gt;
&lt;br /&gt;
Удаления элемента может быть выполнено за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;, как и вставка, при использовании двухсвязного списка.&lt;br /&gt;
&lt;br /&gt;
== Линейное разрешение коллизий ==&lt;br /&gt;
[[Файл:close_hash.png|thumb|380px|right|Пример хеш-таблицы с открытой адресацией и линейным пробированием.]]&lt;br /&gt;
Все элементы хранятся непосредственно в хеш-таблице, без использования связных списков. В отличии от хеширования с цепочками, при использовании этого метода может возникнуть ситуация, когда хеш-таблица окажется полностью заполненной, следовательно будет невозможно добавлять в неё новые элементы. Так что при возникновении такой ситуации решением может быть динамическое увеличение размера хеш-таблицы, с одновременной её перестройкой.&lt;br /&gt;
&lt;br /&gt;
=== Стратегии поиска ===&lt;br /&gt;
&lt;br /&gt;
''' Последовательный поиск '''&lt;br /&gt;
&lt;br /&gt;
При попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt;i+1, i+2, i+3&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку. В неё и запишем элемент.&lt;br /&gt;
&lt;br /&gt;
[[Файл:hashtables1.png|400px|Последовательный поиск, частный случай линейного поиска.]]&lt;br /&gt;
&lt;br /&gt;
''' Линейный поиск '''&lt;br /&gt;
&lt;br /&gt;
Выбираем шаг &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;. При попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt;i+(1 \cdot q), i+(2 \cdot q), i+(3 \cdot q)&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку. В неё и запишем элемент.&lt;br /&gt;
По сути последовательный поиск - частный случай линейного, где &amp;lt;tex&amp;gt;q=1&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Hashtables56.PNG|400px|Линейный поиск с шагом q.]]&lt;br /&gt;
&lt;br /&gt;
''' Квадратичный поиск '''&lt;br /&gt;
&lt;br /&gt;
Шаг &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; не фиксирован, а изменяется квадратично: &amp;lt;tex&amp;gt;q = 1,4,9,16...&amp;lt;/tex&amp;gt;. Соответственно при попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt; i+1, i+4, i+9&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку.&lt;br /&gt;
&lt;br /&gt;
[[Файл:hashtables3.png|400px|Квадратичный поиск.]]&lt;br /&gt;
&lt;br /&gt;
=== Проверка наличия элемента в таблице===&lt;br /&gt;
&lt;br /&gt;
Проверка осуществляется аналогично добавлению: мы проверяем ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; и другие, в соответствии с выбранной стратегией, пока не найдём искомый элемент или свободную ячейку.&lt;br /&gt;
&lt;br /&gt;
При поиске элемента может получится так, что мы дойдём до конца таблицы. Обычно поиск продолжается, начиная с другого конца, пока мы не придём в ту ячейку, откуда начинался поиск.&lt;br /&gt;
&lt;br /&gt;
=== Проблемы данных стратегий ===&lt;br /&gt;
&lt;br /&gt;
Проблем две — крайне нетривиальное удаление элемента из таблицы и образование кластеров  — последовательностей занятых ячеек.&lt;br /&gt;
&lt;br /&gt;
Кластеризация замедляет все операции с хеш-таблицей: при добавлении требуется перебирать всё больше элементов, при проверке тоже. Чем больше в таблице элементов, тем больше в ней кластеры и тем выше вероятность того, что добавляемый элемент попадёт в кластер.&lt;br /&gt;
Для защиты от кластеризации используется двойное хеширование и [[Хеширование кукушки|хеширование кукушки]].&lt;br /&gt;
&lt;br /&gt;
=== Удаление элемента без пометок ===&lt;br /&gt;
&lt;br /&gt;
Рассуждение будет описывать случай с линейным поиском хеша. Будем при удалении элемента сдвигать всё последующие на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; позиций назад. При этом:&lt;br /&gt;
* если в цепочке встречается элемент с другим хешем, то он должен остаться на своём месте (такая ситуация может возникнуть если оставшаяся часть цепочки была добавлена позже этого элемента)&lt;br /&gt;
* в цепочке не должно оставаться &amp;quot;дырок&amp;quot;, тогда любой элемент с данным хешем будет доступен из начала цепи&lt;br /&gt;
&lt;br /&gt;
Учитывая это будем действовать следующим образом: при поиске следующего элемента цепочки будем пропускать все ячейки с другим значением хеша, первый найденный элемент копировать в текущую ячейку, и затем рекурсивно его удалять. Если такой следующей ячейки нет, то текущий элемент можно просто удалить, сторонние цепочки при этом не разрушатся (чего нельзя сказать про случай квадратичного поиска).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
''' Псевдокод '''&lt;br /&gt;
&lt;br /&gt;
 '''function''' delete('''Item''' i):&lt;br /&gt;
      j = i + q&lt;br /&gt;
      '''while''' table[j] == ''null'' '''or''' table[j].key != table[i].key&lt;br /&gt;
         '''if''' table[j] == ''null''&lt;br /&gt;
            table[i] = ''null''&lt;br /&gt;
            '''return'''&lt;br /&gt;
         j += q&lt;br /&gt;
      table[i] = table[j]&lt;br /&gt;
      delete(j)    &lt;br /&gt;
&lt;br /&gt;
Хеш-таблицу считаем зацикленной&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Утверждение&lt;br /&gt;
|about=о времени работы&lt;br /&gt;
|statement=Асимптотически время работы &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\mathrm{find}&amp;lt;/tex&amp;gt; совпадают&lt;br /&gt;
|proof=&lt;br /&gt;
Заметим что указатель &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; в каждой итерации перемещается вперёд на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; (с учётом рекурсивных вызовов &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt;). То есть этот алгоритм последовательно пройдёт по цепочке от удаляемого элемента до последнего {{---}} с учётом вызова &amp;lt;tex&amp;gt;\mathrm{find}&amp;lt;/tex&amp;gt; собственно для нахождения удаляемого элемента, мы посетим все ячейки цепи.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Вариант с зацикливанием мы не рассматриваем, поскольку если &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; взаимнопросто с размером хеш-таблицы, то для зацикливания в ней вообще не должно быть свободных позиций&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Теперь докажем почему этот алгоритм работает. Собственно нам требуется сохранение трёх условий.&lt;br /&gt;
* В редактируемой цепи не остаётся дырок&lt;br /&gt;
Докажем по индукции. Если на данной итерации мы просто удаляем элемент (база), то после него ничего нет, всё верно. Если же нет, то вызванный в конце &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt; (см. псевдокод) заметёт созданную дыру (скопированный элемент), и сам, по предположению, новых не создаст.&lt;br /&gt;
* Элементы, которые уже на своих местах, не должны быть сдвинуты.&lt;br /&gt;
Это учтено.&lt;br /&gt;
* В других цепочках не появятся дыры&lt;br /&gt;
Противное возможно только в том случае, если какой-то элемент был действительно удалён. Удаляем мы только последнюю ячейку в цепи, и если бы на её месте возникла дыра для сторонней цепочки, это бы означало что элемент, стоящий на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; позиций назад, одновременно принадлежал нашей и другой цепочкам, что невозможно.&lt;br /&gt;
&lt;br /&gt;
==Двойное хеширование==&lt;br /&gt;
'''Двойное хеширование''' (англ. double hashing) {{---}} метод борьбы с коллизиями, возникающими при открытой адресации, основанный на использовании двух хеш-функций для построения различных последовательностей исследования хеш-таблицы.&lt;br /&gt;
&lt;br /&gt;
===Принцип двойного хеширования===&lt;br /&gt;
При двойном хешировании используются две независимые хеш-функции &amp;lt;tex&amp;gt; h_1(k) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; h_2(k) &amp;lt;/tex&amp;gt;. Пусть &amp;lt;tex&amp;gt; k &amp;lt;/tex&amp;gt; {{---}} это наш ключ, &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt; {{---}} размер нашей таблицы, &amp;lt;tex&amp;gt;n \bmod m &amp;lt;/tex&amp;gt; {{---}} остаток от деления &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt; на &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, тогда сначала исследуется ячейка с адресом &amp;lt;tex&amp;gt; h_1(k) &amp;lt;/tex&amp;gt;, если она уже занята, то рассматривается &amp;lt;tex&amp;gt; (h_1(k) +  h_2(k)) \bmod m &amp;lt;/tex&amp;gt;, затем &amp;lt;tex&amp;gt; (h_1(k) +  2 \cdot h_2(k)) \bmod m &amp;lt;/tex&amp;gt; и так далее. В общем случае идёт проверка последовательности ячеек &amp;lt;tex&amp;gt; (h_1(k) +  i \cdot h_2(k)) \bmod m &amp;lt;/tex&amp;gt; где &amp;lt;tex&amp;gt;  i = (0, 1, \; ... \;,  m - 1) &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, операции вставки, удаления и поиска в лучшем случае выполняются за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;, в худшем {{---}} за &amp;lt;tex&amp;gt;O(m)&amp;lt;/tex&amp;gt;, что не отличается от обычного [[Открытое_и_закрытое_хеширование#Линейное разрешение коллизий|линейного разрешения коллизий]].&lt;br /&gt;
Однако в среднем, при грамотном выборе хеш-функций, двойное хеширование будет выдавать лучшие результаты, за счёт того, что вероятность совпадения значений сразу двух независимых хеш-функций ниже, чем одной.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt;\forall x \neq y \; \exists h_1,h_2 : p(h_1(x)=h_1(y))&amp;gt; p((h_1(x)=h_1(y)) \land (h_2(x)=h_2(y)))&amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Выбор хеш-функций===&lt;br /&gt;
&amp;lt;tex&amp;gt; h_1 &amp;lt;/tex&amp;gt; может быть обычной хеш-функцией. Однако чтобы последовательность исследования могла охватить всю таблицу, &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; должна возвращать значения:&lt;br /&gt;
*не равные &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
*независимые  от &amp;lt;tex&amp;gt; h_1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
*взаимно простые с величиной хеш-таблицы&lt;br /&gt;
&lt;br /&gt;
Есть два удобных способа это сделать. Первый состоит в том, что в качестве размера таблицы используется простое число, а &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; возвращает натуральные числа, меньшие &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;. Второй {{---}} размер таблицы является степенью двойки, а &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; возвращает нечетные значения.&lt;br /&gt;
&lt;br /&gt;
Например, если размер таблицы равен &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, то в качестве &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; можно использовать функцию вида &amp;lt;tex&amp;gt; h_2(k) = k \bmod (m-1) + 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Файл: Вставка при двойном хэшировании.svg.jpeg|thumb|right|Вставка при двойном хешировании]]&lt;br /&gt;
&lt;br /&gt;
===Пример===&lt;br /&gt;
&lt;br /&gt;
Показана хеш-таблица размером 13 ячеек, в которой используются вспомогательные функции:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h(k,i) = (h_1(k) + i \cdot h_2(k)) \bmod 13 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h_1(k) = k \bmod 13 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h_2(k) = 1 + k \bmod 11 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Мы хотим вставить ключ 14. Изначально &amp;lt;tex&amp;gt; i = 0 &amp;lt;/tex&amp;gt;. Тогда &amp;lt;tex&amp;gt; h(14,0) = (h_1(14) + 0\cdot h_2(14)) \bmod 13 = 1 &amp;lt;/tex&amp;gt;. Но ячейка с индексом 1 занята, поэтому увеличиваем &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; на 1 и пересчитываем значение хеш-функции. Делаем так, пока не дойдем до пустой ячейки. При &amp;lt;tex&amp;gt; i = 2 &amp;lt;/tex&amp;gt; получаем &amp;lt;tex&amp;gt; h(14,2) = (h_1(14) + 2\cdot h_2(14)) \bmod 13 = 9 &amp;lt;/tex&amp;gt;. Ячейка с номером 9 свободна, значит записываем туда наш ключ.&lt;br /&gt;
&lt;br /&gt;
Таким образом, основная особенность двойного хеширования состоит в том, что при различных &amp;lt;tex&amp;gt; k &amp;lt;/tex&amp;gt; пара &amp;lt;tex&amp;gt; (h_1(k),h_2(k)) &amp;lt;/tex&amp;gt; дает различные последовательности ячеек для исследования.&lt;br /&gt;
&lt;br /&gt;
===Простая реализация===&lt;br /&gt;
Пусть у нас есть некоторый объект &amp;lt;tex&amp;gt; item &amp;lt;/tex&amp;gt;, в котором определено поле &amp;lt;tex&amp;gt; key &amp;lt;/tex&amp;gt;, от которого можно вычислить хеш-функции &amp;lt;tex&amp;gt; h_1(key)&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; h_2(key) &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Так же у нас есть таблица &amp;lt;tex&amp;gt; table &amp;lt;/tex&amp;gt; величиной &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, состоящая из объектов типа &amp;lt;tex&amp;gt; item &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
'''Вставка'''&lt;br /&gt;
 '''function''' add('''Item''' item):&lt;br /&gt;
      x = h1(item.key)&lt;br /&gt;
      y = h2(item.key)&lt;br /&gt;
         '''for''' (i = 0..m)    	&lt;br /&gt;
            '''if''' table[x] == ''null''&lt;br /&gt;
               table[x] = item&lt;br /&gt;
            '''return'''      &lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      table.resize()&amp;lt;span style=&amp;quot;color:Green&amp;quot;&amp;gt;// ошибка, требуется увеличить размер таблицы&lt;br /&gt;
&lt;br /&gt;
'''Поиск'''&lt;br /&gt;
 '''Item''' search('''Item''' key):&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m)&lt;br /&gt;
         '''if''' table[x] != ''null''&lt;br /&gt;
            '''if''' table[x].key == key&lt;br /&gt;
               '''return''' table[x]&lt;br /&gt;
            '''else'''&lt;br /&gt;
               '''return''' ''null''&lt;br /&gt;
      x = (x + y) '''mod''' m   &lt;br /&gt;
      '''return''' ''null''&lt;br /&gt;
&lt;br /&gt;
===Реализация с удалением===&lt;br /&gt;
Что бы наша хеш-таблица поддерживала удаление, требуется добавить массив &amp;lt;tex&amp;gt;deleted&amp;lt;/tex&amp;gt; типов &amp;lt;tex&amp;gt;bool&amp;lt;/tex&amp;gt;, равный по величине массиву &amp;lt;tex&amp;gt;table&amp;lt;/tex&amp;gt;. Теперь при удалении мы просто будем помечать наш объект ''как удалённый'', а при добавлении как ''не удалённый'' и замещать новым добавляемым объектом. При поиске, помимо равенства ключей, мы смотрим, удалён ли элемент, если да, то идём дальше.&lt;br /&gt;
&lt;br /&gt;
'''Вставка'''&lt;br /&gt;
 '''function''' add('''Item''' item)&lt;br /&gt;
      x = h1(item.key)&lt;br /&gt;
      y = h2(item.key)&lt;br /&gt;
      '''for''' (i = 0..m)   	&lt;br /&gt;
         '''if''' table[x] == '''null''' '''or''' deleted[x]&lt;br /&gt;
            table[x] = item&lt;br /&gt;
            deleted[x] = '''false'''&lt;br /&gt;
            '''return'''      &lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      table.resize()&amp;lt;span style=&amp;quot;color:Green&amp;quot;&amp;gt;// ошибка, требуется увеличить размер таблицы&lt;br /&gt;
'''Поиск'''&lt;br /&gt;
 '''Item''' search('''Item''' key)&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m) &lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key '''and''' !deleted[x]&lt;br /&gt;
               '''return''' table[x]&lt;br /&gt;
         '''else'''&lt;br /&gt;
            '''return''' '''null'''&lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      '''return''' '''null'''&lt;br /&gt;
&lt;br /&gt;
'''Удаление'''&lt;br /&gt;
 '''function''' remove('''Item''' key)&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m)&lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key&lt;br /&gt;
               deleted[x] = '''true'''&lt;br /&gt;
         '''else''' &lt;br /&gt;
            '''return'''&lt;br /&gt;
      x = (x + y) '''mod''' m&lt;br /&gt;
&lt;br /&gt;
== Разрешение коллизий в Java 8==&lt;br /&gt;
В Java 8 для разрешения коллизий используется модифицированный метод цепочек. Суть его заключается в том, что когда количество элементов в корзине превышает определенное значение, данный корзина переходит от использования связного списка к использованию [[АВЛ-дерево|сбалансированного дерева]]. Но данный метод имеет смысл лишь тогда, когда на элементах хеш-таблицы задан линейный порядок. То есть при использовании данный типа &amp;lt;tex&amp;gt;int&amp;lt;/tex&amp;gt; или &amp;lt;tex&amp;gt;double&amp;lt;/tex&amp;gt; имеет смысл переходить к дереву поиска, а при использовании каких-нибудь ссылок на объекты не имеет, так как они не реализуют нужный интерфейс. Такой подход позволяет улучшить производительность с &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt;. Данный способ используется в таких коллекциях как HashMap, LinkedHashMap и ConcurrentHashMap.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Hashing_in_Java8.png|500px|Хеширование в Java 8.]]&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
* [[Хеширование]]&lt;br /&gt;
* [[Хеширование_кукушки|Хеширование кукушки]]&lt;br /&gt;
* [[Идеальное_хеширование|Идеальное хеширование]]&lt;br /&gt;
&lt;br /&gt;
== Источники информации ==&lt;br /&gt;
* Бакнелл Дж. М. «Фундаментальные алгоритмы и структуры данных в Delphi», 2003&lt;br /&gt;
* Кормен, Томас Х., Лейзерсон, Чарльз И., Ривест, Рональд Л., Штайн Клиффорд «Алгоритмы: построение и анализ», 2-е издание. Пер. с англ. — М.:Издательский дом &amp;quot;Вильямс&amp;quot;, 2010.— Парал. тит. англ. — ISBN 978-5-8459-0857-5 (рус.)&lt;br /&gt;
* Дональд Кнут. «Искусство программирования, том 3. Сортировка и поиск» {{---}} «Вильямс», 2007 г.{{---}} ISBN 0-201-89685-0&lt;br /&gt;
* Седжвик Р. «Фундаментальные алгоритмы на C. Части 1-4. Анализ. Структуры данных. Сортировка. Поиск», 2003&lt;br /&gt;
* [http://openjdk.java.net/jeps/180 Handle Frequent HashMap Collisions with Balanced Trees]&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Double_hashing Wikipedia {{---}} Double_hashing]&lt;br /&gt;
* [http://ru.wikipedia.org/wiki/%D0%A5%D0%B5%D1%88-%D1%82%D0%B0%D0%B1%D0%BB%D0%B8%D1%86%D0%B0 Разрешение коллизий]&lt;br /&gt;
* [http://rain.ifmo.ru/cat/view.php/vis/hashtables/hash-2001-2 Пример хеш таблицы]&lt;br /&gt;
* [http://research.cs.vt.edu/AVresearch/hashing/double.php Пример хеш таблицы с двойным хешированием]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория: Хеширование]]&lt;br /&gt;
[[Категория: Структуры данных]]&lt;/div&gt;</summary>
		<author><name>Kozichuk</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%A0%D0%B0%D0%B7%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BA%D0%BE%D0%BB%D0%BB%D0%B8%D0%B7%D0%B8%D0%B9&amp;diff=47797</id>
		<title>Разрешение коллизий</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A0%D0%B0%D0%B7%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BA%D0%BE%D0%BB%D0%BB%D0%B8%D0%B7%D0%B8%D0%B9&amp;diff=47797"/>
				<updated>2015-06-06T16:16:35Z</updated>
		
		<summary type="html">&lt;p&gt;Kozichuk: /* Удаление элемента без пометок */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Разрешение [[Хеш-таблица|коллизий]]''' (англ. collision resolution) в [[Хеш-таблица|хеш-таблице]], задача, решаемая несколькими способами: метод цепочек, открытая адресация и т.д. Очень важно сводить количество коллизий к минимуму, так как это увеличивает время работы с хеш-таблицами. &lt;br /&gt;
&lt;br /&gt;
== Разрешение коллизий с помощью цепочек ==&lt;br /&gt;
[[Файл:open_hash.png|thumb|380px|right|Разрешение коллизий при помощи цепочек.]]&lt;br /&gt;
Каждая ячейка &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; массива &amp;lt;tex&amp;gt;H&amp;lt;/tex&amp;gt; содержит указатель на начало [[Список|списка]] всех элементов, хеш-код которых равен &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, либо указывает на их отсутствие. Коллизии приводят к тому, что появляются списки размером больше одного элемента.&lt;br /&gt;
&lt;br /&gt;
В зависимости от того нужна ли нам уникальность значений операции вставки у нас будет работать за разное время. Если не важна, то мы используем список, время вставки в который будет в худшем случае равна &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;. Иначе мы проверяем есть ли в списке данный элемент, а потом в случае его отсутствия мы его добавляем. В таком случае вставка элемента в худшем случае будет выполнена за &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Время работы поиска в наихудшем случае пропорционально длине списка, а если все &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; ключей захешировались в одну и ту же ячейку (создав список длиной &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;) время поиска будет равно &amp;lt;tex&amp;gt;\Theta(n)&amp;lt;/tex&amp;gt; плюс время вычисления хеш-функции, что ничуть не лучше, чем использование связного списка для хранения всех &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; элементов.&lt;br /&gt;
&lt;br /&gt;
Удаления элемента может быть выполнено за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;, как и вставка, при использовании двухсвязного списка.&lt;br /&gt;
&lt;br /&gt;
== Линейное разрешение коллизий ==&lt;br /&gt;
[[Файл:close_hash.png|thumb|380px|right|Пример хеш-таблицы с открытой адресацией и линейным пробированием.]]&lt;br /&gt;
Все элементы хранятся непосредственно в хеш-таблице, без использования связных списков. В отличии от хеширования с цепочками, при использовании этого метода может возникнуть ситуация, когда хеш-таблица окажется полностью заполненной, следовательно будет невозможно добавлять в неё новые элементы. Так что при возникновении такой ситуации решением может быть динамическое увеличение размера хеш-таблицы, с одновременной её перестройкой.&lt;br /&gt;
&lt;br /&gt;
=== Стратегии поиска ===&lt;br /&gt;
&lt;br /&gt;
''' Последовательный поиск '''&lt;br /&gt;
&lt;br /&gt;
При попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt;i+1, i+2, i+3&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку. В неё и запишем элемент.&lt;br /&gt;
&lt;br /&gt;
[[Файл:hashtables1.png|400px|Последовательный поиск, частный случай линейного поиска.]]&lt;br /&gt;
&lt;br /&gt;
''' Линейный поиск '''&lt;br /&gt;
&lt;br /&gt;
Выбираем шаг &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;. При попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt;i+(1 \cdot q), i+(2 \cdot q), i+(3 \cdot q)&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку. В неё и запишем элемент.&lt;br /&gt;
По сути последовательный поиск - частный случай линейного, где &amp;lt;tex&amp;gt;q=1&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Hashtables56.PNG|400px|Линейный поиск с шагом q.]]&lt;br /&gt;
&lt;br /&gt;
''' Квадратичный поиск '''&lt;br /&gt;
&lt;br /&gt;
Шаг &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; не фиксирован, а изменяется квадратично: &amp;lt;tex&amp;gt;q = 1,4,9,16...&amp;lt;/tex&amp;gt;. Соответственно при попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt; i+1, i+4, i+9&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку.&lt;br /&gt;
&lt;br /&gt;
[[Файл:hashtables3.png|400px|Квадратичный поиск.]]&lt;br /&gt;
&lt;br /&gt;
=== Проверка наличия элемента в таблице===&lt;br /&gt;
&lt;br /&gt;
Проверка осуществляется аналогично добавлению: мы проверяем ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; и другие, в соответствии с выбранной стратегией, пока не найдём искомый элемент или свободную ячейку.&lt;br /&gt;
&lt;br /&gt;
При поиске элемента может получится так, что мы дойдём до конца таблицы. Обычно поиск продолжается, начиная с другого конца, пока мы не придём в ту ячейку, откуда начинался поиск.&lt;br /&gt;
&lt;br /&gt;
=== Проблемы данных стратегий ===&lt;br /&gt;
&lt;br /&gt;
Проблем две — крайне нетривиальное удаление элемента из таблицы и образование кластеров  — последовательностей занятых ячеек.&lt;br /&gt;
&lt;br /&gt;
Кластеризация замедляет все операции с хеш-таблицей: при добавлении требуется перебирать всё больше элементов, при проверке тоже. Чем больше в таблице элементов, тем больше в ней кластеры и тем выше вероятность того, что добавляемый элемент попадёт в кластер.&lt;br /&gt;
Для защиты от кластеризации используется двойное хеширование и [[Хеширование кукушки|хеширование кукушки]].&lt;br /&gt;
&lt;br /&gt;
=== Удаление элемента без пометок ===&lt;br /&gt;
&lt;br /&gt;
Рассуждение будет описывать случай с линейным поиском хеша. Будем при удалении элемента сдвигать всё последующие на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; позиций назад. При этом:&lt;br /&gt;
* если в цепочке встречается элемент с другим хешем, то он должен остаться на своём месте (такая ситуация может возникнуть если оставшаяся часть цепочки была добавлена позже этого элемента)&lt;br /&gt;
* в цепочке не должно оставаться &amp;quot;дырок&amp;quot;, тогда любой элемент с данным хешем будет доступен из начала цепи&lt;br /&gt;
&lt;br /&gt;
Учитывая это будем действовать следующим образом: при поиске следующего элемента цепочки будем пропускать все ячейки с другим значением хеша, первый найденный элемент копировать в текущую ячейку, и затем рекурсивно его удалять. Если такой следующей ячейки нет, то текущий элемент можно просто удалить, сторонние цепочки при этом не разрушатся (чего нельзя сказать про случай квадратичного поиска).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
''' Псевдокод '''&lt;br /&gt;
&lt;br /&gt;
 '''function''' delete('''Item''' i):&lt;br /&gt;
      j = i + q&lt;br /&gt;
      '''while''' table[j] == ''null'' '''or''' table[j].key != table[i].key&lt;br /&gt;
         '''if''' table[j] == ''null''&lt;br /&gt;
            table[i] = ''null''&lt;br /&gt;
            '''return'''&lt;br /&gt;
         j += q&lt;br /&gt;
      table[i] = table[j]&lt;br /&gt;
      delete(j)    &lt;br /&gt;
&lt;br /&gt;
Хеш-таблицу считаем зацикленной&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Утверждение&lt;br /&gt;
|about=о времени работы&lt;br /&gt;
|statement=Асимптотически время работы &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\mathrm{find}&amp;lt;/tex&amp;gt; совпадают&lt;br /&gt;
|proof=&lt;br /&gt;
Заметим что указатель &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; в каждой итерации перемещается вперёд на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; (с учётом рекурсивных вызовов &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt;). То есть этот алгоритм последовательно пройдёт по цепочке от удаляемого элемента до последнего {{---}} с учётом вызова &amp;lt;tex&amp;gt;\mathrm{find}&amp;lt;/tex&amp;gt; собственно для нахождения удаляемого элемента, мы посетим все ячейки цепи.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Вариант с зацикливанием мы не рассматриваем, поскольку если &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; взаимнопросто с размером хеш-таблицы, то для зацикливания в ней вообще не должно быть свободных позиций&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Теперь докажем почему этот алгоритм работает. Собственно нам требуется сохранение трёх условий.&lt;br /&gt;
* В редактируемой цепи не остаётся дырок&lt;br /&gt;
Докажем по индукции. Если на данной итерации мы просто удаляем элемент (база), то после него ничего нет, всё верно. Если же нет, то вызванный в конце &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt; (см. псевдокод) заметёт созданную дыру (скопированный элемент), и сам, по предположению, новых не создаст.&lt;br /&gt;
* Элементы, которые уже на своих местах, не должны быть сдвинуты.&lt;br /&gt;
Это учтено.&lt;br /&gt;
* В других цепочках не появятся дыры&lt;br /&gt;
Противное возможно только в том случае, если какой-то элемент был действительно удалён. Удаляем мы только последнюю ячейку в цепи, и если бы на её месте возникла дыра для сторонней цепочки, это бы означало что элемент, стоящий на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; позиций назад, одновременно принадлежал нашей и другой цепочкам, что невозможно.&lt;br /&gt;
&lt;br /&gt;
==Двойное хеширование==&lt;br /&gt;
'''Двойное хеширование''' (англ. double hashing) {{---}} метод борьбы с коллизиями, возникающими при открытой адресации, основанный на использовании двух хеш-функций для построения различных последовательностей исследования хеш-таблицы.&lt;br /&gt;
&lt;br /&gt;
===Принцип двойного хеширования===&lt;br /&gt;
При двойном хешировании используются две независимые хеш-функции &amp;lt;tex&amp;gt; h_1(k) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; h_2(k) &amp;lt;/tex&amp;gt;. Пусть &amp;lt;tex&amp;gt; k &amp;lt;/tex&amp;gt; {{---}} это наш ключ, &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt; {{---}} размер нашей таблицы, &amp;lt;tex&amp;gt;n \bmod m &amp;lt;/tex&amp;gt; {{---}} остаток от деления &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt; на &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, тогда сначала исследуется ячейка с адресом &amp;lt;tex&amp;gt; h_1(k) &amp;lt;/tex&amp;gt;, если она уже занята, то рассматривается &amp;lt;tex&amp;gt; (h_1(k) +  h_2(k)) \bmod m &amp;lt;/tex&amp;gt;, затем &amp;lt;tex&amp;gt; (h_1(k) +  2 \cdot h_2(k)) \bmod m &amp;lt;/tex&amp;gt; и так далее. В общем случае идёт проверка последовательности ячеек &amp;lt;tex&amp;gt; (h_1(k) +  i \cdot h_2(k)) \bmod m &amp;lt;/tex&amp;gt; где &amp;lt;tex&amp;gt;  i = (0, 1, \; ... \;,  m - 1) &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, операции вставки, удаления и поиска в лучшем случае выполняются за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;, в худшем {{---}} за &amp;lt;tex&amp;gt;O(m)&amp;lt;/tex&amp;gt;, что не отличается от обычного [[Открытое_и_закрытое_хеширование#Линейное разрешение коллизий|линейного разрешения коллизий]].&lt;br /&gt;
Однако в среднем, при грамотном выборе хеш-функций, двойное хеширование будет выдавать лучшие результаты, за счёт того, что вероятность совпадения значений сразу двух независимых хеш-функций ниже, чем одной.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt;\forall x \neq y \; \exists h_1,h_2 : p(h_1(x)=h_1(y))&amp;gt; p((h_1(x)=h_1(y)) \land (h_2(x)=h_2(y)))&amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Выбор хеш-функций===&lt;br /&gt;
&amp;lt;tex&amp;gt; h_1 &amp;lt;/tex&amp;gt; может быть обычной хеш-функцией. Однако чтобы последовательность исследования могла охватить всю таблицу, &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; должна возвращать значения:&lt;br /&gt;
*не равные &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
*независимые  от &amp;lt;tex&amp;gt; h_1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
*взаимно простые с величиной хеш-таблицы&lt;br /&gt;
&lt;br /&gt;
Есть два удобных способа это сделать. Первый состоит в том, что в качестве размера таблицы используется простое число, а &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; возвращает натуральные числа, меньшие &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;. Второй {{---}} размер таблицы является степенью двойки, а &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; возвращает нечетные значения.&lt;br /&gt;
&lt;br /&gt;
Например, если размер таблицы равен &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, то в качестве &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; можно использовать функцию вида &amp;lt;tex&amp;gt; h_2(k) = k \bmod (m-1) + 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Файл: Вставка при двойном хэшировании.svg.jpeg|thumb|right|Вставка при двойном хешировании]]&lt;br /&gt;
&lt;br /&gt;
===Пример===&lt;br /&gt;
&lt;br /&gt;
Показана хеш-таблица размером 13 ячеек, в которой используются вспомогательные функции:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h(k,i) = (h_1(k) + i \cdot h_2(k)) \bmod 13 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h_1(k) = k \bmod 13 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h_2(k) = 1 + k \bmod 11 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Мы хотим вставить ключ 14. Изначально &amp;lt;tex&amp;gt; i = 0 &amp;lt;/tex&amp;gt;. Тогда &amp;lt;tex&amp;gt; h(14,0) = (h_1(14) + 0\cdot h_2(14)) \bmod 13 = 1 &amp;lt;/tex&amp;gt;. Но ячейка с индексом 1 занята, поэтому увеличиваем &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; на 1 и пересчитываем значение хеш-функции. Делаем так, пока не дойдем до пустой ячейки. При &amp;lt;tex&amp;gt; i = 2 &amp;lt;/tex&amp;gt; получаем &amp;lt;tex&amp;gt; h(14,2) = (h_1(14) + 2\cdot h_2(14)) \bmod 13 = 9 &amp;lt;/tex&amp;gt;. Ячейка с номером 9 свободна, значит записываем туда наш ключ.&lt;br /&gt;
&lt;br /&gt;
Таким образом, основная особенность двойного хеширования состоит в том, что при различных &amp;lt;tex&amp;gt; k &amp;lt;/tex&amp;gt; пара &amp;lt;tex&amp;gt; (h_1(k),h_2(k)) &amp;lt;/tex&amp;gt; дает различные последовательности ячеек для исследования.&lt;br /&gt;
&lt;br /&gt;
===Простая реализация===&lt;br /&gt;
Пусть у нас есть некоторый объект &amp;lt;tex&amp;gt; item &amp;lt;/tex&amp;gt;, в котором определено поле &amp;lt;tex&amp;gt; key &amp;lt;/tex&amp;gt;, от которого можно вычислить хеш-функции &amp;lt;tex&amp;gt; h_1(key)&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; h_2(key) &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Так же у нас есть таблица &amp;lt;tex&amp;gt; table &amp;lt;/tex&amp;gt; величиной &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, состоящая из объектов типа &amp;lt;tex&amp;gt; item &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
'''Вставка'''&lt;br /&gt;
 '''function''' add('''Item''' item)&lt;br /&gt;
      x = h1(item.key)&lt;br /&gt;
      y = h2(item.key)&lt;br /&gt;
         '''for''' (i = 0..m)    	&lt;br /&gt;
            '''if''' table[x] == ''null''&lt;br /&gt;
               table[x] = item&lt;br /&gt;
            '''return'''      &lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      table.resize()&amp;lt;span style=&amp;quot;color:Green&amp;quot;&amp;gt;// ошибка, требуется увеличить размер таблицы&lt;br /&gt;
&lt;br /&gt;
'''Поиск'''&lt;br /&gt;
 '''Item''' search('''Item''' key)&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m)&lt;br /&gt;
         '''if''' table[x] != ''null''&lt;br /&gt;
            '''if''' table[x].key == key&lt;br /&gt;
               '''return''' table[x]&lt;br /&gt;
            '''else'''&lt;br /&gt;
               '''return''' ''null''&lt;br /&gt;
      x = (x + y) '''mod''' m   &lt;br /&gt;
      '''return''' ''null''&lt;br /&gt;
&lt;br /&gt;
===Реализация с удалением===&lt;br /&gt;
Что бы наша хеш-таблица поддерживала удаление, требуется добавить массив &amp;lt;tex&amp;gt;deleted&amp;lt;/tex&amp;gt; типов &amp;lt;tex&amp;gt;bool&amp;lt;/tex&amp;gt;, равный по величине массиву &amp;lt;tex&amp;gt;table&amp;lt;/tex&amp;gt;. Теперь при удалении мы просто будем помечать наш объект ''как удалённый'', а при добавлении как ''не удалённый'' и замещать новым добавляемым объектом. При поиске, помимо равенства ключей, мы смотрим, удалён ли элемент, если да, то идём дальше.&lt;br /&gt;
&lt;br /&gt;
'''Вставка'''&lt;br /&gt;
 '''function''' add('''Item''' item)&lt;br /&gt;
      x = h1(item.key)&lt;br /&gt;
      y = h2(item.key)&lt;br /&gt;
      '''for''' (i = 0..m)   	&lt;br /&gt;
         '''if''' table[x] == '''null''' '''or''' deleted[x]&lt;br /&gt;
            table[x] = item&lt;br /&gt;
            deleted[x] = '''false'''&lt;br /&gt;
            '''return'''      &lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      table.resize()&amp;lt;span style=&amp;quot;color:Green&amp;quot;&amp;gt;// ошибка, требуется увеличить размер таблицы&lt;br /&gt;
'''Поиск'''&lt;br /&gt;
 '''Item''' search('''Item''' key)&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m) &lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key '''and''' !deleted[x]&lt;br /&gt;
               '''return''' table[x]&lt;br /&gt;
         '''else'''&lt;br /&gt;
            '''return''' '''null'''&lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      '''return''' '''null'''&lt;br /&gt;
&lt;br /&gt;
'''Удаление'''&lt;br /&gt;
 '''function''' remove('''Item''' key)&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m)&lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key&lt;br /&gt;
               deleted[x] = '''true'''&lt;br /&gt;
         '''else''' &lt;br /&gt;
            '''return'''&lt;br /&gt;
      x = (x + y) '''mod''' m&lt;br /&gt;
&lt;br /&gt;
== Разрешение коллизий в Java 8==&lt;br /&gt;
В Java 8 для разрешения коллизий используется модифицированный метод цепочек. Суть его заключается в том, что когда количество элементов в корзине превышает определенное значение, данный корзина переходит от использования связного списка к использованию [[АВЛ-дерево|сбалансированного дерева]]. Но данный метод имеет смысл лишь тогда, когда на элементах хеш-таблицы задан линейный порядок. То есть при использовании данный типа &amp;lt;tex&amp;gt;int&amp;lt;/tex&amp;gt; или &amp;lt;tex&amp;gt;double&amp;lt;/tex&amp;gt; имеет смысл переходить к дереву поиска, а при использовании каких-нибудь ссылок на объекты не имеет, так как они не реализуют нужный интерфейс. Такой подход позволяет улучшить производительность с &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt;. Данный способ используется в таких коллекциях как HashMap, LinkedHashMap и ConcurrentHashMap.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Hashing_in_Java8.png|500px|Хеширование в Java 8.]]&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
* [[Хеширование]]&lt;br /&gt;
* [[Хеширование_кукушки|Хеширование кукушки]]&lt;br /&gt;
* [[Идеальное_хеширование|Идеальное хеширование]]&lt;br /&gt;
&lt;br /&gt;
== Источники информации ==&lt;br /&gt;
* Бакнелл Дж. М. «Фундаментальные алгоритмы и структуры данных в Delphi», 2003&lt;br /&gt;
* Кормен, Томас Х., Лейзерсон, Чарльз И., Ривест, Рональд Л., Штайн Клиффорд «Алгоритмы: построение и анализ», 2-е издание. Пер. с англ. — М.:Издательский дом &amp;quot;Вильямс&amp;quot;, 2010.— Парал. тит. англ. — ISBN 978-5-8459-0857-5 (рус.)&lt;br /&gt;
* Дональд Кнут. «Искусство программирования, том 3. Сортировка и поиск» {{---}} «Вильямс», 2007 г.{{---}} ISBN 0-201-89685-0&lt;br /&gt;
* Седжвик Р. «Фундаментальные алгоритмы на C. Части 1-4. Анализ. Структуры данных. Сортировка. Поиск», 2003&lt;br /&gt;
* [http://openjdk.java.net/jeps/180 Handle Frequent HashMap Collisions with Balanced Trees]&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Double_hashing Wikipedia {{---}} Double_hashing]&lt;br /&gt;
* [http://ru.wikipedia.org/wiki/%D0%A5%D0%B5%D1%88-%D1%82%D0%B0%D0%B1%D0%BB%D0%B8%D1%86%D0%B0 Разрешение коллизий]&lt;br /&gt;
* [http://rain.ifmo.ru/cat/view.php/vis/hashtables/hash-2001-2 Пример хеш таблицы]&lt;br /&gt;
* [http://research.cs.vt.edu/AVresearch/hashing/double.php Пример хеш таблицы с двойным хешированием]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория: Хеширование]]&lt;br /&gt;
[[Категория: Структуры данных]]&lt;/div&gt;</summary>
		<author><name>Kozichuk</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%A0%D0%B0%D0%B7%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BA%D0%BE%D0%BB%D0%BB%D0%B8%D0%B7%D0%B8%D0%B9&amp;diff=47794</id>
		<title>Разрешение коллизий</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A0%D0%B0%D0%B7%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BA%D0%BE%D0%BB%D0%BB%D0%B8%D0%B7%D0%B8%D0%B9&amp;diff=47794"/>
				<updated>2015-06-06T16:03:43Z</updated>
		
		<summary type="html">&lt;p&gt;Kozichuk: /* Разрешение коллизий с помощью цепочек */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Разрешение [[Хеш-таблица|коллизий]]''' (англ. collision resolution) в [[Хеш-таблица|хеш-таблице]], задача, решаемая несколькими способами: метод цепочек, открытая адресация и т.д. Очень важно сводить количество коллизий к минимуму, так как это увеличивает время работы с хеш-таблицами. &lt;br /&gt;
&lt;br /&gt;
== Разрешение коллизий с помощью цепочек ==&lt;br /&gt;
[[Файл:open_hash.png|thumb|380px|right|Разрешение коллизий при помощи цепочек.]]&lt;br /&gt;
Каждая ячейка &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; массива &amp;lt;tex&amp;gt;H&amp;lt;/tex&amp;gt; содержит указатель на начало [[Список|списка]] всех элементов, хеш-код которых равен &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, либо указывает на их отсутствие. Коллизии приводят к тому, что появляются списки размером больше одного элемента.&lt;br /&gt;
&lt;br /&gt;
В зависимости от того нужна ли нам уникальность значений операции вставки у нас будет работать за разное время. Если не важна, то мы используем список, время вставки в который будет в худшем случае равна &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;. Иначе мы проверяем есть ли в списке данный элемент, а потом в случае его отсутствия мы его добавляем. В таком случае вставка элемента в худшем случае будет выполнена за &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Время работы поиска в наихудшем случае пропорционально длине списка, а если все &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; ключей захешировались в одну и ту же ячейку (создав список длиной &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;) время поиска будет равно &amp;lt;tex&amp;gt;\Theta(n)&amp;lt;/tex&amp;gt; плюс время вычисления хеш-функции, что ничуть не лучше, чем использование связного списка для хранения всех &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; элементов.&lt;br /&gt;
&lt;br /&gt;
Удаления элемента может быть выполнено за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;, как и вставка, при использовании двухсвязного списка.&lt;br /&gt;
&lt;br /&gt;
== Линейное разрешение коллизий ==&lt;br /&gt;
[[Файл:close_hash.png|thumb|380px|right|Пример хеш-таблицы с открытой адресацией и линейным пробированием.]]&lt;br /&gt;
Все элементы хранятся непосредственно в хеш-таблице, без использования связных списков. В отличии от хеширования с цепочками, при использовании этого метода может возникнуть ситуация, когда хеш-таблица окажется полностью заполненной, следовательно будет невозможно добавлять в неё новые элементы. Так что при возникновении такой ситуации решением может быть динамическое увеличение размера хеш-таблицы, с одновременной её перестройкой.&lt;br /&gt;
&lt;br /&gt;
=== Стратегии поиска ===&lt;br /&gt;
&lt;br /&gt;
''' Последовательный поиск '''&lt;br /&gt;
&lt;br /&gt;
При попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt;i+1, i+2, i+3&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку. В неё и запишем элемент.&lt;br /&gt;
&lt;br /&gt;
[[Файл:hashtables1.png|400px|Последовательный поиск, частный случай линейного поиска.]]&lt;br /&gt;
&lt;br /&gt;
''' Линейный поиск '''&lt;br /&gt;
&lt;br /&gt;
Выбираем шаг &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;. При попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt;i+(1 \cdot q), i+(2 \cdot q), i+(3 \cdot q)&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку. В неё и запишем элемент.&lt;br /&gt;
По сути последовательный поиск - частный случай линейного, где &amp;lt;tex&amp;gt;q=1&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Hashtables56.PNG|400px|Линейный поиск с шагом q.]]&lt;br /&gt;
&lt;br /&gt;
''' Квадратичный поиск '''&lt;br /&gt;
&lt;br /&gt;
Шаг &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; не фиксирован, а изменяется квадратично: &amp;lt;tex&amp;gt;q = 1,4,9,16...&amp;lt;/tex&amp;gt;. Соответственно при попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt; i+1, i+4, i+9&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку.&lt;br /&gt;
&lt;br /&gt;
[[Файл:hashtables3.png|400px|Квадратичный поиск.]]&lt;br /&gt;
&lt;br /&gt;
=== Проверка наличия элемента в таблице===&lt;br /&gt;
&lt;br /&gt;
Проверка осуществляется аналогично добавлению: мы проверяем ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; и другие, в соответствии с выбранной стратегией, пока не найдём искомый элемент или свободную ячейку.&lt;br /&gt;
&lt;br /&gt;
При поиске элемента может получится так, что мы дойдём до конца таблицы. Обычно поиск продолжается, начиная с другого конца, пока мы не придём в ту ячейку, откуда начинался поиск.&lt;br /&gt;
&lt;br /&gt;
=== Проблемы данных стратегий ===&lt;br /&gt;
&lt;br /&gt;
Проблем две — крайне нетривиальное удаление элемента из таблицы и образование кластеров  — последовательностей занятых ячеек.&lt;br /&gt;
&lt;br /&gt;
Кластеризация замедляет все операции с хеш-таблицей: при добавлении требуется перебирать всё больше элементов, при проверке тоже. Чем больше в таблице элементов, тем больше в ней кластеры и тем выше вероятность того, что добавляемый элемент попадёт в кластер.&lt;br /&gt;
Для защиты от кластеризации используется двойное хеширование и [[Хеширование кукушки|хеширование кукушки]].&lt;br /&gt;
&lt;br /&gt;
=== Удаление элемента без пометок ===&lt;br /&gt;
&lt;br /&gt;
Рассуждение будет описывать случай с линейным поиском хеша. Будем при удалении элемента сдвигать всё последующие на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; позиций назад. При этом:&lt;br /&gt;
* если в цепочке встречается элемент с другим хешем, то он должен остаться на своём месте (такая ситуация может возникнуть если оставшаяся часть цепочки была добавлена позже этого элемента)&lt;br /&gt;
* в цепочке не должно оставаться &amp;quot;дырок&amp;quot;, тогда любой элемент с данным хешем будет доступен из начала цепи&lt;br /&gt;
&lt;br /&gt;
Учитывая это будем действовать следующим образом: при поиске следующего элемента цепочки будем пропускать все ячейки с другим значением хеша, первый найденный элемент копировать в текущую ячейку, и затем рекурсивно его удалять. Если такой следующей ячейки нет, то текущий элемент можно просто удалить, сторонние цепочки при этом не разрушатся (чего нельзя сказать про случай квадратичного поиска).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
''' Псевдокод '''&lt;br /&gt;
&lt;br /&gt;
 '''function''' delete('''Item''' i) &lt;br /&gt;
      j = i + q&lt;br /&gt;
      '''while''' table[j] == ''null'' '''or''' table[j].key != table[i].key&lt;br /&gt;
         '''if''' table[j] == ''null''&lt;br /&gt;
            table[i] = ''null''&lt;br /&gt;
            '''return'''&lt;br /&gt;
         j += q&lt;br /&gt;
      table[i] = table[j]&lt;br /&gt;
      delete(j)    &lt;br /&gt;
&lt;br /&gt;
Хеш-таблицу считаем зацикленной&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Утверждение&lt;br /&gt;
|about=о времени работы&lt;br /&gt;
|statement=Асимптотически время работы &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\mathrm{find}&amp;lt;/tex&amp;gt; совпадают&lt;br /&gt;
|proof=&lt;br /&gt;
Заметим что указатель &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; в каждой итерации перемещается вперёд на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; (с учётом рекурсивных вызовов &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt;). То есть этот алгоритм последовательно пройдёт по цепочке от удаляемого элемента до последнего {{---}} с учётом вызова &amp;lt;tex&amp;gt;\mathrm{find}&amp;lt;/tex&amp;gt; собственно для нахождения удаляемого элемента, мы посетим все ячейки цепи.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Вариант с зацикливанием мы не рассматриваем, поскольку если &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; взаимнопросто с размером хеш-таблицы, то для зацикливания в ней вообще не должно быть свободных позиций&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Теперь докажем почему этот алгоритм работает. Собственно нам требуется сохранение трёх условий.&lt;br /&gt;
* В редактируемой цепи не остаётся дырок&lt;br /&gt;
Докажем по индукции. Если на данной итерации мы просто удаляем элемент (база), то после него ничего нет, всё верно. Если же нет, то вызванный в конце &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt; (см. псевдокод) заметёт созданную дыру (скопированный элемент), и сам, по предположению, новых не создаст.&lt;br /&gt;
* Элементы, которые уже на своих местах, не должны быть сдвинуты.&lt;br /&gt;
Это учтено.&lt;br /&gt;
* В других цепочках не появятся дыры&lt;br /&gt;
Противное возможно только в том случае, если какой-то элемент был действительно удалён. Удаляем мы только последнюю ячейку в цепи, и если бы на её месте возникла дыра для сторонней цепочки, это бы означало что элемент, стоящий на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; позиций назад, одновременно принадлежал нашей и другой цепочкам, что невозможно.&lt;br /&gt;
&lt;br /&gt;
==Двойное хеширование==&lt;br /&gt;
'''Двойное хеширование''' (англ. double hashing) {{---}} метод борьбы с коллизиями, возникающими при открытой адресации, основанный на использовании двух хеш-функций для построения различных последовательностей исследования хеш-таблицы.&lt;br /&gt;
&lt;br /&gt;
===Принцип двойного хеширования===&lt;br /&gt;
При двойном хешировании используются две независимые хеш-функции &amp;lt;tex&amp;gt; h_1(k) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; h_2(k) &amp;lt;/tex&amp;gt;. Пусть &amp;lt;tex&amp;gt; k &amp;lt;/tex&amp;gt; {{---}} это наш ключ, &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt; {{---}} размер нашей таблицы, &amp;lt;tex&amp;gt;n \bmod m &amp;lt;/tex&amp;gt; {{---}} остаток от деления &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt; на &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, тогда сначала исследуется ячейка с адресом &amp;lt;tex&amp;gt; h_1(k) &amp;lt;/tex&amp;gt;, если она уже занята, то рассматривается &amp;lt;tex&amp;gt; (h_1(k) +  h_2(k)) \bmod m &amp;lt;/tex&amp;gt;, затем &amp;lt;tex&amp;gt; (h_1(k) +  2 \cdot h_2(k)) \bmod m &amp;lt;/tex&amp;gt; и так далее. В общем случае идёт проверка последовательности ячеек &amp;lt;tex&amp;gt; (h_1(k) +  i \cdot h_2(k)) \bmod m &amp;lt;/tex&amp;gt; где &amp;lt;tex&amp;gt;  i = (0, 1, \; ... \;,  m - 1) &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, операции вставки, удаления и поиска в лучшем случае выполняются за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;, в худшем {{---}} за &amp;lt;tex&amp;gt;O(m)&amp;lt;/tex&amp;gt;, что не отличается от обычного [[Открытое_и_закрытое_хеширование#Линейное разрешение коллизий|линейного разрешения коллизий]].&lt;br /&gt;
Однако в среднем, при грамотном выборе хеш-функций, двойное хеширование будет выдавать лучшие результаты, за счёт того, что вероятность совпадения значений сразу двух независимых хеш-функций ниже, чем одной.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt;\forall x \neq y \; \exists h_1,h_2 : p(h_1(x)=h_1(y))&amp;gt; p((h_1(x)=h_1(y)) \land (h_2(x)=h_2(y)))&amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Выбор хеш-функций===&lt;br /&gt;
&amp;lt;tex&amp;gt; h_1 &amp;lt;/tex&amp;gt; может быть обычной хеш-функцией. Однако чтобы последовательность исследования могла охватить всю таблицу, &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; должна возвращать значения:&lt;br /&gt;
*не равные &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
*независимые  от &amp;lt;tex&amp;gt; h_1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
*взаимно простые с величиной хеш-таблицы&lt;br /&gt;
&lt;br /&gt;
Есть два удобных способа это сделать. Первый состоит в том, что в качестве размера таблицы используется простое число, а &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; возвращает натуральные числа, меньшие &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;. Второй {{---}} размер таблицы является степенью двойки, а &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; возвращает нечетные значения.&lt;br /&gt;
&lt;br /&gt;
Например, если размер таблицы равен &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, то в качестве &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; можно использовать функцию вида &amp;lt;tex&amp;gt; h_2(k) = k \bmod (m-1) + 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Файл: Вставка при двойном хэшировании.svg.jpeg|thumb|right|Вставка при двойном хешировании]]&lt;br /&gt;
&lt;br /&gt;
===Пример===&lt;br /&gt;
&lt;br /&gt;
Показана хеш-таблица размером 13 ячеек, в которой используются вспомогательные функции:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h(k,i) = (h_1(k) + i \cdot h_2(k)) \bmod 13 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h_1(k) = k \bmod 13 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h_2(k) = 1 + k \bmod 11 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Мы хотим вставить ключ 14. Изначально &amp;lt;tex&amp;gt; i = 0 &amp;lt;/tex&amp;gt;. Тогда &amp;lt;tex&amp;gt; h(14,0) = (h_1(14) + 0\cdot h_2(14)) \bmod 13 = 1 &amp;lt;/tex&amp;gt;. Но ячейка с индексом 1 занята, поэтому увеличиваем &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; на 1 и пересчитываем значение хеш-функции. Делаем так, пока не дойдем до пустой ячейки. При &amp;lt;tex&amp;gt; i = 2 &amp;lt;/tex&amp;gt; получаем &amp;lt;tex&amp;gt; h(14,2) = (h_1(14) + 2\cdot h_2(14)) \bmod 13 = 9 &amp;lt;/tex&amp;gt;. Ячейка с номером 9 свободна, значит записываем туда наш ключ.&lt;br /&gt;
&lt;br /&gt;
Таким образом, основная особенность двойного хеширования состоит в том, что при различных &amp;lt;tex&amp;gt; k &amp;lt;/tex&amp;gt; пара &amp;lt;tex&amp;gt; (h_1(k),h_2(k)) &amp;lt;/tex&amp;gt; дает различные последовательности ячеек для исследования.&lt;br /&gt;
&lt;br /&gt;
===Простая реализация===&lt;br /&gt;
Пусть у нас есть некоторый объект &amp;lt;tex&amp;gt; item &amp;lt;/tex&amp;gt;, в котором определено поле &amp;lt;tex&amp;gt; key &amp;lt;/tex&amp;gt;, от которого можно вычислить хеш-функции &amp;lt;tex&amp;gt; h_1(key)&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; h_2(key) &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Так же у нас есть таблица &amp;lt;tex&amp;gt; table &amp;lt;/tex&amp;gt; величиной &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, состоящая из объектов типа &amp;lt;tex&amp;gt; item &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
'''Вставка'''&lt;br /&gt;
 '''function''' add('''Item''' item)&lt;br /&gt;
      x = h1(item.key)&lt;br /&gt;
      y = h2(item.key)&lt;br /&gt;
         '''for''' (i = 0..m)    	&lt;br /&gt;
            '''if''' table[x] == ''null''&lt;br /&gt;
               table[x] = item&lt;br /&gt;
            '''return'''      &lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      table.resize()&amp;lt;span style=&amp;quot;color:Green&amp;quot;&amp;gt;// ошибка, требуется увеличить размер таблицы&lt;br /&gt;
&lt;br /&gt;
'''Поиск'''&lt;br /&gt;
 '''Item''' search('''Item''' key)&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m)&lt;br /&gt;
         '''if''' table[x] != ''null''&lt;br /&gt;
            '''if''' table[x].key == key&lt;br /&gt;
               '''return''' table[x]&lt;br /&gt;
            '''else'''&lt;br /&gt;
               '''return''' ''null''&lt;br /&gt;
      x = (x + y) '''mod''' m   &lt;br /&gt;
      '''return''' ''null''&lt;br /&gt;
&lt;br /&gt;
===Реализация с удалением===&lt;br /&gt;
Что бы наша хеш-таблица поддерживала удаление, требуется добавить массив &amp;lt;tex&amp;gt;deleted&amp;lt;/tex&amp;gt; типов &amp;lt;tex&amp;gt;bool&amp;lt;/tex&amp;gt;, равный по величине массиву &amp;lt;tex&amp;gt;table&amp;lt;/tex&amp;gt;. Теперь при удалении мы просто будем помечать наш объект ''как удалённый'', а при добавлении как ''не удалённый'' и замещать новым добавляемым объектом. При поиске, помимо равенства ключей, мы смотрим, удалён ли элемент, если да, то идём дальше.&lt;br /&gt;
&lt;br /&gt;
'''Вставка'''&lt;br /&gt;
 '''function''' add('''Item''' item)&lt;br /&gt;
      x = h1(item.key)&lt;br /&gt;
      y = h2(item.key)&lt;br /&gt;
      '''for''' (i = 0..m)   	&lt;br /&gt;
         '''if''' table[x] == '''null''' '''or''' deleted[x]&lt;br /&gt;
            table[x] = item&lt;br /&gt;
            deleted[x] = '''false'''&lt;br /&gt;
            '''return'''      &lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      table.resize()&amp;lt;span style=&amp;quot;color:Green&amp;quot;&amp;gt;// ошибка, требуется увеличить размер таблицы&lt;br /&gt;
'''Поиск'''&lt;br /&gt;
 '''Item''' search('''Item''' key)&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m) &lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key '''and''' !deleted[x]&lt;br /&gt;
               '''return''' table[x]&lt;br /&gt;
         '''else'''&lt;br /&gt;
            '''return''' '''null'''&lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      '''return''' '''null'''&lt;br /&gt;
&lt;br /&gt;
'''Удаление'''&lt;br /&gt;
 '''function''' remove('''Item''' key)&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m)&lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key&lt;br /&gt;
               deleted[x] = '''true'''&lt;br /&gt;
         '''else''' &lt;br /&gt;
            '''return'''&lt;br /&gt;
      x = (x + y) '''mod''' m&lt;br /&gt;
&lt;br /&gt;
== Разрешение коллизий в Java 8==&lt;br /&gt;
В Java 8 для разрешения коллизий используется модифицированный метод цепочек. Суть его заключается в том, что когда количество элементов в корзине превышает определенное значение, данный корзина переходит от использования связного списка к использованию [[АВЛ-дерево|сбалансированного дерева]]. Но данный метод имеет смысл лишь тогда, когда на элементах хеш-таблицы задан линейный порядок. То есть при использовании данный типа &amp;lt;tex&amp;gt;int&amp;lt;/tex&amp;gt; или &amp;lt;tex&amp;gt;double&amp;lt;/tex&amp;gt; имеет смысл переходить к дереву поиска, а при использовании каких-нибудь ссылок на объекты не имеет, так как они не реализуют нужный интерфейс. Такой подход позволяет улучшить производительность с &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt;. Данный способ используется в таких коллекциях как HashMap, LinkedHashMap и ConcurrentHashMap.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Hashing_in_Java8.png|500px|Хеширование в Java 8.]]&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
* [[Хеширование]]&lt;br /&gt;
* [[Хеширование_кукушки|Хеширование кукушки]]&lt;br /&gt;
* [[Идеальное_хеширование|Идеальное хеширование]]&lt;br /&gt;
&lt;br /&gt;
== Источники информации ==&lt;br /&gt;
* Бакнелл Дж. М. «Фундаментальные алгоритмы и структуры данных в Delphi», 2003&lt;br /&gt;
* Кормен, Томас Х., Лейзерсон, Чарльз И., Ривест, Рональд Л., Штайн Клиффорд «Алгоритмы: построение и анализ», 2-е издание. Пер. с англ. — М.:Издательский дом &amp;quot;Вильямс&amp;quot;, 2010.— Парал. тит. англ. — ISBN 978-5-8459-0857-5 (рус.)&lt;br /&gt;
* Дональд Кнут. «Искусство программирования, том 3. Сортировка и поиск» {{---}} «Вильямс», 2007 г.{{---}} ISBN 0-201-89685-0&lt;br /&gt;
* Седжвик Р. «Фундаментальные алгоритмы на C. Части 1-4. Анализ. Структуры данных. Сортировка. Поиск», 2003&lt;br /&gt;
* [http://openjdk.java.net/jeps/180 Handle Frequent HashMap Collisions with Balanced Trees]&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Double_hashing Wikipedia {{---}} Double_hashing]&lt;br /&gt;
* [http://ru.wikipedia.org/wiki/%D0%A5%D0%B5%D1%88-%D1%82%D0%B0%D0%B1%D0%BB%D0%B8%D1%86%D0%B0 Разрешение коллизий]&lt;br /&gt;
* [http://rain.ifmo.ru/cat/view.php/vis/hashtables/hash-2001-2 Пример хеш таблицы]&lt;br /&gt;
* [http://research.cs.vt.edu/AVresearch/hashing/double.php Пример хеш таблицы с двойным хешированием]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория: Хеширование]]&lt;br /&gt;
[[Категория: Структуры данных]]&lt;/div&gt;</summary>
		<author><name>Kozichuk</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%A0%D0%B0%D0%B7%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BA%D0%BE%D0%BB%D0%BB%D0%B8%D0%B7%D0%B8%D0%B9&amp;diff=47793</id>
		<title>Разрешение коллизий</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A0%D0%B0%D0%B7%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BA%D0%BE%D0%BB%D0%BB%D0%B8%D0%B7%D0%B8%D0%B9&amp;diff=47793"/>
				<updated>2015-06-06T16:01:09Z</updated>
		
		<summary type="html">&lt;p&gt;Kozichuk: /* Разрешение коллизий в Java 8 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Разрешение [[Хеш-таблица|коллизий]]''' (англ. collision resolution) в [[Хеш-таблица|хеш-таблице]], задача, решаемая несколькими способами: метод цепочек, открытая адресация и т.д. Очень важно сводить количество коллизий к минимуму, так как это увеличивает время работы с хеш-таблицами. &lt;br /&gt;
&lt;br /&gt;
== Разрешение коллизий с помощью цепочек ==&lt;br /&gt;
[[Файл:open_hash.png|thumb|380px|right|Разрешение коллизий при помощи цепочек.]]&lt;br /&gt;
Каждая ячейка &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; массива &amp;lt;tex&amp;gt;H&amp;lt;/tex&amp;gt; содержит указатель на начало [[Список|списка]] всех элементов, хеш-код которых равен &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, либо указывает на их отсутствие. Коллизии приводят к тому, что появляются списки размером больше одного элемента.&lt;br /&gt;
&lt;br /&gt;
В зависимости от того нужна ли нам уникальность значений операции вставки у нас будет работать за разное время. Если не важна, то мы используем список, время вставки в который будет в худшем случае равна &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;. Иначе мы реализуем Set. В таком случае вставка элемента в худшем случае будет выполнена за &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Время работы поиска в наихудшем случае пропорционально длине списка, а если все &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; ключей захешировались в одну и ту же ячейку (создав список длиной &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;) время поиска будет равно &amp;lt;tex&amp;gt;\Theta(n)&amp;lt;/tex&amp;gt; плюс время вычисления хеш-функции, что ничуть не лучше, чем использование связного списка для хранения всех &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; элементов.&lt;br /&gt;
&lt;br /&gt;
Удаления элемента может быть выполнено за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;, как и вставка, при использовании двухсвязного списка.&lt;br /&gt;
&lt;br /&gt;
== Линейное разрешение коллизий ==&lt;br /&gt;
[[Файл:close_hash.png|thumb|380px|right|Пример хеш-таблицы с открытой адресацией и линейным пробированием.]]&lt;br /&gt;
Все элементы хранятся непосредственно в хеш-таблице, без использования связных списков. В отличии от хеширования с цепочками, при использовании этого метода может возникнуть ситуация, когда хеш-таблица окажется полностью заполненной, следовательно будет невозможно добавлять в неё новые элементы. Так что при возникновении такой ситуации решением может быть динамическое увеличение размера хеш-таблицы, с одновременной её перестройкой.&lt;br /&gt;
&lt;br /&gt;
=== Стратегии поиска ===&lt;br /&gt;
&lt;br /&gt;
''' Последовательный поиск '''&lt;br /&gt;
&lt;br /&gt;
При попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt;i+1, i+2, i+3&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку. В неё и запишем элемент.&lt;br /&gt;
&lt;br /&gt;
[[Файл:hashtables1.png|400px|Последовательный поиск, частный случай линейного поиска.]]&lt;br /&gt;
&lt;br /&gt;
''' Линейный поиск '''&lt;br /&gt;
&lt;br /&gt;
Выбираем шаг &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;. При попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt;i+(1 \cdot q), i+(2 \cdot q), i+(3 \cdot q)&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку. В неё и запишем элемент.&lt;br /&gt;
По сути последовательный поиск - частный случай линейного, где &amp;lt;tex&amp;gt;q=1&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Hashtables56.PNG|400px|Линейный поиск с шагом q.]]&lt;br /&gt;
&lt;br /&gt;
''' Квадратичный поиск '''&lt;br /&gt;
&lt;br /&gt;
Шаг &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; не фиксирован, а изменяется квадратично: &amp;lt;tex&amp;gt;q = 1,4,9,16...&amp;lt;/tex&amp;gt;. Соответственно при попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt; i+1, i+4, i+9&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку.&lt;br /&gt;
&lt;br /&gt;
[[Файл:hashtables3.png|400px|Квадратичный поиск.]]&lt;br /&gt;
&lt;br /&gt;
=== Проверка наличия элемента в таблице===&lt;br /&gt;
&lt;br /&gt;
Проверка осуществляется аналогично добавлению: мы проверяем ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; и другие, в соответствии с выбранной стратегией, пока не найдём искомый элемент или свободную ячейку.&lt;br /&gt;
&lt;br /&gt;
При поиске элемента может получится так, что мы дойдём до конца таблицы. Обычно поиск продолжается, начиная с другого конца, пока мы не придём в ту ячейку, откуда начинался поиск.&lt;br /&gt;
&lt;br /&gt;
=== Проблемы данных стратегий ===&lt;br /&gt;
&lt;br /&gt;
Проблем две — крайне нетривиальное удаление элемента из таблицы и образование кластеров  — последовательностей занятых ячеек.&lt;br /&gt;
&lt;br /&gt;
Кластеризация замедляет все операции с хеш-таблицей: при добавлении требуется перебирать всё больше элементов, при проверке тоже. Чем больше в таблице элементов, тем больше в ней кластеры и тем выше вероятность того, что добавляемый элемент попадёт в кластер.&lt;br /&gt;
Для защиты от кластеризации используется двойное хеширование и [[Хеширование кукушки|хеширование кукушки]].&lt;br /&gt;
&lt;br /&gt;
=== Удаление элемента без пометок ===&lt;br /&gt;
&lt;br /&gt;
Рассуждение будет описывать случай с линейным поиском хеша. Будем при удалении элемента сдвигать всё последующие на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; позиций назад. При этом:&lt;br /&gt;
* если в цепочке встречается элемент с другим хешем, то он должен остаться на своём месте (такая ситуация может возникнуть если оставшаяся часть цепочки была добавлена позже этого элемента)&lt;br /&gt;
* в цепочке не должно оставаться &amp;quot;дырок&amp;quot;, тогда любой элемент с данным хешем будет доступен из начала цепи&lt;br /&gt;
&lt;br /&gt;
Учитывая это будем действовать следующим образом: при поиске следующего элемента цепочки будем пропускать все ячейки с другим значением хеша, первый найденный элемент копировать в текущую ячейку, и затем рекурсивно его удалять. Если такой следующей ячейки нет, то текущий элемент можно просто удалить, сторонние цепочки при этом не разрушатся (чего нельзя сказать про случай квадратичного поиска).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
''' Псевдокод '''&lt;br /&gt;
&lt;br /&gt;
 '''function''' delete('''Item''' i) &lt;br /&gt;
      j = i + q&lt;br /&gt;
      '''while''' table[j] == ''null'' '''or''' table[j].key != table[i].key&lt;br /&gt;
         '''if''' table[j] == ''null''&lt;br /&gt;
            table[i] = ''null''&lt;br /&gt;
            '''return'''&lt;br /&gt;
         j += q&lt;br /&gt;
      table[i] = table[j]&lt;br /&gt;
      delete(j)    &lt;br /&gt;
&lt;br /&gt;
Хеш-таблицу считаем зацикленной&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Утверждение&lt;br /&gt;
|about=о времени работы&lt;br /&gt;
|statement=Асимптотически время работы &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\mathrm{find}&amp;lt;/tex&amp;gt; совпадают&lt;br /&gt;
|proof=&lt;br /&gt;
Заметим что указатель &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; в каждой итерации перемещается вперёд на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; (с учётом рекурсивных вызовов &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt;). То есть этот алгоритм последовательно пройдёт по цепочке от удаляемого элемента до последнего {{---}} с учётом вызова &amp;lt;tex&amp;gt;\mathrm{find}&amp;lt;/tex&amp;gt; собственно для нахождения удаляемого элемента, мы посетим все ячейки цепи.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Вариант с зацикливанием мы не рассматриваем, поскольку если &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; взаимнопросто с размером хеш-таблицы, то для зацикливания в ней вообще не должно быть свободных позиций&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Теперь докажем почему этот алгоритм работает. Собственно нам требуется сохранение трёх условий.&lt;br /&gt;
* В редактируемой цепи не остаётся дырок&lt;br /&gt;
Докажем по индукции. Если на данной итерации мы просто удаляем элемент (база), то после него ничего нет, всё верно. Если же нет, то вызванный в конце &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt; (см. псевдокод) заметёт созданную дыру (скопированный элемент), и сам, по предположению, новых не создаст.&lt;br /&gt;
* Элементы, которые уже на своих местах, не должны быть сдвинуты.&lt;br /&gt;
Это учтено.&lt;br /&gt;
* В других цепочках не появятся дыры&lt;br /&gt;
Противное возможно только в том случае, если какой-то элемент был действительно удалён. Удаляем мы только последнюю ячейку в цепи, и если бы на её месте возникла дыра для сторонней цепочки, это бы означало что элемент, стоящий на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; позиций назад, одновременно принадлежал нашей и другой цепочкам, что невозможно.&lt;br /&gt;
&lt;br /&gt;
==Двойное хеширование==&lt;br /&gt;
'''Двойное хеширование''' (англ. double hashing) {{---}} метод борьбы с коллизиями, возникающими при открытой адресации, основанный на использовании двух хеш-функций для построения различных последовательностей исследования хеш-таблицы.&lt;br /&gt;
&lt;br /&gt;
===Принцип двойного хеширования===&lt;br /&gt;
При двойном хешировании используются две независимые хеш-функции &amp;lt;tex&amp;gt; h_1(k) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; h_2(k) &amp;lt;/tex&amp;gt;. Пусть &amp;lt;tex&amp;gt; k &amp;lt;/tex&amp;gt; {{---}} это наш ключ, &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt; {{---}} размер нашей таблицы, &amp;lt;tex&amp;gt;n \bmod m &amp;lt;/tex&amp;gt; {{---}} остаток от деления &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt; на &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, тогда сначала исследуется ячейка с адресом &amp;lt;tex&amp;gt; h_1(k) &amp;lt;/tex&amp;gt;, если она уже занята, то рассматривается &amp;lt;tex&amp;gt; (h_1(k) +  h_2(k)) \bmod m &amp;lt;/tex&amp;gt;, затем &amp;lt;tex&amp;gt; (h_1(k) +  2 \cdot h_2(k)) \bmod m &amp;lt;/tex&amp;gt; и так далее. В общем случае идёт проверка последовательности ячеек &amp;lt;tex&amp;gt; (h_1(k) +  i \cdot h_2(k)) \bmod m &amp;lt;/tex&amp;gt; где &amp;lt;tex&amp;gt;  i = (0, 1, \; ... \;,  m - 1) &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, операции вставки, удаления и поиска в лучшем случае выполняются за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;, в худшем {{---}} за &amp;lt;tex&amp;gt;O(m)&amp;lt;/tex&amp;gt;, что не отличается от обычного [[Открытое_и_закрытое_хеширование#Линейное разрешение коллизий|линейного разрешения коллизий]].&lt;br /&gt;
Однако в среднем, при грамотном выборе хеш-функций, двойное хеширование будет выдавать лучшие результаты, за счёт того, что вероятность совпадения значений сразу двух независимых хеш-функций ниже, чем одной.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt;\forall x \neq y \; \exists h_1,h_2 : p(h_1(x)=h_1(y))&amp;gt; p((h_1(x)=h_1(y)) \land (h_2(x)=h_2(y)))&amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Выбор хеш-функций===&lt;br /&gt;
&amp;lt;tex&amp;gt; h_1 &amp;lt;/tex&amp;gt; может быть обычной хеш-функцией. Однако чтобы последовательность исследования могла охватить всю таблицу, &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; должна возвращать значения:&lt;br /&gt;
*не равные &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
*независимые  от &amp;lt;tex&amp;gt; h_1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
*взаимно простые с величиной хеш-таблицы&lt;br /&gt;
&lt;br /&gt;
Есть два удобных способа это сделать. Первый состоит в том, что в качестве размера таблицы используется простое число, а &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; возвращает натуральные числа, меньшие &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;. Второй {{---}} размер таблицы является степенью двойки, а &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; возвращает нечетные значения.&lt;br /&gt;
&lt;br /&gt;
Например, если размер таблицы равен &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, то в качестве &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; можно использовать функцию вида &amp;lt;tex&amp;gt; h_2(k) = k \bmod (m-1) + 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Файл: Вставка при двойном хэшировании.svg.jpeg|thumb|right|Вставка при двойном хешировании]]&lt;br /&gt;
&lt;br /&gt;
===Пример===&lt;br /&gt;
&lt;br /&gt;
Показана хеш-таблица размером 13 ячеек, в которой используются вспомогательные функции:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h(k,i) = (h_1(k) + i \cdot h_2(k)) \bmod 13 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h_1(k) = k \bmod 13 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h_2(k) = 1 + k \bmod 11 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Мы хотим вставить ключ 14. Изначально &amp;lt;tex&amp;gt; i = 0 &amp;lt;/tex&amp;gt;. Тогда &amp;lt;tex&amp;gt; h(14,0) = (h_1(14) + 0\cdot h_2(14)) \bmod 13 = 1 &amp;lt;/tex&amp;gt;. Но ячейка с индексом 1 занята, поэтому увеличиваем &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; на 1 и пересчитываем значение хеш-функции. Делаем так, пока не дойдем до пустой ячейки. При &amp;lt;tex&amp;gt; i = 2 &amp;lt;/tex&amp;gt; получаем &amp;lt;tex&amp;gt; h(14,2) = (h_1(14) + 2\cdot h_2(14)) \bmod 13 = 9 &amp;lt;/tex&amp;gt;. Ячейка с номером 9 свободна, значит записываем туда наш ключ.&lt;br /&gt;
&lt;br /&gt;
Таким образом, основная особенность двойного хеширования состоит в том, что при различных &amp;lt;tex&amp;gt; k &amp;lt;/tex&amp;gt; пара &amp;lt;tex&amp;gt; (h_1(k),h_2(k)) &amp;lt;/tex&amp;gt; дает различные последовательности ячеек для исследования.&lt;br /&gt;
&lt;br /&gt;
===Простая реализация===&lt;br /&gt;
Пусть у нас есть некоторый объект &amp;lt;tex&amp;gt; item &amp;lt;/tex&amp;gt;, в котором определено поле &amp;lt;tex&amp;gt; key &amp;lt;/tex&amp;gt;, от которого можно вычислить хеш-функции &amp;lt;tex&amp;gt; h_1(key)&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; h_2(key) &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Так же у нас есть таблица &amp;lt;tex&amp;gt; table &amp;lt;/tex&amp;gt; величиной &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, состоящая из объектов типа &amp;lt;tex&amp;gt; item &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
'''Вставка'''&lt;br /&gt;
 '''function''' add('''Item''' item)&lt;br /&gt;
      x = h1(item.key)&lt;br /&gt;
      y = h2(item.key)&lt;br /&gt;
         '''for''' (i = 0..m)    	&lt;br /&gt;
            '''if''' table[x] == ''null''&lt;br /&gt;
               table[x] = item&lt;br /&gt;
            '''return'''      &lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      table.resize()&amp;lt;span style=&amp;quot;color:Green&amp;quot;&amp;gt;// ошибка, требуется увеличить размер таблицы&lt;br /&gt;
&lt;br /&gt;
'''Поиск'''&lt;br /&gt;
 '''Item''' search('''Item''' key)&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m)&lt;br /&gt;
         '''if''' table[x] != ''null''&lt;br /&gt;
            '''if''' table[x].key == key&lt;br /&gt;
               '''return''' table[x]&lt;br /&gt;
            '''else'''&lt;br /&gt;
               '''return''' ''null''&lt;br /&gt;
      x = (x + y) '''mod''' m   &lt;br /&gt;
      '''return''' ''null''&lt;br /&gt;
&lt;br /&gt;
===Реализация с удалением===&lt;br /&gt;
Что бы наша хеш-таблица поддерживала удаление, требуется добавить массив &amp;lt;tex&amp;gt;deleted&amp;lt;/tex&amp;gt; типов &amp;lt;tex&amp;gt;bool&amp;lt;/tex&amp;gt;, равный по величине массиву &amp;lt;tex&amp;gt;table&amp;lt;/tex&amp;gt;. Теперь при удалении мы просто будем помечать наш объект ''как удалённый'', а при добавлении как ''не удалённый'' и замещать новым добавляемым объектом. При поиске, помимо равенства ключей, мы смотрим, удалён ли элемент, если да, то идём дальше.&lt;br /&gt;
&lt;br /&gt;
'''Вставка'''&lt;br /&gt;
 '''function''' add('''Item''' item)&lt;br /&gt;
      x = h1(item.key)&lt;br /&gt;
      y = h2(item.key)&lt;br /&gt;
      '''for''' (i = 0..m)   	&lt;br /&gt;
         '''if''' table[x] == '''null''' '''or''' deleted[x]&lt;br /&gt;
            table[x] = item&lt;br /&gt;
            deleted[x] = '''false'''&lt;br /&gt;
            '''return'''      &lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      table.resize()&amp;lt;span style=&amp;quot;color:Green&amp;quot;&amp;gt;// ошибка, требуется увеличить размер таблицы&lt;br /&gt;
'''Поиск'''&lt;br /&gt;
 '''Item''' search('''Item''' key)&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m) &lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key '''and''' !deleted[x]&lt;br /&gt;
               '''return''' table[x]&lt;br /&gt;
         '''else'''&lt;br /&gt;
            '''return''' '''null'''&lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      '''return''' '''null'''&lt;br /&gt;
&lt;br /&gt;
'''Удаление'''&lt;br /&gt;
 '''function''' remove('''Item''' key)&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m)&lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key&lt;br /&gt;
               deleted[x] = '''true'''&lt;br /&gt;
         '''else''' &lt;br /&gt;
            '''return'''&lt;br /&gt;
      x = (x + y) '''mod''' m&lt;br /&gt;
&lt;br /&gt;
== Разрешение коллизий в Java 8==&lt;br /&gt;
В Java 8 для разрешения коллизий используется модифицированный метод цепочек. Суть его заключается в том, что когда количество элементов в корзине превышает определенное значение, данный корзина переходит от использования связного списка к использованию [[АВЛ-дерево|сбалансированного дерева]]. Но данный метод имеет смысл лишь тогда, когда на элементах хеш-таблицы задан линейный порядок. То есть при использовании данный типа &amp;lt;tex&amp;gt;int&amp;lt;/tex&amp;gt; или &amp;lt;tex&amp;gt;double&amp;lt;/tex&amp;gt; имеет смысл переходить к дереву поиска, а при использовании каких-нибудь ссылок на объекты не имеет, так как они не реализуют нужный интерфейс. Такой подход позволяет улучшить производительность с &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt;. Данный способ используется в таких коллекциях как HashMap, LinkedHashMap и ConcurrentHashMap.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Hashing_in_Java8.png|500px|Хеширование в Java 8.]]&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
* [[Хеширование]]&lt;br /&gt;
* [[Хеширование_кукушки|Хеширование кукушки]]&lt;br /&gt;
* [[Идеальное_хеширование|Идеальное хеширование]]&lt;br /&gt;
&lt;br /&gt;
== Источники информации ==&lt;br /&gt;
* Бакнелл Дж. М. «Фундаментальные алгоритмы и структуры данных в Delphi», 2003&lt;br /&gt;
* Кормен, Томас Х., Лейзерсон, Чарльз И., Ривест, Рональд Л., Штайн Клиффорд «Алгоритмы: построение и анализ», 2-е издание. Пер. с англ. — М.:Издательский дом &amp;quot;Вильямс&amp;quot;, 2010.— Парал. тит. англ. — ISBN 978-5-8459-0857-5 (рус.)&lt;br /&gt;
* Дональд Кнут. «Искусство программирования, том 3. Сортировка и поиск» {{---}} «Вильямс», 2007 г.{{---}} ISBN 0-201-89685-0&lt;br /&gt;
* Седжвик Р. «Фундаментальные алгоритмы на C. Части 1-4. Анализ. Структуры данных. Сортировка. Поиск», 2003&lt;br /&gt;
* [http://openjdk.java.net/jeps/180 Handle Frequent HashMap Collisions with Balanced Trees]&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Double_hashing Wikipedia {{---}} Double_hashing]&lt;br /&gt;
* [http://ru.wikipedia.org/wiki/%D0%A5%D0%B5%D1%88-%D1%82%D0%B0%D0%B1%D0%BB%D0%B8%D1%86%D0%B0 Разрешение коллизий]&lt;br /&gt;
* [http://rain.ifmo.ru/cat/view.php/vis/hashtables/hash-2001-2 Пример хеш таблицы]&lt;br /&gt;
* [http://research.cs.vt.edu/AVresearch/hashing/double.php Пример хеш таблицы с двойным хешированием]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория: Хеширование]]&lt;br /&gt;
[[Категория: Структуры данных]]&lt;/div&gt;</summary>
		<author><name>Kozichuk</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%A0%D0%B0%D0%B7%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BA%D0%BE%D0%BB%D0%BB%D0%B8%D0%B7%D0%B8%D0%B9&amp;diff=47518</id>
		<title>Разрешение коллизий</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A0%D0%B0%D0%B7%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BA%D0%BE%D0%BB%D0%BB%D0%B8%D0%B7%D0%B8%D0%B9&amp;diff=47518"/>
				<updated>2015-06-05T01:27:58Z</updated>
		
		<summary type="html">&lt;p&gt;Kozichuk: /* Разрешение коллизий в Java 8 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Разрешение [[Хеш-таблица|коллизий]]''' (англ. collision resolution) в [[Хеш-таблица|хеш-таблице]], задача, решаемая несколькими способами: метод цепочек, открытая адресация и т.д. Очень важно сводить количество коллизий к минимуму, так как это увеличивает время работы с хеш-таблицами. &lt;br /&gt;
&lt;br /&gt;
== Разрешение коллизий с помощью цепочек ==&lt;br /&gt;
[[Файл:open_hash.png|thumb|380px|right|Разрешение коллизий при помощи цепочек.]]&lt;br /&gt;
Каждая ячейка &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; массива &amp;lt;tex&amp;gt;H&amp;lt;/tex&amp;gt; содержит указатель на начало [[Список|списка]] всех элементов, хеш-код которых равен &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, либо указывает на их отсутствие. Коллизии приводят к тому, что появляются списки размером больше одного элемента.&lt;br /&gt;
&lt;br /&gt;
В зависимости от того нужна ли нам уникальность значений операции вставки у нас будет работать за разное время. Если не важна, то мы используем список, время вставки в который будет в худшем случае равна &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;. Иначе мы реализуем Set. В таком случае вставка элемента в худшем случае будет выполнена за &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Время работы поиска в наихудшем случае пропорционально длине списка, а если все &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; ключей захешировались в одну и ту же ячейку (создав список длиной &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;) время поиска будет равно &amp;lt;tex&amp;gt;\Theta(n)&amp;lt;/tex&amp;gt; плюс время вычисления хеш-функции, что ничуть не лучше, чем использование связного списка для хранения всех &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; элементов.&lt;br /&gt;
&lt;br /&gt;
Удаления элемента может быть выполнено за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;, как и вставка, при использовании двухсвязного списка.&lt;br /&gt;
&lt;br /&gt;
== Линейное разрешение коллизий ==&lt;br /&gt;
[[Файл:close_hash.png|thumb|380px|right|Пример хеш-таблицы с открытой адресацией и линейным пробированием.]]&lt;br /&gt;
Все элементы хранятся непосредственно в хеш-таблице, без использования связных списков. В отличии от хеширования с цепочками, при использовании этого метода может возникнуть ситуация, когда хеш-таблица окажется полностью заполненной, следовательно будет невозможно добавлять в неё новые элементы. Так что при возникновении такой ситуации решением может быть динамическое увеличение размера хеш-таблицы, с одновременной её перестройкой.&lt;br /&gt;
&lt;br /&gt;
=== Стратегии поиска ===&lt;br /&gt;
&lt;br /&gt;
''' Последовательный поиск '''&lt;br /&gt;
&lt;br /&gt;
При попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt;i+1, i+2, i+3&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку. В неё и запишем элемент.&lt;br /&gt;
&lt;br /&gt;
[[Файл:hashtables1.png|400px|Последовательный поиск, частный случай линейного поиска.]]&lt;br /&gt;
&lt;br /&gt;
''' Линейный поиск '''&lt;br /&gt;
&lt;br /&gt;
Выбираем шаг &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;. При попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt;i+(1 \cdot q), i+(2 \cdot q), i+(3 \cdot q)&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку. В неё и запишем элемент.&lt;br /&gt;
По сути последовательный поиск - частный случай линейного, где &amp;lt;tex&amp;gt;q=1&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Hashtables56.PNG|400px|Линейный поиск с шагом q.]]&lt;br /&gt;
&lt;br /&gt;
''' Квадратичный поиск '''&lt;br /&gt;
&lt;br /&gt;
Шаг &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; не фиксирован, а изменяется квадратично: &amp;lt;tex&amp;gt;q = 1,4,9,16...&amp;lt;/tex&amp;gt;. Соответственно при попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt; i+1, i+4, i+9&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку.&lt;br /&gt;
&lt;br /&gt;
[[Файл:hashtables3.png|400px|Квадратичный поиск.]]&lt;br /&gt;
&lt;br /&gt;
=== Проверка наличия элемента в таблице===&lt;br /&gt;
&lt;br /&gt;
Проверка осуществляется аналогично добавлению: мы проверяем ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; и другие, в соответствии с выбранной стратегией, пока не найдём искомый элемент или свободную ячейку.&lt;br /&gt;
&lt;br /&gt;
При поиске элемента может получится так, что мы дойдём до конца таблицы. Обычно поиск продолжается, начиная с другого конца, пока мы не придём в ту ячейку, откуда начинался поиск.&lt;br /&gt;
&lt;br /&gt;
=== Проблемы данных стратегий ===&lt;br /&gt;
&lt;br /&gt;
Проблем две — крайне нетривиальное удаление элемента из таблицы и образование кластеров  — последовательностей занятых ячеек.&lt;br /&gt;
&lt;br /&gt;
Кластеризация замедляет все операции с хеш-таблицей: при добавлении требуется перебирать всё больше элементов, при проверке тоже. Чем больше в таблице элементов, тем больше в ней кластеры и тем выше вероятность того, что добавляемый элемент попадёт в кластер.&lt;br /&gt;
Для защиты от кластеризации используется двойное хеширование и [[Хеширование кукушки|хеширование кукушки]].&lt;br /&gt;
&lt;br /&gt;
=== Удаление элемента без пометок ===&lt;br /&gt;
&lt;br /&gt;
Рассуждение будет описывать случай с линейным поиском хеша. Будем при удалении элемента сдвигать всё последующие на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; позиций назад. При этом:&lt;br /&gt;
* если в цепочке встречается элемент с другим хешем, то он должен остаться на своём месте (такая ситуация может возникнуть если оставшаяся часть цепочки была добавлена позже этого элемента)&lt;br /&gt;
* в цепочке не должно оставаться &amp;quot;дырок&amp;quot;, тогда любой элемент с данным хешем будет доступен из начала цепи&lt;br /&gt;
&lt;br /&gt;
Учитывая это будем действовать следующим образом: при поиске следующего элемента цепочки будем пропускать все ячейки с другим значением хеша, первый найденный элемент копировать в текущую ячейку, и затем рекурсивно его удалять. Если такой следующей ячейки нет, то текущий элемент можно просто удалить, сторонние цепочки при этом не разрушатся (чего нельзя сказать про случай квадратичного поиска).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
''' Псевдокод '''&lt;br /&gt;
&lt;br /&gt;
 '''function''' delete('''Item''' i) &lt;br /&gt;
      j = i + q&lt;br /&gt;
      '''while''' table[j] == ''null'' '''or''' table[j].key != table[i].key&lt;br /&gt;
         '''if''' table[j] == ''null''&lt;br /&gt;
            table[i] = ''null''&lt;br /&gt;
            '''return'''&lt;br /&gt;
         j += q&lt;br /&gt;
      table[i] = table[j]&lt;br /&gt;
      delete(j)    &lt;br /&gt;
&lt;br /&gt;
Хеш-таблицу считаем зацикленной&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Утверждение&lt;br /&gt;
|about=о времени работы&lt;br /&gt;
|statement=Асимптотически время работы &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\mathrm{find}&amp;lt;/tex&amp;gt; совпадают&lt;br /&gt;
|proof=&lt;br /&gt;
Заметим что указатель &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; в каждой итерации перемещается вперёд на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; (с учётом рекурсивных вызовов &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt;). То есть этот алгоритм последовательно пройдёт по цепочке от удаляемого элемента до последнего {{---}} с учётом вызова &amp;lt;tex&amp;gt;\mathrm{find}&amp;lt;/tex&amp;gt; собственно для нахождения удаляемого элемента, мы посетим все ячейки цепи.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Вариант с зацикливанием мы не рассматриваем, поскольку если &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; взаимнопросто с размером хеш-таблицы, то для зацикливания в ней вообще не должно быть свободных позиций&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Теперь докажем почему этот алгоритм работает. Собственно нам требуется сохранение трёх условий.&lt;br /&gt;
* В редактируемой цепи не остаётся дырок&lt;br /&gt;
Докажем по индукции. Если на данной итерации мы просто удаляем элемент (база), то после него ничего нет, всё верно. Если же нет, то вызванный в конце &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt; (см. псевдокод) заметёт созданную дыру (скопированный элемент), и сам, по предположению, новых не создаст.&lt;br /&gt;
* Элементы, которые уже на своих местах, не должны быть сдвинуты.&lt;br /&gt;
Это учтено.&lt;br /&gt;
* В других цепочках не появятся дыры&lt;br /&gt;
Противное возможно только в том случае, если какой-то элемент был действительно удалён. Удаляем мы только последнюю ячейку в цепи, и если бы на её месте возникла дыра для сторонней цепочки, это бы означало что элемент, стоящий на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; позиций назад, одновременно принадлежал нашей и другой цепочкам, что невозможно.&lt;br /&gt;
&lt;br /&gt;
==Двойное хеширование==&lt;br /&gt;
'''Двойное хеширование''' (англ. double hashing) {{---}} метод борьбы с коллизиями, возникающими при открытой адресации, основанный на использовании двух хеш-функций для построения различных последовательностей исследования хеш-таблицы.&lt;br /&gt;
&lt;br /&gt;
===Принцип двойного хеширования===&lt;br /&gt;
При двойном хешировании используются две независимые хеш-функции &amp;lt;tex&amp;gt; h_1(k) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; h_2(k) &amp;lt;/tex&amp;gt;. Пусть &amp;lt;tex&amp;gt; k &amp;lt;/tex&amp;gt; {{---}} это наш ключ, &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt; {{---}} размер нашей таблицы, &amp;lt;tex&amp;gt;n \bmod m &amp;lt;/tex&amp;gt; {{---}} остаток от деления &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt; на &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, тогда сначала исследуется ячейка с адресом &amp;lt;tex&amp;gt; h_1(k) &amp;lt;/tex&amp;gt;, если она уже занята, то рассматривается &amp;lt;tex&amp;gt; (h_1(k) +  h_2(k)) \bmod m &amp;lt;/tex&amp;gt;, затем &amp;lt;tex&amp;gt; (h_1(k) +  2 \cdot h_2(k)) \bmod m &amp;lt;/tex&amp;gt; и так далее. В общем случае идёт проверка последовательности ячеек &amp;lt;tex&amp;gt; (h_1(k) +  i \cdot h_2(k)) \bmod m &amp;lt;/tex&amp;gt; где &amp;lt;tex&amp;gt;  i = (0, 1, \; ... \;,  m - 1) &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, операции вставки, удаления и поиска в лучшем случае выполняются за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;, в худшем {{---}} за &amp;lt;tex&amp;gt;O(m)&amp;lt;/tex&amp;gt;, что не отличается от обычного [[Открытое_и_закрытое_хеширование#Линейное разрешение коллизий|линейного разрешения коллизий]].&lt;br /&gt;
Однако в среднем, при грамотном выборе хеш-функций, двойное хеширование будет выдавать лучшие результаты, за счёт того, что вероятность совпадения значений сразу двух независимых хеш-функций ниже, чем одной.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt;\forall x \neq y \; \exists h_1,h_2 : p(h_1(x)=h_1(y))&amp;gt; p((h_1(x)=h_1(y)) \land (h_2(x)=h_2(y)))&amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Выбор хеш-функций===&lt;br /&gt;
&amp;lt;tex&amp;gt; h_1 &amp;lt;/tex&amp;gt; может быть обычной хеш-функцией. Однако чтобы последовательность исследования могла охватить всю таблицу, &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; должна возвращать значения:&lt;br /&gt;
*не равные &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
*независимые  от &amp;lt;tex&amp;gt; h_1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
*взаимно простые с величиной хеш-таблицы&lt;br /&gt;
&lt;br /&gt;
Есть два удобных способа это сделать. Первый состоит в том, что в качестве размера таблицы используется простое число, а &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; возвращает натуральные числа, меньшие &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;. Второй {{---}} размер таблицы является степенью двойки, а &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; возвращает нечетные значения.&lt;br /&gt;
&lt;br /&gt;
Например, если размер таблицы равен &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, то в качестве &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; можно использовать функцию вида &amp;lt;tex&amp;gt; h_2(k) = k \bmod (m-1) + 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Файл: Вставка при двойном хэшировании.svg.jpeg|thumb|right|Вставка при двойном хешировании]]&lt;br /&gt;
&lt;br /&gt;
===Пример===&lt;br /&gt;
&lt;br /&gt;
Показана хеш-таблица размером 13 ячеек, в которой используются вспомогательные функции:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h(k,i) = (h_1(k) + i \cdot h_2(k)) \bmod 13 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h_1(k) = k \bmod 13 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h_2(k) = 1 + k \bmod 11 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Мы хотим вставить ключ 14. Изначально &amp;lt;tex&amp;gt; i = 0 &amp;lt;/tex&amp;gt;. Тогда &amp;lt;tex&amp;gt; h(14,0) = (h_1(14) + 0\cdot h_2(14)) \bmod 13 = 1 &amp;lt;/tex&amp;gt;. Но ячейка с индексом 1 занята, поэтому увеличиваем &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; на 1 и пересчитываем значение хеш-функции. Делаем так, пока не дойдем до пустой ячейки. При &amp;lt;tex&amp;gt; i = 2 &amp;lt;/tex&amp;gt; получаем &amp;lt;tex&amp;gt; h(14,2) = (h_1(14) + 2\cdot h_2(14)) \bmod 13 = 9 &amp;lt;/tex&amp;gt;. Ячейка с номером 9 свободна, значит записываем туда наш ключ.&lt;br /&gt;
&lt;br /&gt;
Таким образом, основная особенность двойного хеширования состоит в том, что при различных &amp;lt;tex&amp;gt; k &amp;lt;/tex&amp;gt; пара &amp;lt;tex&amp;gt; (h_1(k),h_2(k)) &amp;lt;/tex&amp;gt; дает различные последовательности ячеек для исследования.&lt;br /&gt;
&lt;br /&gt;
===Простая реализация===&lt;br /&gt;
Пусть у нас есть некоторый объект &amp;lt;tex&amp;gt; item &amp;lt;/tex&amp;gt;, в котором определено поле &amp;lt;tex&amp;gt; key &amp;lt;/tex&amp;gt;, от которого можно вычислить хеш-функции &amp;lt;tex&amp;gt; h_1(key)&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; h_2(key) &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Так же у нас есть таблица &amp;lt;tex&amp;gt; table &amp;lt;/tex&amp;gt; величиной &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, состоящая из объектов типа &amp;lt;tex&amp;gt; item &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
'''Вставка'''&lt;br /&gt;
 '''function''' add('''Item''' item)&lt;br /&gt;
      x = h1(item.key)&lt;br /&gt;
      y = h2(item.key)&lt;br /&gt;
         '''for''' (i = 0..m)    	&lt;br /&gt;
            '''if''' table[x] == ''null''&lt;br /&gt;
               table[x] = item&lt;br /&gt;
            '''return'''      &lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      table.resize()&amp;lt;span style=&amp;quot;color:Green&amp;quot;&amp;gt;// ошибка, требуется увеличить размер таблицы&lt;br /&gt;
&lt;br /&gt;
'''Поиск'''&lt;br /&gt;
 '''Item''' search('''Item''' key)&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m)&lt;br /&gt;
         '''if''' table[x] != ''null''&lt;br /&gt;
            '''if''' table[x].key == key&lt;br /&gt;
               '''return''' table[x]&lt;br /&gt;
            '''else'''&lt;br /&gt;
               '''return''' ''null''&lt;br /&gt;
      x = (x + y) '''mod''' m   &lt;br /&gt;
      '''return''' ''null''&lt;br /&gt;
&lt;br /&gt;
===Реализация с удалением===&lt;br /&gt;
Что бы наша хеш-таблица поддерживала удаление, требуется добавить массив &amp;lt;tex&amp;gt;deleted&amp;lt;/tex&amp;gt; типов &amp;lt;tex&amp;gt;bool&amp;lt;/tex&amp;gt;, равный по величине массиву &amp;lt;tex&amp;gt;table&amp;lt;/tex&amp;gt;. Теперь при удалении мы просто будем помечать наш объект ''как удалённый'', а при добавлении как ''не удалённый'' и замещать новым добавляемым объектом. При поиске, помимо равенства ключей, мы смотрим, удалён ли элемент, если да, то идём дальше.&lt;br /&gt;
&lt;br /&gt;
'''Вставка'''&lt;br /&gt;
 '''function''' add('''Item''' item)&lt;br /&gt;
      x = h1(item.key)&lt;br /&gt;
      y = h2(item.key)&lt;br /&gt;
      '''for''' (i = 0..m)   	&lt;br /&gt;
         '''if''' table[x] == '''null''' '''or''' deleted[x]&lt;br /&gt;
            table[x] = item&lt;br /&gt;
            deleted[x] = '''false'''&lt;br /&gt;
            '''return'''      &lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      table.resize()&amp;lt;span style=&amp;quot;color:Green&amp;quot;&amp;gt;// ошибка, требуется увеличить размер таблицы&lt;br /&gt;
'''Поиск'''&lt;br /&gt;
 '''Item''' search('''Item''' key)&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m) &lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key '''and''' !deleted[x]&lt;br /&gt;
               '''return''' table[x]&lt;br /&gt;
         '''else'''&lt;br /&gt;
            '''return''' '''null'''&lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      '''return''' '''null'''&lt;br /&gt;
&lt;br /&gt;
'''Удаление'''&lt;br /&gt;
 '''function''' remove('''Item''' key)&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m)&lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key&lt;br /&gt;
               deleted[x] = '''true'''&lt;br /&gt;
         '''else''' &lt;br /&gt;
            '''return'''&lt;br /&gt;
      x = (x + y) '''mod''' m&lt;br /&gt;
&lt;br /&gt;
== Разрешение коллизий в Java 8==&lt;br /&gt;
В Java 8 для разрешения коллизий используется модифицированный метод цепочек. Суть его заключается в том, что когда количество элементов в корзине превышает определенное значение, данный корзина переходит от использования связного списка к использованию сбалансированного дерева. Но данный метод имеет смысл лишь тогда, когда на элементах хеш-таблицы задан линейный порядок. То есть при использовании данный типа &amp;lt;tex&amp;gt;int&amp;lt;/tex&amp;gt; или &amp;lt;tex&amp;gt;double&amp;lt;/tex&amp;gt; имеет смысл переходить к дереву поиска, а при использовании каких-нибудь ссылок на объекты не имеет, так как они не реализуют нужный интерфейс. Такой подход позволяет улучшить производительность с &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt;. Данный способ используется в таких коллекциях как HashMap, LinkedHashMap и ConcurrentHashMap.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Hashing_in_Java8.png|500px|Хеширование в Java 8.]]&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
* [[Хеширование]]&lt;br /&gt;
* [[Хеширование_кукушки|Хеширование кукушки]]&lt;br /&gt;
* [[Идеальное_хеширование|Идеальное хеширование]]&lt;br /&gt;
&lt;br /&gt;
== Источники информации ==&lt;br /&gt;
* Бакнелл Дж. М. «Фундаментальные алгоритмы и структуры данных в Delphi», 2003&lt;br /&gt;
* Кормен, Томас Х., Лейзерсон, Чарльз И., Ривест, Рональд Л., Штайн Клиффорд «Алгоритмы: построение и анализ», 2-е издание. Пер. с англ. — М.:Издательский дом &amp;quot;Вильямс&amp;quot;, 2010.— Парал. тит. англ. — ISBN 978-5-8459-0857-5 (рус.)&lt;br /&gt;
* Дональд Кнут. «Искусство программирования, том 3. Сортировка и поиск» {{---}} «Вильямс», 2007 г.{{---}} ISBN 0-201-89685-0&lt;br /&gt;
* Седжвик Р. «Фундаментальные алгоритмы на C. Части 1-4. Анализ. Структуры данных. Сортировка. Поиск», 2003&lt;br /&gt;
* [http://openjdk.java.net/jeps/180 Handle Frequent HashMap Collisions with Balanced Trees]&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Double_hashing Wikipedia {{---}} Double_hashing]&lt;br /&gt;
* [http://ru.wikipedia.org/wiki/%D0%A5%D0%B5%D1%88-%D1%82%D0%B0%D0%B1%D0%BB%D0%B8%D1%86%D0%B0 Разрешение коллизий]&lt;br /&gt;
* [http://rain.ifmo.ru/cat/view.php/vis/hashtables/hash-2001-2 Пример хеш таблицы]&lt;br /&gt;
* [http://research.cs.vt.edu/AVresearch/hashing/double.php Пример хеш таблицы с двойным хешированием]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория: Хеширование]]&lt;br /&gt;
[[Категория: Структуры данных]]&lt;/div&gt;</summary>
		<author><name>Kozichuk</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%A0%D0%B0%D0%B7%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BA%D0%BE%D0%BB%D0%BB%D0%B8%D0%B7%D0%B8%D0%B9&amp;diff=47517</id>
		<title>Разрешение коллизий</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A0%D0%B0%D0%B7%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BA%D0%BE%D0%BB%D0%BB%D0%B8%D0%B7%D0%B8%D0%B9&amp;diff=47517"/>
				<updated>2015-06-05T01:17:32Z</updated>
		
		<summary type="html">&lt;p&gt;Kozichuk: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Разрешение [[Хеш-таблица|коллизий]]''' (англ. collision resolution) в [[Хеш-таблица|хеш-таблице]], задача, решаемая несколькими способами: метод цепочек, открытая адресация и т.д. Очень важно сводить количество коллизий к минимуму, так как это увеличивает время работы с хеш-таблицами. &lt;br /&gt;
&lt;br /&gt;
== Разрешение коллизий с помощью цепочек ==&lt;br /&gt;
[[Файл:open_hash.png|thumb|380px|right|Разрешение коллизий при помощи цепочек.]]&lt;br /&gt;
Каждая ячейка &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; массива &amp;lt;tex&amp;gt;H&amp;lt;/tex&amp;gt; содержит указатель на начало [[Список|списка]] всех элементов, хеш-код которых равен &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, либо указывает на их отсутствие. Коллизии приводят к тому, что появляются списки размером больше одного элемента.&lt;br /&gt;
&lt;br /&gt;
В зависимости от того нужна ли нам уникальность значений операции вставки у нас будет работать за разное время. Если не важна, то мы используем список, время вставки в который будет в худшем случае равна &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;. Иначе мы реализуем Set. В таком случае вставка элемента в худшем случае будет выполнена за &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Время работы поиска в наихудшем случае пропорционально длине списка, а если все &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; ключей захешировались в одну и ту же ячейку (создав список длиной &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;) время поиска будет равно &amp;lt;tex&amp;gt;\Theta(n)&amp;lt;/tex&amp;gt; плюс время вычисления хеш-функции, что ничуть не лучше, чем использование связного списка для хранения всех &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; элементов.&lt;br /&gt;
&lt;br /&gt;
Удаления элемента может быть выполнено за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;, как и вставка, при использовании двухсвязного списка.&lt;br /&gt;
&lt;br /&gt;
== Линейное разрешение коллизий ==&lt;br /&gt;
[[Файл:close_hash.png|thumb|380px|right|Пример хеш-таблицы с открытой адресацией и линейным пробированием.]]&lt;br /&gt;
Все элементы хранятся непосредственно в хеш-таблице, без использования связных списков. В отличии от хеширования с цепочками, при использовании этого метода может возникнуть ситуация, когда хеш-таблица окажется полностью заполненной, следовательно будет невозможно добавлять в неё новые элементы. Так что при возникновении такой ситуации решением может быть динамическое увеличение размера хеш-таблицы, с одновременной её перестройкой.&lt;br /&gt;
&lt;br /&gt;
=== Стратегии поиска ===&lt;br /&gt;
&lt;br /&gt;
''' Последовательный поиск '''&lt;br /&gt;
&lt;br /&gt;
При попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt;i+1, i+2, i+3&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку. В неё и запишем элемент.&lt;br /&gt;
&lt;br /&gt;
[[Файл:hashtables1.png|400px|Последовательный поиск, частный случай линейного поиска.]]&lt;br /&gt;
&lt;br /&gt;
''' Линейный поиск '''&lt;br /&gt;
&lt;br /&gt;
Выбираем шаг &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;. При попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt;i+(1 \cdot q), i+(2 \cdot q), i+(3 \cdot q)&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку. В неё и запишем элемент.&lt;br /&gt;
По сути последовательный поиск - частный случай линейного, где &amp;lt;tex&amp;gt;q=1&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Hashtables56.PNG|400px|Линейный поиск с шагом q.]]&lt;br /&gt;
&lt;br /&gt;
''' Квадратичный поиск '''&lt;br /&gt;
&lt;br /&gt;
Шаг &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; не фиксирован, а изменяется квадратично: &amp;lt;tex&amp;gt;q = 1,4,9,16...&amp;lt;/tex&amp;gt;. Соответственно при попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt; i+1, i+4, i+9&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку.&lt;br /&gt;
&lt;br /&gt;
[[Файл:hashtables3.png|400px|Квадратичный поиск.]]&lt;br /&gt;
&lt;br /&gt;
=== Проверка наличия элемента в таблице===&lt;br /&gt;
&lt;br /&gt;
Проверка осуществляется аналогично добавлению: мы проверяем ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; и другие, в соответствии с выбранной стратегией, пока не найдём искомый элемент или свободную ячейку.&lt;br /&gt;
&lt;br /&gt;
При поиске элемента может получится так, что мы дойдём до конца таблицы. Обычно поиск продолжается, начиная с другого конца, пока мы не придём в ту ячейку, откуда начинался поиск.&lt;br /&gt;
&lt;br /&gt;
=== Проблемы данных стратегий ===&lt;br /&gt;
&lt;br /&gt;
Проблем две — крайне нетривиальное удаление элемента из таблицы и образование кластеров  — последовательностей занятых ячеек.&lt;br /&gt;
&lt;br /&gt;
Кластеризация замедляет все операции с хеш-таблицей: при добавлении требуется перебирать всё больше элементов, при проверке тоже. Чем больше в таблице элементов, тем больше в ней кластеры и тем выше вероятность того, что добавляемый элемент попадёт в кластер.&lt;br /&gt;
Для защиты от кластеризации используется двойное хеширование и [[Хеширование кукушки|хеширование кукушки]].&lt;br /&gt;
&lt;br /&gt;
=== Удаление элемента без пометок ===&lt;br /&gt;
&lt;br /&gt;
Рассуждение будет описывать случай с линейным поиском хеша. Будем при удалении элемента сдвигать всё последующие на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; позиций назад. При этом:&lt;br /&gt;
* если в цепочке встречается элемент с другим хешем, то он должен остаться на своём месте (такая ситуация может возникнуть если оставшаяся часть цепочки была добавлена позже этого элемента)&lt;br /&gt;
* в цепочке не должно оставаться &amp;quot;дырок&amp;quot;, тогда любой элемент с данным хешем будет доступен из начала цепи&lt;br /&gt;
&lt;br /&gt;
Учитывая это будем действовать следующим образом: при поиске следующего элемента цепочки будем пропускать все ячейки с другим значением хеша, первый найденный элемент копировать в текущую ячейку, и затем рекурсивно его удалять. Если такой следующей ячейки нет, то текущий элемент можно просто удалить, сторонние цепочки при этом не разрушатся (чего нельзя сказать про случай квадратичного поиска).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
''' Псевдокод '''&lt;br /&gt;
&lt;br /&gt;
 '''function''' delete('''Item''' i) &lt;br /&gt;
      j = i + q&lt;br /&gt;
      '''while''' table[j] == ''null'' '''or''' table[j].key != table[i].key&lt;br /&gt;
         '''if''' table[j] == ''null''&lt;br /&gt;
            table[i] = ''null''&lt;br /&gt;
            '''return'''&lt;br /&gt;
         j += q&lt;br /&gt;
      table[i] = table[j]&lt;br /&gt;
      delete(j)    &lt;br /&gt;
&lt;br /&gt;
Хеш-таблицу считаем зацикленной&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Утверждение&lt;br /&gt;
|about=о времени работы&lt;br /&gt;
|statement=Асимптотически время работы &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\mathrm{find}&amp;lt;/tex&amp;gt; совпадают&lt;br /&gt;
|proof=&lt;br /&gt;
Заметим что указатель &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; в каждой итерации перемещается вперёд на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; (с учётом рекурсивных вызовов &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt;). То есть этот алгоритм последовательно пройдёт по цепочке от удаляемого элемента до последнего {{---}} с учётом вызова &amp;lt;tex&amp;gt;\mathrm{find}&amp;lt;/tex&amp;gt; собственно для нахождения удаляемого элемента, мы посетим все ячейки цепи.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Вариант с зацикливанием мы не рассматриваем, поскольку если &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; взаимнопросто с размером хеш-таблицы, то для зацикливания в ней вообще не должно быть свободных позиций&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Теперь докажем почему этот алгоритм работает. Собственно нам требуется сохранение трёх условий.&lt;br /&gt;
* В редактируемой цепи не остаётся дырок&lt;br /&gt;
Докажем по индукции. Если на данной итерации мы просто удаляем элемент (база), то после него ничего нет, всё верно. Если же нет, то вызванный в конце &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt; (см. псевдокод) заметёт созданную дыру (скопированный элемент), и сам, по предположению, новых не создаст.&lt;br /&gt;
* Элементы, которые уже на своих местах, не должны быть сдвинуты.&lt;br /&gt;
Это учтено.&lt;br /&gt;
* В других цепочках не появятся дыры&lt;br /&gt;
Противное возможно только в том случае, если какой-то элемент был действительно удалён. Удаляем мы только последнюю ячейку в цепи, и если бы на её месте возникла дыра для сторонней цепочки, это бы означало что элемент, стоящий на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; позиций назад, одновременно принадлежал нашей и другой цепочкам, что невозможно.&lt;br /&gt;
&lt;br /&gt;
==Двойное хеширование==&lt;br /&gt;
'''Двойное хеширование''' (англ. double hashing) {{---}} метод борьбы с коллизиями, возникающими при открытой адресации, основанный на использовании двух хеш-функций для построения различных последовательностей исследования хеш-таблицы.&lt;br /&gt;
&lt;br /&gt;
===Принцип двойного хеширования===&lt;br /&gt;
При двойном хешировании используются две независимые хеш-функции &amp;lt;tex&amp;gt; h_1(k) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; h_2(k) &amp;lt;/tex&amp;gt;. Пусть &amp;lt;tex&amp;gt; k &amp;lt;/tex&amp;gt; {{---}} это наш ключ, &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt; {{---}} размер нашей таблицы, &amp;lt;tex&amp;gt;n \bmod m &amp;lt;/tex&amp;gt; {{---}} остаток от деления &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt; на &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, тогда сначала исследуется ячейка с адресом &amp;lt;tex&amp;gt; h_1(k) &amp;lt;/tex&amp;gt;, если она уже занята, то рассматривается &amp;lt;tex&amp;gt; (h_1(k) +  h_2(k)) \bmod m &amp;lt;/tex&amp;gt;, затем &amp;lt;tex&amp;gt; (h_1(k) +  2 \cdot h_2(k)) \bmod m &amp;lt;/tex&amp;gt; и так далее. В общем случае идёт проверка последовательности ячеек &amp;lt;tex&amp;gt; (h_1(k) +  i \cdot h_2(k)) \bmod m &amp;lt;/tex&amp;gt; где &amp;lt;tex&amp;gt;  i = (0, 1, \; ... \;,  m - 1) &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, операции вставки, удаления и поиска в лучшем случае выполняются за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;, в худшем {{---}} за &amp;lt;tex&amp;gt;O(m)&amp;lt;/tex&amp;gt;, что не отличается от обычного [[Открытое_и_закрытое_хеширование#Линейное разрешение коллизий|линейного разрешения коллизий]].&lt;br /&gt;
Однако в среднем, при грамотном выборе хеш-функций, двойное хеширование будет выдавать лучшие результаты, за счёт того, что вероятность совпадения значений сразу двух независимых хеш-функций ниже, чем одной.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt;\forall x \neq y \; \exists h_1,h_2 : p(h_1(x)=h_1(y))&amp;gt; p((h_1(x)=h_1(y)) \land (h_2(x)=h_2(y)))&amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Выбор хеш-функций===&lt;br /&gt;
&amp;lt;tex&amp;gt; h_1 &amp;lt;/tex&amp;gt; может быть обычной хеш-функцией. Однако чтобы последовательность исследования могла охватить всю таблицу, &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; должна возвращать значения:&lt;br /&gt;
*не равные &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
*независимые  от &amp;lt;tex&amp;gt; h_1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
*взаимно простые с величиной хеш-таблицы&lt;br /&gt;
&lt;br /&gt;
Есть два удобных способа это сделать. Первый состоит в том, что в качестве размера таблицы используется простое число, а &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; возвращает натуральные числа, меньшие &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;. Второй {{---}} размер таблицы является степенью двойки, а &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; возвращает нечетные значения.&lt;br /&gt;
&lt;br /&gt;
Например, если размер таблицы равен &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, то в качестве &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; можно использовать функцию вида &amp;lt;tex&amp;gt; h_2(k) = k \bmod (m-1) + 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Файл: Вставка при двойном хэшировании.svg.jpeg|thumb|right|Вставка при двойном хешировании]]&lt;br /&gt;
&lt;br /&gt;
===Пример===&lt;br /&gt;
&lt;br /&gt;
Показана хеш-таблица размером 13 ячеек, в которой используются вспомогательные функции:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h(k,i) = (h_1(k) + i \cdot h_2(k)) \bmod 13 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h_1(k) = k \bmod 13 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h_2(k) = 1 + k \bmod 11 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Мы хотим вставить ключ 14. Изначально &amp;lt;tex&amp;gt; i = 0 &amp;lt;/tex&amp;gt;. Тогда &amp;lt;tex&amp;gt; h(14,0) = (h_1(14) + 0\cdot h_2(14)) \bmod 13 = 1 &amp;lt;/tex&amp;gt;. Но ячейка с индексом 1 занята, поэтому увеличиваем &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; на 1 и пересчитываем значение хеш-функции. Делаем так, пока не дойдем до пустой ячейки. При &amp;lt;tex&amp;gt; i = 2 &amp;lt;/tex&amp;gt; получаем &amp;lt;tex&amp;gt; h(14,2) = (h_1(14) + 2\cdot h_2(14)) \bmod 13 = 9 &amp;lt;/tex&amp;gt;. Ячейка с номером 9 свободна, значит записываем туда наш ключ.&lt;br /&gt;
&lt;br /&gt;
Таким образом, основная особенность двойного хеширования состоит в том, что при различных &amp;lt;tex&amp;gt; k &amp;lt;/tex&amp;gt; пара &amp;lt;tex&amp;gt; (h_1(k),h_2(k)) &amp;lt;/tex&amp;gt; дает различные последовательности ячеек для исследования.&lt;br /&gt;
&lt;br /&gt;
===Простая реализация===&lt;br /&gt;
Пусть у нас есть некоторый объект &amp;lt;tex&amp;gt; item &amp;lt;/tex&amp;gt;, в котором определено поле &amp;lt;tex&amp;gt; key &amp;lt;/tex&amp;gt;, от которого можно вычислить хеш-функции &amp;lt;tex&amp;gt; h_1(key)&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; h_2(key) &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Так же у нас есть таблица &amp;lt;tex&amp;gt; table &amp;lt;/tex&amp;gt; величиной &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, состоящая из объектов типа &amp;lt;tex&amp;gt; item &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
'''Вставка'''&lt;br /&gt;
 '''function''' add('''Item''' item)&lt;br /&gt;
      x = h1(item.key)&lt;br /&gt;
      y = h2(item.key)&lt;br /&gt;
         '''for''' (i = 0..m)    	&lt;br /&gt;
            '''if''' table[x] == ''null''&lt;br /&gt;
               table[x] = item&lt;br /&gt;
            '''return'''      &lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      table.resize()&amp;lt;span style=&amp;quot;color:Green&amp;quot;&amp;gt;// ошибка, требуется увеличить размер таблицы&lt;br /&gt;
&lt;br /&gt;
'''Поиск'''&lt;br /&gt;
 '''Item''' search('''Item''' key)&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m)&lt;br /&gt;
         '''if''' table[x] != ''null''&lt;br /&gt;
            '''if''' table[x].key == key&lt;br /&gt;
               '''return''' table[x]&lt;br /&gt;
            '''else'''&lt;br /&gt;
               '''return''' ''null''&lt;br /&gt;
      x = (x + y) '''mod''' m   &lt;br /&gt;
      '''return''' ''null''&lt;br /&gt;
&lt;br /&gt;
===Реализация с удалением===&lt;br /&gt;
Что бы наша хеш-таблица поддерживала удаление, требуется добавить массив &amp;lt;tex&amp;gt;deleted&amp;lt;/tex&amp;gt; типов &amp;lt;tex&amp;gt;bool&amp;lt;/tex&amp;gt;, равный по величине массиву &amp;lt;tex&amp;gt;table&amp;lt;/tex&amp;gt;. Теперь при удалении мы просто будем помечать наш объект ''как удалённый'', а при добавлении как ''не удалённый'' и замещать новым добавляемым объектом. При поиске, помимо равенства ключей, мы смотрим, удалён ли элемент, если да, то идём дальше.&lt;br /&gt;
&lt;br /&gt;
'''Вставка'''&lt;br /&gt;
 '''function''' add('''Item''' item)&lt;br /&gt;
      x = h1(item.key)&lt;br /&gt;
      y = h2(item.key)&lt;br /&gt;
      '''for''' (i = 0..m)   	&lt;br /&gt;
         '''if''' table[x] == '''null''' '''or''' deleted[x]&lt;br /&gt;
            table[x] = item&lt;br /&gt;
            deleted[x] = '''false'''&lt;br /&gt;
            '''return'''      &lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      table.resize()&amp;lt;span style=&amp;quot;color:Green&amp;quot;&amp;gt;// ошибка, требуется увеличить размер таблицы&lt;br /&gt;
'''Поиск'''&lt;br /&gt;
 '''Item''' search('''Item''' key)&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m) &lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key '''and''' !deleted[x]&lt;br /&gt;
               '''return''' table[x]&lt;br /&gt;
         '''else'''&lt;br /&gt;
            '''return''' '''null'''&lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      '''return''' '''null'''&lt;br /&gt;
&lt;br /&gt;
'''Удаление'''&lt;br /&gt;
 '''function''' remove('''Item''' key)&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m)&lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key&lt;br /&gt;
               deleted[x] = '''true'''&lt;br /&gt;
         '''else''' &lt;br /&gt;
            '''return'''&lt;br /&gt;
      x = (x + y) '''mod''' m&lt;br /&gt;
&lt;br /&gt;
== Разрешение коллизий в Java 8==&lt;br /&gt;
В Java 8 для разрешения коллизий используется модифицированный метод цепочек. Суть его заключается в том, что когда количество элементов в корзине превышает определенное значение, данный корзина переходит от использования связного списка к использованию сбалансированного дерева. Такой подход позволяет улучшить производительность с &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt;.  Данный способ используется в таких коллекциях как HashMap, LinkedHashMap и ConcurrentHashMap.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Hashing_in_Java8.png|500px|Хеширование в Java 8.]]&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
* [[Хеширование]]&lt;br /&gt;
* [[Хеширование_кукушки|Хеширование кукушки]]&lt;br /&gt;
* [[Идеальное_хеширование|Идеальное хеширование]]&lt;br /&gt;
&lt;br /&gt;
== Источники информации ==&lt;br /&gt;
* Бакнелл Дж. М. «Фундаментальные алгоритмы и структуры данных в Delphi», 2003&lt;br /&gt;
* Кормен, Томас Х., Лейзерсон, Чарльз И., Ривест, Рональд Л., Штайн Клиффорд «Алгоритмы: построение и анализ», 2-е издание. Пер. с англ. — М.:Издательский дом &amp;quot;Вильямс&amp;quot;, 2010.— Парал. тит. англ. — ISBN 978-5-8459-0857-5 (рус.)&lt;br /&gt;
* Дональд Кнут. «Искусство программирования, том 3. Сортировка и поиск» {{---}} «Вильямс», 2007 г.{{---}} ISBN 0-201-89685-0&lt;br /&gt;
* Седжвик Р. «Фундаментальные алгоритмы на C. Части 1-4. Анализ. Структуры данных. Сортировка. Поиск», 2003&lt;br /&gt;
* [http://openjdk.java.net/jeps/180 Handle Frequent HashMap Collisions with Balanced Trees]&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Double_hashing Wikipedia {{---}} Double_hashing]&lt;br /&gt;
* [http://ru.wikipedia.org/wiki/%D0%A5%D0%B5%D1%88-%D1%82%D0%B0%D0%B1%D0%BB%D0%B8%D1%86%D0%B0 Разрешение коллизий]&lt;br /&gt;
* [http://rain.ifmo.ru/cat/view.php/vis/hashtables/hash-2001-2 Пример хеш таблицы]&lt;br /&gt;
* [http://research.cs.vt.edu/AVresearch/hashing/double.php Пример хеш таблицы с двойным хешированием]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория: Хеширование]]&lt;br /&gt;
[[Категория: Структуры данных]]&lt;/div&gt;</summary>
		<author><name>Kozichuk</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%A0%D0%B0%D0%B7%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BA%D0%BE%D0%BB%D0%BB%D0%B8%D0%B7%D0%B8%D0%B9&amp;diff=47239</id>
		<title>Разрешение коллизий</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A0%D0%B0%D0%B7%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BA%D0%BE%D0%BB%D0%BB%D0%B8%D0%B7%D0%B8%D0%B9&amp;diff=47239"/>
				<updated>2015-06-02T15:30:27Z</updated>
		
		<summary type="html">&lt;p&gt;Kozichuk: /* Удаление элемента без пометок */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Разрешение [[Хеш-таблица|коллизий]]''' (англ. collision resolution) в [[Хеш-таблица|хеш-таблице]], задача, решаемая несколькими способами: метод цепочек, открытая адресация и т.д. Очень важно сводить количество коллизий к минимуму, так как это увеличивает время работы с хеш-таблицами. &lt;br /&gt;
&lt;br /&gt;
== Разрешение коллизий с помощью цепочек ==&lt;br /&gt;
[[Файл:open_hash.png|thumb|380px|right|Разрешение коллизий при помощи цепочек.]]&lt;br /&gt;
Каждая ячейка &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; массива &amp;lt;tex&amp;gt;H&amp;lt;/tex&amp;gt; содержит указатель на начало [[Список|списка]] всех элементов, хеш-код которых равен &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, либо указывает на их отсутствие. Коллизии приводят к тому, что появляются списки размером больше одного элемента.&lt;br /&gt;
&lt;br /&gt;
В зависимости от того нужна ли нам уникальность значений операции вставки у нас будет работать за разное время. Если не важна, то мы используем список, время вставки в который будет в худшем случае равна &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;. Иначе мы реализуем Set. В таком случае вставка элемента в худшем случае будет выполнена за &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Время работы поиска в наихудшем случае пропорционально длине списка, а если все &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; ключей захешировались в одну и ту же ячейку (создав список длиной &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;) время поиска будет равно &amp;lt;tex&amp;gt;\Theta(n)&amp;lt;/tex&amp;gt; плюс время вычисления хеш-функции, что ничуть не лучше, чем использование связного списка для хранения всех &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; элементов.&lt;br /&gt;
&lt;br /&gt;
Удаления элемента может быть выполнено за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;, как и вставка, при использовании двухсвязного списка.&lt;br /&gt;
&lt;br /&gt;
== Линейное разрешение коллизий ==&lt;br /&gt;
[[Файл:close_hash.png|thumb|380px|right|Пример хеш-таблицы с открытой адресацией и линейным пробированием.]]&lt;br /&gt;
Все элементы хранятся непосредственно в хеш-таблице, без использования связных списков. В отличии от хеширования с цепочками, при использовании этого метода может возникнуть ситуация, когда хеш-таблица окажется полностью заполненной, следовательно будет невозможно добавлять в неё новые элементы. Так что при возникновении такой ситуации решением может быть динамическое увеличение размера хеш-таблицы, с одновременной её перестройкой.&lt;br /&gt;
&lt;br /&gt;
=== Стратегии поиска ===&lt;br /&gt;
&lt;br /&gt;
''' Последовательный поиск '''&lt;br /&gt;
&lt;br /&gt;
При попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt;i+1, i+2, i+3&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку. В неё и запишем элемент.&lt;br /&gt;
&lt;br /&gt;
[[Файл:hashtables1.png|400px|Последовательный поиск, частный случай линейного поиска.]]&lt;br /&gt;
&lt;br /&gt;
''' Линейный поиск '''&lt;br /&gt;
&lt;br /&gt;
Выбираем шаг &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;. При попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt;i+(1 \cdot q), i+(2 \cdot q), i+(3 \cdot q)&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку. В неё и запишем элемент.&lt;br /&gt;
По сути последовательный поиск - частный случай линейного, где &amp;lt;tex&amp;gt;q=1&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Hashtables56.PNG|400px|Линейный поиск с шагом q.]]&lt;br /&gt;
&lt;br /&gt;
''' Квадратичный поиск '''&lt;br /&gt;
&lt;br /&gt;
Шаг &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; не фиксирован, а изменяется квадратично: &amp;lt;tex&amp;gt;q = 1,4,9,16...&amp;lt;/tex&amp;gt;. Соответственно при попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt; i+1, i+4, i+9&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку.&lt;br /&gt;
&lt;br /&gt;
[[Файл:hashtables3.png|400px|Квадратичный поиск.]]&lt;br /&gt;
&lt;br /&gt;
=== Проверка наличия элемента в таблице===&lt;br /&gt;
&lt;br /&gt;
Проверка осуществляется аналогично добавлению: мы проверяем ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; и другие, в соответствии с выбранной стратегией, пока не найдём искомый элемент или свободную ячейку.&lt;br /&gt;
&lt;br /&gt;
При поиске элемента может получится так, что мы дойдём до конца таблицы. Обычно поиск продолжается, начиная с другого конца, пока мы не придём в ту ячейку, откуда начинался поиск.&lt;br /&gt;
&lt;br /&gt;
=== Проблемы данных стратегий ===&lt;br /&gt;
&lt;br /&gt;
Проблем две — крайне нетривиальное удаление элемента из таблицы и образование кластеров  — последовательностей занятых ячеек.&lt;br /&gt;
&lt;br /&gt;
Кластеризация замедляет все операции с хеш-таблицей: при добавлении требуется перебирать всё больше элементов, при проверке тоже. Чем больше в таблице элементов, тем больше в ней кластеры и тем выше вероятность того, что добавляемый элемент попадёт в кластер.&lt;br /&gt;
Для защиты от кластеризации используется двойное хеширование и [[Хеширование кукушки|хеширование кукушки]].&lt;br /&gt;
&lt;br /&gt;
=== Удаление элемента без пометок ===&lt;br /&gt;
&lt;br /&gt;
Рассуждение будет описывать случай с линейным поиском хеша. Будем при удалении элемента сдвигать всё последующие на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; позиций назад. При этом:&lt;br /&gt;
* если в цепочке встречается элемент с другим хешем, то он должен остаться на своём месте (такая ситуация может возникнуть если оставшаяся часть цепочки была добавлена позже этого элемента)&lt;br /&gt;
* в цепочке не должно оставаться &amp;quot;дырок&amp;quot;, тогда любой элемент с данным хешем будет доступен из начала цепи&lt;br /&gt;
&lt;br /&gt;
Учитывая это будем действовать следующим образом: при поиске следующего элемента цепочки будем пропускать все ячейки с другим значением хеша, первый найденный элемент копировать в текущую ячейку, и затем рекурсивно его удалять. Если такой следующей ячейки нет, то текущий элемент можно просто удалить, сторонние цепочки при этом не разрушатся (чего нельзя сказать про случай квадратичного поиска).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
''' Псевдокод '''&lt;br /&gt;
&lt;br /&gt;
 '''function''' delete('''Item''' i) &lt;br /&gt;
      j = i + q&lt;br /&gt;
      '''while''' table[j] == ''null'' '''or''' table[j].key != table[i].key&lt;br /&gt;
         '''if''' table[j] == ''null''&lt;br /&gt;
            table[i] = ''null''&lt;br /&gt;
            '''return'''&lt;br /&gt;
         j += q&lt;br /&gt;
      table[i] = table[j]&lt;br /&gt;
      delete(j)    &lt;br /&gt;
&lt;br /&gt;
Хеш-таблицу считаем зацикленной&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Утверждение&lt;br /&gt;
|about=о времени работы&lt;br /&gt;
|statement=Асимптотически время работы &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\mathrm{find}&amp;lt;/tex&amp;gt; совпадают&lt;br /&gt;
|proof=&lt;br /&gt;
Заметим что указатель &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; в каждой итерации перемещается вперёд на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; (с учётом рекурсивных вызовов &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt;). То есть этот алгоритм последовательно пройдёт по цепочке от удаляемого элемента до последнего {{---}} с учётом вызова &amp;lt;tex&amp;gt;\mathrm{find}&amp;lt;/tex&amp;gt; собственно для нахождения удаляемого элемента, мы посетим все ячейки цепи.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Вариант с зацикливанием мы не рассматриваем, поскольку если &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; взаимнопросто с размером хеш-таблицы, то для зацикливания в ней вообще не должно быть свободных позиций&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Теперь докажем почему этот алгоритм работает. Собственно нам требуется сохранение трёх условий.&lt;br /&gt;
* В редактируемой цепи не остаётся дырок&lt;br /&gt;
Докажем по индукции. Если на данной итерации мы просто удаляем элемент (база), то после него ничего нет, всё верно. Если же нет, то вызванный в конце &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt; (см. псевдокод) заметёт созданную дыру (скопированный элемент), и сам, по предположению, новых не создаст.&lt;br /&gt;
* Элементы, которые уже на своих местах, не должны быть сдвинуты.&lt;br /&gt;
Это учтено.&lt;br /&gt;
* В других цепочках не появятся дыры&lt;br /&gt;
Противное возможно только в том случае, если какой-то элемент был действительно удалён. Удаляем мы только последнюю ячейку в цепи, и если бы на её месте возникла дыра для сторонней цепочки, это бы означало что элемент, стоящий на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; позиций назад, одновременно принадлежал нашей и другой цепочкам, что невозможно.&lt;br /&gt;
&lt;br /&gt;
==Двойное хеширование==&lt;br /&gt;
'''Двойное хеширование''' (англ. double hashing) {{---}} метод борьбы с коллизиями, возникающими при открытой адресации, основанный на использовании двух хеш-функций для построения различных последовательностей исследования хеш-таблицы.&lt;br /&gt;
&lt;br /&gt;
===Принцип двойного хеширования===&lt;br /&gt;
При двойном хешировании используются две независимые хеш-функции &amp;lt;tex&amp;gt; h_1(k) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; h_2(k) &amp;lt;/tex&amp;gt;. Пусть &amp;lt;tex&amp;gt; k &amp;lt;/tex&amp;gt; {{---}} это наш ключ, &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt; {{---}} размер нашей таблицы, &amp;lt;tex&amp;gt;n \bmod m &amp;lt;/tex&amp;gt; {{---}} остаток от деления &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt; на &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, тогда сначала исследуется ячейка с адресом &amp;lt;tex&amp;gt; h_1(k) &amp;lt;/tex&amp;gt;, если она уже занята, то рассматривается &amp;lt;tex&amp;gt; (h_1(k) +  h_2(k)) \bmod m &amp;lt;/tex&amp;gt;, затем &amp;lt;tex&amp;gt; (h_1(k) +  2 \cdot h_2(k)) \bmod m &amp;lt;/tex&amp;gt; и так далее. В общем случае идёт проверка последовательности ячеек &amp;lt;tex&amp;gt; (h_1(k) +  i \cdot h_2(k)) \bmod m &amp;lt;/tex&amp;gt; где &amp;lt;tex&amp;gt;  i = (0, 1, \; ... \;,  m - 1) &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, операции вставки, удаления и поиска в лучшем случае выполняются за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;, в худшем {{---}} за &amp;lt;tex&amp;gt;O(m)&amp;lt;/tex&amp;gt;, что не отличается от обычного [[Открытое_и_закрытое_хеширование#Линейное разрешение коллизий|линейного разрешения коллизий]].&lt;br /&gt;
Однако в среднем, при грамотном выборе хеш-функций, двойное хеширование будет выдавать лучшие результаты, за счёт того, что вероятность совпадения значений сразу двух независимых хеш-функций ниже, чем одной.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt;\forall x \neq y \; \exists h_1,h_2 : p(h_1(x)=h_1(y))&amp;gt; p((h_1(x)=h_1(y)) \land (h_2(x)=h_2(y)))&amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Выбор хеш-функций===&lt;br /&gt;
&amp;lt;tex&amp;gt; h_1 &amp;lt;/tex&amp;gt; может быть обычной хеш-функцией. Однако чтобы последовательность исследования могла охватить всю таблицу, &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; должна возвращать значения:&lt;br /&gt;
*не равные &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
*независимые  от &amp;lt;tex&amp;gt; h_1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
*взаимно простые с величиной хеш-таблицы&lt;br /&gt;
&lt;br /&gt;
Есть два удобных способа это сделать. Первый состоит в том, что в качестве размера таблицы используется простое число, а &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; возвращает натуральные числа, меньшие &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;. Второй {{---}} размер таблицы является степенью двойки, а &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; возвращает нечетные значения.&lt;br /&gt;
&lt;br /&gt;
Например, если размер таблицы равен &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, то в качестве &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; можно использовать функцию вида &amp;lt;tex&amp;gt; h_2(k) = k \bmod (m-1) + 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Файл: Вставка при двойном хэшировании.svg.jpeg|thumb|right|Вставка при двойном хешировании]]&lt;br /&gt;
&lt;br /&gt;
===Пример===&lt;br /&gt;
&lt;br /&gt;
Показана хеш-таблица размером 13 ячеек, в которой используются вспомогательные функции:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h(k,i) = (h_1(k) + i \cdot h_2(k)) \bmod 13 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h_1(k) = k \bmod 13 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h_2(k) = 1 + k \bmod 11 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Мы хотим вставить ключ 14. Изначально &amp;lt;tex&amp;gt; i = 0 &amp;lt;/tex&amp;gt;. Тогда &amp;lt;tex&amp;gt; h(14,0) = (h_1(14) + 0\cdot h_2(14)) \bmod 13 = 1 &amp;lt;/tex&amp;gt;. Но ячейка с индексом 1 занята, поэтому увеличиваем &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; на 1 и пересчитываем значение хеш-функции. Делаем так, пока не дойдем до пустой ячейки. При &amp;lt;tex&amp;gt; i = 2 &amp;lt;/tex&amp;gt; получаем &amp;lt;tex&amp;gt; h(14,2) = (h_1(14) + 2\cdot h_2(14)) \bmod 13 = 9 &amp;lt;/tex&amp;gt;. Ячейка с номером 9 свободна, значит записываем туда наш ключ.&lt;br /&gt;
&lt;br /&gt;
Таким образом, основная особенность двойного хеширования состоит в том, что при различных &amp;lt;tex&amp;gt; k &amp;lt;/tex&amp;gt; пара &amp;lt;tex&amp;gt; (h_1(k),h_2(k)) &amp;lt;/tex&amp;gt; дает различные последовательности ячеек для исследования.&lt;br /&gt;
&lt;br /&gt;
===Простая реализация===&lt;br /&gt;
Пусть у нас есть некоторый объект &amp;lt;tex&amp;gt; item &amp;lt;/tex&amp;gt;, в котором определено поле &amp;lt;tex&amp;gt; key &amp;lt;/tex&amp;gt;, от которого можно вычислить хеш-функции &amp;lt;tex&amp;gt; h_1(key)&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; h_2(key) &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Так же у нас есть таблица &amp;lt;tex&amp;gt; table &amp;lt;/tex&amp;gt; величиной &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, состоящая из объектов типа &amp;lt;tex&amp;gt; item &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
'''Вставка'''&lt;br /&gt;
 '''function''' add('''Item''' item)&lt;br /&gt;
      x = h1(item.key)&lt;br /&gt;
      y = h2(item.key)&lt;br /&gt;
         '''for''' (i = 0..m)    	&lt;br /&gt;
            '''if''' table[x] == ''null''&lt;br /&gt;
               table[x] = item&lt;br /&gt;
            '''return'''      &lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      table.resize()&amp;lt;span style=&amp;quot;color:Green&amp;quot;&amp;gt;// ошибка, требуется увеличить размер таблицы&lt;br /&gt;
&lt;br /&gt;
'''Поиск'''&lt;br /&gt;
 '''Item''' search('''Item''' key)&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m)&lt;br /&gt;
         '''if''' table[x] != ''null''&lt;br /&gt;
            '''if''' table[x].key == key&lt;br /&gt;
               '''return''' table[x]&lt;br /&gt;
            '''else'''&lt;br /&gt;
               '''return''' ''null''&lt;br /&gt;
      x = (x + y) '''mod''' m   &lt;br /&gt;
      '''return''' ''null''&lt;br /&gt;
&lt;br /&gt;
===Реализация с удалением===&lt;br /&gt;
Что бы наша хеш-таблица поддерживала удаление, требуется добавить массив &amp;lt;tex&amp;gt;deleted&amp;lt;/tex&amp;gt; типов &amp;lt;tex&amp;gt;bool&amp;lt;/tex&amp;gt;, равный по величине массиву &amp;lt;tex&amp;gt;table&amp;lt;/tex&amp;gt;. Теперь при удалении мы просто будем помечать наш объект ''как удалённый'', а при добавлении как ''не удалённый'' и замещать новым добавляемым объектом. При поиске, помимо равенства ключей, мы смотрим, удалён ли элемент, если да, то идём дальше.&lt;br /&gt;
&lt;br /&gt;
'''Вставка'''&lt;br /&gt;
 '''function''' add('''Item''' item)&lt;br /&gt;
      x = h1(item.key)&lt;br /&gt;
      y = h2(item.key)&lt;br /&gt;
      '''for''' (i = 0..m)   	&lt;br /&gt;
         '''if''' table[x] == '''null''' '''or''' deleted[x]&lt;br /&gt;
            table[x] = item&lt;br /&gt;
            deleted[x] = '''false'''&lt;br /&gt;
            '''return'''      &lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      table.resize()&amp;lt;span style=&amp;quot;color:Green&amp;quot;&amp;gt;// ошибка, требуется увеличить размер таблицы&lt;br /&gt;
'''Поиск'''&lt;br /&gt;
 '''Item''' search('''Item''' key)&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m) &lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key '''and''' !deleted[x]&lt;br /&gt;
               '''return''' table[x]&lt;br /&gt;
         '''else'''&lt;br /&gt;
            '''return''' '''null'''&lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      '''return''' '''null'''&lt;br /&gt;
&lt;br /&gt;
'''Удаление'''&lt;br /&gt;
 '''function''' remove('''Item''' key)&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m)&lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key&lt;br /&gt;
               deleted[x] = '''true'''&lt;br /&gt;
         '''else''' &lt;br /&gt;
            '''return'''&lt;br /&gt;
      x = (x + y) '''mod''' m&lt;br /&gt;
&lt;br /&gt;
== Разрешение коллизий в Java 8==&lt;br /&gt;
В Java 8 для разрешения коллизий используется модифицированный метод цепочек. Суть его заключается в том, что когда количество элементов в корзине превышает определенное значение, данный корзина переходит от использования связного списка к использованию сбалансированного дерева. Такой подход позволяет улучшить производительность с &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt;.  Данный способ используется в таких коллекциях как HashMap, LinkedHashMap и ConcurrentHashMap.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Hashing_in_Java8.png|500px|Хеширование в Java 8.]]&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
* [[Хеширование]]&lt;br /&gt;
* [[Хеширование_кукушки|Хеширование кукушки]]&lt;br /&gt;
* [[Идеальное_хеширование|Идеальное хеширование]]&lt;br /&gt;
&lt;br /&gt;
== Источники информации ==&lt;br /&gt;
* Бакнелл Дж. М. «Фундаментальные алгоритмы и структуры данных в Delphi», 2003&lt;br /&gt;
* Кормен, Томас Х., Лейзерсон, Чарльз И., Ривест, Рональд Л., Штайн Клиффорд «Алгоритмы: построение и анализ», 2-е издание. Пер. с англ. — М.:Издательский дом &amp;quot;Вильямс&amp;quot;, 2010.— Парал. тит. англ. — ISBN 978-5-8459-0857-5 (рус.)&lt;br /&gt;
* Дональд Кнут. «Искусство программирования, том 3. Сортировка и поиск» {{---}} «Вильямс», 2007 г.{{---}} ISBN 0-201-89685-0&lt;br /&gt;
* Седжвик Р. «Фундаментальные алгоритмы на C. Части 1-4. Анализ. Структуры данных. Сортировка. Поиск», 2003&lt;br /&gt;
* [http://openjdk.java.net/jeps/180 Handle Frequent HashMap Collisions with Balanced Trees]&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Double_hashing Wikipedia {{---}} Double_hashing]&lt;br /&gt;
* [http://ru.wikipedia.org/wiki/%D0%A5%D0%B5%D1%88-%D1%82%D0%B0%D0%B1%D0%BB%D0%B8%D1%86%D0%B0 Разрешение коллизий]&lt;br /&gt;
* [http://rain.ifmo.ru/cat/view.php/vis/hashtables/hash-2001-2 Пример хеш таблицы]&lt;br /&gt;
* [http://research.cs.vt.edu/AVresearch/hashing/double.php Пример хеш таблицы с двойным хешированием]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория: Хеширование]]&lt;/div&gt;</summary>
		<author><name>Kozichuk</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%A0%D0%B0%D0%B7%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BA%D0%BE%D0%BB%D0%BB%D0%B8%D0%B7%D0%B8%D0%B9&amp;diff=47237</id>
		<title>Разрешение коллизий</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A0%D0%B0%D0%B7%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BA%D0%BE%D0%BB%D0%BB%D0%B8%D0%B7%D0%B8%D0%B9&amp;diff=47237"/>
				<updated>2015-06-02T15:29:46Z</updated>
		
		<summary type="html">&lt;p&gt;Kozichuk: /* Простая реализация */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Разрешение [[Хеш-таблица|коллизий]]''' (англ. collision resolution) в [[Хеш-таблица|хеш-таблице]], задача, решаемая несколькими способами: метод цепочек, открытая адресация и т.д. Очень важно сводить количество коллизий к минимуму, так как это увеличивает время работы с хеш-таблицами. &lt;br /&gt;
&lt;br /&gt;
== Разрешение коллизий с помощью цепочек ==&lt;br /&gt;
[[Файл:open_hash.png|thumb|380px|right|Разрешение коллизий при помощи цепочек.]]&lt;br /&gt;
Каждая ячейка &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; массива &amp;lt;tex&amp;gt;H&amp;lt;/tex&amp;gt; содержит указатель на начало [[Список|списка]] всех элементов, хеш-код которых равен &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, либо указывает на их отсутствие. Коллизии приводят к тому, что появляются списки размером больше одного элемента.&lt;br /&gt;
&lt;br /&gt;
В зависимости от того нужна ли нам уникальность значений операции вставки у нас будет работать за разное время. Если не важна, то мы используем список, время вставки в который будет в худшем случае равна &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;. Иначе мы реализуем Set. В таком случае вставка элемента в худшем случае будет выполнена за &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Время работы поиска в наихудшем случае пропорционально длине списка, а если все &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; ключей захешировались в одну и ту же ячейку (создав список длиной &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;) время поиска будет равно &amp;lt;tex&amp;gt;\Theta(n)&amp;lt;/tex&amp;gt; плюс время вычисления хеш-функции, что ничуть не лучше, чем использование связного списка для хранения всех &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; элементов.&lt;br /&gt;
&lt;br /&gt;
Удаления элемента может быть выполнено за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;, как и вставка, при использовании двухсвязного списка.&lt;br /&gt;
&lt;br /&gt;
== Линейное разрешение коллизий ==&lt;br /&gt;
[[Файл:close_hash.png|thumb|380px|right|Пример хеш-таблицы с открытой адресацией и линейным пробированием.]]&lt;br /&gt;
Все элементы хранятся непосредственно в хеш-таблице, без использования связных списков. В отличии от хеширования с цепочками, при использовании этого метода может возникнуть ситуация, когда хеш-таблица окажется полностью заполненной, следовательно будет невозможно добавлять в неё новые элементы. Так что при возникновении такой ситуации решением может быть динамическое увеличение размера хеш-таблицы, с одновременной её перестройкой.&lt;br /&gt;
&lt;br /&gt;
=== Стратегии поиска ===&lt;br /&gt;
&lt;br /&gt;
''' Последовательный поиск '''&lt;br /&gt;
&lt;br /&gt;
При попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt;i+1, i+2, i+3&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку. В неё и запишем элемент.&lt;br /&gt;
&lt;br /&gt;
[[Файл:hashtables1.png|400px|Последовательный поиск, частный случай линейного поиска.]]&lt;br /&gt;
&lt;br /&gt;
''' Линейный поиск '''&lt;br /&gt;
&lt;br /&gt;
Выбираем шаг &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;. При попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt;i+(1 \cdot q), i+(2 \cdot q), i+(3 \cdot q)&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку. В неё и запишем элемент.&lt;br /&gt;
По сути последовательный поиск - частный случай линейного, где &amp;lt;tex&amp;gt;q=1&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Hashtables56.PNG|400px|Линейный поиск с шагом q.]]&lt;br /&gt;
&lt;br /&gt;
''' Квадратичный поиск '''&lt;br /&gt;
&lt;br /&gt;
Шаг &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; не фиксирован, а изменяется квадратично: &amp;lt;tex&amp;gt;q = 1,4,9,16...&amp;lt;/tex&amp;gt;. Соответственно при попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt; i+1, i+4, i+9&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку.&lt;br /&gt;
&lt;br /&gt;
[[Файл:hashtables3.png|400px|Квадратичный поиск.]]&lt;br /&gt;
&lt;br /&gt;
=== Проверка наличия элемента в таблице===&lt;br /&gt;
&lt;br /&gt;
Проверка осуществляется аналогично добавлению: мы проверяем ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; и другие, в соответствии с выбранной стратегией, пока не найдём искомый элемент или свободную ячейку.&lt;br /&gt;
&lt;br /&gt;
При поиске элемента может получится так, что мы дойдём до конца таблицы. Обычно поиск продолжается, начиная с другого конца, пока мы не придём в ту ячейку, откуда начинался поиск.&lt;br /&gt;
&lt;br /&gt;
=== Проблемы данных стратегий ===&lt;br /&gt;
&lt;br /&gt;
Проблем две — крайне нетривиальное удаление элемента из таблицы и образование кластеров  — последовательностей занятых ячеек.&lt;br /&gt;
&lt;br /&gt;
Кластеризация замедляет все операции с хеш-таблицей: при добавлении требуется перебирать всё больше элементов, при проверке тоже. Чем больше в таблице элементов, тем больше в ней кластеры и тем выше вероятность того, что добавляемый элемент попадёт в кластер.&lt;br /&gt;
Для защиты от кластеризации используется двойное хеширование и [[Хеширование кукушки|хеширование кукушки]].&lt;br /&gt;
&lt;br /&gt;
=== Удаление элемента без пометок ===&lt;br /&gt;
&lt;br /&gt;
Рассуждение будет описывать случай с линейным поиском хеша. Будем при удалении элемента сдвигать всё последующие на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; позиций назад. При этом:&lt;br /&gt;
* если в цепочке встречается элемент с другим хешем, то он должен остаться на своём месте (такая ситуация может возникнуть если оставшаяся часть цепочки была добавлена позже этого элемента)&lt;br /&gt;
* в цепочке не должно оставаться &amp;quot;дырок&amp;quot;, тогда любой элемент с данным хешем будет доступен из начала цепи&lt;br /&gt;
&lt;br /&gt;
Учитывая это будем действовать следующим образом: при поиске следующего элемента цепочки будем пропускать все ячейки с другим значением хеша, первый найденный элемент копировать в текущую ячейку, и затем рекурсивно его удалять. Если такой следующей ячейки нет, то текущий элемент можно просто удалить, сторонние цепочки при этом не разрушатся (чего нельзя сказать про случай квадратичного поиска).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
''' Псевдокод '''&lt;br /&gt;
&lt;br /&gt;
 '''function''' delete('''Item''' i) &lt;br /&gt;
      j = i + q&lt;br /&gt;
      '''while''' table[j] == '''null''' '''or''' table[j].key != table[i].key&lt;br /&gt;
         '''if''' table[j] == '''null'''&lt;br /&gt;
            table[i] = '''null'''&lt;br /&gt;
            '''return'''&lt;br /&gt;
         j += q&lt;br /&gt;
      table[i] = table[j]&lt;br /&gt;
      delete(j)    &lt;br /&gt;
&lt;br /&gt;
Хеш-таблицу считаем зацикленной&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Утверждение&lt;br /&gt;
|about=о времени работы&lt;br /&gt;
|statement=Асимптотически время работы &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\mathrm{find}&amp;lt;/tex&amp;gt; совпадают&lt;br /&gt;
|proof=&lt;br /&gt;
Заметим что указатель &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; в каждой итерации перемещается вперёд на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; (с учётом рекурсивных вызовов &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt;). То есть этот алгоритм последовательно пройдёт по цепочке от удаляемого элемента до последнего {{---}} с учётом вызова &amp;lt;tex&amp;gt;\mathrm{find}&amp;lt;/tex&amp;gt; собственно для нахождения удаляемого элемента, мы посетим все ячейки цепи.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Вариант с зацикливанием мы не рассматриваем, поскольку если &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; взаимнопросто с размером хеш-таблицы, то для зацикливания в ней вообще не должно быть свободных позиций&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Теперь докажем почему этот алгоритм работает. Собственно нам требуется сохранение трёх условий.&lt;br /&gt;
* В редактируемой цепи не остаётся дырок&lt;br /&gt;
Докажем по индукции. Если на данной итерации мы просто удаляем элемент (база), то после него ничего нет, всё верно. Если же нет, то вызванный в конце &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt; (см. псевдокод) заметёт созданную дыру (скопированный элемент), и сам, по предположению, новых не создаст.&lt;br /&gt;
* Элементы, которые уже на своих местах, не должны быть сдвинуты.&lt;br /&gt;
Это учтено.&lt;br /&gt;
* В других цепочках не появятся дыры&lt;br /&gt;
Противное возможно только в том случае, если какой-то элемент был действительно удалён. Удаляем мы только последнюю ячейку в цепи, и если бы на её месте возникла дыра для сторонней цепочки, это бы означало что элемент, стоящий на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; позиций назад, одновременно принадлежал нашей и другой цепочкам, что невозможно.&lt;br /&gt;
&lt;br /&gt;
==Двойное хеширование==&lt;br /&gt;
'''Двойное хеширование''' (англ. double hashing) {{---}} метод борьбы с коллизиями, возникающими при открытой адресации, основанный на использовании двух хеш-функций для построения различных последовательностей исследования хеш-таблицы.&lt;br /&gt;
&lt;br /&gt;
===Принцип двойного хеширования===&lt;br /&gt;
При двойном хешировании используются две независимые хеш-функции &amp;lt;tex&amp;gt; h_1(k) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; h_2(k) &amp;lt;/tex&amp;gt;. Пусть &amp;lt;tex&amp;gt; k &amp;lt;/tex&amp;gt; {{---}} это наш ключ, &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt; {{---}} размер нашей таблицы, &amp;lt;tex&amp;gt;n \bmod m &amp;lt;/tex&amp;gt; {{---}} остаток от деления &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt; на &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, тогда сначала исследуется ячейка с адресом &amp;lt;tex&amp;gt; h_1(k) &amp;lt;/tex&amp;gt;, если она уже занята, то рассматривается &amp;lt;tex&amp;gt; (h_1(k) +  h_2(k)) \bmod m &amp;lt;/tex&amp;gt;, затем &amp;lt;tex&amp;gt; (h_1(k) +  2 \cdot h_2(k)) \bmod m &amp;lt;/tex&amp;gt; и так далее. В общем случае идёт проверка последовательности ячеек &amp;lt;tex&amp;gt; (h_1(k) +  i \cdot h_2(k)) \bmod m &amp;lt;/tex&amp;gt; где &amp;lt;tex&amp;gt;  i = (0, 1, \; ... \;,  m - 1) &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, операции вставки, удаления и поиска в лучшем случае выполняются за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;, в худшем {{---}} за &amp;lt;tex&amp;gt;O(m)&amp;lt;/tex&amp;gt;, что не отличается от обычного [[Открытое_и_закрытое_хеширование#Линейное разрешение коллизий|линейного разрешения коллизий]].&lt;br /&gt;
Однако в среднем, при грамотном выборе хеш-функций, двойное хеширование будет выдавать лучшие результаты, за счёт того, что вероятность совпадения значений сразу двух независимых хеш-функций ниже, чем одной.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt;\forall x \neq y \; \exists h_1,h_2 : p(h_1(x)=h_1(y))&amp;gt; p((h_1(x)=h_1(y)) \land (h_2(x)=h_2(y)))&amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Выбор хеш-функций===&lt;br /&gt;
&amp;lt;tex&amp;gt; h_1 &amp;lt;/tex&amp;gt; может быть обычной хеш-функцией. Однако чтобы последовательность исследования могла охватить всю таблицу, &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; должна возвращать значения:&lt;br /&gt;
*не равные &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
*независимые  от &amp;lt;tex&amp;gt; h_1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
*взаимно простые с величиной хеш-таблицы&lt;br /&gt;
&lt;br /&gt;
Есть два удобных способа это сделать. Первый состоит в том, что в качестве размера таблицы используется простое число, а &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; возвращает натуральные числа, меньшие &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;. Второй {{---}} размер таблицы является степенью двойки, а &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; возвращает нечетные значения.&lt;br /&gt;
&lt;br /&gt;
Например, если размер таблицы равен &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, то в качестве &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; можно использовать функцию вида &amp;lt;tex&amp;gt; h_2(k) = k \bmod (m-1) + 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Файл: Вставка при двойном хэшировании.svg.jpeg|thumb|right|Вставка при двойном хешировании]]&lt;br /&gt;
&lt;br /&gt;
===Пример===&lt;br /&gt;
&lt;br /&gt;
Показана хеш-таблица размером 13 ячеек, в которой используются вспомогательные функции:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h(k,i) = (h_1(k) + i \cdot h_2(k)) \bmod 13 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h_1(k) = k \bmod 13 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h_2(k) = 1 + k \bmod 11 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Мы хотим вставить ключ 14. Изначально &amp;lt;tex&amp;gt; i = 0 &amp;lt;/tex&amp;gt;. Тогда &amp;lt;tex&amp;gt; h(14,0) = (h_1(14) + 0\cdot h_2(14)) \bmod 13 = 1 &amp;lt;/tex&amp;gt;. Но ячейка с индексом 1 занята, поэтому увеличиваем &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; на 1 и пересчитываем значение хеш-функции. Делаем так, пока не дойдем до пустой ячейки. При &amp;lt;tex&amp;gt; i = 2 &amp;lt;/tex&amp;gt; получаем &amp;lt;tex&amp;gt; h(14,2) = (h_1(14) + 2\cdot h_2(14)) \bmod 13 = 9 &amp;lt;/tex&amp;gt;. Ячейка с номером 9 свободна, значит записываем туда наш ключ.&lt;br /&gt;
&lt;br /&gt;
Таким образом, основная особенность двойного хеширования состоит в том, что при различных &amp;lt;tex&amp;gt; k &amp;lt;/tex&amp;gt; пара &amp;lt;tex&amp;gt; (h_1(k),h_2(k)) &amp;lt;/tex&amp;gt; дает различные последовательности ячеек для исследования.&lt;br /&gt;
&lt;br /&gt;
===Простая реализация===&lt;br /&gt;
Пусть у нас есть некоторый объект &amp;lt;tex&amp;gt; item &amp;lt;/tex&amp;gt;, в котором определено поле &amp;lt;tex&amp;gt; key &amp;lt;/tex&amp;gt;, от которого можно вычислить хеш-функции &amp;lt;tex&amp;gt; h_1(key)&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; h_2(key) &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Так же у нас есть таблица &amp;lt;tex&amp;gt; table &amp;lt;/tex&amp;gt; величиной &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, состоящая из объектов типа &amp;lt;tex&amp;gt; item &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
'''Вставка'''&lt;br /&gt;
 '''function''' add('''Item''' item)&lt;br /&gt;
      x = h1(item.key)&lt;br /&gt;
      y = h2(item.key)&lt;br /&gt;
         '''for''' (i = 0..m)    	&lt;br /&gt;
            '''if''' table[x] == ''null''&lt;br /&gt;
               table[x] = item&lt;br /&gt;
            '''return'''      &lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      table.resize()&amp;lt;span style=&amp;quot;color:Green&amp;quot;&amp;gt;// ошибка, требуется увеличить размер таблицы&lt;br /&gt;
&lt;br /&gt;
'''Поиск'''&lt;br /&gt;
 '''Item''' search('''Item''' key)&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m)&lt;br /&gt;
         '''if''' table[x] != ''null''&lt;br /&gt;
            '''if''' table[x].key == key&lt;br /&gt;
               '''return''' table[x]&lt;br /&gt;
            '''else'''&lt;br /&gt;
               '''return''' ''null''&lt;br /&gt;
      x = (x + y) '''mod''' m   &lt;br /&gt;
      '''return''' ''null''&lt;br /&gt;
&lt;br /&gt;
===Реализация с удалением===&lt;br /&gt;
Что бы наша хеш-таблица поддерживала удаление, требуется добавить массив &amp;lt;tex&amp;gt;deleted&amp;lt;/tex&amp;gt; типов &amp;lt;tex&amp;gt;bool&amp;lt;/tex&amp;gt;, равный по величине массиву &amp;lt;tex&amp;gt;table&amp;lt;/tex&amp;gt;. Теперь при удалении мы просто будем помечать наш объект ''как удалённый'', а при добавлении как ''не удалённый'' и замещать новым добавляемым объектом. При поиске, помимо равенства ключей, мы смотрим, удалён ли элемент, если да, то идём дальше.&lt;br /&gt;
&lt;br /&gt;
'''Вставка'''&lt;br /&gt;
 '''function''' add('''Item''' item)&lt;br /&gt;
      x = h1(item.key)&lt;br /&gt;
      y = h2(item.key)&lt;br /&gt;
      '''for''' (i = 0..m)   	&lt;br /&gt;
         '''if''' table[x] == '''null''' '''or''' deleted[x]&lt;br /&gt;
            table[x] = item&lt;br /&gt;
            deleted[x] = '''false'''&lt;br /&gt;
            '''return'''      &lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      table.resize()&amp;lt;span style=&amp;quot;color:Green&amp;quot;&amp;gt;// ошибка, требуется увеличить размер таблицы&lt;br /&gt;
'''Поиск'''&lt;br /&gt;
 '''Item''' search('''Item''' key)&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m) &lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key '''and''' !deleted[x]&lt;br /&gt;
               '''return''' table[x]&lt;br /&gt;
         '''else'''&lt;br /&gt;
            '''return''' '''null'''&lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      '''return''' '''null'''&lt;br /&gt;
&lt;br /&gt;
'''Удаление'''&lt;br /&gt;
 '''function''' remove('''Item''' key)&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m)&lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key&lt;br /&gt;
               deleted[x] = '''true'''&lt;br /&gt;
         '''else''' &lt;br /&gt;
            '''return'''&lt;br /&gt;
      x = (x + y) '''mod''' m&lt;br /&gt;
&lt;br /&gt;
== Разрешение коллизий в Java 8==&lt;br /&gt;
В Java 8 для разрешения коллизий используется модифицированный метод цепочек. Суть его заключается в том, что когда количество элементов в корзине превышает определенное значение, данный корзина переходит от использования связного списка к использованию сбалансированного дерева. Такой подход позволяет улучшить производительность с &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt;.  Данный способ используется в таких коллекциях как HashMap, LinkedHashMap и ConcurrentHashMap.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Hashing_in_Java8.png|500px|Хеширование в Java 8.]]&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
* [[Хеширование]]&lt;br /&gt;
* [[Хеширование_кукушки|Хеширование кукушки]]&lt;br /&gt;
* [[Идеальное_хеширование|Идеальное хеширование]]&lt;br /&gt;
&lt;br /&gt;
== Источники информации ==&lt;br /&gt;
* Бакнелл Дж. М. «Фундаментальные алгоритмы и структуры данных в Delphi», 2003&lt;br /&gt;
* Кормен, Томас Х., Лейзерсон, Чарльз И., Ривест, Рональд Л., Штайн Клиффорд «Алгоритмы: построение и анализ», 2-е издание. Пер. с англ. — М.:Издательский дом &amp;quot;Вильямс&amp;quot;, 2010.— Парал. тит. англ. — ISBN 978-5-8459-0857-5 (рус.)&lt;br /&gt;
* Дональд Кнут. «Искусство программирования, том 3. Сортировка и поиск» {{---}} «Вильямс», 2007 г.{{---}} ISBN 0-201-89685-0&lt;br /&gt;
* Седжвик Р. «Фундаментальные алгоритмы на C. Части 1-4. Анализ. Структуры данных. Сортировка. Поиск», 2003&lt;br /&gt;
* [http://openjdk.java.net/jeps/180 Handle Frequent HashMap Collisions with Balanced Trees]&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Double_hashing Wikipedia {{---}} Double_hashing]&lt;br /&gt;
* [http://ru.wikipedia.org/wiki/%D0%A5%D0%B5%D1%88-%D1%82%D0%B0%D0%B1%D0%BB%D0%B8%D1%86%D0%B0 Разрешение коллизий]&lt;br /&gt;
* [http://rain.ifmo.ru/cat/view.php/vis/hashtables/hash-2001-2 Пример хеш таблицы]&lt;br /&gt;
* [http://research.cs.vt.edu/AVresearch/hashing/double.php Пример хеш таблицы с двойным хешированием]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория: Хеширование]]&lt;/div&gt;</summary>
		<author><name>Kozichuk</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%A0%D0%B0%D0%B7%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BA%D0%BE%D0%BB%D0%BB%D0%B8%D0%B7%D0%B8%D0%B9&amp;diff=47236</id>
		<title>Разрешение коллизий</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A0%D0%B0%D0%B7%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BA%D0%BE%D0%BB%D0%BB%D0%B8%D0%B7%D0%B8%D0%B9&amp;diff=47236"/>
				<updated>2015-06-02T15:27:33Z</updated>
		
		<summary type="html">&lt;p&gt;Kozichuk: /* Разрешение коллизий в Java 8 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Разрешение [[Хеш-таблица|коллизий]]''' (англ. collision resolution) в [[Хеш-таблица|хеш-таблице]], задача, решаемая несколькими способами: метод цепочек, открытая адресация и т.д. Очень важно сводить количество коллизий к минимуму, так как это увеличивает время работы с хеш-таблицами. &lt;br /&gt;
&lt;br /&gt;
== Разрешение коллизий с помощью цепочек ==&lt;br /&gt;
[[Файл:open_hash.png|thumb|380px|right|Разрешение коллизий при помощи цепочек.]]&lt;br /&gt;
Каждая ячейка &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; массива &amp;lt;tex&amp;gt;H&amp;lt;/tex&amp;gt; содержит указатель на начало [[Список|списка]] всех элементов, хеш-код которых равен &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, либо указывает на их отсутствие. Коллизии приводят к тому, что появляются списки размером больше одного элемента.&lt;br /&gt;
&lt;br /&gt;
В зависимости от того нужна ли нам уникальность значений операции вставки у нас будет работать за разное время. Если не важна, то мы используем список, время вставки в который будет в худшем случае равна &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;. Иначе мы реализуем Set. В таком случае вставка элемента в худшем случае будет выполнена за &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Время работы поиска в наихудшем случае пропорционально длине списка, а если все &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; ключей захешировались в одну и ту же ячейку (создав список длиной &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;) время поиска будет равно &amp;lt;tex&amp;gt;\Theta(n)&amp;lt;/tex&amp;gt; плюс время вычисления хеш-функции, что ничуть не лучше, чем использование связного списка для хранения всех &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; элементов.&lt;br /&gt;
&lt;br /&gt;
Удаления элемента может быть выполнено за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;, как и вставка, при использовании двухсвязного списка.&lt;br /&gt;
&lt;br /&gt;
== Линейное разрешение коллизий ==&lt;br /&gt;
[[Файл:close_hash.png|thumb|380px|right|Пример хеш-таблицы с открытой адресацией и линейным пробированием.]]&lt;br /&gt;
Все элементы хранятся непосредственно в хеш-таблице, без использования связных списков. В отличии от хеширования с цепочками, при использовании этого метода может возникнуть ситуация, когда хеш-таблица окажется полностью заполненной, следовательно будет невозможно добавлять в неё новые элементы. Так что при возникновении такой ситуации решением может быть динамическое увеличение размера хеш-таблицы, с одновременной её перестройкой.&lt;br /&gt;
&lt;br /&gt;
=== Стратегии поиска ===&lt;br /&gt;
&lt;br /&gt;
''' Последовательный поиск '''&lt;br /&gt;
&lt;br /&gt;
При попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt;i+1, i+2, i+3&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку. В неё и запишем элемент.&lt;br /&gt;
&lt;br /&gt;
[[Файл:hashtables1.png|400px|Последовательный поиск, частный случай линейного поиска.]]&lt;br /&gt;
&lt;br /&gt;
''' Линейный поиск '''&lt;br /&gt;
&lt;br /&gt;
Выбираем шаг &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;. При попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt;i+(1 \cdot q), i+(2 \cdot q), i+(3 \cdot q)&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку. В неё и запишем элемент.&lt;br /&gt;
По сути последовательный поиск - частный случай линейного, где &amp;lt;tex&amp;gt;q=1&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Hashtables56.PNG|400px|Линейный поиск с шагом q.]]&lt;br /&gt;
&lt;br /&gt;
''' Квадратичный поиск '''&lt;br /&gt;
&lt;br /&gt;
Шаг &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; не фиксирован, а изменяется квадратично: &amp;lt;tex&amp;gt;q = 1,4,9,16...&amp;lt;/tex&amp;gt;. Соответственно при попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt; i+1, i+4, i+9&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку.&lt;br /&gt;
&lt;br /&gt;
[[Файл:hashtables3.png|400px|Квадратичный поиск.]]&lt;br /&gt;
&lt;br /&gt;
=== Проверка наличия элемента в таблице===&lt;br /&gt;
&lt;br /&gt;
Проверка осуществляется аналогично добавлению: мы проверяем ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; и другие, в соответствии с выбранной стратегией, пока не найдём искомый элемент или свободную ячейку.&lt;br /&gt;
&lt;br /&gt;
При поиске элемента может получится так, что мы дойдём до конца таблицы. Обычно поиск продолжается, начиная с другого конца, пока мы не придём в ту ячейку, откуда начинался поиск.&lt;br /&gt;
&lt;br /&gt;
=== Проблемы данных стратегий ===&lt;br /&gt;
&lt;br /&gt;
Проблем две — крайне нетривиальное удаление элемента из таблицы и образование кластеров  — последовательностей занятых ячеек.&lt;br /&gt;
&lt;br /&gt;
Кластеризация замедляет все операции с хеш-таблицей: при добавлении требуется перебирать всё больше элементов, при проверке тоже. Чем больше в таблице элементов, тем больше в ней кластеры и тем выше вероятность того, что добавляемый элемент попадёт в кластер.&lt;br /&gt;
Для защиты от кластеризации используется двойное хеширование и [[Хеширование кукушки|хеширование кукушки]].&lt;br /&gt;
&lt;br /&gt;
=== Удаление элемента без пометок ===&lt;br /&gt;
&lt;br /&gt;
Рассуждение будет описывать случай с линейным поиском хеша. Будем при удалении элемента сдвигать всё последующие на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; позиций назад. При этом:&lt;br /&gt;
* если в цепочке встречается элемент с другим хешем, то он должен остаться на своём месте (такая ситуация может возникнуть если оставшаяся часть цепочки была добавлена позже этого элемента)&lt;br /&gt;
* в цепочке не должно оставаться &amp;quot;дырок&amp;quot;, тогда любой элемент с данным хешем будет доступен из начала цепи&lt;br /&gt;
&lt;br /&gt;
Учитывая это будем действовать следующим образом: при поиске следующего элемента цепочки будем пропускать все ячейки с другим значением хеша, первый найденный элемент копировать в текущую ячейку, и затем рекурсивно его удалять. Если такой следующей ячейки нет, то текущий элемент можно просто удалить, сторонние цепочки при этом не разрушатся (чего нельзя сказать про случай квадратичного поиска).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
''' Псевдокод '''&lt;br /&gt;
&lt;br /&gt;
 '''function''' delete('''Item''' i) &lt;br /&gt;
      j = i + q&lt;br /&gt;
      '''while''' table[j] == '''null''' '''or''' table[j].key != table[i].key&lt;br /&gt;
         '''if''' table[j] == '''null'''&lt;br /&gt;
            table[i] = '''null'''&lt;br /&gt;
            '''return'''&lt;br /&gt;
         j += q&lt;br /&gt;
      table[i] = table[j]&lt;br /&gt;
      delete(j)    &lt;br /&gt;
&lt;br /&gt;
Хеш-таблицу считаем зацикленной&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Утверждение&lt;br /&gt;
|about=о времени работы&lt;br /&gt;
|statement=Асимптотически время работы &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\mathrm{find}&amp;lt;/tex&amp;gt; совпадают&lt;br /&gt;
|proof=&lt;br /&gt;
Заметим что указатель &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; в каждой итерации перемещается вперёд на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; (с учётом рекурсивных вызовов &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt;). То есть этот алгоритм последовательно пройдёт по цепочке от удаляемого элемента до последнего {{---}} с учётом вызова &amp;lt;tex&amp;gt;\mathrm{find}&amp;lt;/tex&amp;gt; собственно для нахождения удаляемого элемента, мы посетим все ячейки цепи.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Вариант с зацикливанием мы не рассматриваем, поскольку если &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; взаимнопросто с размером хеш-таблицы, то для зацикливания в ней вообще не должно быть свободных позиций&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Теперь докажем почему этот алгоритм работает. Собственно нам требуется сохранение трёх условий.&lt;br /&gt;
* В редактируемой цепи не остаётся дырок&lt;br /&gt;
Докажем по индукции. Если на данной итерации мы просто удаляем элемент (база), то после него ничего нет, всё верно. Если же нет, то вызванный в конце &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt; (см. псевдокод) заметёт созданную дыру (скопированный элемент), и сам, по предположению, новых не создаст.&lt;br /&gt;
* Элементы, которые уже на своих местах, не должны быть сдвинуты.&lt;br /&gt;
Это учтено.&lt;br /&gt;
* В других цепочках не появятся дыры&lt;br /&gt;
Противное возможно только в том случае, если какой-то элемент был действительно удалён. Удаляем мы только последнюю ячейку в цепи, и если бы на её месте возникла дыра для сторонней цепочки, это бы означало что элемент, стоящий на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; позиций назад, одновременно принадлежал нашей и другой цепочкам, что невозможно.&lt;br /&gt;
&lt;br /&gt;
==Двойное хеширование==&lt;br /&gt;
'''Двойное хеширование''' (англ. double hashing) {{---}} метод борьбы с коллизиями, возникающими при открытой адресации, основанный на использовании двух хеш-функций для построения различных последовательностей исследования хеш-таблицы.&lt;br /&gt;
&lt;br /&gt;
===Принцип двойного хеширования===&lt;br /&gt;
При двойном хешировании используются две независимые хеш-функции &amp;lt;tex&amp;gt; h_1(k) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; h_2(k) &amp;lt;/tex&amp;gt;. Пусть &amp;lt;tex&amp;gt; k &amp;lt;/tex&amp;gt; {{---}} это наш ключ, &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt; {{---}} размер нашей таблицы, &amp;lt;tex&amp;gt;n \bmod m &amp;lt;/tex&amp;gt; {{---}} остаток от деления &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt; на &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, тогда сначала исследуется ячейка с адресом &amp;lt;tex&amp;gt; h_1(k) &amp;lt;/tex&amp;gt;, если она уже занята, то рассматривается &amp;lt;tex&amp;gt; (h_1(k) +  h_2(k)) \bmod m &amp;lt;/tex&amp;gt;, затем &amp;lt;tex&amp;gt; (h_1(k) +  2 \cdot h_2(k)) \bmod m &amp;lt;/tex&amp;gt; и так далее. В общем случае идёт проверка последовательности ячеек &amp;lt;tex&amp;gt; (h_1(k) +  i \cdot h_2(k)) \bmod m &amp;lt;/tex&amp;gt; где &amp;lt;tex&amp;gt;  i = (0, 1, \; ... \;,  m - 1) &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, операции вставки, удаления и поиска в лучшем случае выполняются за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;, в худшем {{---}} за &amp;lt;tex&amp;gt;O(m)&amp;lt;/tex&amp;gt;, что не отличается от обычного [[Открытое_и_закрытое_хеширование#Линейное разрешение коллизий|линейного разрешения коллизий]].&lt;br /&gt;
Однако в среднем, при грамотном выборе хеш-функций, двойное хеширование будет выдавать лучшие результаты, за счёт того, что вероятность совпадения значений сразу двух независимых хеш-функций ниже, чем одной.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt;\forall x \neq y \; \exists h_1,h_2 : p(h_1(x)=h_1(y))&amp;gt; p((h_1(x)=h_1(y)) \land (h_2(x)=h_2(y)))&amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Выбор хеш-функций===&lt;br /&gt;
&amp;lt;tex&amp;gt; h_1 &amp;lt;/tex&amp;gt; может быть обычной хеш-функцией. Однако чтобы последовательность исследования могла охватить всю таблицу, &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; должна возвращать значения:&lt;br /&gt;
*не равные &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
*независимые  от &amp;lt;tex&amp;gt; h_1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
*взаимно простые с величиной хеш-таблицы&lt;br /&gt;
&lt;br /&gt;
Есть два удобных способа это сделать. Первый состоит в том, что в качестве размера таблицы используется простое число, а &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; возвращает натуральные числа, меньшие &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;. Второй {{---}} размер таблицы является степенью двойки, а &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; возвращает нечетные значения.&lt;br /&gt;
&lt;br /&gt;
Например, если размер таблицы равен &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, то в качестве &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; можно использовать функцию вида &amp;lt;tex&amp;gt; h_2(k) = k \bmod (m-1) + 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Файл: Вставка при двойном хэшировании.svg.jpeg|thumb|right|Вставка при двойном хешировании]]&lt;br /&gt;
&lt;br /&gt;
===Пример===&lt;br /&gt;
&lt;br /&gt;
Показана хеш-таблица размером 13 ячеек, в которой используются вспомогательные функции:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h(k,i) = (h_1(k) + i \cdot h_2(k)) \bmod 13 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h_1(k) = k \bmod 13 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h_2(k) = 1 + k \bmod 11 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Мы хотим вставить ключ 14. Изначально &amp;lt;tex&amp;gt; i = 0 &amp;lt;/tex&amp;gt;. Тогда &amp;lt;tex&amp;gt; h(14,0) = (h_1(14) + 0\cdot h_2(14)) \bmod 13 = 1 &amp;lt;/tex&amp;gt;. Но ячейка с индексом 1 занята, поэтому увеличиваем &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; на 1 и пересчитываем значение хеш-функции. Делаем так, пока не дойдем до пустой ячейки. При &amp;lt;tex&amp;gt; i = 2 &amp;lt;/tex&amp;gt; получаем &amp;lt;tex&amp;gt; h(14,2) = (h_1(14) + 2\cdot h_2(14)) \bmod 13 = 9 &amp;lt;/tex&amp;gt;. Ячейка с номером 9 свободна, значит записываем туда наш ключ.&lt;br /&gt;
&lt;br /&gt;
Таким образом, основная особенность двойного хеширования состоит в том, что при различных &amp;lt;tex&amp;gt; k &amp;lt;/tex&amp;gt; пара &amp;lt;tex&amp;gt; (h_1(k),h_2(k)) &amp;lt;/tex&amp;gt; дает различные последовательности ячеек для исследования.&lt;br /&gt;
&lt;br /&gt;
===Простая реализация===&lt;br /&gt;
Пусть у нас есть некоторый объект &amp;lt;tex&amp;gt; item &amp;lt;/tex&amp;gt;, в котором определено поле &amp;lt;tex&amp;gt; key &amp;lt;/tex&amp;gt;, от которого можно вычислить хеш-функции &amp;lt;tex&amp;gt; h_1(key)&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; h_2(key) &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Так же у нас есть таблица &amp;lt;tex&amp;gt; table &amp;lt;/tex&amp;gt; величиной &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, состоящая из объектов типа &amp;lt;tex&amp;gt; item &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
'''Вставка'''&lt;br /&gt;
 '''function''' add('''Item''' item)&lt;br /&gt;
      x = h1(item.key)&lt;br /&gt;
      y = h2(item.key)&lt;br /&gt;
         '''for''' (i = 0..m)    	&lt;br /&gt;
            '''if''' table[x] == '''null'''&lt;br /&gt;
               table[x] = item&lt;br /&gt;
            '''return'''      &lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      table.resize()&amp;lt;span style=&amp;quot;color:Green&amp;quot;&amp;gt;// ошибка, требуется увеличить размер таблицы&lt;br /&gt;
&lt;br /&gt;
'''Поиск'''&lt;br /&gt;
 '''Item''' search('''Item''' key)&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m)&lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key&lt;br /&gt;
               '''return''' table[x]&lt;br /&gt;
            '''else'''&lt;br /&gt;
               '''return''' '''null'''&lt;br /&gt;
      x = (x + y) '''mod''' m   &lt;br /&gt;
      '''return''' '''null'''&lt;br /&gt;
&lt;br /&gt;
===Реализация с удалением===&lt;br /&gt;
Что бы наша хеш-таблица поддерживала удаление, требуется добавить массив &amp;lt;tex&amp;gt;deleted&amp;lt;/tex&amp;gt; типов &amp;lt;tex&amp;gt;bool&amp;lt;/tex&amp;gt;, равный по величине массиву &amp;lt;tex&amp;gt;table&amp;lt;/tex&amp;gt;. Теперь при удалении мы просто будем помечать наш объект ''как удалённый'', а при добавлении как ''не удалённый'' и замещать новым добавляемым объектом. При поиске, помимо равенства ключей, мы смотрим, удалён ли элемент, если да, то идём дальше.&lt;br /&gt;
&lt;br /&gt;
'''Вставка'''&lt;br /&gt;
 '''function''' add('''Item''' item)&lt;br /&gt;
      x = h1(item.key)&lt;br /&gt;
      y = h2(item.key)&lt;br /&gt;
      '''for''' (i = 0..m)   	&lt;br /&gt;
         '''if''' table[x] == '''null''' '''or''' deleted[x]&lt;br /&gt;
            table[x] = item&lt;br /&gt;
            deleted[x] = '''false'''&lt;br /&gt;
            '''return'''      &lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      table.resize()&amp;lt;span style=&amp;quot;color:Green&amp;quot;&amp;gt;// ошибка, требуется увеличить размер таблицы&lt;br /&gt;
'''Поиск'''&lt;br /&gt;
 '''Item''' search('''Item''' key)&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m) &lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key '''and''' !deleted[x]&lt;br /&gt;
               '''return''' table[x]&lt;br /&gt;
         '''else'''&lt;br /&gt;
            '''return''' '''null'''&lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      '''return''' '''null'''&lt;br /&gt;
&lt;br /&gt;
'''Удаление'''&lt;br /&gt;
 '''function''' remove('''Item''' key)&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m)&lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key&lt;br /&gt;
               deleted[x] = '''true'''&lt;br /&gt;
         '''else''' &lt;br /&gt;
            '''return'''&lt;br /&gt;
      x = (x + y) '''mod''' m&lt;br /&gt;
&lt;br /&gt;
== Разрешение коллизий в Java 8==&lt;br /&gt;
В Java 8 для разрешения коллизий используется модифицированный метод цепочек. Суть его заключается в том, что когда количество элементов в корзине превышает определенное значение, данный корзина переходит от использования связного списка к использованию сбалансированного дерева. Такой подход позволяет улучшить производительность с &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt;.  Данный способ используется в таких коллекциях как HashMap, LinkedHashMap и ConcurrentHashMap.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Hashing_in_Java8.png|500px|Хеширование в Java 8.]]&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
* [[Хеширование]]&lt;br /&gt;
* [[Хеширование_кукушки|Хеширование кукушки]]&lt;br /&gt;
* [[Идеальное_хеширование|Идеальное хеширование]]&lt;br /&gt;
&lt;br /&gt;
== Источники информации ==&lt;br /&gt;
* Бакнелл Дж. М. «Фундаментальные алгоритмы и структуры данных в Delphi», 2003&lt;br /&gt;
* Кормен, Томас Х., Лейзерсон, Чарльз И., Ривест, Рональд Л., Штайн Клиффорд «Алгоритмы: построение и анализ», 2-е издание. Пер. с англ. — М.:Издательский дом &amp;quot;Вильямс&amp;quot;, 2010.— Парал. тит. англ. — ISBN 978-5-8459-0857-5 (рус.)&lt;br /&gt;
* Дональд Кнут. «Искусство программирования, том 3. Сортировка и поиск» {{---}} «Вильямс», 2007 г.{{---}} ISBN 0-201-89685-0&lt;br /&gt;
* Седжвик Р. «Фундаментальные алгоритмы на C. Части 1-4. Анализ. Структуры данных. Сортировка. Поиск», 2003&lt;br /&gt;
* [http://openjdk.java.net/jeps/180 Handle Frequent HashMap Collisions with Balanced Trees]&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Double_hashing Wikipedia {{---}} Double_hashing]&lt;br /&gt;
* [http://ru.wikipedia.org/wiki/%D0%A5%D0%B5%D1%88-%D1%82%D0%B0%D0%B1%D0%BB%D0%B8%D1%86%D0%B0 Разрешение коллизий]&lt;br /&gt;
* [http://rain.ifmo.ru/cat/view.php/vis/hashtables/hash-2001-2 Пример хеш таблицы]&lt;br /&gt;
* [http://research.cs.vt.edu/AVresearch/hashing/double.php Пример хеш таблицы с двойным хешированием]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория: Хеширование]]&lt;/div&gt;</summary>
		<author><name>Kozichuk</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%A0%D0%B0%D0%B7%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BA%D0%BE%D0%BB%D0%BB%D0%B8%D0%B7%D0%B8%D0%B9&amp;diff=47158</id>
		<title>Разрешение коллизий</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A0%D0%B0%D0%B7%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BA%D0%BE%D0%BB%D0%BB%D0%B8%D0%B7%D0%B8%D0%B9&amp;diff=47158"/>
				<updated>2015-05-31T22:49:54Z</updated>
		
		<summary type="html">&lt;p&gt;Kozichuk: /* Разрешение коллизий с помощью цепочек */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Разрешение [[Хеш-таблица|коллизий]]''' (англ. collision resolution) в [[Хеш-таблица|хеш-таблице]], задача, решаемая несколькими способами: метод цепочек, открытая адресация и т.д. Очень важно сводить количество коллизий к минимуму, так как это увеличивает время работы с хеш-таблицами. &lt;br /&gt;
&lt;br /&gt;
== Разрешение коллизий с помощью цепочек ==&lt;br /&gt;
[[Файл:open_hash.png|thumb|380px|right|Разрешение коллизий при помощи цепочек.]]&lt;br /&gt;
Каждая ячейка &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; массива &amp;lt;tex&amp;gt;H&amp;lt;/tex&amp;gt; содержит указатель на начало [[Список|списка]] всех элементов, хеш-код которых равен &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, либо указывает на их отсутствие. Коллизии приводят к тому, что появляются списки размером больше одного элемента.&lt;br /&gt;
&lt;br /&gt;
В зависимости от того нужна ли нам уникальность значений операции вставки у нас будет работать за разное время. Если не важна, то мы используем список, время вставки в который будет в худшем случае равна &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;. Иначе мы реализуем Set. В таком случае вставка элемента в худшем случае будет выполнена за &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Время работы поиска в наихудшем случае пропорционально длине списка, а если все &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; ключей захешировались в одну и ту же ячейку (создав список длиной &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;) время поиска будет равно &amp;lt;tex&amp;gt;\Theta(n)&amp;lt;/tex&amp;gt; плюс время вычисления хеш-функции, что ничуть не лучше, чем использование связного списка для хранения всех &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; элементов.&lt;br /&gt;
&lt;br /&gt;
Удаления элемента может быть выполнено за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;, как и вставка, при использовании двухсвязного списка.&lt;br /&gt;
&lt;br /&gt;
== Линейное разрешение коллизий ==&lt;br /&gt;
[[Файл:close_hash.png|thumb|380px|right|Пример хеш-таблицы с открытой адресацией и линейным пробированием.]]&lt;br /&gt;
Все элементы хранятся непосредственно в хеш-таблице, без использования связных списков. В отличии от хеширования с цепочками, при использовании этого метода может возникнуть ситуация, когда хеш-таблица окажется полностью заполненной, следовательно будет невозможно добавлять в неё новые элементы. Так что при возникновении такой ситуации решением может быть динамическое увеличение размера хеш-таблицы, с одновременной её перестройкой.&lt;br /&gt;
&lt;br /&gt;
=== Стратегии поиска ===&lt;br /&gt;
&lt;br /&gt;
''' Последовательный поиск '''&lt;br /&gt;
&lt;br /&gt;
При попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt;i+1, i+2, i+3&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку. В неё и запишем элемент.&lt;br /&gt;
&lt;br /&gt;
[[Файл:hashtables1.png|400px|Последовательный поиск, частный случай линейного поиска.]]&lt;br /&gt;
&lt;br /&gt;
''' Линейный поиск '''&lt;br /&gt;
&lt;br /&gt;
Выбираем шаг &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;. При попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt;i+(1 \cdot q), i+(2 \cdot q), i+(3 \cdot q)&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку. В неё и запишем элемент.&lt;br /&gt;
По сути последовательный поиск - частный случай линейного, где &amp;lt;tex&amp;gt;q=1&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Hashtables56.PNG|400px|Линейный поиск с шагом q.]]&lt;br /&gt;
&lt;br /&gt;
''' Квадратичный поиск '''&lt;br /&gt;
&lt;br /&gt;
Шаг &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; не фиксирован, а изменяется квадратично: &amp;lt;tex&amp;gt;q = 1,4,9,16...&amp;lt;/tex&amp;gt;. Соответственно при попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt; i+1, i+4, i+9&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку.&lt;br /&gt;
&lt;br /&gt;
[[Файл:hashtables3.png|400px|Квадратичный поиск.]]&lt;br /&gt;
&lt;br /&gt;
=== Проверка наличия элемента в таблице===&lt;br /&gt;
&lt;br /&gt;
Проверка осуществляется аналогично добавлению: мы проверяем ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; и другие, в соответствии с выбранной стратегией, пока не найдём искомый элемент или свободную ячейку.&lt;br /&gt;
&lt;br /&gt;
При поиске элемента может получится так, что мы дойдём до конца таблицы. Обычно поиск продолжается, начиная с другого конца, пока мы не придём в ту ячейку, откуда начинался поиск.&lt;br /&gt;
&lt;br /&gt;
=== Проблемы данных стратегий ===&lt;br /&gt;
&lt;br /&gt;
Проблем две — крайне нетривиальное удаление элемента из таблицы и образование кластеров  — последовательностей занятых ячеек.&lt;br /&gt;
&lt;br /&gt;
Кластеризация замедляет все операции с хеш-таблицей: при добавлении требуется перебирать всё больше элементов, при проверке тоже. Чем больше в таблице элементов, тем больше в ней кластеры и тем выше вероятность того, что добавляемый элемент попадёт в кластер.&lt;br /&gt;
Для защиты от кластеризации используется двойное хеширование и [[Хеширование кукушки|хеширование кукушки]].&lt;br /&gt;
&lt;br /&gt;
=== Удаление элемента без пометок ===&lt;br /&gt;
&lt;br /&gt;
Рассуждение будет описывать случай с линейным поиском хеша. Будем при удалении элемента сдвигать всё последующие на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; позиций назад. При этом:&lt;br /&gt;
* если в цепочке встречается элемент с другим хешем, то он должен остаться на своём месте (такая ситуация может возникнуть если оставшаяся часть цепочки была добавлена позже этого элемента)&lt;br /&gt;
* в цепочке не должно оставаться &amp;quot;дырок&amp;quot;, тогда любой элемент с данным хешем будет доступен из начала цепи&lt;br /&gt;
&lt;br /&gt;
Учитывая это будем действовать следующим образом: при поиске следующего элемента цепочки будем пропускать все ячейки с другим значением хеша, первый найденный элемент копировать в текущую ячейку, и затем рекурсивно его удалять. Если такой следующей ячейки нет, то текущий элемент можно просто удалить, сторонние цепочки при этом не разрушатся (чего нельзя сказать про случай квадратичного поиска).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
''' Псевдокод '''&lt;br /&gt;
&lt;br /&gt;
 '''function''' delete('''Item''' i) &lt;br /&gt;
      j = i + q&lt;br /&gt;
      '''while''' table[j] == '''null''' '''or''' table[j].key != table[i].key&lt;br /&gt;
         '''if''' table[j] == '''null'''&lt;br /&gt;
            table[i] = '''null'''&lt;br /&gt;
            '''return'''&lt;br /&gt;
         j += q&lt;br /&gt;
      table[i] = table[j]&lt;br /&gt;
      delete(j)    &lt;br /&gt;
&lt;br /&gt;
Хеш-таблицу считаем зацикленной&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Утверждение&lt;br /&gt;
|about=о времени работы&lt;br /&gt;
|statement=Асимптотически время работы &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\mathrm{find}&amp;lt;/tex&amp;gt; совпадают&lt;br /&gt;
|proof=&lt;br /&gt;
Заметим что указатель &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; в каждой итерации перемещается вперёд на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; (с учётом рекурсивных вызовов &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt;). То есть этот алгоритм последовательно пройдёт по цепочке от удаляемого элемента до последнего {{---}} с учётом вызова &amp;lt;tex&amp;gt;\mathrm{find}&amp;lt;/tex&amp;gt; собственно для нахождения удаляемого элемента, мы посетим все ячейки цепи.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Вариант с зацикливанием мы не рассматриваем, поскольку если &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; взаимнопросто с размером хеш-таблицы, то для зацикливания в ней вообще не должно быть свободных позиций&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Теперь докажем почему этот алгоритм работает. Собственно нам требуется сохранение трёх условий.&lt;br /&gt;
* В редактируемой цепи не остаётся дырок&lt;br /&gt;
Докажем по индукции. Если на данной итерации мы просто удаляем элемент (база), то после него ничего нет, всё верно. Если же нет, то вызванный в конце &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt; (см. псевдокод) заметёт созданную дыру (скопированный элемент), и сам, по предположению, новых не создаст.&lt;br /&gt;
* Элементы, которые уже на своих местах, не должны быть сдвинуты.&lt;br /&gt;
Это учтено.&lt;br /&gt;
* В других цепочках не появятся дыры&lt;br /&gt;
Противное возможно только в том случае, если какой-то элемент был действительно удалён. Удаляем мы только последнюю ячейку в цепи, и если бы на её месте возникла дыра для сторонней цепочки, это бы означало что элемент, стоящий на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; позиций назад, одновременно принадлежал нашей и другой цепочкам, что невозможно.&lt;br /&gt;
&lt;br /&gt;
==Двойное хеширование==&lt;br /&gt;
'''Двойное хеширование''' (англ. double hashing) {{---}} метод борьбы с коллизиями, возникающими при открытой адресации, основанный на использовании двух хеш-функций для построения различных последовательностей исследования хеш-таблицы.&lt;br /&gt;
&lt;br /&gt;
===Принцип двойного хеширования===&lt;br /&gt;
При двойном хешировании используются две независимые хеш-функции &amp;lt;tex&amp;gt; h_1(k) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; h_2(k) &amp;lt;/tex&amp;gt;. Пусть &amp;lt;tex&amp;gt; k &amp;lt;/tex&amp;gt; {{---}} это наш ключ, &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt; {{---}} размер нашей таблицы, &amp;lt;tex&amp;gt;n \bmod m &amp;lt;/tex&amp;gt; {{---}} остаток от деления &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt; на &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, тогда сначала исследуется ячейка с адресом &amp;lt;tex&amp;gt; h_1(k) &amp;lt;/tex&amp;gt;, если она уже занята, то рассматривается &amp;lt;tex&amp;gt; (h_1(k) +  h_2(k)) \bmod m &amp;lt;/tex&amp;gt;, затем &amp;lt;tex&amp;gt; (h_1(k) +  2 \cdot h_2(k)) \bmod m &amp;lt;/tex&amp;gt; и так далее. В общем случае идёт проверка последовательности ячеек &amp;lt;tex&amp;gt; (h_1(k) +  i \cdot h_2(k)) \bmod m &amp;lt;/tex&amp;gt; где &amp;lt;tex&amp;gt;  i = (0, 1, \; ... \;,  m - 1) &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, операции вставки, удаления и поиска в лучшем случае выполняются за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;, в худшем {{---}} за &amp;lt;tex&amp;gt;O(m)&amp;lt;/tex&amp;gt;, что не отличается от обычного [[Открытое_и_закрытое_хеширование#Линейное разрешение коллизий|линейного разрешения коллизий]].&lt;br /&gt;
Однако в среднем, при грамотном выборе хеш-функций, двойное хеширование будет выдавать лучшие результаты, за счёт того, что вероятность совпадения значений сразу двух независимых хеш-функций ниже, чем одной.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt;\forall x \neq y \; \exists h_1,h_2 : p(h_1(x)=h_1(y))&amp;gt; p((h_1(x)=h_1(y)) \land (h_2(x)=h_2(y)))&amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Выбор хеш-функций===&lt;br /&gt;
&amp;lt;tex&amp;gt; h_1 &amp;lt;/tex&amp;gt; может быть обычной хеш-функцией. Однако чтобы последовательность исследования могла охватить всю таблицу, &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; должна возвращать значения:&lt;br /&gt;
*не равные &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
*независимые  от &amp;lt;tex&amp;gt; h_1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
*взаимно простые с величиной хеш-таблицы&lt;br /&gt;
&lt;br /&gt;
Есть два удобных способа это сделать. Первый состоит в том, что в качестве размера таблицы используется простое число, а &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; возвращает натуральные числа, меньшие &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;. Второй {{---}} размер таблицы является степенью двойки, а &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; возвращает нечетные значения.&lt;br /&gt;
&lt;br /&gt;
Например, если размер таблицы равен &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, то в качестве &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; можно использовать функцию вида &amp;lt;tex&amp;gt; h_2(k) = k \bmod (m-1) + 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Файл: Вставка при двойном хэшировании.svg.jpeg|thumb|right|Вставка при двойном хешировании]]&lt;br /&gt;
&lt;br /&gt;
===Пример===&lt;br /&gt;
&lt;br /&gt;
Показана хеш-таблица размером 13 ячеек, в которой используются вспомогательные функции:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h(k,i) = (h_1(k) + i \cdot h_2(k)) \bmod 13 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h_1(k) = k \bmod 13 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h_2(k) = 1 + k \bmod 11 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Мы хотим вставить ключ 14. Изначально &amp;lt;tex&amp;gt; i = 0 &amp;lt;/tex&amp;gt;. Тогда &amp;lt;tex&amp;gt; h(14,0) = (h_1(14) + 0\cdot h_2(14)) \bmod 13 = 1 &amp;lt;/tex&amp;gt;. Но ячейка с индексом 1 занята, поэтому увеличиваем &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; на 1 и пересчитываем значение хеш-функции. Делаем так, пока не дойдем до пустой ячейки. При &amp;lt;tex&amp;gt; i = 2 &amp;lt;/tex&amp;gt; получаем &amp;lt;tex&amp;gt; h(14,2) = (h_1(14) + 2\cdot h_2(14)) \bmod 13 = 9 &amp;lt;/tex&amp;gt;. Ячейка с номером 9 свободна, значит записываем туда наш ключ.&lt;br /&gt;
&lt;br /&gt;
Таким образом, основная особенность двойного хеширования состоит в том, что при различных &amp;lt;tex&amp;gt; k &amp;lt;/tex&amp;gt; пара &amp;lt;tex&amp;gt; (h_1(k),h_2(k)) &amp;lt;/tex&amp;gt; дает различные последовательности ячеек для исследования.&lt;br /&gt;
&lt;br /&gt;
===Простая реализация===&lt;br /&gt;
Пусть у нас есть некоторый объект &amp;lt;tex&amp;gt; item &amp;lt;/tex&amp;gt;, в котором определено поле &amp;lt;tex&amp;gt; key &amp;lt;/tex&amp;gt;, от которого можно вычислить хеш-функции &amp;lt;tex&amp;gt; h_1(key)&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; h_2(key) &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Так же у нас есть таблица &amp;lt;tex&amp;gt; table &amp;lt;/tex&amp;gt; величиной &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, состоящая из объектов типа &amp;lt;tex&amp;gt; item &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
'''Вставка'''&lt;br /&gt;
 '''function''' add('''Item''' item)&lt;br /&gt;
      x = h1(item.key)&lt;br /&gt;
      y = h2(item.key)&lt;br /&gt;
         '''for''' (i = 0..m)    	&lt;br /&gt;
            '''if''' table[x] == '''null'''&lt;br /&gt;
               table[x] = item&lt;br /&gt;
            '''return'''      &lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      table.resize()&amp;lt;span style=&amp;quot;color:Green&amp;quot;&amp;gt;// ошибка, требуется увеличить размер таблицы&lt;br /&gt;
&lt;br /&gt;
'''Поиск'''&lt;br /&gt;
 '''Item''' search('''Item''' key)&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m)&lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key&lt;br /&gt;
               '''return''' table[x]&lt;br /&gt;
            '''else'''&lt;br /&gt;
               '''return''' '''null'''&lt;br /&gt;
      x = (x + y) '''mod''' m   &lt;br /&gt;
      '''return''' '''null'''&lt;br /&gt;
&lt;br /&gt;
===Реализация с удалением===&lt;br /&gt;
Что бы наша хеш-таблица поддерживала удаление, требуется добавить массив &amp;lt;tex&amp;gt;deleted&amp;lt;/tex&amp;gt; типов &amp;lt;tex&amp;gt;bool&amp;lt;/tex&amp;gt;, равный по величине массиву &amp;lt;tex&amp;gt;table&amp;lt;/tex&amp;gt;. Теперь при удалении мы просто будем помечать наш объект ''как удалённый'', а при добавлении как ''не удалённый'' и замещать новым добавляемым объектом. При поиске, помимо равенства ключей, мы смотрим, удалён ли элемент, если да, то идём дальше.&lt;br /&gt;
&lt;br /&gt;
'''Вставка'''&lt;br /&gt;
 '''function''' add('''Item''' item)&lt;br /&gt;
      x = h1(item.key)&lt;br /&gt;
      y = h2(item.key)&lt;br /&gt;
      '''for''' (i = 0..m)   	&lt;br /&gt;
         '''if''' table[x] == '''null''' '''or''' deleted[x]&lt;br /&gt;
            table[x] = item&lt;br /&gt;
            deleted[x] = '''false'''&lt;br /&gt;
            '''return'''      &lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      table.resize()&amp;lt;span style=&amp;quot;color:Green&amp;quot;&amp;gt;// ошибка, требуется увеличить размер таблицы&lt;br /&gt;
'''Поиск'''&lt;br /&gt;
 '''Item''' search('''Item''' key)&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m) &lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key '''and''' !deleted[x]&lt;br /&gt;
               '''return''' table[x]&lt;br /&gt;
         '''else'''&lt;br /&gt;
            '''return''' '''null'''&lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      '''return''' '''null'''&lt;br /&gt;
&lt;br /&gt;
'''Удаление'''&lt;br /&gt;
 '''function''' remove('''Item''' key)&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m)&lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key&lt;br /&gt;
               deleted[x] = '''true'''&lt;br /&gt;
         '''else''' &lt;br /&gt;
            '''return'''&lt;br /&gt;
      x = (x + y) '''mod''' m&lt;br /&gt;
&lt;br /&gt;
== Разрешение коллизий в Java 8==&lt;br /&gt;
В Java 8 для разрешения коллизий используется модифицированный метод цепочек. Суть его заключается в том, что когда количество элементов в корзине превышает определенное значение, данный бакет переходит от использования связного списка к использованию сбалансированного дерева. Такой подход позволяет улучшить производительность с &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt;.  Данный способ используется в таких коллекциях как HashMap, LinkedHashMap и ConcurrentHashMap.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Hashing_in_Java8.png|500px|Хеширование в Java 8.]]&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
* [[Хеширование]]&lt;br /&gt;
* [[Хеширование_кукушки|Хеширование кукушки]]&lt;br /&gt;
* [[Идеальное_хеширование|Идеальное хеширование]]&lt;br /&gt;
&lt;br /&gt;
== Источники информации ==&lt;br /&gt;
* Бакнелл Дж. М. «Фундаментальные алгоритмы и структуры данных в Delphi», 2003&lt;br /&gt;
* Кормен, Томас Х., Лейзерсон, Чарльз И., Ривест, Рональд Л., Штайн Клиффорд «Алгоритмы: построение и анализ», 2-е издание. Пер. с англ. — М.:Издательский дом &amp;quot;Вильямс&amp;quot;, 2010.— Парал. тит. англ. — ISBN 978-5-8459-0857-5 (рус.)&lt;br /&gt;
* Дональд Кнут. «Искусство программирования, том 3. Сортировка и поиск» {{---}} «Вильямс», 2007 г.{{---}} ISBN 0-201-89685-0&lt;br /&gt;
* Седжвик Р. «Фундаментальные алгоритмы на C. Части 1-4. Анализ. Структуры данных. Сортировка. Поиск», 2003&lt;br /&gt;
* [http://openjdk.java.net/jeps/180 Handle Frequent HashMap Collisions with Balanced Trees]&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Double_hashing Wikipedia {{---}} Double_hashing]&lt;br /&gt;
* [http://ru.wikipedia.org/wiki/%D0%A5%D0%B5%D1%88-%D1%82%D0%B0%D0%B1%D0%BB%D0%B8%D1%86%D0%B0 Разрешение коллизий]&lt;br /&gt;
* [http://rain.ifmo.ru/cat/view.php/vis/hashtables/hash-2001-2 Пример хеш таблицы]&lt;br /&gt;
* [http://research.cs.vt.edu/AVresearch/hashing/double.php Пример хеш таблицы с двойным хешированием]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория: Хеширование]]&lt;/div&gt;</summary>
		<author><name>Kozichuk</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%A0%D0%B0%D0%B7%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BA%D0%BE%D0%BB%D0%BB%D0%B8%D0%B7%D0%B8%D0%B9&amp;diff=47157</id>
		<title>Разрешение коллизий</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A0%D0%B0%D0%B7%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BA%D0%BE%D0%BB%D0%BB%D0%B8%D0%B7%D0%B8%D0%B9&amp;diff=47157"/>
				<updated>2015-05-31T21:44:07Z</updated>
		
		<summary type="html">&lt;p&gt;Kozichuk: /* Удаление элемента без пометок */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Разрешение [[Хеш-таблица|коллизий]]''' (англ. collision resolution) в [[Хеш-таблица|хеш-таблице]], задача, решаемая несколькими способами: метод цепочек, открытая адресация и т.д. Очень важно сводить количество коллизий к минимуму, так как это увеличивает время работы с хеш-таблицами. &lt;br /&gt;
&lt;br /&gt;
== Разрешение коллизий с помощью цепочек ==&lt;br /&gt;
[[Файл:open_hash.png|thumb|380px|right|Разрешение коллизий при помощи цепочек.]]&lt;br /&gt;
Каждая ячейка &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; массива &amp;lt;tex&amp;gt;H&amp;lt;/tex&amp;gt; содержит указатель на начало [[Список|списка]] всех элементов, хеш-код которых равен &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, либо указывает на их отсутствие. Коллизии приводят к тому, что появляются списки размером больше одного элемента.&lt;br /&gt;
&lt;br /&gt;
Время, необходимое для вставки в наихудшем случае равно &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;. Это операция выполняет быстро, так как считается, что вставляемый элемент отсутствует в таблице, но если потребуется, то перед вставкой мы можем выполнить поиск этого элемента.&lt;br /&gt;
&lt;br /&gt;
Время работы поиска в наихудшем случае пропорционально длине списка, а если все &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; ключей захешировались в одну и ту же ячейку (создав список длиной &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;) время поиска будет равно &amp;lt;tex&amp;gt;\Theta(n)&amp;lt;/tex&amp;gt; плюс время вычисления хеш-функции, что ничуть не лучше, чем использование связного списка для хранения всех &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; элементов.&lt;br /&gt;
&lt;br /&gt;
Удаления элемента может быть выполнено за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;, как и вставка, при использовании двухсвязного списка.&lt;br /&gt;
&lt;br /&gt;
== Линейное разрешение коллизий ==&lt;br /&gt;
[[Файл:close_hash.png|thumb|380px|right|Пример хеш-таблицы с открытой адресацией и линейным пробированием.]]&lt;br /&gt;
Все элементы хранятся непосредственно в хеш-таблице, без использования связных списков. В отличии от хеширования с цепочками, при использовании этого метода может возникнуть ситуация, когда хеш-таблица окажется полностью заполненной, следовательно будет невозможно добавлять в неё новые элементы. Так что при возникновении такой ситуации решением может быть динамическое увеличение размера хеш-таблицы, с одновременной её перестройкой.&lt;br /&gt;
&lt;br /&gt;
=== Стратегии поиска ===&lt;br /&gt;
&lt;br /&gt;
''' Последовательный поиск '''&lt;br /&gt;
&lt;br /&gt;
При попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt;i+1, i+2, i+3&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку. В неё и запишем элемент.&lt;br /&gt;
&lt;br /&gt;
[[Файл:hashtables1.png|400px|Последовательный поиск, частный случай линейного поиска.]]&lt;br /&gt;
&lt;br /&gt;
''' Линейный поиск '''&lt;br /&gt;
&lt;br /&gt;
Выбираем шаг &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;. При попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt;i+(1 \cdot q), i+(2 \cdot q), i+(3 \cdot q)&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку. В неё и запишем элемент.&lt;br /&gt;
По сути последовательный поиск - частный случай линейного, где &amp;lt;tex&amp;gt;q=1&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Hashtables56.PNG|400px|Линейный поиск с шагом q.]]&lt;br /&gt;
&lt;br /&gt;
''' Квадратичный поиск '''&lt;br /&gt;
&lt;br /&gt;
Шаг &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; не фиксирован, а изменяется квадратично: &amp;lt;tex&amp;gt;q = 1,4,9,16...&amp;lt;/tex&amp;gt;. Соответственно при попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt; i+1, i+4, i+9&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку.&lt;br /&gt;
&lt;br /&gt;
[[Файл:hashtables3.png|400px|Квадратичный поиск.]]&lt;br /&gt;
&lt;br /&gt;
=== Проверка наличия элемента в таблице===&lt;br /&gt;
&lt;br /&gt;
Проверка осуществляется аналогично добавлению: мы проверяем ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; и другие, в соответствии с выбранной стратегией, пока не найдём искомый элемент или свободную ячейку.&lt;br /&gt;
&lt;br /&gt;
При поиске элемента может получится так, что мы дойдём до конца таблицы. Обычно поиск продолжается, начиная с другого конца, пока мы не придём в ту ячейку, откуда начинался поиск.&lt;br /&gt;
&lt;br /&gt;
=== Проблемы данных стратегий ===&lt;br /&gt;
&lt;br /&gt;
Проблем две — крайне нетривиальное удаление элемента из таблицы и образование кластеров  — последовательностей занятых ячеек.&lt;br /&gt;
&lt;br /&gt;
Кластеризация замедляет все операции с хеш-таблицей: при добавлении требуется перебирать всё больше элементов, при проверке тоже. Чем больше в таблице элементов, тем больше в ней кластеры и тем выше вероятность того, что добавляемый элемент попадёт в кластер.&lt;br /&gt;
Для защиты от кластеризации используется двойное хеширование и [[Хеширование кукушки|хеширование кукушки]].&lt;br /&gt;
&lt;br /&gt;
=== Удаление элемента без пометок ===&lt;br /&gt;
&lt;br /&gt;
Рассуждение будет описывать случай с линейным поиском хеша. Будем при удалении элемента сдвигать всё последующие на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; позиций назад. При этом:&lt;br /&gt;
* если в цепочке встречается элемент с другим хешем, то он должен остаться на своём месте (такая ситуация может возникнуть если оставшаяся часть цепочки была добавлена позже этого элемента)&lt;br /&gt;
* в цепочке не должно оставаться &amp;quot;дырок&amp;quot;, тогда любой элемент с данным хешем будет доступен из начала цепи&lt;br /&gt;
&lt;br /&gt;
Учитывая это будем действовать следующим образом: при поиске следующего элемента цепочки будем пропускать все ячейки с другим значением хеша, первый найденный элемент копировать в текущую ячейку, и затем рекурсивно его удалять. Если такой следующей ячейки нет, то текущий элемент можно просто удалить, сторонние цепочки при этом не разрушатся (чего нельзя сказать про случай квадратичного поиска).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
''' Псевдокод '''&lt;br /&gt;
&lt;br /&gt;
 '''function''' delete('''Item''' i) &lt;br /&gt;
      j = i + q&lt;br /&gt;
      '''while''' table[j] == '''null''' '''or''' table[j].key != table[i].key&lt;br /&gt;
         '''if''' table[j] == '''null'''&lt;br /&gt;
            table[i] = '''null'''&lt;br /&gt;
            '''return'''&lt;br /&gt;
         j += q&lt;br /&gt;
      table[i] = table[j]&lt;br /&gt;
      delete(j)    &lt;br /&gt;
&lt;br /&gt;
Хеш-таблицу считаем зацикленной&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Утверждение&lt;br /&gt;
|about=о времени работы&lt;br /&gt;
|statement=Асимптотически время работы &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\mathrm{find}&amp;lt;/tex&amp;gt; совпадают&lt;br /&gt;
|proof=&lt;br /&gt;
Заметим что указатель &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; в каждой итерации перемещается вперёд на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; (с учётом рекурсивных вызовов &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt;). То есть этот алгоритм последовательно пройдёт по цепочке от удаляемого элемента до последнего {{---}} с учётом вызова &amp;lt;tex&amp;gt;\mathrm{find}&amp;lt;/tex&amp;gt; собственно для нахождения удаляемого элемента, мы посетим все ячейки цепи.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Вариант с зацикливанием мы не рассматриваем, поскольку если &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; взаимнопросто с размером хеш-таблицы, то для зацикливания в ней вообще не должно быть свободных позиций&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Теперь докажем почему этот алгоритм работает. Собственно нам требуется сохранение трёх условий.&lt;br /&gt;
* В редактируемой цепи не остаётся дырок&lt;br /&gt;
Докажем по индукции. Если на данной итерации мы просто удаляем элемент (база), то после него ничего нет, всё верно. Если же нет, то вызванный в конце &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt; (см. псевдокод) заметёт созданную дыру (скопированный элемент), и сам, по предположению, новых не создаст.&lt;br /&gt;
* Элементы, которые уже на своих местах, не должны быть сдвинуты.&lt;br /&gt;
Это учтено.&lt;br /&gt;
* В других цепочках не появятся дыры&lt;br /&gt;
Противное возможно только в том случае, если какой-то элемент был действительно удалён. Удаляем мы только последнюю ячейку в цепи, и если бы на её месте возникла дыра для сторонней цепочки, это бы означало что элемент, стоящий на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; позиций назад, одновременно принадлежал нашей и другой цепочкам, что невозможно.&lt;br /&gt;
&lt;br /&gt;
==Двойное хеширование==&lt;br /&gt;
'''Двойное хеширование''' (англ. double hashing) {{---}} метод борьбы с коллизиями, возникающими при открытой адресации, основанный на использовании двух хеш-функций для построения различных последовательностей исследования хеш-таблицы.&lt;br /&gt;
&lt;br /&gt;
===Принцип двойного хеширования===&lt;br /&gt;
При двойном хешировании используются две независимые хеш-функции &amp;lt;tex&amp;gt; h_1(k) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; h_2(k) &amp;lt;/tex&amp;gt;. Пусть &amp;lt;tex&amp;gt; k &amp;lt;/tex&amp;gt; {{---}} это наш ключ, &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt; {{---}} размер нашей таблицы, &amp;lt;tex&amp;gt;n \bmod m &amp;lt;/tex&amp;gt; {{---}} остаток от деления &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt; на &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, тогда сначала исследуется ячейка с адресом &amp;lt;tex&amp;gt; h_1(k) &amp;lt;/tex&amp;gt;, если она уже занята, то рассматривается &amp;lt;tex&amp;gt; (h_1(k) +  h_2(k)) \bmod m &amp;lt;/tex&amp;gt;, затем &amp;lt;tex&amp;gt; (h_1(k) +  2 \cdot h_2(k)) \bmod m &amp;lt;/tex&amp;gt; и так далее. В общем случае идёт проверка последовательности ячеек &amp;lt;tex&amp;gt; (h_1(k) +  i \cdot h_2(k)) \bmod m &amp;lt;/tex&amp;gt; где &amp;lt;tex&amp;gt;  i = (0, 1, \; ... \;,  m - 1) &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, операции вставки, удаления и поиска в лучшем случае выполняются за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;, в худшем {{---}} за &amp;lt;tex&amp;gt;O(m)&amp;lt;/tex&amp;gt;, что не отличается от обычного [[Открытое_и_закрытое_хеширование#Линейное разрешение коллизий|линейного разрешения коллизий]].&lt;br /&gt;
Однако в среднем, при грамотном выборе хеш-функций, двойное хеширование будет выдавать лучшие результаты, за счёт того, что вероятность совпадения значений сразу двух независимых хеш-функций ниже, чем одной.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt;\forall x \neq y \; \exists h_1,h_2 : p(h_1(x)=h_1(y))&amp;gt; p((h_1(x)=h_1(y)) \land (h_2(x)=h_2(y)))&amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Выбор хеш-функций===&lt;br /&gt;
&amp;lt;tex&amp;gt; h_1 &amp;lt;/tex&amp;gt; может быть обычной хеш-функцией. Однако чтобы последовательность исследования могла охватить всю таблицу, &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; должна возвращать значения:&lt;br /&gt;
*не равные &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
*независимые  от &amp;lt;tex&amp;gt; h_1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
*взаимно простые с величиной хеш-таблицы&lt;br /&gt;
&lt;br /&gt;
Есть два удобных способа это сделать. Первый состоит в том, что в качестве размера таблицы используется простое число, а &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; возвращает натуральные числа, меньшие &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;. Второй {{---}} размер таблицы является степенью двойки, а &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; возвращает нечетные значения.&lt;br /&gt;
&lt;br /&gt;
Например, если размер таблицы равен &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, то в качестве &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; можно использовать функцию вида &amp;lt;tex&amp;gt; h_2(k) = k \bmod (m-1) + 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Файл: Вставка при двойном хэшировании.svg.jpeg|thumb|right|Вставка при двойном хешировании]]&lt;br /&gt;
&lt;br /&gt;
===Пример===&lt;br /&gt;
&lt;br /&gt;
Показана хеш-таблица размером 13 ячеек, в которой используются вспомогательные функции:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h(k,i) = (h_1(k) + i \cdot h_2(k)) \bmod 13 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h_1(k) = k \bmod 13 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h_2(k) = 1 + k \bmod 11 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Мы хотим вставить ключ 14. Изначально &amp;lt;tex&amp;gt; i = 0 &amp;lt;/tex&amp;gt;. Тогда &amp;lt;tex&amp;gt; h(14,0) = (h_1(14) + 0\cdot h_2(14)) \bmod 13 = 1 &amp;lt;/tex&amp;gt;. Но ячейка с индексом 1 занята, поэтому увеличиваем &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; на 1 и пересчитываем значение хеш-функции. Делаем так, пока не дойдем до пустой ячейки. При &amp;lt;tex&amp;gt; i = 2 &amp;lt;/tex&amp;gt; получаем &amp;lt;tex&amp;gt; h(14,2) = (h_1(14) + 2\cdot h_2(14)) \bmod 13 = 9 &amp;lt;/tex&amp;gt;. Ячейка с номером 9 свободна, значит записываем туда наш ключ.&lt;br /&gt;
&lt;br /&gt;
Таким образом, основная особенность двойного хеширования состоит в том, что при различных &amp;lt;tex&amp;gt; k &amp;lt;/tex&amp;gt; пара &amp;lt;tex&amp;gt; (h_1(k),h_2(k)) &amp;lt;/tex&amp;gt; дает различные последовательности ячеек для исследования.&lt;br /&gt;
&lt;br /&gt;
===Простая реализация===&lt;br /&gt;
Пусть у нас есть некоторый объект &amp;lt;tex&amp;gt; item &amp;lt;/tex&amp;gt;, в котором определено поле &amp;lt;tex&amp;gt; key &amp;lt;/tex&amp;gt;, от которого можно вычислить хеш-функции &amp;lt;tex&amp;gt; h_1(key)&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; h_2(key) &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Так же у нас есть таблица &amp;lt;tex&amp;gt; table &amp;lt;/tex&amp;gt; величиной &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, состоящая из объектов типа &amp;lt;tex&amp;gt; item &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
'''Вставка'''&lt;br /&gt;
 '''function''' add('''Item''' item)&lt;br /&gt;
      x = h1(item.key)&lt;br /&gt;
      y = h2(item.key)&lt;br /&gt;
         '''for''' (i = 0..m)    	&lt;br /&gt;
            '''if''' table[x] == '''null'''&lt;br /&gt;
               table[x] = item&lt;br /&gt;
            '''return'''      &lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      table.resize()&amp;lt;span style=&amp;quot;color:Green&amp;quot;&amp;gt;// ошибка, требуется увеличить размер таблицы&lt;br /&gt;
&lt;br /&gt;
'''Поиск'''&lt;br /&gt;
 '''Item''' search('''Item''' key)&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m)&lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key&lt;br /&gt;
               '''return''' table[x]&lt;br /&gt;
            '''else'''&lt;br /&gt;
               '''return''' '''null'''&lt;br /&gt;
      x = (x + y) '''mod''' m   &lt;br /&gt;
      '''return''' '''null'''&lt;br /&gt;
&lt;br /&gt;
===Реализация с удалением===&lt;br /&gt;
Что бы наша хеш-таблица поддерживала удаление, требуется добавить массив &amp;lt;tex&amp;gt;deleted&amp;lt;/tex&amp;gt; типов &amp;lt;tex&amp;gt;bool&amp;lt;/tex&amp;gt;, равный по величине массиву &amp;lt;tex&amp;gt;table&amp;lt;/tex&amp;gt;. Теперь при удалении мы просто будем помечать наш объект ''как удалённый'', а при добавлении как ''не удалённый'' и замещать новым добавляемым объектом. При поиске, помимо равенства ключей, мы смотрим, удалён ли элемент, если да, то идём дальше.&lt;br /&gt;
&lt;br /&gt;
'''Вставка'''&lt;br /&gt;
 '''function''' add('''Item''' item)&lt;br /&gt;
      x = h1(item.key)&lt;br /&gt;
      y = h2(item.key)&lt;br /&gt;
      '''for''' (i = 0..m)   	&lt;br /&gt;
         '''if''' table[x] == '''null''' '''or''' deleted[x]&lt;br /&gt;
            table[x] = item&lt;br /&gt;
            deleted[x] = '''false'''&lt;br /&gt;
            '''return'''      &lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      table.resize()&amp;lt;span style=&amp;quot;color:Green&amp;quot;&amp;gt;// ошибка, требуется увеличить размер таблицы&lt;br /&gt;
'''Поиск'''&lt;br /&gt;
 '''Item''' search('''Item''' key)&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m) &lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key '''and''' !deleted[x]&lt;br /&gt;
               '''return''' table[x]&lt;br /&gt;
         '''else'''&lt;br /&gt;
            '''return''' '''null'''&lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      '''return''' '''null'''&lt;br /&gt;
&lt;br /&gt;
'''Удаление'''&lt;br /&gt;
 '''function''' remove('''Item''' key)&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m)&lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key&lt;br /&gt;
               deleted[x] = '''true'''&lt;br /&gt;
         '''else''' &lt;br /&gt;
            '''return'''&lt;br /&gt;
      x = (x + y) '''mod''' m&lt;br /&gt;
&lt;br /&gt;
== Разрешение коллизий в Java 8==&lt;br /&gt;
В Java 8 для разрешения коллизий используется модифицированный метод цепочек. Суть его заключается в том, что когда количество элементов в корзине превышает определенное значение, данный бакет переходит от использования связного списка к использованию сбалансированного дерева. Такой подход позволяет улучшить производительность с &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt;.  Данный способ используется в таких коллекциях как HashMap, LinkedHashMap и ConcurrentHashMap.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Hashing_in_Java8.png|500px|Хеширование в Java 8.]]&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
* [[Хеширование]]&lt;br /&gt;
* [[Хеширование_кукушки|Хеширование кукушки]]&lt;br /&gt;
* [[Идеальное_хеширование|Идеальное хеширование]]&lt;br /&gt;
&lt;br /&gt;
== Источники информации ==&lt;br /&gt;
* Бакнелл Дж. М. «Фундаментальные алгоритмы и структуры данных в Delphi», 2003&lt;br /&gt;
* Кормен, Томас Х., Лейзерсон, Чарльз И., Ривест, Рональд Л., Штайн Клиффорд «Алгоритмы: построение и анализ», 2-е издание. Пер. с англ. — М.:Издательский дом &amp;quot;Вильямс&amp;quot;, 2010.— Парал. тит. англ. — ISBN 978-5-8459-0857-5 (рус.)&lt;br /&gt;
* Дональд Кнут. «Искусство программирования, том 3. Сортировка и поиск» {{---}} «Вильямс», 2007 г.{{---}} ISBN 0-201-89685-0&lt;br /&gt;
* Седжвик Р. «Фундаментальные алгоритмы на C. Части 1-4. Анализ. Структуры данных. Сортировка. Поиск», 2003&lt;br /&gt;
* [http://openjdk.java.net/jeps/180 Handle Frequent HashMap Collisions with Balanced Trees]&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Double_hashing Wikipedia {{---}} Double_hashing]&lt;br /&gt;
* [http://ru.wikipedia.org/wiki/%D0%A5%D0%B5%D1%88-%D1%82%D0%B0%D0%B1%D0%BB%D0%B8%D1%86%D0%B0 Разрешение коллизий]&lt;br /&gt;
* [http://rain.ifmo.ru/cat/view.php/vis/hashtables/hash-2001-2 Пример хеш таблицы]&lt;br /&gt;
* [http://research.cs.vt.edu/AVresearch/hashing/double.php Пример хеш таблицы с двойным хешированием]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория: Хеширование]]&lt;/div&gt;</summary>
		<author><name>Kozichuk</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%A0%D0%B0%D0%B7%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BA%D0%BE%D0%BB%D0%BB%D0%B8%D0%B7%D0%B8%D0%B9&amp;diff=47155</id>
		<title>Разрешение коллизий</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A0%D0%B0%D0%B7%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BA%D0%BE%D0%BB%D0%BB%D0%B8%D0%B7%D0%B8%D0%B9&amp;diff=47155"/>
				<updated>2015-05-31T21:27:51Z</updated>
		
		<summary type="html">&lt;p&gt;Kozichuk: /* Источники Информации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Разрешение [[Хеш-таблица|коллизий]]''' (англ. collision resolution) в [[Хеш-таблица|хеш-таблице]], задача, решаемая несколькими способами: метод цепочек, открытая адресация и т.д. Очень важно сводить количество коллизий к минимуму, так как это увеличивает время работы с хеш-таблицами. &lt;br /&gt;
&lt;br /&gt;
== Разрешение коллизий с помощью цепочек ==&lt;br /&gt;
[[Файл:open_hash.png|thumb|380px|right|Разрешение коллизий при помощи цепочек.]]&lt;br /&gt;
Каждая ячейка &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; массива &amp;lt;tex&amp;gt;H&amp;lt;/tex&amp;gt; содержит указатель на начало [[Список|списка]] всех элементов, хеш-код которых равен &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, либо указывает на их отсутствие. Коллизии приводят к тому, что появляются списки размером больше одного элемента.&lt;br /&gt;
&lt;br /&gt;
Время, необходимое для вставки в наихудшем случае равно &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;. Это операция выполняет быстро, так как считается, что вставляемый элемент отсутствует в таблице, но если потребуется, то перед вставкой мы можем выполнить поиск этого элемента.&lt;br /&gt;
&lt;br /&gt;
Время работы поиска в наихудшем случае пропорционально длине списка, а если все &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; ключей захешировались в одну и ту же ячейку (создав список длиной &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;) время поиска будет равно &amp;lt;tex&amp;gt;\Theta(n)&amp;lt;/tex&amp;gt; плюс время вычисления хеш-функции, что ничуть не лучше, чем использование связного списка для хранения всех &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; элементов.&lt;br /&gt;
&lt;br /&gt;
Удаления элемента может быть выполнено за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;, как и вставка, при использовании двухсвязного списка.&lt;br /&gt;
&lt;br /&gt;
== Линейное разрешение коллизий ==&lt;br /&gt;
[[Файл:close_hash.png|thumb|380px|right|Пример хеш-таблицы с открытой адресацией и линейным пробированием.]]&lt;br /&gt;
Все элементы хранятся непосредственно в хеш-таблице, без использования связных списков. В отличии от хеширования с цепочками, при использовании этого метода может возникнуть ситуация, когда хеш-таблица окажется полностью заполненной, следовательно будет невозможно добавлять в неё новые элементы. Так что при возникновении такой ситуации решением может быть динамическое увеличение размера хеш-таблицы, с одновременной её перестройкой.&lt;br /&gt;
&lt;br /&gt;
=== Стратегии поиска ===&lt;br /&gt;
&lt;br /&gt;
''' Последовательный поиск '''&lt;br /&gt;
&lt;br /&gt;
При попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt;i+1, i+2, i+3&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку. В неё и запишем элемент.&lt;br /&gt;
&lt;br /&gt;
[[Файл:hashtables1.png|400px|Последовательный поиск, частный случай линейного поиска.]]&lt;br /&gt;
&lt;br /&gt;
''' Линейный поиск '''&lt;br /&gt;
&lt;br /&gt;
Выбираем шаг &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;. При попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt;i+(1 \cdot q), i+(2 \cdot q), i+(3 \cdot q)&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку. В неё и запишем элемент.&lt;br /&gt;
По сути последовательный поиск - частный случай линейного, где &amp;lt;tex&amp;gt;q=1&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Hashtables56.PNG|400px|Линейный поиск с шагом q.]]&lt;br /&gt;
&lt;br /&gt;
''' Квадратичный поиск '''&lt;br /&gt;
&lt;br /&gt;
Шаг &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; не фиксирован, а изменяется квадратично: &amp;lt;tex&amp;gt;q = 1,4,9,16...&amp;lt;/tex&amp;gt;. Соответственно при попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt; i+1, i+4, i+9&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку.&lt;br /&gt;
&lt;br /&gt;
[[Файл:hashtables3.png|400px|Квадратичный поиск.]]&lt;br /&gt;
&lt;br /&gt;
=== Проверка наличия элемента в таблице===&lt;br /&gt;
&lt;br /&gt;
Проверка осуществляется аналогично добавлению: мы проверяем ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; и другие, в соответствии с выбранной стратегией, пока не найдём искомый элемент или свободную ячейку.&lt;br /&gt;
&lt;br /&gt;
При поиске элемента может получится так, что мы дойдём до конца таблицы. Обычно поиск продолжается, начиная с другого конца, пока мы не придём в ту ячейку, откуда начинался поиск.&lt;br /&gt;
&lt;br /&gt;
=== Проблемы данных стратегий ===&lt;br /&gt;
&lt;br /&gt;
Проблем две — крайне нетривиальное удаление элемента из таблицы и образование кластеров  — последовательностей занятых ячеек.&lt;br /&gt;
&lt;br /&gt;
Кластеризация замедляет все операции с хеш-таблицей: при добавлении требуется перебирать всё больше элементов, при проверке тоже. Чем больше в таблице элементов, тем больше в ней кластеры и тем выше вероятность того, что добавляемый элемент попадёт в кластер.&lt;br /&gt;
Для защиты от кластеризации используется двойное хеширование и [[Хеширование кукушки|хеширование кукушки]].&lt;br /&gt;
&lt;br /&gt;
=== Удаление элемента без пометок ===&lt;br /&gt;
&lt;br /&gt;
Рассуждение будет описывать случай с линейным поиском хеша. Будем при удалении элемента сдвигать всё последующие на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; позиций назад. При этом:&lt;br /&gt;
* если в цепочке встречается элемент с другим хешем, то он должен остаться на своём месте (такая ситуация может возникнуть если оставшаяся часть цепочки была добавлена позже этого элемента)&lt;br /&gt;
* в цепочке не должно оставаться &amp;quot;дырок&amp;quot;, тогда любой элемент с данным хешем будет доступен из начала цепи&lt;br /&gt;
&lt;br /&gt;
Учитывая это будем действовать следующим образом: при поиске следующего элемента цепочки будем пропускать все ячейки с другим значением хеша, первый найденный элемент копировать в текущую ячейку, и затем рекурсивно его удалять. Если такой следующей ячейки нет, то текущий элемент можно просто удалить, сторонние цепочки при этом не разрушатся (чего нельзя сказать про случай квадратичного поиска).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
''' Псевдокод '''&lt;br /&gt;
&lt;br /&gt;
 '''function''' delete('''Item''' i) &lt;br /&gt;
      j = i + q&lt;br /&gt;
      '''while''' table[j] == '''null''' '''or''' table[j].key != table[i].key&lt;br /&gt;
         '''if''' table[j] == '''null'''&lt;br /&gt;
            table[i] = '''null'''&lt;br /&gt;
            '''return'''&lt;br /&gt;
         j += q&lt;br /&gt;
      table[i] = table[j]&lt;br /&gt;
      delete(j);    &lt;br /&gt;
&lt;br /&gt;
Хеш-таблицу считаем зацикленной&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Утверждение&lt;br /&gt;
|about=о времени работы&lt;br /&gt;
|statement=Асимптотически время работы &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\mathrm{find}&amp;lt;/tex&amp;gt; совпадают&lt;br /&gt;
|proof=&lt;br /&gt;
Заметим что указатель &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; в каждой итерации перемещается вперёд на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; (с учётом рекурсивных вызовов &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt;). То есть этот алгоритм последовательно пройдёт по цепочке от удаляемого элемента до последнего {{---}} с учётом вызова &amp;lt;tex&amp;gt;\mathrm{find}&amp;lt;/tex&amp;gt; собственно для нахождения удаляемого элемента, мы посетим все ячейки цепи.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Вариант с зацикливанием мы не рассматриваем, поскольку если &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; взаимнопросто с размером хеш-таблицы, то для зацикливания в ней вообще не должно быть свободных позиций&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Теперь докажем почему этот алгоритм работает. Собственно нам требуется сохранение трёх условий.&lt;br /&gt;
* В редактируемой цепи не остаётся дырок&lt;br /&gt;
Докажем по индукции. Если на данной итерации мы просто удаляем элемент (база), то после него ничего нет, всё верно. Если же нет, то вызванный в конце &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt; (см. псевдокод) заметёт созданную дыру (скопированный элемент), и сам, по предположению, новых не создаст.&lt;br /&gt;
* Элементы, которые уже на своих местах, не должны быть сдвинуты.&lt;br /&gt;
Это учтено.&lt;br /&gt;
* В других цепочках не появятся дыры&lt;br /&gt;
Противное возможно только в том случае, если какой-то элемент был действительно удалён. Удаляем мы только последнюю ячейку в цепи, и если бы на её месте возникла дыра для сторонней цепочки, это бы означало что элемент, стоящий на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; позиций назад, одновременно принадлежал нашей и другой цепочкам, что невозможно.&lt;br /&gt;
&lt;br /&gt;
==Двойное хеширование==&lt;br /&gt;
'''Двойное хеширование''' (англ. double hashing) {{---}} метод борьбы с коллизиями, возникающими при открытой адресации, основанный на использовании двух хеш-функций для построения различных последовательностей исследования хеш-таблицы.&lt;br /&gt;
&lt;br /&gt;
===Принцип двойного хеширования===&lt;br /&gt;
При двойном хешировании используются две независимые хеш-функции &amp;lt;tex&amp;gt; h_1(k) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; h_2(k) &amp;lt;/tex&amp;gt;. Пусть &amp;lt;tex&amp;gt; k &amp;lt;/tex&amp;gt; {{---}} это наш ключ, &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt; {{---}} размер нашей таблицы, &amp;lt;tex&amp;gt;n \bmod m &amp;lt;/tex&amp;gt; {{---}} остаток от деления &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt; на &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, тогда сначала исследуется ячейка с адресом &amp;lt;tex&amp;gt; h_1(k) &amp;lt;/tex&amp;gt;, если она уже занята, то рассматривается &amp;lt;tex&amp;gt; (h_1(k) +  h_2(k)) \bmod m &amp;lt;/tex&amp;gt;, затем &amp;lt;tex&amp;gt; (h_1(k) +  2 \cdot h_2(k)) \bmod m &amp;lt;/tex&amp;gt; и так далее. В общем случае идёт проверка последовательности ячеек &amp;lt;tex&amp;gt; (h_1(k) +  i \cdot h_2(k)) \bmod m &amp;lt;/tex&amp;gt; где &amp;lt;tex&amp;gt;  i = (0, 1, \; ... \;,  m - 1) &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, операции вставки, удаления и поиска в лучшем случае выполняются за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;, в худшем {{---}} за &amp;lt;tex&amp;gt;O(m)&amp;lt;/tex&amp;gt;, что не отличается от обычного [[Открытое_и_закрытое_хеширование#Линейное разрешение коллизий|линейного разрешения коллизий]].&lt;br /&gt;
Однако в среднем, при грамотном выборе хеш-функций, двойное хеширование будет выдавать лучшие результаты, за счёт того, что вероятность совпадения значений сразу двух независимых хеш-функций ниже, чем одной.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt;\forall x \neq y \; \exists h_1,h_2 : p(h_1(x)=h_1(y))&amp;gt; p((h_1(x)=h_1(y)) \land (h_2(x)=h_2(y)))&amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Выбор хеш-функций===&lt;br /&gt;
&amp;lt;tex&amp;gt; h_1 &amp;lt;/tex&amp;gt; может быть обычной хеш-функцией. Однако чтобы последовательность исследования могла охватить всю таблицу, &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; должна возвращать значения:&lt;br /&gt;
*не равные &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
*независимые  от &amp;lt;tex&amp;gt; h_1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
*взаимно простые с величиной хеш-таблицы&lt;br /&gt;
&lt;br /&gt;
Есть два удобных способа это сделать. Первый состоит в том, что в качестве размера таблицы используется простое число, а &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; возвращает натуральные числа, меньшие &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;. Второй {{---}} размер таблицы является степенью двойки, а &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; возвращает нечетные значения.&lt;br /&gt;
&lt;br /&gt;
Например, если размер таблицы равен &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, то в качестве &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; можно использовать функцию вида &amp;lt;tex&amp;gt; h_2(k) = k \bmod (m-1) + 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Файл: Вставка при двойном хэшировании.svg.jpeg|thumb|right|Вставка при двойном хешировании]]&lt;br /&gt;
&lt;br /&gt;
===Пример===&lt;br /&gt;
&lt;br /&gt;
Показана хеш-таблица размером 13 ячеек, в которой используются вспомогательные функции:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h(k,i) = (h_1(k) + i \cdot h_2(k)) \bmod 13 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h_1(k) = k \bmod 13 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h_2(k) = 1 + k \bmod 11 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Мы хотим вставить ключ 14. Изначально &amp;lt;tex&amp;gt; i = 0 &amp;lt;/tex&amp;gt;. Тогда &amp;lt;tex&amp;gt; h(14,0) = (h_1(14) + 0\cdot h_2(14)) \bmod 13 = 1 &amp;lt;/tex&amp;gt;. Но ячейка с индексом 1 занята, поэтому увеличиваем &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; на 1 и пересчитываем значение хеш-функции. Делаем так, пока не дойдем до пустой ячейки. При &amp;lt;tex&amp;gt; i = 2 &amp;lt;/tex&amp;gt; получаем &amp;lt;tex&amp;gt; h(14,2) = (h_1(14) + 2\cdot h_2(14)) \bmod 13 = 9 &amp;lt;/tex&amp;gt;. Ячейка с номером 9 свободна, значит записываем туда наш ключ.&lt;br /&gt;
&lt;br /&gt;
Таким образом, основная особенность двойного хеширования состоит в том, что при различных &amp;lt;tex&amp;gt; k &amp;lt;/tex&amp;gt; пара &amp;lt;tex&amp;gt; (h_1(k),h_2(k)) &amp;lt;/tex&amp;gt; дает различные последовательности ячеек для исследования.&lt;br /&gt;
&lt;br /&gt;
===Простая реализация===&lt;br /&gt;
Пусть у нас есть некоторый объект &amp;lt;tex&amp;gt; item &amp;lt;/tex&amp;gt;, в котором определено поле &amp;lt;tex&amp;gt; key &amp;lt;/tex&amp;gt;, от которого можно вычислить хеш-функции &amp;lt;tex&amp;gt; h_1(key)&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; h_2(key) &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Так же у нас есть таблица &amp;lt;tex&amp;gt; table &amp;lt;/tex&amp;gt; величиной &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, состоящая из объектов типа &amp;lt;tex&amp;gt; item &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
'''Вставка'''&lt;br /&gt;
 '''function''' add('''Item''' item)&lt;br /&gt;
      x = h1(item.key)&lt;br /&gt;
      y = h2(item.key)&lt;br /&gt;
         '''for''' (i = 0..m)    	&lt;br /&gt;
            '''if''' table[x] == '''null'''&lt;br /&gt;
               table[x] = item&lt;br /&gt;
            '''return'''      &lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      table.resize()&amp;lt;span style=&amp;quot;color:Green&amp;quot;&amp;gt;// ошибка, требуется увеличить размер таблицы&lt;br /&gt;
&lt;br /&gt;
'''Поиск'''&lt;br /&gt;
 '''Item''' search('''Item''' key)&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m)&lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key&lt;br /&gt;
               '''return''' table[x]&lt;br /&gt;
            '''else'''&lt;br /&gt;
               '''return''' '''null'''&lt;br /&gt;
      x = (x + y) '''mod''' m   &lt;br /&gt;
      '''return''' '''null'''&lt;br /&gt;
&lt;br /&gt;
===Реализация с удалением===&lt;br /&gt;
Что бы наша хеш-таблица поддерживала удаление, требуется добавить массив &amp;lt;tex&amp;gt;deleted&amp;lt;/tex&amp;gt; типов &amp;lt;tex&amp;gt;bool&amp;lt;/tex&amp;gt;, равный по величине массиву &amp;lt;tex&amp;gt;table&amp;lt;/tex&amp;gt;. Теперь при удалении мы просто будем помечать наш объект ''как удалённый'', а при добавлении как ''не удалённый'' и замещать новым добавляемым объектом. При поиске, помимо равенства ключей, мы смотрим, удалён ли элемент, если да, то идём дальше.&lt;br /&gt;
&lt;br /&gt;
'''Вставка'''&lt;br /&gt;
 '''function''' add('''Item''' item)&lt;br /&gt;
      x = h1(item.key)&lt;br /&gt;
      y = h2(item.key)&lt;br /&gt;
      '''for''' (i = 0..m)   	&lt;br /&gt;
         '''if''' table[x] == '''null''' '''or''' deleted[x]&lt;br /&gt;
            table[x] = item&lt;br /&gt;
            deleted[x] = '''false'''&lt;br /&gt;
            '''return'''      &lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      table.resize()&amp;lt;span style=&amp;quot;color:Green&amp;quot;&amp;gt;// ошибка, требуется увеличить размер таблицы&lt;br /&gt;
'''Поиск'''&lt;br /&gt;
 '''Item''' search('''Item''' key)&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m) &lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key '''and''' !deleted[x]&lt;br /&gt;
               '''return''' table[x]&lt;br /&gt;
         '''else'''&lt;br /&gt;
            '''return''' '''null'''&lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      '''return''' '''null'''&lt;br /&gt;
&lt;br /&gt;
'''Удаление'''&lt;br /&gt;
 '''function''' remove('''Item''' key)&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m)&lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key&lt;br /&gt;
               deleted[x] = '''true'''&lt;br /&gt;
         '''else''' &lt;br /&gt;
            '''return'''&lt;br /&gt;
      x = (x + y) '''mod''' m&lt;br /&gt;
&lt;br /&gt;
== Разрешение коллизий в Java 8==&lt;br /&gt;
В Java 8 для разрешения коллизий используется модифицированный метод цепочек. Суть его заключается в том, что когда количество элементов в корзине превышает определенное значение, данный бакет переходит от использования связного списка к использованию сбалансированного дерева. Такой подход позволяет улучшить производительность с &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt;.  Данный способ используется в таких коллекциях как HashMap, LinkedHashMap и ConcurrentHashMap.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Hashing_in_Java8.png|500px|Хеширование в Java 8.]]&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
* [[Хеширование]]&lt;br /&gt;
* [[Хеширование_кукушки|Хеширование кукушки]]&lt;br /&gt;
* [[Идеальное_хеширование|Идеальное хеширование]]&lt;br /&gt;
&lt;br /&gt;
== Источники информации ==&lt;br /&gt;
* Бакнелл Дж. М. «Фундаментальные алгоритмы и структуры данных в Delphi», 2003&lt;br /&gt;
* Кормен, Томас Х., Лейзерсон, Чарльз И., Ривест, Рональд Л., Штайн Клиффорд «Алгоритмы: построение и анализ», 2-е издание. Пер. с англ. — М.:Издательский дом &amp;quot;Вильямс&amp;quot;, 2010.— Парал. тит. англ. — ISBN 978-5-8459-0857-5 (рус.)&lt;br /&gt;
* Дональд Кнут. «Искусство программирования, том 3. Сортировка и поиск» {{---}} «Вильямс», 2007 г.{{---}} ISBN 0-201-89685-0&lt;br /&gt;
* Седжвик Р. «Фундаментальные алгоритмы на C. Части 1-4. Анализ. Структуры данных. Сортировка. Поиск», 2003&lt;br /&gt;
* [http://openjdk.java.net/jeps/180 Handle Frequent HashMap Collisions with Balanced Trees]&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Double_hashing Wikipedia {{---}} Double_hashing]&lt;br /&gt;
* [http://ru.wikipedia.org/wiki/%D0%A5%D0%B5%D1%88-%D1%82%D0%B0%D0%B1%D0%BB%D0%B8%D1%86%D0%B0 Разрешение коллизий]&lt;br /&gt;
* [http://rain.ifmo.ru/cat/view.php/vis/hashtables/hash-2001-2 Пример хеш таблицы]&lt;br /&gt;
* [http://research.cs.vt.edu/AVresearch/hashing/double.php Пример хеш таблицы с двойным хешированием]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория: Хеширование]]&lt;/div&gt;</summary>
		<author><name>Kozichuk</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%A0%D0%B0%D0%B7%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BA%D0%BE%D0%BB%D0%BB%D0%B8%D0%B7%D0%B8%D0%B9&amp;diff=47154</id>
		<title>Разрешение коллизий</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A0%D0%B0%D0%B7%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BA%D0%BE%D0%BB%D0%BB%D0%B8%D0%B7%D0%B8%D0%B9&amp;diff=47154"/>
				<updated>2015-05-31T21:27:23Z</updated>
		
		<summary type="html">&lt;p&gt;Kozichuk: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Разрешение [[Хеш-таблица|коллизий]]''' (англ. collision resolution) в [[Хеш-таблица|хеш-таблице]], задача, решаемая несколькими способами: метод цепочек, открытая адресация и т.д. Очень важно сводить количество коллизий к минимуму, так как это увеличивает время работы с хеш-таблицами. &lt;br /&gt;
&lt;br /&gt;
== Разрешение коллизий с помощью цепочек ==&lt;br /&gt;
[[Файл:open_hash.png|thumb|380px|right|Разрешение коллизий при помощи цепочек.]]&lt;br /&gt;
Каждая ячейка &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; массива &amp;lt;tex&amp;gt;H&amp;lt;/tex&amp;gt; содержит указатель на начало [[Список|списка]] всех элементов, хеш-код которых равен &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, либо указывает на их отсутствие. Коллизии приводят к тому, что появляются списки размером больше одного элемента.&lt;br /&gt;
&lt;br /&gt;
Время, необходимое для вставки в наихудшем случае равно &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;. Это операция выполняет быстро, так как считается, что вставляемый элемент отсутствует в таблице, но если потребуется, то перед вставкой мы можем выполнить поиск этого элемента.&lt;br /&gt;
&lt;br /&gt;
Время работы поиска в наихудшем случае пропорционально длине списка, а если все &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; ключей захешировались в одну и ту же ячейку (создав список длиной &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;) время поиска будет равно &amp;lt;tex&amp;gt;\Theta(n)&amp;lt;/tex&amp;gt; плюс время вычисления хеш-функции, что ничуть не лучше, чем использование связного списка для хранения всех &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; элементов.&lt;br /&gt;
&lt;br /&gt;
Удаления элемента может быть выполнено за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;, как и вставка, при использовании двухсвязного списка.&lt;br /&gt;
&lt;br /&gt;
== Линейное разрешение коллизий ==&lt;br /&gt;
[[Файл:close_hash.png|thumb|380px|right|Пример хеш-таблицы с открытой адресацией и линейным пробированием.]]&lt;br /&gt;
Все элементы хранятся непосредственно в хеш-таблице, без использования связных списков. В отличии от хеширования с цепочками, при использовании этого метода может возникнуть ситуация, когда хеш-таблица окажется полностью заполненной, следовательно будет невозможно добавлять в неё новые элементы. Так что при возникновении такой ситуации решением может быть динамическое увеличение размера хеш-таблицы, с одновременной её перестройкой.&lt;br /&gt;
&lt;br /&gt;
=== Стратегии поиска ===&lt;br /&gt;
&lt;br /&gt;
''' Последовательный поиск '''&lt;br /&gt;
&lt;br /&gt;
При попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt;i+1, i+2, i+3&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку. В неё и запишем элемент.&lt;br /&gt;
&lt;br /&gt;
[[Файл:hashtables1.png|400px|Последовательный поиск, частный случай линейного поиска.]]&lt;br /&gt;
&lt;br /&gt;
''' Линейный поиск '''&lt;br /&gt;
&lt;br /&gt;
Выбираем шаг &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;. При попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt;i+(1 \cdot q), i+(2 \cdot q), i+(3 \cdot q)&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку. В неё и запишем элемент.&lt;br /&gt;
По сути последовательный поиск - частный случай линейного, где &amp;lt;tex&amp;gt;q=1&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Hashtables56.PNG|400px|Линейный поиск с шагом q.]]&lt;br /&gt;
&lt;br /&gt;
''' Квадратичный поиск '''&lt;br /&gt;
&lt;br /&gt;
Шаг &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; не фиксирован, а изменяется квадратично: &amp;lt;tex&amp;gt;q = 1,4,9,16...&amp;lt;/tex&amp;gt;. Соответственно при попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt; i+1, i+4, i+9&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку.&lt;br /&gt;
&lt;br /&gt;
[[Файл:hashtables3.png|400px|Квадратичный поиск.]]&lt;br /&gt;
&lt;br /&gt;
=== Проверка наличия элемента в таблице===&lt;br /&gt;
&lt;br /&gt;
Проверка осуществляется аналогично добавлению: мы проверяем ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; и другие, в соответствии с выбранной стратегией, пока не найдём искомый элемент или свободную ячейку.&lt;br /&gt;
&lt;br /&gt;
При поиске элемента может получится так, что мы дойдём до конца таблицы. Обычно поиск продолжается, начиная с другого конца, пока мы не придём в ту ячейку, откуда начинался поиск.&lt;br /&gt;
&lt;br /&gt;
=== Проблемы данных стратегий ===&lt;br /&gt;
&lt;br /&gt;
Проблем две — крайне нетривиальное удаление элемента из таблицы и образование кластеров  — последовательностей занятых ячеек.&lt;br /&gt;
&lt;br /&gt;
Кластеризация замедляет все операции с хеш-таблицей: при добавлении требуется перебирать всё больше элементов, при проверке тоже. Чем больше в таблице элементов, тем больше в ней кластеры и тем выше вероятность того, что добавляемый элемент попадёт в кластер.&lt;br /&gt;
Для защиты от кластеризации используется двойное хеширование и [[Хеширование кукушки|хеширование кукушки]].&lt;br /&gt;
&lt;br /&gt;
=== Удаление элемента без пометок ===&lt;br /&gt;
&lt;br /&gt;
Рассуждение будет описывать случай с линейным поиском хеша. Будем при удалении элемента сдвигать всё последующие на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; позиций назад. При этом:&lt;br /&gt;
* если в цепочке встречается элемент с другим хешем, то он должен остаться на своём месте (такая ситуация может возникнуть если оставшаяся часть цепочки была добавлена позже этого элемента)&lt;br /&gt;
* в цепочке не должно оставаться &amp;quot;дырок&amp;quot;, тогда любой элемент с данным хешем будет доступен из начала цепи&lt;br /&gt;
&lt;br /&gt;
Учитывая это будем действовать следующим образом: при поиске следующего элемента цепочки будем пропускать все ячейки с другим значением хеша, первый найденный элемент копировать в текущую ячейку, и затем рекурсивно его удалять. Если такой следующей ячейки нет, то текущий элемент можно просто удалить, сторонние цепочки при этом не разрушатся (чего нельзя сказать про случай квадратичного поиска).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
''' Псевдокод '''&lt;br /&gt;
&lt;br /&gt;
 '''function''' delete('''Item''' i) &lt;br /&gt;
      j = i + q&lt;br /&gt;
      '''while''' table[j] == '''null''' '''or''' table[j].key != table[i].key&lt;br /&gt;
         '''if''' table[j] == '''null'''&lt;br /&gt;
            table[i] = '''null'''&lt;br /&gt;
            '''return'''&lt;br /&gt;
         j += q&lt;br /&gt;
      table[i] = table[j]&lt;br /&gt;
      delete(j);    &lt;br /&gt;
&lt;br /&gt;
Хеш-таблицу считаем зацикленной&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Утверждение&lt;br /&gt;
|about=о времени работы&lt;br /&gt;
|statement=Асимптотически время работы &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\mathrm{find}&amp;lt;/tex&amp;gt; совпадают&lt;br /&gt;
|proof=&lt;br /&gt;
Заметим что указатель &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; в каждой итерации перемещается вперёд на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; (с учётом рекурсивных вызовов &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt;). То есть этот алгоритм последовательно пройдёт по цепочке от удаляемого элемента до последнего {{---}} с учётом вызова &amp;lt;tex&amp;gt;\mathrm{find}&amp;lt;/tex&amp;gt; собственно для нахождения удаляемого элемента, мы посетим все ячейки цепи.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Вариант с зацикливанием мы не рассматриваем, поскольку если &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; взаимнопросто с размером хеш-таблицы, то для зацикливания в ней вообще не должно быть свободных позиций&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Теперь докажем почему этот алгоритм работает. Собственно нам требуется сохранение трёх условий.&lt;br /&gt;
* В редактируемой цепи не остаётся дырок&lt;br /&gt;
Докажем по индукции. Если на данной итерации мы просто удаляем элемент (база), то после него ничего нет, всё верно. Если же нет, то вызванный в конце &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt; (см. псевдокод) заметёт созданную дыру (скопированный элемент), и сам, по предположению, новых не создаст.&lt;br /&gt;
* Элементы, которые уже на своих местах, не должны быть сдвинуты.&lt;br /&gt;
Это учтено.&lt;br /&gt;
* В других цепочках не появятся дыры&lt;br /&gt;
Противное возможно только в том случае, если какой-то элемент был действительно удалён. Удаляем мы только последнюю ячейку в цепи, и если бы на её месте возникла дыра для сторонней цепочки, это бы означало что элемент, стоящий на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; позиций назад, одновременно принадлежал нашей и другой цепочкам, что невозможно.&lt;br /&gt;
&lt;br /&gt;
==Двойное хеширование==&lt;br /&gt;
'''Двойное хеширование''' (англ. double hashing) {{---}} метод борьбы с коллизиями, возникающими при открытой адресации, основанный на использовании двух хеш-функций для построения различных последовательностей исследования хеш-таблицы.&lt;br /&gt;
&lt;br /&gt;
===Принцип двойного хеширования===&lt;br /&gt;
При двойном хешировании используются две независимые хеш-функции &amp;lt;tex&amp;gt; h_1(k) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; h_2(k) &amp;lt;/tex&amp;gt;. Пусть &amp;lt;tex&amp;gt; k &amp;lt;/tex&amp;gt; {{---}} это наш ключ, &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt; {{---}} размер нашей таблицы, &amp;lt;tex&amp;gt;n \bmod m &amp;lt;/tex&amp;gt; {{---}} остаток от деления &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt; на &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, тогда сначала исследуется ячейка с адресом &amp;lt;tex&amp;gt; h_1(k) &amp;lt;/tex&amp;gt;, если она уже занята, то рассматривается &amp;lt;tex&amp;gt; (h_1(k) +  h_2(k)) \bmod m &amp;lt;/tex&amp;gt;, затем &amp;lt;tex&amp;gt; (h_1(k) +  2 \cdot h_2(k)) \bmod m &amp;lt;/tex&amp;gt; и так далее. В общем случае идёт проверка последовательности ячеек &amp;lt;tex&amp;gt; (h_1(k) +  i \cdot h_2(k)) \bmod m &amp;lt;/tex&amp;gt; где &amp;lt;tex&amp;gt;  i = (0, 1, \; ... \;,  m - 1) &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, операции вставки, удаления и поиска в лучшем случае выполняются за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;, в худшем {{---}} за &amp;lt;tex&amp;gt;O(m)&amp;lt;/tex&amp;gt;, что не отличается от обычного [[Открытое_и_закрытое_хеширование#Линейное разрешение коллизий|линейного разрешения коллизий]].&lt;br /&gt;
Однако в среднем, при грамотном выборе хеш-функций, двойное хеширование будет выдавать лучшие результаты, за счёт того, что вероятность совпадения значений сразу двух независимых хеш-функций ниже, чем одной.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt;\forall x \neq y \; \exists h_1,h_2 : p(h_1(x)=h_1(y))&amp;gt; p((h_1(x)=h_1(y)) \land (h_2(x)=h_2(y)))&amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Выбор хеш-функций===&lt;br /&gt;
&amp;lt;tex&amp;gt; h_1 &amp;lt;/tex&amp;gt; может быть обычной хеш-функцией. Однако чтобы последовательность исследования могла охватить всю таблицу, &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; должна возвращать значения:&lt;br /&gt;
*не равные &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
*независимые  от &amp;lt;tex&amp;gt; h_1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
*взаимно простые с величиной хеш-таблицы&lt;br /&gt;
&lt;br /&gt;
Есть два удобных способа это сделать. Первый состоит в том, что в качестве размера таблицы используется простое число, а &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; возвращает натуральные числа, меньшие &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;. Второй {{---}} размер таблицы является степенью двойки, а &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; возвращает нечетные значения.&lt;br /&gt;
&lt;br /&gt;
Например, если размер таблицы равен &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, то в качестве &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; можно использовать функцию вида &amp;lt;tex&amp;gt; h_2(k) = k \bmod (m-1) + 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Файл: Вставка при двойном хэшировании.svg.jpeg|thumb|right|Вставка при двойном хешировании]]&lt;br /&gt;
&lt;br /&gt;
===Пример===&lt;br /&gt;
&lt;br /&gt;
Показана хеш-таблица размером 13 ячеек, в которой используются вспомогательные функции:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h(k,i) = (h_1(k) + i \cdot h_2(k)) \bmod 13 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h_1(k) = k \bmod 13 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h_2(k) = 1 + k \bmod 11 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Мы хотим вставить ключ 14. Изначально &amp;lt;tex&amp;gt; i = 0 &amp;lt;/tex&amp;gt;. Тогда &amp;lt;tex&amp;gt; h(14,0) = (h_1(14) + 0\cdot h_2(14)) \bmod 13 = 1 &amp;lt;/tex&amp;gt;. Но ячейка с индексом 1 занята, поэтому увеличиваем &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; на 1 и пересчитываем значение хеш-функции. Делаем так, пока не дойдем до пустой ячейки. При &amp;lt;tex&amp;gt; i = 2 &amp;lt;/tex&amp;gt; получаем &amp;lt;tex&amp;gt; h(14,2) = (h_1(14) + 2\cdot h_2(14)) \bmod 13 = 9 &amp;lt;/tex&amp;gt;. Ячейка с номером 9 свободна, значит записываем туда наш ключ.&lt;br /&gt;
&lt;br /&gt;
Таким образом, основная особенность двойного хеширования состоит в том, что при различных &amp;lt;tex&amp;gt; k &amp;lt;/tex&amp;gt; пара &amp;lt;tex&amp;gt; (h_1(k),h_2(k)) &amp;lt;/tex&amp;gt; дает различные последовательности ячеек для исследования.&lt;br /&gt;
&lt;br /&gt;
===Простая реализация===&lt;br /&gt;
Пусть у нас есть некоторый объект &amp;lt;tex&amp;gt; item &amp;lt;/tex&amp;gt;, в котором определено поле &amp;lt;tex&amp;gt; key &amp;lt;/tex&amp;gt;, от которого можно вычислить хеш-функции &amp;lt;tex&amp;gt; h_1(key)&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; h_2(key) &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Так же у нас есть таблица &amp;lt;tex&amp;gt; table &amp;lt;/tex&amp;gt; величиной &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, состоящая из объектов типа &amp;lt;tex&amp;gt; item &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
'''Вставка'''&lt;br /&gt;
 '''function''' add('''Item''' item)&lt;br /&gt;
      x = h1(item.key)&lt;br /&gt;
      y = h2(item.key)&lt;br /&gt;
         '''for''' (i = 0..m)    	&lt;br /&gt;
            '''if''' table[x] == '''null'''&lt;br /&gt;
               table[x] = item&lt;br /&gt;
            '''return'''      &lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      table.resize()&amp;lt;span style=&amp;quot;color:Green&amp;quot;&amp;gt;// ошибка, требуется увеличить размер таблицы&lt;br /&gt;
&lt;br /&gt;
'''Поиск'''&lt;br /&gt;
 '''Item''' search('''Item''' key)&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m)&lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key&lt;br /&gt;
               '''return''' table[x]&lt;br /&gt;
            '''else'''&lt;br /&gt;
               '''return''' '''null'''&lt;br /&gt;
      x = (x + y) '''mod''' m   &lt;br /&gt;
      '''return''' '''null'''&lt;br /&gt;
&lt;br /&gt;
===Реализация с удалением===&lt;br /&gt;
Что бы наша хеш-таблица поддерживала удаление, требуется добавить массив &amp;lt;tex&amp;gt;deleted&amp;lt;/tex&amp;gt; типов &amp;lt;tex&amp;gt;bool&amp;lt;/tex&amp;gt;, равный по величине массиву &amp;lt;tex&amp;gt;table&amp;lt;/tex&amp;gt;. Теперь при удалении мы просто будем помечать наш объект ''как удалённый'', а при добавлении как ''не удалённый'' и замещать новым добавляемым объектом. При поиске, помимо равенства ключей, мы смотрим, удалён ли элемент, если да, то идём дальше.&lt;br /&gt;
&lt;br /&gt;
'''Вставка'''&lt;br /&gt;
 '''function''' add('''Item''' item)&lt;br /&gt;
      x = h1(item.key)&lt;br /&gt;
      y = h2(item.key)&lt;br /&gt;
      '''for''' (i = 0..m)   	&lt;br /&gt;
         '''if''' table[x] == '''null''' '''or''' deleted[x]&lt;br /&gt;
            table[x] = item&lt;br /&gt;
            deleted[x] = '''false'''&lt;br /&gt;
            '''return'''      &lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      table.resize()&amp;lt;span style=&amp;quot;color:Green&amp;quot;&amp;gt;// ошибка, требуется увеличить размер таблицы&lt;br /&gt;
'''Поиск'''&lt;br /&gt;
 '''Item''' search('''Item''' key)&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m) &lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key '''and''' !deleted[x]&lt;br /&gt;
               '''return''' table[x]&lt;br /&gt;
         '''else'''&lt;br /&gt;
            '''return''' '''null'''&lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      '''return''' '''null'''&lt;br /&gt;
&lt;br /&gt;
'''Удаление'''&lt;br /&gt;
 '''function''' remove('''Item''' key)&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m)&lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key&lt;br /&gt;
               deleted[x] = '''true'''&lt;br /&gt;
         '''else''' &lt;br /&gt;
            '''return'''&lt;br /&gt;
      x = (x + y) '''mod''' m&lt;br /&gt;
&lt;br /&gt;
== Разрешение коллизий в Java 8==&lt;br /&gt;
В Java 8 для разрешения коллизий используется модифицированный метод цепочек. Суть его заключается в том, что когда количество элементов в корзине превышает определенное значение, данный бакет переходит от использования связного списка к использованию сбалансированного дерева. Такой подход позволяет улучшить производительность с &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt;.  Данный способ используется в таких коллекциях как HashMap, LinkedHashMap и ConcurrentHashMap.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Hashing_in_Java8.png|500px|Хеширование в Java 8.]]&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
* [[Хеширование]]&lt;br /&gt;
* [[Хеширование_кукушки|Хеширование кукушки]]&lt;br /&gt;
* [[Идеальное_хеширование|Идеальное хеширование]]&lt;br /&gt;
&lt;br /&gt;
== Источники Информации ==&lt;br /&gt;
* Бакнелл Дж. М. «Фундаментальные алгоритмы и структуры данных в Delphi», 2003&lt;br /&gt;
* Кормен, Томас Х., Лейзерсон, Чарльз И., Ривест, Рональд Л., Штайн Клиффорд «Алгоритмы: построение и анализ», 2-е издание. Пер. с англ. — М.:Издательский дом &amp;quot;Вильямс&amp;quot;, 2010.— Парал. тит. англ. — ISBN 978-5-8459-0857-5 (рус.)&lt;br /&gt;
* Дональд Кнут. «Искусство программирования, том 3. Сортировка и поиск» {{---}} «Вильямс», 2007 г.{{---}} ISBN 0-201-89685-0&lt;br /&gt;
* Седжвик Р. «Фундаментальные алгоритмы на C. Части 1-4. Анализ. Структуры данных. Сортировка. Поиск», 2003&lt;br /&gt;
* [http://openjdk.java.net/jeps/180 Handle Frequent HashMap Collisions with Balanced Trees]&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Double_hashing Wikipedia {{---}} Double_hashing]&lt;br /&gt;
* [http://ru.wikipedia.org/wiki/%D0%A5%D0%B5%D1%88-%D1%82%D0%B0%D0%B1%D0%BB%D0%B8%D1%86%D0%B0 Разрешение коллизий]&lt;br /&gt;
* [http://rain.ifmo.ru/cat/view.php/vis/hashtables/hash-2001-2 Пример хеш таблицы]&lt;br /&gt;
* [http://research.cs.vt.edu/AVresearch/hashing/double.php Пример хеш таблицы с двойным хешированием]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория: Хеширование]]&lt;/div&gt;</summary>
		<author><name>Kozichuk</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%A0%D0%B0%D0%B7%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BA%D0%BE%D0%BB%D0%BB%D0%B8%D0%B7%D0%B8%D0%B9&amp;diff=47153</id>
		<title>Разрешение коллизий</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A0%D0%B0%D0%B7%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BA%D0%BE%D0%BB%D0%BB%D0%B8%D0%B7%D0%B8%D0%B9&amp;diff=47153"/>
				<updated>2015-05-31T21:25:45Z</updated>
		
		<summary type="html">&lt;p&gt;Kozichuk: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Разрешение [[Хеш-таблица|коллизий]]''' (англ. collision resolution) в [[Хеш-таблица|хеш-таблице]], задача, решаемая несколькими способами: метод цепочек, открытая адресация и т.д. Очень важно сводить количество коллизий к минимуму в хеш-таблицах, так как это увеличивает время работы с ними. &lt;br /&gt;
&lt;br /&gt;
== Разрешение коллизий с помощью цепочек ==&lt;br /&gt;
[[Файл:open_hash.png|thumb|380px|right|Разрешение коллизий при помощи цепочек.]]&lt;br /&gt;
Каждая ячейка &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; массива &amp;lt;tex&amp;gt;H&amp;lt;/tex&amp;gt; содержит указатель на начало [[Список|списка]] всех элементов, хеш-код которых равен &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, либо указывает на их отсутствие. Коллизии приводят к тому, что появляются списки размером больше одного элемента.&lt;br /&gt;
&lt;br /&gt;
Время, необходимое для вставки в наихудшем случае равно &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;. Это операция выполняет быстро, так как считается, что вставляемый элемент отсутствует в таблице, но если потребуется, то перед вставкой мы можем выполнить поиск этого элемента.&lt;br /&gt;
&lt;br /&gt;
Время работы поиска в наихудшем случае пропорционально длине списка, а если все &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; ключей захешировались в одну и ту же ячейку (создав список длиной &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;) время поиска будет равно &amp;lt;tex&amp;gt;\Theta(n)&amp;lt;/tex&amp;gt; плюс время вычисления хеш-функции, что ничуть не лучше, чем использование связного списка для хранения всех &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; элементов.&lt;br /&gt;
&lt;br /&gt;
Удаления элемента может быть выполнено за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;, как и вставка, при использовании двухсвязного списка.&lt;br /&gt;
&lt;br /&gt;
== Линейное разрешение коллизий ==&lt;br /&gt;
[[Файл:close_hash.png|thumb|380px|right|Пример хеш-таблицы с открытой адресацией и линейным пробированием.]]&lt;br /&gt;
Все элементы хранятся непосредственно в хеш-таблице, без использования связных списков. В отличии от хеширования с цепочками, при использовании этого метода может возникнуть ситуация, когда хеш-таблица окажется полностью заполненной, следовательно будет невозможно добавлять в неё новые элементы. Так что при возникновении такой ситуации решением может быть динамическое увеличение размера хеш-таблицы, с одновременной её перестройкой.&lt;br /&gt;
&lt;br /&gt;
=== Стратегии поиска ===&lt;br /&gt;
&lt;br /&gt;
''' Последовательный поиск '''&lt;br /&gt;
&lt;br /&gt;
При попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt;i+1, i+2, i+3&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку. В неё и запишем элемент.&lt;br /&gt;
&lt;br /&gt;
[[Файл:hashtables1.png|400px|Последовательный поиск, частный случай линейного поиска.]]&lt;br /&gt;
&lt;br /&gt;
''' Линейный поиск '''&lt;br /&gt;
&lt;br /&gt;
Выбираем шаг &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;. При попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt;i+(1 \cdot q), i+(2 \cdot q), i+(3 \cdot q)&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку. В неё и запишем элемент.&lt;br /&gt;
По сути последовательный поиск - частный случай линейного, где &amp;lt;tex&amp;gt;q=1&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Hashtables56.PNG|400px|Линейный поиск с шагом q.]]&lt;br /&gt;
&lt;br /&gt;
''' Квадратичный поиск '''&lt;br /&gt;
&lt;br /&gt;
Шаг &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; не фиксирован, а изменяется квадратично: &amp;lt;tex&amp;gt;q = 1,4,9,16...&amp;lt;/tex&amp;gt;. Соответственно при попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt; i+1, i+4, i+9&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку.&lt;br /&gt;
&lt;br /&gt;
[[Файл:hashtables3.png|400px|Квадратичный поиск.]]&lt;br /&gt;
&lt;br /&gt;
=== Проверка наличия элемента в таблице===&lt;br /&gt;
&lt;br /&gt;
Проверка осуществляется аналогично добавлению: мы проверяем ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; и другие, в соответствии с выбранной стратегией, пока не найдём искомый элемент или свободную ячейку.&lt;br /&gt;
&lt;br /&gt;
При поиске элемента может получится так, что мы дойдём до конца таблицы. Обычно поиск продолжается, начиная с другого конца, пока мы не придём в ту ячейку, откуда начинался поиск.&lt;br /&gt;
&lt;br /&gt;
=== Проблемы данных стратегий ===&lt;br /&gt;
&lt;br /&gt;
Проблем две — крайне нетривиальное удаление элемента из таблицы и образование кластеров  — последовательностей занятых ячеек.&lt;br /&gt;
&lt;br /&gt;
Кластеризация замедляет все операции с хеш-таблицей: при добавлении требуется перебирать всё больше элементов, при проверке тоже. Чем больше в таблице элементов, тем больше в ней кластеры и тем выше вероятность того, что добавляемый элемент попадёт в кластер.&lt;br /&gt;
Для защиты от кластеризации используется двойное хеширование и [[Хеширование кукушки|хеширование кукушки]].&lt;br /&gt;
&lt;br /&gt;
=== Удаление элемента без пометок ===&lt;br /&gt;
&lt;br /&gt;
Рассуждение будет описывать случай с линейным поиском хеша. Будем при удалении элемента сдвигать всё последующие на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; позиций назад. При этом:&lt;br /&gt;
* если в цепочке встречается элемент с другим хешем, то он должен остаться на своём месте (такая ситуация может возникнуть если оставшаяся часть цепочки была добавлена позже этого элемента)&lt;br /&gt;
* в цепочке не должно оставаться &amp;quot;дырок&amp;quot;, тогда любой элемент с данным хешем будет доступен из начала цепи&lt;br /&gt;
&lt;br /&gt;
Учитывая это будем действовать следующим образом: при поиске следующего элемента цепочки будем пропускать все ячейки с другим значением хеша, первый найденный элемент копировать в текущую ячейку, и затем рекурсивно его удалять. Если такой следующей ячейки нет, то текущий элемент можно просто удалить, сторонние цепочки при этом не разрушатся (чего нельзя сказать про случай квадратичного поиска).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
''' Псевдокод '''&lt;br /&gt;
&lt;br /&gt;
 '''function''' delete('''Item''' i) &lt;br /&gt;
      j = i + q&lt;br /&gt;
      '''while''' table[j] == '''null''' '''or''' table[j].key != table[i].key&lt;br /&gt;
         '''if''' table[j] == '''null'''&lt;br /&gt;
            table[i] = '''null'''&lt;br /&gt;
            '''return'''&lt;br /&gt;
         j += q&lt;br /&gt;
      table[i] = table[j]&lt;br /&gt;
      delete(j);    &lt;br /&gt;
&lt;br /&gt;
Хеш-таблицу считаем зацикленной&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Утверждение&lt;br /&gt;
|about=о времени работы&lt;br /&gt;
|statement=Асимптотически время работы &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\mathrm{find}&amp;lt;/tex&amp;gt; совпадают&lt;br /&gt;
|proof=&lt;br /&gt;
Заметим что указатель &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; в каждой итерации перемещается вперёд на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; (с учётом рекурсивных вызовов &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt;). То есть этот алгоритм последовательно пройдёт по цепочке от удаляемого элемента до последнего {{---}} с учётом вызова &amp;lt;tex&amp;gt;\mathrm{find}&amp;lt;/tex&amp;gt; собственно для нахождения удаляемого элемента, мы посетим все ячейки цепи.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Вариант с зацикливанием мы не рассматриваем, поскольку если &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; взаимнопросто с размером хеш-таблицы, то для зацикливания в ней вообще не должно быть свободных позиций&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Теперь докажем почему этот алгоритм работает. Собственно нам требуется сохранение трёх условий.&lt;br /&gt;
* В редактируемой цепи не остаётся дырок&lt;br /&gt;
Докажем по индукции. Если на данной итерации мы просто удаляем элемент (база), то после него ничего нет, всё верно. Если же нет, то вызванный в конце &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt; (см. псевдокод) заметёт созданную дыру (скопированный элемент), и сам, по предположению, новых не создаст.&lt;br /&gt;
* Элементы, которые уже на своих местах, не должны быть сдвинуты.&lt;br /&gt;
Это учтено.&lt;br /&gt;
* В других цепочках не появятся дыры&lt;br /&gt;
Противное возможно только в том случае, если какой-то элемент был действительно удалён. Удаляем мы только последнюю ячейку в цепи, и если бы на её месте возникла дыра для сторонней цепочки, это бы означало что элемент, стоящий на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; позиций назад, одновременно принадлежал нашей и другой цепочкам, что невозможно.&lt;br /&gt;
&lt;br /&gt;
==Двойное хеширование==&lt;br /&gt;
'''Двойное хеширование''' (англ. double hashing) {{---}} метод борьбы с коллизиями, возникающими при открытой адресации, основанный на использовании двух хеш-функций для построения различных последовательностей исследования хеш-таблицы.&lt;br /&gt;
&lt;br /&gt;
===Принцип двойного хеширования===&lt;br /&gt;
При двойном хешировании используются две независимые хеш-функции &amp;lt;tex&amp;gt; h_1(k) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; h_2(k) &amp;lt;/tex&amp;gt;. Пусть &amp;lt;tex&amp;gt; k &amp;lt;/tex&amp;gt; {{---}} это наш ключ, &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt; {{---}} размер нашей таблицы, &amp;lt;tex&amp;gt;n \bmod m &amp;lt;/tex&amp;gt; {{---}} остаток от деления &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt; на &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, тогда сначала исследуется ячейка с адресом &amp;lt;tex&amp;gt; h_1(k) &amp;lt;/tex&amp;gt;, если она уже занята, то рассматривается &amp;lt;tex&amp;gt; (h_1(k) +  h_2(k)) \bmod m &amp;lt;/tex&amp;gt;, затем &amp;lt;tex&amp;gt; (h_1(k) +  2 \cdot h_2(k)) \bmod m &amp;lt;/tex&amp;gt; и так далее. В общем случае идёт проверка последовательности ячеек &amp;lt;tex&amp;gt; (h_1(k) +  i \cdot h_2(k)) \bmod m &amp;lt;/tex&amp;gt; где &amp;lt;tex&amp;gt;  i = (0, 1, \; ... \;,  m - 1) &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, операции вставки, удаления и поиска в лучшем случае выполняются за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;, в худшем {{---}} за &amp;lt;tex&amp;gt;O(m)&amp;lt;/tex&amp;gt;, что не отличается от обычного [[Открытое_и_закрытое_хеширование#Линейное разрешение коллизий|линейного разрешения коллизий]].&lt;br /&gt;
Однако в среднем, при грамотном выборе хеш-функций, двойное хеширование будет выдавать лучшие результаты, за счёт того, что вероятность совпадения значений сразу двух независимых хеш-функций ниже, чем одной.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt;\forall x \neq y \; \exists h_1,h_2 : p(h_1(x)=h_1(y))&amp;gt; p((h_1(x)=h_1(y)) \land (h_2(x)=h_2(y)))&amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Выбор хеш-функций===&lt;br /&gt;
&amp;lt;tex&amp;gt; h_1 &amp;lt;/tex&amp;gt; может быть обычной хеш-функцией. Однако чтобы последовательность исследования могла охватить всю таблицу, &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; должна возвращать значения:&lt;br /&gt;
*не равные &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
*независимые  от &amp;lt;tex&amp;gt; h_1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
*взаимно простые с величиной хеш-таблицы&lt;br /&gt;
&lt;br /&gt;
Есть два удобных способа это сделать. Первый состоит в том, что в качестве размера таблицы используется простое число, а &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; возвращает натуральные числа, меньшие &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;. Второй {{---}} размер таблицы является степенью двойки, а &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; возвращает нечетные значения.&lt;br /&gt;
&lt;br /&gt;
Например, если размер таблицы равен &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, то в качестве &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; можно использовать функцию вида &amp;lt;tex&amp;gt; h_2(k) = k \bmod (m-1) + 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Файл: Вставка при двойном хэшировании.svg.jpeg|thumb|right|Вставка при двойном хешировании]]&lt;br /&gt;
&lt;br /&gt;
===Пример===&lt;br /&gt;
&lt;br /&gt;
Показана хеш-таблица размером 13 ячеек, в которой используются вспомогательные функции:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h(k,i) = (h_1(k) + i \cdot h_2(k)) \bmod 13 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h_1(k) = k \bmod 13 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h_2(k) = 1 + k \bmod 11 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Мы хотим вставить ключ 14. Изначально &amp;lt;tex&amp;gt; i = 0 &amp;lt;/tex&amp;gt;. Тогда &amp;lt;tex&amp;gt; h(14,0) = (h_1(14) + 0\cdot h_2(14)) \bmod 13 = 1 &amp;lt;/tex&amp;gt;. Но ячейка с индексом 1 занята, поэтому увеличиваем &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; на 1 и пересчитываем значение хеш-функции. Делаем так, пока не дойдем до пустой ячейки. При &amp;lt;tex&amp;gt; i = 2 &amp;lt;/tex&amp;gt; получаем &amp;lt;tex&amp;gt; h(14,2) = (h_1(14) + 2\cdot h_2(14)) \bmod 13 = 9 &amp;lt;/tex&amp;gt;. Ячейка с номером 9 свободна, значит записываем туда наш ключ.&lt;br /&gt;
&lt;br /&gt;
Таким образом, основная особенность двойного хеширования состоит в том, что при различных &amp;lt;tex&amp;gt; k &amp;lt;/tex&amp;gt; пара &amp;lt;tex&amp;gt; (h_1(k),h_2(k)) &amp;lt;/tex&amp;gt; дает различные последовательности ячеек для исследования.&lt;br /&gt;
&lt;br /&gt;
===Простая реализация===&lt;br /&gt;
Пусть у нас есть некоторый объект &amp;lt;tex&amp;gt; item &amp;lt;/tex&amp;gt;, в котором определено поле &amp;lt;tex&amp;gt; key &amp;lt;/tex&amp;gt;, от которого можно вычислить хеш-функции &amp;lt;tex&amp;gt; h_1(key)&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; h_2(key) &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Так же у нас есть таблица &amp;lt;tex&amp;gt; table &amp;lt;/tex&amp;gt; величиной &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, состоящая из объектов типа &amp;lt;tex&amp;gt; item &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
'''Вставка'''&lt;br /&gt;
 '''function''' add('''Item''' item)&lt;br /&gt;
      x = h1(item.key)&lt;br /&gt;
      y = h2(item.key)&lt;br /&gt;
         '''for''' (i = 0..m)    	&lt;br /&gt;
            '''if''' table[x] == '''null'''&lt;br /&gt;
               table[x] = item&lt;br /&gt;
            '''return'''      &lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      table.resize()&amp;lt;span style=&amp;quot;color:Green&amp;quot;&amp;gt;// ошибка, требуется увеличить размер таблицы&lt;br /&gt;
&lt;br /&gt;
'''Поиск'''&lt;br /&gt;
 '''Item''' search('''Item''' key)&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m)&lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key&lt;br /&gt;
               '''return''' table[x]&lt;br /&gt;
            '''else'''&lt;br /&gt;
               '''return''' '''null'''&lt;br /&gt;
      x = (x + y) '''mod''' m   &lt;br /&gt;
      '''return''' '''null'''&lt;br /&gt;
&lt;br /&gt;
===Реализация с удалением===&lt;br /&gt;
Что бы наша хеш-таблица поддерживала удаление, требуется добавить массив &amp;lt;tex&amp;gt;deleted&amp;lt;/tex&amp;gt; типов &amp;lt;tex&amp;gt;bool&amp;lt;/tex&amp;gt;, равный по величине массиву &amp;lt;tex&amp;gt;table&amp;lt;/tex&amp;gt;. Теперь при удалении мы просто будем помечать наш объект ''как удалённый'', а при добавлении как ''не удалённый'' и замещать новым добавляемым объектом. При поиске, помимо равенства ключей, мы смотрим, удалён ли элемент, если да, то идём дальше.&lt;br /&gt;
&lt;br /&gt;
'''Вставка'''&lt;br /&gt;
 '''function''' add('''Item''' item)&lt;br /&gt;
      x = h1(item.key)&lt;br /&gt;
      y = h2(item.key)&lt;br /&gt;
      '''for''' (i = 0..m)   	&lt;br /&gt;
         '''if''' table[x] == '''null''' '''or''' deleted[x]&lt;br /&gt;
            table[x] = item&lt;br /&gt;
            deleted[x] = '''false'''&lt;br /&gt;
            '''return'''      &lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      table.resize()&amp;lt;span style=&amp;quot;color:Green&amp;quot;&amp;gt;// ошибка, требуется увеличить размер таблицы&lt;br /&gt;
'''Поиск'''&lt;br /&gt;
 '''Item''' search('''Item''' key)&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m) &lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key '''and''' !deleted[x]&lt;br /&gt;
               '''return''' table[x]&lt;br /&gt;
         '''else'''&lt;br /&gt;
            '''return''' '''null'''&lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      '''return''' '''null'''&lt;br /&gt;
&lt;br /&gt;
'''Удаление'''&lt;br /&gt;
 '''function''' remove('''Item''' key)&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m)&lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key&lt;br /&gt;
               deleted[x] = '''true'''&lt;br /&gt;
         '''else''' &lt;br /&gt;
            '''return'''&lt;br /&gt;
      x = (x + y) '''mod''' m&lt;br /&gt;
&lt;br /&gt;
== Разрешение коллизий в Java 8==&lt;br /&gt;
В Java 8 для разрешения коллизий используется модифицированный метод цепочек. Суть его заключается в том, что когда количество элементов в корзине превышает определенное значение, данный бакет переходит от использования связного списка к использованию сбалансированного дерева. Такой подход позволяет улучшить производительность с &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt;.  Данный способ используется в таких коллекциях как HashMap, LinkedHashMap и ConcurrentHashMap.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Hashing_in_Java8.png|500px|Хеширование в Java 8.]]&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
* [[Хеширование]]&lt;br /&gt;
* [[Хеширование_кукушки|Хеширование кукушки]]&lt;br /&gt;
* [[Идеальное_хеширование|Идеальное хеширование]]&lt;br /&gt;
&lt;br /&gt;
== Источники Информации ==&lt;br /&gt;
* Бакнелл Дж. М. «Фундаментальные алгоритмы и структуры данных в Delphi», 2003&lt;br /&gt;
* Кормен, Томас Х., Лейзерсон, Чарльз И., Ривест, Рональд Л., Штайн Клиффорд «Алгоритмы: построение и анализ», 2-е издание. Пер. с англ. — М.:Издательский дом &amp;quot;Вильямс&amp;quot;, 2010.— Парал. тит. англ. — ISBN 978-5-8459-0857-5 (рус.)&lt;br /&gt;
* Дональд Кнут. «Искусство программирования, том 3. Сортировка и поиск» {{---}} «Вильямс», 2007 г.{{---}} ISBN 0-201-89685-0&lt;br /&gt;
* Седжвик Р. «Фундаментальные алгоритмы на C. Части 1-4. Анализ. Структуры данных. Сортировка. Поиск», 2003&lt;br /&gt;
* [http://openjdk.java.net/jeps/180 Handle Frequent HashMap Collisions with Balanced Trees]&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Double_hashing Wikipedia {{---}} Double_hashing]&lt;br /&gt;
* [http://ru.wikipedia.org/wiki/%D0%A5%D0%B5%D1%88-%D1%82%D0%B0%D0%B1%D0%BB%D0%B8%D1%86%D0%B0 Разрешение коллизий]&lt;br /&gt;
* [http://rain.ifmo.ru/cat/view.php/vis/hashtables/hash-2001-2 Пример хеш таблицы]&lt;br /&gt;
* [http://research.cs.vt.edu/AVresearch/hashing/double.php Пример хеш таблицы с двойным хешированием]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория: Хеширование]]&lt;/div&gt;</summary>
		<author><name>Kozichuk</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%A0%D0%B0%D0%B7%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BA%D0%BE%D0%BB%D0%BB%D0%B8%D0%B7%D0%B8%D0%B9&amp;diff=47152</id>
		<title>Разрешение коллизий</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A0%D0%B0%D0%B7%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BA%D0%BE%D0%BB%D0%BB%D0%B8%D0%B7%D0%B8%D0%B9&amp;diff=47152"/>
				<updated>2015-05-31T21:12:24Z</updated>
		
		<summary type="html">&lt;p&gt;Kozichuk: /* Разрешение коллизий в Java 8 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Разрешение [[Хеш-таблица|коллизий]]''' (англ. collision resolution) в [[Хеш-таблица|хеш-таблице]], задача, решаемая несколькими способами. Например, при помощи метода цепочке или открытой адресации. Коллизии замедляют время работы с хеш-таблицой, поэтому с ними нужно бороться.&lt;br /&gt;
&lt;br /&gt;
== Разрешение коллизий с помощью цепочек ==&lt;br /&gt;
[[Файл:open_hash.png|thumb|380px|right|Разрешение коллизий при помощи цепочек.]]&lt;br /&gt;
Каждая ячейка &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; массива &amp;lt;tex&amp;gt;H&amp;lt;/tex&amp;gt; содержит указатель на начало [[Список|списка]] всех элементов, хеш-код которых равен &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, либо указывает на их отсутствие. Коллизии приводят к тому, что появляются списки размером больше одного элемента.&lt;br /&gt;
&lt;br /&gt;
Время, необходимое для вставки в наихудшем случае равно &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;. Это операция выполняет быстро, так как считается, что вставляемый элемент отсутствует в таблице, но если потребуется, то перед вставкой мы можем выполнить поиск этого элемента.&lt;br /&gt;
&lt;br /&gt;
Время работы поиска в наихудшем случае пропорционально длине списка, а если все &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; ключей захешировались в одну и ту же ячейку (создав список длиной &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;) время поиска будет равно &amp;lt;tex&amp;gt;\Theta(n)&amp;lt;/tex&amp;gt; плюс время вычисления хеш-функции, что ничуть не лучше, чем использование связного списка для хранения всех &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; элементов.&lt;br /&gt;
&lt;br /&gt;
Удаления элемента может быть выполнено за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;, как и вставка, при использовании двухсвязного списка.&lt;br /&gt;
&lt;br /&gt;
== Линейное разрешение коллизий ==&lt;br /&gt;
[[Файл:close_hash.png|thumb|380px|right|Пример хеш-таблицы с открытой адресацией и линейным пробированием.]]&lt;br /&gt;
Все элементы хранятся непосредственно в хеш-таблице, без использования связных списков. В отличии от хеширования с цепочками, при использовании этого метода может возникнуть ситуация, когда хеш-таблица окажется полностью заполненной, следовательно будет невозможно добавлять в неё новые элементы. Так что при возникновении такой ситуации решением может быть динамическое увеличение размера хеш-таблицы, с одновременной её перестройкой.&lt;br /&gt;
&lt;br /&gt;
=== Стратегии поиска ===&lt;br /&gt;
&lt;br /&gt;
''' Последовательный поиск '''&lt;br /&gt;
&lt;br /&gt;
При попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt;i+1, i+2, i+3&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку. В неё и запишем элемент.&lt;br /&gt;
&lt;br /&gt;
[[Файл:hashtables1.png|400px|Последовательный поиск, частный случай линейного поиска.]]&lt;br /&gt;
&lt;br /&gt;
''' Линейный поиск '''&lt;br /&gt;
&lt;br /&gt;
Выбираем шаг &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;. При попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt;i+(1 \cdot q), i+(2 \cdot q), i+(3 \cdot q)&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку. В неё и запишем элемент.&lt;br /&gt;
По сути последовательный поиск - частный случай линейного, где &amp;lt;tex&amp;gt;q=1&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Hashtables56.PNG|400px|Линейный поиск с шагом q.]]&lt;br /&gt;
&lt;br /&gt;
''' Квадратичный поиск '''&lt;br /&gt;
&lt;br /&gt;
Шаг &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; не фиксирован, а изменяется квадратично: &amp;lt;tex&amp;gt;q = 1,4,9,16...&amp;lt;/tex&amp;gt;. Соответственно при попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt; i+1, i+4, i+9&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку.&lt;br /&gt;
&lt;br /&gt;
[[Файл:hashtables3.png|400px|Квадратичный поиск.]]&lt;br /&gt;
&lt;br /&gt;
=== Проверка наличия элемента в таблице===&lt;br /&gt;
&lt;br /&gt;
Проверка осуществляется аналогично добавлению: мы проверяем ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; и другие, в соответствии с выбранной стратегией, пока не найдём искомый элемент или свободную ячейку.&lt;br /&gt;
&lt;br /&gt;
При поиске элемента может получится так, что мы дойдём до конца таблицы. Обычно поиск продолжается, начиная с другого конца, пока мы не придём в ту ячейку, откуда начинался поиск.&lt;br /&gt;
&lt;br /&gt;
=== Проблемы данных стратегий ===&lt;br /&gt;
&lt;br /&gt;
Проблем две — крайне нетривиальное удаление элемента из таблицы и образование кластеров  — последовательностей занятых ячеек.&lt;br /&gt;
&lt;br /&gt;
Кластеризация замедляет все операции с хеш-таблицей: при добавлении требуется перебирать всё больше элементов, при проверке тоже. Чем больше в таблице элементов, тем больше в ней кластеры и тем выше вероятность того, что добавляемый элемент попадёт в кластер.&lt;br /&gt;
Для защиты от кластеризации используется двойное хеширование и [[Хеширование кукушки|хеширование кукушки]].&lt;br /&gt;
&lt;br /&gt;
=== Удаление элемента без пометок ===&lt;br /&gt;
&lt;br /&gt;
Рассуждение будет описывать случай с линейным поиском хеша. Будем при удалении элемента сдвигать всё последующие на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; позиций назад. При этом:&lt;br /&gt;
* если в цепочке встречается элемент с другим хешем, то он должен остаться на своём месте (такая ситуация может возникнуть если оставшаяся часть цепочки была добавлена позже этого элемента)&lt;br /&gt;
* в цепочке не должно оставаться &amp;quot;дырок&amp;quot;, тогда любой элемент с данным хешем будет доступен из начала цепи&lt;br /&gt;
&lt;br /&gt;
Учитывая это будем действовать следующим образом: при поиске следующего элемента цепочки будем пропускать все ячейки с другим значением хеша, первый найденный элемент копировать в текущую ячейку, и затем рекурсивно его удалять. Если такой следующей ячейки нет, то текущий элемент можно просто удалить, сторонние цепочки при этом не разрушатся (чего нельзя сказать про случай квадратичного поиска).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
''' Псевдокод '''&lt;br /&gt;
&lt;br /&gt;
 '''function''' delete('''Item''' i) &lt;br /&gt;
      j = i + q&lt;br /&gt;
      '''while''' table[j] == '''null''' '''or''' table[j].key != table[i].key&lt;br /&gt;
         '''if''' table[j] == '''null'''&lt;br /&gt;
            table[i] = '''null'''&lt;br /&gt;
            '''return'''&lt;br /&gt;
         j += q&lt;br /&gt;
      table[i] = table[j]&lt;br /&gt;
      delete(j);    &lt;br /&gt;
&lt;br /&gt;
Хеш-таблицу считаем зацикленной&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Утверждение&lt;br /&gt;
|about=о времени работы&lt;br /&gt;
|statement=Асимптотически время работы &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\mathrm{find}&amp;lt;/tex&amp;gt; совпадают&lt;br /&gt;
|proof=&lt;br /&gt;
Заметим что указатель &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; в каждой итерации перемещается вперёд на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; (с учётом рекурсивных вызовов &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt;). То есть этот алгоритм последовательно пройдёт по цепочке от удаляемого элемента до последнего {{---}} с учётом вызова &amp;lt;tex&amp;gt;\mathrm{find}&amp;lt;/tex&amp;gt; собственно для нахождения удаляемого элемента, мы посетим все ячейки цепи.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Вариант с зацикливанием мы не рассматриваем, поскольку если &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; взаимнопросто с размером хеш-таблицы, то для зацикливания в ней вообще не должно быть свободных позиций&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Теперь докажем почему этот алгоритм работает. Собственно нам требуется сохранение трёх условий.&lt;br /&gt;
* В редактируемой цепи не остаётся дырок&lt;br /&gt;
Докажем по индукции. Если на данной итерации мы просто удаляем элемент (база), то после него ничего нет, всё верно. Если же нет, то вызванный в конце &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt; (см. псевдокод) заметёт созданную дыру (скопированный элемент), и сам, по предположению, новых не создаст.&lt;br /&gt;
* Элементы, которые уже на своих местах, не должны быть сдвинуты.&lt;br /&gt;
Это учтено.&lt;br /&gt;
* В других цепочках не появятся дыры&lt;br /&gt;
Противное возможно только в том случае, если какой-то элемент был действительно удалён. Удаляем мы только последнюю ячейку в цепи, и если бы на её месте возникла дыра для сторонней цепочки, это бы означало что элемент, стоящий на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; позиций назад, одновременно принадлежал нашей и другой цепочкам, что невозможно.&lt;br /&gt;
&lt;br /&gt;
==Двойное хеширование==&lt;br /&gt;
'''Двойное хеширование''' (англ. double hashing) {{---}} метод борьбы с коллизиями, возникающими при открытой адресации, основанный на использовании двух хеш-функций для построения различных последовательностей исследования хеш-таблицы.&lt;br /&gt;
&lt;br /&gt;
===Принцип двойного хеширования===&lt;br /&gt;
При двойном хешировании используются две независимые хеш-функции &amp;lt;tex&amp;gt; h_1(k) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; h_2(k) &amp;lt;/tex&amp;gt;. Пусть &amp;lt;tex&amp;gt; k &amp;lt;/tex&amp;gt; {{---}} это наш ключ, &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt; {{---}} размер нашей таблицы, &amp;lt;tex&amp;gt;n \bmod m &amp;lt;/tex&amp;gt; {{---}} остаток от деления &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt; на &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, тогда сначала исследуется ячейка с адресом &amp;lt;tex&amp;gt; h_1(k) &amp;lt;/tex&amp;gt;, если она уже занята, то рассматривается &amp;lt;tex&amp;gt; (h_1(k) +  h_2(k)) \bmod m &amp;lt;/tex&amp;gt;, затем &amp;lt;tex&amp;gt; (h_1(k) +  2 \cdot h_2(k)) \bmod m &amp;lt;/tex&amp;gt; и так далее. В общем случае идёт проверка последовательности ячеек &amp;lt;tex&amp;gt; (h_1(k) +  i \cdot h_2(k)) \bmod m &amp;lt;/tex&amp;gt; где &amp;lt;tex&amp;gt;  i = (0, 1, \; ... \;,  m - 1) &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, операции вставки, удаления и поиска в лучшем случае выполняются за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;, в худшем {{---}} за &amp;lt;tex&amp;gt;O(m)&amp;lt;/tex&amp;gt;, что не отличается от обычного [[Открытое_и_закрытое_хеширование#Линейное разрешение коллизий|линейного разрешения коллизий]].&lt;br /&gt;
Однако в среднем, при грамотном выборе хеш-функций, двойное хеширование будет выдавать лучшие результаты, за счёт того, что вероятность совпадения значений сразу двух независимых хеш-функций ниже, чем одной.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt;\forall x \neq y \; \exists h_1,h_2 : p(h_1(x)=h_1(y))&amp;gt; p((h_1(x)=h_1(y)) \land (h_2(x)=h_2(y)))&amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Выбор хеш-функций===&lt;br /&gt;
&amp;lt;tex&amp;gt; h_1 &amp;lt;/tex&amp;gt; может быть обычной хеш-функцией. Однако чтобы последовательность исследования могла охватить всю таблицу, &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; должна возвращать значения:&lt;br /&gt;
*не равные &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
*независимые  от &amp;lt;tex&amp;gt; h_1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
*взаимно простые с величиной хеш-таблицы&lt;br /&gt;
&lt;br /&gt;
Есть два удобных способа это сделать. Первый состоит в том, что в качестве размера таблицы используется простое число, а &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; возвращает натуральные числа, меньшие &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;. Второй {{---}} размер таблицы является степенью двойки, а &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; возвращает нечетные значения.&lt;br /&gt;
&lt;br /&gt;
Например, если размер таблицы равен &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, то в качестве &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; можно использовать функцию вида &amp;lt;tex&amp;gt; h_2(k) = k \bmod (m-1) + 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Файл: Вставка при двойном хэшировании.svg.jpeg|thumb|right|Вставка при двойном хешировании]]&lt;br /&gt;
&lt;br /&gt;
===Пример===&lt;br /&gt;
&lt;br /&gt;
Показана хеш-таблица размером 13 ячеек, в которой используются вспомогательные функции:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h(k,i) = (h_1(k) + i \cdot h_2(k)) \bmod 13 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h_1(k) = k \bmod 13 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h_2(k) = 1 + k \bmod 11 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Мы хотим вставить ключ 14. Изначально &amp;lt;tex&amp;gt; i = 0 &amp;lt;/tex&amp;gt;. Тогда &amp;lt;tex&amp;gt; h(14,0) = (h_1(14) + 0\cdot h_2(14)) \bmod 13 = 1 &amp;lt;/tex&amp;gt;. Но ячейка с индексом 1 занята, поэтому увеличиваем &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; на 1 и пересчитываем значение хеш-функции. Делаем так, пока не дойдем до пустой ячейки. При &amp;lt;tex&amp;gt; i = 2 &amp;lt;/tex&amp;gt; получаем &amp;lt;tex&amp;gt; h(14,2) = (h_1(14) + 2\cdot h_2(14)) \bmod 13 = 9 &amp;lt;/tex&amp;gt;. Ячейка с номером 9 свободна, значит записываем туда наш ключ.&lt;br /&gt;
&lt;br /&gt;
Таким образом, основная особенность двойного хеширования состоит в том, что при различных &amp;lt;tex&amp;gt; k &amp;lt;/tex&amp;gt; пара &amp;lt;tex&amp;gt; (h_1(k),h_2(k)) &amp;lt;/tex&amp;gt; дает различные последовательности ячеек для исследования.&lt;br /&gt;
&lt;br /&gt;
===Простая реализация===&lt;br /&gt;
Пусть у нас есть некоторый объект &amp;lt;tex&amp;gt; item &amp;lt;/tex&amp;gt;, в котором определено поле &amp;lt;tex&amp;gt; key &amp;lt;/tex&amp;gt;, от которого можно вычислить хеш-функции &amp;lt;tex&amp;gt; h_1(key)&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; h_2(key) &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Так же у нас есть таблица &amp;lt;tex&amp;gt; table &amp;lt;/tex&amp;gt; величиной &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, состоящая из объектов типа &amp;lt;tex&amp;gt; item &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
'''Вставка'''&lt;br /&gt;
 '''function''' add('''Item''' item)&lt;br /&gt;
      x = h1(item.key)&lt;br /&gt;
      y = h2(item.key)&lt;br /&gt;
         '''for''' (i = 0..m)    	&lt;br /&gt;
            '''if''' table[x] == '''null'''&lt;br /&gt;
               table[x] = item&lt;br /&gt;
            '''return'''      &lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      table.resize()&amp;lt;span style=&amp;quot;color:Green&amp;quot;&amp;gt;// ошибка, требуется увеличить размер таблицы&lt;br /&gt;
&lt;br /&gt;
'''Поиск'''&lt;br /&gt;
 '''Item''' search('''Item''' key)&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m)&lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key&lt;br /&gt;
               '''return''' table[x]&lt;br /&gt;
            '''else'''&lt;br /&gt;
               '''return''' '''null'''&lt;br /&gt;
      x = (x + y) '''mod''' m   &lt;br /&gt;
      '''return''' '''null'''&lt;br /&gt;
&lt;br /&gt;
===Реализация с удалением===&lt;br /&gt;
Что бы наша хеш-таблица поддерживала удаление, требуется добавить массив &amp;lt;tex&amp;gt;deleted&amp;lt;/tex&amp;gt; типов &amp;lt;tex&amp;gt;bool&amp;lt;/tex&amp;gt;, равный по величине массиву &amp;lt;tex&amp;gt;table&amp;lt;/tex&amp;gt;. Теперь при удалении мы просто будем помечать наш объект ''как удалённый'', а при добавлении как ''не удалённый'' и замещать новым добавляемым объектом. При поиске, помимо равенства ключей, мы смотрим, удалён ли элемент, если да, то идём дальше.&lt;br /&gt;
&lt;br /&gt;
'''Вставка'''&lt;br /&gt;
 '''function''' add('''Item''' item)&lt;br /&gt;
      x = h1(item.key)&lt;br /&gt;
      y = h2(item.key)&lt;br /&gt;
      '''for''' (i = 0..m)   	&lt;br /&gt;
         '''if''' table[x] == '''null''' '''or''' deleted[x]&lt;br /&gt;
            table[x] = item&lt;br /&gt;
            deleted[x] = '''false'''&lt;br /&gt;
            '''return'''      &lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      table.resize()&amp;lt;span style=&amp;quot;color:Green&amp;quot;&amp;gt;// ошибка, требуется увеличить размер таблицы&lt;br /&gt;
'''Поиск'''&lt;br /&gt;
 '''Item''' search('''Item''' key)&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m) &lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key '''and''' !deleted[x]&lt;br /&gt;
               '''return''' table[x]&lt;br /&gt;
         '''else'''&lt;br /&gt;
            '''return''' '''null'''&lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      '''return''' '''null'''&lt;br /&gt;
&lt;br /&gt;
'''Удаление'''&lt;br /&gt;
 '''function''' remove('''Item''' key)&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m)&lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key&lt;br /&gt;
               deleted[x] = '''true'''&lt;br /&gt;
         '''else''' &lt;br /&gt;
            '''return'''&lt;br /&gt;
      x = (x + y) '''mod''' m&lt;br /&gt;
&lt;br /&gt;
== Разрешение коллизий в Java 8==&lt;br /&gt;
В Java 8 для разрешения коллизий используется модифицированный метод цепочек. Суть его заключается в том, что когда количество элементов в корзине превышает определенное значение, данный бакет переходит от использования связного списка к использованию сбалансированного дерева. Такой подход позволяет улучшить производительность с &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;O(log(n))&amp;lt;/tex&amp;gt;.  Данный способ используется в таких коллекциях как HashMap, LinkedHashMap и ConcurrentHashMap.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Hashing_in_Java8.png|500px|Хеширование в Java 8.]]&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
* [[Хеширование]]&lt;br /&gt;
* [[Хеширование_кукушки|Хеширование кукушки]]&lt;br /&gt;
* [[Идеальное_хеширование|Идеальное хеширование]]&lt;br /&gt;
&lt;br /&gt;
== Источники Информации ==&lt;br /&gt;
* Бакнелл Дж. М. «Фундаментальные алгоритмы и структуры данных в Delphi», 2003&lt;br /&gt;
* Кормен, Томас Х., Лейзерсон, Чарльз И., Ривест, Рональд Л., Штайн Клиффорд «Алгоритмы: построение и анализ», 2-е издание. Пер. с англ. — М.:Издательский дом &amp;quot;Вильямс&amp;quot;, 2010.— Парал. тит. англ. — ISBN 978-5-8459-0857-5 (рус.)&lt;br /&gt;
* Дональд Кнут. «Искусство программирования, том 3. Сортировка и поиск» {{---}} «Вильямс», 2007 г.{{---}} ISBN 0-201-89685-0&lt;br /&gt;
* Седжвик Р. «Фундаментальные алгоритмы на C. Части 1-4. Анализ. Структуры данных. Сортировка. Поиск», 2003&lt;br /&gt;
* [http://openjdk.java.net/jeps/180 Handle Frequent HashMap Collisions with Balanced Trees]&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Double_hashing Wikipedia {{---}} Double_hashing]&lt;br /&gt;
* [http://ru.wikipedia.org/wiki/%D0%A5%D0%B5%D1%88-%D1%82%D0%B0%D0%B1%D0%BB%D0%B8%D1%86%D0%B0 Разрешение коллизий]&lt;br /&gt;
* [http://rain.ifmo.ru/cat/view.php/vis/hashtables/hash-2001-2 Пример хеш таблицы]&lt;br /&gt;
* [http://research.cs.vt.edu/AVresearch/hashing/double.php Пример хеш таблицы с двойным хешированием]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория: Хеширование]]&lt;/div&gt;</summary>
		<author><name>Kozichuk</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%A0%D0%B0%D0%B7%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BA%D0%BE%D0%BB%D0%BB%D0%B8%D0%B7%D0%B8%D0%B9&amp;diff=47151</id>
		<title>Разрешение коллизий</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A0%D0%B0%D0%B7%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BA%D0%BE%D0%BB%D0%BB%D0%B8%D0%B7%D0%B8%D0%B9&amp;diff=47151"/>
				<updated>2015-05-31T21:11:59Z</updated>
		
		<summary type="html">&lt;p&gt;Kozichuk: /* Разрешение коллизий в Java 8 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Разрешение [[Хеш-таблица|коллизий]]''' (англ. collision resolution) в [[Хеш-таблица|хеш-таблице]], задача, решаемая несколькими способами. Например, при помощи метода цепочке или открытой адресации. Коллизии замедляют время работы с хеш-таблицой, поэтому с ними нужно бороться.&lt;br /&gt;
&lt;br /&gt;
== Разрешение коллизий с помощью цепочек ==&lt;br /&gt;
[[Файл:open_hash.png|thumb|380px|right|Разрешение коллизий при помощи цепочек.]]&lt;br /&gt;
Каждая ячейка &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; массива &amp;lt;tex&amp;gt;H&amp;lt;/tex&amp;gt; содержит указатель на начало [[Список|списка]] всех элементов, хеш-код которых равен &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, либо указывает на их отсутствие. Коллизии приводят к тому, что появляются списки размером больше одного элемента.&lt;br /&gt;
&lt;br /&gt;
Время, необходимое для вставки в наихудшем случае равно &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;. Это операция выполняет быстро, так как считается, что вставляемый элемент отсутствует в таблице, но если потребуется, то перед вставкой мы можем выполнить поиск этого элемента.&lt;br /&gt;
&lt;br /&gt;
Время работы поиска в наихудшем случае пропорционально длине списка, а если все &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; ключей захешировались в одну и ту же ячейку (создав список длиной &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;) время поиска будет равно &amp;lt;tex&amp;gt;\Theta(n)&amp;lt;/tex&amp;gt; плюс время вычисления хеш-функции, что ничуть не лучше, чем использование связного списка для хранения всех &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; элементов.&lt;br /&gt;
&lt;br /&gt;
Удаления элемента может быть выполнено за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;, как и вставка, при использовании двухсвязного списка.&lt;br /&gt;
&lt;br /&gt;
== Линейное разрешение коллизий ==&lt;br /&gt;
[[Файл:close_hash.png|thumb|380px|right|Пример хеш-таблицы с открытой адресацией и линейным пробированием.]]&lt;br /&gt;
Все элементы хранятся непосредственно в хеш-таблице, без использования связных списков. В отличии от хеширования с цепочками, при использовании этого метода может возникнуть ситуация, когда хеш-таблица окажется полностью заполненной, следовательно будет невозможно добавлять в неё новые элементы. Так что при возникновении такой ситуации решением может быть динамическое увеличение размера хеш-таблицы, с одновременной её перестройкой.&lt;br /&gt;
&lt;br /&gt;
=== Стратегии поиска ===&lt;br /&gt;
&lt;br /&gt;
''' Последовательный поиск '''&lt;br /&gt;
&lt;br /&gt;
При попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt;i+1, i+2, i+3&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку. В неё и запишем элемент.&lt;br /&gt;
&lt;br /&gt;
[[Файл:hashtables1.png|400px|Последовательный поиск, частный случай линейного поиска.]]&lt;br /&gt;
&lt;br /&gt;
''' Линейный поиск '''&lt;br /&gt;
&lt;br /&gt;
Выбираем шаг &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;. При попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt;i+(1 \cdot q), i+(2 \cdot q), i+(3 \cdot q)&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку. В неё и запишем элемент.&lt;br /&gt;
По сути последовательный поиск - частный случай линейного, где &amp;lt;tex&amp;gt;q=1&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Hashtables56.PNG|400px|Линейный поиск с шагом q.]]&lt;br /&gt;
&lt;br /&gt;
''' Квадратичный поиск '''&lt;br /&gt;
&lt;br /&gt;
Шаг &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; не фиксирован, а изменяется квадратично: &amp;lt;tex&amp;gt;q = 1,4,9,16...&amp;lt;/tex&amp;gt;. Соответственно при попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt; i+1, i+4, i+9&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку.&lt;br /&gt;
&lt;br /&gt;
[[Файл:hashtables3.png|400px|Квадратичный поиск.]]&lt;br /&gt;
&lt;br /&gt;
=== Проверка наличия элемента в таблице===&lt;br /&gt;
&lt;br /&gt;
Проверка осуществляется аналогично добавлению: мы проверяем ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; и другие, в соответствии с выбранной стратегией, пока не найдём искомый элемент или свободную ячейку.&lt;br /&gt;
&lt;br /&gt;
При поиске элемента может получится так, что мы дойдём до конца таблицы. Обычно поиск продолжается, начиная с другого конца, пока мы не придём в ту ячейку, откуда начинался поиск.&lt;br /&gt;
&lt;br /&gt;
=== Проблемы данных стратегий ===&lt;br /&gt;
&lt;br /&gt;
Проблем две — крайне нетривиальное удаление элемента из таблицы и образование кластеров  — последовательностей занятых ячеек.&lt;br /&gt;
&lt;br /&gt;
Кластеризация замедляет все операции с хеш-таблицей: при добавлении требуется перебирать всё больше элементов, при проверке тоже. Чем больше в таблице элементов, тем больше в ней кластеры и тем выше вероятность того, что добавляемый элемент попадёт в кластер.&lt;br /&gt;
Для защиты от кластеризации используется двойное хеширование и [[Хеширование кукушки|хеширование кукушки]].&lt;br /&gt;
&lt;br /&gt;
=== Удаление элемента без пометок ===&lt;br /&gt;
&lt;br /&gt;
Рассуждение будет описывать случай с линейным поиском хеша. Будем при удалении элемента сдвигать всё последующие на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; позиций назад. При этом:&lt;br /&gt;
* если в цепочке встречается элемент с другим хешем, то он должен остаться на своём месте (такая ситуация может возникнуть если оставшаяся часть цепочки была добавлена позже этого элемента)&lt;br /&gt;
* в цепочке не должно оставаться &amp;quot;дырок&amp;quot;, тогда любой элемент с данным хешем будет доступен из начала цепи&lt;br /&gt;
&lt;br /&gt;
Учитывая это будем действовать следующим образом: при поиске следующего элемента цепочки будем пропускать все ячейки с другим значением хеша, первый найденный элемент копировать в текущую ячейку, и затем рекурсивно его удалять. Если такой следующей ячейки нет, то текущий элемент можно просто удалить, сторонние цепочки при этом не разрушатся (чего нельзя сказать про случай квадратичного поиска).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
''' Псевдокод '''&lt;br /&gt;
&lt;br /&gt;
 '''function''' delete('''Item''' i) &lt;br /&gt;
      j = i + q&lt;br /&gt;
      '''while''' table[j] == '''null''' '''or''' table[j].key != table[i].key&lt;br /&gt;
         '''if''' table[j] == '''null'''&lt;br /&gt;
            table[i] = '''null'''&lt;br /&gt;
            '''return'''&lt;br /&gt;
         j += q&lt;br /&gt;
      table[i] = table[j]&lt;br /&gt;
      delete(j);    &lt;br /&gt;
&lt;br /&gt;
Хеш-таблицу считаем зацикленной&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Утверждение&lt;br /&gt;
|about=о времени работы&lt;br /&gt;
|statement=Асимптотически время работы &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\mathrm{find}&amp;lt;/tex&amp;gt; совпадают&lt;br /&gt;
|proof=&lt;br /&gt;
Заметим что указатель &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; в каждой итерации перемещается вперёд на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; (с учётом рекурсивных вызовов &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt;). То есть этот алгоритм последовательно пройдёт по цепочке от удаляемого элемента до последнего {{---}} с учётом вызова &amp;lt;tex&amp;gt;\mathrm{find}&amp;lt;/tex&amp;gt; собственно для нахождения удаляемого элемента, мы посетим все ячейки цепи.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Вариант с зацикливанием мы не рассматриваем, поскольку если &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; взаимнопросто с размером хеш-таблицы, то для зацикливания в ней вообще не должно быть свободных позиций&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Теперь докажем почему этот алгоритм работает. Собственно нам требуется сохранение трёх условий.&lt;br /&gt;
* В редактируемой цепи не остаётся дырок&lt;br /&gt;
Докажем по индукции. Если на данной итерации мы просто удаляем элемент (база), то после него ничего нет, всё верно. Если же нет, то вызванный в конце &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt; (см. псевдокод) заметёт созданную дыру (скопированный элемент), и сам, по предположению, новых не создаст.&lt;br /&gt;
* Элементы, которые уже на своих местах, не должны быть сдвинуты.&lt;br /&gt;
Это учтено.&lt;br /&gt;
* В других цепочках не появятся дыры&lt;br /&gt;
Противное возможно только в том случае, если какой-то элемент был действительно удалён. Удаляем мы только последнюю ячейку в цепи, и если бы на её месте возникла дыра для сторонней цепочки, это бы означало что элемент, стоящий на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; позиций назад, одновременно принадлежал нашей и другой цепочкам, что невозможно.&lt;br /&gt;
&lt;br /&gt;
==Двойное хеширование==&lt;br /&gt;
'''Двойное хеширование''' (англ. double hashing) {{---}} метод борьбы с коллизиями, возникающими при открытой адресации, основанный на использовании двух хеш-функций для построения различных последовательностей исследования хеш-таблицы.&lt;br /&gt;
&lt;br /&gt;
===Принцип двойного хеширования===&lt;br /&gt;
При двойном хешировании используются две независимые хеш-функции &amp;lt;tex&amp;gt; h_1(k) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; h_2(k) &amp;lt;/tex&amp;gt;. Пусть &amp;lt;tex&amp;gt; k &amp;lt;/tex&amp;gt; {{---}} это наш ключ, &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt; {{---}} размер нашей таблицы, &amp;lt;tex&amp;gt;n \bmod m &amp;lt;/tex&amp;gt; {{---}} остаток от деления &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt; на &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, тогда сначала исследуется ячейка с адресом &amp;lt;tex&amp;gt; h_1(k) &amp;lt;/tex&amp;gt;, если она уже занята, то рассматривается &amp;lt;tex&amp;gt; (h_1(k) +  h_2(k)) \bmod m &amp;lt;/tex&amp;gt;, затем &amp;lt;tex&amp;gt; (h_1(k) +  2 \cdot h_2(k)) \bmod m &amp;lt;/tex&amp;gt; и так далее. В общем случае идёт проверка последовательности ячеек &amp;lt;tex&amp;gt; (h_1(k) +  i \cdot h_2(k)) \bmod m &amp;lt;/tex&amp;gt; где &amp;lt;tex&amp;gt;  i = (0, 1, \; ... \;,  m - 1) &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, операции вставки, удаления и поиска в лучшем случае выполняются за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;, в худшем {{---}} за &amp;lt;tex&amp;gt;O(m)&amp;lt;/tex&amp;gt;, что не отличается от обычного [[Открытое_и_закрытое_хеширование#Линейное разрешение коллизий|линейного разрешения коллизий]].&lt;br /&gt;
Однако в среднем, при грамотном выборе хеш-функций, двойное хеширование будет выдавать лучшие результаты, за счёт того, что вероятность совпадения значений сразу двух независимых хеш-функций ниже, чем одной.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt;\forall x \neq y \; \exists h_1,h_2 : p(h_1(x)=h_1(y))&amp;gt; p((h_1(x)=h_1(y)) \land (h_2(x)=h_2(y)))&amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Выбор хеш-функций===&lt;br /&gt;
&amp;lt;tex&amp;gt; h_1 &amp;lt;/tex&amp;gt; может быть обычной хеш-функцией. Однако чтобы последовательность исследования могла охватить всю таблицу, &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; должна возвращать значения:&lt;br /&gt;
*не равные &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
*независимые  от &amp;lt;tex&amp;gt; h_1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
*взаимно простые с величиной хеш-таблицы&lt;br /&gt;
&lt;br /&gt;
Есть два удобных способа это сделать. Первый состоит в том, что в качестве размера таблицы используется простое число, а &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; возвращает натуральные числа, меньшие &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;. Второй {{---}} размер таблицы является степенью двойки, а &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; возвращает нечетные значения.&lt;br /&gt;
&lt;br /&gt;
Например, если размер таблицы равен &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, то в качестве &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; можно использовать функцию вида &amp;lt;tex&amp;gt; h_2(k) = k \bmod (m-1) + 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Файл: Вставка при двойном хэшировании.svg.jpeg|thumb|right|Вставка при двойном хешировании]]&lt;br /&gt;
&lt;br /&gt;
===Пример===&lt;br /&gt;
&lt;br /&gt;
Показана хеш-таблица размером 13 ячеек, в которой используются вспомогательные функции:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h(k,i) = (h_1(k) + i \cdot h_2(k)) \bmod 13 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h_1(k) = k \bmod 13 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h_2(k) = 1 + k \bmod 11 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Мы хотим вставить ключ 14. Изначально &amp;lt;tex&amp;gt; i = 0 &amp;lt;/tex&amp;gt;. Тогда &amp;lt;tex&amp;gt; h(14,0) = (h_1(14) + 0\cdot h_2(14)) \bmod 13 = 1 &amp;lt;/tex&amp;gt;. Но ячейка с индексом 1 занята, поэтому увеличиваем &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; на 1 и пересчитываем значение хеш-функции. Делаем так, пока не дойдем до пустой ячейки. При &amp;lt;tex&amp;gt; i = 2 &amp;lt;/tex&amp;gt; получаем &amp;lt;tex&amp;gt; h(14,2) = (h_1(14) + 2\cdot h_2(14)) \bmod 13 = 9 &amp;lt;/tex&amp;gt;. Ячейка с номером 9 свободна, значит записываем туда наш ключ.&lt;br /&gt;
&lt;br /&gt;
Таким образом, основная особенность двойного хеширования состоит в том, что при различных &amp;lt;tex&amp;gt; k &amp;lt;/tex&amp;gt; пара &amp;lt;tex&amp;gt; (h_1(k),h_2(k)) &amp;lt;/tex&amp;gt; дает различные последовательности ячеек для исследования.&lt;br /&gt;
&lt;br /&gt;
===Простая реализация===&lt;br /&gt;
Пусть у нас есть некоторый объект &amp;lt;tex&amp;gt; item &amp;lt;/tex&amp;gt;, в котором определено поле &amp;lt;tex&amp;gt; key &amp;lt;/tex&amp;gt;, от которого можно вычислить хеш-функции &amp;lt;tex&amp;gt; h_1(key)&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; h_2(key) &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Так же у нас есть таблица &amp;lt;tex&amp;gt; table &amp;lt;/tex&amp;gt; величиной &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, состоящая из объектов типа &amp;lt;tex&amp;gt; item &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
'''Вставка'''&lt;br /&gt;
 '''function''' add('''Item''' item)&lt;br /&gt;
      x = h1(item.key)&lt;br /&gt;
      y = h2(item.key)&lt;br /&gt;
         '''for''' (i = 0..m)    	&lt;br /&gt;
            '''if''' table[x] == '''null'''&lt;br /&gt;
               table[x] = item&lt;br /&gt;
            '''return'''      &lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      table.resize()&amp;lt;span style=&amp;quot;color:Green&amp;quot;&amp;gt;// ошибка, требуется увеличить размер таблицы&lt;br /&gt;
&lt;br /&gt;
'''Поиск'''&lt;br /&gt;
 '''Item''' search('''Item''' key)&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m)&lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key&lt;br /&gt;
               '''return''' table[x]&lt;br /&gt;
            '''else'''&lt;br /&gt;
               '''return''' '''null'''&lt;br /&gt;
      x = (x + y) '''mod''' m   &lt;br /&gt;
      '''return''' '''null'''&lt;br /&gt;
&lt;br /&gt;
===Реализация с удалением===&lt;br /&gt;
Что бы наша хеш-таблица поддерживала удаление, требуется добавить массив &amp;lt;tex&amp;gt;deleted&amp;lt;/tex&amp;gt; типов &amp;lt;tex&amp;gt;bool&amp;lt;/tex&amp;gt;, равный по величине массиву &amp;lt;tex&amp;gt;table&amp;lt;/tex&amp;gt;. Теперь при удалении мы просто будем помечать наш объект ''как удалённый'', а при добавлении как ''не удалённый'' и замещать новым добавляемым объектом. При поиске, помимо равенства ключей, мы смотрим, удалён ли элемент, если да, то идём дальше.&lt;br /&gt;
&lt;br /&gt;
'''Вставка'''&lt;br /&gt;
 '''function''' add('''Item''' item)&lt;br /&gt;
      x = h1(item.key)&lt;br /&gt;
      y = h2(item.key)&lt;br /&gt;
      '''for''' (i = 0..m)   	&lt;br /&gt;
         '''if''' table[x] == '''null''' '''or''' deleted[x]&lt;br /&gt;
            table[x] = item&lt;br /&gt;
            deleted[x] = '''false'''&lt;br /&gt;
            '''return'''      &lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      table.resize()&amp;lt;span style=&amp;quot;color:Green&amp;quot;&amp;gt;// ошибка, требуется увеличить размер таблицы&lt;br /&gt;
'''Поиск'''&lt;br /&gt;
 '''Item''' search('''Item''' key)&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m) &lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key '''and''' !deleted[x]&lt;br /&gt;
               '''return''' table[x]&lt;br /&gt;
         '''else'''&lt;br /&gt;
            '''return''' '''null'''&lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      '''return''' '''null'''&lt;br /&gt;
&lt;br /&gt;
'''Удаление'''&lt;br /&gt;
 '''function''' remove('''Item''' key)&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m)&lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key&lt;br /&gt;
               deleted[x] = '''true'''&lt;br /&gt;
         '''else''' &lt;br /&gt;
            '''return'''&lt;br /&gt;
      x = (x + y) '''mod''' m&lt;br /&gt;
&lt;br /&gt;
== Разрешение коллизий в Java 8==&lt;br /&gt;
В Java 8 для разрешения коллизий используется модифицированный метод цепочек. Суть его заключается в том, что когда количество элементов в корзине превышает определенное значение, данный бакет переходит от использования связного списка к использованию сбалансированного дерева. Такой подход позволяет улучшить производительность с &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;O(log(n))&amp;lt;/tex&amp;gt;.  Данный способ используется в таких коллекциях как HashMap, LinkedHashMap и ConcurrentHashMap.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Hashing_in_Java8.png|400px|left|Хеширование в Java 8.]]&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
* [[Хеширование]]&lt;br /&gt;
* [[Хеширование_кукушки|Хеширование кукушки]]&lt;br /&gt;
* [[Идеальное_хеширование|Идеальное хеширование]]&lt;br /&gt;
&lt;br /&gt;
== Источники Информации ==&lt;br /&gt;
* Бакнелл Дж. М. «Фундаментальные алгоритмы и структуры данных в Delphi», 2003&lt;br /&gt;
* Кормен, Томас Х., Лейзерсон, Чарльз И., Ривест, Рональд Л., Штайн Клиффорд «Алгоритмы: построение и анализ», 2-е издание. Пер. с англ. — М.:Издательский дом &amp;quot;Вильямс&amp;quot;, 2010.— Парал. тит. англ. — ISBN 978-5-8459-0857-5 (рус.)&lt;br /&gt;
* Дональд Кнут. «Искусство программирования, том 3. Сортировка и поиск» {{---}} «Вильямс», 2007 г.{{---}} ISBN 0-201-89685-0&lt;br /&gt;
* Седжвик Р. «Фундаментальные алгоритмы на C. Части 1-4. Анализ. Структуры данных. Сортировка. Поиск», 2003&lt;br /&gt;
* [http://openjdk.java.net/jeps/180 Handle Frequent HashMap Collisions with Balanced Trees]&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Double_hashing Wikipedia {{---}} Double_hashing]&lt;br /&gt;
* [http://ru.wikipedia.org/wiki/%D0%A5%D0%B5%D1%88-%D1%82%D0%B0%D0%B1%D0%BB%D0%B8%D1%86%D0%B0 Разрешение коллизий]&lt;br /&gt;
* [http://rain.ifmo.ru/cat/view.php/vis/hashtables/hash-2001-2 Пример хеш таблицы]&lt;br /&gt;
* [http://research.cs.vt.edu/AVresearch/hashing/double.php Пример хеш таблицы с двойным хешированием]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория: Хеширование]]&lt;/div&gt;</summary>
		<author><name>Kozichuk</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%A0%D0%B0%D0%B7%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BA%D0%BE%D0%BB%D0%BB%D0%B8%D0%B7%D0%B8%D0%B9&amp;diff=47150</id>
		<title>Разрешение коллизий</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A0%D0%B0%D0%B7%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BA%D0%BE%D0%BB%D0%BB%D0%B8%D0%B7%D0%B8%D0%B9&amp;diff=47150"/>
				<updated>2015-05-31T21:11:13Z</updated>
		
		<summary type="html">&lt;p&gt;Kozichuk: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Разрешение [[Хеш-таблица|коллизий]]''' (англ. collision resolution) в [[Хеш-таблица|хеш-таблице]], задача, решаемая несколькими способами. Например, при помощи метода цепочке или открытой адресации. Коллизии замедляют время работы с хеш-таблицой, поэтому с ними нужно бороться.&lt;br /&gt;
&lt;br /&gt;
== Разрешение коллизий с помощью цепочек ==&lt;br /&gt;
[[Файл:open_hash.png|thumb|380px|right|Разрешение коллизий при помощи цепочек.]]&lt;br /&gt;
Каждая ячейка &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; массива &amp;lt;tex&amp;gt;H&amp;lt;/tex&amp;gt; содержит указатель на начало [[Список|списка]] всех элементов, хеш-код которых равен &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, либо указывает на их отсутствие. Коллизии приводят к тому, что появляются списки размером больше одного элемента.&lt;br /&gt;
&lt;br /&gt;
Время, необходимое для вставки в наихудшем случае равно &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;. Это операция выполняет быстро, так как считается, что вставляемый элемент отсутствует в таблице, но если потребуется, то перед вставкой мы можем выполнить поиск этого элемента.&lt;br /&gt;
&lt;br /&gt;
Время работы поиска в наихудшем случае пропорционально длине списка, а если все &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; ключей захешировались в одну и ту же ячейку (создав список длиной &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;) время поиска будет равно &amp;lt;tex&amp;gt;\Theta(n)&amp;lt;/tex&amp;gt; плюс время вычисления хеш-функции, что ничуть не лучше, чем использование связного списка для хранения всех &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; элементов.&lt;br /&gt;
&lt;br /&gt;
Удаления элемента может быть выполнено за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;, как и вставка, при использовании двухсвязного списка.&lt;br /&gt;
&lt;br /&gt;
== Линейное разрешение коллизий ==&lt;br /&gt;
[[Файл:close_hash.png|thumb|380px|right|Пример хеш-таблицы с открытой адресацией и линейным пробированием.]]&lt;br /&gt;
Все элементы хранятся непосредственно в хеш-таблице, без использования связных списков. В отличии от хеширования с цепочками, при использовании этого метода может возникнуть ситуация, когда хеш-таблица окажется полностью заполненной, следовательно будет невозможно добавлять в неё новые элементы. Так что при возникновении такой ситуации решением может быть динамическое увеличение размера хеш-таблицы, с одновременной её перестройкой.&lt;br /&gt;
&lt;br /&gt;
=== Стратегии поиска ===&lt;br /&gt;
&lt;br /&gt;
''' Последовательный поиск '''&lt;br /&gt;
&lt;br /&gt;
При попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt;i+1, i+2, i+3&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку. В неё и запишем элемент.&lt;br /&gt;
&lt;br /&gt;
[[Файл:hashtables1.png|400px|Последовательный поиск, частный случай линейного поиска.]]&lt;br /&gt;
&lt;br /&gt;
''' Линейный поиск '''&lt;br /&gt;
&lt;br /&gt;
Выбираем шаг &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;. При попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt;i+(1 \cdot q), i+(2 \cdot q), i+(3 \cdot q)&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку. В неё и запишем элемент.&lt;br /&gt;
По сути последовательный поиск - частный случай линейного, где &amp;lt;tex&amp;gt;q=1&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Hashtables56.PNG|400px|Линейный поиск с шагом q.]]&lt;br /&gt;
&lt;br /&gt;
''' Квадратичный поиск '''&lt;br /&gt;
&lt;br /&gt;
Шаг &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; не фиксирован, а изменяется квадратично: &amp;lt;tex&amp;gt;q = 1,4,9,16...&amp;lt;/tex&amp;gt;. Соответственно при попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt; i+1, i+4, i+9&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку.&lt;br /&gt;
&lt;br /&gt;
[[Файл:hashtables3.png|400px|Квадратичный поиск.]]&lt;br /&gt;
&lt;br /&gt;
=== Проверка наличия элемента в таблице===&lt;br /&gt;
&lt;br /&gt;
Проверка осуществляется аналогично добавлению: мы проверяем ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; и другие, в соответствии с выбранной стратегией, пока не найдём искомый элемент или свободную ячейку.&lt;br /&gt;
&lt;br /&gt;
При поиске элемента может получится так, что мы дойдём до конца таблицы. Обычно поиск продолжается, начиная с другого конца, пока мы не придём в ту ячейку, откуда начинался поиск.&lt;br /&gt;
&lt;br /&gt;
=== Проблемы данных стратегий ===&lt;br /&gt;
&lt;br /&gt;
Проблем две — крайне нетривиальное удаление элемента из таблицы и образование кластеров  — последовательностей занятых ячеек.&lt;br /&gt;
&lt;br /&gt;
Кластеризация замедляет все операции с хеш-таблицей: при добавлении требуется перебирать всё больше элементов, при проверке тоже. Чем больше в таблице элементов, тем больше в ней кластеры и тем выше вероятность того, что добавляемый элемент попадёт в кластер.&lt;br /&gt;
Для защиты от кластеризации используется двойное хеширование и [[Хеширование кукушки|хеширование кукушки]].&lt;br /&gt;
&lt;br /&gt;
=== Удаление элемента без пометок ===&lt;br /&gt;
&lt;br /&gt;
Рассуждение будет описывать случай с линейным поиском хеша. Будем при удалении элемента сдвигать всё последующие на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; позиций назад. При этом:&lt;br /&gt;
* если в цепочке встречается элемент с другим хешем, то он должен остаться на своём месте (такая ситуация может возникнуть если оставшаяся часть цепочки была добавлена позже этого элемента)&lt;br /&gt;
* в цепочке не должно оставаться &amp;quot;дырок&amp;quot;, тогда любой элемент с данным хешем будет доступен из начала цепи&lt;br /&gt;
&lt;br /&gt;
Учитывая это будем действовать следующим образом: при поиске следующего элемента цепочки будем пропускать все ячейки с другим значением хеша, первый найденный элемент копировать в текущую ячейку, и затем рекурсивно его удалять. Если такой следующей ячейки нет, то текущий элемент можно просто удалить, сторонние цепочки при этом не разрушатся (чего нельзя сказать про случай квадратичного поиска).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
''' Псевдокод '''&lt;br /&gt;
&lt;br /&gt;
 '''function''' delete('''Item''' i) &lt;br /&gt;
      j = i + q&lt;br /&gt;
      '''while''' table[j] == '''null''' '''or''' table[j].key != table[i].key&lt;br /&gt;
         '''if''' table[j] == '''null'''&lt;br /&gt;
            table[i] = '''null'''&lt;br /&gt;
            '''return'''&lt;br /&gt;
         j += q&lt;br /&gt;
      table[i] = table[j]&lt;br /&gt;
      delete(j);    &lt;br /&gt;
&lt;br /&gt;
Хеш-таблицу считаем зацикленной&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Утверждение&lt;br /&gt;
|about=о времени работы&lt;br /&gt;
|statement=Асимптотически время работы &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\mathrm{find}&amp;lt;/tex&amp;gt; совпадают&lt;br /&gt;
|proof=&lt;br /&gt;
Заметим что указатель &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; в каждой итерации перемещается вперёд на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; (с учётом рекурсивных вызовов &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt;). То есть этот алгоритм последовательно пройдёт по цепочке от удаляемого элемента до последнего {{---}} с учётом вызова &amp;lt;tex&amp;gt;\mathrm{find}&amp;lt;/tex&amp;gt; собственно для нахождения удаляемого элемента, мы посетим все ячейки цепи.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Вариант с зацикливанием мы не рассматриваем, поскольку если &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; взаимнопросто с размером хеш-таблицы, то для зацикливания в ней вообще не должно быть свободных позиций&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Теперь докажем почему этот алгоритм работает. Собственно нам требуется сохранение трёх условий.&lt;br /&gt;
* В редактируемой цепи не остаётся дырок&lt;br /&gt;
Докажем по индукции. Если на данной итерации мы просто удаляем элемент (база), то после него ничего нет, всё верно. Если же нет, то вызванный в конце &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt; (см. псевдокод) заметёт созданную дыру (скопированный элемент), и сам, по предположению, новых не создаст.&lt;br /&gt;
* Элементы, которые уже на своих местах, не должны быть сдвинуты.&lt;br /&gt;
Это учтено.&lt;br /&gt;
* В других цепочках не появятся дыры&lt;br /&gt;
Противное возможно только в том случае, если какой-то элемент был действительно удалён. Удаляем мы только последнюю ячейку в цепи, и если бы на её месте возникла дыра для сторонней цепочки, это бы означало что элемент, стоящий на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; позиций назад, одновременно принадлежал нашей и другой цепочкам, что невозможно.&lt;br /&gt;
&lt;br /&gt;
==Двойное хеширование==&lt;br /&gt;
'''Двойное хеширование''' (англ. double hashing) {{---}} метод борьбы с коллизиями, возникающими при открытой адресации, основанный на использовании двух хеш-функций для построения различных последовательностей исследования хеш-таблицы.&lt;br /&gt;
&lt;br /&gt;
===Принцип двойного хеширования===&lt;br /&gt;
При двойном хешировании используются две независимые хеш-функции &amp;lt;tex&amp;gt; h_1(k) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; h_2(k) &amp;lt;/tex&amp;gt;. Пусть &amp;lt;tex&amp;gt; k &amp;lt;/tex&amp;gt; {{---}} это наш ключ, &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt; {{---}} размер нашей таблицы, &amp;lt;tex&amp;gt;n \bmod m &amp;lt;/tex&amp;gt; {{---}} остаток от деления &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt; на &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, тогда сначала исследуется ячейка с адресом &amp;lt;tex&amp;gt; h_1(k) &amp;lt;/tex&amp;gt;, если она уже занята, то рассматривается &amp;lt;tex&amp;gt; (h_1(k) +  h_2(k)) \bmod m &amp;lt;/tex&amp;gt;, затем &amp;lt;tex&amp;gt; (h_1(k) +  2 \cdot h_2(k)) \bmod m &amp;lt;/tex&amp;gt; и так далее. В общем случае идёт проверка последовательности ячеек &amp;lt;tex&amp;gt; (h_1(k) +  i \cdot h_2(k)) \bmod m &amp;lt;/tex&amp;gt; где &amp;lt;tex&amp;gt;  i = (0, 1, \; ... \;,  m - 1) &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, операции вставки, удаления и поиска в лучшем случае выполняются за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;, в худшем {{---}} за &amp;lt;tex&amp;gt;O(m)&amp;lt;/tex&amp;gt;, что не отличается от обычного [[Открытое_и_закрытое_хеширование#Линейное разрешение коллизий|линейного разрешения коллизий]].&lt;br /&gt;
Однако в среднем, при грамотном выборе хеш-функций, двойное хеширование будет выдавать лучшие результаты, за счёт того, что вероятность совпадения значений сразу двух независимых хеш-функций ниже, чем одной.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt;\forall x \neq y \; \exists h_1,h_2 : p(h_1(x)=h_1(y))&amp;gt; p((h_1(x)=h_1(y)) \land (h_2(x)=h_2(y)))&amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Выбор хеш-функций===&lt;br /&gt;
&amp;lt;tex&amp;gt; h_1 &amp;lt;/tex&amp;gt; может быть обычной хеш-функцией. Однако чтобы последовательность исследования могла охватить всю таблицу, &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; должна возвращать значения:&lt;br /&gt;
*не равные &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
*независимые  от &amp;lt;tex&amp;gt; h_1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
*взаимно простые с величиной хеш-таблицы&lt;br /&gt;
&lt;br /&gt;
Есть два удобных способа это сделать. Первый состоит в том, что в качестве размера таблицы используется простое число, а &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; возвращает натуральные числа, меньшие &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;. Второй {{---}} размер таблицы является степенью двойки, а &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; возвращает нечетные значения.&lt;br /&gt;
&lt;br /&gt;
Например, если размер таблицы равен &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, то в качестве &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; можно использовать функцию вида &amp;lt;tex&amp;gt; h_2(k) = k \bmod (m-1) + 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Файл: Вставка при двойном хэшировании.svg.jpeg|thumb|right|Вставка при двойном хешировании]]&lt;br /&gt;
&lt;br /&gt;
===Пример===&lt;br /&gt;
&lt;br /&gt;
Показана хеш-таблица размером 13 ячеек, в которой используются вспомогательные функции:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h(k,i) = (h_1(k) + i \cdot h_2(k)) \bmod 13 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h_1(k) = k \bmod 13 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h_2(k) = 1 + k \bmod 11 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Мы хотим вставить ключ 14. Изначально &amp;lt;tex&amp;gt; i = 0 &amp;lt;/tex&amp;gt;. Тогда &amp;lt;tex&amp;gt; h(14,0) = (h_1(14) + 0\cdot h_2(14)) \bmod 13 = 1 &amp;lt;/tex&amp;gt;. Но ячейка с индексом 1 занята, поэтому увеличиваем &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; на 1 и пересчитываем значение хеш-функции. Делаем так, пока не дойдем до пустой ячейки. При &amp;lt;tex&amp;gt; i = 2 &amp;lt;/tex&amp;gt; получаем &amp;lt;tex&amp;gt; h(14,2) = (h_1(14) + 2\cdot h_2(14)) \bmod 13 = 9 &amp;lt;/tex&amp;gt;. Ячейка с номером 9 свободна, значит записываем туда наш ключ.&lt;br /&gt;
&lt;br /&gt;
Таким образом, основная особенность двойного хеширования состоит в том, что при различных &amp;lt;tex&amp;gt; k &amp;lt;/tex&amp;gt; пара &amp;lt;tex&amp;gt; (h_1(k),h_2(k)) &amp;lt;/tex&amp;gt; дает различные последовательности ячеек для исследования.&lt;br /&gt;
&lt;br /&gt;
===Простая реализация===&lt;br /&gt;
Пусть у нас есть некоторый объект &amp;lt;tex&amp;gt; item &amp;lt;/tex&amp;gt;, в котором определено поле &amp;lt;tex&amp;gt; key &amp;lt;/tex&amp;gt;, от которого можно вычислить хеш-функции &amp;lt;tex&amp;gt; h_1(key)&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; h_2(key) &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Так же у нас есть таблица &amp;lt;tex&amp;gt; table &amp;lt;/tex&amp;gt; величиной &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, состоящая из объектов типа &amp;lt;tex&amp;gt; item &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
'''Вставка'''&lt;br /&gt;
 '''function''' add('''Item''' item)&lt;br /&gt;
      x = h1(item.key)&lt;br /&gt;
      y = h2(item.key)&lt;br /&gt;
         '''for''' (i = 0..m)    	&lt;br /&gt;
            '''if''' table[x] == '''null'''&lt;br /&gt;
               table[x] = item&lt;br /&gt;
            '''return'''      &lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      table.resize()&amp;lt;span style=&amp;quot;color:Green&amp;quot;&amp;gt;// ошибка, требуется увеличить размер таблицы&lt;br /&gt;
&lt;br /&gt;
'''Поиск'''&lt;br /&gt;
 '''Item''' search('''Item''' key)&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m)&lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key&lt;br /&gt;
               '''return''' table[x]&lt;br /&gt;
            '''else'''&lt;br /&gt;
               '''return''' '''null'''&lt;br /&gt;
      x = (x + y) '''mod''' m   &lt;br /&gt;
      '''return''' '''null'''&lt;br /&gt;
&lt;br /&gt;
===Реализация с удалением===&lt;br /&gt;
Что бы наша хеш-таблица поддерживала удаление, требуется добавить массив &amp;lt;tex&amp;gt;deleted&amp;lt;/tex&amp;gt; типов &amp;lt;tex&amp;gt;bool&amp;lt;/tex&amp;gt;, равный по величине массиву &amp;lt;tex&amp;gt;table&amp;lt;/tex&amp;gt;. Теперь при удалении мы просто будем помечать наш объект ''как удалённый'', а при добавлении как ''не удалённый'' и замещать новым добавляемым объектом. При поиске, помимо равенства ключей, мы смотрим, удалён ли элемент, если да, то идём дальше.&lt;br /&gt;
&lt;br /&gt;
'''Вставка'''&lt;br /&gt;
 '''function''' add('''Item''' item)&lt;br /&gt;
      x = h1(item.key)&lt;br /&gt;
      y = h2(item.key)&lt;br /&gt;
      '''for''' (i = 0..m)   	&lt;br /&gt;
         '''if''' table[x] == '''null''' '''or''' deleted[x]&lt;br /&gt;
            table[x] = item&lt;br /&gt;
            deleted[x] = '''false'''&lt;br /&gt;
            '''return'''      &lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      table.resize()&amp;lt;span style=&amp;quot;color:Green&amp;quot;&amp;gt;// ошибка, требуется увеличить размер таблицы&lt;br /&gt;
'''Поиск'''&lt;br /&gt;
 '''Item''' search('''Item''' key)&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m) &lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key '''and''' !deleted[x]&lt;br /&gt;
               '''return''' table[x]&lt;br /&gt;
         '''else'''&lt;br /&gt;
            '''return''' '''null'''&lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      '''return''' '''null'''&lt;br /&gt;
&lt;br /&gt;
'''Удаление'''&lt;br /&gt;
 '''function''' remove('''Item''' key)&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m)&lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key&lt;br /&gt;
               deleted[x] = '''true'''&lt;br /&gt;
         '''else''' &lt;br /&gt;
            '''return'''&lt;br /&gt;
      x = (x + y) '''mod''' m&lt;br /&gt;
&lt;br /&gt;
== Разрешение коллизий в Java 8==&lt;br /&gt;
В Java 8 для разрешения коллизий используется модифицированный метод цепочек. Суть его заключается в том, что когда количество элементов в корзине превышает определенное значение, данный бакет переходит от использования связного списка к использованию сбалансированного дерева. Такой подход позволяет улучшить производительность с &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;O(log(n))&amp;lt;/tex&amp;gt;.  Данный способ используется в таких коллекциях как HashMap, LinkedHashMap и ConcurrentHashMap.&lt;br /&gt;
[[Файл:Hashing_in_Java8.png|400px|Хеширование в Java 8.]]&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
* [[Хеширование]]&lt;br /&gt;
* [[Хеширование_кукушки|Хеширование кукушки]]&lt;br /&gt;
* [[Идеальное_хеширование|Идеальное хеширование]]&lt;br /&gt;
&lt;br /&gt;
== Источники Информации ==&lt;br /&gt;
* Бакнелл Дж. М. «Фундаментальные алгоритмы и структуры данных в Delphi», 2003&lt;br /&gt;
* Кормен, Томас Х., Лейзерсон, Чарльз И., Ривест, Рональд Л., Штайн Клиффорд «Алгоритмы: построение и анализ», 2-е издание. Пер. с англ. — М.:Издательский дом &amp;quot;Вильямс&amp;quot;, 2010.— Парал. тит. англ. — ISBN 978-5-8459-0857-5 (рус.)&lt;br /&gt;
* Дональд Кнут. «Искусство программирования, том 3. Сортировка и поиск» {{---}} «Вильямс», 2007 г.{{---}} ISBN 0-201-89685-0&lt;br /&gt;
* Седжвик Р. «Фундаментальные алгоритмы на C. Части 1-4. Анализ. Структуры данных. Сортировка. Поиск», 2003&lt;br /&gt;
* [http://openjdk.java.net/jeps/180 Handle Frequent HashMap Collisions with Balanced Trees]&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Double_hashing Wikipedia {{---}} Double_hashing]&lt;br /&gt;
* [http://ru.wikipedia.org/wiki/%D0%A5%D0%B5%D1%88-%D1%82%D0%B0%D0%B1%D0%BB%D0%B8%D1%86%D0%B0 Разрешение коллизий]&lt;br /&gt;
* [http://rain.ifmo.ru/cat/view.php/vis/hashtables/hash-2001-2 Пример хеш таблицы]&lt;br /&gt;
* [http://research.cs.vt.edu/AVresearch/hashing/double.php Пример хеш таблицы с двойным хешированием]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория: Хеширование]]&lt;/div&gt;</summary>
		<author><name>Kozichuk</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%A0%D0%B0%D0%B7%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BA%D0%BE%D0%BB%D0%BB%D0%B8%D0%B7%D0%B8%D0%B9&amp;diff=47149</id>
		<title>Разрешение коллизий</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A0%D0%B0%D0%B7%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BA%D0%BE%D0%BB%D0%BB%D0%B8%D0%B7%D0%B8%D0%B9&amp;diff=47149"/>
				<updated>2015-05-31T21:10:09Z</updated>
		
		<summary type="html">&lt;p&gt;Kozichuk: /* См. также */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Разрешение [[Хеш-таблица|коллизий]]''' (англ. collision resolution) в [[Хеш-таблица|хеш-таблице]], задача, решаемая несколькими способами. Например, при помощи метода цепочке или открытой адресации. Коллизии замедляют время работы с хеш-таблицой, поэтому с ними нужно бороться.&lt;br /&gt;
&lt;br /&gt;
== Разрешение коллизий с помощью цепочек ==&lt;br /&gt;
[[Файл:open_hash.png|thumb|380px|right|Разрешение коллизий при помощи цепочек.]]&lt;br /&gt;
Каждая ячейка &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; массива &amp;lt;tex&amp;gt;H&amp;lt;/tex&amp;gt; содержит указатель на начало [[Список|списка]] всех элементов, хеш-код которых равен &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, либо указывает на их отсутствие. Коллизии приводят к тому, что появляются списки размером больше одного элемента.&lt;br /&gt;
&lt;br /&gt;
Время, необходимое для вставки в наихудшем случае равно &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;. Это операция выполняет быстро, так как считается, что вставляемый элемент отсутствует в таблице, но если потребуется, то перед вставкой мы можем выполнить поиск этого элемента.&lt;br /&gt;
&lt;br /&gt;
Время работы поиска в наихудшем случае пропорционально длине списка, а если все &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; ключей захешировались в одну и ту же ячейку (создав список длиной &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;) время поиска будет равно &amp;lt;tex&amp;gt;\Theta(n)&amp;lt;/tex&amp;gt; плюс время вычисления хеш-функции, что ничуть не лучше, чем использование связного списка для хранения всех &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; элементов.&lt;br /&gt;
&lt;br /&gt;
Удаления элемента может быть выполнено за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;, как и вставка, при использовании двухсвязного списка.&lt;br /&gt;
&lt;br /&gt;
== Линейное разрешение коллизий ==&lt;br /&gt;
[[Файл:close_hash.png|thumb|380px|right|Пример хеш-таблицы с открытой адресацией и линейным пробированием.]]&lt;br /&gt;
Все элементы хранятся непосредственно в хеш-таблице, без использования связных списков. В отличии от хеширования с цепочками, при использовании этого метода может возникнуть ситуация, когда хеш-таблица окажется полностью заполненной, следовательно будет невозможно добавлять в неё новые элементы. Так что при возникновении такой ситуации решением может быть динамическое увеличение размера хеш-таблицы, с одновременной её перестройкой.&lt;br /&gt;
&lt;br /&gt;
=== Стратегии поиска ===&lt;br /&gt;
&lt;br /&gt;
''' Последовательный поиск '''&lt;br /&gt;
&lt;br /&gt;
При попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt;i+1, i+2, i+3&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку. В неё и запишем элемент.&lt;br /&gt;
&lt;br /&gt;
[[Файл:hashtables1.png|400px|Последовательный поиск, частный случай линейного поиска.]]&lt;br /&gt;
&lt;br /&gt;
''' Линейный поиск '''&lt;br /&gt;
&lt;br /&gt;
Выбираем шаг &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;. При попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt;i+(1 \cdot q), i+(2 \cdot q), i+(3 \cdot q)&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку. В неё и запишем элемент.&lt;br /&gt;
По сути последовательный поиск - частный случай линейного, где &amp;lt;tex&amp;gt;q=1&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Hashtables56.PNG|400px|Линейный поиск с шагом q.]]&lt;br /&gt;
&lt;br /&gt;
''' Квадратичный поиск '''&lt;br /&gt;
&lt;br /&gt;
Шаг &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; не фиксирован, а изменяется квадратично: &amp;lt;tex&amp;gt;q = 1,4,9,16...&amp;lt;/tex&amp;gt;. Соответственно при попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt; i+1, i+4, i+9&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку.&lt;br /&gt;
&lt;br /&gt;
[[Файл:hashtables3.png|400px|Квадратичный поиск.]]&lt;br /&gt;
&lt;br /&gt;
=== Проверка наличия элемента в таблице===&lt;br /&gt;
&lt;br /&gt;
Проверка осуществляется аналогично добавлению: мы проверяем ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; и другие, в соответствии с выбранной стратегией, пока не найдём искомый элемент или свободную ячейку.&lt;br /&gt;
&lt;br /&gt;
При поиске элемента может получится так, что мы дойдём до конца таблицы. Обычно поиск продолжается, начиная с другого конца, пока мы не придём в ту ячейку, откуда начинался поиск.&lt;br /&gt;
&lt;br /&gt;
=== Проблемы данных стратегий ===&lt;br /&gt;
&lt;br /&gt;
Проблем две — крайне нетривиальное удаление элемента из таблицы и образование кластеров  — последовательностей занятых ячеек.&lt;br /&gt;
&lt;br /&gt;
Кластеризация замедляет все операции с хеш-таблицей: при добавлении требуется перебирать всё больше элементов, при проверке тоже. Чем больше в таблице элементов, тем больше в ней кластеры и тем выше вероятность того, что добавляемый элемент попадёт в кластер.&lt;br /&gt;
Для защиты от кластеризации используется двойное хеширование и [[Хеширование кукушки|хеширование кукушки]].&lt;br /&gt;
&lt;br /&gt;
=== Удаление элемента без пометок ===&lt;br /&gt;
&lt;br /&gt;
Рассуждение будет описывать случай с линейным поиском хеша. Будем при удалении элемента сдвигать всё последующие на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; позиций назад. При этом:&lt;br /&gt;
* если в цепочке встречается элемент с другим хешем, то он должен остаться на своём месте (такая ситуация может возникнуть если оставшаяся часть цепочки была добавлена позже этого элемента)&lt;br /&gt;
* в цепочке не должно оставаться &amp;quot;дырок&amp;quot;, тогда любой элемент с данным хешем будет доступен из начала цепи&lt;br /&gt;
&lt;br /&gt;
Учитывая это будем действовать следующим образом: при поиске следующего элемента цепочки будем пропускать все ячейки с другим значением хеша, первый найденный элемент копировать в текущую ячейку, и затем рекурсивно его удалять. Если такой следующей ячейки нет, то текущий элемент можно просто удалить, сторонние цепочки при этом не разрушатся (чего нельзя сказать про случай квадратичного поиска).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
''' Псевдокод '''&lt;br /&gt;
&lt;br /&gt;
 '''function''' delete('''Item''' i) &lt;br /&gt;
      j = i + q&lt;br /&gt;
      '''while''' table[j] == '''null''' '''or''' table[j].key != table[i].key&lt;br /&gt;
         '''if''' table[j] == '''null'''&lt;br /&gt;
            table[i] = '''null'''&lt;br /&gt;
            '''return'''&lt;br /&gt;
         j += q&lt;br /&gt;
      table[i] = table[j]&lt;br /&gt;
      delete(j);    &lt;br /&gt;
&lt;br /&gt;
Хеш-таблицу считаем зацикленной&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Утверждение&lt;br /&gt;
|about=о времени работы&lt;br /&gt;
|statement=Асимптотически время работы &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\mathrm{find}&amp;lt;/tex&amp;gt; совпадают&lt;br /&gt;
|proof=&lt;br /&gt;
Заметим что указатель &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; в каждой итерации перемещается вперёд на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; (с учётом рекурсивных вызовов &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt;). То есть этот алгоритм последовательно пройдёт по цепочке от удаляемого элемента до последнего {{---}} с учётом вызова &amp;lt;tex&amp;gt;\mathrm{find}&amp;lt;/tex&amp;gt; собственно для нахождения удаляемого элемента, мы посетим все ячейки цепи.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Вариант с зацикливанием мы не рассматриваем, поскольку если &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; взаимнопросто с размером хеш-таблицы, то для зацикливания в ней вообще не должно быть свободных позиций&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Теперь докажем почему этот алгоритм работает. Собственно нам требуется сохранение трёх условий.&lt;br /&gt;
* В редактируемой цепи не остаётся дырок&lt;br /&gt;
Докажем по индукции. Если на данной итерации мы просто удаляем элемент (база), то после него ничего нет, всё верно. Если же нет, то вызванный в конце &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt; (см. псевдокод) заметёт созданную дыру (скопированный элемент), и сам, по предположению, новых не создаст.&lt;br /&gt;
* Элементы, которые уже на своих местах, не должны быть сдвинуты.&lt;br /&gt;
Это учтено.&lt;br /&gt;
* В других цепочках не появятся дыры&lt;br /&gt;
Противное возможно только в том случае, если какой-то элемент был действительно удалён. Удаляем мы только последнюю ячейку в цепи, и если бы на её месте возникла дыра для сторонней цепочки, это бы означало что элемент, стоящий на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; позиций назад, одновременно принадлежал нашей и другой цепочкам, что невозможно.&lt;br /&gt;
&lt;br /&gt;
==Двойное хеширование==&lt;br /&gt;
'''Двойное хеширование''' (англ. double hashing) {{---}} метод борьбы с коллизиями, возникающими при открытой адресации, основанный на использовании двух хеш-функций для построения различных последовательностей исследования хеш-таблицы.&lt;br /&gt;
&lt;br /&gt;
===Принцип двойного хеширования===&lt;br /&gt;
При двойном хешировании используются две независимые хеш-функции &amp;lt;tex&amp;gt; h_1(k) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; h_2(k) &amp;lt;/tex&amp;gt;. Пусть &amp;lt;tex&amp;gt; k &amp;lt;/tex&amp;gt; {{---}} это наш ключ, &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt; {{---}} размер нашей таблицы, &amp;lt;tex&amp;gt;n \bmod m &amp;lt;/tex&amp;gt; {{---}} остаток от деления &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt; на &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, тогда сначала исследуется ячейка с адресом &amp;lt;tex&amp;gt; h_1(k) &amp;lt;/tex&amp;gt;, если она уже занята, то рассматривается &amp;lt;tex&amp;gt; (h_1(k) +  h_2(k)) \bmod m &amp;lt;/tex&amp;gt;, затем &amp;lt;tex&amp;gt; (h_1(k) +  2 \cdot h_2(k)) \bmod m &amp;lt;/tex&amp;gt; и так далее. В общем случае идёт проверка последовательности ячеек &amp;lt;tex&amp;gt; (h_1(k) +  i \cdot h_2(k)) \bmod m &amp;lt;/tex&amp;gt; где &amp;lt;tex&amp;gt;  i = (0, 1, \; ... \;,  m - 1) &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, операции вставки, удаления и поиска в лучшем случае выполняются за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;, в худшем {{---}} за &amp;lt;tex&amp;gt;O(m)&amp;lt;/tex&amp;gt;, что не отличается от обычного [[Открытое_и_закрытое_хеширование#Линейное разрешение коллизий|линейного разрешения коллизий]].&lt;br /&gt;
Однако в среднем, при грамотном выборе хеш-функций, двойное хеширование будет выдавать лучшие результаты, за счёт того, что вероятность совпадения значений сразу двух независимых хеш-функций ниже, чем одной.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt;\forall x \neq y \; \exists h_1,h_2 : p(h_1(x)=h_1(y))&amp;gt; p((h_1(x)=h_1(y)) \land (h_2(x)=h_2(y)))&amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Выбор хеш-функций===&lt;br /&gt;
&amp;lt;tex&amp;gt; h_1 &amp;lt;/tex&amp;gt; может быть обычной хеш-функцией. Однако чтобы последовательность исследования могла охватить всю таблицу, &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; должна возвращать значения:&lt;br /&gt;
*не равные &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
*независимые  от &amp;lt;tex&amp;gt; h_1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
*взаимно простые с величиной хеш-таблицы&lt;br /&gt;
&lt;br /&gt;
Есть два удобных способа это сделать. Первый состоит в том, что в качестве размера таблицы используется простое число, а &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; возвращает натуральные числа, меньшие &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;. Второй {{---}} размер таблицы является степенью двойки, а &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; возвращает нечетные значения.&lt;br /&gt;
&lt;br /&gt;
Например, если размер таблицы равен &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, то в качестве &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; можно использовать функцию вида &amp;lt;tex&amp;gt; h_2(k) = k \bmod (m-1) + 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Файл: Вставка при двойном хэшировании.svg.jpeg|thumb|right|Вставка при двойном хешировании]]&lt;br /&gt;
&lt;br /&gt;
===Пример===&lt;br /&gt;
&lt;br /&gt;
Показана хеш-таблица размером 13 ячеек, в которой используются вспомогательные функции:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h(k,i) = (h_1(k) + i \cdot h_2(k)) \bmod 13 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h_1(k) = k \bmod 13 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h_2(k) = 1 + k \bmod 11 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Мы хотим вставить ключ 14. Изначально &amp;lt;tex&amp;gt; i = 0 &amp;lt;/tex&amp;gt;. Тогда &amp;lt;tex&amp;gt; h(14,0) = (h_1(14) + 0\cdot h_2(14)) \bmod 13 = 1 &amp;lt;/tex&amp;gt;. Но ячейка с индексом 1 занята, поэтому увеличиваем &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; на 1 и пересчитываем значение хеш-функции. Делаем так, пока не дойдем до пустой ячейки. При &amp;lt;tex&amp;gt; i = 2 &amp;lt;/tex&amp;gt; получаем &amp;lt;tex&amp;gt; h(14,2) = (h_1(14) + 2\cdot h_2(14)) \bmod 13 = 9 &amp;lt;/tex&amp;gt;. Ячейка с номером 9 свободна, значит записываем туда наш ключ.&lt;br /&gt;
&lt;br /&gt;
Таким образом, основная особенность двойного хеширования состоит в том, что при различных &amp;lt;tex&amp;gt; k &amp;lt;/tex&amp;gt; пара &amp;lt;tex&amp;gt; (h_1(k),h_2(k)) &amp;lt;/tex&amp;gt; дает различные последовательности ячеек для исследования.&lt;br /&gt;
&lt;br /&gt;
===Простая реализация===&lt;br /&gt;
Пусть у нас есть некоторый объект &amp;lt;tex&amp;gt; item &amp;lt;/tex&amp;gt;, в котором определено поле &amp;lt;tex&amp;gt; key &amp;lt;/tex&amp;gt;, от которого можно вычислить хеш-функции &amp;lt;tex&amp;gt; h_1(key)&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; h_2(key) &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Так же у нас есть таблица &amp;lt;tex&amp;gt; table &amp;lt;/tex&amp;gt; величиной &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, состоящая из объектов типа &amp;lt;tex&amp;gt; item &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
'''Вставка'''&lt;br /&gt;
 '''function''' add('''Item''' item)&lt;br /&gt;
      x = h1(item.key)&lt;br /&gt;
      y = h2(item.key)&lt;br /&gt;
         '''for''' (i = 0..m)    	&lt;br /&gt;
            '''if''' table[x] == '''null'''&lt;br /&gt;
               table[x] = item&lt;br /&gt;
            '''return'''      &lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      table.resize()&amp;lt;span style=&amp;quot;color:Green&amp;quot;&amp;gt;// ошибка, требуется увеличить размер таблицы&lt;br /&gt;
&lt;br /&gt;
'''Поиск'''&lt;br /&gt;
 '''Item''' search('''Item''' key)&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m)&lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key&lt;br /&gt;
               '''return''' table[x]&lt;br /&gt;
            '''else'''&lt;br /&gt;
               '''return''' '''null'''&lt;br /&gt;
      x = (x + y) '''mod''' m   &lt;br /&gt;
      '''return''' '''null'''&lt;br /&gt;
&lt;br /&gt;
===Реализация с удалением===&lt;br /&gt;
Что бы наша хеш-таблица поддерживала удаление, требуется добавить массив &amp;lt;tex&amp;gt;deleted&amp;lt;/tex&amp;gt; типов &amp;lt;tex&amp;gt;bool&amp;lt;/tex&amp;gt;, равный по величине массиву &amp;lt;tex&amp;gt;table&amp;lt;/tex&amp;gt;. Теперь при удалении мы просто будем помечать наш объект ''как удалённый'', а при добавлении как ''не удалённый'' и замещать новым добавляемым объектом. При поиске, помимо равенства ключей, мы смотрим, удалён ли элемент, если да, то идём дальше.&lt;br /&gt;
&lt;br /&gt;
'''Вставка'''&lt;br /&gt;
 '''function''' add('''Item''' item)&lt;br /&gt;
      x = h1(item.key)&lt;br /&gt;
      y = h2(item.key)&lt;br /&gt;
      '''for''' (i = 0..m)   	&lt;br /&gt;
         '''if''' table[x] == '''null''' '''or''' deleted[x]&lt;br /&gt;
            table[x] = item&lt;br /&gt;
            deleted[x] = '''false'''&lt;br /&gt;
            '''return'''      &lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      table.resize()&amp;lt;span style=&amp;quot;color:Green&amp;quot;&amp;gt;// ошибка, требуется увеличить размер таблицы&lt;br /&gt;
'''Поиск'''&lt;br /&gt;
 '''Item''' search('''Item''' key)&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m) &lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key '''and''' !deleted[x]&lt;br /&gt;
               '''return''' table[x]&lt;br /&gt;
         '''else'''&lt;br /&gt;
            '''return''' '''null'''&lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      '''return''' '''null'''&lt;br /&gt;
&lt;br /&gt;
'''Удаление'''&lt;br /&gt;
 '''function''' remove('''Item''' key)&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m)&lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key&lt;br /&gt;
               deleted[x] = '''true'''&lt;br /&gt;
         '''else''' &lt;br /&gt;
            '''return'''&lt;br /&gt;
      x = (x + y) '''mod''' m&lt;br /&gt;
&lt;br /&gt;
== Разрешение коллизий в Java 8==&lt;br /&gt;
В Java 8 для разрешения коллизий используется модифицированный метод цепочек. Суть его заключается в том, что когда количество элементов в корзине превышает определенное значение, данный бакет переходит от использования связного списка к использованию сбалансированного дерева. Такой подход позволяет улучшить производительность с &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;O(log(n))&amp;lt;/tex&amp;gt;.  Данный способ используется в таких коллекциях как HashMap, LinkedHashMap и ConcurrentHashMap.&lt;br /&gt;
[[Файл:Hashing_in_Java8.png|400px|Хеширование в Java 8.]]&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
* [[Хеширование]]&lt;br /&gt;
* [[Хеширование_кукушки|Хеширование кукушки]]&lt;br /&gt;
* [[Идеальное_хеширование|Идеальное хеширование]]&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
* Бакнелл Дж. М. «Фундаментальные алгоритмы и структуры данных в Delphi», 2003&lt;br /&gt;
* Кормен, Томас Х., Лейзерсон, Чарльз И., Ривест, Рональд Л., Штайн Клиффорд «Алгоритмы: построение и анализ», 2-е издание. Пер. с англ. — М.:Издательский дом &amp;quot;Вильямс&amp;quot;, 2010.— Парал. тит. англ. — ISBN 978-5-8459-0857-5 (рус.)&lt;br /&gt;
* Дональд Кнут. «Искусство программирования, том 3. Сортировка и поиск» {{---}} «Вильямс», 2007 г.{{---}} ISBN 0-201-89685-0&lt;br /&gt;
* Седжвик Р. «Фундаментальные алгоритмы на C. Части 1-4. Анализ. Структуры данных. Сортировка. Поиск», 2003&lt;br /&gt;
&lt;br /&gt;
==Ссылки==&lt;br /&gt;
* [http://openjdk.java.net/jeps/180 Handle Frequent HashMap Collisions with Balanced Trees]&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Double_hashing Wikipedia {{---}} Double_hashing]&lt;br /&gt;
* [http://ru.wikipedia.org/wiki/%D0%A5%D0%B5%D1%88-%D1%82%D0%B0%D0%B1%D0%BB%D0%B8%D1%86%D0%B0 Разрешение коллизий]&lt;br /&gt;
* [http://rain.ifmo.ru/cat/view.php/vis/hashtables/hash-2001-2 Пример хеш таблицы]&lt;br /&gt;
* [http://research.cs.vt.edu/AVresearch/hashing/double.php Пример хеш таблицы с двойным хешированием]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория: Хеширование]]&lt;/div&gt;</summary>
		<author><name>Kozichuk</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%A0%D0%B0%D0%B7%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BA%D0%BE%D0%BB%D0%BB%D0%B8%D0%B7%D0%B8%D0%B9&amp;diff=47148</id>
		<title>Разрешение коллизий</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A0%D0%B0%D0%B7%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BA%D0%BE%D0%BB%D0%BB%D0%B8%D0%B7%D0%B8%D0%B9&amp;diff=47148"/>
				<updated>2015-05-31T21:09:05Z</updated>
		
		<summary type="html">&lt;p&gt;Kozichuk: /* Разрешение коллизий в Java 8 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Разрешение [[Хеш-таблица|коллизий]]''' (англ. collision resolution) в [[Хеш-таблица|хеш-таблице]], задача, решаемая несколькими способами. Например, при помощи метода цепочке или открытой адресации. Коллизии замедляют время работы с хеш-таблицой, поэтому с ними нужно бороться.&lt;br /&gt;
&lt;br /&gt;
== Разрешение коллизий с помощью цепочек ==&lt;br /&gt;
[[Файл:open_hash.png|thumb|380px|right|Разрешение коллизий при помощи цепочек.]]&lt;br /&gt;
Каждая ячейка &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; массива &amp;lt;tex&amp;gt;H&amp;lt;/tex&amp;gt; содержит указатель на начало [[Список|списка]] всех элементов, хеш-код которых равен &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, либо указывает на их отсутствие. Коллизии приводят к тому, что появляются списки размером больше одного элемента.&lt;br /&gt;
&lt;br /&gt;
Время, необходимое для вставки в наихудшем случае равно &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;. Это операция выполняет быстро, так как считается, что вставляемый элемент отсутствует в таблице, но если потребуется, то перед вставкой мы можем выполнить поиск этого элемента.&lt;br /&gt;
&lt;br /&gt;
Время работы поиска в наихудшем случае пропорционально длине списка, а если все &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; ключей захешировались в одну и ту же ячейку (создав список длиной &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;) время поиска будет равно &amp;lt;tex&amp;gt;\Theta(n)&amp;lt;/tex&amp;gt; плюс время вычисления хеш-функции, что ничуть не лучше, чем использование связного списка для хранения всех &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; элементов.&lt;br /&gt;
&lt;br /&gt;
Удаления элемента может быть выполнено за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;, как и вставка, при использовании двухсвязного списка.&lt;br /&gt;
&lt;br /&gt;
== Линейное разрешение коллизий ==&lt;br /&gt;
[[Файл:close_hash.png|thumb|380px|right|Пример хеш-таблицы с открытой адресацией и линейным пробированием.]]&lt;br /&gt;
Все элементы хранятся непосредственно в хеш-таблице, без использования связных списков. В отличии от хеширования с цепочками, при использовании этого метода может возникнуть ситуация, когда хеш-таблица окажется полностью заполненной, следовательно будет невозможно добавлять в неё новые элементы. Так что при возникновении такой ситуации решением может быть динамическое увеличение размера хеш-таблицы, с одновременной её перестройкой.&lt;br /&gt;
&lt;br /&gt;
=== Стратегии поиска ===&lt;br /&gt;
&lt;br /&gt;
''' Последовательный поиск '''&lt;br /&gt;
&lt;br /&gt;
При попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt;i+1, i+2, i+3&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку. В неё и запишем элемент.&lt;br /&gt;
&lt;br /&gt;
[[Файл:hashtables1.png|400px|Последовательный поиск, частный случай линейного поиска.]]&lt;br /&gt;
&lt;br /&gt;
''' Линейный поиск '''&lt;br /&gt;
&lt;br /&gt;
Выбираем шаг &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;. При попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt;i+(1 \cdot q), i+(2 \cdot q), i+(3 \cdot q)&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку. В неё и запишем элемент.&lt;br /&gt;
По сути последовательный поиск - частный случай линейного, где &amp;lt;tex&amp;gt;q=1&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Hashtables56.PNG|400px|Линейный поиск с шагом q.]]&lt;br /&gt;
&lt;br /&gt;
''' Квадратичный поиск '''&lt;br /&gt;
&lt;br /&gt;
Шаг &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; не фиксирован, а изменяется квадратично: &amp;lt;tex&amp;gt;q = 1,4,9,16...&amp;lt;/tex&amp;gt;. Соответственно при попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt; i+1, i+4, i+9&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку.&lt;br /&gt;
&lt;br /&gt;
[[Файл:hashtables3.png|400px|Квадратичный поиск.]]&lt;br /&gt;
&lt;br /&gt;
=== Проверка наличия элемента в таблице===&lt;br /&gt;
&lt;br /&gt;
Проверка осуществляется аналогично добавлению: мы проверяем ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; и другие, в соответствии с выбранной стратегией, пока не найдём искомый элемент или свободную ячейку.&lt;br /&gt;
&lt;br /&gt;
При поиске элемента может получится так, что мы дойдём до конца таблицы. Обычно поиск продолжается, начиная с другого конца, пока мы не придём в ту ячейку, откуда начинался поиск.&lt;br /&gt;
&lt;br /&gt;
=== Проблемы данных стратегий ===&lt;br /&gt;
&lt;br /&gt;
Проблем две — крайне нетривиальное удаление элемента из таблицы и образование кластеров  — последовательностей занятых ячеек.&lt;br /&gt;
&lt;br /&gt;
Кластеризация замедляет все операции с хеш-таблицей: при добавлении требуется перебирать всё больше элементов, при проверке тоже. Чем больше в таблице элементов, тем больше в ней кластеры и тем выше вероятность того, что добавляемый элемент попадёт в кластер.&lt;br /&gt;
Для защиты от кластеризации используется двойное хеширование и [[Хеширование кукушки|хеширование кукушки]].&lt;br /&gt;
&lt;br /&gt;
=== Удаление элемента без пометок ===&lt;br /&gt;
&lt;br /&gt;
Рассуждение будет описывать случай с линейным поиском хеша. Будем при удалении элемента сдвигать всё последующие на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; позиций назад. При этом:&lt;br /&gt;
* если в цепочке встречается элемент с другим хешем, то он должен остаться на своём месте (такая ситуация может возникнуть если оставшаяся часть цепочки была добавлена позже этого элемента)&lt;br /&gt;
* в цепочке не должно оставаться &amp;quot;дырок&amp;quot;, тогда любой элемент с данным хешем будет доступен из начала цепи&lt;br /&gt;
&lt;br /&gt;
Учитывая это будем действовать следующим образом: при поиске следующего элемента цепочки будем пропускать все ячейки с другим значением хеша, первый найденный элемент копировать в текущую ячейку, и затем рекурсивно его удалять. Если такой следующей ячейки нет, то текущий элемент можно просто удалить, сторонние цепочки при этом не разрушатся (чего нельзя сказать про случай квадратичного поиска).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
''' Псевдокод '''&lt;br /&gt;
&lt;br /&gt;
 '''function''' delete('''Item''' i) &lt;br /&gt;
      j = i + q&lt;br /&gt;
      '''while''' table[j] == '''null''' '''or''' table[j].key != table[i].key&lt;br /&gt;
         '''if''' table[j] == '''null'''&lt;br /&gt;
            table[i] = '''null'''&lt;br /&gt;
            '''return'''&lt;br /&gt;
         j += q&lt;br /&gt;
      table[i] = table[j]&lt;br /&gt;
      delete(j);    &lt;br /&gt;
&lt;br /&gt;
Хеш-таблицу считаем зацикленной&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Утверждение&lt;br /&gt;
|about=о времени работы&lt;br /&gt;
|statement=Асимптотически время работы &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\mathrm{find}&amp;lt;/tex&amp;gt; совпадают&lt;br /&gt;
|proof=&lt;br /&gt;
Заметим что указатель &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; в каждой итерации перемещается вперёд на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; (с учётом рекурсивных вызовов &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt;). То есть этот алгоритм последовательно пройдёт по цепочке от удаляемого элемента до последнего {{---}} с учётом вызова &amp;lt;tex&amp;gt;\mathrm{find}&amp;lt;/tex&amp;gt; собственно для нахождения удаляемого элемента, мы посетим все ячейки цепи.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Вариант с зацикливанием мы не рассматриваем, поскольку если &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; взаимнопросто с размером хеш-таблицы, то для зацикливания в ней вообще не должно быть свободных позиций&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Теперь докажем почему этот алгоритм работает. Собственно нам требуется сохранение трёх условий.&lt;br /&gt;
* В редактируемой цепи не остаётся дырок&lt;br /&gt;
Докажем по индукции. Если на данной итерации мы просто удаляем элемент (база), то после него ничего нет, всё верно. Если же нет, то вызванный в конце &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt; (см. псевдокод) заметёт созданную дыру (скопированный элемент), и сам, по предположению, новых не создаст.&lt;br /&gt;
* Элементы, которые уже на своих местах, не должны быть сдвинуты.&lt;br /&gt;
Это учтено.&lt;br /&gt;
* В других цепочках не появятся дыры&lt;br /&gt;
Противное возможно только в том случае, если какой-то элемент был действительно удалён. Удаляем мы только последнюю ячейку в цепи, и если бы на её месте возникла дыра для сторонней цепочки, это бы означало что элемент, стоящий на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; позиций назад, одновременно принадлежал нашей и другой цепочкам, что невозможно.&lt;br /&gt;
&lt;br /&gt;
==Двойное хеширование==&lt;br /&gt;
'''Двойное хеширование''' (англ. double hashing) {{---}} метод борьбы с коллизиями, возникающими при открытой адресации, основанный на использовании двух хеш-функций для построения различных последовательностей исследования хеш-таблицы.&lt;br /&gt;
&lt;br /&gt;
===Принцип двойного хеширования===&lt;br /&gt;
При двойном хешировании используются две независимые хеш-функции &amp;lt;tex&amp;gt; h_1(k) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; h_2(k) &amp;lt;/tex&amp;gt;. Пусть &amp;lt;tex&amp;gt; k &amp;lt;/tex&amp;gt; {{---}} это наш ключ, &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt; {{---}} размер нашей таблицы, &amp;lt;tex&amp;gt;n \bmod m &amp;lt;/tex&amp;gt; {{---}} остаток от деления &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt; на &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, тогда сначала исследуется ячейка с адресом &amp;lt;tex&amp;gt; h_1(k) &amp;lt;/tex&amp;gt;, если она уже занята, то рассматривается &amp;lt;tex&amp;gt; (h_1(k) +  h_2(k)) \bmod m &amp;lt;/tex&amp;gt;, затем &amp;lt;tex&amp;gt; (h_1(k) +  2 \cdot h_2(k)) \bmod m &amp;lt;/tex&amp;gt; и так далее. В общем случае идёт проверка последовательности ячеек &amp;lt;tex&amp;gt; (h_1(k) +  i \cdot h_2(k)) \bmod m &amp;lt;/tex&amp;gt; где &amp;lt;tex&amp;gt;  i = (0, 1, \; ... \;,  m - 1) &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, операции вставки, удаления и поиска в лучшем случае выполняются за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;, в худшем {{---}} за &amp;lt;tex&amp;gt;O(m)&amp;lt;/tex&amp;gt;, что не отличается от обычного [[Открытое_и_закрытое_хеширование#Линейное разрешение коллизий|линейного разрешения коллизий]].&lt;br /&gt;
Однако в среднем, при грамотном выборе хеш-функций, двойное хеширование будет выдавать лучшие результаты, за счёт того, что вероятность совпадения значений сразу двух независимых хеш-функций ниже, чем одной.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt;\forall x \neq y \; \exists h_1,h_2 : p(h_1(x)=h_1(y))&amp;gt; p((h_1(x)=h_1(y)) \land (h_2(x)=h_2(y)))&amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Выбор хеш-функций===&lt;br /&gt;
&amp;lt;tex&amp;gt; h_1 &amp;lt;/tex&amp;gt; может быть обычной хеш-функцией. Однако чтобы последовательность исследования могла охватить всю таблицу, &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; должна возвращать значения:&lt;br /&gt;
*не равные &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
*независимые  от &amp;lt;tex&amp;gt; h_1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
*взаимно простые с величиной хеш-таблицы&lt;br /&gt;
&lt;br /&gt;
Есть два удобных способа это сделать. Первый состоит в том, что в качестве размера таблицы используется простое число, а &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; возвращает натуральные числа, меньшие &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;. Второй {{---}} размер таблицы является степенью двойки, а &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; возвращает нечетные значения.&lt;br /&gt;
&lt;br /&gt;
Например, если размер таблицы равен &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, то в качестве &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; можно использовать функцию вида &amp;lt;tex&amp;gt; h_2(k) = k \bmod (m-1) + 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Файл: Вставка при двойном хэшировании.svg.jpeg|thumb|right|Вставка при двойном хешировании]]&lt;br /&gt;
&lt;br /&gt;
===Пример===&lt;br /&gt;
&lt;br /&gt;
Показана хеш-таблица размером 13 ячеек, в которой используются вспомогательные функции:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h(k,i) = (h_1(k) + i \cdot h_2(k)) \bmod 13 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h_1(k) = k \bmod 13 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h_2(k) = 1 + k \bmod 11 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Мы хотим вставить ключ 14. Изначально &amp;lt;tex&amp;gt; i = 0 &amp;lt;/tex&amp;gt;. Тогда &amp;lt;tex&amp;gt; h(14,0) = (h_1(14) + 0\cdot h_2(14)) \bmod 13 = 1 &amp;lt;/tex&amp;gt;. Но ячейка с индексом 1 занята, поэтому увеличиваем &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; на 1 и пересчитываем значение хеш-функции. Делаем так, пока не дойдем до пустой ячейки. При &amp;lt;tex&amp;gt; i = 2 &amp;lt;/tex&amp;gt; получаем &amp;lt;tex&amp;gt; h(14,2) = (h_1(14) + 2\cdot h_2(14)) \bmod 13 = 9 &amp;lt;/tex&amp;gt;. Ячейка с номером 9 свободна, значит записываем туда наш ключ.&lt;br /&gt;
&lt;br /&gt;
Таким образом, основная особенность двойного хеширования состоит в том, что при различных &amp;lt;tex&amp;gt; k &amp;lt;/tex&amp;gt; пара &amp;lt;tex&amp;gt; (h_1(k),h_2(k)) &amp;lt;/tex&amp;gt; дает различные последовательности ячеек для исследования.&lt;br /&gt;
&lt;br /&gt;
===Простая реализация===&lt;br /&gt;
Пусть у нас есть некоторый объект &amp;lt;tex&amp;gt; item &amp;lt;/tex&amp;gt;, в котором определено поле &amp;lt;tex&amp;gt; key &amp;lt;/tex&amp;gt;, от которого можно вычислить хеш-функции &amp;lt;tex&amp;gt; h_1(key)&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; h_2(key) &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Так же у нас есть таблица &amp;lt;tex&amp;gt; table &amp;lt;/tex&amp;gt; величиной &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, состоящая из объектов типа &amp;lt;tex&amp;gt; item &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
'''Вставка'''&lt;br /&gt;
 '''function''' add('''Item''' item)&lt;br /&gt;
      x = h1(item.key)&lt;br /&gt;
      y = h2(item.key)&lt;br /&gt;
         '''for''' (i = 0..m)    	&lt;br /&gt;
            '''if''' table[x] == '''null'''&lt;br /&gt;
               table[x] = item&lt;br /&gt;
            '''return'''      &lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      table.resize()&amp;lt;span style=&amp;quot;color:Green&amp;quot;&amp;gt;// ошибка, требуется увеличить размер таблицы&lt;br /&gt;
&lt;br /&gt;
'''Поиск'''&lt;br /&gt;
 '''Item''' search('''Item''' key)&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m)&lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key&lt;br /&gt;
               '''return''' table[x]&lt;br /&gt;
            '''else'''&lt;br /&gt;
               '''return''' '''null'''&lt;br /&gt;
      x = (x + y) '''mod''' m   &lt;br /&gt;
      '''return''' '''null'''&lt;br /&gt;
&lt;br /&gt;
===Реализация с удалением===&lt;br /&gt;
Что бы наша хеш-таблица поддерживала удаление, требуется добавить массив &amp;lt;tex&amp;gt;deleted&amp;lt;/tex&amp;gt; типов &amp;lt;tex&amp;gt;bool&amp;lt;/tex&amp;gt;, равный по величине массиву &amp;lt;tex&amp;gt;table&amp;lt;/tex&amp;gt;. Теперь при удалении мы просто будем помечать наш объект ''как удалённый'', а при добавлении как ''не удалённый'' и замещать новым добавляемым объектом. При поиске, помимо равенства ключей, мы смотрим, удалён ли элемент, если да, то идём дальше.&lt;br /&gt;
&lt;br /&gt;
'''Вставка'''&lt;br /&gt;
 '''function''' add('''Item''' item)&lt;br /&gt;
      x = h1(item.key)&lt;br /&gt;
      y = h2(item.key)&lt;br /&gt;
      '''for''' (i = 0..m)   	&lt;br /&gt;
         '''if''' table[x] == '''null''' '''or''' deleted[x]&lt;br /&gt;
            table[x] = item&lt;br /&gt;
            deleted[x] = '''false'''&lt;br /&gt;
            '''return'''      &lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      table.resize()&amp;lt;span style=&amp;quot;color:Green&amp;quot;&amp;gt;// ошибка, требуется увеличить размер таблицы&lt;br /&gt;
'''Поиск'''&lt;br /&gt;
 '''Item''' search('''Item''' key)&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m) &lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key '''and''' !deleted[x]&lt;br /&gt;
               '''return''' table[x]&lt;br /&gt;
         '''else'''&lt;br /&gt;
            '''return''' '''null'''&lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      '''return''' '''null'''&lt;br /&gt;
&lt;br /&gt;
'''Удаление'''&lt;br /&gt;
 '''function''' remove('''Item''' key)&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m)&lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key&lt;br /&gt;
               deleted[x] = '''true'''&lt;br /&gt;
         '''else''' &lt;br /&gt;
            '''return'''&lt;br /&gt;
      x = (x + y) '''mod''' m&lt;br /&gt;
&lt;br /&gt;
== Разрешение коллизий в Java 8==&lt;br /&gt;
В Java 8 для разрешения коллизий используется модифицированный метод цепочек. Суть его заключается в том, что когда количество элементов в корзине превышает определенное значение, данный бакет переходит от использования связного списка к использованию сбалансированного дерева. Такой подход позволяет улучшить производительность с &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;O(log(n))&amp;lt;/tex&amp;gt;.  Данный способ используется в таких коллекциях как HashMap, LinkedHashMap и ConcurrentHashMap.&lt;br /&gt;
[[Файл:Hashing_in_Java8.png|400px|Хеширование в Java 8.]]&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
* [[Хеширование]]&lt;br /&gt;
* [[Хеширование_кукушки|Хеширование кукушки]]&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
* Бакнелл Дж. М. «Фундаментальные алгоритмы и структуры данных в Delphi», 2003&lt;br /&gt;
* Кормен, Томас Х., Лейзерсон, Чарльз И., Ривест, Рональд Л., Штайн Клиффорд «Алгоритмы: построение и анализ», 2-е издание. Пер. с англ. — М.:Издательский дом &amp;quot;Вильямс&amp;quot;, 2010.— Парал. тит. англ. — ISBN 978-5-8459-0857-5 (рус.)&lt;br /&gt;
* Дональд Кнут. «Искусство программирования, том 3. Сортировка и поиск» {{---}} «Вильямс», 2007 г.{{---}} ISBN 0-201-89685-0&lt;br /&gt;
* Седжвик Р. «Фундаментальные алгоритмы на C. Части 1-4. Анализ. Структуры данных. Сортировка. Поиск», 2003&lt;br /&gt;
&lt;br /&gt;
==Ссылки==&lt;br /&gt;
* [http://openjdk.java.net/jeps/180 Handle Frequent HashMap Collisions with Balanced Trees]&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Double_hashing Wikipedia {{---}} Double_hashing]&lt;br /&gt;
* [http://ru.wikipedia.org/wiki/%D0%A5%D0%B5%D1%88-%D1%82%D0%B0%D0%B1%D0%BB%D0%B8%D1%86%D0%B0 Разрешение коллизий]&lt;br /&gt;
* [http://rain.ifmo.ru/cat/view.php/vis/hashtables/hash-2001-2 Пример хеш таблицы]&lt;br /&gt;
* [http://research.cs.vt.edu/AVresearch/hashing/double.php Пример хеш таблицы с двойным хешированием]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория: Хеширование]]&lt;/div&gt;</summary>
		<author><name>Kozichuk</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%A0%D0%B0%D0%B7%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BA%D0%BE%D0%BB%D0%BB%D0%B8%D0%B7%D0%B8%D0%B9&amp;diff=47147</id>
		<title>Разрешение коллизий</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A0%D0%B0%D0%B7%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BA%D0%BE%D0%BB%D0%BB%D0%B8%D0%B7%D0%B8%D0%B9&amp;diff=47147"/>
				<updated>2015-05-31T21:08:36Z</updated>
		
		<summary type="html">&lt;p&gt;Kozichuk: /* Разрешение коллизий в Java 8 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Разрешение [[Хеш-таблица|коллизий]]''' (англ. collision resolution) в [[Хеш-таблица|хеш-таблице]], задача, решаемая несколькими способами. Например, при помощи метода цепочке или открытой адресации. Коллизии замедляют время работы с хеш-таблицой, поэтому с ними нужно бороться.&lt;br /&gt;
&lt;br /&gt;
== Разрешение коллизий с помощью цепочек ==&lt;br /&gt;
[[Файл:open_hash.png|thumb|380px|right|Разрешение коллизий при помощи цепочек.]]&lt;br /&gt;
Каждая ячейка &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; массива &amp;lt;tex&amp;gt;H&amp;lt;/tex&amp;gt; содержит указатель на начало [[Список|списка]] всех элементов, хеш-код которых равен &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, либо указывает на их отсутствие. Коллизии приводят к тому, что появляются списки размером больше одного элемента.&lt;br /&gt;
&lt;br /&gt;
Время, необходимое для вставки в наихудшем случае равно &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;. Это операция выполняет быстро, так как считается, что вставляемый элемент отсутствует в таблице, но если потребуется, то перед вставкой мы можем выполнить поиск этого элемента.&lt;br /&gt;
&lt;br /&gt;
Время работы поиска в наихудшем случае пропорционально длине списка, а если все &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; ключей захешировались в одну и ту же ячейку (создав список длиной &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;) время поиска будет равно &amp;lt;tex&amp;gt;\Theta(n)&amp;lt;/tex&amp;gt; плюс время вычисления хеш-функции, что ничуть не лучше, чем использование связного списка для хранения всех &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; элементов.&lt;br /&gt;
&lt;br /&gt;
Удаления элемента может быть выполнено за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;, как и вставка, при использовании двухсвязного списка.&lt;br /&gt;
&lt;br /&gt;
== Линейное разрешение коллизий ==&lt;br /&gt;
[[Файл:close_hash.png|thumb|380px|right|Пример хеш-таблицы с открытой адресацией и линейным пробированием.]]&lt;br /&gt;
Все элементы хранятся непосредственно в хеш-таблице, без использования связных списков. В отличии от хеширования с цепочками, при использовании этого метода может возникнуть ситуация, когда хеш-таблица окажется полностью заполненной, следовательно будет невозможно добавлять в неё новые элементы. Так что при возникновении такой ситуации решением может быть динамическое увеличение размера хеш-таблицы, с одновременной её перестройкой.&lt;br /&gt;
&lt;br /&gt;
=== Стратегии поиска ===&lt;br /&gt;
&lt;br /&gt;
''' Последовательный поиск '''&lt;br /&gt;
&lt;br /&gt;
При попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt;i+1, i+2, i+3&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку. В неё и запишем элемент.&lt;br /&gt;
&lt;br /&gt;
[[Файл:hashtables1.png|400px|Последовательный поиск, частный случай линейного поиска.]]&lt;br /&gt;
&lt;br /&gt;
''' Линейный поиск '''&lt;br /&gt;
&lt;br /&gt;
Выбираем шаг &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;. При попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt;i+(1 \cdot q), i+(2 \cdot q), i+(3 \cdot q)&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку. В неё и запишем элемент.&lt;br /&gt;
По сути последовательный поиск - частный случай линейного, где &amp;lt;tex&amp;gt;q=1&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Hashtables56.PNG|400px|Линейный поиск с шагом q.]]&lt;br /&gt;
&lt;br /&gt;
''' Квадратичный поиск '''&lt;br /&gt;
&lt;br /&gt;
Шаг &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; не фиксирован, а изменяется квадратично: &amp;lt;tex&amp;gt;q = 1,4,9,16...&amp;lt;/tex&amp;gt;. Соответственно при попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt; i+1, i+4, i+9&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку.&lt;br /&gt;
&lt;br /&gt;
[[Файл:hashtables3.png|400px|Квадратичный поиск.]]&lt;br /&gt;
&lt;br /&gt;
=== Проверка наличия элемента в таблице===&lt;br /&gt;
&lt;br /&gt;
Проверка осуществляется аналогично добавлению: мы проверяем ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; и другие, в соответствии с выбранной стратегией, пока не найдём искомый элемент или свободную ячейку.&lt;br /&gt;
&lt;br /&gt;
При поиске элемента может получится так, что мы дойдём до конца таблицы. Обычно поиск продолжается, начиная с другого конца, пока мы не придём в ту ячейку, откуда начинался поиск.&lt;br /&gt;
&lt;br /&gt;
=== Проблемы данных стратегий ===&lt;br /&gt;
&lt;br /&gt;
Проблем две — крайне нетривиальное удаление элемента из таблицы и образование кластеров  — последовательностей занятых ячеек.&lt;br /&gt;
&lt;br /&gt;
Кластеризация замедляет все операции с хеш-таблицей: при добавлении требуется перебирать всё больше элементов, при проверке тоже. Чем больше в таблице элементов, тем больше в ней кластеры и тем выше вероятность того, что добавляемый элемент попадёт в кластер.&lt;br /&gt;
Для защиты от кластеризации используется двойное хеширование и [[Хеширование кукушки|хеширование кукушки]].&lt;br /&gt;
&lt;br /&gt;
=== Удаление элемента без пометок ===&lt;br /&gt;
&lt;br /&gt;
Рассуждение будет описывать случай с линейным поиском хеша. Будем при удалении элемента сдвигать всё последующие на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; позиций назад. При этом:&lt;br /&gt;
* если в цепочке встречается элемент с другим хешем, то он должен остаться на своём месте (такая ситуация может возникнуть если оставшаяся часть цепочки была добавлена позже этого элемента)&lt;br /&gt;
* в цепочке не должно оставаться &amp;quot;дырок&amp;quot;, тогда любой элемент с данным хешем будет доступен из начала цепи&lt;br /&gt;
&lt;br /&gt;
Учитывая это будем действовать следующим образом: при поиске следующего элемента цепочки будем пропускать все ячейки с другим значением хеша, первый найденный элемент копировать в текущую ячейку, и затем рекурсивно его удалять. Если такой следующей ячейки нет, то текущий элемент можно просто удалить, сторонние цепочки при этом не разрушатся (чего нельзя сказать про случай квадратичного поиска).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
''' Псевдокод '''&lt;br /&gt;
&lt;br /&gt;
 '''function''' delete('''Item''' i) &lt;br /&gt;
      j = i + q&lt;br /&gt;
      '''while''' table[j] == '''null''' '''or''' table[j].key != table[i].key&lt;br /&gt;
         '''if''' table[j] == '''null'''&lt;br /&gt;
            table[i] = '''null'''&lt;br /&gt;
            '''return'''&lt;br /&gt;
         j += q&lt;br /&gt;
      table[i] = table[j]&lt;br /&gt;
      delete(j);    &lt;br /&gt;
&lt;br /&gt;
Хеш-таблицу считаем зацикленной&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Утверждение&lt;br /&gt;
|about=о времени работы&lt;br /&gt;
|statement=Асимптотически время работы &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\mathrm{find}&amp;lt;/tex&amp;gt; совпадают&lt;br /&gt;
|proof=&lt;br /&gt;
Заметим что указатель &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; в каждой итерации перемещается вперёд на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; (с учётом рекурсивных вызовов &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt;). То есть этот алгоритм последовательно пройдёт по цепочке от удаляемого элемента до последнего {{---}} с учётом вызова &amp;lt;tex&amp;gt;\mathrm{find}&amp;lt;/tex&amp;gt; собственно для нахождения удаляемого элемента, мы посетим все ячейки цепи.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Вариант с зацикливанием мы не рассматриваем, поскольку если &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; взаимнопросто с размером хеш-таблицы, то для зацикливания в ней вообще не должно быть свободных позиций&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Теперь докажем почему этот алгоритм работает. Собственно нам требуется сохранение трёх условий.&lt;br /&gt;
* В редактируемой цепи не остаётся дырок&lt;br /&gt;
Докажем по индукции. Если на данной итерации мы просто удаляем элемент (база), то после него ничего нет, всё верно. Если же нет, то вызванный в конце &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt; (см. псевдокод) заметёт созданную дыру (скопированный элемент), и сам, по предположению, новых не создаст.&lt;br /&gt;
* Элементы, которые уже на своих местах, не должны быть сдвинуты.&lt;br /&gt;
Это учтено.&lt;br /&gt;
* В других цепочках не появятся дыры&lt;br /&gt;
Противное возможно только в том случае, если какой-то элемент был действительно удалён. Удаляем мы только последнюю ячейку в цепи, и если бы на её месте возникла дыра для сторонней цепочки, это бы означало что элемент, стоящий на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; позиций назад, одновременно принадлежал нашей и другой цепочкам, что невозможно.&lt;br /&gt;
&lt;br /&gt;
==Двойное хеширование==&lt;br /&gt;
'''Двойное хеширование''' (англ. double hashing) {{---}} метод борьбы с коллизиями, возникающими при открытой адресации, основанный на использовании двух хеш-функций для построения различных последовательностей исследования хеш-таблицы.&lt;br /&gt;
&lt;br /&gt;
===Принцип двойного хеширования===&lt;br /&gt;
При двойном хешировании используются две независимые хеш-функции &amp;lt;tex&amp;gt; h_1(k) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; h_2(k) &amp;lt;/tex&amp;gt;. Пусть &amp;lt;tex&amp;gt; k &amp;lt;/tex&amp;gt; {{---}} это наш ключ, &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt; {{---}} размер нашей таблицы, &amp;lt;tex&amp;gt;n \bmod m &amp;lt;/tex&amp;gt; {{---}} остаток от деления &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt; на &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, тогда сначала исследуется ячейка с адресом &amp;lt;tex&amp;gt; h_1(k) &amp;lt;/tex&amp;gt;, если она уже занята, то рассматривается &amp;lt;tex&amp;gt; (h_1(k) +  h_2(k)) \bmod m &amp;lt;/tex&amp;gt;, затем &amp;lt;tex&amp;gt; (h_1(k) +  2 \cdot h_2(k)) \bmod m &amp;lt;/tex&amp;gt; и так далее. В общем случае идёт проверка последовательности ячеек &amp;lt;tex&amp;gt; (h_1(k) +  i \cdot h_2(k)) \bmod m &amp;lt;/tex&amp;gt; где &amp;lt;tex&amp;gt;  i = (0, 1, \; ... \;,  m - 1) &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, операции вставки, удаления и поиска в лучшем случае выполняются за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;, в худшем {{---}} за &amp;lt;tex&amp;gt;O(m)&amp;lt;/tex&amp;gt;, что не отличается от обычного [[Открытое_и_закрытое_хеширование#Линейное разрешение коллизий|линейного разрешения коллизий]].&lt;br /&gt;
Однако в среднем, при грамотном выборе хеш-функций, двойное хеширование будет выдавать лучшие результаты, за счёт того, что вероятность совпадения значений сразу двух независимых хеш-функций ниже, чем одной.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt;\forall x \neq y \; \exists h_1,h_2 : p(h_1(x)=h_1(y))&amp;gt; p((h_1(x)=h_1(y)) \land (h_2(x)=h_2(y)))&amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Выбор хеш-функций===&lt;br /&gt;
&amp;lt;tex&amp;gt; h_1 &amp;lt;/tex&amp;gt; может быть обычной хеш-функцией. Однако чтобы последовательность исследования могла охватить всю таблицу, &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; должна возвращать значения:&lt;br /&gt;
*не равные &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
*независимые  от &amp;lt;tex&amp;gt; h_1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
*взаимно простые с величиной хеш-таблицы&lt;br /&gt;
&lt;br /&gt;
Есть два удобных способа это сделать. Первый состоит в том, что в качестве размера таблицы используется простое число, а &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; возвращает натуральные числа, меньшие &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;. Второй {{---}} размер таблицы является степенью двойки, а &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; возвращает нечетные значения.&lt;br /&gt;
&lt;br /&gt;
Например, если размер таблицы равен &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, то в качестве &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; можно использовать функцию вида &amp;lt;tex&amp;gt; h_2(k) = k \bmod (m-1) + 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Файл: Вставка при двойном хэшировании.svg.jpeg|thumb|right|Вставка при двойном хешировании]]&lt;br /&gt;
&lt;br /&gt;
===Пример===&lt;br /&gt;
&lt;br /&gt;
Показана хеш-таблица размером 13 ячеек, в которой используются вспомогательные функции:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h(k,i) = (h_1(k) + i \cdot h_2(k)) \bmod 13 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h_1(k) = k \bmod 13 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h_2(k) = 1 + k \bmod 11 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Мы хотим вставить ключ 14. Изначально &amp;lt;tex&amp;gt; i = 0 &amp;lt;/tex&amp;gt;. Тогда &amp;lt;tex&amp;gt; h(14,0) = (h_1(14) + 0\cdot h_2(14)) \bmod 13 = 1 &amp;lt;/tex&amp;gt;. Но ячейка с индексом 1 занята, поэтому увеличиваем &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; на 1 и пересчитываем значение хеш-функции. Делаем так, пока не дойдем до пустой ячейки. При &amp;lt;tex&amp;gt; i = 2 &amp;lt;/tex&amp;gt; получаем &amp;lt;tex&amp;gt; h(14,2) = (h_1(14) + 2\cdot h_2(14)) \bmod 13 = 9 &amp;lt;/tex&amp;gt;. Ячейка с номером 9 свободна, значит записываем туда наш ключ.&lt;br /&gt;
&lt;br /&gt;
Таким образом, основная особенность двойного хеширования состоит в том, что при различных &amp;lt;tex&amp;gt; k &amp;lt;/tex&amp;gt; пара &amp;lt;tex&amp;gt; (h_1(k),h_2(k)) &amp;lt;/tex&amp;gt; дает различные последовательности ячеек для исследования.&lt;br /&gt;
&lt;br /&gt;
===Простая реализация===&lt;br /&gt;
Пусть у нас есть некоторый объект &amp;lt;tex&amp;gt; item &amp;lt;/tex&amp;gt;, в котором определено поле &amp;lt;tex&amp;gt; key &amp;lt;/tex&amp;gt;, от которого можно вычислить хеш-функции &amp;lt;tex&amp;gt; h_1(key)&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; h_2(key) &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Так же у нас есть таблица &amp;lt;tex&amp;gt; table &amp;lt;/tex&amp;gt; величиной &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, состоящая из объектов типа &amp;lt;tex&amp;gt; item &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
'''Вставка'''&lt;br /&gt;
 '''function''' add('''Item''' item)&lt;br /&gt;
      x = h1(item.key)&lt;br /&gt;
      y = h2(item.key)&lt;br /&gt;
         '''for''' (i = 0..m)    	&lt;br /&gt;
            '''if''' table[x] == '''null'''&lt;br /&gt;
               table[x] = item&lt;br /&gt;
            '''return'''      &lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      table.resize()&amp;lt;span style=&amp;quot;color:Green&amp;quot;&amp;gt;// ошибка, требуется увеличить размер таблицы&lt;br /&gt;
&lt;br /&gt;
'''Поиск'''&lt;br /&gt;
 '''Item''' search('''Item''' key)&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m)&lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key&lt;br /&gt;
               '''return''' table[x]&lt;br /&gt;
            '''else'''&lt;br /&gt;
               '''return''' '''null'''&lt;br /&gt;
      x = (x + y) '''mod''' m   &lt;br /&gt;
      '''return''' '''null'''&lt;br /&gt;
&lt;br /&gt;
===Реализация с удалением===&lt;br /&gt;
Что бы наша хеш-таблица поддерживала удаление, требуется добавить массив &amp;lt;tex&amp;gt;deleted&amp;lt;/tex&amp;gt; типов &amp;lt;tex&amp;gt;bool&amp;lt;/tex&amp;gt;, равный по величине массиву &amp;lt;tex&amp;gt;table&amp;lt;/tex&amp;gt;. Теперь при удалении мы просто будем помечать наш объект ''как удалённый'', а при добавлении как ''не удалённый'' и замещать новым добавляемым объектом. При поиске, помимо равенства ключей, мы смотрим, удалён ли элемент, если да, то идём дальше.&lt;br /&gt;
&lt;br /&gt;
'''Вставка'''&lt;br /&gt;
 '''function''' add('''Item''' item)&lt;br /&gt;
      x = h1(item.key)&lt;br /&gt;
      y = h2(item.key)&lt;br /&gt;
      '''for''' (i = 0..m)   	&lt;br /&gt;
         '''if''' table[x] == '''null''' '''or''' deleted[x]&lt;br /&gt;
            table[x] = item&lt;br /&gt;
            deleted[x] = '''false'''&lt;br /&gt;
            '''return'''      &lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      table.resize()&amp;lt;span style=&amp;quot;color:Green&amp;quot;&amp;gt;// ошибка, требуется увеличить размер таблицы&lt;br /&gt;
'''Поиск'''&lt;br /&gt;
 '''Item''' search('''Item''' key)&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m) &lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key '''and''' !deleted[x]&lt;br /&gt;
               '''return''' table[x]&lt;br /&gt;
         '''else'''&lt;br /&gt;
            '''return''' '''null'''&lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      '''return''' '''null'''&lt;br /&gt;
&lt;br /&gt;
'''Удаление'''&lt;br /&gt;
 '''function''' remove('''Item''' key)&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m)&lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key&lt;br /&gt;
               deleted[x] = '''true'''&lt;br /&gt;
         '''else''' &lt;br /&gt;
            '''return'''&lt;br /&gt;
      x = (x + y) '''mod''' m&lt;br /&gt;
&lt;br /&gt;
== Разрешение коллизий в Java 8==&lt;br /&gt;
В Java 8 для разрешения коллизий используется модифицированный метод цепочек. Суть его заключается в том, что когда количество элементов в корзине превышает определенное значение, данный бакет переходит от использования связного списка к использованию сбалансированного дерева. Такой подход позволяет улучшить производительность с &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;O(log(n))&amp;lt;/tex&amp;gt;.  Данный способ используется в таких коллекциях как HashMap, LinkedHashMap и ConcurrentHashMap.&lt;br /&gt;
[[Файл:Hashing_in_Java8.png|thumb|left|400px|Хеширование в Java 8.]]&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
* [[Хеширование]]&lt;br /&gt;
* [[Хеширование_кукушки|Хеширование кукушки]]&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
* Бакнелл Дж. М. «Фундаментальные алгоритмы и структуры данных в Delphi», 2003&lt;br /&gt;
* Кормен, Томас Х., Лейзерсон, Чарльз И., Ривест, Рональд Л., Штайн Клиффорд «Алгоритмы: построение и анализ», 2-е издание. Пер. с англ. — М.:Издательский дом &amp;quot;Вильямс&amp;quot;, 2010.— Парал. тит. англ. — ISBN 978-5-8459-0857-5 (рус.)&lt;br /&gt;
* Дональд Кнут. «Искусство программирования, том 3. Сортировка и поиск» {{---}} «Вильямс», 2007 г.{{---}} ISBN 0-201-89685-0&lt;br /&gt;
* Седжвик Р. «Фундаментальные алгоритмы на C. Части 1-4. Анализ. Структуры данных. Сортировка. Поиск», 2003&lt;br /&gt;
&lt;br /&gt;
==Ссылки==&lt;br /&gt;
* [http://openjdk.java.net/jeps/180 Handle Frequent HashMap Collisions with Balanced Trees]&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Double_hashing Wikipedia {{---}} Double_hashing]&lt;br /&gt;
* [http://ru.wikipedia.org/wiki/%D0%A5%D0%B5%D1%88-%D1%82%D0%B0%D0%B1%D0%BB%D0%B8%D1%86%D0%B0 Разрешение коллизий]&lt;br /&gt;
* [http://rain.ifmo.ru/cat/view.php/vis/hashtables/hash-2001-2 Пример хеш таблицы]&lt;br /&gt;
* [http://research.cs.vt.edu/AVresearch/hashing/double.php Пример хеш таблицы с двойным хешированием]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория: Хеширование]]&lt;/div&gt;</summary>
		<author><name>Kozichuk</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%A0%D0%B0%D0%B7%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BA%D0%BE%D0%BB%D0%BB%D0%B8%D0%B7%D0%B8%D0%B9&amp;diff=47146</id>
		<title>Разрешение коллизий</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A0%D0%B0%D0%B7%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BA%D0%BE%D0%BB%D0%BB%D0%B8%D0%B7%D0%B8%D0%B9&amp;diff=47146"/>
				<updated>2015-05-31T21:02:34Z</updated>
		
		<summary type="html">&lt;p&gt;Kozichuk: /* Удаление элемента без пометок */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Разрешение [[Хеш-таблица|коллизий]]''' (англ. collision resolution) в [[Хеш-таблица|хеш-таблице]], задача, решаемая несколькими способами. Например, при помощи метода цепочке или открытой адресации. Коллизии замедляют время работы с хеш-таблицой, поэтому с ними нужно бороться.&lt;br /&gt;
&lt;br /&gt;
== Разрешение коллизий с помощью цепочек ==&lt;br /&gt;
[[Файл:open_hash.png|thumb|380px|right|Разрешение коллизий при помощи цепочек.]]&lt;br /&gt;
Каждая ячейка &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; массива &amp;lt;tex&amp;gt;H&amp;lt;/tex&amp;gt; содержит указатель на начало [[Список|списка]] всех элементов, хеш-код которых равен &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, либо указывает на их отсутствие. Коллизии приводят к тому, что появляются списки размером больше одного элемента.&lt;br /&gt;
&lt;br /&gt;
Время, необходимое для вставки в наихудшем случае равно &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;. Это операция выполняет быстро, так как считается, что вставляемый элемент отсутствует в таблице, но если потребуется, то перед вставкой мы можем выполнить поиск этого элемента.&lt;br /&gt;
&lt;br /&gt;
Время работы поиска в наихудшем случае пропорционально длине списка, а если все &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; ключей захешировались в одну и ту же ячейку (создав список длиной &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;) время поиска будет равно &amp;lt;tex&amp;gt;\Theta(n)&amp;lt;/tex&amp;gt; плюс время вычисления хеш-функции, что ничуть не лучше, чем использование связного списка для хранения всех &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; элементов.&lt;br /&gt;
&lt;br /&gt;
Удаления элемента может быть выполнено за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;, как и вставка, при использовании двухсвязного списка.&lt;br /&gt;
&lt;br /&gt;
== Линейное разрешение коллизий ==&lt;br /&gt;
[[Файл:close_hash.png|thumb|380px|right|Пример хеш-таблицы с открытой адресацией и линейным пробированием.]]&lt;br /&gt;
Все элементы хранятся непосредственно в хеш-таблице, без использования связных списков. В отличии от хеширования с цепочками, при использовании этого метода может возникнуть ситуация, когда хеш-таблица окажется полностью заполненной, следовательно будет невозможно добавлять в неё новые элементы. Так что при возникновении такой ситуации решением может быть динамическое увеличение размера хеш-таблицы, с одновременной её перестройкой.&lt;br /&gt;
&lt;br /&gt;
=== Стратегии поиска ===&lt;br /&gt;
&lt;br /&gt;
''' Последовательный поиск '''&lt;br /&gt;
&lt;br /&gt;
При попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt;i+1, i+2, i+3&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку. В неё и запишем элемент.&lt;br /&gt;
&lt;br /&gt;
[[Файл:hashtables1.png|400px|Последовательный поиск, частный случай линейного поиска.]]&lt;br /&gt;
&lt;br /&gt;
''' Линейный поиск '''&lt;br /&gt;
&lt;br /&gt;
Выбираем шаг &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;. При попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt;i+(1 \cdot q), i+(2 \cdot q), i+(3 \cdot q)&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку. В неё и запишем элемент.&lt;br /&gt;
По сути последовательный поиск - частный случай линейного, где &amp;lt;tex&amp;gt;q=1&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Hashtables56.PNG|400px|Линейный поиск с шагом q.]]&lt;br /&gt;
&lt;br /&gt;
''' Квадратичный поиск '''&lt;br /&gt;
&lt;br /&gt;
Шаг &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; не фиксирован, а изменяется квадратично: &amp;lt;tex&amp;gt;q = 1,4,9,16...&amp;lt;/tex&amp;gt;. Соответственно при попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt; i+1, i+4, i+9&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку.&lt;br /&gt;
&lt;br /&gt;
[[Файл:hashtables3.png|400px|Квадратичный поиск.]]&lt;br /&gt;
&lt;br /&gt;
=== Проверка наличия элемента в таблице===&lt;br /&gt;
&lt;br /&gt;
Проверка осуществляется аналогично добавлению: мы проверяем ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; и другие, в соответствии с выбранной стратегией, пока не найдём искомый элемент или свободную ячейку.&lt;br /&gt;
&lt;br /&gt;
При поиске элемента может получится так, что мы дойдём до конца таблицы. Обычно поиск продолжается, начиная с другого конца, пока мы не придём в ту ячейку, откуда начинался поиск.&lt;br /&gt;
&lt;br /&gt;
=== Проблемы данных стратегий ===&lt;br /&gt;
&lt;br /&gt;
Проблем две — крайне нетривиальное удаление элемента из таблицы и образование кластеров  — последовательностей занятых ячеек.&lt;br /&gt;
&lt;br /&gt;
Кластеризация замедляет все операции с хеш-таблицей: при добавлении требуется перебирать всё больше элементов, при проверке тоже. Чем больше в таблице элементов, тем больше в ней кластеры и тем выше вероятность того, что добавляемый элемент попадёт в кластер.&lt;br /&gt;
Для защиты от кластеризации используется двойное хеширование и [[Хеширование кукушки|хеширование кукушки]].&lt;br /&gt;
&lt;br /&gt;
=== Удаление элемента без пометок ===&lt;br /&gt;
&lt;br /&gt;
Рассуждение будет описывать случай с линейным поиском хеша. Будем при удалении элемента сдвигать всё последующие на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; позиций назад. При этом:&lt;br /&gt;
* если в цепочке встречается элемент с другим хешем, то он должен остаться на своём месте (такая ситуация может возникнуть если оставшаяся часть цепочки была добавлена позже этого элемента)&lt;br /&gt;
* в цепочке не должно оставаться &amp;quot;дырок&amp;quot;, тогда любой элемент с данным хешем будет доступен из начала цепи&lt;br /&gt;
&lt;br /&gt;
Учитывая это будем действовать следующим образом: при поиске следующего элемента цепочки будем пропускать все ячейки с другим значением хеша, первый найденный элемент копировать в текущую ячейку, и затем рекурсивно его удалять. Если такой следующей ячейки нет, то текущий элемент можно просто удалить, сторонние цепочки при этом не разрушатся (чего нельзя сказать про случай квадратичного поиска).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
''' Псевдокод '''&lt;br /&gt;
&lt;br /&gt;
 '''function''' delete('''Item''' i) &lt;br /&gt;
      j = i + q&lt;br /&gt;
      '''while''' table[j] == '''null''' '''or''' table[j].key != table[i].key&lt;br /&gt;
         '''if''' table[j] == '''null'''&lt;br /&gt;
            table[i] = '''null'''&lt;br /&gt;
            '''return'''&lt;br /&gt;
         j += q&lt;br /&gt;
      table[i] = table[j]&lt;br /&gt;
      delete(j);    &lt;br /&gt;
&lt;br /&gt;
Хеш-таблицу считаем зацикленной&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Утверждение&lt;br /&gt;
|about=о времени работы&lt;br /&gt;
|statement=Асимптотически время работы &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\mathrm{find}&amp;lt;/tex&amp;gt; совпадают&lt;br /&gt;
|proof=&lt;br /&gt;
Заметим что указатель &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; в каждой итерации перемещается вперёд на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; (с учётом рекурсивных вызовов &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt;). То есть этот алгоритм последовательно пройдёт по цепочке от удаляемого элемента до последнего {{---}} с учётом вызова &amp;lt;tex&amp;gt;\mathrm{find}&amp;lt;/tex&amp;gt; собственно для нахождения удаляемого элемента, мы посетим все ячейки цепи.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Вариант с зацикливанием мы не рассматриваем, поскольку если &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; взаимнопросто с размером хеш-таблицы, то для зацикливания в ней вообще не должно быть свободных позиций&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Теперь докажем почему этот алгоритм работает. Собственно нам требуется сохранение трёх условий.&lt;br /&gt;
* В редактируемой цепи не остаётся дырок&lt;br /&gt;
Докажем по индукции. Если на данной итерации мы просто удаляем элемент (база), то после него ничего нет, всё верно. Если же нет, то вызванный в конце &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt; (см. псевдокод) заметёт созданную дыру (скопированный элемент), и сам, по предположению, новых не создаст.&lt;br /&gt;
* Элементы, которые уже на своих местах, не должны быть сдвинуты.&lt;br /&gt;
Это учтено.&lt;br /&gt;
* В других цепочках не появятся дыры&lt;br /&gt;
Противное возможно только в том случае, если какой-то элемент был действительно удалён. Удаляем мы только последнюю ячейку в цепи, и если бы на её месте возникла дыра для сторонней цепочки, это бы означало что элемент, стоящий на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; позиций назад, одновременно принадлежал нашей и другой цепочкам, что невозможно.&lt;br /&gt;
&lt;br /&gt;
==Двойное хеширование==&lt;br /&gt;
'''Двойное хеширование''' (англ. double hashing) {{---}} метод борьбы с коллизиями, возникающими при открытой адресации, основанный на использовании двух хеш-функций для построения различных последовательностей исследования хеш-таблицы.&lt;br /&gt;
&lt;br /&gt;
===Принцип двойного хеширования===&lt;br /&gt;
При двойном хешировании используются две независимые хеш-функции &amp;lt;tex&amp;gt; h_1(k) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; h_2(k) &amp;lt;/tex&amp;gt;. Пусть &amp;lt;tex&amp;gt; k &amp;lt;/tex&amp;gt; {{---}} это наш ключ, &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt; {{---}} размер нашей таблицы, &amp;lt;tex&amp;gt;n \bmod m &amp;lt;/tex&amp;gt; {{---}} остаток от деления &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt; на &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, тогда сначала исследуется ячейка с адресом &amp;lt;tex&amp;gt; h_1(k) &amp;lt;/tex&amp;gt;, если она уже занята, то рассматривается &amp;lt;tex&amp;gt; (h_1(k) +  h_2(k)) \bmod m &amp;lt;/tex&amp;gt;, затем &amp;lt;tex&amp;gt; (h_1(k) +  2 \cdot h_2(k)) \bmod m &amp;lt;/tex&amp;gt; и так далее. В общем случае идёт проверка последовательности ячеек &amp;lt;tex&amp;gt; (h_1(k) +  i \cdot h_2(k)) \bmod m &amp;lt;/tex&amp;gt; где &amp;lt;tex&amp;gt;  i = (0, 1, \; ... \;,  m - 1) &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, операции вставки, удаления и поиска в лучшем случае выполняются за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;, в худшем {{---}} за &amp;lt;tex&amp;gt;O(m)&amp;lt;/tex&amp;gt;, что не отличается от обычного [[Открытое_и_закрытое_хеширование#Линейное разрешение коллизий|линейного разрешения коллизий]].&lt;br /&gt;
Однако в среднем, при грамотном выборе хеш-функций, двойное хеширование будет выдавать лучшие результаты, за счёт того, что вероятность совпадения значений сразу двух независимых хеш-функций ниже, чем одной.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt;\forall x \neq y \; \exists h_1,h_2 : p(h_1(x)=h_1(y))&amp;gt; p((h_1(x)=h_1(y)) \land (h_2(x)=h_2(y)))&amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Выбор хеш-функций===&lt;br /&gt;
&amp;lt;tex&amp;gt; h_1 &amp;lt;/tex&amp;gt; может быть обычной хеш-функцией. Однако чтобы последовательность исследования могла охватить всю таблицу, &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; должна возвращать значения:&lt;br /&gt;
*не равные &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
*независимые  от &amp;lt;tex&amp;gt; h_1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
*взаимно простые с величиной хеш-таблицы&lt;br /&gt;
&lt;br /&gt;
Есть два удобных способа это сделать. Первый состоит в том, что в качестве размера таблицы используется простое число, а &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; возвращает натуральные числа, меньшие &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;. Второй {{---}} размер таблицы является степенью двойки, а &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; возвращает нечетные значения.&lt;br /&gt;
&lt;br /&gt;
Например, если размер таблицы равен &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, то в качестве &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; можно использовать функцию вида &amp;lt;tex&amp;gt; h_2(k) = k \bmod (m-1) + 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Файл: Вставка при двойном хэшировании.svg.jpeg|thumb|right|Вставка при двойном хешировании]]&lt;br /&gt;
&lt;br /&gt;
===Пример===&lt;br /&gt;
&lt;br /&gt;
Показана хеш-таблица размером 13 ячеек, в которой используются вспомогательные функции:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h(k,i) = (h_1(k) + i \cdot h_2(k)) \bmod 13 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h_1(k) = k \bmod 13 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h_2(k) = 1 + k \bmod 11 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Мы хотим вставить ключ 14. Изначально &amp;lt;tex&amp;gt; i = 0 &amp;lt;/tex&amp;gt;. Тогда &amp;lt;tex&amp;gt; h(14,0) = (h_1(14) + 0\cdot h_2(14)) \bmod 13 = 1 &amp;lt;/tex&amp;gt;. Но ячейка с индексом 1 занята, поэтому увеличиваем &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; на 1 и пересчитываем значение хеш-функции. Делаем так, пока не дойдем до пустой ячейки. При &amp;lt;tex&amp;gt; i = 2 &amp;lt;/tex&amp;gt; получаем &amp;lt;tex&amp;gt; h(14,2) = (h_1(14) + 2\cdot h_2(14)) \bmod 13 = 9 &amp;lt;/tex&amp;gt;. Ячейка с номером 9 свободна, значит записываем туда наш ключ.&lt;br /&gt;
&lt;br /&gt;
Таким образом, основная особенность двойного хеширования состоит в том, что при различных &amp;lt;tex&amp;gt; k &amp;lt;/tex&amp;gt; пара &amp;lt;tex&amp;gt; (h_1(k),h_2(k)) &amp;lt;/tex&amp;gt; дает различные последовательности ячеек для исследования.&lt;br /&gt;
&lt;br /&gt;
===Простая реализация===&lt;br /&gt;
Пусть у нас есть некоторый объект &amp;lt;tex&amp;gt; item &amp;lt;/tex&amp;gt;, в котором определено поле &amp;lt;tex&amp;gt; key &amp;lt;/tex&amp;gt;, от которого можно вычислить хеш-функции &amp;lt;tex&amp;gt; h_1(key)&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; h_2(key) &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Так же у нас есть таблица &amp;lt;tex&amp;gt; table &amp;lt;/tex&amp;gt; величиной &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, состоящая из объектов типа &amp;lt;tex&amp;gt; item &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
'''Вставка'''&lt;br /&gt;
 '''function''' add('''Item''' item)&lt;br /&gt;
      x = h1(item.key)&lt;br /&gt;
      y = h2(item.key)&lt;br /&gt;
         '''for''' (i = 0..m)    	&lt;br /&gt;
            '''if''' table[x] == '''null'''&lt;br /&gt;
               table[x] = item&lt;br /&gt;
            '''return'''      &lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      table.resize()&amp;lt;span style=&amp;quot;color:Green&amp;quot;&amp;gt;// ошибка, требуется увеличить размер таблицы&lt;br /&gt;
&lt;br /&gt;
'''Поиск'''&lt;br /&gt;
 '''Item''' search('''Item''' key)&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m)&lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key&lt;br /&gt;
               '''return''' table[x]&lt;br /&gt;
            '''else'''&lt;br /&gt;
               '''return''' '''null'''&lt;br /&gt;
      x = (x + y) '''mod''' m   &lt;br /&gt;
      '''return''' '''null'''&lt;br /&gt;
&lt;br /&gt;
===Реализация с удалением===&lt;br /&gt;
Что бы наша хеш-таблица поддерживала удаление, требуется добавить массив &amp;lt;tex&amp;gt;deleted&amp;lt;/tex&amp;gt; типов &amp;lt;tex&amp;gt;bool&amp;lt;/tex&amp;gt;, равный по величине массиву &amp;lt;tex&amp;gt;table&amp;lt;/tex&amp;gt;. Теперь при удалении мы просто будем помечать наш объект ''как удалённый'', а при добавлении как ''не удалённый'' и замещать новым добавляемым объектом. При поиске, помимо равенства ключей, мы смотрим, удалён ли элемент, если да, то идём дальше.&lt;br /&gt;
&lt;br /&gt;
'''Вставка'''&lt;br /&gt;
 '''function''' add('''Item''' item)&lt;br /&gt;
      x = h1(item.key)&lt;br /&gt;
      y = h2(item.key)&lt;br /&gt;
      '''for''' (i = 0..m)   	&lt;br /&gt;
         '''if''' table[x] == '''null''' '''or''' deleted[x]&lt;br /&gt;
            table[x] = item&lt;br /&gt;
            deleted[x] = '''false'''&lt;br /&gt;
            '''return'''      &lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      table.resize()&amp;lt;span style=&amp;quot;color:Green&amp;quot;&amp;gt;// ошибка, требуется увеличить размер таблицы&lt;br /&gt;
'''Поиск'''&lt;br /&gt;
 '''Item''' search('''Item''' key)&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m) &lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key '''and''' !deleted[x]&lt;br /&gt;
               '''return''' table[x]&lt;br /&gt;
         '''else'''&lt;br /&gt;
            '''return''' '''null'''&lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      '''return''' '''null'''&lt;br /&gt;
&lt;br /&gt;
'''Удаление'''&lt;br /&gt;
 '''function''' remove('''Item''' key)&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m)&lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key&lt;br /&gt;
               deleted[x] = '''true'''&lt;br /&gt;
         '''else''' &lt;br /&gt;
            '''return'''&lt;br /&gt;
      x = (x + y) '''mod''' m&lt;br /&gt;
&lt;br /&gt;
== Разрешение коллизий в Java 8==&lt;br /&gt;
[[Файл:Hashing_in_Java8.png|thumb|400px|Хеширование в Java 8.]]&lt;br /&gt;
В Java 8 для разрешения коллизий используется модифицированный метод цепочек. Суть его заключается в том, что когда количество элементов в бакете превышает определенное значение, данный бакет переходит от использования связного списка к использованию сбалансированного дерева. Такой подход позволяет улучшить производительность с &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;O(log(n))&amp;lt;/tex&amp;gt;.  Данный способ используется в таких коллекциях как HashMap, LinkedHashMap и ConcurrentHashMap.&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
* [[Хеширование]]&lt;br /&gt;
* [[Хеширование_кукушки|Хеширование кукушки]]&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
* Бакнелл Дж. М. «Фундаментальные алгоритмы и структуры данных в Delphi», 2003&lt;br /&gt;
* Кормен, Томас Х., Лейзерсон, Чарльз И., Ривест, Рональд Л., Штайн Клиффорд «Алгоритмы: построение и анализ», 2-е издание. Пер. с англ. — М.:Издательский дом &amp;quot;Вильямс&amp;quot;, 2010.— Парал. тит. англ. — ISBN 978-5-8459-0857-5 (рус.)&lt;br /&gt;
* Дональд Кнут. «Искусство программирования, том 3. Сортировка и поиск» {{---}} «Вильямс», 2007 г.{{---}} ISBN 0-201-89685-0&lt;br /&gt;
* Седжвик Р. «Фундаментальные алгоритмы на C. Части 1-4. Анализ. Структуры данных. Сортировка. Поиск», 2003&lt;br /&gt;
&lt;br /&gt;
==Ссылки==&lt;br /&gt;
* [http://openjdk.java.net/jeps/180 Handle Frequent HashMap Collisions with Balanced Trees]&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Double_hashing Wikipedia {{---}} Double_hashing]&lt;br /&gt;
* [http://ru.wikipedia.org/wiki/%D0%A5%D0%B5%D1%88-%D1%82%D0%B0%D0%B1%D0%BB%D0%B8%D1%86%D0%B0 Разрешение коллизий]&lt;br /&gt;
* [http://rain.ifmo.ru/cat/view.php/vis/hashtables/hash-2001-2 Пример хеш таблицы]&lt;br /&gt;
* [http://research.cs.vt.edu/AVresearch/hashing/double.php Пример хеш таблицы с двойным хешированием]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория: Хеширование]]&lt;/div&gt;</summary>
		<author><name>Kozichuk</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%A0%D0%B0%D0%B7%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BA%D0%BE%D0%BB%D0%BB%D0%B8%D0%B7%D0%B8%D0%B9&amp;diff=47145</id>
		<title>Разрешение коллизий</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A0%D0%B0%D0%B7%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BA%D0%BE%D0%BB%D0%BB%D0%B8%D0%B7%D0%B8%D0%B9&amp;diff=47145"/>
				<updated>2015-05-31T21:02:08Z</updated>
		
		<summary type="html">&lt;p&gt;Kozichuk: /* Реализация с удалением */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Разрешение [[Хеш-таблица|коллизий]]''' (англ. collision resolution) в [[Хеш-таблица|хеш-таблице]], задача, решаемая несколькими способами. Например, при помощи метода цепочке или открытой адресации. Коллизии замедляют время работы с хеш-таблицой, поэтому с ними нужно бороться.&lt;br /&gt;
&lt;br /&gt;
== Разрешение коллизий с помощью цепочек ==&lt;br /&gt;
[[Файл:open_hash.png|thumb|380px|right|Разрешение коллизий при помощи цепочек.]]&lt;br /&gt;
Каждая ячейка &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; массива &amp;lt;tex&amp;gt;H&amp;lt;/tex&amp;gt; содержит указатель на начало [[Список|списка]] всех элементов, хеш-код которых равен &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, либо указывает на их отсутствие. Коллизии приводят к тому, что появляются списки размером больше одного элемента.&lt;br /&gt;
&lt;br /&gt;
Время, необходимое для вставки в наихудшем случае равно &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;. Это операция выполняет быстро, так как считается, что вставляемый элемент отсутствует в таблице, но если потребуется, то перед вставкой мы можем выполнить поиск этого элемента.&lt;br /&gt;
&lt;br /&gt;
Время работы поиска в наихудшем случае пропорционально длине списка, а если все &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; ключей захешировались в одну и ту же ячейку (создав список длиной &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;) время поиска будет равно &amp;lt;tex&amp;gt;\Theta(n)&amp;lt;/tex&amp;gt; плюс время вычисления хеш-функции, что ничуть не лучше, чем использование связного списка для хранения всех &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; элементов.&lt;br /&gt;
&lt;br /&gt;
Удаления элемента может быть выполнено за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;, как и вставка, при использовании двухсвязного списка.&lt;br /&gt;
&lt;br /&gt;
== Линейное разрешение коллизий ==&lt;br /&gt;
[[Файл:close_hash.png|thumb|380px|right|Пример хеш-таблицы с открытой адресацией и линейным пробированием.]]&lt;br /&gt;
Все элементы хранятся непосредственно в хеш-таблице, без использования связных списков. В отличии от хеширования с цепочками, при использовании этого метода может возникнуть ситуация, когда хеш-таблица окажется полностью заполненной, следовательно будет невозможно добавлять в неё новые элементы. Так что при возникновении такой ситуации решением может быть динамическое увеличение размера хеш-таблицы, с одновременной её перестройкой.&lt;br /&gt;
&lt;br /&gt;
=== Стратегии поиска ===&lt;br /&gt;
&lt;br /&gt;
''' Последовательный поиск '''&lt;br /&gt;
&lt;br /&gt;
При попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt;i+1, i+2, i+3&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку. В неё и запишем элемент.&lt;br /&gt;
&lt;br /&gt;
[[Файл:hashtables1.png|400px|Последовательный поиск, частный случай линейного поиска.]]&lt;br /&gt;
&lt;br /&gt;
''' Линейный поиск '''&lt;br /&gt;
&lt;br /&gt;
Выбираем шаг &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;. При попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt;i+(1 \cdot q), i+(2 \cdot q), i+(3 \cdot q)&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку. В неё и запишем элемент.&lt;br /&gt;
По сути последовательный поиск - частный случай линейного, где &amp;lt;tex&amp;gt;q=1&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Hashtables56.PNG|400px|Линейный поиск с шагом q.]]&lt;br /&gt;
&lt;br /&gt;
''' Квадратичный поиск '''&lt;br /&gt;
&lt;br /&gt;
Шаг &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; не фиксирован, а изменяется квадратично: &amp;lt;tex&amp;gt;q = 1,4,9,16...&amp;lt;/tex&amp;gt;. Соответственно при попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt; i+1, i+4, i+9&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку.&lt;br /&gt;
&lt;br /&gt;
[[Файл:hashtables3.png|400px|Квадратичный поиск.]]&lt;br /&gt;
&lt;br /&gt;
=== Проверка наличия элемента в таблице===&lt;br /&gt;
&lt;br /&gt;
Проверка осуществляется аналогично добавлению: мы проверяем ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; и другие, в соответствии с выбранной стратегией, пока не найдём искомый элемент или свободную ячейку.&lt;br /&gt;
&lt;br /&gt;
При поиске элемента может получится так, что мы дойдём до конца таблицы. Обычно поиск продолжается, начиная с другого конца, пока мы не придём в ту ячейку, откуда начинался поиск.&lt;br /&gt;
&lt;br /&gt;
=== Проблемы данных стратегий ===&lt;br /&gt;
&lt;br /&gt;
Проблем две — крайне нетривиальное удаление элемента из таблицы и образование кластеров  — последовательностей занятых ячеек.&lt;br /&gt;
&lt;br /&gt;
Кластеризация замедляет все операции с хеш-таблицей: при добавлении требуется перебирать всё больше элементов, при проверке тоже. Чем больше в таблице элементов, тем больше в ней кластеры и тем выше вероятность того, что добавляемый элемент попадёт в кластер.&lt;br /&gt;
Для защиты от кластеризации используется двойное хеширование и [[Хеширование кукушки|хеширование кукушки]].&lt;br /&gt;
&lt;br /&gt;
=== Удаление элемента без пометок ===&lt;br /&gt;
&lt;br /&gt;
Рассуждение будет описывать случай с линейным поиском хеша. Будем при удалении элемента сдвигать всё последующие на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; позиций назад. При этом:&lt;br /&gt;
* если в цепочке встречается элемент с другим хешем, то он должен остаться на своём месте (такая ситуация может возникнуть если оставшаяся часть цепочки была добавлена позже этого элемента)&lt;br /&gt;
* в цепочке не должно оставаться &amp;quot;дырок&amp;quot;, тогда любой элемент с данным хешем будет доступен из начала цепи&lt;br /&gt;
&lt;br /&gt;
Учитывая это будем действовать следующим образом: при поиске следующего элемента цепочки будем пропускать все ячейки с другим значением хеша, первый найденный элемент копировать в текущую ячейку, и затем рекурсивно его удалять. Если такой следующей ячейки нет, то текущий элемент можно просто удалить, сторонние цепочки при этом не разрушатся (чего нельзя сказать про случай квадратичного поиска).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
''' Псевдокод '''&lt;br /&gt;
&lt;br /&gt;
 '''function''' delete('''Item''' i) &lt;br /&gt;
      j = i + q&lt;br /&gt;
      '''while''' table[j] == '''null''' '''or''' table[j].key != table[i].key&lt;br /&gt;
         '''if''' (table[j] == '''null''')&lt;br /&gt;
            table[i] = '''null'''&lt;br /&gt;
            '''return'''&lt;br /&gt;
         j += q&lt;br /&gt;
      table[i] = table[j]&lt;br /&gt;
      delete(j);    &lt;br /&gt;
&lt;br /&gt;
Хеш-таблицу считаем зацикленной&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Утверждение&lt;br /&gt;
|about=о времени работы&lt;br /&gt;
|statement=Асимптотически время работы &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\mathrm{find}&amp;lt;/tex&amp;gt; совпадают&lt;br /&gt;
|proof=&lt;br /&gt;
Заметим что указатель &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; в каждой итерации перемещается вперёд на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; (с учётом рекурсивных вызовов &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt;). То есть этот алгоритм последовательно пройдёт по цепочке от удаляемого элемента до последнего {{---}} с учётом вызова &amp;lt;tex&amp;gt;\mathrm{find}&amp;lt;/tex&amp;gt; собственно для нахождения удаляемого элемента, мы посетим все ячейки цепи.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Вариант с зацикливанием мы не рассматриваем, поскольку если &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; взаимнопросто с размером хеш-таблицы, то для зацикливания в ней вообще не должно быть свободных позиций&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Теперь докажем почему этот алгоритм работает. Собственно нам требуется сохранение трёх условий.&lt;br /&gt;
* В редактируемой цепи не остаётся дырок&lt;br /&gt;
Докажем по индукции. Если на данной итерации мы просто удаляем элемент (база), то после него ничего нет, всё верно. Если же нет, то вызванный в конце &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt; (см. псевдокод) заметёт созданную дыру (скопированный элемент), и сам, по предположению, новых не создаст.&lt;br /&gt;
* Элементы, которые уже на своих местах, не должны быть сдвинуты.&lt;br /&gt;
Это учтено.&lt;br /&gt;
* В других цепочках не появятся дыры&lt;br /&gt;
Противное возможно только в том случае, если какой-то элемент был действительно удалён. Удаляем мы только последнюю ячейку в цепи, и если бы на её месте возникла дыра для сторонней цепочки, это бы означало что элемент, стоящий на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; позиций назад, одновременно принадлежал нашей и другой цепочкам, что невозможно.&lt;br /&gt;
&lt;br /&gt;
==Двойное хеширование==&lt;br /&gt;
'''Двойное хеширование''' (англ. double hashing) {{---}} метод борьбы с коллизиями, возникающими при открытой адресации, основанный на использовании двух хеш-функций для построения различных последовательностей исследования хеш-таблицы.&lt;br /&gt;
&lt;br /&gt;
===Принцип двойного хеширования===&lt;br /&gt;
При двойном хешировании используются две независимые хеш-функции &amp;lt;tex&amp;gt; h_1(k) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; h_2(k) &amp;lt;/tex&amp;gt;. Пусть &amp;lt;tex&amp;gt; k &amp;lt;/tex&amp;gt; {{---}} это наш ключ, &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt; {{---}} размер нашей таблицы, &amp;lt;tex&amp;gt;n \bmod m &amp;lt;/tex&amp;gt; {{---}} остаток от деления &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt; на &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, тогда сначала исследуется ячейка с адресом &amp;lt;tex&amp;gt; h_1(k) &amp;lt;/tex&amp;gt;, если она уже занята, то рассматривается &amp;lt;tex&amp;gt; (h_1(k) +  h_2(k)) \bmod m &amp;lt;/tex&amp;gt;, затем &amp;lt;tex&amp;gt; (h_1(k) +  2 \cdot h_2(k)) \bmod m &amp;lt;/tex&amp;gt; и так далее. В общем случае идёт проверка последовательности ячеек &amp;lt;tex&amp;gt; (h_1(k) +  i \cdot h_2(k)) \bmod m &amp;lt;/tex&amp;gt; где &amp;lt;tex&amp;gt;  i = (0, 1, \; ... \;,  m - 1) &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, операции вставки, удаления и поиска в лучшем случае выполняются за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;, в худшем {{---}} за &amp;lt;tex&amp;gt;O(m)&amp;lt;/tex&amp;gt;, что не отличается от обычного [[Открытое_и_закрытое_хеширование#Линейное разрешение коллизий|линейного разрешения коллизий]].&lt;br /&gt;
Однако в среднем, при грамотном выборе хеш-функций, двойное хеширование будет выдавать лучшие результаты, за счёт того, что вероятность совпадения значений сразу двух независимых хеш-функций ниже, чем одной.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt;\forall x \neq y \; \exists h_1,h_2 : p(h_1(x)=h_1(y))&amp;gt; p((h_1(x)=h_1(y)) \land (h_2(x)=h_2(y)))&amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Выбор хеш-функций===&lt;br /&gt;
&amp;lt;tex&amp;gt; h_1 &amp;lt;/tex&amp;gt; может быть обычной хеш-функцией. Однако чтобы последовательность исследования могла охватить всю таблицу, &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; должна возвращать значения:&lt;br /&gt;
*не равные &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
*независимые  от &amp;lt;tex&amp;gt; h_1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
*взаимно простые с величиной хеш-таблицы&lt;br /&gt;
&lt;br /&gt;
Есть два удобных способа это сделать. Первый состоит в том, что в качестве размера таблицы используется простое число, а &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; возвращает натуральные числа, меньшие &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;. Второй {{---}} размер таблицы является степенью двойки, а &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; возвращает нечетные значения.&lt;br /&gt;
&lt;br /&gt;
Например, если размер таблицы равен &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, то в качестве &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; можно использовать функцию вида &amp;lt;tex&amp;gt; h_2(k) = k \bmod (m-1) + 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Файл: Вставка при двойном хэшировании.svg.jpeg|thumb|right|Вставка при двойном хешировании]]&lt;br /&gt;
&lt;br /&gt;
===Пример===&lt;br /&gt;
&lt;br /&gt;
Показана хеш-таблица размером 13 ячеек, в которой используются вспомогательные функции:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h(k,i) = (h_1(k) + i \cdot h_2(k)) \bmod 13 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h_1(k) = k \bmod 13 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h_2(k) = 1 + k \bmod 11 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Мы хотим вставить ключ 14. Изначально &amp;lt;tex&amp;gt; i = 0 &amp;lt;/tex&amp;gt;. Тогда &amp;lt;tex&amp;gt; h(14,0) = (h_1(14) + 0\cdot h_2(14)) \bmod 13 = 1 &amp;lt;/tex&amp;gt;. Но ячейка с индексом 1 занята, поэтому увеличиваем &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; на 1 и пересчитываем значение хеш-функции. Делаем так, пока не дойдем до пустой ячейки. При &amp;lt;tex&amp;gt; i = 2 &amp;lt;/tex&amp;gt; получаем &amp;lt;tex&amp;gt; h(14,2) = (h_1(14) + 2\cdot h_2(14)) \bmod 13 = 9 &amp;lt;/tex&amp;gt;. Ячейка с номером 9 свободна, значит записываем туда наш ключ.&lt;br /&gt;
&lt;br /&gt;
Таким образом, основная особенность двойного хеширования состоит в том, что при различных &amp;lt;tex&amp;gt; k &amp;lt;/tex&amp;gt; пара &amp;lt;tex&amp;gt; (h_1(k),h_2(k)) &amp;lt;/tex&amp;gt; дает различные последовательности ячеек для исследования.&lt;br /&gt;
&lt;br /&gt;
===Простая реализация===&lt;br /&gt;
Пусть у нас есть некоторый объект &amp;lt;tex&amp;gt; item &amp;lt;/tex&amp;gt;, в котором определено поле &amp;lt;tex&amp;gt; key &amp;lt;/tex&amp;gt;, от которого можно вычислить хеш-функции &amp;lt;tex&amp;gt; h_1(key)&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; h_2(key) &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Так же у нас есть таблица &amp;lt;tex&amp;gt; table &amp;lt;/tex&amp;gt; величиной &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, состоящая из объектов типа &amp;lt;tex&amp;gt; item &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
'''Вставка'''&lt;br /&gt;
 '''function''' add('''Item''' item)&lt;br /&gt;
      x = h1(item.key)&lt;br /&gt;
      y = h2(item.key)&lt;br /&gt;
         '''for''' (i = 0..m)    	&lt;br /&gt;
            '''if''' table[x] == '''null'''&lt;br /&gt;
               table[x] = item&lt;br /&gt;
            '''return'''      &lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      table.resize()&amp;lt;span style=&amp;quot;color:Green&amp;quot;&amp;gt;// ошибка, требуется увеличить размер таблицы&lt;br /&gt;
&lt;br /&gt;
'''Поиск'''&lt;br /&gt;
 '''Item''' search('''Item''' key)&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m)&lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key&lt;br /&gt;
               '''return''' table[x]&lt;br /&gt;
            '''else'''&lt;br /&gt;
               '''return''' '''null'''&lt;br /&gt;
      x = (x + y) '''mod''' m   &lt;br /&gt;
      '''return''' '''null'''&lt;br /&gt;
&lt;br /&gt;
===Реализация с удалением===&lt;br /&gt;
Что бы наша хеш-таблица поддерживала удаление, требуется добавить массив &amp;lt;tex&amp;gt;deleted&amp;lt;/tex&amp;gt; типов &amp;lt;tex&amp;gt;bool&amp;lt;/tex&amp;gt;, равный по величине массиву &amp;lt;tex&amp;gt;table&amp;lt;/tex&amp;gt;. Теперь при удалении мы просто будем помечать наш объект ''как удалённый'', а при добавлении как ''не удалённый'' и замещать новым добавляемым объектом. При поиске, помимо равенства ключей, мы смотрим, удалён ли элемент, если да, то идём дальше.&lt;br /&gt;
&lt;br /&gt;
'''Вставка'''&lt;br /&gt;
 '''function''' add('''Item''' item)&lt;br /&gt;
      x = h1(item.key)&lt;br /&gt;
      y = h2(item.key)&lt;br /&gt;
      '''for''' (i = 0..m)   	&lt;br /&gt;
         '''if''' table[x] == '''null''' '''or''' deleted[x]&lt;br /&gt;
            table[x] = item&lt;br /&gt;
            deleted[x] = '''false'''&lt;br /&gt;
            '''return'''      &lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      table.resize()&amp;lt;span style=&amp;quot;color:Green&amp;quot;&amp;gt;// ошибка, требуется увеличить размер таблицы&lt;br /&gt;
'''Поиск'''&lt;br /&gt;
 '''Item''' search('''Item''' key)&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m) &lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key '''and''' !deleted[x]&lt;br /&gt;
               '''return''' table[x]&lt;br /&gt;
         '''else'''&lt;br /&gt;
            '''return''' '''null'''&lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      '''return''' '''null'''&lt;br /&gt;
&lt;br /&gt;
'''Удаление'''&lt;br /&gt;
 '''function''' remove('''Item''' key)&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m)&lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key&lt;br /&gt;
               deleted[x] = '''true'''&lt;br /&gt;
         '''else''' &lt;br /&gt;
            '''return'''&lt;br /&gt;
      x = (x + y) '''mod''' m&lt;br /&gt;
&lt;br /&gt;
== Разрешение коллизий в Java 8==&lt;br /&gt;
[[Файл:Hashing_in_Java8.png|thumb|400px|Хеширование в Java 8.]]&lt;br /&gt;
В Java 8 для разрешения коллизий используется модифицированный метод цепочек. Суть его заключается в том, что когда количество элементов в бакете превышает определенное значение, данный бакет переходит от использования связного списка к использованию сбалансированного дерева. Такой подход позволяет улучшить производительность с &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;O(log(n))&amp;lt;/tex&amp;gt;.  Данный способ используется в таких коллекциях как HashMap, LinkedHashMap и ConcurrentHashMap.&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
* [[Хеширование]]&lt;br /&gt;
* [[Хеширование_кукушки|Хеширование кукушки]]&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
* Бакнелл Дж. М. «Фундаментальные алгоритмы и структуры данных в Delphi», 2003&lt;br /&gt;
* Кормен, Томас Х., Лейзерсон, Чарльз И., Ривест, Рональд Л., Штайн Клиффорд «Алгоритмы: построение и анализ», 2-е издание. Пер. с англ. — М.:Издательский дом &amp;quot;Вильямс&amp;quot;, 2010.— Парал. тит. англ. — ISBN 978-5-8459-0857-5 (рус.)&lt;br /&gt;
* Дональд Кнут. «Искусство программирования, том 3. Сортировка и поиск» {{---}} «Вильямс», 2007 г.{{---}} ISBN 0-201-89685-0&lt;br /&gt;
* Седжвик Р. «Фундаментальные алгоритмы на C. Части 1-4. Анализ. Структуры данных. Сортировка. Поиск», 2003&lt;br /&gt;
&lt;br /&gt;
==Ссылки==&lt;br /&gt;
* [http://openjdk.java.net/jeps/180 Handle Frequent HashMap Collisions with Balanced Trees]&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Double_hashing Wikipedia {{---}} Double_hashing]&lt;br /&gt;
* [http://ru.wikipedia.org/wiki/%D0%A5%D0%B5%D1%88-%D1%82%D0%B0%D0%B1%D0%BB%D0%B8%D1%86%D0%B0 Разрешение коллизий]&lt;br /&gt;
* [http://rain.ifmo.ru/cat/view.php/vis/hashtables/hash-2001-2 Пример хеш таблицы]&lt;br /&gt;
* [http://research.cs.vt.edu/AVresearch/hashing/double.php Пример хеш таблицы с двойным хешированием]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория: Хеширование]]&lt;/div&gt;</summary>
		<author><name>Kozichuk</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%A0%D0%B0%D0%B7%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BA%D0%BE%D0%BB%D0%BB%D0%B8%D0%B7%D0%B8%D0%B9&amp;diff=47144</id>
		<title>Разрешение коллизий</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A0%D0%B0%D0%B7%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BA%D0%BE%D0%BB%D0%BB%D0%B8%D0%B7%D0%B8%D0%B9&amp;diff=47144"/>
				<updated>2015-05-31T21:01:22Z</updated>
		
		<summary type="html">&lt;p&gt;Kozichuk: /* Удаление элемента без пометок */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Разрешение [[Хеш-таблица|коллизий]]''' (англ. collision resolution) в [[Хеш-таблица|хеш-таблице]], задача, решаемая несколькими способами. Например, при помощи метода цепочке или открытой адресации. Коллизии замедляют время работы с хеш-таблицой, поэтому с ними нужно бороться.&lt;br /&gt;
&lt;br /&gt;
== Разрешение коллизий с помощью цепочек ==&lt;br /&gt;
[[Файл:open_hash.png|thumb|380px|right|Разрешение коллизий при помощи цепочек.]]&lt;br /&gt;
Каждая ячейка &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; массива &amp;lt;tex&amp;gt;H&amp;lt;/tex&amp;gt; содержит указатель на начало [[Список|списка]] всех элементов, хеш-код которых равен &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, либо указывает на их отсутствие. Коллизии приводят к тому, что появляются списки размером больше одного элемента.&lt;br /&gt;
&lt;br /&gt;
Время, необходимое для вставки в наихудшем случае равно &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;. Это операция выполняет быстро, так как считается, что вставляемый элемент отсутствует в таблице, но если потребуется, то перед вставкой мы можем выполнить поиск этого элемента.&lt;br /&gt;
&lt;br /&gt;
Время работы поиска в наихудшем случае пропорционально длине списка, а если все &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; ключей захешировались в одну и ту же ячейку (создав список длиной &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;) время поиска будет равно &amp;lt;tex&amp;gt;\Theta(n)&amp;lt;/tex&amp;gt; плюс время вычисления хеш-функции, что ничуть не лучше, чем использование связного списка для хранения всех &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; элементов.&lt;br /&gt;
&lt;br /&gt;
Удаления элемента может быть выполнено за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;, как и вставка, при использовании двухсвязного списка.&lt;br /&gt;
&lt;br /&gt;
== Линейное разрешение коллизий ==&lt;br /&gt;
[[Файл:close_hash.png|thumb|380px|right|Пример хеш-таблицы с открытой адресацией и линейным пробированием.]]&lt;br /&gt;
Все элементы хранятся непосредственно в хеш-таблице, без использования связных списков. В отличии от хеширования с цепочками, при использовании этого метода может возникнуть ситуация, когда хеш-таблица окажется полностью заполненной, следовательно будет невозможно добавлять в неё новые элементы. Так что при возникновении такой ситуации решением может быть динамическое увеличение размера хеш-таблицы, с одновременной её перестройкой.&lt;br /&gt;
&lt;br /&gt;
=== Стратегии поиска ===&lt;br /&gt;
&lt;br /&gt;
''' Последовательный поиск '''&lt;br /&gt;
&lt;br /&gt;
При попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt;i+1, i+2, i+3&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку. В неё и запишем элемент.&lt;br /&gt;
&lt;br /&gt;
[[Файл:hashtables1.png|400px|Последовательный поиск, частный случай линейного поиска.]]&lt;br /&gt;
&lt;br /&gt;
''' Линейный поиск '''&lt;br /&gt;
&lt;br /&gt;
Выбираем шаг &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;. При попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt;i+(1 \cdot q), i+(2 \cdot q), i+(3 \cdot q)&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку. В неё и запишем элемент.&lt;br /&gt;
По сути последовательный поиск - частный случай линейного, где &amp;lt;tex&amp;gt;q=1&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Hashtables56.PNG|400px|Линейный поиск с шагом q.]]&lt;br /&gt;
&lt;br /&gt;
''' Квадратичный поиск '''&lt;br /&gt;
&lt;br /&gt;
Шаг &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; не фиксирован, а изменяется квадратично: &amp;lt;tex&amp;gt;q = 1,4,9,16...&amp;lt;/tex&amp;gt;. Соответственно при попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt; i+1, i+4, i+9&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку.&lt;br /&gt;
&lt;br /&gt;
[[Файл:hashtables3.png|400px|Квадратичный поиск.]]&lt;br /&gt;
&lt;br /&gt;
=== Проверка наличия элемента в таблице===&lt;br /&gt;
&lt;br /&gt;
Проверка осуществляется аналогично добавлению: мы проверяем ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; и другие, в соответствии с выбранной стратегией, пока не найдём искомый элемент или свободную ячейку.&lt;br /&gt;
&lt;br /&gt;
При поиске элемента может получится так, что мы дойдём до конца таблицы. Обычно поиск продолжается, начиная с другого конца, пока мы не придём в ту ячейку, откуда начинался поиск.&lt;br /&gt;
&lt;br /&gt;
=== Проблемы данных стратегий ===&lt;br /&gt;
&lt;br /&gt;
Проблем две — крайне нетривиальное удаление элемента из таблицы и образование кластеров  — последовательностей занятых ячеек.&lt;br /&gt;
&lt;br /&gt;
Кластеризация замедляет все операции с хеш-таблицей: при добавлении требуется перебирать всё больше элементов, при проверке тоже. Чем больше в таблице элементов, тем больше в ней кластеры и тем выше вероятность того, что добавляемый элемент попадёт в кластер.&lt;br /&gt;
Для защиты от кластеризации используется двойное хеширование и [[Хеширование кукушки|хеширование кукушки]].&lt;br /&gt;
&lt;br /&gt;
=== Удаление элемента без пометок ===&lt;br /&gt;
&lt;br /&gt;
Рассуждение будет описывать случай с линейным поиском хеша. Будем при удалении элемента сдвигать всё последующие на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; позиций назад. При этом:&lt;br /&gt;
* если в цепочке встречается элемент с другим хешем, то он должен остаться на своём месте (такая ситуация может возникнуть если оставшаяся часть цепочки была добавлена позже этого элемента)&lt;br /&gt;
* в цепочке не должно оставаться &amp;quot;дырок&amp;quot;, тогда любой элемент с данным хешем будет доступен из начала цепи&lt;br /&gt;
&lt;br /&gt;
Учитывая это будем действовать следующим образом: при поиске следующего элемента цепочки будем пропускать все ячейки с другим значением хеша, первый найденный элемент копировать в текущую ячейку, и затем рекурсивно его удалять. Если такой следующей ячейки нет, то текущий элемент можно просто удалить, сторонние цепочки при этом не разрушатся (чего нельзя сказать про случай квадратичного поиска).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
''' Псевдокод '''&lt;br /&gt;
&lt;br /&gt;
 '''function''' delete('''Item''' i) &lt;br /&gt;
      j = i + q&lt;br /&gt;
      '''while''' table[j] == '''null''' '''or''' table[j].key != table[i].key&lt;br /&gt;
         '''if''' (table[j] == '''null''')&lt;br /&gt;
            table[i] = '''null'''&lt;br /&gt;
            '''return'''&lt;br /&gt;
         j += q&lt;br /&gt;
      table[i] = table[j]&lt;br /&gt;
      delete(j);    &lt;br /&gt;
&lt;br /&gt;
Хеш-таблицу считаем зацикленной&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Утверждение&lt;br /&gt;
|about=о времени работы&lt;br /&gt;
|statement=Асимптотически время работы &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\mathrm{find}&amp;lt;/tex&amp;gt; совпадают&lt;br /&gt;
|proof=&lt;br /&gt;
Заметим что указатель &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; в каждой итерации перемещается вперёд на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; (с учётом рекурсивных вызовов &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt;). То есть этот алгоритм последовательно пройдёт по цепочке от удаляемого элемента до последнего {{---}} с учётом вызова &amp;lt;tex&amp;gt;\mathrm{find}&amp;lt;/tex&amp;gt; собственно для нахождения удаляемого элемента, мы посетим все ячейки цепи.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Вариант с зацикливанием мы не рассматриваем, поскольку если &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; взаимнопросто с размером хеш-таблицы, то для зацикливания в ней вообще не должно быть свободных позиций&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Теперь докажем почему этот алгоритм работает. Собственно нам требуется сохранение трёх условий.&lt;br /&gt;
* В редактируемой цепи не остаётся дырок&lt;br /&gt;
Докажем по индукции. Если на данной итерации мы просто удаляем элемент (база), то после него ничего нет, всё верно. Если же нет, то вызванный в конце &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt; (см. псевдокод) заметёт созданную дыру (скопированный элемент), и сам, по предположению, новых не создаст.&lt;br /&gt;
* Элементы, которые уже на своих местах, не должны быть сдвинуты.&lt;br /&gt;
Это учтено.&lt;br /&gt;
* В других цепочках не появятся дыры&lt;br /&gt;
Противное возможно только в том случае, если какой-то элемент был действительно удалён. Удаляем мы только последнюю ячейку в цепи, и если бы на её месте возникла дыра для сторонней цепочки, это бы означало что элемент, стоящий на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; позиций назад, одновременно принадлежал нашей и другой цепочкам, что невозможно.&lt;br /&gt;
&lt;br /&gt;
==Двойное хеширование==&lt;br /&gt;
'''Двойное хеширование''' (англ. double hashing) {{---}} метод борьбы с коллизиями, возникающими при открытой адресации, основанный на использовании двух хеш-функций для построения различных последовательностей исследования хеш-таблицы.&lt;br /&gt;
&lt;br /&gt;
===Принцип двойного хеширования===&lt;br /&gt;
При двойном хешировании используются две независимые хеш-функции &amp;lt;tex&amp;gt; h_1(k) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; h_2(k) &amp;lt;/tex&amp;gt;. Пусть &amp;lt;tex&amp;gt; k &amp;lt;/tex&amp;gt; {{---}} это наш ключ, &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt; {{---}} размер нашей таблицы, &amp;lt;tex&amp;gt;n \bmod m &amp;lt;/tex&amp;gt; {{---}} остаток от деления &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt; на &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, тогда сначала исследуется ячейка с адресом &amp;lt;tex&amp;gt; h_1(k) &amp;lt;/tex&amp;gt;, если она уже занята, то рассматривается &amp;lt;tex&amp;gt; (h_1(k) +  h_2(k)) \bmod m &amp;lt;/tex&amp;gt;, затем &amp;lt;tex&amp;gt; (h_1(k) +  2 \cdot h_2(k)) \bmod m &amp;lt;/tex&amp;gt; и так далее. В общем случае идёт проверка последовательности ячеек &amp;lt;tex&amp;gt; (h_1(k) +  i \cdot h_2(k)) \bmod m &amp;lt;/tex&amp;gt; где &amp;lt;tex&amp;gt;  i = (0, 1, \; ... \;,  m - 1) &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, операции вставки, удаления и поиска в лучшем случае выполняются за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;, в худшем {{---}} за &amp;lt;tex&amp;gt;O(m)&amp;lt;/tex&amp;gt;, что не отличается от обычного [[Открытое_и_закрытое_хеширование#Линейное разрешение коллизий|линейного разрешения коллизий]].&lt;br /&gt;
Однако в среднем, при грамотном выборе хеш-функций, двойное хеширование будет выдавать лучшие результаты, за счёт того, что вероятность совпадения значений сразу двух независимых хеш-функций ниже, чем одной.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt;\forall x \neq y \; \exists h_1,h_2 : p(h_1(x)=h_1(y))&amp;gt; p((h_1(x)=h_1(y)) \land (h_2(x)=h_2(y)))&amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Выбор хеш-функций===&lt;br /&gt;
&amp;lt;tex&amp;gt; h_1 &amp;lt;/tex&amp;gt; может быть обычной хеш-функцией. Однако чтобы последовательность исследования могла охватить всю таблицу, &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; должна возвращать значения:&lt;br /&gt;
*не равные &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
*независимые  от &amp;lt;tex&amp;gt; h_1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
*взаимно простые с величиной хеш-таблицы&lt;br /&gt;
&lt;br /&gt;
Есть два удобных способа это сделать. Первый состоит в том, что в качестве размера таблицы используется простое число, а &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; возвращает натуральные числа, меньшие &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;. Второй {{---}} размер таблицы является степенью двойки, а &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; возвращает нечетные значения.&lt;br /&gt;
&lt;br /&gt;
Например, если размер таблицы равен &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, то в качестве &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; можно использовать функцию вида &amp;lt;tex&amp;gt; h_2(k) = k \bmod (m-1) + 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Файл: Вставка при двойном хэшировании.svg.jpeg|thumb|right|Вставка при двойном хешировании]]&lt;br /&gt;
&lt;br /&gt;
===Пример===&lt;br /&gt;
&lt;br /&gt;
Показана хеш-таблица размером 13 ячеек, в которой используются вспомогательные функции:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h(k,i) = (h_1(k) + i \cdot h_2(k)) \bmod 13 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h_1(k) = k \bmod 13 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h_2(k) = 1 + k \bmod 11 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Мы хотим вставить ключ 14. Изначально &amp;lt;tex&amp;gt; i = 0 &amp;lt;/tex&amp;gt;. Тогда &amp;lt;tex&amp;gt; h(14,0) = (h_1(14) + 0\cdot h_2(14)) \bmod 13 = 1 &amp;lt;/tex&amp;gt;. Но ячейка с индексом 1 занята, поэтому увеличиваем &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; на 1 и пересчитываем значение хеш-функции. Делаем так, пока не дойдем до пустой ячейки. При &amp;lt;tex&amp;gt; i = 2 &amp;lt;/tex&amp;gt; получаем &amp;lt;tex&amp;gt; h(14,2) = (h_1(14) + 2\cdot h_2(14)) \bmod 13 = 9 &amp;lt;/tex&amp;gt;. Ячейка с номером 9 свободна, значит записываем туда наш ключ.&lt;br /&gt;
&lt;br /&gt;
Таким образом, основная особенность двойного хеширования состоит в том, что при различных &amp;lt;tex&amp;gt; k &amp;lt;/tex&amp;gt; пара &amp;lt;tex&amp;gt; (h_1(k),h_2(k)) &amp;lt;/tex&amp;gt; дает различные последовательности ячеек для исследования.&lt;br /&gt;
&lt;br /&gt;
===Простая реализация===&lt;br /&gt;
Пусть у нас есть некоторый объект &amp;lt;tex&amp;gt; item &amp;lt;/tex&amp;gt;, в котором определено поле &amp;lt;tex&amp;gt; key &amp;lt;/tex&amp;gt;, от которого можно вычислить хеш-функции &amp;lt;tex&amp;gt; h_1(key)&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; h_2(key) &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Так же у нас есть таблица &amp;lt;tex&amp;gt; table &amp;lt;/tex&amp;gt; величиной &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, состоящая из объектов типа &amp;lt;tex&amp;gt; item &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
'''Вставка'''&lt;br /&gt;
 '''function''' add('''Item''' item)&lt;br /&gt;
      x = h1(item.key)&lt;br /&gt;
      y = h2(item.key)&lt;br /&gt;
         '''for''' (i = 0..m)    	&lt;br /&gt;
            '''if''' table[x] == '''null'''&lt;br /&gt;
               table[x] = item&lt;br /&gt;
            '''return'''      &lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      table.resize()&amp;lt;span style=&amp;quot;color:Green&amp;quot;&amp;gt;// ошибка, требуется увеличить размер таблицы&lt;br /&gt;
&lt;br /&gt;
'''Поиск'''&lt;br /&gt;
 '''Item''' search('''Item''' key)&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m)&lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key&lt;br /&gt;
               '''return''' table[x]&lt;br /&gt;
            '''else'''&lt;br /&gt;
               '''return''' '''null'''&lt;br /&gt;
      x = (x + y) '''mod''' m   &lt;br /&gt;
      '''return''' '''null'''&lt;br /&gt;
&lt;br /&gt;
===Реализация с удалением===&lt;br /&gt;
Что бы наша хеш-таблица поддерживала удаление, требуется добавить массив &amp;lt;tex&amp;gt;deleted&amp;lt;/tex&amp;gt; типов &amp;lt;tex&amp;gt;bool&amp;lt;/tex&amp;gt;, равный по величине массиву &amp;lt;tex&amp;gt;table&amp;lt;/tex&amp;gt;. Теперь при удалении мы просто будем помечать наш объект ''как удалённый'', а при добавлении как ''не удалённый'' и замещать новым добавляемым объектом. При поиске, помимо равенства ключей, мы смотрим, удалён ли элемент, если да, то идём дальше.&lt;br /&gt;
&lt;br /&gt;
'''Вставка'''&lt;br /&gt;
 '''function''' add('''Item''' item)&lt;br /&gt;
      x = h1(item.key)&lt;br /&gt;
      y = h2(item.key)&lt;br /&gt;
      '''for''' (i = 0..m)   	&lt;br /&gt;
         '''if''' table[x] == '''null''' '''or''' deleted[x]&lt;br /&gt;
            table[x] = item&lt;br /&gt;
            deleted[x] = '''false'''&lt;br /&gt;
            '''return'''      &lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      table.resize()  &amp;lt;span style=&amp;quot;color:Green&amp;quot;&amp;gt;// ошибка, требуется увеличить размер таблицы&lt;br /&gt;
'''Поиск'''&lt;br /&gt;
 '''Item''' search('''Item''' key)&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m) &lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key '''and''' !deleted[x]&lt;br /&gt;
               '''return''' table[x]&lt;br /&gt;
         '''else'''&lt;br /&gt;
            '''return''' '''null'''&lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      '''return''' '''null'''&lt;br /&gt;
&lt;br /&gt;
'''Удаление'''&lt;br /&gt;
 '''function''' remove('''Item''' key)&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m)&lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key&lt;br /&gt;
               deleted[x] = '''true'''&lt;br /&gt;
         '''else''' &lt;br /&gt;
            '''return'''&lt;br /&gt;
      x = (x + y) '''mod''' m&lt;br /&gt;
&lt;br /&gt;
== Разрешение коллизий в Java 8==&lt;br /&gt;
[[Файл:Hashing_in_Java8.png|thumb|400px|Хеширование в Java 8.]]&lt;br /&gt;
В Java 8 для разрешения коллизий используется модифицированный метод цепочек. Суть его заключается в том, что когда количество элементов в бакете превышает определенное значение, данный бакет переходит от использования связного списка к использованию сбалансированного дерева. Такой подход позволяет улучшить производительность с &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;O(log(n))&amp;lt;/tex&amp;gt;.  Данный способ используется в таких коллекциях как HashMap, LinkedHashMap и ConcurrentHashMap.&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
* [[Хеширование]]&lt;br /&gt;
* [[Хеширование_кукушки|Хеширование кукушки]]&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
* Бакнелл Дж. М. «Фундаментальные алгоритмы и структуры данных в Delphi», 2003&lt;br /&gt;
* Кормен, Томас Х., Лейзерсон, Чарльз И., Ривест, Рональд Л., Штайн Клиффорд «Алгоритмы: построение и анализ», 2-е издание. Пер. с англ. — М.:Издательский дом &amp;quot;Вильямс&amp;quot;, 2010.— Парал. тит. англ. — ISBN 978-5-8459-0857-5 (рус.)&lt;br /&gt;
* Дональд Кнут. «Искусство программирования, том 3. Сортировка и поиск» {{---}} «Вильямс», 2007 г.{{---}} ISBN 0-201-89685-0&lt;br /&gt;
* Седжвик Р. «Фундаментальные алгоритмы на C. Части 1-4. Анализ. Структуры данных. Сортировка. Поиск», 2003&lt;br /&gt;
&lt;br /&gt;
==Ссылки==&lt;br /&gt;
* [http://openjdk.java.net/jeps/180 Handle Frequent HashMap Collisions with Balanced Trees]&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Double_hashing Wikipedia {{---}} Double_hashing]&lt;br /&gt;
* [http://ru.wikipedia.org/wiki/%D0%A5%D0%B5%D1%88-%D1%82%D0%B0%D0%B1%D0%BB%D0%B8%D1%86%D0%B0 Разрешение коллизий]&lt;br /&gt;
* [http://rain.ifmo.ru/cat/view.php/vis/hashtables/hash-2001-2 Пример хеш таблицы]&lt;br /&gt;
* [http://research.cs.vt.edu/AVresearch/hashing/double.php Пример хеш таблицы с двойным хешированием]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория: Хеширование]]&lt;/div&gt;</summary>
		<author><name>Kozichuk</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%A0%D0%B0%D0%B7%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BA%D0%BE%D0%BB%D0%BB%D0%B8%D0%B7%D0%B8%D0%B9&amp;diff=47143</id>
		<title>Разрешение коллизий</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A0%D0%B0%D0%B7%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BA%D0%BE%D0%BB%D0%BB%D0%B8%D0%B7%D0%B8%D0%B9&amp;diff=47143"/>
				<updated>2015-05-31T21:00:53Z</updated>
		
		<summary type="html">&lt;p&gt;Kozichuk: /* Удаление элемента без пометок */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Разрешение [[Хеш-таблица|коллизий]]''' (англ. collision resolution) в [[Хеш-таблица|хеш-таблице]], задача, решаемая несколькими способами. Например, при помощи метода цепочке или открытой адресации. Коллизии замедляют время работы с хеш-таблицой, поэтому с ними нужно бороться.&lt;br /&gt;
&lt;br /&gt;
== Разрешение коллизий с помощью цепочек ==&lt;br /&gt;
[[Файл:open_hash.png|thumb|380px|right|Разрешение коллизий при помощи цепочек.]]&lt;br /&gt;
Каждая ячейка &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; массива &amp;lt;tex&amp;gt;H&amp;lt;/tex&amp;gt; содержит указатель на начало [[Список|списка]] всех элементов, хеш-код которых равен &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, либо указывает на их отсутствие. Коллизии приводят к тому, что появляются списки размером больше одного элемента.&lt;br /&gt;
&lt;br /&gt;
Время, необходимое для вставки в наихудшем случае равно &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;. Это операция выполняет быстро, так как считается, что вставляемый элемент отсутствует в таблице, но если потребуется, то перед вставкой мы можем выполнить поиск этого элемента.&lt;br /&gt;
&lt;br /&gt;
Время работы поиска в наихудшем случае пропорционально длине списка, а если все &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; ключей захешировались в одну и ту же ячейку (создав список длиной &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;) время поиска будет равно &amp;lt;tex&amp;gt;\Theta(n)&amp;lt;/tex&amp;gt; плюс время вычисления хеш-функции, что ничуть не лучше, чем использование связного списка для хранения всех &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; элементов.&lt;br /&gt;
&lt;br /&gt;
Удаления элемента может быть выполнено за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;, как и вставка, при использовании двухсвязного списка.&lt;br /&gt;
&lt;br /&gt;
== Линейное разрешение коллизий ==&lt;br /&gt;
[[Файл:close_hash.png|thumb|380px|right|Пример хеш-таблицы с открытой адресацией и линейным пробированием.]]&lt;br /&gt;
Все элементы хранятся непосредственно в хеш-таблице, без использования связных списков. В отличии от хеширования с цепочками, при использовании этого метода может возникнуть ситуация, когда хеш-таблица окажется полностью заполненной, следовательно будет невозможно добавлять в неё новые элементы. Так что при возникновении такой ситуации решением может быть динамическое увеличение размера хеш-таблицы, с одновременной её перестройкой.&lt;br /&gt;
&lt;br /&gt;
=== Стратегии поиска ===&lt;br /&gt;
&lt;br /&gt;
''' Последовательный поиск '''&lt;br /&gt;
&lt;br /&gt;
При попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt;i+1, i+2, i+3&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку. В неё и запишем элемент.&lt;br /&gt;
&lt;br /&gt;
[[Файл:hashtables1.png|400px|Последовательный поиск, частный случай линейного поиска.]]&lt;br /&gt;
&lt;br /&gt;
''' Линейный поиск '''&lt;br /&gt;
&lt;br /&gt;
Выбираем шаг &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt;. При попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt;i+(1 \cdot q), i+(2 \cdot q), i+(3 \cdot q)&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку. В неё и запишем элемент.&lt;br /&gt;
По сути последовательный поиск - частный случай линейного, где &amp;lt;tex&amp;gt;q=1&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Hashtables56.PNG|400px|Линейный поиск с шагом q.]]&lt;br /&gt;
&lt;br /&gt;
''' Квадратичный поиск '''&lt;br /&gt;
&lt;br /&gt;
Шаг &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; не фиксирован, а изменяется квадратично: &amp;lt;tex&amp;gt;q = 1,4,9,16...&amp;lt;/tex&amp;gt;. Соответственно при попытке добавить элемент в занятую ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; начинаем последовательно просматривать ячейки &amp;lt;tex&amp;gt; i+1, i+4, i+9&amp;lt;/tex&amp;gt; и так далее, пока не найдём свободную ячейку.&lt;br /&gt;
&lt;br /&gt;
[[Файл:hashtables3.png|400px|Квадратичный поиск.]]&lt;br /&gt;
&lt;br /&gt;
=== Проверка наличия элемента в таблице===&lt;br /&gt;
&lt;br /&gt;
Проверка осуществляется аналогично добавлению: мы проверяем ячейку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; и другие, в соответствии с выбранной стратегией, пока не найдём искомый элемент или свободную ячейку.&lt;br /&gt;
&lt;br /&gt;
При поиске элемента может получится так, что мы дойдём до конца таблицы. Обычно поиск продолжается, начиная с другого конца, пока мы не придём в ту ячейку, откуда начинался поиск.&lt;br /&gt;
&lt;br /&gt;
=== Проблемы данных стратегий ===&lt;br /&gt;
&lt;br /&gt;
Проблем две — крайне нетривиальное удаление элемента из таблицы и образование кластеров  — последовательностей занятых ячеек.&lt;br /&gt;
&lt;br /&gt;
Кластеризация замедляет все операции с хеш-таблицей: при добавлении требуется перебирать всё больше элементов, при проверке тоже. Чем больше в таблице элементов, тем больше в ней кластеры и тем выше вероятность того, что добавляемый элемент попадёт в кластер.&lt;br /&gt;
Для защиты от кластеризации используется двойное хеширование и [[Хеширование кукушки|хеширование кукушки]].&lt;br /&gt;
&lt;br /&gt;
=== Удаление элемента без пометок ===&lt;br /&gt;
&lt;br /&gt;
Рассуждение будет описывать случай с линейным поиском хеша. Будем при удалении элемента сдвигать всё последующие на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; позиций назад. При этом:&lt;br /&gt;
* если в цепочке встречается элемент с другим хешем, то он должен остаться на своём месте (такая ситуация может возникнуть если оставшаяся часть цепочки была добавлена позже этого элемента)&lt;br /&gt;
* в цепочке не должно оставаться &amp;quot;дырок&amp;quot;, тогда любой элемент с данным хешем будет доступен из начала цепи&lt;br /&gt;
&lt;br /&gt;
Учитывая это будем действовать следующим образом: при поиске следующего элемента цепочки будем пропускать все ячейки с другим значением хеша, первый найденный элемент копировать в текущую ячейку, и затем рекурсивно его удалять. Если такой следующей ячейки нет, то текущий элемент можно просто удалить, сторонние цепочки при этом не разрушатся (чего нельзя сказать про случай квадратичного поиска).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
''' Псевдокод '''&lt;br /&gt;
&lt;br /&gt;
 '''function''' delete('''Item''' i) &lt;br /&gt;
      j = i + q&lt;br /&gt;
      '''while''' table[j] == '''null''' || table[j].key != table[i].key&lt;br /&gt;
         '''if''' (table[j] == '''null''')&lt;br /&gt;
            table[i] = '''null'''&lt;br /&gt;
            '''return'''&lt;br /&gt;
         j += q&lt;br /&gt;
      table[i] = table[j]&lt;br /&gt;
      delete(j);    &lt;br /&gt;
&lt;br /&gt;
Хеш-таблицу считаем зацикленной&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Утверждение&lt;br /&gt;
|about=о времени работы&lt;br /&gt;
|statement=Асимптотически время работы &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\mathrm{find}&amp;lt;/tex&amp;gt; совпадают&lt;br /&gt;
|proof=&lt;br /&gt;
Заметим что указатель &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; в каждой итерации перемещается вперёд на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; (с учётом рекурсивных вызовов &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt;). То есть этот алгоритм последовательно пройдёт по цепочке от удаляемого элемента до последнего {{---}} с учётом вызова &amp;lt;tex&amp;gt;\mathrm{find}&amp;lt;/tex&amp;gt; собственно для нахождения удаляемого элемента, мы посетим все ячейки цепи.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Вариант с зацикливанием мы не рассматриваем, поскольку если &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; взаимнопросто с размером хеш-таблицы, то для зацикливания в ней вообще не должно быть свободных позиций&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Теперь докажем почему этот алгоритм работает. Собственно нам требуется сохранение трёх условий.&lt;br /&gt;
* В редактируемой цепи не остаётся дырок&lt;br /&gt;
Докажем по индукции. Если на данной итерации мы просто удаляем элемент (база), то после него ничего нет, всё верно. Если же нет, то вызванный в конце &amp;lt;tex&amp;gt;\mathrm{delete}&amp;lt;/tex&amp;gt; (см. псевдокод) заметёт созданную дыру (скопированный элемент), и сам, по предположению, новых не создаст.&lt;br /&gt;
* Элементы, которые уже на своих местах, не должны быть сдвинуты.&lt;br /&gt;
Это учтено.&lt;br /&gt;
* В других цепочках не появятся дыры&lt;br /&gt;
Противное возможно только в том случае, если какой-то элемент был действительно удалён. Удаляем мы только последнюю ячейку в цепи, и если бы на её месте возникла дыра для сторонней цепочки, это бы означало что элемент, стоящий на &amp;lt;tex&amp;gt;q&amp;lt;/tex&amp;gt; позиций назад, одновременно принадлежал нашей и другой цепочкам, что невозможно.&lt;br /&gt;
&lt;br /&gt;
==Двойное хеширование==&lt;br /&gt;
'''Двойное хеширование''' (англ. double hashing) {{---}} метод борьбы с коллизиями, возникающими при открытой адресации, основанный на использовании двух хеш-функций для построения различных последовательностей исследования хеш-таблицы.&lt;br /&gt;
&lt;br /&gt;
===Принцип двойного хеширования===&lt;br /&gt;
При двойном хешировании используются две независимые хеш-функции &amp;lt;tex&amp;gt; h_1(k) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; h_2(k) &amp;lt;/tex&amp;gt;. Пусть &amp;lt;tex&amp;gt; k &amp;lt;/tex&amp;gt; {{---}} это наш ключ, &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt; {{---}} размер нашей таблицы, &amp;lt;tex&amp;gt;n \bmod m &amp;lt;/tex&amp;gt; {{---}} остаток от деления &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt; на &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, тогда сначала исследуется ячейка с адресом &amp;lt;tex&amp;gt; h_1(k) &amp;lt;/tex&amp;gt;, если она уже занята, то рассматривается &amp;lt;tex&amp;gt; (h_1(k) +  h_2(k)) \bmod m &amp;lt;/tex&amp;gt;, затем &amp;lt;tex&amp;gt; (h_1(k) +  2 \cdot h_2(k)) \bmod m &amp;lt;/tex&amp;gt; и так далее. В общем случае идёт проверка последовательности ячеек &amp;lt;tex&amp;gt; (h_1(k) +  i \cdot h_2(k)) \bmod m &amp;lt;/tex&amp;gt; где &amp;lt;tex&amp;gt;  i = (0, 1, \; ... \;,  m - 1) &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, операции вставки, удаления и поиска в лучшем случае выполняются за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;, в худшем {{---}} за &amp;lt;tex&amp;gt;O(m)&amp;lt;/tex&amp;gt;, что не отличается от обычного [[Открытое_и_закрытое_хеширование#Линейное разрешение коллизий|линейного разрешения коллизий]].&lt;br /&gt;
Однако в среднем, при грамотном выборе хеш-функций, двойное хеширование будет выдавать лучшие результаты, за счёт того, что вероятность совпадения значений сразу двух независимых хеш-функций ниже, чем одной.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt;\forall x \neq y \; \exists h_1,h_2 : p(h_1(x)=h_1(y))&amp;gt; p((h_1(x)=h_1(y)) \land (h_2(x)=h_2(y)))&amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Выбор хеш-функций===&lt;br /&gt;
&amp;lt;tex&amp;gt; h_1 &amp;lt;/tex&amp;gt; может быть обычной хеш-функцией. Однако чтобы последовательность исследования могла охватить всю таблицу, &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; должна возвращать значения:&lt;br /&gt;
*не равные &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
*независимые  от &amp;lt;tex&amp;gt; h_1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
*взаимно простые с величиной хеш-таблицы&lt;br /&gt;
&lt;br /&gt;
Есть два удобных способа это сделать. Первый состоит в том, что в качестве размера таблицы используется простое число, а &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; возвращает натуральные числа, меньшие &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;. Второй {{---}} размер таблицы является степенью двойки, а &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; возвращает нечетные значения.&lt;br /&gt;
&lt;br /&gt;
Например, если размер таблицы равен &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, то в качестве &amp;lt;tex&amp;gt; h_2 &amp;lt;/tex&amp;gt; можно использовать функцию вида &amp;lt;tex&amp;gt; h_2(k) = k \bmod (m-1) + 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Файл: Вставка при двойном хэшировании.svg.jpeg|thumb|right|Вставка при двойном хешировании]]&lt;br /&gt;
&lt;br /&gt;
===Пример===&lt;br /&gt;
&lt;br /&gt;
Показана хеш-таблица размером 13 ячеек, в которой используются вспомогательные функции:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h(k,i) = (h_1(k) + i \cdot h_2(k)) \bmod 13 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h_1(k) = k \bmod 13 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt; h_2(k) = 1 + k \bmod 11 &amp;lt;/tex&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Мы хотим вставить ключ 14. Изначально &amp;lt;tex&amp;gt; i = 0 &amp;lt;/tex&amp;gt;. Тогда &amp;lt;tex&amp;gt; h(14,0) = (h_1(14) + 0\cdot h_2(14)) \bmod 13 = 1 &amp;lt;/tex&amp;gt;. Но ячейка с индексом 1 занята, поэтому увеличиваем &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; на 1 и пересчитываем значение хеш-функции. Делаем так, пока не дойдем до пустой ячейки. При &amp;lt;tex&amp;gt; i = 2 &amp;lt;/tex&amp;gt; получаем &amp;lt;tex&amp;gt; h(14,2) = (h_1(14) + 2\cdot h_2(14)) \bmod 13 = 9 &amp;lt;/tex&amp;gt;. Ячейка с номером 9 свободна, значит записываем туда наш ключ.&lt;br /&gt;
&lt;br /&gt;
Таким образом, основная особенность двойного хеширования состоит в том, что при различных &amp;lt;tex&amp;gt; k &amp;lt;/tex&amp;gt; пара &amp;lt;tex&amp;gt; (h_1(k),h_2(k)) &amp;lt;/tex&amp;gt; дает различные последовательности ячеек для исследования.&lt;br /&gt;
&lt;br /&gt;
===Простая реализация===&lt;br /&gt;
Пусть у нас есть некоторый объект &amp;lt;tex&amp;gt; item &amp;lt;/tex&amp;gt;, в котором определено поле &amp;lt;tex&amp;gt; key &amp;lt;/tex&amp;gt;, от которого можно вычислить хеш-функции &amp;lt;tex&amp;gt; h_1(key)&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; h_2(key) &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Так же у нас есть таблица &amp;lt;tex&amp;gt; table &amp;lt;/tex&amp;gt; величиной &amp;lt;tex&amp;gt; m &amp;lt;/tex&amp;gt;, состоящая из объектов типа &amp;lt;tex&amp;gt; item &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
'''Вставка'''&lt;br /&gt;
 '''function''' add('''Item''' item)&lt;br /&gt;
      x = h1(item.key)&lt;br /&gt;
      y = h2(item.key)&lt;br /&gt;
         '''for''' (i = 0..m)    	&lt;br /&gt;
            '''if''' table[x] == '''null'''&lt;br /&gt;
               table[x] = item&lt;br /&gt;
            '''return'''      &lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      table.resize()&amp;lt;span style=&amp;quot;color:Green&amp;quot;&amp;gt;// ошибка, требуется увеличить размер таблицы&lt;br /&gt;
&lt;br /&gt;
'''Поиск'''&lt;br /&gt;
 '''Item''' search('''Item''' key)&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m)&lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key&lt;br /&gt;
               '''return''' table[x]&lt;br /&gt;
            '''else'''&lt;br /&gt;
               '''return''' '''null'''&lt;br /&gt;
      x = (x + y) '''mod''' m   &lt;br /&gt;
      '''return''' '''null'''&lt;br /&gt;
&lt;br /&gt;
===Реализация с удалением===&lt;br /&gt;
Что бы наша хеш-таблица поддерживала удаление, требуется добавить массив &amp;lt;tex&amp;gt;deleted&amp;lt;/tex&amp;gt; типов &amp;lt;tex&amp;gt;bool&amp;lt;/tex&amp;gt;, равный по величине массиву &amp;lt;tex&amp;gt;table&amp;lt;/tex&amp;gt;. Теперь при удалении мы просто будем помечать наш объект ''как удалённый'', а при добавлении как ''не удалённый'' и замещать новым добавляемым объектом. При поиске, помимо равенства ключей, мы смотрим, удалён ли элемент, если да, то идём дальше.&lt;br /&gt;
&lt;br /&gt;
'''Вставка'''&lt;br /&gt;
 '''function''' add('''Item''' item)&lt;br /&gt;
      x = h1(item.key)&lt;br /&gt;
      y = h2(item.key)&lt;br /&gt;
      '''for''' (i = 0..m)   	&lt;br /&gt;
         '''if''' table[x] == '''null''' '''or''' deleted[x]&lt;br /&gt;
            table[x] = item&lt;br /&gt;
            deleted[x] = '''false'''&lt;br /&gt;
            '''return'''      &lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      table.resize()  &amp;lt;span style=&amp;quot;color:Green&amp;quot;&amp;gt;// ошибка, требуется увеличить размер таблицы&lt;br /&gt;
'''Поиск'''&lt;br /&gt;
 '''Item''' search('''Item''' key)&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m) &lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key '''and''' !deleted[x]&lt;br /&gt;
               '''return''' table[x]&lt;br /&gt;
         '''else'''&lt;br /&gt;
            '''return''' '''null'''&lt;br /&gt;
         x = (x + y) '''mod''' m   &lt;br /&gt;
      '''return''' '''null'''&lt;br /&gt;
&lt;br /&gt;
'''Удаление'''&lt;br /&gt;
 '''function''' remove('''Item''' key)&lt;br /&gt;
      x = h1(key)&lt;br /&gt;
      y = h2(key)&lt;br /&gt;
      '''for''' (i = 0..m)&lt;br /&gt;
         '''if''' table[x] != '''null'''&lt;br /&gt;
            '''if''' table[x].key == key&lt;br /&gt;
               deleted[x] = '''true'''&lt;br /&gt;
         '''else''' &lt;br /&gt;
            '''return'''&lt;br /&gt;
      x = (x + y) '''mod''' m&lt;br /&gt;
&lt;br /&gt;
== Разрешение коллизий в Java 8==&lt;br /&gt;
[[Файл:Hashing_in_Java8.png|thumb|400px|Хеширование в Java 8.]]&lt;br /&gt;
В Java 8 для разрешения коллизий используется модифицированный метод цепочек. Суть его заключается в том, что когда количество элементов в бакете превышает определенное значение, данный бакет переходит от использования связного списка к использованию сбалансированного дерева. Такой подход позволяет улучшить производительность с &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;O(log(n))&amp;lt;/tex&amp;gt;.  Данный способ используется в таких коллекциях как HashMap, LinkedHashMap и ConcurrentHashMap.&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
* [[Хеширование]]&lt;br /&gt;
* [[Хеширование_кукушки|Хеширование кукушки]]&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
* Бакнелл Дж. М. «Фундаментальные алгоритмы и структуры данных в Delphi», 2003&lt;br /&gt;
* Кормен, Томас Х., Лейзерсон, Чарльз И., Ривест, Рональд Л., Штайн Клиффорд «Алгоритмы: построение и анализ», 2-е издание. Пер. с англ. — М.:Издательский дом &amp;quot;Вильямс&amp;quot;, 2010.— Парал. тит. англ. — ISBN 978-5-8459-0857-5 (рус.)&lt;br /&gt;
* Дональд Кнут. «Искусство программирования, том 3. Сортировка и поиск» {{---}} «Вильямс», 2007 г.{{---}} ISBN 0-201-89685-0&lt;br /&gt;
* Седжвик Р. «Фундаментальные алгоритмы на C. Части 1-4. Анализ. Структуры данных. Сортировка. Поиск», 2003&lt;br /&gt;
&lt;br /&gt;
==Ссылки==&lt;br /&gt;
* [http://openjdk.java.net/jeps/180 Handle Frequent HashMap Collisions with Balanced Trees]&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Double_hashing Wikipedia {{---}} Double_hashing]&lt;br /&gt;
* [http://ru.wikipedia.org/wiki/%D0%A5%D0%B5%D1%88-%D1%82%D0%B0%D0%B1%D0%BB%D0%B8%D1%86%D0%B0 Разрешение коллизий]&lt;br /&gt;
* [http://rain.ifmo.ru/cat/view.php/vis/hashtables/hash-2001-2 Пример хеш таблицы]&lt;br /&gt;
* [http://research.cs.vt.edu/AVresearch/hashing/double.php Пример хеш таблицы с двойным хешированием]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория: Хеширование]]&lt;/div&gt;</summary>
		<author><name>Kozichuk</name></author>	</entry>

	</feed>