Изменения

Перейти к: навигация, поиск

СНМ (реализация с помощью леса корневых деревьев)

3031 байт добавлено, 19:12, 4 сентября 2022
м
rollbackEdits.php mass rollback
Данная реализация СНМ позволяет добиться наилучшей асимптотики при работе с этой структурой данных. А именно, обе операции (<tex>\mathrm{get}</tex> и <tex>\mathrm{union}</tex>) выполняются в среднем за практически константное время.
==Реализация==
Каждое множество хранится в виде дерева. Элементы множества хранятся в узлах вершинах дерева. У каждого множества есть его представитель {{---}} один из элементов этого множества, он хранится в корне дерева. В каждом узле, кроме корня, хранится ссылка на "родителя".
При объединении двух множеств, корень одного дерева подвешивается к другому (операция ''<tex>\mathrm{union}</tex>''). Таким образом, чтобы определить, в каком множестве находится элемент достаточно пройтись по ссылкам по дереву вверх до корня (операция ''<tex>\mathrm{get}</tex>'').
Без использования дополнительных "улучшений", такое дерево может выродиться в линейный список, где <tex>\mathrm{get}</tex> будет работать за линейное время, и никакого выигрыша по сравнению с [[СНМ(наивные_реализации)|наивными реализацимиреализациями]] не будет. Выигрыш в скорости можно получить, используя две эвристики: '''объединение по рангу''' (union by rank) и '''сжатие пути''' (path compression).
===Объединение по рангу===
Эта эвристика аналогична [[СНМ(списки_с_весовой_эвристикой)|весовой эвристике у связных списков]]. Идея в том, чтобы при объединении подвешивать дерево с меньшей глубиной к дереву с большей.
Вместо того, чтобы явно хранить высоту дерева, можно хранить его ранг, который по сути является некой верхней оценкой высоты дерева. У дерева, состоящего ровно из одного элемента ранг равен <tex>10</tex>. При объединении дерево с меньшим рангом подвешивается к дереву с большим, и ранг объединенного дерева становится равным большему из этих двух рангов. Если ранги объединяемых деревьев равны, то не важно какое к какому дереву подвешивать, но ранг объединенного дерева следует делать большим на <tex>1</tex>.
===Сжатие пути===
===Псевдокод===
Для реализации СНМ будем поддерживать следующие массивы:<tex>p[x]</tex> {{---}} массив "родителей", <tex>r[x]</tex> {{---}} массив рангов.===='''get'''==== '''function''' '''get'''(x: '''int'''): '''int''' '''if''' p[x] != x p[x] = get(p[x]) '''return''' p[x]
===='''union'''==== '''function''' '''union'''(x: '''int''', y: '''int'''): x = get(x) y = get(y) '''if''' x == y '''return''' '''if''' r[x] == r[y] r[x]++ '''if''' r[x] <tex>r[y] p[x]= y '''else''' p[y] = x Также возможна реализация функции <tex>\mathrm{get}</tex> без использования <tex>\mathrm{{---O(\log n)}} массив "родителей"</tex> дополнительной памяти.
<tex>r[x]</tex> {{---}} массив рангов.===='''get'''==== '''function''' '''get'''(x: '''int'''): '''int''' if p[x] ! root = x '''while''' p[xroot] != get(p[x])root return root = p[xroot====union=== i = union(x, y) x '''while''' p[i] != get(x)i y j = get(y) if x == y return; if r[x] == r[y] rp[xi]++ if r[x] < r[y] p[xi] = yroot elsei = j p[y] = x '''return''' root
==Асимптотика==
| ''<tex>\mathrm{get}</tex>'' || <tex>\mathrm{O(\log n)}</tex> || <tex>\mathrm{O(\mathrm{\alpha(m, n)})}</tex>
|-
| ''<tex>\mathrm{union}</tex>'' || <tex>\mathrm{O(1\log n)}</tex> || <tex>\mathrm{O(1\mathrm{\alpha(m, n)})}</tex>
|}
* Где <tex>m </tex> {{---}} общее количество операций * , <tex>n </tex> {{---}} полное количество элементов * , <tex>\mathrm{\alpha(m, n)}</tex> {{---}} функция, обратная к функции Аккермана (если <tex>m</tex> операций <tex>\mathrm{get }</tex> и <tex>n</tex> элементов).
Докажем, что если глубина множества (т.е. его ранг) равна <tex>k</tex>, то в нем содержится как минимум <tex>2^k</tex> элементов. Из этого свойства следует, что глубина множества с <tex>n</tex> элементами есть <tex>\mathrm{O(\log n)}</tex>, а значит и время работы операции <tex>\mathrm{get}</tex> является логарифмическим.
{| class="wikitable" border = 1
|-
!<tex>\mathbf{m \backslash n}</tex> !! <tex>\mathbf{0 }</tex> !! <tex>\mathbf{1 }</tex> !! <tex>\mathbf{2 }</tex> !! <tex>\mathbf{3 }</tex> !! <tex>\mathbf{4 }</tex> !! <tex>\mathbf{5}</tex>|- style = "text-align = center"| ! <tex>\mathbf{1 }</tex> || <tex>1 </tex> || <tex>2 </tex> || <tex>4 </tex> || <tex>8 </tex> || <tex>16 </tex> || <tex>32</tex>
|-
| ! <tex>\mathbf{2 }</tex> || <tex>2 </tex> || <tex>4 </tex> || <tex>16 </tex> || <tex>65536 </tex> || <tex>2^{2^{16}}</tex> || <tex>2^{2^{2^{16}}}</tex>
|-
| ! <tex>\mathbf{3 }</tex> || <tex>2 </tex> || <tex>16 </tex> || <tex>\underbrace{2^{2^{\cdot^{\cdot^{\cdot^2}}}}}_{17}</tex> || <tex>\underbrace{2^{2^{\cdot^{\cdot^{\cdot^2}}}}}_{A(3, 2)}</tex> || <tex>\cdots</tex> || <tex>\cdots</tex>
|-
| ! <tex>\mathbf{4 }</tex> || <tex>2 </tex> || <tex>\underbrace{2^{2^{\cdot^{\cdot^{\cdot^2}}}}}_{17}</tex> || <tex>\cdots</tex> || <tex>\cdots</tex> || <tex>\cdots</tex> || <tex>\cdots</tex>
|}
Функция, обратная функции Аккермана <tex>\mathrm{\alpha(m, n)}</tex>, равна минимальному <tex>i</tex> такому, что <tex>\mathrm{A \left (i, \left [\fracdfrac{m}{n} \right ] \right )} \geq geqslant \log n</tex>. Как видно из таблицы значений для функции Аккермана, обратная функция для любых значений, которые могут возникнуть при решении прикладных задач, не превышает 4, то есть можно считать, что операция <tex>\mathrm{get }</tex> выполняется за константное время.
===Анализ реализации с ранговой эвристикой===
===Анализ Проведем анализ реализации с ранговой эвристикой. Будем доказывать, что амортизационная стоимость <tex> \mathrm{get} =\mathrm{O(\log^{*}n)} </tex>.{{Определение|definition='''Итерированный логарифм''' (англ. ''Iterated logarithm'') <tex>\mathrm{\log^*n}</tex> — минимальное число логарифмирований <tex>n</tex>, необходимое для получения значения, не превосходящего <tex>1</tex>.}}'''Пример''': <tex>\mathrm{\log^*_2 16} =3</tex>
Проведем анализ реализации с ранговой эвристикой, будем доказывать более слабую оценку (итерированный логарифм Рассмотрим <tex> n </tex> операций <tex>\mathrm({union} </tex> и <tex> m </tex> операций <tex> \lg*n)mathrm{get} </tex>).Рассмотрим Можем считать, что число операций <tex> a \mathrm{union} </tex> равно числу элементов множества, так как количество операций <tex> \mathrm{union} </tex> и не превосходит количество элементов множества <tex>n</tex>. Заметим, что <tex> b m\geqslant n</tex> операций , так как при каждом вызове операции <tex> \mathrm{getunion} </tex> (дважды вызывается операция <tex> b > a \mathrm{get}</tex>).Не теряя общности, будем считать, что <tex> \mathrm{union } </tex> принимает в качестве аргументов представителей,
то есть <tex> \mathrm{union(v_1,v_2)} </tex> заменяем на <tex> \mathrm{union(get(v_1),get(v_2))} </tex>.
Оценим стоимость операции <tex> \mathrm{get(v)} </tex>.
Обозначим <tex> \mathrm{R(v)} </tex> — ранг вершины, <tex>\mathrm{P(v)}</tex> — представитель множества, содержащего <tex> \mathrm{v }</tex>,
<tex> \mathrm{L(v)} </tex> — отец вершины,
<tex> \mathrm{K(v)} </tex> — количество вершин в поддереве, корнем которого является <tex> \mathrm{v }</tex>.*<tex>\mathrm(\lg*n)</tex> - минимальное число логарифмирований <tex>n</tex>, необходимое для получения значения, не превосходящего <tex>1</tex>
{{Утверждение
|statement=
<tex> \mathrm{R(P(v))} > \geqslant \mathrm{R(v)} </tex>
|proof=
Из Если <tex>\mathrm{v}</tex> — представитель множества, то <tex>\mathrm{P(v)}=\mathrm{v}</tex> и <tex> \mathrm{R(P(v))} = \mathrm{R(v)} </tex>. Иначе, из принципа работы функции <tex> \mathrm{getunion} </tex> следует:
#<tex> \mathrm{R(L(v))}>\mathrm{R(v)} </tex>.
#Между <tex> \mathrm{v } </tex> и <tex> \mathrm{P(v)} </tex> существует путь вида: <tex> \mathrm{v } \rightarrow \mathrm{L(v)} \rightarrow \mathrm{L(L(v))} \rightarrow \dots \rightarrow \mathrm{P(v)} </tex>.
Записав неравенство из первого пункта вдоль пути из второго пункта, получаем требуемое.
}}
{{Утверждение
|statement=
<tex> \mathrm{R(v)} = i \Rightarrow {\mathrm{K(v)}} \geqslant {2^i }</tex>
|proof=
Докажем по индукции:
Из последнего утверждения следует:
#{{Утверждение|statement=<tex> \mathrm{R(v)} \le leqslant \log_2a log_2n </tex>.#}}{{Утверждение|statement=Количество вершин ранга <tex> i \le leqslant \dfrac{n} {a \over 2^i} </tex>.|proof=Если бы это было не так, то просуммировав количество вершин всех рангов, мы получили бы число большее <tex>n</tex>. Это противоречит условию, по которому <tex>n</tex> — число всех вершин. Значит утверждение верно.}}
{{Теорема
|statement=
Амортизационная стоимость <tex> \mathrm{get} = \mathrm{O(\log^{*}an)} </tex>
|proof=
Рассмотрим некоторое число <tex> x </tex>.
Разобьем наши ребра на три класса:
#Ведут Рассмотрим все вызовы функции <tex>\mathrm{get(u)}</tex>. В процессе выполнения каждой операции двигаемся вверх по одному из деревьев, заканчивая поиск в его корне. Если вершина <tex>u</tex> не корень или в сына и не сын корня.#, то во время рекурсивных вызовов функции <tex>\mathrm{get(u)}</tex> текущее значение <tex> \mathrm{R(PL(vu))} </tex> возрастает.Пусть <tex>m</tex> — количество вызовов операции <tex>\geqslant x^mathrm{get(u)}</tex>, <tex>n</tex> — количество вызовов операции <tex>\mathrm{Runion(v, u)}}</tex>, и <tex>m\geqslant n</tex>.#Все остальные.Разделим все вершины на <tex>4</tex> типа:
Обозначим эти классы :1. <tex> T_1u</tex> — корень. Таких вызовов <tex>\mathrm{get(u)}</tex> будет ровно <tex>m</tex>.:2. <tex>u</tex> — сын корня. Таких вызовов <tex>\mathrm{get(u)}</tex> будет не больше чем <tex>m</tex>.Оставшиеся вершины разделим на::3. Быстро растущие вызовы — такие что <tex>\mathrm{R(P(u))} \geqslant i^{\mathrm{R(u)}}</tex>, T_2, T_3 где <tex>i</tex> — число из диапазона <tex dpi="150">e ^{\frac{1}{e}} < i < 2</tex> <tex dpi="150">(e ^{\frac{1}{e}}\approx </tex> <tex>1.44</tex><tex dpi="150">)</tex>.:4. Медленно растущие вызовы — <tex>\mathrm{R(P(u))} < i^{\mathrm{R(u)}}</tex>.
Амортизационная стоимость Для первых двух типов вершин одна операция <tex>\mathrm{get(u)}</tex> работает за истинное время <tex>\mathrm{O(1)}</tex>, поэтому их суммарное время работы не превышает <tex>2\cdot m</tex>.
<center>При каждом вложенном вызове функции <tex>S = {\sum_{\mathrm{get}} \limits} ({\sum_{v:v \in \mathrm{getu)},v \in T_1} \limits 1}+</tex> для вершин третьего типа ранг по условию возрастает до <tex>i^{\sum_{v:v \in \mathrm{get},v \in T_2R(u)} \limits 1} + {</tex>. Ранг вершины может меняться в пределах от <tex>0</tex> до <tex>\sum_{v: \in log_2n</tex>. Значит количество рекурсивных вызовов равняется количеству возведений в степень <tex>\mathrm{getR(n)},v \in T_3} \limits 1} ) </ b tex> числа </tex>,i</centertex>,где необходимых для достижения числа <tex> {v \in \mathrm{get} } log_2n</tex> означает, . Или что реброто же самое, начало которого находится в количеству логарифмирований по основанию <tex> v i</tex>, было пройдено во время выполнения текущего числа <tex> \mathrm{get} log_2n</tex> для получения <tex>1</tex>. Ребро и еще одному логарифмированию для получения <tex> v 0</tex> эквивалентно вершине, в которой оно начинаетсяВ силу того, что Количество логарифмирований описывается функцией <texdpi="130">{\sum_log^*_{v:v i} \in left (\mathrm{get},v log_2 n \in T_1} right )</tex>. С учетом последнего логарифмирования формула примет вид <tex dpi="130">\limits 1log^*_{i} = n</tex>.Тогда время работы <tex>m</tex> быстро растущих вызовов равно <tex>\mathrm{O(1m\cdot \log^* n)} </tex> получаем:.
Поскольку количество вершин с рангом <tex>k</tex> не превышает число <tex>\dfrac{n}{2^k}</tex>, то суммарное время работы медленно растущих вызовов равно
<center>
<texdpi="150">S = \sum_u \limits i^{\mathrm{OR(1u)} + {}=\sum_{k=0}^{\mathrm{get}log n} \limits} ~ {\sum_{v:v \in \mathrm{get{R(u)}=k},v \in T_2} \limits} 1/b+ {i^k \leqslant \sum_{k=0}^{\mathrm{get}log n} \limitsi^k \cdot \frac{n} ~ {2^k} \leqslant n \cdot \sum_{v:v k=0}^{\in log n} \limits \mathrmdfrac{geti^k},v \in T_3{2^k} = \limitsmathrm{O(n)} 1 / b</tex>.
</center>
 Во В итоге получаем, что суммарное время работы операции <tex> \mathrm{get} </tex> после прохождения K ребер из второго класса <tex> \mathrm{R(v_1)} \geqslant x^{x^{.^{.^{.^{x^{R(vu)}}}}}} </tex>. Из выше сказанного и первого следствия второго утверждения получаем, что:<center>равняется <tex> {\sum_{v:v \in T = \mathrm{get},v \in T_2} \limits} \le \mathrm{\log^*_xO(\log_2am)} = + \mathrm{O(m\log^*a)}</tex>.</center> Для того, чтобы <tex> \mathrm{cdot \log^*_x(\log_2an)} </tex> существовал необходимо, чтобы <tex> x > e ^{ 1 /e } \approx 1,44 </tex>.  Рассмотрим сумму<center> <tex>{\sum_{+\mathrm{get}} \limits} ~ {\sum_{v:v \in \mathrm{get},v \in T_3} \limits} 1/b < {\sum_{\mathrm{get}} \limits} ~ {\sum_{v:v \in \mathrm{get},v \in T_3} \limits} 1/a </tex>. </center> Из первого утверждения и в силу использования сжатия путей следует,что <tex> \mathrm{R(PO(x)n)}</tex> cтрого увеличивается при переходе по ребру из <tex> T_3 </tex>. Как максимум через <tex> x^{= \mathrm{RO(k)}} </tex> переходов ребро перестанет появляться в классе <tex> T_3 </tex>. <center><tex>{m\sum_{cdot \mathrm{get}} \limits}~ {\sum_{v:v \in \mathrm{get},v \in T_3} \limits} 1/a = {\sum_v \limits ~\sum_{\mathrm{get}: in ~ this ~ \mathrm{get} ~ v \in T_3} \limits } 1/a \le \sum_v \limits xlog^{\mathrm{R(v*n + n)}} /a</tex>.</center> Из второго следствия второго утверждения следует:<center> С учетом того факта что <tex> {m\sum_{\mathrm{get}} \limits}~ {\sum_{v:v \in \mathrm{get},v \in T_3} \limits} 1/a \le \sum_{Rank=0}^{\log_2a} \limits {ax^{Rank} \over 2^{Rank} a}geqslant n</tex>.</center> При <tex> x < 2~</tex>:<center><tex>{\sum_{\mathrm{get}} \limits}~ {\sum_{v:v \in \mathrm{get},v \in T_3} \limits} 1/a\le\sum_{Rank=0}^{\log_2a} \limits {x^{Rank} \over 2^{Rank}}\le\sum_{Rank=0}^\infty \limits {x^{Rank} \over 2^{Rank}}\le{ 2 \over 2-x } = \mathrm{O(1)}амортизированное время работы равно </tex>.</center>   Итак <tex> S = \mathrm{O(1)} + \mathrm{O(\log^*xn)} + \mathrm{O(1)} = \mathrm{O(\log^*x)} </tex>.В силу того, что интервал <tex>(1.45; 2)</tex> не пустой, теорема доказана.
}}
 
==Ссылки==
* [http://habrahabr.ru/blogs/algorithm/104772/ Система непересекающихся множеств {{---}} описание этой реализации на habrahabr.ru]
* [http://ru.wikipedia.org/wiki/Функция_Аккермана Функция Аккермана {{---}} Википедия]
== См. также ==
* [[СНМ (наивные реализации)]]
== Литература Источники информации==* [http://habrahabr.ru/blogs/algorithm/104772/ Система непересекающихся множеств {{---}} описание этой реализации на habrahabr.ru]* [http://ru.wikipedia.org/wiki/Функция_Аккермана Функция Аккермана {{---}} Википедия]
* Томас Кормен, Чарльз Лейзерсон, Рональд Ривест, Клиффорд Штайн. Алгоритмы. Построение и анализ — Вильямс, 2010. - стр 589. — ISBN 978-5-8459-0857-4
 
[[Категория: Дискретная математика и алгоритмы]]
[[Категория: Структуры данных]]
1632
правки

Навигация