<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ru">
	<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Daryin</id>
	<title>Алговики - Вклад [ru]</title>
	<link rel="self" type="application/atom+xml" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Daryin"/>
	<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/ru/%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/Daryin"/>
	<updated>2026-10-23T03:39:12Z</updated>
	<subtitle>Вклад</subtitle>
	<generator>MediaWiki 1.43.5</generator>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%91%D0%B5%D0%BB%D0%BB%D0%BC%D0%B0%D0%BD%D0%B0-%D0%A4%D0%BE%D1%80%D0%B4%D0%B0&amp;diff=20154</id>
		<title>Алгоритм Беллмана-Форда</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%91%D0%B5%D0%BB%D0%BB%D0%BC%D0%B0%D0%BD%D0%B0-%D0%A4%D0%BE%D1%80%D0%B4%D0%B0&amp;diff=20154"/>
		<updated>2016-12-05T22:18:36Z</updated>

		<summary type="html">&lt;p&gt;Daryin: /* Масштабируемость алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм Беллмана-Форда&#039;&#039;&#039;&amp;lt;ref&amp;gt;Bellman, Richard. “On a Routing Problem.” Quarterly of Applied Mathematics 16 (1958): 87–90.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Ford, L R. Network Flow Theory. Rand.org, RAND Corporation, 1958.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Moore, Edward F. “The Shortest Path Through a Maze,” International Symposium on the Theory of Switching, 285–92, 1959.&amp;lt;/ref&amp;gt; предназначен для решения [[Поиск кратчайшего пути от одной вершины (SSSP)|задачи поиска кратчайшего пути на графе]]. Для заданного ориентированного взвешенного графа алгоритм находит кратчайшие расстояния от выделенной вершины-источника до всех остальных вершин графа. Алгоритм Беллмана-Форда масштабируется хуже других алгоритмов решения указанной задачи (сложность &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt; против &amp;lt;math&amp;gt;O(m + n\ln n)&amp;lt;/math&amp;gt; у [[Алгоритм Дейкстры|алгоритма Дейкстры]]), однако его отличительной особенностью является применимость к графам с произвольными, в том числе отрицательными, весами.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
Пусть задан граф &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; с весами рёбер &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt; и выделенной вершиной-источником &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;. Обозначим через &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt; кратчайшее расстояние от источника &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; до вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Алгоритм Беллмана-Форда ищет функцию &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt; как единственное решение уравнения&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
        d(v) = \min \{ d(w) + f(e) \mid e = (w, v) \in E \}, \quad \forall v \ne u,&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
с начальным условием &amp;lt;math&amp;gt;d(u) = 0&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основной операцией алгоритма является релаксация ребра: если &amp;lt;math&amp;gt;e = (w, v) \in E&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;d(v) &amp;gt; d(w) + f(e)&amp;lt;/math&amp;gt;, то производится присваивание &amp;lt;math&amp;gt;d(v) \leftarrow d(w) + f(e)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
Алгоритм последовательно уточняет значения функции &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* В самом начале производится присваивание &amp;lt;math&amp;gt;d(u) = 0&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;d(v) = \infty&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\forall v \ne u&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Далее происходит &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; итерация, в ходе каждой из которых производится релаксация всех рёбер графа.&lt;br /&gt;
&lt;br /&gt;
Структуру можно описать следующим образом:&lt;br /&gt;
&lt;br /&gt;
1.	Инициализация: всем вершинам присваивается предполагаемое расстояние &amp;lt;math&amp;gt;t(v)=\infty&amp;lt;/math&amp;gt;, кроме вершины-источника, для которой &amp;lt;math&amp;gt;t(u)=0&amp;lt;/math&amp;gt; .&lt;br /&gt;
&lt;br /&gt;
2.	Релаксация множества рёбер &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
а)	Для каждого ребра &amp;lt;math&amp;gt;e=(v,z) \in E&amp;lt;/math&amp;gt; вычисляется новое предполагаемое расстояние &amp;lt;math&amp;gt;t^&#039; (z)=t(v)+ w(e)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
б)	Если &amp;lt;math&amp;gt;t^&#039; (z)&amp;lt; t(z)&amp;lt;/math&amp;gt;, то происходит присваивание &amp;lt;math&amp;gt;t(z) := t&#039; (z)&amp;lt;/math&amp;gt; (релаксация ребра &amp;lt;math&amp;gt;e&amp;lt;/math&amp;gt; ).&lt;br /&gt;
&lt;br /&gt;
3.	Алгоритм производит релаксацию всех рёбер графа до тех пор, пока на очередной итерации происходит релаксация хотя бы одного ребра.&lt;br /&gt;
&lt;br /&gt;
Если на &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;-й итерации всё ещё производилась релаксацию рёбер, то в графе присутствует цикл отрицательной длины. Ребро &amp;lt;math&amp;gt;e=(v,z)&amp;lt;/math&amp;gt;, лежащее на таком цикле, может быть найдено проверкой следующего условия (проверяется для всех рёбер за линейное время): &amp;lt;math&amp;gt;t(v)+w(e)&amp;lt;d(z)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
Последовательный алгоритм реализуется следующим псевдокодом:&lt;br /&gt;
&lt;br /&gt;
 &#039;&#039;&#039;Входные данные&#039;&#039;&#039;:&lt;br /&gt;
   граф с вершинами &#039;&#039;V&#039;&#039;, рёбрами &#039;&#039;E&#039;&#039; с весами &#039;&#039;f&#039;&#039;(&#039;&#039;e&#039;&#039;);&lt;br /&gt;
   вершина-источник &#039;&#039;u&#039;&#039;.&lt;br /&gt;
 &#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: расстояния &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) до каждой вершины &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039; от вершины &#039;&#039;u&#039;&#039;.&lt;br /&gt;
 &lt;br /&gt;
 &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039; &#039;&#039;&#039;do&#039;&#039;&#039; &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) := ∞&lt;br /&gt;
 &#039;&#039;d&#039;&#039;(&#039;&#039;u&#039;&#039;) = 0&lt;br /&gt;
 &lt;br /&gt;
 &#039;&#039;&#039;for&#039;&#039;&#039; &#039;&#039;i&#039;&#039; &#039;&#039;&#039;from&#039;&#039;&#039; 1 &#039;&#039;&#039;to&#039;&#039;&#039; |&#039;&#039;V&#039;&#039;| - 1:&lt;br /&gt;
     &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;e&#039;&#039; = (&#039;&#039;w&#039;&#039;, &#039;&#039;v&#039;&#039;) ∈ &#039;&#039;E&#039;&#039;:&lt;br /&gt;
         &#039;&#039;&#039;if&#039;&#039;&#039; &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) &amp;gt; &#039;&#039;d&#039;&#039;(&#039;&#039;w&#039;&#039;) + &#039;&#039;f&#039;&#039;(&#039;&#039;e&#039;&#039;):&lt;br /&gt;
             &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) := &#039;&#039;d&#039;&#039;(&#039;&#039;w&#039;&#039;) + &#039;&#039;f&#039;&#039;(&#039;&#039;e&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Алгоритм выполняет &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; итерацию, на каждой из которых происходит релаксация &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; рёбер. Таким образом, общий объём работы составляет &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&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;
На рисунке 1 представлен информационный граф алгоритма, демонстрирующий описанные уровни параллелизма. На приведенном далее информационном графе нижний уровень параллелизма обозначен в горизонтальных плоскостях. Множество всех плоскостей представляет собой верхний уровень параллелизма (операции в каждой плоскости могут выполняться параллельно).&lt;br /&gt;
&lt;br /&gt;
Нижний уровень параллелизма на графе алгоритма расположен на уровнях {2 и 3}, соответствующим операциям инициализации массива дистанций (2) и обновления массива c использованием данных массива ребер {3}. Операция {4} - проверка того, были ли изменения на последней итерации и выход из цикла, если таковых не было.&lt;br /&gt;
&lt;br /&gt;
[[file:APSP.png|thumb|center|700px|Рисунок 1. Информационный граф обобщенного алгоритма Беллмана-Форда.]]&lt;br /&gt;
&lt;br /&gt;
Верхний уровень параллелизма, как уже говорилось, заключается в параллельном подсчете дистанций для различных вершин-источников, и на рисунке отмечен разными плоскостями.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
При использовании атомарных операций для вычисления минимума релаксация рёбер может производится параллельно. В этом случае потребуется &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; шагов при использовании &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt; процессоров.&lt;br /&gt;
&lt;br /&gt;
[[Алгоритм Δ-шагания]] может рассматриваться как параллельная версия алгоритма Беллмана-Форда.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: взвешенный граф &amp;lt;math&amp;gt;(V, E, W)&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин &amp;lt;math&amp;gt;v_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; рёбер &amp;lt;math&amp;gt;e_j = (v^{(1)}_{j}, v^{(2)}_{j})&amp;lt;/math&amp;gt; с весами &amp;lt;math&amp;gt;f_j&amp;lt;/math&amp;gt;), вершина-источник &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(m + n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039; (возможные варианты):&lt;br /&gt;
# для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного графа – последнее ребро &amp;lt;math&amp;gt;e^*_v = (w, v)&amp;lt;/math&amp;gt;, лежащее на кратчайшем пути от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, или соответствующая вершина &amp;lt;math&amp;gt;w&amp;lt;/math&amp;gt;;&lt;br /&gt;
# для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного графа – суммарный вес &amp;lt;math&amp;gt;f^*(v)&amp;lt;/math&amp;gt; кратчайшего пути от от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
	&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма===&lt;br /&gt;
&lt;br /&gt;
Алгоритм может распознавать наличие отрицательных циклов в графе. Ребро &amp;lt;math&amp;gt;e = (v, w)&amp;lt;/math&amp;gt; лежит на таком цикле, если вычисленные алгоритмом кратчайшие расстояния &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt; удовлетворяют условию&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
        d(v) + f(e) &amp;lt; d(w),&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
где &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt; – вес ребра &amp;lt;math&amp;gt;e&amp;lt;/math&amp;gt;. Условие может быть проверено для всех рёбер графа за время &amp;lt;math&amp;gt;O(m)&amp;lt;/math&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;
# чтение данных о ребре не должно вызывать промаха по кэшу, так как данные о рёбрах читаются последовательно&lt;br /&gt;
# чтение и обновление массива расстояний, напротив, почти наверняка вызовет два промаха по кэшу (по одному для каждого конца ребра)&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
Программа, реализующая алгоритм поиска кратчайших путей, состоит из двух частей: части, отвечающей за общую координацию вычислений, а так же параллельные вычисления на многоядерных CPU, и GPU части, отвечающей только за вычисления на графическом ускорителе.&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
==== Масштабируемость алгоритма ====&lt;br /&gt;
&lt;br /&gt;
Алгоритм обладает значительным потенциалом масштабируемости, так как каждое ребро обрабатывается независимо и можно поручить каждому вычислительному процессу свою часть рёбер графа. Узким местом является доступ к разделяемому всеми процессами массиву расстояний. Алгоритм позволяет ослабить требования к синхронизации данных этого массива между процессами (когда один процесс может не сразу увидеть новое значение расстояния, записанное другим процессом), за счёт, может быть, большего количества глобальных итераций.&lt;br /&gt;
&lt;br /&gt;
==== Масштабируемость реализации алгоритма ====&lt;br /&gt;
Проведём исследование масштабируемости параллельной реализации алгоритма Беллмана-Форда согласно [[Scalability methodology|методике]]. Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов-2 [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Набор и границы значений изменяемых [[Глоссарий#Параметры запуска|параметров запуска]] реализации алгоритма: &lt;br /&gt;
&lt;br /&gt;
*	число процессоров [1 : 28] с шагом 1;&lt;br /&gt;
*	размер графа [2^20 : 2^27].&lt;br /&gt;
&lt;br /&gt;
Проведем отдельные исследования сильной масштабируемости вширь реализации алгоритма Беллмана-Форда.&lt;br /&gt;
&lt;br /&gt;
Производительность определена как TEPS (от англ. Traversed Edges Per Second), то есть число ребер графа, который алгоритм обрабатывает в секунду. С помощью данной метрики можно сравнивать производительность для различных размеров графа, оценивая, насколько понижается эффективность обработки графа при увеличении его размера.&lt;br /&gt;
&lt;br /&gt;
[[file:APSP scaling wide.png|thumb|center|700px|Рисунок 2. Параллельная реализация алгоритма Беллмана-Форда масштабируемость различных версий реализации алгоритма: производительность в зависимости от размера графа]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
* C++: [http://www.boost.org/libs/graph/doc/ Boost Graph Library] (функция &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph/doc/bellman_ford_shortest.html bellman_ford_shortest]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Python: [https://networkx.github.io NetworkX] (функция &amp;lt;code&amp;gt;[http://networkx.github.io/documentation/networkx-1.9.1/reference/generated/networkx.algorithms.shortest_paths.weighted.bellman_ford.html bellman_ford]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Java: [http://jgrapht.org JGraphT] (класс &amp;lt;code&amp;gt;[http://jgrapht.org/javadoc/org/jgrapht/alg/BellmanFordShortestPath.html BellmanFordShortestPath]&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Начатые статьи]]&lt;/div&gt;</summary>
		<author><name>Daryin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%91%D0%B5%D0%BB%D0%BB%D0%BC%D0%B0%D0%BD%D0%B0-%D0%A4%D0%BE%D1%80%D0%B4%D0%B0&amp;diff=20153</id>
		<title>Алгоритм Беллмана-Форда</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%91%D0%B5%D0%BB%D0%BB%D0%BC%D0%B0%D0%BD%D0%B0-%D0%A4%D0%BE%D1%80%D0%B4%D0%B0&amp;diff=20153"/>
		<updated>2016-12-05T22:14:10Z</updated>

		<summary type="html">&lt;p&gt;Daryin: /* Количественная оценка локальности */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм Беллмана-Форда&#039;&#039;&#039;&amp;lt;ref&amp;gt;Bellman, Richard. “On a Routing Problem.” Quarterly of Applied Mathematics 16 (1958): 87–90.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Ford, L R. Network Flow Theory. Rand.org, RAND Corporation, 1958.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Moore, Edward F. “The Shortest Path Through a Maze,” International Symposium on the Theory of Switching, 285–92, 1959.&amp;lt;/ref&amp;gt; предназначен для решения [[Поиск кратчайшего пути от одной вершины (SSSP)|задачи поиска кратчайшего пути на графе]]. Для заданного ориентированного взвешенного графа алгоритм находит кратчайшие расстояния от выделенной вершины-источника до всех остальных вершин графа. Алгоритм Беллмана-Форда масштабируется хуже других алгоритмов решения указанной задачи (сложность &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt; против &amp;lt;math&amp;gt;O(m + n\ln n)&amp;lt;/math&amp;gt; у [[Алгоритм Дейкстры|алгоритма Дейкстры]]), однако его отличительной особенностью является применимость к графам с произвольными, в том числе отрицательными, весами.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
Пусть задан граф &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; с весами рёбер &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt; и выделенной вершиной-источником &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;. Обозначим через &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt; кратчайшее расстояние от источника &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; до вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Алгоритм Беллмана-Форда ищет функцию &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt; как единственное решение уравнения&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
        d(v) = \min \{ d(w) + f(e) \mid e = (w, v) \in E \}, \quad \forall v \ne u,&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
с начальным условием &amp;lt;math&amp;gt;d(u) = 0&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основной операцией алгоритма является релаксация ребра: если &amp;lt;math&amp;gt;e = (w, v) \in E&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;d(v) &amp;gt; d(w) + f(e)&amp;lt;/math&amp;gt;, то производится присваивание &amp;lt;math&amp;gt;d(v) \leftarrow d(w) + f(e)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
Алгоритм последовательно уточняет значения функции &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* В самом начале производится присваивание &amp;lt;math&amp;gt;d(u) = 0&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;d(v) = \infty&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\forall v \ne u&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Далее происходит &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; итерация, в ходе каждой из которых производится релаксация всех рёбер графа.&lt;br /&gt;
&lt;br /&gt;
Структуру можно описать следующим образом:&lt;br /&gt;
&lt;br /&gt;
1.	Инициализация: всем вершинам присваивается предполагаемое расстояние &amp;lt;math&amp;gt;t(v)=\infty&amp;lt;/math&amp;gt;, кроме вершины-источника, для которой &amp;lt;math&amp;gt;t(u)=0&amp;lt;/math&amp;gt; .&lt;br /&gt;
&lt;br /&gt;
2.	Релаксация множества рёбер &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
а)	Для каждого ребра &amp;lt;math&amp;gt;e=(v,z) \in E&amp;lt;/math&amp;gt; вычисляется новое предполагаемое расстояние &amp;lt;math&amp;gt;t^&#039; (z)=t(v)+ w(e)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
б)	Если &amp;lt;math&amp;gt;t^&#039; (z)&amp;lt; t(z)&amp;lt;/math&amp;gt;, то происходит присваивание &amp;lt;math&amp;gt;t(z) := t&#039; (z)&amp;lt;/math&amp;gt; (релаксация ребра &amp;lt;math&amp;gt;e&amp;lt;/math&amp;gt; ).&lt;br /&gt;
&lt;br /&gt;
3.	Алгоритм производит релаксацию всех рёбер графа до тех пор, пока на очередной итерации происходит релаксация хотя бы одного ребра.&lt;br /&gt;
&lt;br /&gt;
Если на &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;-й итерации всё ещё производилась релаксацию рёбер, то в графе присутствует цикл отрицательной длины. Ребро &amp;lt;math&amp;gt;e=(v,z)&amp;lt;/math&amp;gt;, лежащее на таком цикле, может быть найдено проверкой следующего условия (проверяется для всех рёбер за линейное время): &amp;lt;math&amp;gt;t(v)+w(e)&amp;lt;d(z)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
Последовательный алгоритм реализуется следующим псевдокодом:&lt;br /&gt;
&lt;br /&gt;
 &#039;&#039;&#039;Входные данные&#039;&#039;&#039;:&lt;br /&gt;
   граф с вершинами &#039;&#039;V&#039;&#039;, рёбрами &#039;&#039;E&#039;&#039; с весами &#039;&#039;f&#039;&#039;(&#039;&#039;e&#039;&#039;);&lt;br /&gt;
   вершина-источник &#039;&#039;u&#039;&#039;.&lt;br /&gt;
 &#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: расстояния &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) до каждой вершины &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039; от вершины &#039;&#039;u&#039;&#039;.&lt;br /&gt;
 &lt;br /&gt;
 &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039; &#039;&#039;&#039;do&#039;&#039;&#039; &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) := ∞&lt;br /&gt;
 &#039;&#039;d&#039;&#039;(&#039;&#039;u&#039;&#039;) = 0&lt;br /&gt;
 &lt;br /&gt;
 &#039;&#039;&#039;for&#039;&#039;&#039; &#039;&#039;i&#039;&#039; &#039;&#039;&#039;from&#039;&#039;&#039; 1 &#039;&#039;&#039;to&#039;&#039;&#039; |&#039;&#039;V&#039;&#039;| - 1:&lt;br /&gt;
     &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;e&#039;&#039; = (&#039;&#039;w&#039;&#039;, &#039;&#039;v&#039;&#039;) ∈ &#039;&#039;E&#039;&#039;:&lt;br /&gt;
         &#039;&#039;&#039;if&#039;&#039;&#039; &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) &amp;gt; &#039;&#039;d&#039;&#039;(&#039;&#039;w&#039;&#039;) + &#039;&#039;f&#039;&#039;(&#039;&#039;e&#039;&#039;):&lt;br /&gt;
             &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) := &#039;&#039;d&#039;&#039;(&#039;&#039;w&#039;&#039;) + &#039;&#039;f&#039;&#039;(&#039;&#039;e&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Алгоритм выполняет &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; итерацию, на каждой из которых происходит релаксация &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; рёбер. Таким образом, общий объём работы составляет &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&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;
На рисунке 1 представлен информационный граф алгоритма, демонстрирующий описанные уровни параллелизма. На приведенном далее информационном графе нижний уровень параллелизма обозначен в горизонтальных плоскостях. Множество всех плоскостей представляет собой верхний уровень параллелизма (операции в каждой плоскости могут выполняться параллельно).&lt;br /&gt;
&lt;br /&gt;
Нижний уровень параллелизма на графе алгоритма расположен на уровнях {2 и 3}, соответствующим операциям инициализации массива дистанций (2) и обновления массива c использованием данных массива ребер {3}. Операция {4} - проверка того, были ли изменения на последней итерации и выход из цикла, если таковых не было.&lt;br /&gt;
&lt;br /&gt;
[[file:APSP.png|thumb|center|700px|Рисунок 1. Информационный граф обобщенного алгоритма Беллмана-Форда.]]&lt;br /&gt;
&lt;br /&gt;
Верхний уровень параллелизма, как уже говорилось, заключается в параллельном подсчете дистанций для различных вершин-источников, и на рисунке отмечен разными плоскостями.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
При использовании атомарных операций для вычисления минимума релаксация рёбер может производится параллельно. В этом случае потребуется &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; шагов при использовании &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt; процессоров.&lt;br /&gt;
&lt;br /&gt;
[[Алгоритм Δ-шагания]] может рассматриваться как параллельная версия алгоритма Беллмана-Форда.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: взвешенный граф &amp;lt;math&amp;gt;(V, E, W)&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин &amp;lt;math&amp;gt;v_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; рёбер &amp;lt;math&amp;gt;e_j = (v^{(1)}_{j}, v^{(2)}_{j})&amp;lt;/math&amp;gt; с весами &amp;lt;math&amp;gt;f_j&amp;lt;/math&amp;gt;), вершина-источник &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(m + n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039; (возможные варианты):&lt;br /&gt;
# для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного графа – последнее ребро &amp;lt;math&amp;gt;e^*_v = (w, v)&amp;lt;/math&amp;gt;, лежащее на кратчайшем пути от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, или соответствующая вершина &amp;lt;math&amp;gt;w&amp;lt;/math&amp;gt;;&lt;br /&gt;
# для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного графа – суммарный вес &amp;lt;math&amp;gt;f^*(v)&amp;lt;/math&amp;gt; кратчайшего пути от от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
	&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма===&lt;br /&gt;
&lt;br /&gt;
Алгоритм может распознавать наличие отрицательных циклов в графе. Ребро &amp;lt;math&amp;gt;e = (v, w)&amp;lt;/math&amp;gt; лежит на таком цикле, если вычисленные алгоритмом кратчайшие расстояния &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt; удовлетворяют условию&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
        d(v) + f(e) &amp;lt; d(w),&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
где &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt; – вес ребра &amp;lt;math&amp;gt;e&amp;lt;/math&amp;gt;. Условие может быть проверено для всех рёбер графа за время &amp;lt;math&amp;gt;O(m)&amp;lt;/math&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;
# чтение данных о ребре не должно вызывать промаха по кэшу, так как данные о рёбрах читаются последовательно&lt;br /&gt;
# чтение и обновление массива расстояний, напротив, почти наверняка вызовет два промаха по кэшу (по одному для каждого конца ребра)&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
Программа, реализующая алгоритм поиска кратчайших путей, состоит из двух частей: части, отвечающей за общую координацию вычислений, а так же параллельные вычисления на многоядерных CPU, и GPU части, отвечающей только за вычисления на графическом ускорителе.&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
==== Масштабируемость алгоритма ====&lt;br /&gt;
==== Масштабируемость реализации алгоритма ====&lt;br /&gt;
Проведём исследование масштабируемости параллельной реализации алгоритма Беллмана-Форда согласно [[Scalability methodology|методике]]. Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов-2 [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Набор и границы значений изменяемых [[Глоссарий#Параметры запуска|параметров запуска]] реализации алгоритма: &lt;br /&gt;
&lt;br /&gt;
*	число процессоров [1 : 28] с шагом 1;&lt;br /&gt;
*	размер графа [2^20 : 2^27].&lt;br /&gt;
&lt;br /&gt;
Проведем отдельные исследования сильной масштабируемости вширь реализации алгоритма Беллмана-Форда.&lt;br /&gt;
&lt;br /&gt;
Производительность определена как TEPS (от англ. Traversed Edges Per Second), то есть число ребер графа, который алгоритм обрабатывает в секунду. С помощью данной метрики можно сравнивать производительность для различных размеров графа, оценивая, насколько понижается эффективность обработки графа при увеличении его размера.&lt;br /&gt;
&lt;br /&gt;
[[file:APSP scaling wide.png|thumb|center|700px|Рисунок 2. Параллельная реализация алгоритма Беллмана-Форда масштабируемость различных версий реализации алгоритма: производительность в зависимости от размера графа]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
* C++: [http://www.boost.org/libs/graph/doc/ Boost Graph Library] (функция &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph/doc/bellman_ford_shortest.html bellman_ford_shortest]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Python: [https://networkx.github.io NetworkX] (функция &amp;lt;code&amp;gt;[http://networkx.github.io/documentation/networkx-1.9.1/reference/generated/networkx.algorithms.shortest_paths.weighted.bellman_ford.html bellman_ford]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Java: [http://jgrapht.org JGraphT] (класс &amp;lt;code&amp;gt;[http://jgrapht.org/javadoc/org/jgrapht/alg/BellmanFordShortestPath.html BellmanFordShortestPath]&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Начатые статьи]]&lt;/div&gt;</summary>
		<author><name>Daryin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%91%D0%B5%D0%BB%D0%BB%D0%BC%D0%B0%D0%BD%D0%B0-%D0%A4%D0%BE%D1%80%D0%B4%D0%B0&amp;diff=20152</id>
		<title>Алгоритм Беллмана-Форда</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%91%D0%B5%D0%BB%D0%BB%D0%BC%D0%B0%D0%BD%D0%B0-%D0%A4%D0%BE%D1%80%D0%B4%D0%B0&amp;diff=20152"/>
		<updated>2016-12-05T22:12:13Z</updated>

		<summary type="html">&lt;p&gt;Daryin: /* Структура обращений в память и качественная оценка локальности */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм Беллмана-Форда&#039;&#039;&#039;&amp;lt;ref&amp;gt;Bellman, Richard. “On a Routing Problem.” Quarterly of Applied Mathematics 16 (1958): 87–90.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Ford, L R. Network Flow Theory. Rand.org, RAND Corporation, 1958.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Moore, Edward F. “The Shortest Path Through a Maze,” International Symposium on the Theory of Switching, 285–92, 1959.&amp;lt;/ref&amp;gt; предназначен для решения [[Поиск кратчайшего пути от одной вершины (SSSP)|задачи поиска кратчайшего пути на графе]]. Для заданного ориентированного взвешенного графа алгоритм находит кратчайшие расстояния от выделенной вершины-источника до всех остальных вершин графа. Алгоритм Беллмана-Форда масштабируется хуже других алгоритмов решения указанной задачи (сложность &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt; против &amp;lt;math&amp;gt;O(m + n\ln n)&amp;lt;/math&amp;gt; у [[Алгоритм Дейкстры|алгоритма Дейкстры]]), однако его отличительной особенностью является применимость к графам с произвольными, в том числе отрицательными, весами.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
Пусть задан граф &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; с весами рёбер &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt; и выделенной вершиной-источником &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;. Обозначим через &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt; кратчайшее расстояние от источника &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; до вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Алгоритм Беллмана-Форда ищет функцию &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt; как единственное решение уравнения&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
        d(v) = \min \{ d(w) + f(e) \mid e = (w, v) \in E \}, \quad \forall v \ne u,&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
с начальным условием &amp;lt;math&amp;gt;d(u) = 0&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основной операцией алгоритма является релаксация ребра: если &amp;lt;math&amp;gt;e = (w, v) \in E&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;d(v) &amp;gt; d(w) + f(e)&amp;lt;/math&amp;gt;, то производится присваивание &amp;lt;math&amp;gt;d(v) \leftarrow d(w) + f(e)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
Алгоритм последовательно уточняет значения функции &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* В самом начале производится присваивание &amp;lt;math&amp;gt;d(u) = 0&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;d(v) = \infty&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\forall v \ne u&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Далее происходит &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; итерация, в ходе каждой из которых производится релаксация всех рёбер графа.&lt;br /&gt;
&lt;br /&gt;
Структуру можно описать следующим образом:&lt;br /&gt;
&lt;br /&gt;
1.	Инициализация: всем вершинам присваивается предполагаемое расстояние &amp;lt;math&amp;gt;t(v)=\infty&amp;lt;/math&amp;gt;, кроме вершины-источника, для которой &amp;lt;math&amp;gt;t(u)=0&amp;lt;/math&amp;gt; .&lt;br /&gt;
&lt;br /&gt;
2.	Релаксация множества рёбер &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
а)	Для каждого ребра &amp;lt;math&amp;gt;e=(v,z) \in E&amp;lt;/math&amp;gt; вычисляется новое предполагаемое расстояние &amp;lt;math&amp;gt;t^&#039; (z)=t(v)+ w(e)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
б)	Если &amp;lt;math&amp;gt;t^&#039; (z)&amp;lt; t(z)&amp;lt;/math&amp;gt;, то происходит присваивание &amp;lt;math&amp;gt;t(z) := t&#039; (z)&amp;lt;/math&amp;gt; (релаксация ребра &amp;lt;math&amp;gt;e&amp;lt;/math&amp;gt; ).&lt;br /&gt;
&lt;br /&gt;
3.	Алгоритм производит релаксацию всех рёбер графа до тех пор, пока на очередной итерации происходит релаксация хотя бы одного ребра.&lt;br /&gt;
&lt;br /&gt;
Если на &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;-й итерации всё ещё производилась релаксацию рёбер, то в графе присутствует цикл отрицательной длины. Ребро &amp;lt;math&amp;gt;e=(v,z)&amp;lt;/math&amp;gt;, лежащее на таком цикле, может быть найдено проверкой следующего условия (проверяется для всех рёбер за линейное время): &amp;lt;math&amp;gt;t(v)+w(e)&amp;lt;d(z)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
Последовательный алгоритм реализуется следующим псевдокодом:&lt;br /&gt;
&lt;br /&gt;
 &#039;&#039;&#039;Входные данные&#039;&#039;&#039;:&lt;br /&gt;
   граф с вершинами &#039;&#039;V&#039;&#039;, рёбрами &#039;&#039;E&#039;&#039; с весами &#039;&#039;f&#039;&#039;(&#039;&#039;e&#039;&#039;);&lt;br /&gt;
   вершина-источник &#039;&#039;u&#039;&#039;.&lt;br /&gt;
 &#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: расстояния &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) до каждой вершины &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039; от вершины &#039;&#039;u&#039;&#039;.&lt;br /&gt;
 &lt;br /&gt;
 &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039; &#039;&#039;&#039;do&#039;&#039;&#039; &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) := ∞&lt;br /&gt;
 &#039;&#039;d&#039;&#039;(&#039;&#039;u&#039;&#039;) = 0&lt;br /&gt;
 &lt;br /&gt;
 &#039;&#039;&#039;for&#039;&#039;&#039; &#039;&#039;i&#039;&#039; &#039;&#039;&#039;from&#039;&#039;&#039; 1 &#039;&#039;&#039;to&#039;&#039;&#039; |&#039;&#039;V&#039;&#039;| - 1:&lt;br /&gt;
     &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;e&#039;&#039; = (&#039;&#039;w&#039;&#039;, &#039;&#039;v&#039;&#039;) ∈ &#039;&#039;E&#039;&#039;:&lt;br /&gt;
         &#039;&#039;&#039;if&#039;&#039;&#039; &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) &amp;gt; &#039;&#039;d&#039;&#039;(&#039;&#039;w&#039;&#039;) + &#039;&#039;f&#039;&#039;(&#039;&#039;e&#039;&#039;):&lt;br /&gt;
             &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) := &#039;&#039;d&#039;&#039;(&#039;&#039;w&#039;&#039;) + &#039;&#039;f&#039;&#039;(&#039;&#039;e&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Алгоритм выполняет &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; итерацию, на каждой из которых происходит релаксация &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; рёбер. Таким образом, общий объём работы составляет &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&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;
На рисунке 1 представлен информационный граф алгоритма, демонстрирующий описанные уровни параллелизма. На приведенном далее информационном графе нижний уровень параллелизма обозначен в горизонтальных плоскостях. Множество всех плоскостей представляет собой верхний уровень параллелизма (операции в каждой плоскости могут выполняться параллельно).&lt;br /&gt;
&lt;br /&gt;
Нижний уровень параллелизма на графе алгоритма расположен на уровнях {2 и 3}, соответствующим операциям инициализации массива дистанций (2) и обновления массива c использованием данных массива ребер {3}. Операция {4} - проверка того, были ли изменения на последней итерации и выход из цикла, если таковых не было.&lt;br /&gt;
&lt;br /&gt;
[[file:APSP.png|thumb|center|700px|Рисунок 1. Информационный граф обобщенного алгоритма Беллмана-Форда.]]&lt;br /&gt;
&lt;br /&gt;
Верхний уровень параллелизма, как уже говорилось, заключается в параллельном подсчете дистанций для различных вершин-источников, и на рисунке отмечен разными плоскостями.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
При использовании атомарных операций для вычисления минимума релаксация рёбер может производится параллельно. В этом случае потребуется &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; шагов при использовании &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt; процессоров.&lt;br /&gt;
&lt;br /&gt;
[[Алгоритм Δ-шагания]] может рассматриваться как параллельная версия алгоритма Беллмана-Форда.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: взвешенный граф &amp;lt;math&amp;gt;(V, E, W)&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин &amp;lt;math&amp;gt;v_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; рёбер &amp;lt;math&amp;gt;e_j = (v^{(1)}_{j}, v^{(2)}_{j})&amp;lt;/math&amp;gt; с весами &amp;lt;math&amp;gt;f_j&amp;lt;/math&amp;gt;), вершина-источник &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(m + n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039; (возможные варианты):&lt;br /&gt;
# для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного графа – последнее ребро &amp;lt;math&amp;gt;e^*_v = (w, v)&amp;lt;/math&amp;gt;, лежащее на кратчайшем пути от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, или соответствующая вершина &amp;lt;math&amp;gt;w&amp;lt;/math&amp;gt;;&lt;br /&gt;
# для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного графа – суммарный вес &amp;lt;math&amp;gt;f^*(v)&amp;lt;/math&amp;gt; кратчайшего пути от от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
	&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма===&lt;br /&gt;
&lt;br /&gt;
Алгоритм может распознавать наличие отрицательных циклов в графе. Ребро &amp;lt;math&amp;gt;e = (v, w)&amp;lt;/math&amp;gt; лежит на таком цикле, если вычисленные алгоритмом кратчайшие расстояния &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt; удовлетворяют условию&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
        d(v) + f(e) &amp;lt; d(w),&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
где &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt; – вес ребра &amp;lt;math&amp;gt;e&amp;lt;/math&amp;gt;. Условие может быть проверено для всех рёбер графа за время &amp;lt;math&amp;gt;O(m)&amp;lt;/math&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;
Программа, реализующая алгоритм поиска кратчайших путей, состоит из двух частей: части, отвечающей за общую координацию вычислений, а так же параллельные вычисления на многоядерных CPU, и GPU части, отвечающей только за вычисления на графическом ускорителе.&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
==== Масштабируемость алгоритма ====&lt;br /&gt;
==== Масштабируемость реализации алгоритма ====&lt;br /&gt;
Проведём исследование масштабируемости параллельной реализации алгоритма Беллмана-Форда согласно [[Scalability methodology|методике]]. Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов-2 [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Набор и границы значений изменяемых [[Глоссарий#Параметры запуска|параметров запуска]] реализации алгоритма: &lt;br /&gt;
&lt;br /&gt;
*	число процессоров [1 : 28] с шагом 1;&lt;br /&gt;
*	размер графа [2^20 : 2^27].&lt;br /&gt;
&lt;br /&gt;
Проведем отдельные исследования сильной масштабируемости вширь реализации алгоритма Беллмана-Форда.&lt;br /&gt;
&lt;br /&gt;
Производительность определена как TEPS (от англ. Traversed Edges Per Second), то есть число ребер графа, который алгоритм обрабатывает в секунду. С помощью данной метрики можно сравнивать производительность для различных размеров графа, оценивая, насколько понижается эффективность обработки графа при увеличении его размера.&lt;br /&gt;
&lt;br /&gt;
[[file:APSP scaling wide.png|thumb|center|700px|Рисунок 2. Параллельная реализация алгоритма Беллмана-Форда масштабируемость различных версий реализации алгоритма: производительность в зависимости от размера графа]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
* C++: [http://www.boost.org/libs/graph/doc/ Boost Graph Library] (функция &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph/doc/bellman_ford_shortest.html bellman_ford_shortest]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Python: [https://networkx.github.io NetworkX] (функция &amp;lt;code&amp;gt;[http://networkx.github.io/documentation/networkx-1.9.1/reference/generated/networkx.algorithms.shortest_paths.weighted.bellman_ford.html bellman_ford]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Java: [http://jgrapht.org JGraphT] (класс &amp;lt;code&amp;gt;[http://jgrapht.org/javadoc/org/jgrapht/alg/BellmanFordShortestPath.html BellmanFordShortestPath]&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Начатые статьи]]&lt;/div&gt;</summary>
		<author><name>Daryin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%91%D0%BE%D1%80%D1%83%D0%B2%D0%BA%D0%B8&amp;diff=20151</id>
		<title>Алгоритм Борувки</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%91%D0%BE%D1%80%D1%83%D0%B2%D0%BA%D0%B8&amp;diff=20151"/>
		<updated>2016-12-05T22:08:55Z</updated>

		<summary type="html">&lt;p&gt;Daryin: /* Масштабируемость алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм Борувки&#039;&#039;&#039;&amp;lt;ref&amp;gt;Borůvka, Otakar. “O Jistém Problému Minimálním.” Práce Moravské Přírodovědecké Společnosti III, no. 3 (1926): 37–58.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Jarník, Vojtěch. “O Jistém Problému Minimálním (Z Dopisu Panu O. Borůvkovi).” Práce Moravské Přírodovědecké Společnosti 6, no. 4 (1930): 57–63.&amp;lt;/ref&amp;gt; предназначен для решения [[Построение минимального остовного дерева (MST)|задачи о построении минимального остовного дерева]] во взвешенном неориентированном графе. Алгоритм хорошо параллелизуется и является основой для распределённого [[Алгоритм GHS|алгоритма GHS]].&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Пусть задан связный неориентированный граф &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; с весами рёбер &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt;. Предполагается, что веса всех рёбер различны (если это не так, то можно упорядочить рёбра сначала по весу, а потом по номеру).&lt;br /&gt;
&lt;br /&gt;
Алгоритм Борувки основан на следующих двух свойствах задачи:&lt;br /&gt;
* &#039;&#039;&#039;Минимальное ребро фрагмента&#039;&#039;&#039;. Пусть &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; – фрагмент минимального остовного дерева и &amp;lt;math&amp;gt;e_F&amp;lt;/math&amp;gt; – ребро наименьшего веса, исходящее из &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; (т.е. ровно один его конец является вершиной из &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt;). Если ребро &amp;lt;math&amp;gt;e_F&amp;lt;/math&amp;gt; единственно, то оно принадлежит минимальному остовному дереву.&lt;br /&gt;
* &#039;&#039;&#039;Схлопывание фрагментов&#039;&#039;&#039;. Пусть &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; – фрагмент минимального остовного дерева графа &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;, а граф &amp;lt;math&amp;gt;G&#039;&amp;lt;/math&amp;gt; получен из &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt; склеиванием вершин, принадлежащих &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt;. Тогда объединение &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; и минимального остовного дерева графа &amp;lt;math&amp;gt;G&#039;&amp;lt;/math&amp;gt; даёт минимальное остовное дерево исходного графа &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В начале работы алгоритма каждая вершина графа &amp;lt;math&amp;gt;G&amp;lt;/math&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;
Пусть задан связный граф &amp;lt;math&amp;gt;G=(V,E)&amp;lt;/math&amp;gt; с вершинами &amp;lt;math&amp;gt;V = ( v_{1}, v_{2}, ..., v_{n} )&amp;lt;/math&amp;gt; и рёбрами &amp;lt;math&amp;gt;E = ( e_{1}, e_{2}, ..., e_{m} )&amp;lt;/math&amp;gt;. Каждому ребру &amp;lt;math&amp;gt;e \in E&amp;lt;/math&amp;gt; приписан вес &amp;lt;math&amp;gt;w(e)&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Требуется построить дерево &amp;lt;math&amp;gt;T^* \subseteq E&amp;lt;/math&amp;gt;, связывающее все вершины, и имеющее наименьший возможный вес среди всех таких деревьев:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; w(T^* )= \min_T( w(T)) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
где вес множества рёбер есть сумма их весов:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;w(T)=\sum_{e \in T} (w(T))&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если граф &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt; не является связным, то дерева, связывающего все вершины, не существует. &lt;br /&gt;
&lt;br /&gt;
В этом случае необходимо найти минимальной остовное дерево для каждой компоненты связности &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;. Набор таких деревьев называется минимальным остовным лесом (сокращённо MSF – Minimum Spanning Forest).&lt;br /&gt;
&lt;br /&gt;
==== Вспомогательный алгоритм: система непересекающихся множеств (Union-Find)====&lt;br /&gt;
&lt;br /&gt;
Во всех алгоритмах решения задачи требуется отслеживать, каким уже построенным фрагментам дерева принадлежат те или иные вершины графа. Для этого используется структура данных «система непересекающихся множеств» (Union-Find). Данная структура поддерживает две операции:&lt;br /&gt;
&lt;br /&gt;
1.	&amp;lt;math&amp;gt;FIND(v) = w&amp;lt;/math&amp;gt; – по вершине v возвращает вершину w – «корень» фрагмента, которому принадлежит вершина v. При этом гарантируется, что вершины u и v принадлежат одному и тому же фрагменту, тогда и только тогда, когда &amp;lt;math&amp;gt;FIND(u) = FIND(v)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
2.	&amp;lt;math&amp;gt;MERGE(u, v)&amp;lt;/math&amp;gt; – объединяет два фрагмента, которым принадлежат вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v.&amp;lt;/math&amp;gt; (Если они уже лежат в одном фрагменте, то ничего не происходит.) При практической реализации удобно, чтобы данная операция возвращала значение истина, если объединение фрагментов имело место, и ложь в противном случае.&lt;br /&gt;
&lt;br /&gt;
==== Последовательная версия====&lt;br /&gt;
&lt;br /&gt;
Классический последовательный алгоритм Union-Find описан в статье Тарьяна. Каждой вершине v приписывается указатель на вершину-родителя &amp;lt;math&amp;gt;parent(v)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
1.	Изначально &amp;lt;math&amp;gt;parent(v) := v&amp;lt;/math&amp;gt; для всех вершин.&lt;br /&gt;
&lt;br /&gt;
2.	&amp;lt;math&amp;gt;FIND(v)&amp;lt;/math&amp;gt; выполняется следующим образом: полагаем &amp;lt;math&amp;gt;u := v&amp;lt;/math&amp;gt;, и далее следуем по указателям &amp;lt;math&amp;gt;u := parent(u)&amp;lt;/math&amp;gt; до тех пор, пока не станет &amp;lt;math&amp;gt;u = parent(u)&amp;lt;/math&amp;gt;. Это и будет результат операции. Дополнительно можно «схлопывать» дерево: присвоить всем посещённым вершинами: &amp;lt;math&amp;gt;parent(u_i) := u&amp;lt;/math&amp;gt;, либо производить схлопывание по пути: &amp;lt;math&amp;gt;parent(u) := parent(parent(u)))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
3.	&amp;lt;math&amp;gt;MERGE(u, v)&amp;lt;/math&amp;gt; выполняется следующим образом: вначале находим корневые вершины &amp;lt;math&amp;gt;u := FIND(u), v := FIND(v)&amp;lt;/math&amp;gt;. Если &amp;lt;math&amp;gt;u = v&amp;lt;/math&amp;gt;, то исходные вершины принадлежат одному фрагменту и объединения фрагментов не происходит. В противном случае полагаем одно из &amp;lt;math&amp;gt;parent(u) := v&amp;lt;/math&amp;gt; или &amp;lt;math&amp;gt;parent(v) := u&amp;lt;/math&amp;gt;. Дополнительно можно отслеживать количество вершин в каждом из фрагментов, чтобы меньший фрагмент подсоединять к большему, а не наоборот (оценки сложности доказываются именно при такой реализации, однако на практике алгоритм хорошо работает и без подсчёта количества вершин).&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
В алгоритме Борувки фрагменты минимального остовного дерева наращиваются постепенно присоединением минимального ребра, выходящего из каждого фрагмента.&lt;br /&gt;
&lt;br /&gt;
1.	В самом начале каждая вершина является отдельным фрагментом.&lt;br /&gt;
&lt;br /&gt;
2.	На каждом шаге:&lt;br /&gt;
&lt;br /&gt;
*	Для каждого фрагмента определяется минимальное по весу исходящее ребро.&lt;br /&gt;
&lt;br /&gt;
*	Минимальные рёбра добавляются в минимальное остовное дерево, а соответствующие фрагменты объединяются.&lt;br /&gt;
&lt;br /&gt;
3.	Алгоритм останавливается, когда остаётся только один фрагмент, либо когда ни у одного из фрагментов нет исходящих рёбер.&lt;br /&gt;
&lt;br /&gt;
Поиск минимальных исходящих рёбер может выполняться независимо для каждого фрагмента. Таким образом, данную стадию вычислений можно эффективно параллелизовать (в том числе с использованием массового параллелизма графических ускорителей).&lt;br /&gt;
&lt;br /&gt;
Объединение фрагментов также может быть реализовано параллельно, с использованием описанной выше параллельной версии структуры Union-Find.&lt;br /&gt;
&lt;br /&gt;
Аккуратный подсчёт количества активных фрагментов позволяет остановить алгоритм Борувки на один шаг раньше обычного:&lt;br /&gt;
&lt;br /&gt;
1.	В начале итерации счётчик активных фрагментов обнуляется.&lt;br /&gt;
&lt;br /&gt;
2.	На этапе поиска минимальных рёбер счётчик увеличивается на единицу для каждого фрагмента, у которого были исходящие рёбра.&lt;br /&gt;
&lt;br /&gt;
3.	На этапе объединения фрагментов счётчик уменьшается на единицу каждый раз, когда операция &amp;lt;math&amp;gt;MERGE(u, v)&amp;lt;/math&amp;gt; вернула значение истина.&lt;br /&gt;
&lt;br /&gt;
Если в конце итерации счётчик равен 0 или 1, то вычисления останавливаются.&lt;br /&gt;
Параллелизм возможен на этапе сортировки рёбер по весу, однако основной ход алгоритма является последовательным.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Последовательная сложность алгоритма Борувки для графа с &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершинами и &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; рёбрами составляет &amp;lt;math&amp;gt;O(m \ln(n))&amp;lt;/math&amp;gt; операций.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
В описанном подходе существует два уровня параллелизма: параллелизм в классическом алгоритме Борувки (нижний), и параллелизм в алгоритме обработка графа, не помещающегося в память.&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;&#039;Нижний уровень параллелизма&#039;&#039;&#039;: поиск минимальных исходящих рёбер может выполняться независимо для каждого фрагмента, благодаря чему данную стадию вычислений можно эффективно параллелизовать (как на GPU, так и на CPU). Объединение фрагментов также может быть реализовано параллельно, с использованием описанной структуры Union-Find. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Верхний уровень параллелизма&#039;&#039;&#039;: построение отдельных минимальных основных деревьев для каждого из списков ребер может производиться параллельно. Например, список ребер может разбиваться на две части, одна из которых обрабатывается на GPU, а вторая параллельно на CPU. &lt;br /&gt;
&lt;br /&gt;
[[file:MST low.png|thumb|center|1000px|Рисунок 1. Информационный граф нижнего уровня параллелизма]]&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационные графы и подробное описание каждого из них. Так же можно считать, что на рисунке 1 представлен информационный граф классического алгоритма Борувки, а на рисунке 2 — алгоритма обработки графа.&lt;br /&gt;
&lt;br /&gt;
Нижний уровень параллелизма на графе алгоритма (рисунок 1) расположен на уровнях {3, 4, 5}, соответствующим операциям параллельного поиска минимальных исходящих ребер, а так же уровнях {6, 7, 8}, соответствующим операциям параллельного объединения деревьев. Так же, различные операции копирования {1, 2, 8, 9} выполняются параллельно. После выполнения тела цикла, производится проверка {12} того, сколько деревьев осталось на текущем шаге, и если данное число не изменилось, то происходит выход из цикла, иначе аналогичная следующая итерация. &lt;br /&gt;
&lt;br /&gt;
Верхний уровень параллелизма (рисунок 2), как уже говорилось, заключается в параллельном вычислении минимального основного дерева (compute mst) для различных частей графа. Перед этим производится процесс инициализации (init process), данные которого используют последующие параллельные compute mst. Затем, после параллельных вычислений mst, происходит вычисление итого основного дерево, после чего после чего полученный результат сохраняется (save results).&lt;br /&gt;
&lt;br /&gt;
[[file:MST up.png|thumb|center|1000px|Рисунок 2. Информационный граф верхнего уровня параллелизма]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритму Борувки обладает большим потенциалом параллелизма, так как его основная операция (выбор минимального исходящего ребра во фрагменте) может исполнятся независимо для каждого фрагмента.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: взвешенный граф &amp;lt;math&amp;gt;(V, E, W)&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин &amp;lt;math&amp;gt;v_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; рёбер &amp;lt;math&amp;gt;e_j = (v^{(1)}_{j},&lt;br /&gt;
v^{(2)}_{j})&amp;lt;/math&amp;gt; с весами &amp;lt;math&amp;gt;f_j&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(m + n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: список рёбер минимального остовного дерева (для несвязного графа – список минимальных остовных деревьев для всех компонент связности).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
# Алгоритм останавливается за конечное число шагов, поскольку на каждом шаге становится по крайней мере на один фрагмент меньше.&lt;br /&gt;
# Более того, число фрагментов на каждом шаге уменьшается как минимум вдвое, так что общее число шагов составляет не более &amp;lt;math&amp;gt;\log_2 n&amp;lt;/math&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;
На этапе поиска минимального ребра происходят следующие обращения к памяти:&lt;br /&gt;
# Чтение информации о рёбрах. Может производится последовательно.&lt;br /&gt;
# Проверка принадлежности ребра одному и тому же фрагменту - два чтения массива &amp;lt;math&amp;gt;parent(u)&amp;lt;/math&amp;gt; с вероятным промахой по кэшу.&lt;br /&gt;
# Чтение и обновление минимального веса ребра фрагмента. Данная информация может быть закеширована, особенно на поздних шагах, однако обновление необходимо производить атомарно, что требует инвализации кэша.&lt;br /&gt;
&lt;br /&gt;
На этапе схлопывания фрагментов требуется атомарно обновить массив &amp;lt;math&amp;gt;parent(u)&amp;lt;/math&amp;gt; для каждого добавляемого в MST ребра. В зависимости от реализации параллельной структуры Union-Find корни фрагментов могут находиться ближе к началу массива, что позволяет закешировать эту наиболее часто читаемую область. Требование атомарности, однако, ограничивает эффект от такого кэширования.&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
Программа, реализующая алгоритм Борувки, состоит из двух частей: &lt;br /&gt;
&lt;br /&gt;
1. части, отвечающей за общую координацию вычислений&lt;br /&gt;
&lt;br /&gt;
2. части, отвечающей за параллельные вычисления на многоядерных CPU или GPU.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Описанный выше последовательный алгоритм не может применяться в параллельной программе: в реализации &amp;lt;math&amp;gt;MERGE&amp;lt;/math&amp;gt; результаты операций &amp;lt;math&amp;gt;FIND(u)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;FIND(v)&amp;lt;/math&amp;gt; могут постоянно меняться, что приведёт к race condition. Параллельный вариант алгоритма описан в статье.&lt;br /&gt;
&lt;br /&gt;
1.	Каждой вершине v соответствует запись &amp;lt;math&amp;gt;A[v] = { parent, rank }&amp;lt;/math&amp;gt;. Изначально &amp;lt;math&amp;gt;A[v] := { v, 0 }&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
2.	Вспомогательная операция &amp;lt;math&amp;gt;UPDATE(v, rank_v, u, rank_u)&amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
old := A[v]&lt;br /&gt;
&lt;br /&gt;
if old.parent != v or old.rank != rank_v then return false&lt;br /&gt;
&lt;br /&gt;
new := { u, rank_u }&lt;br /&gt;
&lt;br /&gt;
return CAS(A[v], old, new)&lt;br /&gt;
&lt;br /&gt;
3.	Операция &amp;lt;math&amp;gt;FIND(v)&amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
    while v != A[v].parent do&lt;br /&gt;
        u := A[v].parent&lt;br /&gt;
        CAS(A[v].parent, u, A[u].parent)&lt;br /&gt;
        v := A[u].parent&lt;br /&gt;
    return v&lt;br /&gt;
&lt;br /&gt;
4.	Операция UNION(u, v):&lt;br /&gt;
&lt;br /&gt;
    while true do&lt;br /&gt;
        (u, v) := (FIND(u), FIND(v))&lt;br /&gt;
        if u = v then return false&lt;br /&gt;
        (rank_u, rank_v) := (A[u].rank, A[v].rank)&lt;br /&gt;
        if (A[u].rank, u) &amp;gt; (A[v].rank, v) then&lt;br /&gt;
            swap((u, rank_u), (v, rank_v))&lt;br /&gt;
        if UPDATE(u, rank_u, v, rank_u) then&lt;br /&gt;
            if rank_u = rank_v then&lt;br /&gt;
                UPDATE(v, rank_v, v, rank_v + 1)&lt;br /&gt;
            return true&lt;br /&gt;
&lt;br /&gt;
Для описанной версии алгоритма гарантируется свойство wait-free. На практике может использоваться упрощённая версия без подсчёта рангов, обладающая более слабым свойством lock-free, но в ряде случаев выигрывающая по скорости.&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;
Проведём исследование масштабируемости параллельной реализации алгоритма Борувки согласно [[Scalability methodology|методике]]. Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов-2 [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Набор и границы значений изменяемых [[Глоссарий#Параметры запуска|параметров запуска]] реализации алгоритма: &lt;br /&gt;
&lt;br /&gt;
*	число процессоров [1 : 28] с шагом 1;&lt;br /&gt;
*	размер графа [2^20 : 2^27].&lt;br /&gt;
&lt;br /&gt;
Проведем отдельные исследования сильной масштабируемости и масштабируемости вширь реализации алгоритма Борувки.&lt;br /&gt;
&lt;br /&gt;
Производительность определена как TEPS (от англ. Traversed Edges Per Second), то есть число ребер графа, который алгоритм обрабатывает в секунду. С помощью данной метрики можно сравнивать производительность для различных размеров графа, оценивая, насколько понижается эффективность обработки графа при увеличении его размера&lt;br /&gt;
&lt;br /&gt;
[[file:MST scaling strong.png|thumb|center|700px|Рисунок 3. Параллельная реализация алгоритма Борувки масштабируемость CPU версии: производительность в зависимости от числа запущенных CPU-потоков.]]&lt;br /&gt;
&lt;br /&gt;
[[file:MST scaling wide.png|thumb|center|700px|Рисунок 4. Параллельная реализация алгоритма Борувки масштабируемость различных версий реализации алгоритма: производительность в зависимости от размера графа]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
* C++, MPI: [http://www.boost.org/libs/graph_parallel/doc/html/index.html Parallel Boost Graph Library]; функции &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#dense-boruvka-minimum-spanning-tree dense_boruvka_minimum_spanning_tree]&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#boruvka-then-merge boruvka_then_merge]&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#boruvka-mixed-merge boruvka_mixed_merge]&amp;lt;/code&amp;gt; сочетают алгоритм Борувки и [[алгоритм Крускала]].&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Начатые статьи]]&lt;/div&gt;</summary>
		<author><name>Daryin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%91%D0%BE%D1%80%D1%83%D0%B2%D0%BA%D0%B8&amp;diff=20150</id>
		<title>Алгоритм Борувки</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%91%D0%BE%D1%80%D1%83%D0%B2%D0%BA%D0%B8&amp;diff=20150"/>
		<updated>2016-12-05T22:05:13Z</updated>

		<summary type="html">&lt;p&gt;Daryin: /* Количественная оценка локальности */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм Борувки&#039;&#039;&#039;&amp;lt;ref&amp;gt;Borůvka, Otakar. “O Jistém Problému Minimálním.” Práce Moravské Přírodovědecké Společnosti III, no. 3 (1926): 37–58.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Jarník, Vojtěch. “O Jistém Problému Minimálním (Z Dopisu Panu O. Borůvkovi).” Práce Moravské Přírodovědecké Společnosti 6, no. 4 (1930): 57–63.&amp;lt;/ref&amp;gt; предназначен для решения [[Построение минимального остовного дерева (MST)|задачи о построении минимального остовного дерева]] во взвешенном неориентированном графе. Алгоритм хорошо параллелизуется и является основой для распределённого [[Алгоритм GHS|алгоритма GHS]].&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Пусть задан связный неориентированный граф &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; с весами рёбер &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt;. Предполагается, что веса всех рёбер различны (если это не так, то можно упорядочить рёбра сначала по весу, а потом по номеру).&lt;br /&gt;
&lt;br /&gt;
Алгоритм Борувки основан на следующих двух свойствах задачи:&lt;br /&gt;
* &#039;&#039;&#039;Минимальное ребро фрагмента&#039;&#039;&#039;. Пусть &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; – фрагмент минимального остовного дерева и &amp;lt;math&amp;gt;e_F&amp;lt;/math&amp;gt; – ребро наименьшего веса, исходящее из &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; (т.е. ровно один его конец является вершиной из &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt;). Если ребро &amp;lt;math&amp;gt;e_F&amp;lt;/math&amp;gt; единственно, то оно принадлежит минимальному остовному дереву.&lt;br /&gt;
* &#039;&#039;&#039;Схлопывание фрагментов&#039;&#039;&#039;. Пусть &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; – фрагмент минимального остовного дерева графа &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;, а граф &amp;lt;math&amp;gt;G&#039;&amp;lt;/math&amp;gt; получен из &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt; склеиванием вершин, принадлежащих &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt;. Тогда объединение &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; и минимального остовного дерева графа &amp;lt;math&amp;gt;G&#039;&amp;lt;/math&amp;gt; даёт минимальное остовное дерево исходного графа &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В начале работы алгоритма каждая вершина графа &amp;lt;math&amp;gt;G&amp;lt;/math&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;
Пусть задан связный граф &amp;lt;math&amp;gt;G=(V,E)&amp;lt;/math&amp;gt; с вершинами &amp;lt;math&amp;gt;V = ( v_{1}, v_{2}, ..., v_{n} )&amp;lt;/math&amp;gt; и рёбрами &amp;lt;math&amp;gt;E = ( e_{1}, e_{2}, ..., e_{m} )&amp;lt;/math&amp;gt;. Каждому ребру &amp;lt;math&amp;gt;e \in E&amp;lt;/math&amp;gt; приписан вес &amp;lt;math&amp;gt;w(e)&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Требуется построить дерево &amp;lt;math&amp;gt;T^* \subseteq E&amp;lt;/math&amp;gt;, связывающее все вершины, и имеющее наименьший возможный вес среди всех таких деревьев:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; w(T^* )= \min_T( w(T)) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
где вес множества рёбер есть сумма их весов:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;w(T)=\sum_{e \in T} (w(T))&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если граф &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt; не является связным, то дерева, связывающего все вершины, не существует. &lt;br /&gt;
&lt;br /&gt;
В этом случае необходимо найти минимальной остовное дерево для каждой компоненты связности &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;. Набор таких деревьев называется минимальным остовным лесом (сокращённо MSF – Minimum Spanning Forest).&lt;br /&gt;
&lt;br /&gt;
==== Вспомогательный алгоритм: система непересекающихся множеств (Union-Find)====&lt;br /&gt;
&lt;br /&gt;
Во всех алгоритмах решения задачи требуется отслеживать, каким уже построенным фрагментам дерева принадлежат те или иные вершины графа. Для этого используется структура данных «система непересекающихся множеств» (Union-Find). Данная структура поддерживает две операции:&lt;br /&gt;
&lt;br /&gt;
1.	&amp;lt;math&amp;gt;FIND(v) = w&amp;lt;/math&amp;gt; – по вершине v возвращает вершину w – «корень» фрагмента, которому принадлежит вершина v. При этом гарантируется, что вершины u и v принадлежат одному и тому же фрагменту, тогда и только тогда, когда &amp;lt;math&amp;gt;FIND(u) = FIND(v)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
2.	&amp;lt;math&amp;gt;MERGE(u, v)&amp;lt;/math&amp;gt; – объединяет два фрагмента, которым принадлежат вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v.&amp;lt;/math&amp;gt; (Если они уже лежат в одном фрагменте, то ничего не происходит.) При практической реализации удобно, чтобы данная операция возвращала значение истина, если объединение фрагментов имело место, и ложь в противном случае.&lt;br /&gt;
&lt;br /&gt;
==== Последовательная версия====&lt;br /&gt;
&lt;br /&gt;
Классический последовательный алгоритм Union-Find описан в статье Тарьяна. Каждой вершине v приписывается указатель на вершину-родителя &amp;lt;math&amp;gt;parent(v)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
1.	Изначально &amp;lt;math&amp;gt;parent(v) := v&amp;lt;/math&amp;gt; для всех вершин.&lt;br /&gt;
&lt;br /&gt;
2.	&amp;lt;math&amp;gt;FIND(v)&amp;lt;/math&amp;gt; выполняется следующим образом: полагаем &amp;lt;math&amp;gt;u := v&amp;lt;/math&amp;gt;, и далее следуем по указателям &amp;lt;math&amp;gt;u := parent(u)&amp;lt;/math&amp;gt; до тех пор, пока не станет &amp;lt;math&amp;gt;u = parent(u)&amp;lt;/math&amp;gt;. Это и будет результат операции. Дополнительно можно «схлопывать» дерево: присвоить всем посещённым вершинами: &amp;lt;math&amp;gt;parent(u_i) := u&amp;lt;/math&amp;gt;, либо производить схлопывание по пути: &amp;lt;math&amp;gt;parent(u) := parent(parent(u)))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
3.	&amp;lt;math&amp;gt;MERGE(u, v)&amp;lt;/math&amp;gt; выполняется следующим образом: вначале находим корневые вершины &amp;lt;math&amp;gt;u := FIND(u), v := FIND(v)&amp;lt;/math&amp;gt;. Если &amp;lt;math&amp;gt;u = v&amp;lt;/math&amp;gt;, то исходные вершины принадлежат одному фрагменту и объединения фрагментов не происходит. В противном случае полагаем одно из &amp;lt;math&amp;gt;parent(u) := v&amp;lt;/math&amp;gt; или &amp;lt;math&amp;gt;parent(v) := u&amp;lt;/math&amp;gt;. Дополнительно можно отслеживать количество вершин в каждом из фрагментов, чтобы меньший фрагмент подсоединять к большему, а не наоборот (оценки сложности доказываются именно при такой реализации, однако на практике алгоритм хорошо работает и без подсчёта количества вершин).&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
В алгоритме Борувки фрагменты минимального остовного дерева наращиваются постепенно присоединением минимального ребра, выходящего из каждого фрагмента.&lt;br /&gt;
&lt;br /&gt;
1.	В самом начале каждая вершина является отдельным фрагментом.&lt;br /&gt;
&lt;br /&gt;
2.	На каждом шаге:&lt;br /&gt;
&lt;br /&gt;
*	Для каждого фрагмента определяется минимальное по весу исходящее ребро.&lt;br /&gt;
&lt;br /&gt;
*	Минимальные рёбра добавляются в минимальное остовное дерево, а соответствующие фрагменты объединяются.&lt;br /&gt;
&lt;br /&gt;
3.	Алгоритм останавливается, когда остаётся только один фрагмент, либо когда ни у одного из фрагментов нет исходящих рёбер.&lt;br /&gt;
&lt;br /&gt;
Поиск минимальных исходящих рёбер может выполняться независимо для каждого фрагмента. Таким образом, данную стадию вычислений можно эффективно параллелизовать (в том числе с использованием массового параллелизма графических ускорителей).&lt;br /&gt;
&lt;br /&gt;
Объединение фрагментов также может быть реализовано параллельно, с использованием описанной выше параллельной версии структуры Union-Find.&lt;br /&gt;
&lt;br /&gt;
Аккуратный подсчёт количества активных фрагментов позволяет остановить алгоритм Борувки на один шаг раньше обычного:&lt;br /&gt;
&lt;br /&gt;
1.	В начале итерации счётчик активных фрагментов обнуляется.&lt;br /&gt;
&lt;br /&gt;
2.	На этапе поиска минимальных рёбер счётчик увеличивается на единицу для каждого фрагмента, у которого были исходящие рёбра.&lt;br /&gt;
&lt;br /&gt;
3.	На этапе объединения фрагментов счётчик уменьшается на единицу каждый раз, когда операция &amp;lt;math&amp;gt;MERGE(u, v)&amp;lt;/math&amp;gt; вернула значение истина.&lt;br /&gt;
&lt;br /&gt;
Если в конце итерации счётчик равен 0 или 1, то вычисления останавливаются.&lt;br /&gt;
Параллелизм возможен на этапе сортировки рёбер по весу, однако основной ход алгоритма является последовательным.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Последовательная сложность алгоритма Борувки для графа с &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершинами и &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; рёбрами составляет &amp;lt;math&amp;gt;O(m \ln(n))&amp;lt;/math&amp;gt; операций.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
В описанном подходе существует два уровня параллелизма: параллелизм в классическом алгоритме Борувки (нижний), и параллелизм в алгоритме обработка графа, не помещающегося в память.&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;&#039;Нижний уровень параллелизма&#039;&#039;&#039;: поиск минимальных исходящих рёбер может выполняться независимо для каждого фрагмента, благодаря чему данную стадию вычислений можно эффективно параллелизовать (как на GPU, так и на CPU). Объединение фрагментов также может быть реализовано параллельно, с использованием описанной структуры Union-Find. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Верхний уровень параллелизма&#039;&#039;&#039;: построение отдельных минимальных основных деревьев для каждого из списков ребер может производиться параллельно. Например, список ребер может разбиваться на две части, одна из которых обрабатывается на GPU, а вторая параллельно на CPU. &lt;br /&gt;
&lt;br /&gt;
[[file:MST low.png|thumb|center|1000px|Рисунок 1. Информационный граф нижнего уровня параллелизма]]&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационные графы и подробное описание каждого из них. Так же можно считать, что на рисунке 1 представлен информационный граф классического алгоритма Борувки, а на рисунке 2 — алгоритма обработки графа.&lt;br /&gt;
&lt;br /&gt;
Нижний уровень параллелизма на графе алгоритма (рисунок 1) расположен на уровнях {3, 4, 5}, соответствующим операциям параллельного поиска минимальных исходящих ребер, а так же уровнях {6, 7, 8}, соответствующим операциям параллельного объединения деревьев. Так же, различные операции копирования {1, 2, 8, 9} выполняются параллельно. После выполнения тела цикла, производится проверка {12} того, сколько деревьев осталось на текущем шаге, и если данное число не изменилось, то происходит выход из цикла, иначе аналогичная следующая итерация. &lt;br /&gt;
&lt;br /&gt;
Верхний уровень параллелизма (рисунок 2), как уже говорилось, заключается в параллельном вычислении минимального основного дерева (compute mst) для различных частей графа. Перед этим производится процесс инициализации (init process), данные которого используют последующие параллельные compute mst. Затем, после параллельных вычислений mst, происходит вычисление итого основного дерево, после чего после чего полученный результат сохраняется (save results).&lt;br /&gt;
&lt;br /&gt;
[[file:MST up.png|thumb|center|1000px|Рисунок 2. Информационный граф верхнего уровня параллелизма]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритму Борувки обладает большим потенциалом параллелизма, так как его основная операция (выбор минимального исходящего ребра во фрагменте) может исполнятся независимо для каждого фрагмента.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: взвешенный граф &amp;lt;math&amp;gt;(V, E, W)&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин &amp;lt;math&amp;gt;v_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; рёбер &amp;lt;math&amp;gt;e_j = (v^{(1)}_{j},&lt;br /&gt;
v^{(2)}_{j})&amp;lt;/math&amp;gt; с весами &amp;lt;math&amp;gt;f_j&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(m + n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: список рёбер минимального остовного дерева (для несвязного графа – список минимальных остовных деревьев для всех компонент связности).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
# Алгоритм останавливается за конечное число шагов, поскольку на каждом шаге становится по крайней мере на один фрагмент меньше.&lt;br /&gt;
# Более того, число фрагментов на каждом шаге уменьшается как минимум вдвое, так что общее число шагов составляет не более &amp;lt;math&amp;gt;\log_2 n&amp;lt;/math&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;
На этапе поиска минимального ребра происходят следующие обращения к памяти:&lt;br /&gt;
# Чтение информации о рёбрах. Может производится последовательно.&lt;br /&gt;
# Проверка принадлежности ребра одному и тому же фрагменту - два чтения массива &amp;lt;math&amp;gt;parent(u)&amp;lt;/math&amp;gt; с вероятным промахой по кэшу.&lt;br /&gt;
# Чтение и обновление минимального веса ребра фрагмента. Данная информация может быть закеширована, особенно на поздних шагах, однако обновление необходимо производить атомарно, что требует инвализации кэша.&lt;br /&gt;
&lt;br /&gt;
На этапе схлопывания фрагментов требуется атомарно обновить массив &amp;lt;math&amp;gt;parent(u)&amp;lt;/math&amp;gt; для каждого добавляемого в MST ребра. В зависимости от реализации параллельной структуры Union-Find корни фрагментов могут находиться ближе к началу массива, что позволяет закешировать эту наиболее часто читаемую область. Требование атомарности, однако, ограничивает эффект от такого кэширования.&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
Программа, реализующая алгоритм Борувки, состоит из двух частей: &lt;br /&gt;
&lt;br /&gt;
1. части, отвечающей за общую координацию вычислений&lt;br /&gt;
&lt;br /&gt;
2. части, отвечающей за параллельные вычисления на многоядерных CPU или GPU.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Описанный выше последовательный алгоритм не может применяться в параллельной программе: в реализации &amp;lt;math&amp;gt;MERGE&amp;lt;/math&amp;gt; результаты операций &amp;lt;math&amp;gt;FIND(u)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;FIND(v)&amp;lt;/math&amp;gt; могут постоянно меняться, что приведёт к race condition. Параллельный вариант алгоритма описан в статье.&lt;br /&gt;
&lt;br /&gt;
1.	Каждой вершине v соответствует запись &amp;lt;math&amp;gt;A[v] = { parent, rank }&amp;lt;/math&amp;gt;. Изначально &amp;lt;math&amp;gt;A[v] := { v, 0 }&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
2.	Вспомогательная операция &amp;lt;math&amp;gt;UPDATE(v, rank_v, u, rank_u)&amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
old := A[v]&lt;br /&gt;
&lt;br /&gt;
if old.parent != v or old.rank != rank_v then return false&lt;br /&gt;
&lt;br /&gt;
new := { u, rank_u }&lt;br /&gt;
&lt;br /&gt;
return CAS(A[v], old, new)&lt;br /&gt;
&lt;br /&gt;
3.	Операция &amp;lt;math&amp;gt;FIND(v)&amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
    while v != A[v].parent do&lt;br /&gt;
        u := A[v].parent&lt;br /&gt;
        CAS(A[v].parent, u, A[u].parent)&lt;br /&gt;
        v := A[u].parent&lt;br /&gt;
    return v&lt;br /&gt;
&lt;br /&gt;
4.	Операция UNION(u, v):&lt;br /&gt;
&lt;br /&gt;
    while true do&lt;br /&gt;
        (u, v) := (FIND(u), FIND(v))&lt;br /&gt;
        if u = v then return false&lt;br /&gt;
        (rank_u, rank_v) := (A[u].rank, A[v].rank)&lt;br /&gt;
        if (A[u].rank, u) &amp;gt; (A[v].rank, v) then&lt;br /&gt;
            swap((u, rank_u), (v, rank_v))&lt;br /&gt;
        if UPDATE(u, rank_u, v, rank_u) then&lt;br /&gt;
            if rank_u = rank_v then&lt;br /&gt;
                UPDATE(v, rank_v, v, rank_v + 1)&lt;br /&gt;
            return true&lt;br /&gt;
&lt;br /&gt;
Для описанной версии алгоритма гарантируется свойство wait-free. На практике может использоваться упрощённая версия без подсчёта рангов, обладающая более слабым свойством lock-free, но в ряде случаев выигрывающая по скорости.&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
==== Масштабируемость алгоритма ====&lt;br /&gt;
==== Масштабируемость реализации алгоритма ====&lt;br /&gt;
Проведём исследование масштабируемости параллельной реализации алгоритма Борувки согласно [[Scalability methodology|методике]]. Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов-2 [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Набор и границы значений изменяемых [[Глоссарий#Параметры запуска|параметров запуска]] реализации алгоритма: &lt;br /&gt;
&lt;br /&gt;
*	число процессоров [1 : 28] с шагом 1;&lt;br /&gt;
*	размер графа [2^20 : 2^27].&lt;br /&gt;
&lt;br /&gt;
Проведем отдельные исследования сильной масштабируемости и масштабируемости вширь реализации алгоритма Борувки.&lt;br /&gt;
&lt;br /&gt;
Производительность определена как TEPS (от англ. Traversed Edges Per Second), то есть число ребер графа, который алгоритм обрабатывает в секунду. С помощью данной метрики можно сравнивать производительность для различных размеров графа, оценивая, насколько понижается эффективность обработки графа при увеличении его размера&lt;br /&gt;
&lt;br /&gt;
[[file:MST scaling strong.png|thumb|center|700px|Рисунок 3. Параллельная реализация алгоритма Борувки масштабируемость CPU версии: производительность в зависимости от числа запущенных CPU-потоков.]]&lt;br /&gt;
&lt;br /&gt;
[[file:MST scaling wide.png|thumb|center|700px|Рисунок 4. Параллельная реализация алгоритма Борувки масштабируемость различных версий реализации алгоритма: производительность в зависимости от размера графа]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
* C++, MPI: [http://www.boost.org/libs/graph_parallel/doc/html/index.html Parallel Boost Graph Library]; функции &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#dense-boruvka-minimum-spanning-tree dense_boruvka_minimum_spanning_tree]&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#boruvka-then-merge boruvka_then_merge]&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#boruvka-mixed-merge boruvka_mixed_merge]&amp;lt;/code&amp;gt; сочетают алгоритм Борувки и [[алгоритм Крускала]].&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Начатые статьи]]&lt;/div&gt;</summary>
		<author><name>Daryin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%91%D0%BE%D1%80%D1%83%D0%B2%D0%BA%D0%B8&amp;diff=20149</id>
		<title>Алгоритм Борувки</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%91%D0%BE%D1%80%D1%83%D0%B2%D0%BA%D0%B8&amp;diff=20149"/>
		<updated>2016-12-05T21:59:16Z</updated>

		<summary type="html">&lt;p&gt;Daryin: /* Структура обращений в память и качественная оценка локальности */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм Борувки&#039;&#039;&#039;&amp;lt;ref&amp;gt;Borůvka, Otakar. “O Jistém Problému Minimálním.” Práce Moravské Přírodovědecké Společnosti III, no. 3 (1926): 37–58.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Jarník, Vojtěch. “O Jistém Problému Minimálním (Z Dopisu Panu O. Borůvkovi).” Práce Moravské Přírodovědecké Společnosti 6, no. 4 (1930): 57–63.&amp;lt;/ref&amp;gt; предназначен для решения [[Построение минимального остовного дерева (MST)|задачи о построении минимального остовного дерева]] во взвешенном неориентированном графе. Алгоритм хорошо параллелизуется и является основой для распределённого [[Алгоритм GHS|алгоритма GHS]].&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Пусть задан связный неориентированный граф &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; с весами рёбер &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt;. Предполагается, что веса всех рёбер различны (если это не так, то можно упорядочить рёбра сначала по весу, а потом по номеру).&lt;br /&gt;
&lt;br /&gt;
Алгоритм Борувки основан на следующих двух свойствах задачи:&lt;br /&gt;
* &#039;&#039;&#039;Минимальное ребро фрагмента&#039;&#039;&#039;. Пусть &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; – фрагмент минимального остовного дерева и &amp;lt;math&amp;gt;e_F&amp;lt;/math&amp;gt; – ребро наименьшего веса, исходящее из &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; (т.е. ровно один его конец является вершиной из &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt;). Если ребро &amp;lt;math&amp;gt;e_F&amp;lt;/math&amp;gt; единственно, то оно принадлежит минимальному остовному дереву.&lt;br /&gt;
* &#039;&#039;&#039;Схлопывание фрагментов&#039;&#039;&#039;. Пусть &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; – фрагмент минимального остовного дерева графа &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;, а граф &amp;lt;math&amp;gt;G&#039;&amp;lt;/math&amp;gt; получен из &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt; склеиванием вершин, принадлежащих &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt;. Тогда объединение &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; и минимального остовного дерева графа &amp;lt;math&amp;gt;G&#039;&amp;lt;/math&amp;gt; даёт минимальное остовное дерево исходного графа &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В начале работы алгоритма каждая вершина графа &amp;lt;math&amp;gt;G&amp;lt;/math&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;
Пусть задан связный граф &amp;lt;math&amp;gt;G=(V,E)&amp;lt;/math&amp;gt; с вершинами &amp;lt;math&amp;gt;V = ( v_{1}, v_{2}, ..., v_{n} )&amp;lt;/math&amp;gt; и рёбрами &amp;lt;math&amp;gt;E = ( e_{1}, e_{2}, ..., e_{m} )&amp;lt;/math&amp;gt;. Каждому ребру &amp;lt;math&amp;gt;e \in E&amp;lt;/math&amp;gt; приписан вес &amp;lt;math&amp;gt;w(e)&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Требуется построить дерево &amp;lt;math&amp;gt;T^* \subseteq E&amp;lt;/math&amp;gt;, связывающее все вершины, и имеющее наименьший возможный вес среди всех таких деревьев:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; w(T^* )= \min_T( w(T)) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
где вес множества рёбер есть сумма их весов:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;w(T)=\sum_{e \in T} (w(T))&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если граф &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt; не является связным, то дерева, связывающего все вершины, не существует. &lt;br /&gt;
&lt;br /&gt;
В этом случае необходимо найти минимальной остовное дерево для каждой компоненты связности &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;. Набор таких деревьев называется минимальным остовным лесом (сокращённо MSF – Minimum Spanning Forest).&lt;br /&gt;
&lt;br /&gt;
==== Вспомогательный алгоритм: система непересекающихся множеств (Union-Find)====&lt;br /&gt;
&lt;br /&gt;
Во всех алгоритмах решения задачи требуется отслеживать, каким уже построенным фрагментам дерева принадлежат те или иные вершины графа. Для этого используется структура данных «система непересекающихся множеств» (Union-Find). Данная структура поддерживает две операции:&lt;br /&gt;
&lt;br /&gt;
1.	&amp;lt;math&amp;gt;FIND(v) = w&amp;lt;/math&amp;gt; – по вершине v возвращает вершину w – «корень» фрагмента, которому принадлежит вершина v. При этом гарантируется, что вершины u и v принадлежат одному и тому же фрагменту, тогда и только тогда, когда &amp;lt;math&amp;gt;FIND(u) = FIND(v)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
2.	&amp;lt;math&amp;gt;MERGE(u, v)&amp;lt;/math&amp;gt; – объединяет два фрагмента, которым принадлежат вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v.&amp;lt;/math&amp;gt; (Если они уже лежат в одном фрагменте, то ничего не происходит.) При практической реализации удобно, чтобы данная операция возвращала значение истина, если объединение фрагментов имело место, и ложь в противном случае.&lt;br /&gt;
&lt;br /&gt;
==== Последовательная версия====&lt;br /&gt;
&lt;br /&gt;
Классический последовательный алгоритм Union-Find описан в статье Тарьяна. Каждой вершине v приписывается указатель на вершину-родителя &amp;lt;math&amp;gt;parent(v)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
1.	Изначально &amp;lt;math&amp;gt;parent(v) := v&amp;lt;/math&amp;gt; для всех вершин.&lt;br /&gt;
&lt;br /&gt;
2.	&amp;lt;math&amp;gt;FIND(v)&amp;lt;/math&amp;gt; выполняется следующим образом: полагаем &amp;lt;math&amp;gt;u := v&amp;lt;/math&amp;gt;, и далее следуем по указателям &amp;lt;math&amp;gt;u := parent(u)&amp;lt;/math&amp;gt; до тех пор, пока не станет &amp;lt;math&amp;gt;u = parent(u)&amp;lt;/math&amp;gt;. Это и будет результат операции. Дополнительно можно «схлопывать» дерево: присвоить всем посещённым вершинами: &amp;lt;math&amp;gt;parent(u_i) := u&amp;lt;/math&amp;gt;, либо производить схлопывание по пути: &amp;lt;math&amp;gt;parent(u) := parent(parent(u)))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
3.	&amp;lt;math&amp;gt;MERGE(u, v)&amp;lt;/math&amp;gt; выполняется следующим образом: вначале находим корневые вершины &amp;lt;math&amp;gt;u := FIND(u), v := FIND(v)&amp;lt;/math&amp;gt;. Если &amp;lt;math&amp;gt;u = v&amp;lt;/math&amp;gt;, то исходные вершины принадлежат одному фрагменту и объединения фрагментов не происходит. В противном случае полагаем одно из &amp;lt;math&amp;gt;parent(u) := v&amp;lt;/math&amp;gt; или &amp;lt;math&amp;gt;parent(v) := u&amp;lt;/math&amp;gt;. Дополнительно можно отслеживать количество вершин в каждом из фрагментов, чтобы меньший фрагмент подсоединять к большему, а не наоборот (оценки сложности доказываются именно при такой реализации, однако на практике алгоритм хорошо работает и без подсчёта количества вершин).&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
В алгоритме Борувки фрагменты минимального остовного дерева наращиваются постепенно присоединением минимального ребра, выходящего из каждого фрагмента.&lt;br /&gt;
&lt;br /&gt;
1.	В самом начале каждая вершина является отдельным фрагментом.&lt;br /&gt;
&lt;br /&gt;
2.	На каждом шаге:&lt;br /&gt;
&lt;br /&gt;
*	Для каждого фрагмента определяется минимальное по весу исходящее ребро.&lt;br /&gt;
&lt;br /&gt;
*	Минимальные рёбра добавляются в минимальное остовное дерево, а соответствующие фрагменты объединяются.&lt;br /&gt;
&lt;br /&gt;
3.	Алгоритм останавливается, когда остаётся только один фрагмент, либо когда ни у одного из фрагментов нет исходящих рёбер.&lt;br /&gt;
&lt;br /&gt;
Поиск минимальных исходящих рёбер может выполняться независимо для каждого фрагмента. Таким образом, данную стадию вычислений можно эффективно параллелизовать (в том числе с использованием массового параллелизма графических ускорителей).&lt;br /&gt;
&lt;br /&gt;
Объединение фрагментов также может быть реализовано параллельно, с использованием описанной выше параллельной версии структуры Union-Find.&lt;br /&gt;
&lt;br /&gt;
Аккуратный подсчёт количества активных фрагментов позволяет остановить алгоритм Борувки на один шаг раньше обычного:&lt;br /&gt;
&lt;br /&gt;
1.	В начале итерации счётчик активных фрагментов обнуляется.&lt;br /&gt;
&lt;br /&gt;
2.	На этапе поиска минимальных рёбер счётчик увеличивается на единицу для каждого фрагмента, у которого были исходящие рёбра.&lt;br /&gt;
&lt;br /&gt;
3.	На этапе объединения фрагментов счётчик уменьшается на единицу каждый раз, когда операция &amp;lt;math&amp;gt;MERGE(u, v)&amp;lt;/math&amp;gt; вернула значение истина.&lt;br /&gt;
&lt;br /&gt;
Если в конце итерации счётчик равен 0 или 1, то вычисления останавливаются.&lt;br /&gt;
Параллелизм возможен на этапе сортировки рёбер по весу, однако основной ход алгоритма является последовательным.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Последовательная сложность алгоритма Борувки для графа с &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершинами и &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; рёбрами составляет &amp;lt;math&amp;gt;O(m \ln(n))&amp;lt;/math&amp;gt; операций.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
В описанном подходе существует два уровня параллелизма: параллелизм в классическом алгоритме Борувки (нижний), и параллелизм в алгоритме обработка графа, не помещающегося в память.&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;&#039;Нижний уровень параллелизма&#039;&#039;&#039;: поиск минимальных исходящих рёбер может выполняться независимо для каждого фрагмента, благодаря чему данную стадию вычислений можно эффективно параллелизовать (как на GPU, так и на CPU). Объединение фрагментов также может быть реализовано параллельно, с использованием описанной структуры Union-Find. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Верхний уровень параллелизма&#039;&#039;&#039;: построение отдельных минимальных основных деревьев для каждого из списков ребер может производиться параллельно. Например, список ребер может разбиваться на две части, одна из которых обрабатывается на GPU, а вторая параллельно на CPU. &lt;br /&gt;
&lt;br /&gt;
[[file:MST low.png|thumb|center|1000px|Рисунок 1. Информационный граф нижнего уровня параллелизма]]&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационные графы и подробное описание каждого из них. Так же можно считать, что на рисунке 1 представлен информационный граф классического алгоритма Борувки, а на рисунке 2 — алгоритма обработки графа.&lt;br /&gt;
&lt;br /&gt;
Нижний уровень параллелизма на графе алгоритма (рисунок 1) расположен на уровнях {3, 4, 5}, соответствующим операциям параллельного поиска минимальных исходящих ребер, а так же уровнях {6, 7, 8}, соответствующим операциям параллельного объединения деревьев. Так же, различные операции копирования {1, 2, 8, 9} выполняются параллельно. После выполнения тела цикла, производится проверка {12} того, сколько деревьев осталось на текущем шаге, и если данное число не изменилось, то происходит выход из цикла, иначе аналогичная следующая итерация. &lt;br /&gt;
&lt;br /&gt;
Верхний уровень параллелизма (рисунок 2), как уже говорилось, заключается в параллельном вычислении минимального основного дерева (compute mst) для различных частей графа. Перед этим производится процесс инициализации (init process), данные которого используют последующие параллельные compute mst. Затем, после параллельных вычислений mst, происходит вычисление итого основного дерево, после чего после чего полученный результат сохраняется (save results).&lt;br /&gt;
&lt;br /&gt;
[[file:MST up.png|thumb|center|1000px|Рисунок 2. Информационный граф верхнего уровня параллелизма]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритму Борувки обладает большим потенциалом параллелизма, так как его основная операция (выбор минимального исходящего ребра во фрагменте) может исполнятся независимо для каждого фрагмента.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: взвешенный граф &amp;lt;math&amp;gt;(V, E, W)&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин &amp;lt;math&amp;gt;v_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; рёбер &amp;lt;math&amp;gt;e_j = (v^{(1)}_{j},&lt;br /&gt;
v^{(2)}_{j})&amp;lt;/math&amp;gt; с весами &amp;lt;math&amp;gt;f_j&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(m + n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: список рёбер минимального остовного дерева (для несвязного графа – список минимальных остовных деревьев для всех компонент связности).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
# Алгоритм останавливается за конечное число шагов, поскольку на каждом шаге становится по крайней мере на один фрагмент меньше.&lt;br /&gt;
# Более того, число фрагментов на каждом шаге уменьшается как минимум вдвое, так что общее число шагов составляет не более &amp;lt;math&amp;gt;\log_2 n&amp;lt;/math&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;
Программа, реализующая алгоритм Борувки, состоит из двух частей: &lt;br /&gt;
&lt;br /&gt;
1. части, отвечающей за общую координацию вычислений&lt;br /&gt;
&lt;br /&gt;
2. части, отвечающей за параллельные вычисления на многоядерных CPU или GPU.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Описанный выше последовательный алгоритм не может применяться в параллельной программе: в реализации &amp;lt;math&amp;gt;MERGE&amp;lt;/math&amp;gt; результаты операций &amp;lt;math&amp;gt;FIND(u)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;FIND(v)&amp;lt;/math&amp;gt; могут постоянно меняться, что приведёт к race condition. Параллельный вариант алгоритма описан в статье.&lt;br /&gt;
&lt;br /&gt;
1.	Каждой вершине v соответствует запись &amp;lt;math&amp;gt;A[v] = { parent, rank }&amp;lt;/math&amp;gt;. Изначально &amp;lt;math&amp;gt;A[v] := { v, 0 }&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
2.	Вспомогательная операция &amp;lt;math&amp;gt;UPDATE(v, rank_v, u, rank_u)&amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
old := A[v]&lt;br /&gt;
&lt;br /&gt;
if old.parent != v or old.rank != rank_v then return false&lt;br /&gt;
&lt;br /&gt;
new := { u, rank_u }&lt;br /&gt;
&lt;br /&gt;
return CAS(A[v], old, new)&lt;br /&gt;
&lt;br /&gt;
3.	Операция &amp;lt;math&amp;gt;FIND(v)&amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
    while v != A[v].parent do&lt;br /&gt;
        u := A[v].parent&lt;br /&gt;
        CAS(A[v].parent, u, A[u].parent)&lt;br /&gt;
        v := A[u].parent&lt;br /&gt;
    return v&lt;br /&gt;
&lt;br /&gt;
4.	Операция UNION(u, v):&lt;br /&gt;
&lt;br /&gt;
    while true do&lt;br /&gt;
        (u, v) := (FIND(u), FIND(v))&lt;br /&gt;
        if u = v then return false&lt;br /&gt;
        (rank_u, rank_v) := (A[u].rank, A[v].rank)&lt;br /&gt;
        if (A[u].rank, u) &amp;gt; (A[v].rank, v) then&lt;br /&gt;
            swap((u, rank_u), (v, rank_v))&lt;br /&gt;
        if UPDATE(u, rank_u, v, rank_u) then&lt;br /&gt;
            if rank_u = rank_v then&lt;br /&gt;
                UPDATE(v, rank_v, v, rank_v + 1)&lt;br /&gt;
            return true&lt;br /&gt;
&lt;br /&gt;
Для описанной версии алгоритма гарантируется свойство wait-free. На практике может использоваться упрощённая версия без подсчёта рангов, обладающая более слабым свойством lock-free, но в ряде случаев выигрывающая по скорости.&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
==== Масштабируемость алгоритма ====&lt;br /&gt;
==== Масштабируемость реализации алгоритма ====&lt;br /&gt;
Проведём исследование масштабируемости параллельной реализации алгоритма Борувки согласно [[Scalability methodology|методике]]. Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов-2 [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Набор и границы значений изменяемых [[Глоссарий#Параметры запуска|параметров запуска]] реализации алгоритма: &lt;br /&gt;
&lt;br /&gt;
*	число процессоров [1 : 28] с шагом 1;&lt;br /&gt;
*	размер графа [2^20 : 2^27].&lt;br /&gt;
&lt;br /&gt;
Проведем отдельные исследования сильной масштабируемости и масштабируемости вширь реализации алгоритма Борувки.&lt;br /&gt;
&lt;br /&gt;
Производительность определена как TEPS (от англ. Traversed Edges Per Second), то есть число ребер графа, который алгоритм обрабатывает в секунду. С помощью данной метрики можно сравнивать производительность для различных размеров графа, оценивая, насколько понижается эффективность обработки графа при увеличении его размера&lt;br /&gt;
&lt;br /&gt;
[[file:MST scaling strong.png|thumb|center|700px|Рисунок 3. Параллельная реализация алгоритма Борувки масштабируемость CPU версии: производительность в зависимости от числа запущенных CPU-потоков.]]&lt;br /&gt;
&lt;br /&gt;
[[file:MST scaling wide.png|thumb|center|700px|Рисунок 4. Параллельная реализация алгоритма Борувки масштабируемость различных версий реализации алгоритма: производительность в зависимости от размера графа]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
* C++, MPI: [http://www.boost.org/libs/graph_parallel/doc/html/index.html Parallel Boost Graph Library]; функции &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#dense-boruvka-minimum-spanning-tree dense_boruvka_minimum_spanning_tree]&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#boruvka-then-merge boruvka_then_merge]&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#boruvka-mixed-merge boruvka_mixed_merge]&amp;lt;/code&amp;gt; сочетают алгоритм Борувки и [[алгоритм Крускала]].&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Начатые статьи]]&lt;/div&gt;</summary>
		<author><name>Daryin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%91%D0%BE%D1%80%D1%83%D0%B2%D0%BA%D0%B8&amp;diff=20147</id>
		<title>Алгоритм Борувки</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%91%D0%BE%D1%80%D1%83%D0%B2%D0%BA%D0%B8&amp;diff=20147"/>
		<updated>2016-12-05T21:53:08Z</updated>

		<summary type="html">&lt;p&gt;Daryin: /* Свойства алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм Борувки&#039;&#039;&#039;&amp;lt;ref&amp;gt;Borůvka, Otakar. “O Jistém Problému Minimálním.” Práce Moravské Přírodovědecké Společnosti III, no. 3 (1926): 37–58.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Jarník, Vojtěch. “O Jistém Problému Minimálním (Z Dopisu Panu O. Borůvkovi).” Práce Moravské Přírodovědecké Společnosti 6, no. 4 (1930): 57–63.&amp;lt;/ref&amp;gt; предназначен для решения [[Построение минимального остовного дерева (MST)|задачи о построении минимального остовного дерева]] во взвешенном неориентированном графе. Алгоритм хорошо параллелизуется и является основой для распределённого [[Алгоритм GHS|алгоритма GHS]].&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Пусть задан связный неориентированный граф &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; с весами рёбер &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt;. Предполагается, что веса всех рёбер различны (если это не так, то можно упорядочить рёбра сначала по весу, а потом по номеру).&lt;br /&gt;
&lt;br /&gt;
Алгоритм Борувки основан на следующих двух свойствах задачи:&lt;br /&gt;
* &#039;&#039;&#039;Минимальное ребро фрагмента&#039;&#039;&#039;. Пусть &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; – фрагмент минимального остовного дерева и &amp;lt;math&amp;gt;e_F&amp;lt;/math&amp;gt; – ребро наименьшего веса, исходящее из &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; (т.е. ровно один его конец является вершиной из &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt;). Если ребро &amp;lt;math&amp;gt;e_F&amp;lt;/math&amp;gt; единственно, то оно принадлежит минимальному остовному дереву.&lt;br /&gt;
* &#039;&#039;&#039;Схлопывание фрагментов&#039;&#039;&#039;. Пусть &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; – фрагмент минимального остовного дерева графа &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;, а граф &amp;lt;math&amp;gt;G&#039;&amp;lt;/math&amp;gt; получен из &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt; склеиванием вершин, принадлежащих &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt;. Тогда объединение &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; и минимального остовного дерева графа &amp;lt;math&amp;gt;G&#039;&amp;lt;/math&amp;gt; даёт минимальное остовное дерево исходного графа &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В начале работы алгоритма каждая вершина графа &amp;lt;math&amp;gt;G&amp;lt;/math&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;
Пусть задан связный граф &amp;lt;math&amp;gt;G=(V,E)&amp;lt;/math&amp;gt; с вершинами &amp;lt;math&amp;gt;V = ( v_{1}, v_{2}, ..., v_{n} )&amp;lt;/math&amp;gt; и рёбрами &amp;lt;math&amp;gt;E = ( e_{1}, e_{2}, ..., e_{m} )&amp;lt;/math&amp;gt;. Каждому ребру &amp;lt;math&amp;gt;e \in E&amp;lt;/math&amp;gt; приписан вес &amp;lt;math&amp;gt;w(e)&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Требуется построить дерево &amp;lt;math&amp;gt;T^* \subseteq E&amp;lt;/math&amp;gt;, связывающее все вершины, и имеющее наименьший возможный вес среди всех таких деревьев:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; w(T^* )= \min_T( w(T)) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
где вес множества рёбер есть сумма их весов:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;w(T)=\sum_{e \in T} (w(T))&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если граф &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt; не является связным, то дерева, связывающего все вершины, не существует. &lt;br /&gt;
&lt;br /&gt;
В этом случае необходимо найти минимальной остовное дерево для каждой компоненты связности &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;. Набор таких деревьев называется минимальным остовным лесом (сокращённо MSF – Minimum Spanning Forest).&lt;br /&gt;
&lt;br /&gt;
==== Вспомогательный алгоритм: система непересекающихся множеств (Union-Find)====&lt;br /&gt;
&lt;br /&gt;
Во всех алгоритмах решения задачи требуется отслеживать, каким уже построенным фрагментам дерева принадлежат те или иные вершины графа. Для этого используется структура данных «система непересекающихся множеств» (Union-Find). Данная структура поддерживает две операции:&lt;br /&gt;
&lt;br /&gt;
1.	&amp;lt;math&amp;gt;FIND(v) = w&amp;lt;/math&amp;gt; – по вершине v возвращает вершину w – «корень» фрагмента, которому принадлежит вершина v. При этом гарантируется, что вершины u и v принадлежат одному и тому же фрагменту, тогда и только тогда, когда &amp;lt;math&amp;gt;FIND(u) = FIND(v)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
2.	&amp;lt;math&amp;gt;MERGE(u, v)&amp;lt;/math&amp;gt; – объединяет два фрагмента, которым принадлежат вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v.&amp;lt;/math&amp;gt; (Если они уже лежат в одном фрагменте, то ничего не происходит.) При практической реализации удобно, чтобы данная операция возвращала значение истина, если объединение фрагментов имело место, и ложь в противном случае.&lt;br /&gt;
&lt;br /&gt;
==== Последовательная версия====&lt;br /&gt;
&lt;br /&gt;
Классический последовательный алгоритм Union-Find описан в статье Тарьяна. Каждой вершине v приписывается указатель на вершину-родителя &amp;lt;math&amp;gt;parent(v)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
1.	Изначально &amp;lt;math&amp;gt;parent(v) := v&amp;lt;/math&amp;gt; для всех вершин.&lt;br /&gt;
&lt;br /&gt;
2.	&amp;lt;math&amp;gt;FIND(v)&amp;lt;/math&amp;gt; выполняется следующим образом: полагаем &amp;lt;math&amp;gt;u := v&amp;lt;/math&amp;gt;, и далее следуем по указателям &amp;lt;math&amp;gt;u := parent(u)&amp;lt;/math&amp;gt; до тех пор, пока не станет &amp;lt;math&amp;gt;u = parent(u)&amp;lt;/math&amp;gt;. Это и будет результат операции. Дополнительно можно «схлопывать» дерево: присвоить всем посещённым вершинами: &amp;lt;math&amp;gt;parent(u_i) := u&amp;lt;/math&amp;gt;, либо производить схлопывание по пути: &amp;lt;math&amp;gt;parent(u) := parent(parent(u)))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
3.	&amp;lt;math&amp;gt;MERGE(u, v)&amp;lt;/math&amp;gt; выполняется следующим образом: вначале находим корневые вершины &amp;lt;math&amp;gt;u := FIND(u), v := FIND(v)&amp;lt;/math&amp;gt;. Если &amp;lt;math&amp;gt;u = v&amp;lt;/math&amp;gt;, то исходные вершины принадлежат одному фрагменту и объединения фрагментов не происходит. В противном случае полагаем одно из &amp;lt;math&amp;gt;parent(u) := v&amp;lt;/math&amp;gt; или &amp;lt;math&amp;gt;parent(v) := u&amp;lt;/math&amp;gt;. Дополнительно можно отслеживать количество вершин в каждом из фрагментов, чтобы меньший фрагмент подсоединять к большему, а не наоборот (оценки сложности доказываются именно при такой реализации, однако на практике алгоритм хорошо работает и без подсчёта количества вершин).&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
В алгоритме Борувки фрагменты минимального остовного дерева наращиваются постепенно присоединением минимального ребра, выходящего из каждого фрагмента.&lt;br /&gt;
&lt;br /&gt;
1.	В самом начале каждая вершина является отдельным фрагментом.&lt;br /&gt;
&lt;br /&gt;
2.	На каждом шаге:&lt;br /&gt;
&lt;br /&gt;
*	Для каждого фрагмента определяется минимальное по весу исходящее ребро.&lt;br /&gt;
&lt;br /&gt;
*	Минимальные рёбра добавляются в минимальное остовное дерево, а соответствующие фрагменты объединяются.&lt;br /&gt;
&lt;br /&gt;
3.	Алгоритм останавливается, когда остаётся только один фрагмент, либо когда ни у одного из фрагментов нет исходящих рёбер.&lt;br /&gt;
&lt;br /&gt;
Поиск минимальных исходящих рёбер может выполняться независимо для каждого фрагмента. Таким образом, данную стадию вычислений можно эффективно параллелизовать (в том числе с использованием массового параллелизма графических ускорителей).&lt;br /&gt;
&lt;br /&gt;
Объединение фрагментов также может быть реализовано параллельно, с использованием описанной выше параллельной версии структуры Union-Find.&lt;br /&gt;
&lt;br /&gt;
Аккуратный подсчёт количества активных фрагментов позволяет остановить алгоритм Борувки на один шаг раньше обычного:&lt;br /&gt;
&lt;br /&gt;
1.	В начале итерации счётчик активных фрагментов обнуляется.&lt;br /&gt;
&lt;br /&gt;
2.	На этапе поиска минимальных рёбер счётчик увеличивается на единицу для каждого фрагмента, у которого были исходящие рёбра.&lt;br /&gt;
&lt;br /&gt;
3.	На этапе объединения фрагментов счётчик уменьшается на единицу каждый раз, когда операция &amp;lt;math&amp;gt;MERGE(u, v)&amp;lt;/math&amp;gt; вернула значение истина.&lt;br /&gt;
&lt;br /&gt;
Если в конце итерации счётчик равен 0 или 1, то вычисления останавливаются.&lt;br /&gt;
Параллелизм возможен на этапе сортировки рёбер по весу, однако основной ход алгоритма является последовательным.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Последовательная сложность алгоритма Борувки для графа с &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершинами и &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; рёбрами составляет &amp;lt;math&amp;gt;O(m \ln(n))&amp;lt;/math&amp;gt; операций.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
В описанном подходе существует два уровня параллелизма: параллелизм в классическом алгоритме Борувки (нижний), и параллелизм в алгоритме обработка графа, не помещающегося в память.&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;&#039;Нижний уровень параллелизма&#039;&#039;&#039;: поиск минимальных исходящих рёбер может выполняться независимо для каждого фрагмента, благодаря чему данную стадию вычислений можно эффективно параллелизовать (как на GPU, так и на CPU). Объединение фрагментов также может быть реализовано параллельно, с использованием описанной структуры Union-Find. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Верхний уровень параллелизма&#039;&#039;&#039;: построение отдельных минимальных основных деревьев для каждого из списков ребер может производиться параллельно. Например, список ребер может разбиваться на две части, одна из которых обрабатывается на GPU, а вторая параллельно на CPU. &lt;br /&gt;
&lt;br /&gt;
[[file:MST low.png|thumb|center|1000px|Рисунок 1. Информационный граф нижнего уровня параллелизма]]&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационные графы и подробное описание каждого из них. Так же можно считать, что на рисунке 1 представлен информационный граф классического алгоритма Борувки, а на рисунке 2 — алгоритма обработки графа.&lt;br /&gt;
&lt;br /&gt;
Нижний уровень параллелизма на графе алгоритма (рисунок 1) расположен на уровнях {3, 4, 5}, соответствующим операциям параллельного поиска минимальных исходящих ребер, а так же уровнях {6, 7, 8}, соответствующим операциям параллельного объединения деревьев. Так же, различные операции копирования {1, 2, 8, 9} выполняются параллельно. После выполнения тела цикла, производится проверка {12} того, сколько деревьев осталось на текущем шаге, и если данное число не изменилось, то происходит выход из цикла, иначе аналогичная следующая итерация. &lt;br /&gt;
&lt;br /&gt;
Верхний уровень параллелизма (рисунок 2), как уже говорилось, заключается в параллельном вычислении минимального основного дерева (compute mst) для различных частей графа. Перед этим производится процесс инициализации (init process), данные которого используют последующие параллельные compute mst. Затем, после параллельных вычислений mst, происходит вычисление итого основного дерево, после чего после чего полученный результат сохраняется (save results).&lt;br /&gt;
&lt;br /&gt;
[[file:MST up.png|thumb|center|1000px|Рисунок 2. Информационный граф верхнего уровня параллелизма]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритму Борувки обладает большим потенциалом параллелизма, так как его основная операция (выбор минимального исходящего ребра во фрагменте) может исполнятся независимо для каждого фрагмента.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: взвешенный граф &amp;lt;math&amp;gt;(V, E, W)&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин &amp;lt;math&amp;gt;v_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; рёбер &amp;lt;math&amp;gt;e_j = (v^{(1)}_{j},&lt;br /&gt;
v^{(2)}_{j})&amp;lt;/math&amp;gt; с весами &amp;lt;math&amp;gt;f_j&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(m + n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: список рёбер минимального остовного дерева (для несвязного графа – список минимальных остовных деревьев для всех компонент связности).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
# Алгоритм останавливается за конечное число шагов, поскольку на каждом шаге становится по крайней мере на один фрагмент меньше.&lt;br /&gt;
# Более того, число фрагментов на каждом шаге уменьшается как минимум вдвое, так что общее число шагов составляет не более &amp;lt;math&amp;gt;\log_2 n&amp;lt;/math&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;
1. части, отвечающей за общую координацию вычислений&lt;br /&gt;
&lt;br /&gt;
2. части, отвечающей за параллельные вычисления на многоядерных CPU или GPU.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Описанный выше последовательный алгоритм не может применяться в параллельной программе: в реализации &amp;lt;math&amp;gt;MERGE&amp;lt;/math&amp;gt; результаты операций &amp;lt;math&amp;gt;FIND(u)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;FIND(v)&amp;lt;/math&amp;gt; могут постоянно меняться, что приведёт к race condition. Параллельный вариант алгоритма описан в статье.&lt;br /&gt;
&lt;br /&gt;
1.	Каждой вершине v соответствует запись &amp;lt;math&amp;gt;A[v] = { parent, rank }&amp;lt;/math&amp;gt;. Изначально &amp;lt;math&amp;gt;A[v] := { v, 0 }&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
2.	Вспомогательная операция &amp;lt;math&amp;gt;UPDATE(v, rank_v, u, rank_u)&amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
old := A[v]&lt;br /&gt;
&lt;br /&gt;
if old.parent != v or old.rank != rank_v then return false&lt;br /&gt;
&lt;br /&gt;
new := { u, rank_u }&lt;br /&gt;
&lt;br /&gt;
return CAS(A[v], old, new)&lt;br /&gt;
&lt;br /&gt;
3.	Операция &amp;lt;math&amp;gt;FIND(v)&amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
    while v != A[v].parent do&lt;br /&gt;
        u := A[v].parent&lt;br /&gt;
        CAS(A[v].parent, u, A[u].parent)&lt;br /&gt;
        v := A[u].parent&lt;br /&gt;
    return v&lt;br /&gt;
&lt;br /&gt;
4.	Операция UNION(u, v):&lt;br /&gt;
&lt;br /&gt;
    while true do&lt;br /&gt;
        (u, v) := (FIND(u), FIND(v))&lt;br /&gt;
        if u = v then return false&lt;br /&gt;
        (rank_u, rank_v) := (A[u].rank, A[v].rank)&lt;br /&gt;
        if (A[u].rank, u) &amp;gt; (A[v].rank, v) then&lt;br /&gt;
            swap((u, rank_u), (v, rank_v))&lt;br /&gt;
        if UPDATE(u, rank_u, v, rank_u) then&lt;br /&gt;
            if rank_u = rank_v then&lt;br /&gt;
                UPDATE(v, rank_v, v, rank_v + 1)&lt;br /&gt;
            return true&lt;br /&gt;
&lt;br /&gt;
Для описанной версии алгоритма гарантируется свойство wait-free. На практике может использоваться упрощённая версия без подсчёта рангов, обладающая более слабым свойством lock-free, но в ряде случаев выигрывающая по скорости.&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
==== Масштабируемость алгоритма ====&lt;br /&gt;
==== Масштабируемость реализации алгоритма ====&lt;br /&gt;
Проведём исследование масштабируемости параллельной реализации алгоритма Борувки согласно [[Scalability methodology|методике]]. Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов-2 [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Набор и границы значений изменяемых [[Глоссарий#Параметры запуска|параметров запуска]] реализации алгоритма: &lt;br /&gt;
&lt;br /&gt;
*	число процессоров [1 : 28] с шагом 1;&lt;br /&gt;
*	размер графа [2^20 : 2^27].&lt;br /&gt;
&lt;br /&gt;
Проведем отдельные исследования сильной масштабируемости и масштабируемости вширь реализации алгоритма Борувки.&lt;br /&gt;
&lt;br /&gt;
Производительность определена как TEPS (от англ. Traversed Edges Per Second), то есть число ребер графа, который алгоритм обрабатывает в секунду. С помощью данной метрики можно сравнивать производительность для различных размеров графа, оценивая, насколько понижается эффективность обработки графа при увеличении его размера&lt;br /&gt;
&lt;br /&gt;
[[file:MST scaling strong.png|thumb|center|700px|Рисунок 3. Параллельная реализация алгоритма Борувки масштабируемость CPU версии: производительность в зависимости от числа запущенных CPU-потоков.]]&lt;br /&gt;
&lt;br /&gt;
[[file:MST scaling wide.png|thumb|center|700px|Рисунок 4. Параллельная реализация алгоритма Борувки масштабируемость различных версий реализации алгоритма: производительность в зависимости от размера графа]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
* C++, MPI: [http://www.boost.org/libs/graph_parallel/doc/html/index.html Parallel Boost Graph Library]; функции &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#dense-boruvka-minimum-spanning-tree dense_boruvka_minimum_spanning_tree]&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#boruvka-then-merge boruvka_then_merge]&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#boruvka-mixed-merge boruvka_mixed_merge]&amp;lt;/code&amp;gt; сочетают алгоритм Борувки и [[алгоритм Крускала]].&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Начатые статьи]]&lt;/div&gt;</summary>
		<author><name>Daryin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%91%D0%BE%D1%80%D1%83%D0%B2%D0%BA%D0%B8&amp;diff=20146</id>
		<title>Алгоритм Борувки</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%91%D0%BE%D1%80%D1%83%D0%B2%D0%BA%D0%B8&amp;diff=20146"/>
		<updated>2016-12-05T21:52:05Z</updated>

		<summary type="html">&lt;p&gt;Daryin: /* Свойства алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм Борувки&#039;&#039;&#039;&amp;lt;ref&amp;gt;Borůvka, Otakar. “O Jistém Problému Minimálním.” Práce Moravské Přírodovědecké Společnosti III, no. 3 (1926): 37–58.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Jarník, Vojtěch. “O Jistém Problému Minimálním (Z Dopisu Panu O. Borůvkovi).” Práce Moravské Přírodovědecké Společnosti 6, no. 4 (1930): 57–63.&amp;lt;/ref&amp;gt; предназначен для решения [[Построение минимального остовного дерева (MST)|задачи о построении минимального остовного дерева]] во взвешенном неориентированном графе. Алгоритм хорошо параллелизуется и является основой для распределённого [[Алгоритм GHS|алгоритма GHS]].&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Пусть задан связный неориентированный граф &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; с весами рёбер &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt;. Предполагается, что веса всех рёбер различны (если это не так, то можно упорядочить рёбра сначала по весу, а потом по номеру).&lt;br /&gt;
&lt;br /&gt;
Алгоритм Борувки основан на следующих двух свойствах задачи:&lt;br /&gt;
* &#039;&#039;&#039;Минимальное ребро фрагмента&#039;&#039;&#039;. Пусть &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; – фрагмент минимального остовного дерева и &amp;lt;math&amp;gt;e_F&amp;lt;/math&amp;gt; – ребро наименьшего веса, исходящее из &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; (т.е. ровно один его конец является вершиной из &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt;). Если ребро &amp;lt;math&amp;gt;e_F&amp;lt;/math&amp;gt; единственно, то оно принадлежит минимальному остовному дереву.&lt;br /&gt;
* &#039;&#039;&#039;Схлопывание фрагментов&#039;&#039;&#039;. Пусть &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; – фрагмент минимального остовного дерева графа &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;, а граф &amp;lt;math&amp;gt;G&#039;&amp;lt;/math&amp;gt; получен из &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt; склеиванием вершин, принадлежащих &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt;. Тогда объединение &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; и минимального остовного дерева графа &amp;lt;math&amp;gt;G&#039;&amp;lt;/math&amp;gt; даёт минимальное остовное дерево исходного графа &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В начале работы алгоритма каждая вершина графа &amp;lt;math&amp;gt;G&amp;lt;/math&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;
Пусть задан связный граф &amp;lt;math&amp;gt;G=(V,E)&amp;lt;/math&amp;gt; с вершинами &amp;lt;math&amp;gt;V = ( v_{1}, v_{2}, ..., v_{n} )&amp;lt;/math&amp;gt; и рёбрами &amp;lt;math&amp;gt;E = ( e_{1}, e_{2}, ..., e_{m} )&amp;lt;/math&amp;gt;. Каждому ребру &amp;lt;math&amp;gt;e \in E&amp;lt;/math&amp;gt; приписан вес &amp;lt;math&amp;gt;w(e)&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Требуется построить дерево &amp;lt;math&amp;gt;T^* \subseteq E&amp;lt;/math&amp;gt;, связывающее все вершины, и имеющее наименьший возможный вес среди всех таких деревьев:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; w(T^* )= \min_T( w(T)) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
где вес множества рёбер есть сумма их весов:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;w(T)=\sum_{e \in T} (w(T))&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если граф &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt; не является связным, то дерева, связывающего все вершины, не существует. &lt;br /&gt;
&lt;br /&gt;
В этом случае необходимо найти минимальной остовное дерево для каждой компоненты связности &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;. Набор таких деревьев называется минимальным остовным лесом (сокращённо MSF – Minimum Spanning Forest).&lt;br /&gt;
&lt;br /&gt;
==== Вспомогательный алгоритм: система непересекающихся множеств (Union-Find)====&lt;br /&gt;
&lt;br /&gt;
Во всех алгоритмах решения задачи требуется отслеживать, каким уже построенным фрагментам дерева принадлежат те или иные вершины графа. Для этого используется структура данных «система непересекающихся множеств» (Union-Find). Данная структура поддерживает две операции:&lt;br /&gt;
&lt;br /&gt;
1.	&amp;lt;math&amp;gt;FIND(v) = w&amp;lt;/math&amp;gt; – по вершине v возвращает вершину w – «корень» фрагмента, которому принадлежит вершина v. При этом гарантируется, что вершины u и v принадлежат одному и тому же фрагменту, тогда и только тогда, когда &amp;lt;math&amp;gt;FIND(u) = FIND(v)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
2.	&amp;lt;math&amp;gt;MERGE(u, v)&amp;lt;/math&amp;gt; – объединяет два фрагмента, которым принадлежат вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v.&amp;lt;/math&amp;gt; (Если они уже лежат в одном фрагменте, то ничего не происходит.) При практической реализации удобно, чтобы данная операция возвращала значение истина, если объединение фрагментов имело место, и ложь в противном случае.&lt;br /&gt;
&lt;br /&gt;
==== Последовательная версия====&lt;br /&gt;
&lt;br /&gt;
Классический последовательный алгоритм Union-Find описан в статье Тарьяна. Каждой вершине v приписывается указатель на вершину-родителя &amp;lt;math&amp;gt;parent(v)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
1.	Изначально &amp;lt;math&amp;gt;parent(v) := v&amp;lt;/math&amp;gt; для всех вершин.&lt;br /&gt;
&lt;br /&gt;
2.	&amp;lt;math&amp;gt;FIND(v)&amp;lt;/math&amp;gt; выполняется следующим образом: полагаем &amp;lt;math&amp;gt;u := v&amp;lt;/math&amp;gt;, и далее следуем по указателям &amp;lt;math&amp;gt;u := parent(u)&amp;lt;/math&amp;gt; до тех пор, пока не станет &amp;lt;math&amp;gt;u = parent(u)&amp;lt;/math&amp;gt;. Это и будет результат операции. Дополнительно можно «схлопывать» дерево: присвоить всем посещённым вершинами: &amp;lt;math&amp;gt;parent(u_i) := u&amp;lt;/math&amp;gt;, либо производить схлопывание по пути: &amp;lt;math&amp;gt;parent(u) := parent(parent(u)))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
3.	&amp;lt;math&amp;gt;MERGE(u, v)&amp;lt;/math&amp;gt; выполняется следующим образом: вначале находим корневые вершины &amp;lt;math&amp;gt;u := FIND(u), v := FIND(v)&amp;lt;/math&amp;gt;. Если &amp;lt;math&amp;gt;u = v&amp;lt;/math&amp;gt;, то исходные вершины принадлежат одному фрагменту и объединения фрагментов не происходит. В противном случае полагаем одно из &amp;lt;math&amp;gt;parent(u) := v&amp;lt;/math&amp;gt; или &amp;lt;math&amp;gt;parent(v) := u&amp;lt;/math&amp;gt;. Дополнительно можно отслеживать количество вершин в каждом из фрагментов, чтобы меньший фрагмент подсоединять к большему, а не наоборот (оценки сложности доказываются именно при такой реализации, однако на практике алгоритм хорошо работает и без подсчёта количества вершин).&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
В алгоритме Борувки фрагменты минимального остовного дерева наращиваются постепенно присоединением минимального ребра, выходящего из каждого фрагмента.&lt;br /&gt;
&lt;br /&gt;
1.	В самом начале каждая вершина является отдельным фрагментом.&lt;br /&gt;
&lt;br /&gt;
2.	На каждом шаге:&lt;br /&gt;
&lt;br /&gt;
*	Для каждого фрагмента определяется минимальное по весу исходящее ребро.&lt;br /&gt;
&lt;br /&gt;
*	Минимальные рёбра добавляются в минимальное остовное дерево, а соответствующие фрагменты объединяются.&lt;br /&gt;
&lt;br /&gt;
3.	Алгоритм останавливается, когда остаётся только один фрагмент, либо когда ни у одного из фрагментов нет исходящих рёбер.&lt;br /&gt;
&lt;br /&gt;
Поиск минимальных исходящих рёбер может выполняться независимо для каждого фрагмента. Таким образом, данную стадию вычислений можно эффективно параллелизовать (в том числе с использованием массового параллелизма графических ускорителей).&lt;br /&gt;
&lt;br /&gt;
Объединение фрагментов также может быть реализовано параллельно, с использованием описанной выше параллельной версии структуры Union-Find.&lt;br /&gt;
&lt;br /&gt;
Аккуратный подсчёт количества активных фрагментов позволяет остановить алгоритм Борувки на один шаг раньше обычного:&lt;br /&gt;
&lt;br /&gt;
1.	В начале итерации счётчик активных фрагментов обнуляется.&lt;br /&gt;
&lt;br /&gt;
2.	На этапе поиска минимальных рёбер счётчик увеличивается на единицу для каждого фрагмента, у которого были исходящие рёбра.&lt;br /&gt;
&lt;br /&gt;
3.	На этапе объединения фрагментов счётчик уменьшается на единицу каждый раз, когда операция &amp;lt;math&amp;gt;MERGE(u, v)&amp;lt;/math&amp;gt; вернула значение истина.&lt;br /&gt;
&lt;br /&gt;
Если в конце итерации счётчик равен 0 или 1, то вычисления останавливаются.&lt;br /&gt;
Параллелизм возможен на этапе сортировки рёбер по весу, однако основной ход алгоритма является последовательным.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Последовательная сложность алгоритма Борувки для графа с &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершинами и &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; рёбрами составляет &amp;lt;math&amp;gt;O(m \ln(n))&amp;lt;/math&amp;gt; операций.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
В описанном подходе существует два уровня параллелизма: параллелизм в классическом алгоритме Борувки (нижний), и параллелизм в алгоритме обработка графа, не помещающегося в память.&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;&#039;Нижний уровень параллелизма&#039;&#039;&#039;: поиск минимальных исходящих рёбер может выполняться независимо для каждого фрагмента, благодаря чему данную стадию вычислений можно эффективно параллелизовать (как на GPU, так и на CPU). Объединение фрагментов также может быть реализовано параллельно, с использованием описанной структуры Union-Find. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Верхний уровень параллелизма&#039;&#039;&#039;: построение отдельных минимальных основных деревьев для каждого из списков ребер может производиться параллельно. Например, список ребер может разбиваться на две части, одна из которых обрабатывается на GPU, а вторая параллельно на CPU. &lt;br /&gt;
&lt;br /&gt;
[[file:MST low.png|thumb|center|1000px|Рисунок 1. Информационный граф нижнего уровня параллелизма]]&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационные графы и подробное описание каждого из них. Так же можно считать, что на рисунке 1 представлен информационный граф классического алгоритма Борувки, а на рисунке 2 — алгоритма обработки графа.&lt;br /&gt;
&lt;br /&gt;
Нижний уровень параллелизма на графе алгоритма (рисунок 1) расположен на уровнях {3, 4, 5}, соответствующим операциям параллельного поиска минимальных исходящих ребер, а так же уровнях {6, 7, 8}, соответствующим операциям параллельного объединения деревьев. Так же, различные операции копирования {1, 2, 8, 9} выполняются параллельно. После выполнения тела цикла, производится проверка {12} того, сколько деревьев осталось на текущем шаге, и если данное число не изменилось, то происходит выход из цикла, иначе аналогичная следующая итерация. &lt;br /&gt;
&lt;br /&gt;
Верхний уровень параллелизма (рисунок 2), как уже говорилось, заключается в параллельном вычислении минимального основного дерева (compute mst) для различных частей графа. Перед этим производится процесс инициализации (init process), данные которого используют последующие параллельные compute mst. Затем, после параллельных вычислений mst, происходит вычисление итого основного дерево, после чего после чего полученный результат сохраняется (save results).&lt;br /&gt;
&lt;br /&gt;
[[file:MST up.png|thumb|center|1000px|Рисунок 2. Информационный граф верхнего уровня параллелизма]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритму Борувки обладает большим потенциалом параллелизма, так как его основная операция (выбор минимального исходящего ребра во фрагменте) может исполнятся независимо для каждого фрагмента.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: взвешенный граф &amp;lt;math&amp;gt;(V, E, W)&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин &amp;lt;math&amp;gt;v_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; рёбер &amp;lt;math&amp;gt;e_j = (v^{(1)}_{j},&lt;br /&gt;
v^{(2)}_{j})&amp;lt;/math&amp;gt; с весами &amp;lt;math&amp;gt;f_j&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(m + n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: список рёбер минимального остовного дерева (для несвязного графа – список минимальных остовных деревьев для всех компонент связности).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
# Алгоритм останавливается за конечное число шагов, поскольку на каждом шаге становится по крайней мере на один фрагмент меньше.&lt;br /&gt;
# Более того, число фрагментов на каждом шаге уменьшается как минимум вдвое, так что общее число шагов составляет не более &amp;lt;math&amp;gt;\log_2 n&amp;lt;\math&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;
1. части, отвечающей за общую координацию вычислений&lt;br /&gt;
&lt;br /&gt;
2. части, отвечающей за параллельные вычисления на многоядерных CPU или GPU.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Описанный выше последовательный алгоритм не может применяться в параллельной программе: в реализации &amp;lt;math&amp;gt;MERGE&amp;lt;/math&amp;gt; результаты операций &amp;lt;math&amp;gt;FIND(u)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;FIND(v)&amp;lt;/math&amp;gt; могут постоянно меняться, что приведёт к race condition. Параллельный вариант алгоритма описан в статье.&lt;br /&gt;
&lt;br /&gt;
1.	Каждой вершине v соответствует запись &amp;lt;math&amp;gt;A[v] = { parent, rank }&amp;lt;/math&amp;gt;. Изначально &amp;lt;math&amp;gt;A[v] := { v, 0 }&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
2.	Вспомогательная операция &amp;lt;math&amp;gt;UPDATE(v, rank_v, u, rank_u)&amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
old := A[v]&lt;br /&gt;
&lt;br /&gt;
if old.parent != v or old.rank != rank_v then return false&lt;br /&gt;
&lt;br /&gt;
new := { u, rank_u }&lt;br /&gt;
&lt;br /&gt;
return CAS(A[v], old, new)&lt;br /&gt;
&lt;br /&gt;
3.	Операция &amp;lt;math&amp;gt;FIND(v)&amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
    while v != A[v].parent do&lt;br /&gt;
        u := A[v].parent&lt;br /&gt;
        CAS(A[v].parent, u, A[u].parent)&lt;br /&gt;
        v := A[u].parent&lt;br /&gt;
    return v&lt;br /&gt;
&lt;br /&gt;
4.	Операция UNION(u, v):&lt;br /&gt;
&lt;br /&gt;
    while true do&lt;br /&gt;
        (u, v) := (FIND(u), FIND(v))&lt;br /&gt;
        if u = v then return false&lt;br /&gt;
        (rank_u, rank_v) := (A[u].rank, A[v].rank)&lt;br /&gt;
        if (A[u].rank, u) &amp;gt; (A[v].rank, v) then&lt;br /&gt;
            swap((u, rank_u), (v, rank_v))&lt;br /&gt;
        if UPDATE(u, rank_u, v, rank_u) then&lt;br /&gt;
            if rank_u = rank_v then&lt;br /&gt;
                UPDATE(v, rank_v, v, rank_v + 1)&lt;br /&gt;
            return true&lt;br /&gt;
&lt;br /&gt;
Для описанной версии алгоритма гарантируется свойство wait-free. На практике может использоваться упрощённая версия без подсчёта рангов, обладающая более слабым свойством lock-free, но в ряде случаев выигрывающая по скорости.&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
==== Масштабируемость алгоритма ====&lt;br /&gt;
==== Масштабируемость реализации алгоритма ====&lt;br /&gt;
Проведём исследование масштабируемости параллельной реализации алгоритма Борувки согласно [[Scalability methodology|методике]]. Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов-2 [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Набор и границы значений изменяемых [[Глоссарий#Параметры запуска|параметров запуска]] реализации алгоритма: &lt;br /&gt;
&lt;br /&gt;
*	число процессоров [1 : 28] с шагом 1;&lt;br /&gt;
*	размер графа [2^20 : 2^27].&lt;br /&gt;
&lt;br /&gt;
Проведем отдельные исследования сильной масштабируемости и масштабируемости вширь реализации алгоритма Борувки.&lt;br /&gt;
&lt;br /&gt;
Производительность определена как TEPS (от англ. Traversed Edges Per Second), то есть число ребер графа, который алгоритм обрабатывает в секунду. С помощью данной метрики можно сравнивать производительность для различных размеров графа, оценивая, насколько понижается эффективность обработки графа при увеличении его размера&lt;br /&gt;
&lt;br /&gt;
[[file:MST scaling strong.png|thumb|center|700px|Рисунок 3. Параллельная реализация алгоритма Борувки масштабируемость CPU версии: производительность в зависимости от числа запущенных CPU-потоков.]]&lt;br /&gt;
&lt;br /&gt;
[[file:MST scaling wide.png|thumb|center|700px|Рисунок 4. Параллельная реализация алгоритма Борувки масштабируемость различных версий реализации алгоритма: производительность в зависимости от размера графа]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
* C++, MPI: [http://www.boost.org/libs/graph_parallel/doc/html/index.html Parallel Boost Graph Library]; функции &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#dense-boruvka-minimum-spanning-tree dense_boruvka_minimum_spanning_tree]&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#boruvka-then-merge boruvka_then_merge]&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#boruvka-mixed-merge boruvka_mixed_merge]&amp;lt;/code&amp;gt; сочетают алгоритм Борувки и [[алгоритм Крускала]].&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Начатые статьи]]&lt;/div&gt;</summary>
		<author><name>Daryin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%91%D0%BE%D1%80%D1%83%D0%B2%D0%BA%D0%B8&amp;diff=20145</id>
		<title>Алгоритм Борувки</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%91%D0%BE%D1%80%D1%83%D0%B2%D0%BA%D0%B8&amp;diff=20145"/>
		<updated>2016-12-05T21:46:17Z</updated>

		<summary type="html">&lt;p&gt;Daryin: /* Последовательная версия */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм Борувки&#039;&#039;&#039;&amp;lt;ref&amp;gt;Borůvka, Otakar. “O Jistém Problému Minimálním.” Práce Moravské Přírodovědecké Společnosti III, no. 3 (1926): 37–58.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Jarník, Vojtěch. “O Jistém Problému Minimálním (Z Dopisu Panu O. Borůvkovi).” Práce Moravské Přírodovědecké Společnosti 6, no. 4 (1930): 57–63.&amp;lt;/ref&amp;gt; предназначен для решения [[Построение минимального остовного дерева (MST)|задачи о построении минимального остовного дерева]] во взвешенном неориентированном графе. Алгоритм хорошо параллелизуется и является основой для распределённого [[Алгоритм GHS|алгоритма GHS]].&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Пусть задан связный неориентированный граф &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; с весами рёбер &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt;. Предполагается, что веса всех рёбер различны (если это не так, то можно упорядочить рёбра сначала по весу, а потом по номеру).&lt;br /&gt;
&lt;br /&gt;
Алгоритм Борувки основан на следующих двух свойствах задачи:&lt;br /&gt;
* &#039;&#039;&#039;Минимальное ребро фрагмента&#039;&#039;&#039;. Пусть &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; – фрагмент минимального остовного дерева и &amp;lt;math&amp;gt;e_F&amp;lt;/math&amp;gt; – ребро наименьшего веса, исходящее из &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; (т.е. ровно один его конец является вершиной из &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt;). Если ребро &amp;lt;math&amp;gt;e_F&amp;lt;/math&amp;gt; единственно, то оно принадлежит минимальному остовному дереву.&lt;br /&gt;
* &#039;&#039;&#039;Схлопывание фрагментов&#039;&#039;&#039;. Пусть &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; – фрагмент минимального остовного дерева графа &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;, а граф &amp;lt;math&amp;gt;G&#039;&amp;lt;/math&amp;gt; получен из &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt; склеиванием вершин, принадлежащих &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt;. Тогда объединение &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; и минимального остовного дерева графа &amp;lt;math&amp;gt;G&#039;&amp;lt;/math&amp;gt; даёт минимальное остовное дерево исходного графа &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В начале работы алгоритма каждая вершина графа &amp;lt;math&amp;gt;G&amp;lt;/math&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;
Пусть задан связный граф &amp;lt;math&amp;gt;G=(V,E)&amp;lt;/math&amp;gt; с вершинами &amp;lt;math&amp;gt;V = ( v_{1}, v_{2}, ..., v_{n} )&amp;lt;/math&amp;gt; и рёбрами &amp;lt;math&amp;gt;E = ( e_{1}, e_{2}, ..., e_{m} )&amp;lt;/math&amp;gt;. Каждому ребру &amp;lt;math&amp;gt;e \in E&amp;lt;/math&amp;gt; приписан вес &amp;lt;math&amp;gt;w(e)&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Требуется построить дерево &amp;lt;math&amp;gt;T^* \subseteq E&amp;lt;/math&amp;gt;, связывающее все вершины, и имеющее наименьший возможный вес среди всех таких деревьев:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; w(T^* )= \min_T( w(T)) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
где вес множества рёбер есть сумма их весов:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;w(T)=\sum_{e \in T} (w(T))&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если граф &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt; не является связным, то дерева, связывающего все вершины, не существует. &lt;br /&gt;
&lt;br /&gt;
В этом случае необходимо найти минимальной остовное дерево для каждой компоненты связности &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;. Набор таких деревьев называется минимальным остовным лесом (сокращённо MSF – Minimum Spanning Forest).&lt;br /&gt;
&lt;br /&gt;
==== Вспомогательный алгоритм: система непересекающихся множеств (Union-Find)====&lt;br /&gt;
&lt;br /&gt;
Во всех алгоритмах решения задачи требуется отслеживать, каким уже построенным фрагментам дерева принадлежат те или иные вершины графа. Для этого используется структура данных «система непересекающихся множеств» (Union-Find). Данная структура поддерживает две операции:&lt;br /&gt;
&lt;br /&gt;
1.	&amp;lt;math&amp;gt;FIND(v) = w&amp;lt;/math&amp;gt; – по вершине v возвращает вершину w – «корень» фрагмента, которому принадлежит вершина v. При этом гарантируется, что вершины u и v принадлежат одному и тому же фрагменту, тогда и только тогда, когда &amp;lt;math&amp;gt;FIND(u) = FIND(v)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
2.	&amp;lt;math&amp;gt;MERGE(u, v)&amp;lt;/math&amp;gt; – объединяет два фрагмента, которым принадлежат вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v.&amp;lt;/math&amp;gt; (Если они уже лежат в одном фрагменте, то ничего не происходит.) При практической реализации удобно, чтобы данная операция возвращала значение истина, если объединение фрагментов имело место, и ложь в противном случае.&lt;br /&gt;
&lt;br /&gt;
==== Последовательная версия====&lt;br /&gt;
&lt;br /&gt;
Классический последовательный алгоритм Union-Find описан в статье Тарьяна. Каждой вершине v приписывается указатель на вершину-родителя &amp;lt;math&amp;gt;parent(v)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
1.	Изначально &amp;lt;math&amp;gt;parent(v) := v&amp;lt;/math&amp;gt; для всех вершин.&lt;br /&gt;
&lt;br /&gt;
2.	&amp;lt;math&amp;gt;FIND(v)&amp;lt;/math&amp;gt; выполняется следующим образом: полагаем &amp;lt;math&amp;gt;u := v&amp;lt;/math&amp;gt;, и далее следуем по указателям &amp;lt;math&amp;gt;u := parent(u)&amp;lt;/math&amp;gt; до тех пор, пока не станет &amp;lt;math&amp;gt;u = parent(u)&amp;lt;/math&amp;gt;. Это и будет результат операции. Дополнительно можно «схлопывать» дерево: присвоить всем посещённым вершинами: &amp;lt;math&amp;gt;parent(u_i) := u&amp;lt;/math&amp;gt;, либо производить схлопывание по пути: &amp;lt;math&amp;gt;parent(u) := parent(parent(u)))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
3.	&amp;lt;math&amp;gt;MERGE(u, v)&amp;lt;/math&amp;gt; выполняется следующим образом: вначале находим корневые вершины &amp;lt;math&amp;gt;u := FIND(u), v := FIND(v)&amp;lt;/math&amp;gt;. Если &amp;lt;math&amp;gt;u = v&amp;lt;/math&amp;gt;, то исходные вершины принадлежат одному фрагменту и объединения фрагментов не происходит. В противном случае полагаем одно из &amp;lt;math&amp;gt;parent(u) := v&amp;lt;/math&amp;gt; или &amp;lt;math&amp;gt;parent(v) := u&amp;lt;/math&amp;gt;. Дополнительно можно отслеживать количество вершин в каждом из фрагментов, чтобы меньший фрагмент подсоединять к большему, а не наоборот (оценки сложности доказываются именно при такой реализации, однако на практике алгоритм хорошо работает и без подсчёта количества вершин).&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
В алгоритме Борувки фрагменты минимального остовного дерева наращиваются постепенно присоединением минимального ребра, выходящего из каждого фрагмента.&lt;br /&gt;
&lt;br /&gt;
1.	В самом начале каждая вершина является отдельным фрагментом.&lt;br /&gt;
&lt;br /&gt;
2.	На каждом шаге:&lt;br /&gt;
&lt;br /&gt;
*	Для каждого фрагмента определяется минимальное по весу исходящее ребро.&lt;br /&gt;
&lt;br /&gt;
*	Минимальные рёбра добавляются в минимальное остовное дерево, а соответствующие фрагменты объединяются.&lt;br /&gt;
&lt;br /&gt;
3.	Алгоритм останавливается, когда остаётся только один фрагмент, либо когда ни у одного из фрагментов нет исходящих рёбер.&lt;br /&gt;
&lt;br /&gt;
Поиск минимальных исходящих рёбер может выполняться независимо для каждого фрагмента. Таким образом, данную стадию вычислений можно эффективно параллелизовать (в том числе с использованием массового параллелизма графических ускорителей).&lt;br /&gt;
&lt;br /&gt;
Объединение фрагментов также может быть реализовано параллельно, с использованием описанной выше параллельной версии структуры Union-Find.&lt;br /&gt;
&lt;br /&gt;
Аккуратный подсчёт количества активных фрагментов позволяет остановить алгоритм Борувки на один шаг раньше обычного:&lt;br /&gt;
&lt;br /&gt;
1.	В начале итерации счётчик активных фрагментов обнуляется.&lt;br /&gt;
&lt;br /&gt;
2.	На этапе поиска минимальных рёбер счётчик увеличивается на единицу для каждого фрагмента, у которого были исходящие рёбра.&lt;br /&gt;
&lt;br /&gt;
3.	На этапе объединения фрагментов счётчик уменьшается на единицу каждый раз, когда операция &amp;lt;math&amp;gt;MERGE(u, v)&amp;lt;/math&amp;gt; вернула значение истина.&lt;br /&gt;
&lt;br /&gt;
Если в конце итерации счётчик равен 0 или 1, то вычисления останавливаются.&lt;br /&gt;
Параллелизм возможен на этапе сортировки рёбер по весу, однако основной ход алгоритма является последовательным.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Последовательная сложность алгоритма Борувки для графа с &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершинами и &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; рёбрами составляет &amp;lt;math&amp;gt;O(m \ln(n))&amp;lt;/math&amp;gt; операций.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
В описанном подходе существует два уровня параллелизма: параллелизм в классическом алгоритме Борувки (нижний), и параллелизм в алгоритме обработка графа, не помещающегося в память.&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;&#039;Нижний уровень параллелизма&#039;&#039;&#039;: поиск минимальных исходящих рёбер может выполняться независимо для каждого фрагмента, благодаря чему данную стадию вычислений можно эффективно параллелизовать (как на GPU, так и на CPU). Объединение фрагментов также может быть реализовано параллельно, с использованием описанной структуры Union-Find. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Верхний уровень параллелизма&#039;&#039;&#039;: построение отдельных минимальных основных деревьев для каждого из списков ребер может производиться параллельно. Например, список ребер может разбиваться на две части, одна из которых обрабатывается на GPU, а вторая параллельно на CPU. &lt;br /&gt;
&lt;br /&gt;
[[file:MST low.png|thumb|center|1000px|Рисунок 1. Информационный граф нижнего уровня параллелизма]]&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационные графы и подробное описание каждого из них. Так же можно считать, что на рисунке 1 представлен информационный граф классического алгоритма Борувки, а на рисунке 2 — алгоритма обработки графа.&lt;br /&gt;
&lt;br /&gt;
Нижний уровень параллелизма на графе алгоритма (рисунок 1) расположен на уровнях {3, 4, 5}, соответствующим операциям параллельного поиска минимальных исходящих ребер, а так же уровнях {6, 7, 8}, соответствующим операциям параллельного объединения деревьев. Так же, различные операции копирования {1, 2, 8, 9} выполняются параллельно. После выполнения тела цикла, производится проверка {12} того, сколько деревьев осталось на текущем шаге, и если данное число не изменилось, то происходит выход из цикла, иначе аналогичная следующая итерация. &lt;br /&gt;
&lt;br /&gt;
Верхний уровень параллелизма (рисунок 2), как уже говорилось, заключается в параллельном вычислении минимального основного дерева (compute mst) для различных частей графа. Перед этим производится процесс инициализации (init process), данные которого используют последующие параллельные compute mst. Затем, после параллельных вычислений mst, происходит вычисление итого основного дерево, после чего после чего полученный результат сохраняется (save results).&lt;br /&gt;
&lt;br /&gt;
[[file:MST up.png|thumb|center|1000px|Рисунок 2. Информационный граф верхнего уровня параллелизма]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритму Борувки обладает большим потенциалом параллелизма, так как его основная операция (выбор минимального исходящего ребра во фрагменте) может исполнятся независимо для каждого фрагмента.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: взвешенный граф &amp;lt;math&amp;gt;(V, E, W)&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин &amp;lt;math&amp;gt;v_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; рёбер &amp;lt;math&amp;gt;e_j = (v^{(1)}_{j},&lt;br /&gt;
v^{(2)}_{j})&amp;lt;/math&amp;gt; с весами &amp;lt;math&amp;gt;f_j&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(m + n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: список рёбер минимального остовного дерева (для несвязного графа – список минимальных остовных деревьев для всех компонент связности).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(n)&amp;lt;/math&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;
&lt;br /&gt;
1. части, отвечающей за общую координацию вычислений&lt;br /&gt;
&lt;br /&gt;
2. части, отвечающей за параллельные вычисления на многоядерных CPU или GPU.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Описанный выше последовательный алгоритм не может применяться в параллельной программе: в реализации &amp;lt;math&amp;gt;MERGE&amp;lt;/math&amp;gt; результаты операций &amp;lt;math&amp;gt;FIND(u)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;FIND(v)&amp;lt;/math&amp;gt; могут постоянно меняться, что приведёт к race condition. Параллельный вариант алгоритма описан в статье.&lt;br /&gt;
&lt;br /&gt;
1.	Каждой вершине v соответствует запись &amp;lt;math&amp;gt;A[v] = { parent, rank }&amp;lt;/math&amp;gt;. Изначально &amp;lt;math&amp;gt;A[v] := { v, 0 }&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
2.	Вспомогательная операция &amp;lt;math&amp;gt;UPDATE(v, rank_v, u, rank_u)&amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
old := A[v]&lt;br /&gt;
&lt;br /&gt;
if old.parent != v or old.rank != rank_v then return false&lt;br /&gt;
&lt;br /&gt;
new := { u, rank_u }&lt;br /&gt;
&lt;br /&gt;
return CAS(A[v], old, new)&lt;br /&gt;
&lt;br /&gt;
3.	Операция &amp;lt;math&amp;gt;FIND(v)&amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
    while v != A[v].parent do&lt;br /&gt;
        u := A[v].parent&lt;br /&gt;
        CAS(A[v].parent, u, A[u].parent)&lt;br /&gt;
        v := A[u].parent&lt;br /&gt;
    return v&lt;br /&gt;
&lt;br /&gt;
4.	Операция UNION(u, v):&lt;br /&gt;
&lt;br /&gt;
    while true do&lt;br /&gt;
        (u, v) := (FIND(u), FIND(v))&lt;br /&gt;
        if u = v then return false&lt;br /&gt;
        (rank_u, rank_v) := (A[u].rank, A[v].rank)&lt;br /&gt;
        if (A[u].rank, u) &amp;gt; (A[v].rank, v) then&lt;br /&gt;
            swap((u, rank_u), (v, rank_v))&lt;br /&gt;
        if UPDATE(u, rank_u, v, rank_u) then&lt;br /&gt;
            if rank_u = rank_v then&lt;br /&gt;
                UPDATE(v, rank_v, v, rank_v + 1)&lt;br /&gt;
            return true&lt;br /&gt;
&lt;br /&gt;
Для описанной версии алгоритма гарантируется свойство wait-free. На практике может использоваться упрощённая версия без подсчёта рангов, обладающая более слабым свойством lock-free, но в ряде случаев выигрывающая по скорости.&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
==== Масштабируемость алгоритма ====&lt;br /&gt;
==== Масштабируемость реализации алгоритма ====&lt;br /&gt;
Проведём исследование масштабируемости параллельной реализации алгоритма Борувки согласно [[Scalability methodology|методике]]. Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов-2 [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Набор и границы значений изменяемых [[Глоссарий#Параметры запуска|параметров запуска]] реализации алгоритма: &lt;br /&gt;
&lt;br /&gt;
*	число процессоров [1 : 28] с шагом 1;&lt;br /&gt;
*	размер графа [2^20 : 2^27].&lt;br /&gt;
&lt;br /&gt;
Проведем отдельные исследования сильной масштабируемости и масштабируемости вширь реализации алгоритма Борувки.&lt;br /&gt;
&lt;br /&gt;
Производительность определена как TEPS (от англ. Traversed Edges Per Second), то есть число ребер графа, который алгоритм обрабатывает в секунду. С помощью данной метрики можно сравнивать производительность для различных размеров графа, оценивая, насколько понижается эффективность обработки графа при увеличении его размера&lt;br /&gt;
&lt;br /&gt;
[[file:MST scaling strong.png|thumb|center|700px|Рисунок 3. Параллельная реализация алгоритма Борувки масштабируемость CPU версии: производительность в зависимости от числа запущенных CPU-потоков.]]&lt;br /&gt;
&lt;br /&gt;
[[file:MST scaling wide.png|thumb|center|700px|Рисунок 4. Параллельная реализация алгоритма Борувки масштабируемость различных версий реализации алгоритма: производительность в зависимости от размера графа]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
* C++, MPI: [http://www.boost.org/libs/graph_parallel/doc/html/index.html Parallel Boost Graph Library]; функции &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#dense-boruvka-minimum-spanning-tree dense_boruvka_minimum_spanning_tree]&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#boruvka-then-merge boruvka_then_merge]&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#boruvka-mixed-merge boruvka_mixed_merge]&amp;lt;/code&amp;gt; сочетают алгоритм Борувки и [[алгоритм Крускала]].&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Начатые статьи]]&lt;/div&gt;</summary>
		<author><name>Daryin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%91%D0%BE%D1%80%D1%83%D0%B2%D0%BA%D0%B8&amp;diff=20143</id>
		<title>Алгоритм Борувки</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%91%D0%BE%D1%80%D1%83%D0%B2%D0%BA%D0%B8&amp;diff=20143"/>
		<updated>2016-12-05T21:45:21Z</updated>

		<summary type="html">&lt;p&gt;Daryin: /* Вычислительное ядро алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм Борувки&#039;&#039;&#039;&amp;lt;ref&amp;gt;Borůvka, Otakar. “O Jistém Problému Minimálním.” Práce Moravské Přírodovědecké Společnosti III, no. 3 (1926): 37–58.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Jarník, Vojtěch. “O Jistém Problému Minimálním (Z Dopisu Panu O. Borůvkovi).” Práce Moravské Přírodovědecké Společnosti 6, no. 4 (1930): 57–63.&amp;lt;/ref&amp;gt; предназначен для решения [[Построение минимального остовного дерева (MST)|задачи о построении минимального остовного дерева]] во взвешенном неориентированном графе. Алгоритм хорошо параллелизуется и является основой для распределённого [[Алгоритм GHS|алгоритма GHS]].&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Пусть задан связный неориентированный граф &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; с весами рёбер &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt;. Предполагается, что веса всех рёбер различны (если это не так, то можно упорядочить рёбра сначала по весу, а потом по номеру).&lt;br /&gt;
&lt;br /&gt;
Алгоритм Борувки основан на следующих двух свойствах задачи:&lt;br /&gt;
* &#039;&#039;&#039;Минимальное ребро фрагмента&#039;&#039;&#039;. Пусть &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; – фрагмент минимального остовного дерева и &amp;lt;math&amp;gt;e_F&amp;lt;/math&amp;gt; – ребро наименьшего веса, исходящее из &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; (т.е. ровно один его конец является вершиной из &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt;). Если ребро &amp;lt;math&amp;gt;e_F&amp;lt;/math&amp;gt; единственно, то оно принадлежит минимальному остовному дереву.&lt;br /&gt;
* &#039;&#039;&#039;Схлопывание фрагментов&#039;&#039;&#039;. Пусть &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; – фрагмент минимального остовного дерева графа &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;, а граф &amp;lt;math&amp;gt;G&#039;&amp;lt;/math&amp;gt; получен из &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt; склеиванием вершин, принадлежащих &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt;. Тогда объединение &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; и минимального остовного дерева графа &amp;lt;math&amp;gt;G&#039;&amp;lt;/math&amp;gt; даёт минимальное остовное дерево исходного графа &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В начале работы алгоритма каждая вершина графа &amp;lt;math&amp;gt;G&amp;lt;/math&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;
Пусть задан связный граф &amp;lt;math&amp;gt;G=(V,E)&amp;lt;/math&amp;gt; с вершинами &amp;lt;math&amp;gt;V = ( v_{1}, v_{2}, ..., v_{n} )&amp;lt;/math&amp;gt; и рёбрами &amp;lt;math&amp;gt;E = ( e_{1}, e_{2}, ..., e_{m} )&amp;lt;/math&amp;gt;. Каждому ребру &amp;lt;math&amp;gt;e \in E&amp;lt;/math&amp;gt; приписан вес &amp;lt;math&amp;gt;w(e)&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Требуется построить дерево &amp;lt;math&amp;gt;T^* \subseteq E&amp;lt;/math&amp;gt;, связывающее все вершины, и имеющее наименьший возможный вес среди всех таких деревьев:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; w(T^* )= \min_T( w(T)) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
где вес множества рёбер есть сумма их весов:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;w(T)=\sum_{e \in T} (w(T))&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если граф &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt; не является связным, то дерева, связывающего все вершины, не существует. &lt;br /&gt;
&lt;br /&gt;
В этом случае необходимо найти минимальной остовное дерево для каждой компоненты связности &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;. Набор таких деревьев называется минимальным остовным лесом (сокращённо MSF – Minimum Spanning Forest).&lt;br /&gt;
&lt;br /&gt;
==== Вспомогательный алгоритм: система непересекающихся множеств (Union-Find)====&lt;br /&gt;
&lt;br /&gt;
Во всех алгоритмах решения задачи требуется отслеживать, каким уже построенным фрагментам дерева принадлежат те или иные вершины графа. Для этого используется структура данных «система непересекающихся множеств» (Union-Find). Данная структура поддерживает две операции:&lt;br /&gt;
&lt;br /&gt;
1.	&amp;lt;math&amp;gt;FIND(v) = w&amp;lt;/math&amp;gt; – по вершине v возвращает вершину w – «корень» фрагмента, которому принадлежит вершина v. При этом гарантируется, что вершины u и v принадлежат одному и тому же фрагменту, тогда и только тогда, когда &amp;lt;math&amp;gt;FIND(u) = FIND(v)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
2.	&amp;lt;math&amp;gt;MERGE(u, v)&amp;lt;/math&amp;gt; – объединяет два фрагмента, которым принадлежат вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v.&amp;lt;/math&amp;gt; (Если они уже лежат в одном фрагменте, то ничего не происходит.) При практической реализации удобно, чтобы данная операция возвращала значение истина, если объединение фрагментов имело место, и ложь в противном случае.&lt;br /&gt;
&lt;br /&gt;
==== Последовательная версия====&lt;br /&gt;
&lt;br /&gt;
Классический последовательный алгоритм Union-Find описан в статье Тарьяна. Каждой вершине v приписывается указатель на вершину-родителя &amp;lt;math&amp;gt;parent(v)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
1.	Изначально &amp;lt;math&amp;gt;parent(v) := v&amp;lt;/math&amp;gt; для всех вершин.&lt;br /&gt;
&lt;br /&gt;
2.	&amp;lt;math&amp;gt;FIND(v)&amp;lt;/math&amp;gt; выполняется следующим образом: полагаем &amp;lt;math&amp;gt;u := v&amp;lt;/math&amp;gt;, и далее следуем по указателям &amp;lt;math&amp;gt;u := parent(u)&amp;lt;/math&amp;gt; до тех пор, пока не станет &amp;lt;math&amp;gt;u = parent(u)&amp;lt;/math&amp;gt;. Это и будет результат операции. Дополнительно можно «схлопывать» дерево: присвоить всем посещённым вершинами: &amp;lt;math&amp;gt;parent(ui) := u&amp;lt;/math&amp;gt;, либо производить схлопывание по пути: &amp;lt;math&amp;gt;parent(u) := parent(parent(u)))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
3.	&amp;lt;math&amp;gt;MERGE(u, v)&amp;lt;/math&amp;gt; выполняется следующим образом: вначале находим корневые вершины &amp;lt;math&amp;gt;u := FIND(u), v := FIND(v)&amp;lt;/math&amp;gt;. Если &amp;lt;math&amp;gt;u = v&amp;lt;/math&amp;gt;, то исходные вершины принадлежат одному фрагменту и объединения фрагментов не происходит. В противном случае полагаем одно из &amp;lt;math&amp;gt;parent(u) := v&amp;lt;/math&amp;gt; или &amp;lt;math&amp;gt;parent(v) := u&amp;lt;/math&amp;gt;. Дополнительно можно отслеживать количество вершин в каждом из фрагментов, чтобы меньший фрагмент подсоединять к большему, а не наоборот (оценки сложности доказываются именно при такой реализации, однако на практике алгоритм хорошо работает и без подсчёта количества вершин).&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
В алгоритме Борувки фрагменты минимального остовного дерева наращиваются постепенно присоединением минимального ребра, выходящего из каждого фрагмента.&lt;br /&gt;
&lt;br /&gt;
1.	В самом начале каждая вершина является отдельным фрагментом.&lt;br /&gt;
&lt;br /&gt;
2.	На каждом шаге:&lt;br /&gt;
&lt;br /&gt;
*	Для каждого фрагмента определяется минимальное по весу исходящее ребро.&lt;br /&gt;
&lt;br /&gt;
*	Минимальные рёбра добавляются в минимальное остовное дерево, а соответствующие фрагменты объединяются.&lt;br /&gt;
&lt;br /&gt;
3.	Алгоритм останавливается, когда остаётся только один фрагмент, либо когда ни у одного из фрагментов нет исходящих рёбер.&lt;br /&gt;
&lt;br /&gt;
Поиск минимальных исходящих рёбер может выполняться независимо для каждого фрагмента. Таким образом, данную стадию вычислений можно эффективно параллелизовать (в том числе с использованием массового параллелизма графических ускорителей).&lt;br /&gt;
&lt;br /&gt;
Объединение фрагментов также может быть реализовано параллельно, с использованием описанной выше параллельной версии структуры Union-Find.&lt;br /&gt;
&lt;br /&gt;
Аккуратный подсчёт количества активных фрагментов позволяет остановить алгоритм Борувки на один шаг раньше обычного:&lt;br /&gt;
&lt;br /&gt;
1.	В начале итерации счётчик активных фрагментов обнуляется.&lt;br /&gt;
&lt;br /&gt;
2.	На этапе поиска минимальных рёбер счётчик увеличивается на единицу для каждого фрагмента, у которого были исходящие рёбра.&lt;br /&gt;
&lt;br /&gt;
3.	На этапе объединения фрагментов счётчик уменьшается на единицу каждый раз, когда операция &amp;lt;math&amp;gt;MERGE(u, v)&amp;lt;/math&amp;gt; вернула значение истина.&lt;br /&gt;
&lt;br /&gt;
Если в конце итерации счётчик равен 0 или 1, то вычисления останавливаются.&lt;br /&gt;
Параллелизм возможен на этапе сортировки рёбер по весу, однако основной ход алгоритма является последовательным.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Последовательная сложность алгоритма Борувки для графа с &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершинами и &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; рёбрами составляет &amp;lt;math&amp;gt;O(m \ln(n))&amp;lt;/math&amp;gt; операций.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
В описанном подходе существует два уровня параллелизма: параллелизм в классическом алгоритме Борувки (нижний), и параллелизм в алгоритме обработка графа, не помещающегося в память.&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;&#039;Нижний уровень параллелизма&#039;&#039;&#039;: поиск минимальных исходящих рёбер может выполняться независимо для каждого фрагмента, благодаря чему данную стадию вычислений можно эффективно параллелизовать (как на GPU, так и на CPU). Объединение фрагментов также может быть реализовано параллельно, с использованием описанной структуры Union-Find. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Верхний уровень параллелизма&#039;&#039;&#039;: построение отдельных минимальных основных деревьев для каждого из списков ребер может производиться параллельно. Например, список ребер может разбиваться на две части, одна из которых обрабатывается на GPU, а вторая параллельно на CPU. &lt;br /&gt;
&lt;br /&gt;
[[file:MST low.png|thumb|center|1000px|Рисунок 1. Информационный граф нижнего уровня параллелизма]]&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационные графы и подробное описание каждого из них. Так же можно считать, что на рисунке 1 представлен информационный граф классического алгоритма Борувки, а на рисунке 2 — алгоритма обработки графа.&lt;br /&gt;
&lt;br /&gt;
Нижний уровень параллелизма на графе алгоритма (рисунок 1) расположен на уровнях {3, 4, 5}, соответствующим операциям параллельного поиска минимальных исходящих ребер, а так же уровнях {6, 7, 8}, соответствующим операциям параллельного объединения деревьев. Так же, различные операции копирования {1, 2, 8, 9} выполняются параллельно. После выполнения тела цикла, производится проверка {12} того, сколько деревьев осталось на текущем шаге, и если данное число не изменилось, то происходит выход из цикла, иначе аналогичная следующая итерация. &lt;br /&gt;
&lt;br /&gt;
Верхний уровень параллелизма (рисунок 2), как уже говорилось, заключается в параллельном вычислении минимального основного дерева (compute mst) для различных частей графа. Перед этим производится процесс инициализации (init process), данные которого используют последующие параллельные compute mst. Затем, после параллельных вычислений mst, происходит вычисление итого основного дерево, после чего после чего полученный результат сохраняется (save results).&lt;br /&gt;
&lt;br /&gt;
[[file:MST up.png|thumb|center|1000px|Рисунок 2. Информационный граф верхнего уровня параллелизма]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритму Борувки обладает большим потенциалом параллелизма, так как его основная операция (выбор минимального исходящего ребра во фрагменте) может исполнятся независимо для каждого фрагмента.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: взвешенный граф &amp;lt;math&amp;gt;(V, E, W)&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин &amp;lt;math&amp;gt;v_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; рёбер &amp;lt;math&amp;gt;e_j = (v^{(1)}_{j},&lt;br /&gt;
v^{(2)}_{j})&amp;lt;/math&amp;gt; с весами &amp;lt;math&amp;gt;f_j&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(m + n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: список рёбер минимального остовного дерева (для несвязного графа – список минимальных остовных деревьев для всех компонент связности).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(n)&amp;lt;/math&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;
&lt;br /&gt;
1. части, отвечающей за общую координацию вычислений&lt;br /&gt;
&lt;br /&gt;
2. части, отвечающей за параллельные вычисления на многоядерных CPU или GPU.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Описанный выше последовательный алгоритм не может применяться в параллельной программе: в реализации &amp;lt;math&amp;gt;MERGE&amp;lt;/math&amp;gt; результаты операций &amp;lt;math&amp;gt;FIND(u)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;FIND(v)&amp;lt;/math&amp;gt; могут постоянно меняться, что приведёт к race condition. Параллельный вариант алгоритма описан в статье.&lt;br /&gt;
&lt;br /&gt;
1.	Каждой вершине v соответствует запись &amp;lt;math&amp;gt;A[v] = { parent, rank }&amp;lt;/math&amp;gt;. Изначально &amp;lt;math&amp;gt;A[v] := { v, 0 }&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
2.	Вспомогательная операция &amp;lt;math&amp;gt;UPDATE(v, rank_v, u, rank_u)&amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
old := A[v]&lt;br /&gt;
&lt;br /&gt;
if old.parent != v or old.rank != rank_v then return false&lt;br /&gt;
&lt;br /&gt;
new := { u, rank_u }&lt;br /&gt;
&lt;br /&gt;
return CAS(A[v], old, new)&lt;br /&gt;
&lt;br /&gt;
3.	Операция &amp;lt;math&amp;gt;FIND(v)&amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
    while v != A[v].parent do&lt;br /&gt;
        u := A[v].parent&lt;br /&gt;
        CAS(A[v].parent, u, A[u].parent)&lt;br /&gt;
        v := A[u].parent&lt;br /&gt;
    return v&lt;br /&gt;
&lt;br /&gt;
4.	Операция UNION(u, v):&lt;br /&gt;
&lt;br /&gt;
    while true do&lt;br /&gt;
        (u, v) := (FIND(u), FIND(v))&lt;br /&gt;
        if u = v then return false&lt;br /&gt;
        (rank_u, rank_v) := (A[u].rank, A[v].rank)&lt;br /&gt;
        if (A[u].rank, u) &amp;gt; (A[v].rank, v) then&lt;br /&gt;
            swap((u, rank_u), (v, rank_v))&lt;br /&gt;
        if UPDATE(u, rank_u, v, rank_u) then&lt;br /&gt;
            if rank_u = rank_v then&lt;br /&gt;
                UPDATE(v, rank_v, v, rank_v + 1)&lt;br /&gt;
            return true&lt;br /&gt;
&lt;br /&gt;
Для описанной версии алгоритма гарантируется свойство wait-free. На практике может использоваться упрощённая версия без подсчёта рангов, обладающая более слабым свойством lock-free, но в ряде случаев выигрывающая по скорости.&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
==== Масштабируемость алгоритма ====&lt;br /&gt;
==== Масштабируемость реализации алгоритма ====&lt;br /&gt;
Проведём исследование масштабируемости параллельной реализации алгоритма Борувки согласно [[Scalability methodology|методике]]. Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов-2 [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Набор и границы значений изменяемых [[Глоссарий#Параметры запуска|параметров запуска]] реализации алгоритма: &lt;br /&gt;
&lt;br /&gt;
*	число процессоров [1 : 28] с шагом 1;&lt;br /&gt;
*	размер графа [2^20 : 2^27].&lt;br /&gt;
&lt;br /&gt;
Проведем отдельные исследования сильной масштабируемости и масштабируемости вширь реализации алгоритма Борувки.&lt;br /&gt;
&lt;br /&gt;
Производительность определена как TEPS (от англ. Traversed Edges Per Second), то есть число ребер графа, который алгоритм обрабатывает в секунду. С помощью данной метрики можно сравнивать производительность для различных размеров графа, оценивая, насколько понижается эффективность обработки графа при увеличении его размера&lt;br /&gt;
&lt;br /&gt;
[[file:MST scaling strong.png|thumb|center|700px|Рисунок 3. Параллельная реализация алгоритма Борувки масштабируемость CPU версии: производительность в зависимости от числа запущенных CPU-потоков.]]&lt;br /&gt;
&lt;br /&gt;
[[file:MST scaling wide.png|thumb|center|700px|Рисунок 4. Параллельная реализация алгоритма Борувки масштабируемость различных версий реализации алгоритма: производительность в зависимости от размера графа]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
* C++, MPI: [http://www.boost.org/libs/graph_parallel/doc/html/index.html Parallel Boost Graph Library]; функции &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#dense-boruvka-minimum-spanning-tree dense_boruvka_minimum_spanning_tree]&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#boruvka-then-merge boruvka_then_merge]&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#boruvka-mixed-merge boruvka_mixed_merge]&amp;lt;/code&amp;gt; сочетают алгоритм Борувки и [[алгоритм Крускала]].&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Начатые статьи]]&lt;/div&gt;</summary>
		<author><name>Daryin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%91%D0%BE%D1%80%D1%83%D0%B2%D0%BA%D0%B8&amp;diff=20142</id>
		<title>Алгоритм Борувки</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%91%D0%BE%D1%80%D1%83%D0%B2%D0%BA%D0%B8&amp;diff=20142"/>
		<updated>2016-12-05T21:44:43Z</updated>

		<summary type="html">&lt;p&gt;Daryin: /* Вычислительное ядро алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм Борувки&#039;&#039;&#039;&amp;lt;ref&amp;gt;Borůvka, Otakar. “O Jistém Problému Minimálním.” Práce Moravské Přírodovědecké Společnosti III, no. 3 (1926): 37–58.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Jarník, Vojtěch. “O Jistém Problému Minimálním (Z Dopisu Panu O. Borůvkovi).” Práce Moravské Přírodovědecké Společnosti 6, no. 4 (1930): 57–63.&amp;lt;/ref&amp;gt; предназначен для решения [[Построение минимального остовного дерева (MST)|задачи о построении минимального остовного дерева]] во взвешенном неориентированном графе. Алгоритм хорошо параллелизуется и является основой для распределённого [[Алгоритм GHS|алгоритма GHS]].&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Пусть задан связный неориентированный граф &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; с весами рёбер &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt;. Предполагается, что веса всех рёбер различны (если это не так, то можно упорядочить рёбра сначала по весу, а потом по номеру).&lt;br /&gt;
&lt;br /&gt;
Алгоритм Борувки основан на следующих двух свойствах задачи:&lt;br /&gt;
* &#039;&#039;&#039;Минимальное ребро фрагмента&#039;&#039;&#039;. Пусть &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; – фрагмент минимального остовного дерева и &amp;lt;math&amp;gt;e_F&amp;lt;/math&amp;gt; – ребро наименьшего веса, исходящее из &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; (т.е. ровно один его конец является вершиной из &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt;). Если ребро &amp;lt;math&amp;gt;e_F&amp;lt;/math&amp;gt; единственно, то оно принадлежит минимальному остовному дереву.&lt;br /&gt;
* &#039;&#039;&#039;Схлопывание фрагментов&#039;&#039;&#039;. Пусть &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; – фрагмент минимального остовного дерева графа &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;, а граф &amp;lt;math&amp;gt;G&#039;&amp;lt;/math&amp;gt; получен из &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt; склеиванием вершин, принадлежащих &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt;. Тогда объединение &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; и минимального остовного дерева графа &amp;lt;math&amp;gt;G&#039;&amp;lt;/math&amp;gt; даёт минимальное остовное дерево исходного графа &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В начале работы алгоритма каждая вершина графа &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt; является отдельным фрагментом. На очередном шаге у каждого фрагмента выбирается исходящее ребро минимального веса (если такое ребро существует). Выбранные рёбра добавляются в минимальное остовное дерево, а соответствующие фрагменты склеиваются.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основными операциями являются:&lt;br /&gt;
1. Поиск минимального по весу исходящего ребра в каждом фрагменте.&lt;br /&gt;
2. Объединение фрагментов.&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
В задаче требуется указать в данном связном взвешенном графе дерево, соединяющее все его вершины и имеющее наименьший возможный суммарный вес рёбер. &lt;br /&gt;
&lt;br /&gt;
Классический пример (из статьи Борувки) – спроектировать наиболее дешёвую электрическую сеть, зная стоимость устройства каждого участка электрической линии.&lt;br /&gt;
&lt;br /&gt;
Пусть задан связный граф &amp;lt;math&amp;gt;G=(V,E)&amp;lt;/math&amp;gt; с вершинами &amp;lt;math&amp;gt;V = ( v_{1}, v_{2}, ..., v_{n} )&amp;lt;/math&amp;gt; и рёбрами &amp;lt;math&amp;gt;E = ( e_{1}, e_{2}, ..., e_{m} )&amp;lt;/math&amp;gt;. Каждому ребру &amp;lt;math&amp;gt;e \in E&amp;lt;/math&amp;gt; приписан вес &amp;lt;math&amp;gt;w(e)&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Требуется построить дерево &amp;lt;math&amp;gt;T^* \subseteq E&amp;lt;/math&amp;gt;, связывающее все вершины, и имеющее наименьший возможный вес среди всех таких деревьев:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; w(T^* )= \min_T( w(T)) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
где вес множества рёбер есть сумма их весов:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;w(T)=\sum_{e \in T} (w(T))&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если граф &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt; не является связным, то дерева, связывающего все вершины, не существует. &lt;br /&gt;
&lt;br /&gt;
В этом случае необходимо найти минимальной остовное дерево для каждой компоненты связности &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;. Набор таких деревьев называется минимальным остовным лесом (сокращённо MSF – Minimum Spanning Forest).&lt;br /&gt;
&lt;br /&gt;
==== Вспомогательный алгоритм: система непересекающихся множеств (Union-Find)====&lt;br /&gt;
&lt;br /&gt;
Во всех алгоритмах решения задачи требуется отслеживать, каким уже построенным фрагментам дерева принадлежат те или иные вершины графа. Для этого используется структура данных «система непересекающихся множеств» (Union-Find). Данная структура поддерживает две операции:&lt;br /&gt;
&lt;br /&gt;
1.	&amp;lt;math&amp;gt;FIND(v) = w&amp;lt;/math&amp;gt; – по вершине v возвращает вершину w – «корень» фрагмента, которому принадлежит вершина v. При этом гарантируется, что вершины u и v принадлежат одному и тому же фрагменту, тогда и только тогда, когда &amp;lt;math&amp;gt;FIND(u) = FIND(v)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
2.	&amp;lt;math&amp;gt;MERGE(u, v)&amp;lt;/math&amp;gt; – объединяет два фрагмента, которым принадлежат вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v.&amp;lt;/math&amp;gt; (Если они уже лежат в одном фрагменте, то ничего не происходит.) При практической реализации удобно, чтобы данная операция возвращала значение истина, если объединение фрагментов имело место, и ложь в противном случае.&lt;br /&gt;
&lt;br /&gt;
==== Последовательная версия====&lt;br /&gt;
&lt;br /&gt;
Классический последовательный алгоритм Union-Find описан в статье Тарьяна. Каждой вершине v приписывается указатель на вершину-родителя &amp;lt;math&amp;gt;parent(v)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
1.	Изначально &amp;lt;math&amp;gt;parent(v) := v&amp;lt;/math&amp;gt; для всех вершин.&lt;br /&gt;
&lt;br /&gt;
2.	&amp;lt;math&amp;gt;FIND(v)&amp;lt;/math&amp;gt; выполняется следующим образом: полагаем &amp;lt;math&amp;gt;u := v&amp;lt;/math&amp;gt;, и далее следуем по указателям &amp;lt;math&amp;gt;u := parent(u)&amp;lt;/math&amp;gt; до тех пор, пока не станет &amp;lt;math&amp;gt;u = parent(u)&amp;lt;/math&amp;gt;. Это и будет результат операции. Дополнительно можно «схлопывать» дерево: присвоить всем посещённым вершинами: &amp;lt;math&amp;gt;parent(ui) := u&amp;lt;/math&amp;gt;, либо производить схлопывание по пути: &amp;lt;math&amp;gt;parent(u) := parent(parent(u)))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
3.	&amp;lt;math&amp;gt;MERGE(u, v)&amp;lt;/math&amp;gt; выполняется следующим образом: вначале находим корневые вершины &amp;lt;math&amp;gt;u := FIND(u), v := FIND(v)&amp;lt;/math&amp;gt;. Если &amp;lt;math&amp;gt;u = v&amp;lt;/math&amp;gt;, то исходные вершины принадлежат одному фрагменту и объединения фрагментов не происходит. В противном случае полагаем одно из &amp;lt;math&amp;gt;parent(u) := v&amp;lt;/math&amp;gt; или &amp;lt;math&amp;gt;parent(v) := u&amp;lt;/math&amp;gt;. Дополнительно можно отслеживать количество вершин в каждом из фрагментов, чтобы меньший фрагмент подсоединять к большему, а не наоборот (оценки сложности доказываются именно при такой реализации, однако на практике алгоритм хорошо работает и без подсчёта количества вершин).&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
В алгоритме Борувки фрагменты минимального остовного дерева наращиваются постепенно присоединением минимального ребра, выходящего из каждого фрагмента.&lt;br /&gt;
&lt;br /&gt;
1.	В самом начале каждая вершина является отдельным фрагментом.&lt;br /&gt;
&lt;br /&gt;
2.	На каждом шаге:&lt;br /&gt;
&lt;br /&gt;
*	Для каждого фрагмента определяется минимальное по весу исходящее ребро.&lt;br /&gt;
&lt;br /&gt;
*	Минимальные рёбра добавляются в минимальное остовное дерево, а соответствующие фрагменты объединяются.&lt;br /&gt;
&lt;br /&gt;
3.	Алгоритм останавливается, когда остаётся только один фрагмент, либо когда ни у одного из фрагментов нет исходящих рёбер.&lt;br /&gt;
&lt;br /&gt;
Поиск минимальных исходящих рёбер может выполняться независимо для каждого фрагмента. Таким образом, данную стадию вычислений можно эффективно параллелизовать (в том числе с использованием массового параллелизма графических ускорителей).&lt;br /&gt;
&lt;br /&gt;
Объединение фрагментов также может быть реализовано параллельно, с использованием описанной выше параллельной версии структуры Union-Find.&lt;br /&gt;
&lt;br /&gt;
Аккуратный подсчёт количества активных фрагментов позволяет остановить алгоритм Борувки на один шаг раньше обычного:&lt;br /&gt;
&lt;br /&gt;
1.	В начале итерации счётчик активных фрагментов обнуляется.&lt;br /&gt;
&lt;br /&gt;
2.	На этапе поиска минимальных рёбер счётчик увеличивается на единицу для каждого фрагмента, у которого были исходящие рёбра.&lt;br /&gt;
&lt;br /&gt;
3.	На этапе объединения фрагментов счётчик уменьшается на единицу каждый раз, когда операция &amp;lt;math&amp;gt;MERGE(u, v)&amp;lt;/math&amp;gt; вернула значение истина.&lt;br /&gt;
&lt;br /&gt;
Если в конце итерации счётчик равен 0 или 1, то вычисления останавливаются.&lt;br /&gt;
Параллелизм возможен на этапе сортировки рёбер по весу, однако основной ход алгоритма является последовательным.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Последовательная сложность алгоритма Борувки для графа с &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершинами и &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; рёбрами составляет &amp;lt;math&amp;gt;O(m \ln(n))&amp;lt;/math&amp;gt; операций.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
В описанном подходе существует два уровня параллелизма: параллелизм в классическом алгоритме Борувки (нижний), и параллелизм в алгоритме обработка графа, не помещающегося в память.&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;&#039;Нижний уровень параллелизма&#039;&#039;&#039;: поиск минимальных исходящих рёбер может выполняться независимо для каждого фрагмента, благодаря чему данную стадию вычислений можно эффективно параллелизовать (как на GPU, так и на CPU). Объединение фрагментов также может быть реализовано параллельно, с использованием описанной структуры Union-Find. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Верхний уровень параллелизма&#039;&#039;&#039;: построение отдельных минимальных основных деревьев для каждого из списков ребер может производиться параллельно. Например, список ребер может разбиваться на две части, одна из которых обрабатывается на GPU, а вторая параллельно на CPU. &lt;br /&gt;
&lt;br /&gt;
[[file:MST low.png|thumb|center|1000px|Рисунок 1. Информационный граф нижнего уровня параллелизма]]&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационные графы и подробное описание каждого из них. Так же можно считать, что на рисунке 1 представлен информационный граф классического алгоритма Борувки, а на рисунке 2 — алгоритма обработки графа.&lt;br /&gt;
&lt;br /&gt;
Нижний уровень параллелизма на графе алгоритма (рисунок 1) расположен на уровнях {3, 4, 5}, соответствующим операциям параллельного поиска минимальных исходящих ребер, а так же уровнях {6, 7, 8}, соответствующим операциям параллельного объединения деревьев. Так же, различные операции копирования {1, 2, 8, 9} выполняются параллельно. После выполнения тела цикла, производится проверка {12} того, сколько деревьев осталось на текущем шаге, и если данное число не изменилось, то происходит выход из цикла, иначе аналогичная следующая итерация. &lt;br /&gt;
&lt;br /&gt;
Верхний уровень параллелизма (рисунок 2), как уже говорилось, заключается в параллельном вычислении минимального основного дерева (compute mst) для различных частей графа. Перед этим производится процесс инициализации (init process), данные которого используют последующие параллельные compute mst. Затем, после параллельных вычислений mst, происходит вычисление итого основного дерево, после чего после чего полученный результат сохраняется (save results).&lt;br /&gt;
&lt;br /&gt;
[[file:MST up.png|thumb|center|1000px|Рисунок 2. Информационный граф верхнего уровня параллелизма]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритму Борувки обладает большим потенциалом параллелизма, так как его основная операция (выбор минимального исходящего ребра во фрагменте) может исполнятся независимо для каждого фрагмента.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: взвешенный граф &amp;lt;math&amp;gt;(V, E, W)&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин &amp;lt;math&amp;gt;v_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; рёбер &amp;lt;math&amp;gt;e_j = (v^{(1)}_{j},&lt;br /&gt;
v^{(2)}_{j})&amp;lt;/math&amp;gt; с весами &amp;lt;math&amp;gt;f_j&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(m + n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: список рёбер минимального остовного дерева (для несвязного графа – список минимальных остовных деревьев для всех компонент связности).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(n)&amp;lt;/math&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;
&lt;br /&gt;
1. части, отвечающей за общую координацию вычислений&lt;br /&gt;
&lt;br /&gt;
2. части, отвечающей за параллельные вычисления на многоядерных CPU или GPU.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Описанный выше последовательный алгоритм не может применяться в параллельной программе: в реализации &amp;lt;math&amp;gt;MERGE&amp;lt;/math&amp;gt; результаты операций &amp;lt;math&amp;gt;FIND(u)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;FIND(v)&amp;lt;/math&amp;gt; могут постоянно меняться, что приведёт к race condition. Параллельный вариант алгоритма описан в статье.&lt;br /&gt;
&lt;br /&gt;
1.	Каждой вершине v соответствует запись &amp;lt;math&amp;gt;A[v] = { parent, rank }&amp;lt;/math&amp;gt;. Изначально &amp;lt;math&amp;gt;A[v] := { v, 0 }&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
2.	Вспомогательная операция &amp;lt;math&amp;gt;UPDATE(v, rank_v, u, rank_u)&amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
old := A[v]&lt;br /&gt;
&lt;br /&gt;
if old.parent != v or old.rank != rank_v then return false&lt;br /&gt;
&lt;br /&gt;
new := { u, rank_u }&lt;br /&gt;
&lt;br /&gt;
return CAS(A[v], old, new)&lt;br /&gt;
&lt;br /&gt;
3.	Операция &amp;lt;math&amp;gt;FIND(v)&amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
    while v != A[v].parent do&lt;br /&gt;
        u := A[v].parent&lt;br /&gt;
        CAS(A[v].parent, u, A[u].parent)&lt;br /&gt;
        v := A[u].parent&lt;br /&gt;
    return v&lt;br /&gt;
&lt;br /&gt;
4.	Операция UNION(u, v):&lt;br /&gt;
&lt;br /&gt;
    while true do&lt;br /&gt;
        (u, v) := (FIND(u), FIND(v))&lt;br /&gt;
        if u = v then return false&lt;br /&gt;
        (rank_u, rank_v) := (A[u].rank, A[v].rank)&lt;br /&gt;
        if (A[u].rank, u) &amp;gt; (A[v].rank, v) then&lt;br /&gt;
            swap((u, rank_u), (v, rank_v))&lt;br /&gt;
        if UPDATE(u, rank_u, v, rank_u) then&lt;br /&gt;
            if rank_u = rank_v then&lt;br /&gt;
                UPDATE(v, rank_v, v, rank_v + 1)&lt;br /&gt;
            return true&lt;br /&gt;
&lt;br /&gt;
Для описанной версии алгоритма гарантируется свойство wait-free. На практике может использоваться упрощённая версия без подсчёта рангов, обладающая более слабым свойством lock-free, но в ряде случаев выигрывающая по скорости.&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
==== Масштабируемость алгоритма ====&lt;br /&gt;
==== Масштабируемость реализации алгоритма ====&lt;br /&gt;
Проведём исследование масштабируемости параллельной реализации алгоритма Борувки согласно [[Scalability methodology|методике]]. Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов-2 [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Набор и границы значений изменяемых [[Глоссарий#Параметры запуска|параметров запуска]] реализации алгоритма: &lt;br /&gt;
&lt;br /&gt;
*	число процессоров [1 : 28] с шагом 1;&lt;br /&gt;
*	размер графа [2^20 : 2^27].&lt;br /&gt;
&lt;br /&gt;
Проведем отдельные исследования сильной масштабируемости и масштабируемости вширь реализации алгоритма Борувки.&lt;br /&gt;
&lt;br /&gt;
Производительность определена как TEPS (от англ. Traversed Edges Per Second), то есть число ребер графа, который алгоритм обрабатывает в секунду. С помощью данной метрики можно сравнивать производительность для различных размеров графа, оценивая, насколько понижается эффективность обработки графа при увеличении его размера&lt;br /&gt;
&lt;br /&gt;
[[file:MST scaling strong.png|thumb|center|700px|Рисунок 3. Параллельная реализация алгоритма Борувки масштабируемость CPU версии: производительность в зависимости от числа запущенных CPU-потоков.]]&lt;br /&gt;
&lt;br /&gt;
[[file:MST scaling wide.png|thumb|center|700px|Рисунок 4. Параллельная реализация алгоритма Борувки масштабируемость различных версий реализации алгоритма: производительность в зависимости от размера графа]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
* C++, MPI: [http://www.boost.org/libs/graph_parallel/doc/html/index.html Parallel Boost Graph Library]; функции &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#dense-boruvka-minimum-spanning-tree dense_boruvka_minimum_spanning_tree]&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#boruvka-then-merge boruvka_then_merge]&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#boruvka-mixed-merge boruvka_mixed_merge]&amp;lt;/code&amp;gt; сочетают алгоритм Борувки и [[алгоритм Крускала]].&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Начатые статьи]]&lt;/div&gt;</summary>
		<author><name>Daryin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%91%D0%BE%D1%80%D1%83%D0%B2%D0%BA%D0%B8&amp;diff=20141</id>
		<title>Алгоритм Борувки</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%91%D0%BE%D1%80%D1%83%D0%B2%D0%BA%D0%B8&amp;diff=20141"/>
		<updated>2016-12-05T21:43:43Z</updated>

		<summary type="html">&lt;p&gt;Daryin: /* Схема реализации последовательного алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм Борувки&#039;&#039;&#039;&amp;lt;ref&amp;gt;Borůvka, Otakar. “O Jistém Problému Minimálním.” Práce Moravské Přírodovědecké Společnosti III, no. 3 (1926): 37–58.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Jarník, Vojtěch. “O Jistém Problému Minimálním (Z Dopisu Panu O. Borůvkovi).” Práce Moravské Přírodovědecké Společnosti 6, no. 4 (1930): 57–63.&amp;lt;/ref&amp;gt; предназначен для решения [[Построение минимального остовного дерева (MST)|задачи о построении минимального остовного дерева]] во взвешенном неориентированном графе. Алгоритм хорошо параллелизуется и является основой для распределённого [[Алгоритм GHS|алгоритма GHS]].&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Пусть задан связный неориентированный граф &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; с весами рёбер &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt;. Предполагается, что веса всех рёбер различны (если это не так, то можно упорядочить рёбра сначала по весу, а потом по номеру).&lt;br /&gt;
&lt;br /&gt;
Алгоритм Борувки основан на следующих двух свойствах задачи:&lt;br /&gt;
* &#039;&#039;&#039;Минимальное ребро фрагмента&#039;&#039;&#039;. Пусть &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; – фрагмент минимального остовного дерева и &amp;lt;math&amp;gt;e_F&amp;lt;/math&amp;gt; – ребро наименьшего веса, исходящее из &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; (т.е. ровно один его конец является вершиной из &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt;). Если ребро &amp;lt;math&amp;gt;e_F&amp;lt;/math&amp;gt; единственно, то оно принадлежит минимальному остовному дереву.&lt;br /&gt;
* &#039;&#039;&#039;Схлопывание фрагментов&#039;&#039;&#039;. Пусть &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; – фрагмент минимального остовного дерева графа &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;, а граф &amp;lt;math&amp;gt;G&#039;&amp;lt;/math&amp;gt; получен из &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt; склеиванием вершин, принадлежащих &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt;. Тогда объединение &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; и минимального остовного дерева графа &amp;lt;math&amp;gt;G&#039;&amp;lt;/math&amp;gt; даёт минимальное остовное дерево исходного графа &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В начале работы алгоритма каждая вершина графа &amp;lt;math&amp;gt;G&amp;lt;/math&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;math&amp;gt;G=(V,E)&amp;lt;/math&amp;gt; с вершинами &amp;lt;math&amp;gt;V = ( v_{1}, v_{2}, ..., v_{n} )&amp;lt;/math&amp;gt; и рёбрами &amp;lt;math&amp;gt;E = ( e_{1}, e_{2}, ..., e_{m} )&amp;lt;/math&amp;gt;. Каждому ребру &amp;lt;math&amp;gt;e \in E&amp;lt;/math&amp;gt; приписан вес &amp;lt;math&amp;gt;w(e)&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Требуется построить дерево &amp;lt;math&amp;gt;T^* \subseteq E&amp;lt;/math&amp;gt;, связывающее все вершины, и имеющее наименьший возможный вес среди всех таких деревьев:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; w(T^* )= \min_T( w(T)) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
где вес множества рёбер есть сумма их весов:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;w(T)=\sum_{e \in T} (w(T))&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если граф &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt; не является связным, то дерева, связывающего все вершины, не существует. &lt;br /&gt;
&lt;br /&gt;
В этом случае необходимо найти минимальной остовное дерево для каждой компоненты связности &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;. Набор таких деревьев называется минимальным остовным лесом (сокращённо MSF – Minimum Spanning Forest).&lt;br /&gt;
&lt;br /&gt;
==== Вспомогательный алгоритм: система непересекающихся множеств (Union-Find)====&lt;br /&gt;
&lt;br /&gt;
Во всех алгоритмах решения задачи требуется отслеживать, каким уже построенным фрагментам дерева принадлежат те или иные вершины графа. Для этого используется структура данных «система непересекающихся множеств» (Union-Find). Данная структура поддерживает две операции:&lt;br /&gt;
&lt;br /&gt;
1.	&amp;lt;math&amp;gt;FIND(v) = w&amp;lt;/math&amp;gt; – по вершине v возвращает вершину w – «корень» фрагмента, которому принадлежит вершина v. При этом гарантируется, что вершины u и v принадлежат одному и тому же фрагменту, тогда и только тогда, когда &amp;lt;math&amp;gt;FIND(u) = FIND(v)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
2.	&amp;lt;math&amp;gt;MERGE(u, v)&amp;lt;/math&amp;gt; – объединяет два фрагмента, которым принадлежат вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v.&amp;lt;/math&amp;gt; (Если они уже лежат в одном фрагменте, то ничего не происходит.) При практической реализации удобно, чтобы данная операция возвращала значение истина, если объединение фрагментов имело место, и ложь в противном случае.&lt;br /&gt;
&lt;br /&gt;
==== Последовательная версия====&lt;br /&gt;
&lt;br /&gt;
Классический последовательный алгоритм Union-Find описан в статье Тарьяна. Каждой вершине v приписывается указатель на вершину-родителя &amp;lt;math&amp;gt;parent(v)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
1.	Изначально &amp;lt;math&amp;gt;parent(v) := v&amp;lt;/math&amp;gt; для всех вершин.&lt;br /&gt;
&lt;br /&gt;
2.	&amp;lt;math&amp;gt;FIND(v)&amp;lt;/math&amp;gt; выполняется следующим образом: полагаем &amp;lt;math&amp;gt;u := v&amp;lt;/math&amp;gt;, и далее следуем по указателям &amp;lt;math&amp;gt;u := parent(u)&amp;lt;/math&amp;gt; до тех пор, пока не станет &amp;lt;math&amp;gt;u = parent(u)&amp;lt;/math&amp;gt;. Это и будет результат операции. Дополнительно можно «схлопывать» дерево: присвоить всем посещённым вершинами: &amp;lt;math&amp;gt;parent(ui) := u&amp;lt;/math&amp;gt;, либо производить схлопывание по пути: &amp;lt;math&amp;gt;parent(u) := parent(parent(u)))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
3.	&amp;lt;math&amp;gt;MERGE(u, v)&amp;lt;/math&amp;gt; выполняется следующим образом: вначале находим корневые вершины &amp;lt;math&amp;gt;u := FIND(u), v := FIND(v)&amp;lt;/math&amp;gt;. Если &amp;lt;math&amp;gt;u = v&amp;lt;/math&amp;gt;, то исходные вершины принадлежат одному фрагменту и объединения фрагментов не происходит. В противном случае полагаем одно из &amp;lt;math&amp;gt;parent(u) := v&amp;lt;/math&amp;gt; или &amp;lt;math&amp;gt;parent(v) := u&amp;lt;/math&amp;gt;. Дополнительно можно отслеживать количество вершин в каждом из фрагментов, чтобы меньший фрагмент подсоединять к большему, а не наоборот (оценки сложности доказываются именно при такой реализации, однако на практике алгоритм хорошо работает и без подсчёта количества вершин).&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
В алгоритме Борувки фрагменты минимального остовного дерева наращиваются постепенно присоединением минимального ребра, выходящего из каждого фрагмента.&lt;br /&gt;
&lt;br /&gt;
1.	В самом начале каждая вершина является отдельным фрагментом.&lt;br /&gt;
&lt;br /&gt;
2.	На каждом шаге:&lt;br /&gt;
&lt;br /&gt;
*	Для каждого фрагмента определяется минимальное по весу исходящее ребро.&lt;br /&gt;
&lt;br /&gt;
*	Минимальные рёбра добавляются в минимальное остовное дерево, а соответствующие фрагменты объединяются.&lt;br /&gt;
&lt;br /&gt;
3.	Алгоритм останавливается, когда остаётся только один фрагмент, либо когда ни у одного из фрагментов нет исходящих рёбер.&lt;br /&gt;
&lt;br /&gt;
Поиск минимальных исходящих рёбер может выполняться независимо для каждого фрагмента. Таким образом, данную стадию вычислений можно эффективно параллелизовать (в том числе с использованием массового параллелизма графических ускорителей).&lt;br /&gt;
&lt;br /&gt;
Объединение фрагментов также может быть реализовано параллельно, с использованием описанной выше параллельной версии структуры Union-Find.&lt;br /&gt;
&lt;br /&gt;
Аккуратный подсчёт количества активных фрагментов позволяет остановить алгоритм Борувки на один шаг раньше обычного:&lt;br /&gt;
&lt;br /&gt;
1.	В начале итерации счётчик активных фрагментов обнуляется.&lt;br /&gt;
&lt;br /&gt;
2.	На этапе поиска минимальных рёбер счётчик увеличивается на единицу для каждого фрагмента, у которого были исходящие рёбра.&lt;br /&gt;
&lt;br /&gt;
3.	На этапе объединения фрагментов счётчик уменьшается на единицу каждый раз, когда операция &amp;lt;math&amp;gt;MERGE(u, v)&amp;lt;/math&amp;gt; вернула значение истина.&lt;br /&gt;
&lt;br /&gt;
Если в конце итерации счётчик равен 0 или 1, то вычисления останавливаются.&lt;br /&gt;
Параллелизм возможен на этапе сортировки рёбер по весу, однако основной ход алгоритма является последовательным.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Последовательная сложность алгоритма Борувки для графа с &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершинами и &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; рёбрами составляет &amp;lt;math&amp;gt;O(m \ln(n))&amp;lt;/math&amp;gt; операций.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
В описанном подходе существует два уровня параллелизма: параллелизм в классическом алгоритме Борувки (нижний), и параллелизм в алгоритме обработка графа, не помещающегося в память.&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;&#039;Нижний уровень параллелизма&#039;&#039;&#039;: поиск минимальных исходящих рёбер может выполняться независимо для каждого фрагмента, благодаря чему данную стадию вычислений можно эффективно параллелизовать (как на GPU, так и на CPU). Объединение фрагментов также может быть реализовано параллельно, с использованием описанной структуры Union-Find. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Верхний уровень параллелизма&#039;&#039;&#039;: построение отдельных минимальных основных деревьев для каждого из списков ребер может производиться параллельно. Например, список ребер может разбиваться на две части, одна из которых обрабатывается на GPU, а вторая параллельно на CPU. &lt;br /&gt;
&lt;br /&gt;
[[file:MST low.png|thumb|center|1000px|Рисунок 1. Информационный граф нижнего уровня параллелизма]]&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационные графы и подробное описание каждого из них. Так же можно считать, что на рисунке 1 представлен информационный граф классического алгоритма Борувки, а на рисунке 2 — алгоритма обработки графа.&lt;br /&gt;
&lt;br /&gt;
Нижний уровень параллелизма на графе алгоритма (рисунок 1) расположен на уровнях {3, 4, 5}, соответствующим операциям параллельного поиска минимальных исходящих ребер, а так же уровнях {6, 7, 8}, соответствующим операциям параллельного объединения деревьев. Так же, различные операции копирования {1, 2, 8, 9} выполняются параллельно. После выполнения тела цикла, производится проверка {12} того, сколько деревьев осталось на текущем шаге, и если данное число не изменилось, то происходит выход из цикла, иначе аналогичная следующая итерация. &lt;br /&gt;
&lt;br /&gt;
Верхний уровень параллелизма (рисунок 2), как уже говорилось, заключается в параллельном вычислении минимального основного дерева (compute mst) для различных частей графа. Перед этим производится процесс инициализации (init process), данные которого используют последующие параллельные compute mst. Затем, после параллельных вычислений mst, происходит вычисление итого основного дерево, после чего после чего полученный результат сохраняется (save results).&lt;br /&gt;
&lt;br /&gt;
[[file:MST up.png|thumb|center|1000px|Рисунок 2. Информационный граф верхнего уровня параллелизма]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритму Борувки обладает большим потенциалом параллелизма, так как его основная операция (выбор минимального исходящего ребра во фрагменте) может исполнятся независимо для каждого фрагмента.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: взвешенный граф &amp;lt;math&amp;gt;(V, E, W)&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин &amp;lt;math&amp;gt;v_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; рёбер &amp;lt;math&amp;gt;e_j = (v^{(1)}_{j},&lt;br /&gt;
v^{(2)}_{j})&amp;lt;/math&amp;gt; с весами &amp;lt;math&amp;gt;f_j&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(m + n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: список рёбер минимального остовного дерева (для несвязного графа – список минимальных остовных деревьев для всех компонент связности).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(n)&amp;lt;/math&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;
&lt;br /&gt;
1. части, отвечающей за общую координацию вычислений&lt;br /&gt;
&lt;br /&gt;
2. части, отвечающей за параллельные вычисления на многоядерных CPU или GPU.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Описанный выше последовательный алгоритм не может применяться в параллельной программе: в реализации &amp;lt;math&amp;gt;MERGE&amp;lt;/math&amp;gt; результаты операций &amp;lt;math&amp;gt;FIND(u)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;FIND(v)&amp;lt;/math&amp;gt; могут постоянно меняться, что приведёт к race condition. Параллельный вариант алгоритма описан в статье.&lt;br /&gt;
&lt;br /&gt;
1.	Каждой вершине v соответствует запись &amp;lt;math&amp;gt;A[v] = { parent, rank }&amp;lt;/math&amp;gt;. Изначально &amp;lt;math&amp;gt;A[v] := { v, 0 }&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
2.	Вспомогательная операция &amp;lt;math&amp;gt;UPDATE(v, rank_v, u, rank_u)&amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
old := A[v]&lt;br /&gt;
&lt;br /&gt;
if old.parent != v or old.rank != rank_v then return false&lt;br /&gt;
&lt;br /&gt;
new := { u, rank_u }&lt;br /&gt;
&lt;br /&gt;
return CAS(A[v], old, new)&lt;br /&gt;
&lt;br /&gt;
3.	Операция &amp;lt;math&amp;gt;FIND(v)&amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
    while v != A[v].parent do&lt;br /&gt;
        u := A[v].parent&lt;br /&gt;
        CAS(A[v].parent, u, A[u].parent)&lt;br /&gt;
        v := A[u].parent&lt;br /&gt;
    return v&lt;br /&gt;
&lt;br /&gt;
4.	Операция UNION(u, v):&lt;br /&gt;
&lt;br /&gt;
    while true do&lt;br /&gt;
        (u, v) := (FIND(u), FIND(v))&lt;br /&gt;
        if u = v then return false&lt;br /&gt;
        (rank_u, rank_v) := (A[u].rank, A[v].rank)&lt;br /&gt;
        if (A[u].rank, u) &amp;gt; (A[v].rank, v) then&lt;br /&gt;
            swap((u, rank_u), (v, rank_v))&lt;br /&gt;
        if UPDATE(u, rank_u, v, rank_u) then&lt;br /&gt;
            if rank_u = rank_v then&lt;br /&gt;
                UPDATE(v, rank_v, v, rank_v + 1)&lt;br /&gt;
            return true&lt;br /&gt;
&lt;br /&gt;
Для описанной версии алгоритма гарантируется свойство wait-free. На практике может использоваться упрощённая версия без подсчёта рангов, обладающая более слабым свойством lock-free, но в ряде случаев выигрывающая по скорости.&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
==== Масштабируемость алгоритма ====&lt;br /&gt;
==== Масштабируемость реализации алгоритма ====&lt;br /&gt;
Проведём исследование масштабируемости параллельной реализации алгоритма Борувки согласно [[Scalability methodology|методике]]. Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов-2 [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Набор и границы значений изменяемых [[Глоссарий#Параметры запуска|параметров запуска]] реализации алгоритма: &lt;br /&gt;
&lt;br /&gt;
*	число процессоров [1 : 28] с шагом 1;&lt;br /&gt;
*	размер графа [2^20 : 2^27].&lt;br /&gt;
&lt;br /&gt;
Проведем отдельные исследования сильной масштабируемости и масштабируемости вширь реализации алгоритма Борувки.&lt;br /&gt;
&lt;br /&gt;
Производительность определена как TEPS (от англ. Traversed Edges Per Second), то есть число ребер графа, который алгоритм обрабатывает в секунду. С помощью данной метрики можно сравнивать производительность для различных размеров графа, оценивая, насколько понижается эффективность обработки графа при увеличении его размера&lt;br /&gt;
&lt;br /&gt;
[[file:MST scaling strong.png|thumb|center|700px|Рисунок 3. Параллельная реализация алгоритма Борувки масштабируемость CPU версии: производительность в зависимости от числа запущенных CPU-потоков.]]&lt;br /&gt;
&lt;br /&gt;
[[file:MST scaling wide.png|thumb|center|700px|Рисунок 4. Параллельная реализация алгоритма Борувки масштабируемость различных версий реализации алгоритма: производительность в зависимости от размера графа]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
* C++, MPI: [http://www.boost.org/libs/graph_parallel/doc/html/index.html Parallel Boost Graph Library]; функции &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#dense-boruvka-minimum-spanning-tree dense_boruvka_minimum_spanning_tree]&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#boruvka-then-merge boruvka_then_merge]&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#boruvka-mixed-merge boruvka_mixed_merge]&amp;lt;/code&amp;gt; сочетают алгоритм Борувки и [[алгоритм Крускала]].&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Начатые статьи]]&lt;/div&gt;</summary>
		<author><name>Daryin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%91%D0%BE%D1%80%D1%83%D0%B2%D0%BA%D0%B8&amp;diff=20140</id>
		<title>Алгоритм Борувки</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%91%D0%BE%D1%80%D1%83%D0%B2%D0%BA%D0%B8&amp;diff=20140"/>
		<updated>2016-12-05T21:42:14Z</updated>

		<summary type="html">&lt;p&gt;Daryin: /* Последовательная сложность алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм Борувки&#039;&#039;&#039;&amp;lt;ref&amp;gt;Borůvka, Otakar. “O Jistém Problému Minimálním.” Práce Moravské Přírodovědecké Společnosti III, no. 3 (1926): 37–58.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Jarník, Vojtěch. “O Jistém Problému Minimálním (Z Dopisu Panu O. Borůvkovi).” Práce Moravské Přírodovědecké Společnosti 6, no. 4 (1930): 57–63.&amp;lt;/ref&amp;gt; предназначен для решения [[Построение минимального остовного дерева (MST)|задачи о построении минимального остовного дерева]] во взвешенном неориентированном графе. Алгоритм хорошо параллелизуется и является основой для распределённого [[Алгоритм GHS|алгоритма GHS]].&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Пусть задан связный неориентированный граф &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; с весами рёбер &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt;. Предполагается, что веса всех рёбер различны (если это не так, то можно упорядочить рёбра сначала по весу, а потом по номеру).&lt;br /&gt;
&lt;br /&gt;
Алгоритм Борувки основан на следующих двух свойствах задачи:&lt;br /&gt;
* &#039;&#039;&#039;Минимальное ребро фрагмента&#039;&#039;&#039;. Пусть &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; – фрагмент минимального остовного дерева и &amp;lt;math&amp;gt;e_F&amp;lt;/math&amp;gt; – ребро наименьшего веса, исходящее из &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; (т.е. ровно один его конец является вершиной из &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt;). Если ребро &amp;lt;math&amp;gt;e_F&amp;lt;/math&amp;gt; единственно, то оно принадлежит минимальному остовному дереву.&lt;br /&gt;
* &#039;&#039;&#039;Схлопывание фрагментов&#039;&#039;&#039;. Пусть &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; – фрагмент минимального остовного дерева графа &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;, а граф &amp;lt;math&amp;gt;G&#039;&amp;lt;/math&amp;gt; получен из &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt; склеиванием вершин, принадлежащих &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt;. Тогда объединение &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; и минимального остовного дерева графа &amp;lt;math&amp;gt;G&#039;&amp;lt;/math&amp;gt; даёт минимальное остовное дерево исходного графа &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В начале работы алгоритма каждая вершина графа &amp;lt;math&amp;gt;G&amp;lt;/math&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;math&amp;gt;G=(V,E)&amp;lt;/math&amp;gt; с вершинами &amp;lt;math&amp;gt;V = ( v_{1}, v_{2}, ..., v_{n} )&amp;lt;/math&amp;gt; и рёбрами &amp;lt;math&amp;gt;E = ( e_{1}, e_{2}, ..., e_{m} )&amp;lt;/math&amp;gt;. Каждому ребру &amp;lt;math&amp;gt;e \in E&amp;lt;/math&amp;gt; приписан вес &amp;lt;math&amp;gt;w(e)&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Требуется построить дерево &amp;lt;math&amp;gt;T^* \subseteq E&amp;lt;/math&amp;gt;, связывающее все вершины, и имеющее наименьший возможный вес среди всех таких деревьев:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; w(T^* )= \min_T( w(T)) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
где вес множества рёбер есть сумма их весов:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;w(T)=\sum_{e \in T} (w(T))&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если граф &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt; не является связным, то дерева, связывающего все вершины, не существует. &lt;br /&gt;
&lt;br /&gt;
В этом случае необходимо найти минимальной остовное дерево для каждой компоненты связности &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;. Набор таких деревьев называется минимальным остовным лесом (сокращённо MSF – Minimum Spanning Forest).&lt;br /&gt;
&lt;br /&gt;
==== Вспомогательный алгоритм: система непересекающихся множеств (Union-Find)====&lt;br /&gt;
&lt;br /&gt;
Во всех алгоритмах решения задачи требуется отслеживать, каким уже построенным фрагментам дерева принадлежат те или иные вершины графа. Для этого используется структура данных «система непересекающихся множеств» (Union-Find). Данная структура поддерживает две операции:&lt;br /&gt;
&lt;br /&gt;
1.	&amp;lt;math&amp;gt;FIND(v) = w&amp;lt;/math&amp;gt; – по вершине v возвращает вершину w – «корень» фрагмента, которому принадлежит вершина v. При этом гарантируется, что вершины u и v принадлежат одному и тому же фрагменту, тогда и только тогда, когда &amp;lt;math&amp;gt;FIND(u) = FIND(v)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
2.	&amp;lt;math&amp;gt;MERGE(u, v)&amp;lt;/math&amp;gt; – объединяет два фрагмента, которым принадлежат вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v.&amp;lt;/math&amp;gt; (Если они уже лежат в одном фрагменте, то ничего не происходит.) При практической реализации удобно, чтобы данная операция возвращала значение истина, если объединение фрагментов имело место, и ложь в противном случае.&lt;br /&gt;
&lt;br /&gt;
==== Последовательная версия====&lt;br /&gt;
&lt;br /&gt;
Классический последовательный алгоритм Union-Find описан в статье Тарьяна. Каждой вершине v приписывается указатель на вершину-родителя &amp;lt;math&amp;gt;parent(v)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
1.	Изначально &amp;lt;math&amp;gt;parent(v) := v&amp;lt;/math&amp;gt; для всех вершин.&lt;br /&gt;
&lt;br /&gt;
2.	&amp;lt;math&amp;gt;FIND(v)&amp;lt;/math&amp;gt; выполняется следующим образом: полагаем &amp;lt;math&amp;gt;u := v&amp;lt;/math&amp;gt;, и далее следуем по указателям &amp;lt;math&amp;gt;u := parent(u)&amp;lt;/math&amp;gt; до тех пор, пока не станет &amp;lt;math&amp;gt;u = parent(u)&amp;lt;/math&amp;gt;. Это и будет результат операции. Дополнительно можно «схлопывать» дерево: присвоить всем посещённым вершинами: &amp;lt;math&amp;gt;parent(ui) := u&amp;lt;/math&amp;gt;, либо производить схлопывание по пути: &amp;lt;math&amp;gt;parent(u) := parent(parent(u)))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
3.	&amp;lt;math&amp;gt;MERGE(u, v)&amp;lt;/math&amp;gt; выполняется следующим образом: вначале находим корневые вершины &amp;lt;math&amp;gt;u := FIND(u), v := FIND(v)&amp;lt;/math&amp;gt;. Если &amp;lt;math&amp;gt;u = v&amp;lt;/math&amp;gt;, то исходные вершины принадлежат одному фрагменту и объединения фрагментов не происходит. В противном случае полагаем одно из &amp;lt;math&amp;gt;parent(u) := v&amp;lt;/math&amp;gt; или &amp;lt;math&amp;gt;parent(v) := u&amp;lt;/math&amp;gt;. Дополнительно можно отслеживать количество вершин в каждом из фрагментов, чтобы меньший фрагмент подсоединять к большему, а не наоборот (оценки сложности доказываются именно при такой реализации, однако на практике алгоритм хорошо работает и без подсчёта количества вершин).&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
В алгоритме Борувки фрагменты минимального остовного дерева наращиваются постепенно присоединением минимального ребра, выходящего из каждого фрагмента.&lt;br /&gt;
&lt;br /&gt;
1.	В самом начале каждая вершина является отдельным фрагментом.&lt;br /&gt;
&lt;br /&gt;
2.	На каждом шаге:&lt;br /&gt;
&lt;br /&gt;
*	Для каждого фрагмента определяется минимальное по весу исходящее ребро.&lt;br /&gt;
&lt;br /&gt;
*	Минимальные рёбра добавляются в минимальное остовное дерево, а соответствующие фрагменты объединяются.&lt;br /&gt;
&lt;br /&gt;
3.	Алгоритм останавливается, когда остаётся только один фрагмент, либо когда ни у одного из фрагментов нет исходящих рёбер.&lt;br /&gt;
&lt;br /&gt;
Поиск минимальных исходящих рёбер может выполняться независимо для каждого фрагмента. Таким образом, данную стадию вычислений можно эффективно параллелизовать (в том числе с использованием массового параллелизма графических ускорителей).&lt;br /&gt;
&lt;br /&gt;
Объединение фрагментов также может быть реализовано параллельно, с использованием описанной выше параллельной версии структуры Union-Find.&lt;br /&gt;
&lt;br /&gt;
Аккуратный подсчёт количества активных фрагментов позволяет остановить алгоритм Борувки на один шаг раньше обычного:&lt;br /&gt;
&lt;br /&gt;
1.	В начале итерации счётчик активных фрагментов обнуляется.&lt;br /&gt;
&lt;br /&gt;
2.	На этапе поиска минимальных рёбер счётчик увеличивается на единицу для каждого фрагмента, у которого были исходящие рёбра.&lt;br /&gt;
&lt;br /&gt;
3.	На этапе объединения фрагментов счётчик уменьшается на единицу каждый раз, когда операция UNION(u, v) вернула значение истина.&lt;br /&gt;
&lt;br /&gt;
Если в конце итерации счётчик равен 0 или 1, то вычисления останавливаются.&lt;br /&gt;
Параллелизм возможен на этапе сортировки рёбер по весу, однако основной ход алгоритма является последовательным.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Последовательная сложность алгоритма Борувки для графа с &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершинами и &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; рёбрами составляет &amp;lt;math&amp;gt;O(m \ln(n))&amp;lt;/math&amp;gt; операций.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
В описанном подходе существует два уровня параллелизма: параллелизм в классическом алгоритме Борувки (нижний), и параллелизм в алгоритме обработка графа, не помещающегося в память.&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;&#039;Нижний уровень параллелизма&#039;&#039;&#039;: поиск минимальных исходящих рёбер может выполняться независимо для каждого фрагмента, благодаря чему данную стадию вычислений можно эффективно параллелизовать (как на GPU, так и на CPU). Объединение фрагментов также может быть реализовано параллельно, с использованием описанной структуры Union-Find. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Верхний уровень параллелизма&#039;&#039;&#039;: построение отдельных минимальных основных деревьев для каждого из списков ребер может производиться параллельно. Например, список ребер может разбиваться на две части, одна из которых обрабатывается на GPU, а вторая параллельно на CPU. &lt;br /&gt;
&lt;br /&gt;
[[file:MST low.png|thumb|center|1000px|Рисунок 1. Информационный граф нижнего уровня параллелизма]]&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационные графы и подробное описание каждого из них. Так же можно считать, что на рисунке 1 представлен информационный граф классического алгоритма Борувки, а на рисунке 2 — алгоритма обработки графа.&lt;br /&gt;
&lt;br /&gt;
Нижний уровень параллелизма на графе алгоритма (рисунок 1) расположен на уровнях {3, 4, 5}, соответствующим операциям параллельного поиска минимальных исходящих ребер, а так же уровнях {6, 7, 8}, соответствующим операциям параллельного объединения деревьев. Так же, различные операции копирования {1, 2, 8, 9} выполняются параллельно. После выполнения тела цикла, производится проверка {12} того, сколько деревьев осталось на текущем шаге, и если данное число не изменилось, то происходит выход из цикла, иначе аналогичная следующая итерация. &lt;br /&gt;
&lt;br /&gt;
Верхний уровень параллелизма (рисунок 2), как уже говорилось, заключается в параллельном вычислении минимального основного дерева (compute mst) для различных частей графа. Перед этим производится процесс инициализации (init process), данные которого используют последующие параллельные compute mst. Затем, после параллельных вычислений mst, происходит вычисление итого основного дерево, после чего после чего полученный результат сохраняется (save results).&lt;br /&gt;
&lt;br /&gt;
[[file:MST up.png|thumb|center|1000px|Рисунок 2. Информационный граф верхнего уровня параллелизма]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритму Борувки обладает большим потенциалом параллелизма, так как его основная операция (выбор минимального исходящего ребра во фрагменте) может исполнятся независимо для каждого фрагмента.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: взвешенный граф &amp;lt;math&amp;gt;(V, E, W)&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин &amp;lt;math&amp;gt;v_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; рёбер &amp;lt;math&amp;gt;e_j = (v^{(1)}_{j},&lt;br /&gt;
v^{(2)}_{j})&amp;lt;/math&amp;gt; с весами &amp;lt;math&amp;gt;f_j&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(m + n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: список рёбер минимального остовного дерева (для несвязного графа – список минимальных остовных деревьев для всех компонент связности).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(n)&amp;lt;/math&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;
&lt;br /&gt;
1. части, отвечающей за общую координацию вычислений&lt;br /&gt;
&lt;br /&gt;
2. части, отвечающей за параллельные вычисления на многоядерных CPU или GPU.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Описанный выше последовательный алгоритм не может применяться в параллельной программе: в реализации &amp;lt;math&amp;gt;MERGE&amp;lt;/math&amp;gt; результаты операций &amp;lt;math&amp;gt;FIND(u)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;FIND(v)&amp;lt;/math&amp;gt; могут постоянно меняться, что приведёт к race condition. Параллельный вариант алгоритма описан в статье.&lt;br /&gt;
&lt;br /&gt;
1.	Каждой вершине v соответствует запись &amp;lt;math&amp;gt;A[v] = { parent, rank }&amp;lt;/math&amp;gt;. Изначально &amp;lt;math&amp;gt;A[v] := { v, 0 }&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
2.	Вспомогательная операция &amp;lt;math&amp;gt;UPDATE(v, rank_v, u, rank_u)&amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
old := A[v]&lt;br /&gt;
&lt;br /&gt;
if old.parent != v or old.rank != rank_v then return false&lt;br /&gt;
&lt;br /&gt;
new := { u, rank_u }&lt;br /&gt;
&lt;br /&gt;
return CAS(A[v], old, new)&lt;br /&gt;
&lt;br /&gt;
3.	Операция &amp;lt;math&amp;gt;FIND(v)&amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
    while v != A[v].parent do&lt;br /&gt;
        u := A[v].parent&lt;br /&gt;
        CAS(A[v].parent, u, A[u].parent)&lt;br /&gt;
        v := A[u].parent&lt;br /&gt;
    return v&lt;br /&gt;
&lt;br /&gt;
4.	Операция UNION(u, v):&lt;br /&gt;
&lt;br /&gt;
    while true do&lt;br /&gt;
        (u, v) := (FIND(u), FIND(v))&lt;br /&gt;
        if u = v then return false&lt;br /&gt;
        (rank_u, rank_v) := (A[u].rank, A[v].rank)&lt;br /&gt;
        if (A[u].rank, u) &amp;gt; (A[v].rank, v) then&lt;br /&gt;
            swap((u, rank_u), (v, rank_v))&lt;br /&gt;
        if UPDATE(u, rank_u, v, rank_u) then&lt;br /&gt;
            if rank_u = rank_v then&lt;br /&gt;
                UPDATE(v, rank_v, v, rank_v + 1)&lt;br /&gt;
            return true&lt;br /&gt;
&lt;br /&gt;
Для описанной версии алгоритма гарантируется свойство wait-free. На практике может использоваться упрощённая версия без подсчёта рангов, обладающая более слабым свойством lock-free, но в ряде случаев выигрывающая по скорости.&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
==== Масштабируемость алгоритма ====&lt;br /&gt;
==== Масштабируемость реализации алгоритма ====&lt;br /&gt;
Проведём исследование масштабируемости параллельной реализации алгоритма Борувки согласно [[Scalability methodology|методике]]. Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов-2 [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Набор и границы значений изменяемых [[Глоссарий#Параметры запуска|параметров запуска]] реализации алгоритма: &lt;br /&gt;
&lt;br /&gt;
*	число процессоров [1 : 28] с шагом 1;&lt;br /&gt;
*	размер графа [2^20 : 2^27].&lt;br /&gt;
&lt;br /&gt;
Проведем отдельные исследования сильной масштабируемости и масштабируемости вширь реализации алгоритма Борувки.&lt;br /&gt;
&lt;br /&gt;
Производительность определена как TEPS (от англ. Traversed Edges Per Second), то есть число ребер графа, который алгоритм обрабатывает в секунду. С помощью данной метрики можно сравнивать производительность для различных размеров графа, оценивая, насколько понижается эффективность обработки графа при увеличении его размера&lt;br /&gt;
&lt;br /&gt;
[[file:MST scaling strong.png|thumb|center|700px|Рисунок 3. Параллельная реализация алгоритма Борувки масштабируемость CPU версии: производительность в зависимости от числа запущенных CPU-потоков.]]&lt;br /&gt;
&lt;br /&gt;
[[file:MST scaling wide.png|thumb|center|700px|Рисунок 4. Параллельная реализация алгоритма Борувки масштабируемость различных версий реализации алгоритма: производительность в зависимости от размера графа]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
* C++, MPI: [http://www.boost.org/libs/graph_parallel/doc/html/index.html Parallel Boost Graph Library]; функции &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#dense-boruvka-minimum-spanning-tree dense_boruvka_minimum_spanning_tree]&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#boruvka-then-merge boruvka_then_merge]&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#boruvka-mixed-merge boruvka_mixed_merge]&amp;lt;/code&amp;gt; сочетают алгоритм Борувки и [[алгоритм Крускала]].&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Начатые статьи]]&lt;/div&gt;</summary>
		<author><name>Daryin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%91%D0%BE%D1%80%D1%83%D0%B2%D0%BA%D0%B8&amp;diff=20139</id>
		<title>Алгоритм Борувки</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%91%D0%BE%D1%80%D1%83%D0%B2%D0%BA%D0%B8&amp;diff=20139"/>
		<updated>2016-12-05T21:40:19Z</updated>

		<summary type="html">&lt;p&gt;Daryin: /* Макроструктура алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм Борувки&#039;&#039;&#039;&amp;lt;ref&amp;gt;Borůvka, Otakar. “O Jistém Problému Minimálním.” Práce Moravské Přírodovědecké Společnosti III, no. 3 (1926): 37–58.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Jarník, Vojtěch. “O Jistém Problému Minimálním (Z Dopisu Panu O. Borůvkovi).” Práce Moravské Přírodovědecké Společnosti 6, no. 4 (1930): 57–63.&amp;lt;/ref&amp;gt; предназначен для решения [[Построение минимального остовного дерева (MST)|задачи о построении минимального остовного дерева]] во взвешенном неориентированном графе. Алгоритм хорошо параллелизуется и является основой для распределённого [[Алгоритм GHS|алгоритма GHS]].&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Пусть задан связный неориентированный граф &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; с весами рёбер &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt;. Предполагается, что веса всех рёбер различны (если это не так, то можно упорядочить рёбра сначала по весу, а потом по номеру).&lt;br /&gt;
&lt;br /&gt;
Алгоритм Борувки основан на следующих двух свойствах задачи:&lt;br /&gt;
* &#039;&#039;&#039;Минимальное ребро фрагмента&#039;&#039;&#039;. Пусть &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; – фрагмент минимального остовного дерева и &amp;lt;math&amp;gt;e_F&amp;lt;/math&amp;gt; – ребро наименьшего веса, исходящее из &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; (т.е. ровно один его конец является вершиной из &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt;). Если ребро &amp;lt;math&amp;gt;e_F&amp;lt;/math&amp;gt; единственно, то оно принадлежит минимальному остовному дереву.&lt;br /&gt;
* &#039;&#039;&#039;Схлопывание фрагментов&#039;&#039;&#039;. Пусть &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; – фрагмент минимального остовного дерева графа &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;, а граф &amp;lt;math&amp;gt;G&#039;&amp;lt;/math&amp;gt; получен из &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt; склеиванием вершин, принадлежащих &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt;. Тогда объединение &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; и минимального остовного дерева графа &amp;lt;math&amp;gt;G&#039;&amp;lt;/math&amp;gt; даёт минимальное остовное дерево исходного графа &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В начале работы алгоритма каждая вершина графа &amp;lt;math&amp;gt;G&amp;lt;/math&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;math&amp;gt;G=(V,E)&amp;lt;/math&amp;gt; с вершинами &amp;lt;math&amp;gt;V = ( v_{1}, v_{2}, ..., v_{n} )&amp;lt;/math&amp;gt; и рёбрами &amp;lt;math&amp;gt;E = ( e_{1}, e_{2}, ..., e_{m} )&amp;lt;/math&amp;gt;. Каждому ребру &amp;lt;math&amp;gt;e \in E&amp;lt;/math&amp;gt; приписан вес &amp;lt;math&amp;gt;w(e)&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Требуется построить дерево &amp;lt;math&amp;gt;T^* \subseteq E&amp;lt;/math&amp;gt;, связывающее все вершины, и имеющее наименьший возможный вес среди всех таких деревьев:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; w(T^* )= \min_T( w(T)) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
где вес множества рёбер есть сумма их весов:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;w(T)=\sum_{e \in T} (w(T))&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Если граф &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt; не является связным, то дерева, связывающего все вершины, не существует. &lt;br /&gt;
&lt;br /&gt;
В этом случае необходимо найти минимальной остовное дерево для каждой компоненты связности &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;. Набор таких деревьев называется минимальным остовным лесом (сокращённо MSF – Minimum Spanning Forest).&lt;br /&gt;
&lt;br /&gt;
==== Вспомогательный алгоритм: система непересекающихся множеств (Union-Find)====&lt;br /&gt;
&lt;br /&gt;
Во всех алгоритмах решения задачи требуется отслеживать, каким уже построенным фрагментам дерева принадлежат те или иные вершины графа. Для этого используется структура данных «система непересекающихся множеств» (Union-Find). Данная структура поддерживает две операции:&lt;br /&gt;
&lt;br /&gt;
1.	&amp;lt;math&amp;gt;FIND(v) = w&amp;lt;/math&amp;gt; – по вершине v возвращает вершину w – «корень» фрагмента, которому принадлежит вершина v. При этом гарантируется, что вершины u и v принадлежат одному и тому же фрагменту, тогда и только тогда, когда &amp;lt;math&amp;gt;FIND(u) = FIND(v)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
2.	&amp;lt;math&amp;gt;MERGE(u, v)&amp;lt;/math&amp;gt; – объединяет два фрагмента, которым принадлежат вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v.&amp;lt;/math&amp;gt; (Если они уже лежат в одном фрагменте, то ничего не происходит.) При практической реализации удобно, чтобы данная операция возвращала значение истина, если объединение фрагментов имело место, и ложь в противном случае.&lt;br /&gt;
&lt;br /&gt;
==== Последовательная версия====&lt;br /&gt;
&lt;br /&gt;
Классический последовательный алгоритм Union-Find описан в статье Тарьяна. Каждой вершине v приписывается указатель на вершину-родителя &amp;lt;math&amp;gt;parent(v)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
1.	Изначально &amp;lt;math&amp;gt;parent(v) := v&amp;lt;/math&amp;gt; для всех вершин.&lt;br /&gt;
&lt;br /&gt;
2.	&amp;lt;math&amp;gt;FIND(v)&amp;lt;/math&amp;gt; выполняется следующим образом: полагаем &amp;lt;math&amp;gt;u := v&amp;lt;/math&amp;gt;, и далее следуем по указателям &amp;lt;math&amp;gt;u := parent(u)&amp;lt;/math&amp;gt; до тех пор, пока не станет &amp;lt;math&amp;gt;u = parent(u)&amp;lt;/math&amp;gt;. Это и будет результат операции. Дополнительно можно «схлопывать» дерево: присвоить всем посещённым вершинами: &amp;lt;math&amp;gt;parent(ui) := u&amp;lt;/math&amp;gt;, либо производить схлопывание по пути: &amp;lt;math&amp;gt;parent(u) := parent(parent(u)))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
3.	&amp;lt;math&amp;gt;MERGE(u, v)&amp;lt;/math&amp;gt; выполняется следующим образом: вначале находим корневые вершины &amp;lt;math&amp;gt;u := FIND(u), v := FIND(v)&amp;lt;/math&amp;gt;. Если &amp;lt;math&amp;gt;u = v&amp;lt;/math&amp;gt;, то исходные вершины принадлежат одному фрагменту и объединения фрагментов не происходит. В противном случае полагаем одно из &amp;lt;math&amp;gt;parent(u) := v&amp;lt;/math&amp;gt; или &amp;lt;math&amp;gt;parent(v) := u&amp;lt;/math&amp;gt;. Дополнительно можно отслеживать количество вершин в каждом из фрагментов, чтобы меньший фрагмент подсоединять к большему, а не наоборот (оценки сложности доказываются именно при такой реализации, однако на практике алгоритм хорошо работает и без подсчёта количества вершин).&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
В алгоритме Борувки фрагменты минимального остовного дерева наращиваются постепенно присоединением минимального ребра, выходящего из каждого фрагмента.&lt;br /&gt;
&lt;br /&gt;
1.	В самом начале каждая вершина является отдельным фрагментом.&lt;br /&gt;
&lt;br /&gt;
2.	На каждом шаге:&lt;br /&gt;
&lt;br /&gt;
*	Для каждого фрагмента определяется минимальное по весу исходящее ребро.&lt;br /&gt;
&lt;br /&gt;
*	Минимальные рёбра добавляются в минимальное остовное дерево, а соответствующие фрагменты объединяются.&lt;br /&gt;
&lt;br /&gt;
3.	Алгоритм останавливается, когда остаётся только один фрагмент, либо когда ни у одного из фрагментов нет исходящих рёбер.&lt;br /&gt;
&lt;br /&gt;
Поиск минимальных исходящих рёбер может выполняться независимо для каждого фрагмента. Таким образом, данную стадию вычислений можно эффективно параллелизовать (в том числе с использованием массового параллелизма графических ускорителей).&lt;br /&gt;
&lt;br /&gt;
Объединение фрагментов также может быть реализовано параллельно, с использованием описанной выше параллельной версии структуры Union-Find.&lt;br /&gt;
&lt;br /&gt;
Аккуратный подсчёт количества активных фрагментов позволяет остановить алгоритм Борувки на один шаг раньше обычного:&lt;br /&gt;
&lt;br /&gt;
1.	В начале итерации счётчик активных фрагментов обнуляется.&lt;br /&gt;
&lt;br /&gt;
2.	На этапе поиска минимальных рёбер счётчик увеличивается на единицу для каждого фрагмента, у которого были исходящие рёбра.&lt;br /&gt;
&lt;br /&gt;
3.	На этапе объединения фрагментов счётчик уменьшается на единицу каждый раз, когда операция UNION(u, v) вернула значение истина.&lt;br /&gt;
&lt;br /&gt;
Если в конце итерации счётчик равен 0 или 1, то вычисления останавливаются.&lt;br /&gt;
Параллелизм возможен на этапе сортировки рёбер по весу, однако основной ход алгоритма является последовательным.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Последовательная сложность алгоритма Борувки для графа с &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершинами и &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; рёбрами составляет &amp;lt;math&amp;gt;O(m*ln(n))&amp;lt;/math&amp;gt; операций.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
В описанном подходе существует два уровня параллелизма: параллелизм в классическом алгоритме Борувки (нижний), и параллелизм в алгоритме обработка графа, не помещающегося в память.&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;&#039;Нижний уровень параллелизма&#039;&#039;&#039;: поиск минимальных исходящих рёбер может выполняться независимо для каждого фрагмента, благодаря чему данную стадию вычислений можно эффективно параллелизовать (как на GPU, так и на CPU). Объединение фрагментов также может быть реализовано параллельно, с использованием описанной структуры Union-Find. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Верхний уровень параллелизма&#039;&#039;&#039;: построение отдельных минимальных основных деревьев для каждого из списков ребер может производиться параллельно. Например, список ребер может разбиваться на две части, одна из которых обрабатывается на GPU, а вторая параллельно на CPU. &lt;br /&gt;
&lt;br /&gt;
[[file:MST low.png|thumb|center|1000px|Рисунок 1. Информационный граф нижнего уровня параллелизма]]&lt;br /&gt;
&lt;br /&gt;
Рассмотрим информационные графы и подробное описание каждого из них. Так же можно считать, что на рисунке 1 представлен информационный граф классического алгоритма Борувки, а на рисунке 2 — алгоритма обработки графа.&lt;br /&gt;
&lt;br /&gt;
Нижний уровень параллелизма на графе алгоритма (рисунок 1) расположен на уровнях {3, 4, 5}, соответствующим операциям параллельного поиска минимальных исходящих ребер, а так же уровнях {6, 7, 8}, соответствующим операциям параллельного объединения деревьев. Так же, различные операции копирования {1, 2, 8, 9} выполняются параллельно. После выполнения тела цикла, производится проверка {12} того, сколько деревьев осталось на текущем шаге, и если данное число не изменилось, то происходит выход из цикла, иначе аналогичная следующая итерация. &lt;br /&gt;
&lt;br /&gt;
Верхний уровень параллелизма (рисунок 2), как уже говорилось, заключается в параллельном вычислении минимального основного дерева (compute mst) для различных частей графа. Перед этим производится процесс инициализации (init process), данные которого используют последующие параллельные compute mst. Затем, после параллельных вычислений mst, происходит вычисление итого основного дерево, после чего после чего полученный результат сохраняется (save results).&lt;br /&gt;
&lt;br /&gt;
[[file:MST up.png|thumb|center|1000px|Рисунок 2. Информационный граф верхнего уровня параллелизма]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритму Борувки обладает большим потенциалом параллелизма, так как его основная операция (выбор минимального исходящего ребра во фрагменте) может исполнятся независимо для каждого фрагмента.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: взвешенный граф &amp;lt;math&amp;gt;(V, E, W)&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин &amp;lt;math&amp;gt;v_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; рёбер &amp;lt;math&amp;gt;e_j = (v^{(1)}_{j},&lt;br /&gt;
v^{(2)}_{j})&amp;lt;/math&amp;gt; с весами &amp;lt;math&amp;gt;f_j&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(m + n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: список рёбер минимального остовного дерева (для несвязного графа – список минимальных остовных деревьев для всех компонент связности).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(n)&amp;lt;/math&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;
&lt;br /&gt;
1. части, отвечающей за общую координацию вычислений&lt;br /&gt;
&lt;br /&gt;
2. части, отвечающей за параллельные вычисления на многоядерных CPU или GPU.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Описанный выше последовательный алгоритм не может применяться в параллельной программе: в реализации &amp;lt;math&amp;gt;MERGE&amp;lt;/math&amp;gt; результаты операций &amp;lt;math&amp;gt;FIND(u)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;FIND(v)&amp;lt;/math&amp;gt; могут постоянно меняться, что приведёт к race condition. Параллельный вариант алгоритма описан в статье.&lt;br /&gt;
&lt;br /&gt;
1.	Каждой вершине v соответствует запись &amp;lt;math&amp;gt;A[v] = { parent, rank }&amp;lt;/math&amp;gt;. Изначально &amp;lt;math&amp;gt;A[v] := { v, 0 }&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
2.	Вспомогательная операция &amp;lt;math&amp;gt;UPDATE(v, rank_v, u, rank_u)&amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
old := A[v]&lt;br /&gt;
&lt;br /&gt;
if old.parent != v or old.rank != rank_v then return false&lt;br /&gt;
&lt;br /&gt;
new := { u, rank_u }&lt;br /&gt;
&lt;br /&gt;
return CAS(A[v], old, new)&lt;br /&gt;
&lt;br /&gt;
3.	Операция &amp;lt;math&amp;gt;FIND(v)&amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
    while v != A[v].parent do&lt;br /&gt;
        u := A[v].parent&lt;br /&gt;
        CAS(A[v].parent, u, A[u].parent)&lt;br /&gt;
        v := A[u].parent&lt;br /&gt;
    return v&lt;br /&gt;
&lt;br /&gt;
4.	Операция UNION(u, v):&lt;br /&gt;
&lt;br /&gt;
    while true do&lt;br /&gt;
        (u, v) := (FIND(u), FIND(v))&lt;br /&gt;
        if u = v then return false&lt;br /&gt;
        (rank_u, rank_v) := (A[u].rank, A[v].rank)&lt;br /&gt;
        if (A[u].rank, u) &amp;gt; (A[v].rank, v) then&lt;br /&gt;
            swap((u, rank_u), (v, rank_v))&lt;br /&gt;
        if UPDATE(u, rank_u, v, rank_u) then&lt;br /&gt;
            if rank_u = rank_v then&lt;br /&gt;
                UPDATE(v, rank_v, v, rank_v + 1)&lt;br /&gt;
            return true&lt;br /&gt;
&lt;br /&gt;
Для описанной версии алгоритма гарантируется свойство wait-free. На практике может использоваться упрощённая версия без подсчёта рангов, обладающая более слабым свойством lock-free, но в ряде случаев выигрывающая по скорости.&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
==== Масштабируемость алгоритма ====&lt;br /&gt;
==== Масштабируемость реализации алгоритма ====&lt;br /&gt;
Проведём исследование масштабируемости параллельной реализации алгоритма Борувки согласно [[Scalability methodology|методике]]. Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов-2 [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Набор и границы значений изменяемых [[Глоссарий#Параметры запуска|параметров запуска]] реализации алгоритма: &lt;br /&gt;
&lt;br /&gt;
*	число процессоров [1 : 28] с шагом 1;&lt;br /&gt;
*	размер графа [2^20 : 2^27].&lt;br /&gt;
&lt;br /&gt;
Проведем отдельные исследования сильной масштабируемости и масштабируемости вширь реализации алгоритма Борувки.&lt;br /&gt;
&lt;br /&gt;
Производительность определена как TEPS (от англ. Traversed Edges Per Second), то есть число ребер графа, который алгоритм обрабатывает в секунду. С помощью данной метрики можно сравнивать производительность для различных размеров графа, оценивая, насколько понижается эффективность обработки графа при увеличении его размера&lt;br /&gt;
&lt;br /&gt;
[[file:MST scaling strong.png|thumb|center|700px|Рисунок 3. Параллельная реализация алгоритма Борувки масштабируемость CPU версии: производительность в зависимости от числа запущенных CPU-потоков.]]&lt;br /&gt;
&lt;br /&gt;
[[file:MST scaling wide.png|thumb|center|700px|Рисунок 4. Параллельная реализация алгоритма Борувки масштабируемость различных версий реализации алгоритма: производительность в зависимости от размера графа]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
* C++, MPI: [http://www.boost.org/libs/graph_parallel/doc/html/index.html Parallel Boost Graph Library]; функции &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#dense-boruvka-minimum-spanning-tree dense_boruvka_minimum_spanning_tree]&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#boruvka-then-merge boruvka_then_merge]&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#boruvka-mixed-merge boruvka_mixed_merge]&amp;lt;/code&amp;gt; сочетают алгоритм Борувки и [[алгоритм Крускала]].&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Начатые статьи]]&lt;/div&gt;</summary>
		<author><name>Daryin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9F%D1%80%D0%B8%D0%BC%D0%B0&amp;diff=2093</id>
		<title>Алгоритм Прима</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9F%D1%80%D0%B8%D0%BC%D0%B0&amp;diff=2093"/>
		<updated>2015-07-08T13:23:10Z</updated>

		<summary type="html">&lt;p&gt;Daryin: /* Математическое описание */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Свойства и структура алгоритмов ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм Прима&#039;&#039;&#039;&amp;lt;ref&amp;gt;Prim, R C. “Shortest Connection Networks and Some Generalizations.” Bell System Technical Journal 36, no. 6 (November 1957): 1389–1401. doi:10.1002/j.1538-7305.1957.tb01515.x.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Dijkstra, E W. “A Note on Two Problems in Connexion with Graphs.” Numerische Mathematik 1, no. 1 (December 1959): 269–71. doi:10.1007/BF01386390.&amp;lt;/ref&amp;gt; предназначен для решения [[Построение минимального остовного дерева (MST)|задачи о построении минимального остовного дерева]] во взвешенном неориентированном графе.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание ===&lt;br /&gt;
&lt;br /&gt;
Пусть задан связный неориентированный граф &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; с весами рёбер &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt;. Предполагается, что веса всех рёбер различны (если это не так, то можно упорядочить рёбра сначала по весу, а потом по номеру).&lt;br /&gt;
&lt;br /&gt;
Алгоритм Прима основан на следующих двух свойствах задачи:&lt;br /&gt;
* &#039;&#039;&#039;Минимальное ребро фрагмента&#039;&#039;&#039;. Пусть &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; – фрагмент минимального остовного дерева и &amp;lt;math&amp;gt;e_F&amp;lt;/math&amp;gt; – ребро наименьшего веса, исходящее из &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; (т.е. ровно один его конец является вершиной из &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt;). Если ребро &amp;lt;math&amp;gt;e_F&amp;lt;/math&amp;gt; единственно, то оно принадлежит минимальному остовному дереву.&lt;br /&gt;
* &#039;&#039;&#039;Схлопывание фрагментов&#039;&#039;&#039;. Пусть &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; – фрагмент минимального остовного дерева графа &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;, а граф &amp;lt;math&amp;gt;G&#039;&amp;lt;/math&amp;gt; получен из &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt; склеиванием вершин, принадлежащих &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt;. Тогда объединение &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; и минимального остовного дерева графа &amp;lt;math&amp;gt;G&#039;&amp;lt;/math&amp;gt; даёт минимальное остовное дерево исходного графа &amp;lt;math&amp;gt;G&amp;lt;/math&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;
* При использовании списков вершин сложность &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* При использовании бинарной кучи рёбер сложность &amp;lt;math&amp;gt;O(m \ln m)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* При использовании бинарной кучи вершин и сортировке рёбер у каждой вершины сложность &amp;lt;math&amp;gt;O(m \ln n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* При использовании быстрой кучи&amp;lt;ref&amp;gt;Navarro, Gonzalo, and Rodrigo Paredes. “On Sorting, Heaps, and Minimum Spanning Trees.” Algorithmica 57, no. 4 (March 23, 2010): 585–620. doi:10.1007/s00453-010-9400-6.&amp;lt;/ref&amp;gt; сложность &amp;lt;math&amp;gt;O(m + n \ln^2 n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* При использовании фибоначчиевой кучи&amp;lt;ref&amp;gt;Fredman, Michael L, and Robert Endre Tarjan. “Fibonacci Heaps and Their Uses in Improved Network Optimization Algorithms.” Journal of the ACM 34, no. 3 (July 1987): 596–615. doi:10.1145/28869.28874.&amp;lt;/ref&amp;gt; сложность &amp;lt;math&amp;gt;O(m + n \ln n)&amp;lt;/math&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;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
* C++: [http://www.boost.org/libs/graph/doc/ Boost Graph Library] (функция &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph/doc/prim_minimum_spanning_tree.html prim_minimum_spanning_tree]&amp;lt;/code&amp;gt;), сложность &amp;lt;math&amp;gt;O(m \ln n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Java: [http://jgrapht.org JGraphT] (класс &amp;lt;code&amp;gt;[http://jgrapht.org/javadoc/org/jgrapht/alg/PrimMinimumSpanningTree.html PrimMinimumSpanningTree]&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Daryin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9F%D1%80%D0%B8%D0%BC%D0%B0&amp;diff=2092</id>
		<title>Алгоритм Прима</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9F%D1%80%D0%B8%D0%BC%D0%B0&amp;diff=2092"/>
		<updated>2015-07-08T13:22:58Z</updated>

		<summary type="html">&lt;p&gt;Daryin: /* Математическое описание */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Свойства и структура алгоритмов ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм Прима&#039;&#039;&#039;&amp;lt;ref&amp;gt;Prim, R C. “Shortest Connection Networks and Some Generalizations.” Bell System Technical Journal 36, no. 6 (November 1957): 1389–1401. doi:10.1002/j.1538-7305.1957.tb01515.x.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Dijkstra, E W. “A Note on Two Problems in Connexion with Graphs.” Numerische Mathematik 1, no. 1 (December 1959): 269–71. doi:10.1007/BF01386390.&amp;lt;/ref&amp;gt; предназначен для решения [[Построение минимального остовного дерева (MST)|задачи о построении минимального остовного дерева]] во взвешенном неориентированном графе.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание ===&lt;br /&gt;
&lt;br /&gt;
Пусть задан связный неориентированный граф &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; с весами рёбер &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt;. Предполагается, что веса всех рёбер различны (если это не так, то можно упорядочить рёбра сначала по весу, а потом по номеру).&lt;br /&gt;
&lt;br /&gt;
Алгоритм Борувки основан на следующих двух свойствах задачи:&lt;br /&gt;
* &#039;&#039;&#039;Минимальное ребро фрагмента&#039;&#039;&#039;. Пусть &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; – фрагмент минимального остовного дерева и &amp;lt;math&amp;gt;e_F&amp;lt;/math&amp;gt; – ребро наименьшего веса, исходящее из &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; (т.е. ровно один его конец является вершиной из &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt;). Если ребро &amp;lt;math&amp;gt;e_F&amp;lt;/math&amp;gt; единственно, то оно принадлежит минимальному остовному дереву.&lt;br /&gt;
* &#039;&#039;&#039;Схлопывание фрагментов&#039;&#039;&#039;. Пусть &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; – фрагмент минимального остовного дерева графа &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;, а граф &amp;lt;math&amp;gt;G&#039;&amp;lt;/math&amp;gt; получен из &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt; склеиванием вершин, принадлежащих &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt;. Тогда объединение &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; и минимального остовного дерева графа &amp;lt;math&amp;gt;G&#039;&amp;lt;/math&amp;gt; даёт минимальное остовное дерево исходного графа &amp;lt;math&amp;gt;G&amp;lt;/math&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;
* При использовании списков вершин сложность &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* При использовании бинарной кучи рёбер сложность &amp;lt;math&amp;gt;O(m \ln m)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* При использовании бинарной кучи вершин и сортировке рёбер у каждой вершины сложность &amp;lt;math&amp;gt;O(m \ln n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* При использовании быстрой кучи&amp;lt;ref&amp;gt;Navarro, Gonzalo, and Rodrigo Paredes. “On Sorting, Heaps, and Minimum Spanning Trees.” Algorithmica 57, no. 4 (March 23, 2010): 585–620. doi:10.1007/s00453-010-9400-6.&amp;lt;/ref&amp;gt; сложность &amp;lt;math&amp;gt;O(m + n \ln^2 n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* При использовании фибоначчиевой кучи&amp;lt;ref&amp;gt;Fredman, Michael L, and Robert Endre Tarjan. “Fibonacci Heaps and Their Uses in Improved Network Optimization Algorithms.” Journal of the ACM 34, no. 3 (July 1987): 596–615. doi:10.1145/28869.28874.&amp;lt;/ref&amp;gt; сложность &amp;lt;math&amp;gt;O(m + n \ln n)&amp;lt;/math&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;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
* C++: [http://www.boost.org/libs/graph/doc/ Boost Graph Library] (функция &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph/doc/prim_minimum_spanning_tree.html prim_minimum_spanning_tree]&amp;lt;/code&amp;gt;), сложность &amp;lt;math&amp;gt;O(m \ln n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Java: [http://jgrapht.org JGraphT] (класс &amp;lt;code&amp;gt;[http://jgrapht.org/javadoc/org/jgrapht/alg/PrimMinimumSpanningTree.html PrimMinimumSpanningTree]&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Daryin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9A%D1%80%D1%83%D1%81%D0%BA%D0%B0%D0%BB%D0%B0&amp;diff=2090</id>
		<title>Алгоритм Крускала</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9A%D1%80%D1%83%D1%81%D0%BA%D0%B0%D0%BB%D0%B0&amp;diff=2090"/>
		<updated>2015-07-08T13:07:58Z</updated>

		<summary type="html">&lt;p&gt;Daryin: /* Описание ресурса параллелизма алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Свойства и структура алгоритмов ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм Крускала&#039;&#039;&#039;&amp;lt;ref&amp;gt;Kruskal, Joseph B. “On the Shortest Spanning Subtree of a Graph and the Traveling Salesman Problem.” Proceedings of the American Mathematical Society 7, no. 1 (January 1956): 48–50. doi:10.1090/S0002-9939-1956-0078686-7.&amp;lt;/ref&amp;gt; предназначен для решения [[Построение минимального остовного дерева (MST)|задачи о построении минимального остовного дерева]] во взвешенном неориентированном графе. Последовательная сложность алгоритма &amp;lt;math&amp;gt;O(m \ln n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В отличие от алгоритмов [[Алгоритм Прима|Прима]] и [[Алгоритм Борувки|Борувки]], алгоритм Крускала не требует информации о рёбрах конкретной вершины, вместо этого на его вход подаётся общий список рёбер графа в произвольном порядке. Кроме этого, каждое (ненаправленное) ребро достаточно представить лишь одной из его направленных дуг, что на практике означает в два раза меньший объём вычислений. Последовательная версия алгоритма Крускала работает, как правило, быстрее последовательной версии алгоритма Борувки, а при условии предварительной сортировки списка рёбер по весу сложность алгоритма снижается до &amp;lt;math&amp;gt;O(m \alpha(m, n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание ===&lt;br /&gt;
&lt;br /&gt;
Пусть задан связный неориентированный граф &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; с весами рёбер &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt;. Предполагается, что веса всех рёбер различны (если это не так, то можно упорядочить рёбра сначала по весу, а потом по номеру).&lt;br /&gt;
&lt;br /&gt;
Алгоритм Крускала основан на следующих двух свойствах задачи:&lt;br /&gt;
* &#039;&#039;&#039;Минимальное ребро графа&#039;&#039;&#039;. Если &amp;lt;math&amp;gt;e^*&amp;lt;/math&amp;gt; – единственное ребро графа с минимальным весом, то оно принадлежит минимальному остовному дереву..&lt;br /&gt;
* &#039;&#039;&#039;Схлопывание фрагментов&#039;&#039;&#039;. Пусть &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; – фрагмент минимального остовного дерева графа &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;, а граф &amp;lt;math&amp;gt;G&#039;&amp;lt;/math&amp;gt; получен из &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt; склеиванием вершин, принадлежащих &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt;. Тогда объединение &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; и минимального остовного дерева графа &amp;lt;math&amp;gt;G&#039;&amp;lt;/math&amp;gt; даёт минимальное остовное дерево исходного графа &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В начале работы алгоритма каждая вершина графа &amp;lt;math&amp;gt;G&amp;lt;/math&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;math&amp;gt;m \ln n&amp;lt;/math&amp;gt; (например, с помощью быстрой сортировки), затем просматриваются в порядке увеличения веса за время &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt;, при этом для хранения информации о текущих фрагментах используется [[система непересекающихся множеств]]&amp;lt;ref&amp;gt;Tarjan, Robert Endre. “Efficiency of a Good but Not Linear Set Union Algorithm.” Journal of the ACM 22, no. 2 (April 1975): 215–25. doi:10.1145/321879.321884.&amp;lt;/ref&amp;gt; с общим временем работы &amp;lt;math&amp;gt;O(m \alpha(m, n))&amp;lt;/math&amp;gt;. Итоговая сложность алгоритма &amp;lt;math&amp;gt;O(m \ln n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Как видно, наибольшую сложность имеет этап сортировки, при этом большая часть рёбер сортируется напрасно: они всё равно будут отброшены, как принадлежащие одному фрагменту. Использование инкрементальной быстрой сортировки&amp;lt;ref&amp;gt;Navarro, Gonzalo, and Rodrigo Paredes. “On Sorting, Heaps, and Minimum Spanning Trees.” Algorithmica 57, no. 4 (March 23, 2010): 585–620. doi:10.1007/s00453-010-9400-6.&amp;lt;/ref&amp;gt; (англ. IQS: Incremental Quick Sort) позволяет снизить затраты на сортировку, так что среднее время работы алгоритма составляет &amp;lt;math&amp;gt;O(m + n \ln^2 n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В случае, если рёбра графа изначально отсортированы по весу рёбер, сложность алгоритма снижается до &amp;lt;math&amp;gt;O(m \alpha(m, n))&amp;lt;/math&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;
&#039;&#039;&#039;Ассоциативность по рёбрам&#039;&#039;&#039;. Пусть &amp;lt;math&amp;gt;MSF(E)&amp;lt;/math&amp;gt; – минимальный остовный лес графа с рёбрами &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;. Тогда&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
        MSF(E_1 \cup E_2 \cup \dots \cup E_k) = MSF(MSF(E_1) \cup MSF(E_2) \cup \dots \cup MSF(E_k)).&lt;br /&gt;
&amp;lt;/math&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;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
* C++: [http://www.boost.org/libs/graph/doc/ Boost Graph Library] (функция &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph/doc/kruskal_min_spanning_tree.html kruskal_min_spanning_tree]&amp;lt;/code&amp;gt;), сложность &amp;lt;math&amp;gt;O(m \ln m)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* C++, MPI: [http://www.boost.org/libs/graph_parallel/doc/html/index.html Parallel Boost Graph Library]&lt;br /&gt;
** функция &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#merge-local-minimum-spanning-trees merge_local_minimum_spanning_trees]&amp;lt;/code&amp;gt; реализует алгоритм Крускала;&lt;br /&gt;
** функции &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#dense-boruvka-minimum-spanning-tree dense_boruvka_minimum_spanning_tree]&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#boruvka-then-merge boruvka_then_merge]&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#boruvka-mixed-merge boruvka_mixed_merge]&amp;lt;/code&amp;gt; сочетают [[алгоритм Борувки]] и алгоритм Крускала.&lt;br /&gt;
* Python: [https://networkx.github.io NetworkX] (функция &amp;lt;code&amp;gt;[http://networkx.github.io/documentation/networkx-1.9.1/reference/generated/networkx.algorithms.mst.minimum_spanning_tree.html minimum_spanning_tree]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Java: [http://jgrapht.org JGraphT] (класс &amp;lt;code&amp;gt;[http://jgrapht.org/javadoc/org/jgrapht/alg/KruskalMinimumSpanningTree.html KruskalMinimumSpanningTree]&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Daryin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%91%D0%BE%D1%80%D1%83%D0%B2%D0%BA%D0%B8&amp;diff=2089</id>
		<title>Алгоритм Борувки</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%91%D0%BE%D1%80%D1%83%D0%B2%D0%BA%D0%B8&amp;diff=2089"/>
		<updated>2015-07-08T12:57:39Z</updated>

		<summary type="html">&lt;p&gt;Daryin: /* Описание ресурса параллелизма алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Свойства и структура алгоритмов ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм Борувки&#039;&#039;&#039;&amp;lt;ref&amp;gt;Borůvka, Otakar. “O Jistém Problému Minimálním.” Práce Moravské Přírodovědecké Společnosti III, no. 3 (1926): 37–58.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Jarník, Vojtěch. “O Jistém Problému Minimálním (Z Dopisu Panu O. Borůvkovi).” Práce Moravské Přírodovědecké Společnosti 6, no. 4 (1930): 57–63.&amp;lt;/ref&amp;gt; предназначен для решения [[Построение минимального остовного дерева (MST)|задачи о построении минимального остовного дерева]] во взвешенном неориентированном графе. Алгоритм хорошо параллелизуется и является основой для распределённого [[Алгоритм GHS|алгоритма GHS]].&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание ===&lt;br /&gt;
&lt;br /&gt;
Пусть задан связный неориентированный граф &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; с весами рёбер &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt;. Предполагается, что веса всех рёбер различны (если это не так, то можно упорядочить рёбра сначала по весу, а потом по номеру).&lt;br /&gt;
&lt;br /&gt;
Алгоритм Борувки основан на следующих двух свойствах задачи:&lt;br /&gt;
* &#039;&#039;&#039;Минимальное ребро фрагмента&#039;&#039;&#039;. Пусть &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; – фрагмент минимального остовного дерева и &amp;lt;math&amp;gt;e_F&amp;lt;/math&amp;gt; – ребро наименьшего веса, исходящее из &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; (т.е. ровно один его конец является вершиной из &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt;). Если ребро &amp;lt;math&amp;gt;e_F&amp;lt;/math&amp;gt; единственно, то оно принадлежит минимальному остовному дереву.&lt;br /&gt;
* &#039;&#039;&#039;Схлопывание фрагментов&#039;&#039;&#039;. Пусть &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; – фрагмент минимального остовного дерева графа &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;, а граф &amp;lt;math&amp;gt;G&#039;&amp;lt;/math&amp;gt; получен из &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt; склеиванием вершин, принадлежащих &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt;. Тогда объединение &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; и минимального остовного дерева графа &amp;lt;math&amp;gt;G&#039;&amp;lt;/math&amp;gt; даёт минимальное остовное дерево исходного графа &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В начале работы алгоритма каждая вершина графа &amp;lt;math&amp;gt;G&amp;lt;/math&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;
== Программная реализация алгоритмов ==&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;
* C++, MPI: [http://www.boost.org/libs/graph_parallel/doc/html/index.html Parallel Boost Graph Library]; функции &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#dense-boruvka-minimum-spanning-tree dense_boruvka_minimum_spanning_tree]&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#boruvka-then-merge boruvka_then_merge]&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#boruvka-mixed-merge boruvka_mixed_merge]&amp;lt;/code&amp;gt; сочетают алгоритм Борувки и [[алгоритм Крускала]].&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Daryin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%91%D0%BE%D1%80%D1%83%D0%B2%D0%BA%D0%B8&amp;diff=2088</id>
		<title>Алгоритм Борувки</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%91%D0%BE%D1%80%D1%83%D0%B2%D0%BA%D0%B8&amp;diff=2088"/>
		<updated>2015-07-08T12:55:18Z</updated>

		<summary type="html">&lt;p&gt;Daryin: /* Математическое описание */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Свойства и структура алгоритмов ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм Борувки&#039;&#039;&#039;&amp;lt;ref&amp;gt;Borůvka, Otakar. “O Jistém Problému Minimálním.” Práce Moravské Přírodovědecké Společnosti III, no. 3 (1926): 37–58.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Jarník, Vojtěch. “O Jistém Problému Minimálním (Z Dopisu Panu O. Borůvkovi).” Práce Moravské Přírodovědecké Společnosti 6, no. 4 (1930): 57–63.&amp;lt;/ref&amp;gt; предназначен для решения [[Построение минимального остовного дерева (MST)|задачи о построении минимального остовного дерева]] во взвешенном неориентированном графе. Алгоритм хорошо параллелизуется и является основой для распределённого [[Алгоритм GHS|алгоритма GHS]].&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание ===&lt;br /&gt;
&lt;br /&gt;
Пусть задан связный неориентированный граф &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; с весами рёбер &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt;. Предполагается, что веса всех рёбер различны (если это не так, то можно упорядочить рёбра сначала по весу, а потом по номеру).&lt;br /&gt;
&lt;br /&gt;
Алгоритм Борувки основан на следующих двух свойствах задачи:&lt;br /&gt;
* &#039;&#039;&#039;Минимальное ребро фрагмента&#039;&#039;&#039;. Пусть &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; – фрагмент минимального остовного дерева и &amp;lt;math&amp;gt;e_F&amp;lt;/math&amp;gt; – ребро наименьшего веса, исходящее из &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; (т.е. ровно один его конец является вершиной из &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt;). Если ребро &amp;lt;math&amp;gt;e_F&amp;lt;/math&amp;gt; единственно, то оно принадлежит минимальному остовному дереву.&lt;br /&gt;
* &#039;&#039;&#039;Схлопывание фрагментов&#039;&#039;&#039;. Пусть &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; – фрагмент минимального остовного дерева графа &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;, а граф &amp;lt;math&amp;gt;G&#039;&amp;lt;/math&amp;gt; получен из &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt; склеиванием вершин, принадлежащих &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt;. Тогда объединение &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; и минимального остовного дерева графа &amp;lt;math&amp;gt;G&#039;&amp;lt;/math&amp;gt; даёт минимальное остовное дерево исходного графа &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В начале работы алгоритма каждая вершина графа &amp;lt;math&amp;gt;G&amp;lt;/math&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;
=== Возможные способы и особенности реализации параллельного алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
* C++, MPI: [http://www.boost.org/libs/graph_parallel/doc/html/index.html Parallel Boost Graph Library]; функции &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#dense-boruvka-minimum-spanning-tree dense_boruvka_minimum_spanning_tree]&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#boruvka-then-merge boruvka_then_merge]&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#boruvka-mixed-merge boruvka_mixed_merge]&amp;lt;/code&amp;gt; сочетают алгоритм Борувки и [[алгоритм Крускала]].&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Daryin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9A%D1%80%D1%83%D1%81%D0%BA%D0%B0%D0%BB%D0%B0&amp;diff=2087</id>
		<title>Алгоритм Крускала</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9A%D1%80%D1%83%D1%81%D0%BA%D0%B0%D0%BB%D0%B0&amp;diff=2087"/>
		<updated>2015-07-08T12:54:41Z</updated>

		<summary type="html">&lt;p&gt;Daryin: Отмена правки 2086, сделанной участником Daryin (обс.)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Свойства и структура алгоритмов ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм Крускала&#039;&#039;&#039;&amp;lt;ref&amp;gt;Kruskal, Joseph B. “On the Shortest Spanning Subtree of a Graph and the Traveling Salesman Problem.” Proceedings of the American Mathematical Society 7, no. 1 (January 1956): 48–50. doi:10.1090/S0002-9939-1956-0078686-7.&amp;lt;/ref&amp;gt; предназначен для решения [[Построение минимального остовного дерева (MST)|задачи о построении минимального остовного дерева]] во взвешенном неориентированном графе. Последовательная сложность алгоритма &amp;lt;math&amp;gt;O(m \ln n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В отличие от алгоритмов [[Алгоритм Прима|Прима]] и [[Алгоритм Борувки|Борувки]], алгоритм Крускала не требует информации о рёбрах конкретной вершины, вместо этого на его вход подаётся общий список рёбер графа в произвольном порядке. Кроме этого, каждое (ненаправленное) ребро достаточно представить лишь одной из его направленных дуг, что на практике означает в два раза меньший объём вычислений. Последовательная версия алгоритма Крускала работает, как правило, быстрее последовательной версии алгоритма Борувки, а при условии предварительной сортировки списка рёбер по весу сложность алгоритма снижается до &amp;lt;math&amp;gt;O(m \alpha(m, n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание ===&lt;br /&gt;
&lt;br /&gt;
Пусть задан связный неориентированный граф &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; с весами рёбер &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt;. Предполагается, что веса всех рёбер различны (если это не так, то можно упорядочить рёбра сначала по весу, а потом по номеру).&lt;br /&gt;
&lt;br /&gt;
Алгоритм Крускала основан на следующих двух свойствах задачи:&lt;br /&gt;
* &#039;&#039;&#039;Минимальное ребро графа&#039;&#039;&#039;. Если &amp;lt;math&amp;gt;e^*&amp;lt;/math&amp;gt; – единственное ребро графа с минимальным весом, то оно принадлежит минимальному остовному дереву..&lt;br /&gt;
* &#039;&#039;&#039;Схлопывание фрагментов&#039;&#039;&#039;. Пусть &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; – фрагмент минимального остовного дерева графа &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;, а граф &amp;lt;math&amp;gt;G&#039;&amp;lt;/math&amp;gt; получен из &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt; склеиванием вершин, принадлежащих &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt;. Тогда объединение &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; и минимального остовного дерева графа &amp;lt;math&amp;gt;G&#039;&amp;lt;/math&amp;gt; даёт минимальное остовное дерево исходного графа &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В начале работы алгоритма каждая вершина графа &amp;lt;math&amp;gt;G&amp;lt;/math&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;math&amp;gt;m \ln n&amp;lt;/math&amp;gt; (например, с помощью быстрой сортировки), затем просматриваются в порядке увеличения веса за время &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt;, при этом для хранения информации о текущих фрагментах используется [[система непересекающихся множеств]]&amp;lt;ref&amp;gt;Tarjan, Robert Endre. “Efficiency of a Good but Not Linear Set Union Algorithm.” Journal of the ACM 22, no. 2 (April 1975): 215–25. doi:10.1145/321879.321884.&amp;lt;/ref&amp;gt; с общим временем работы &amp;lt;math&amp;gt;O(m \alpha(m, n))&amp;lt;/math&amp;gt;. Итоговая сложность алгоритма &amp;lt;math&amp;gt;O(m \ln n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Как видно, наибольшую сложность имеет этап сортировки, при этом большая часть рёбер сортируется напрасно: они всё равно будут отброшены, как принадлежащие одному фрагменту. Использование инкрементальной быстрой сортировки&amp;lt;ref&amp;gt;Navarro, Gonzalo, and Rodrigo Paredes. “On Sorting, Heaps, and Minimum Spanning Trees.” Algorithmica 57, no. 4 (March 23, 2010): 585–620. doi:10.1007/s00453-010-9400-6.&amp;lt;/ref&amp;gt; (англ. IQS: Incremental Quick Sort) позволяет снизить затраты на сортировку, так что среднее время работы алгоритма составляет &amp;lt;math&amp;gt;O(m + n \ln^2 n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В случае, если рёбра графа изначально отсортированы по весу рёбер, сложность алгоритма снижается до &amp;lt;math&amp;gt;O(m \alpha(m, n))&amp;lt;/math&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;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
* C++: [http://www.boost.org/libs/graph/doc/ Boost Graph Library] (функция &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph/doc/kruskal_min_spanning_tree.html kruskal_min_spanning_tree]&amp;lt;/code&amp;gt;), сложность &amp;lt;math&amp;gt;O(m \ln m)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* C++, MPI: [http://www.boost.org/libs/graph_parallel/doc/html/index.html Parallel Boost Graph Library]&lt;br /&gt;
** функция &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#merge-local-minimum-spanning-trees merge_local_minimum_spanning_trees]&amp;lt;/code&amp;gt; реализует алгоритм Крускала;&lt;br /&gt;
** функции &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#dense-boruvka-minimum-spanning-tree dense_boruvka_minimum_spanning_tree]&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#boruvka-then-merge boruvka_then_merge]&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#boruvka-mixed-merge boruvka_mixed_merge]&amp;lt;/code&amp;gt; сочетают [[алгоритм Борувки]] и алгоритм Крускала.&lt;br /&gt;
* Python: [https://networkx.github.io NetworkX] (функция &amp;lt;code&amp;gt;[http://networkx.github.io/documentation/networkx-1.9.1/reference/generated/networkx.algorithms.mst.minimum_spanning_tree.html minimum_spanning_tree]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Java: [http://jgrapht.org JGraphT] (класс &amp;lt;code&amp;gt;[http://jgrapht.org/javadoc/org/jgrapht/alg/KruskalMinimumSpanningTree.html KruskalMinimumSpanningTree]&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Daryin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9A%D1%80%D1%83%D1%81%D0%BA%D0%B0%D0%BB%D0%B0&amp;diff=2086</id>
		<title>Алгоритм Крускала</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9A%D1%80%D1%83%D1%81%D0%BA%D0%B0%D0%BB%D0%B0&amp;diff=2086"/>
		<updated>2015-07-08T12:53:22Z</updated>

		<summary type="html">&lt;p&gt;Daryin: /* Описание ресурса параллелизма алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Свойства и структура алгоритмов ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм Крускала&#039;&#039;&#039;&amp;lt;ref&amp;gt;Kruskal, Joseph B. “On the Shortest Spanning Subtree of a Graph and the Traveling Salesman Problem.” Proceedings of the American Mathematical Society 7, no. 1 (January 1956): 48–50. doi:10.1090/S0002-9939-1956-0078686-7.&amp;lt;/ref&amp;gt; предназначен для решения [[Построение минимального остовного дерева (MST)|задачи о построении минимального остовного дерева]] во взвешенном неориентированном графе. Последовательная сложность алгоритма &amp;lt;math&amp;gt;O(m \ln n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В отличие от алгоритмов [[Алгоритм Прима|Прима]] и [[Алгоритм Борувки|Борувки]], алгоритм Крускала не требует информации о рёбрах конкретной вершины, вместо этого на его вход подаётся общий список рёбер графа в произвольном порядке. Кроме этого, каждое (ненаправленное) ребро достаточно представить лишь одной из его направленных дуг, что на практике означает в два раза меньший объём вычислений. Последовательная версия алгоритма Крускала работает, как правило, быстрее последовательной версии алгоритма Борувки, а при условии предварительной сортировки списка рёбер по весу сложность алгоритма снижается до &amp;lt;math&amp;gt;O(m \alpha(m, n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание ===&lt;br /&gt;
&lt;br /&gt;
Пусть задан связный неориентированный граф &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; с весами рёбер &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt;. Предполагается, что веса всех рёбер различны (если это не так, то можно упорядочить рёбра сначала по весу, а потом по номеру).&lt;br /&gt;
&lt;br /&gt;
Алгоритм Крускала основан на следующих двух свойствах задачи:&lt;br /&gt;
* &#039;&#039;&#039;Минимальное ребро графа&#039;&#039;&#039;. Если &amp;lt;math&amp;gt;e^*&amp;lt;/math&amp;gt; – единственное ребро графа с минимальным весом, то оно принадлежит минимальному остовному дереву..&lt;br /&gt;
* &#039;&#039;&#039;Схлопывание фрагментов&#039;&#039;&#039;. Пусть &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; – фрагмент минимального остовного дерева графа &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;, а граф &amp;lt;math&amp;gt;G&#039;&amp;lt;/math&amp;gt; получен из &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt; склеиванием вершин, принадлежащих &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt;. Тогда объединение &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; и минимального остовного дерева графа &amp;lt;math&amp;gt;G&#039;&amp;lt;/math&amp;gt; даёт минимальное остовное дерево исходного графа &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В начале работы алгоритма каждая вершина графа &amp;lt;math&amp;gt;G&amp;lt;/math&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;math&amp;gt;m \ln n&amp;lt;/math&amp;gt; (например, с помощью быстрой сортировки), затем просматриваются в порядке увеличения веса за время &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt;, при этом для хранения информации о текущих фрагментах используется [[система непересекающихся множеств]]&amp;lt;ref&amp;gt;Tarjan, Robert Endre. “Efficiency of a Good but Not Linear Set Union Algorithm.” Journal of the ACM 22, no. 2 (April 1975): 215–25. doi:10.1145/321879.321884.&amp;lt;/ref&amp;gt; с общим временем работы &amp;lt;math&amp;gt;O(m \alpha(m, n))&amp;lt;/math&amp;gt;. Итоговая сложность алгоритма &amp;lt;math&amp;gt;O(m \ln n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Как видно, наибольшую сложность имеет этап сортировки, при этом большая часть рёбер сортируется напрасно: они всё равно будут отброшены, как принадлежащие одному фрагменту. Использование инкрементальной быстрой сортировки&amp;lt;ref&amp;gt;Navarro, Gonzalo, and Rodrigo Paredes. “On Sorting, Heaps, and Minimum Spanning Trees.” Algorithmica 57, no. 4 (March 23, 2010): 585–620. doi:10.1007/s00453-010-9400-6.&amp;lt;/ref&amp;gt; (англ. IQS: Incremental Quick Sort) позволяет снизить затраты на сортировку, так что среднее время работы алгоритма составляет &amp;lt;math&amp;gt;O(m + n \ln^2 n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В случае, если рёбра графа изначально отсортированы по весу рёбер, сложность алгоритма снижается до &amp;lt;math&amp;gt;O(m \alpha(m, n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
=== Описание ресурса параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Пусть задан связный неориентированный граф &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; с весами рёбер &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt;. Предполагается, что веса всех рёбер различны (если это не так, то можно упорядочить рёбра сначала по весу, а потом по номеру).&lt;br /&gt;
&lt;br /&gt;
Алгоритм Борувки основан на следующих двух свойствах задачи:&lt;br /&gt;
* &#039;&#039;&#039;Минимальное ребро фрагмента&#039;&#039;&#039;. Пусть &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; – фрагмент минимального остовного дерева и &amp;lt;math&amp;gt;e_F&amp;lt;/math&amp;gt; – ребро наименьшего веса, исходящее из &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; (т.е. ровно один его конец является вершиной из &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt;). Если ребро &amp;lt;math&amp;gt;e_F&amp;lt;/math&amp;gt; единственно, то оно принадлежит минимальному остовному дереву.&lt;br /&gt;
* &#039;&#039;&#039;Схлопывание фрагментов&#039;&#039;&#039;. Пусть &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; – фрагмент минимального остовного дерева графа &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;, а граф &amp;lt;math&amp;gt;G&#039;&amp;lt;/math&amp;gt; получен из &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt; склеиванием вершин, принадлежащих &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt;. Тогда объединение &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; и минимального остовного дерева графа &amp;lt;math&amp;gt;G&#039;&amp;lt;/math&amp;gt; даёт минимальное остовное дерево исходного графа &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В начале работы алгоритма каждая вершина графа &amp;lt;math&amp;gt;G&amp;lt;/math&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;
* C++: [http://www.boost.org/libs/graph/doc/ Boost Graph Library] (функция &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph/doc/kruskal_min_spanning_tree.html kruskal_min_spanning_tree]&amp;lt;/code&amp;gt;), сложность &amp;lt;math&amp;gt;O(m \ln m)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* C++, MPI: [http://www.boost.org/libs/graph_parallel/doc/html/index.html Parallel Boost Graph Library]&lt;br /&gt;
** функция &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#merge-local-minimum-spanning-trees merge_local_minimum_spanning_trees]&amp;lt;/code&amp;gt; реализует алгоритм Крускала;&lt;br /&gt;
** функции &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#dense-boruvka-minimum-spanning-tree dense_boruvka_minimum_spanning_tree]&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#boruvka-then-merge boruvka_then_merge]&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#boruvka-mixed-merge boruvka_mixed_merge]&amp;lt;/code&amp;gt; сочетают [[алгоритм Борувки]] и алгоритм Крускала.&lt;br /&gt;
* Python: [https://networkx.github.io NetworkX] (функция &amp;lt;code&amp;gt;[http://networkx.github.io/documentation/networkx-1.9.1/reference/generated/networkx.algorithms.mst.minimum_spanning_tree.html minimum_spanning_tree]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Java: [http://jgrapht.org JGraphT] (класс &amp;lt;code&amp;gt;[http://jgrapht.org/javadoc/org/jgrapht/alg/KruskalMinimumSpanningTree.html KruskalMinimumSpanningTree]&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Daryin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9A%D1%80%D1%83%D1%81%D0%BA%D0%B0%D0%BB%D0%B0&amp;diff=2085</id>
		<title>Алгоритм Крускала</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9A%D1%80%D1%83%D1%81%D0%BA%D0%B0%D0%BB%D0%B0&amp;diff=2085"/>
		<updated>2015-07-08T12:52:18Z</updated>

		<summary type="html">&lt;p&gt;Daryin: /* Математическое описание */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Свойства и структура алгоритмов ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм Крускала&#039;&#039;&#039;&amp;lt;ref&amp;gt;Kruskal, Joseph B. “On the Shortest Spanning Subtree of a Graph and the Traveling Salesman Problem.” Proceedings of the American Mathematical Society 7, no. 1 (January 1956): 48–50. doi:10.1090/S0002-9939-1956-0078686-7.&amp;lt;/ref&amp;gt; предназначен для решения [[Построение минимального остовного дерева (MST)|задачи о построении минимального остовного дерева]] во взвешенном неориентированном графе. Последовательная сложность алгоритма &amp;lt;math&amp;gt;O(m \ln n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В отличие от алгоритмов [[Алгоритм Прима|Прима]] и [[Алгоритм Борувки|Борувки]], алгоритм Крускала не требует информации о рёбрах конкретной вершины, вместо этого на его вход подаётся общий список рёбер графа в произвольном порядке. Кроме этого, каждое (ненаправленное) ребро достаточно представить лишь одной из его направленных дуг, что на практике означает в два раза меньший объём вычислений. Последовательная версия алгоритма Крускала работает, как правило, быстрее последовательной версии алгоритма Борувки, а при условии предварительной сортировки списка рёбер по весу сложность алгоритма снижается до &amp;lt;math&amp;gt;O(m \alpha(m, n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание ===&lt;br /&gt;
&lt;br /&gt;
Пусть задан связный неориентированный граф &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; с весами рёбер &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt;. Предполагается, что веса всех рёбер различны (если это не так, то можно упорядочить рёбра сначала по весу, а потом по номеру).&lt;br /&gt;
&lt;br /&gt;
Алгоритм Крускала основан на следующих двух свойствах задачи:&lt;br /&gt;
* &#039;&#039;&#039;Минимальное ребро графа&#039;&#039;&#039;. Если &amp;lt;math&amp;gt;e^*&amp;lt;/math&amp;gt; – единственное ребро графа с минимальным весом, то оно принадлежит минимальному остовному дереву..&lt;br /&gt;
* &#039;&#039;&#039;Схлопывание фрагментов&#039;&#039;&#039;. Пусть &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; – фрагмент минимального остовного дерева графа &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;, а граф &amp;lt;math&amp;gt;G&#039;&amp;lt;/math&amp;gt; получен из &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt; склеиванием вершин, принадлежащих &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt;. Тогда объединение &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; и минимального остовного дерева графа &amp;lt;math&amp;gt;G&#039;&amp;lt;/math&amp;gt; даёт минимальное остовное дерево исходного графа &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В начале работы алгоритма каждая вершина графа &amp;lt;math&amp;gt;G&amp;lt;/math&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;math&amp;gt;m \ln n&amp;lt;/math&amp;gt; (например, с помощью быстрой сортировки), затем просматриваются в порядке увеличения веса за время &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt;, при этом для хранения информации о текущих фрагментах используется [[система непересекающихся множеств]]&amp;lt;ref&amp;gt;Tarjan, Robert Endre. “Efficiency of a Good but Not Linear Set Union Algorithm.” Journal of the ACM 22, no. 2 (April 1975): 215–25. doi:10.1145/321879.321884.&amp;lt;/ref&amp;gt; с общим временем работы &amp;lt;math&amp;gt;O(m \alpha(m, n))&amp;lt;/math&amp;gt;. Итоговая сложность алгоритма &amp;lt;math&amp;gt;O(m \ln n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Как видно, наибольшую сложность имеет этап сортировки, при этом большая часть рёбер сортируется напрасно: они всё равно будут отброшены, как принадлежащие одному фрагменту. Использование инкрементальной быстрой сортировки&amp;lt;ref&amp;gt;Navarro, Gonzalo, and Rodrigo Paredes. “On Sorting, Heaps, and Minimum Spanning Trees.” Algorithmica 57, no. 4 (March 23, 2010): 585–620. doi:10.1007/s00453-010-9400-6.&amp;lt;/ref&amp;gt; (англ. IQS: Incremental Quick Sort) позволяет снизить затраты на сортировку, так что среднее время работы алгоритма составляет &amp;lt;math&amp;gt;O(m + n \ln^2 n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В случае, если рёбра графа изначально отсортированы по весу рёбер, сложность алгоритма снижается до &amp;lt;math&amp;gt;O(m \alpha(m, n))&amp;lt;/math&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;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
* C++: [http://www.boost.org/libs/graph/doc/ Boost Graph Library] (функция &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph/doc/kruskal_min_spanning_tree.html kruskal_min_spanning_tree]&amp;lt;/code&amp;gt;), сложность &amp;lt;math&amp;gt;O(m \ln m)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* C++, MPI: [http://www.boost.org/libs/graph_parallel/doc/html/index.html Parallel Boost Graph Library]&lt;br /&gt;
** функция &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#merge-local-minimum-spanning-trees merge_local_minimum_spanning_trees]&amp;lt;/code&amp;gt; реализует алгоритм Крускала;&lt;br /&gt;
** функции &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#dense-boruvka-minimum-spanning-tree dense_boruvka_minimum_spanning_tree]&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#boruvka-then-merge boruvka_then_merge]&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#boruvka-mixed-merge boruvka_mixed_merge]&amp;lt;/code&amp;gt; сочетают [[алгоритм Борувки]] и алгоритм Крускала.&lt;br /&gt;
* Python: [https://networkx.github.io NetworkX] (функция &amp;lt;code&amp;gt;[http://networkx.github.io/documentation/networkx-1.9.1/reference/generated/networkx.algorithms.mst.minimum_spanning_tree.html minimum_spanning_tree]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Java: [http://jgrapht.org JGraphT] (класс &amp;lt;code&amp;gt;[http://jgrapht.org/javadoc/org/jgrapht/alg/KruskalMinimumSpanningTree.html KruskalMinimumSpanningTree]&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Daryin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9A%D1%80%D1%83%D1%81%D0%BA%D0%B0%D0%BB%D0%B0&amp;diff=2084</id>
		<title>Алгоритм Крускала</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9A%D1%80%D1%83%D1%81%D0%BA%D0%B0%D0%BB%D0%B0&amp;diff=2084"/>
		<updated>2015-07-08T12:49:06Z</updated>

		<summary type="html">&lt;p&gt;Daryin: /* Математическое описание */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Свойства и структура алгоритмов ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм Крускала&#039;&#039;&#039;&amp;lt;ref&amp;gt;Kruskal, Joseph B. “On the Shortest Spanning Subtree of a Graph and the Traveling Salesman Problem.” Proceedings of the American Mathematical Society 7, no. 1 (January 1956): 48–50. doi:10.1090/S0002-9939-1956-0078686-7.&amp;lt;/ref&amp;gt; предназначен для решения [[Построение минимального остовного дерева (MST)|задачи о построении минимального остовного дерева]] во взвешенном неориентированном графе. Последовательная сложность алгоритма &amp;lt;math&amp;gt;O(m \ln n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В отличие от алгоритмов [[Алгоритм Прима|Прима]] и [[Алгоритм Борувки|Борувки]], алгоритм Крускала не требует информации о рёбрах конкретной вершины, вместо этого на его вход подаётся общий список рёбер графа в произвольном порядке. Кроме этого, каждое (ненаправленное) ребро достаточно представить лишь одной из его направленных дуг, что на практике означает в два раза меньший объём вычислений. Последовательная версия алгоритма Крускала работает, как правило, быстрее последовательной версии алгоритма Борувки, а при условии предварительной сортировки списка рёбер по весу сложность алгоритма снижается до &amp;lt;math&amp;gt;O(m \alpha(m, n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание ===&lt;br /&gt;
&lt;br /&gt;
Пусть задан связный неориентированный граф &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; с весами рёбер &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt;. Предполагается, что веса всех рёбер различны (если это не так, то можно упорядочить рёбра сначала по весу, а потом по номеру).&lt;br /&gt;
&lt;br /&gt;
Алгоритм Крускала основан на следующих двух свойствах задачи:&lt;br /&gt;
* &#039;&#039;&#039;Минимальное ребро графа&#039;&#039;&#039;. Если &amp;lt;math&amp;gt;e^*&amp;lt;/math&amp;gt; – единственное ребро графа с минимальным весом, то оно принадлежит минимальному остовному дереву..&lt;br /&gt;
* &#039;&#039;&#039;Схлопывание фрагментов&#039;&#039;&#039;. Пусть &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; – фрагмент минимального остовного дерева графа &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;, а граф &amp;lt;math&amp;gt;G&#039;&amp;lt;/math&amp;gt; получен из &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt; склеиванием вершин, принадлежащих &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt;. Тогда объединение &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; и минимального остовного дерева графа &amp;lt;math&amp;gt;G&#039;&amp;lt;/math&amp;gt; даёт минимальное остовное дерево исходного графа &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В начале работы алгоритма каждая вершина графа &amp;lt;math&amp;gt;G&amp;lt;/math&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;math&amp;gt;m \ln n&amp;lt;/math&amp;gt; (например, с помощью быстрой сортировки), затем просматриваются в порядке увеличения веса за время &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt;, при этом для хранения информации о текущих фрагментах используется [[система непересекающихся множеств]]&amp;lt;ref&amp;gt;Tarjan, Robert Endre. “Efficiency of a Good but Not Linear Set Union Algorithm.” Journal of the ACM 22, no. 2 (April 1975): 215–25. doi:10.1145/321879.321884.&amp;lt;/ref&amp;gt; с общим временем работы &amp;lt;math&amp;gt;O(m \alpha(m, n))&amp;lt;/math&amp;gt;. Итоговая сложность алгоритма &amp;lt;math&amp;gt;O(m \ln n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Как видно, наибольшую сложность имеет этап сортировки, при этом большая часть рёбер сортируется напрасно: они всё равно будут отброшены, как принадлежащие одному фрагменту. Использование инкрементальной быстрой сортировки&amp;lt;ref&amp;gt;Navarro, Gonzalo, and Rodrigo Paredes. “On Sorting, Heaps, and Minimum Spanning Trees.” Algorithmica 57, no. 4 (March 23, 2010): 585–620. doi:10.1007/s00453-010-9400-6.&amp;lt;/ref&amp;gt; (англ. IQS: Incremental Quick Sort) позволяет снизить затраты на сортировку, так что среднее время работы алгоритма составляет &amp;lt;math&amp;gt;O(m + n \ln^2 n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В случае, если рёбра графа изначально отсортированы по весу рёбер, сложность алгоритма снижается до &amp;lt;math&amp;gt;O(m \alpha(m, n))&amp;lt;/math&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;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
* C++: [http://www.boost.org/libs/graph/doc/ Boost Graph Library] (функция &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph/doc/kruskal_min_spanning_tree.html kruskal_min_spanning_tree]&amp;lt;/code&amp;gt;), сложность &amp;lt;math&amp;gt;O(m \ln m)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* C++, MPI: [http://www.boost.org/libs/graph_parallel/doc/html/index.html Parallel Boost Graph Library]&lt;br /&gt;
** функция &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#merge-local-minimum-spanning-trees merge_local_minimum_spanning_trees]&amp;lt;/code&amp;gt; реализует алгоритм Крускала;&lt;br /&gt;
** функции &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#dense-boruvka-minimum-spanning-tree dense_boruvka_minimum_spanning_tree]&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#boruvka-then-merge boruvka_then_merge]&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#boruvka-mixed-merge boruvka_mixed_merge]&amp;lt;/code&amp;gt; сочетают [[алгоритм Борувки]] и алгоритм Крускала.&lt;br /&gt;
* Python: [https://networkx.github.io NetworkX] (функция &amp;lt;code&amp;gt;[http://networkx.github.io/documentation/networkx-1.9.1/reference/generated/networkx.algorithms.mst.minimum_spanning_tree.html minimum_spanning_tree]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Java: [http://jgrapht.org JGraphT] (класс &amp;lt;code&amp;gt;[http://jgrapht.org/javadoc/org/jgrapht/alg/KruskalMinimumSpanningTree.html KruskalMinimumSpanningTree]&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Daryin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9A%D1%80%D1%83%D1%81%D0%BA%D0%B0%D0%BB%D0%B0&amp;diff=2083</id>
		<title>Алгоритм Крускала</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9A%D1%80%D1%83%D1%81%D0%BA%D0%B0%D0%BB%D0%B0&amp;diff=2083"/>
		<updated>2015-07-08T12:41:11Z</updated>

		<summary type="html">&lt;p&gt;Daryin: /* Последовательная сложность алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Свойства и структура алгоритмов ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм Крускала&#039;&#039;&#039;&amp;lt;ref&amp;gt;Kruskal, Joseph B. “On the Shortest Spanning Subtree of a Graph and the Traveling Salesman Problem.” Proceedings of the American Mathematical Society 7, no. 1 (January 1956): 48–50. doi:10.1090/S0002-9939-1956-0078686-7.&amp;lt;/ref&amp;gt; предназначен для решения [[Построение минимального остовного дерева (MST)|задачи о построении минимального остовного дерева]] во взвешенном неориентированном графе. Последовательная сложность алгоритма &amp;lt;math&amp;gt;O(m \ln n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В отличие от алгоритмов [[Алгоритм Прима|Прима]] и [[Алгоритм Борувки|Борувки]], алгоритм Крускала не требует информации о рёбрах конкретной вершины, вместо этого на его вход подаётся общий список рёбер графа в произвольном порядке. Кроме этого, каждое (ненаправленное) ребро достаточно представить лишь одной из его направленных дуг, что на практике означает в два раза меньший объём вычислений. Последовательная версия алгоритма Крускала работает, как правило, быстрее последовательной версии алгоритма Борувки, а при условии предварительной сортировки списка рёбер по весу сложность алгоритма снижается до &amp;lt;math&amp;gt;O(m \alpha(m, n))&amp;lt;/math&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;math&amp;gt;m \ln n&amp;lt;/math&amp;gt; (например, с помощью быстрой сортировки), затем просматриваются в порядке увеличения веса за время &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt;, при этом для хранения информации о текущих фрагментах используется [[система непересекающихся множеств]]&amp;lt;ref&amp;gt;Tarjan, Robert Endre. “Efficiency of a Good but Not Linear Set Union Algorithm.” Journal of the ACM 22, no. 2 (April 1975): 215–25. doi:10.1145/321879.321884.&amp;lt;/ref&amp;gt; с общим временем работы &amp;lt;math&amp;gt;O(m \alpha(m, n))&amp;lt;/math&amp;gt;. Итоговая сложность алгоритма &amp;lt;math&amp;gt;O(m \ln n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Как видно, наибольшую сложность имеет этап сортировки, при этом большая часть рёбер сортируется напрасно: они всё равно будут отброшены, как принадлежащие одному фрагменту. Использование инкрементальной быстрой сортировки&amp;lt;ref&amp;gt;Navarro, Gonzalo, and Rodrigo Paredes. “On Sorting, Heaps, and Minimum Spanning Trees.” Algorithmica 57, no. 4 (March 23, 2010): 585–620. doi:10.1007/s00453-010-9400-6.&amp;lt;/ref&amp;gt; (англ. IQS: Incremental Quick Sort) позволяет снизить затраты на сортировку, так что среднее время работы алгоритма составляет &amp;lt;math&amp;gt;O(m + n \ln^2 n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В случае, если рёбра графа изначально отсортированы по весу рёбер, сложность алгоритма снижается до &amp;lt;math&amp;gt;O(m \alpha(m, n))&amp;lt;/math&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;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
* C++: [http://www.boost.org/libs/graph/doc/ Boost Graph Library] (функция &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph/doc/kruskal_min_spanning_tree.html kruskal_min_spanning_tree]&amp;lt;/code&amp;gt;), сложность &amp;lt;math&amp;gt;O(m \ln m)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* C++, MPI: [http://www.boost.org/libs/graph_parallel/doc/html/index.html Parallel Boost Graph Library]&lt;br /&gt;
** функция &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#merge-local-minimum-spanning-trees merge_local_minimum_spanning_trees]&amp;lt;/code&amp;gt; реализует алгоритм Крускала;&lt;br /&gt;
** функции &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#dense-boruvka-minimum-spanning-tree dense_boruvka_minimum_spanning_tree]&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#boruvka-then-merge boruvka_then_merge]&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#boruvka-mixed-merge boruvka_mixed_merge]&amp;lt;/code&amp;gt; сочетают [[алгоритм Борувки]] и алгоритм Крускала.&lt;br /&gt;
* Python: [https://networkx.github.io NetworkX] (функция &amp;lt;code&amp;gt;[http://networkx.github.io/documentation/networkx-1.9.1/reference/generated/networkx.algorithms.mst.minimum_spanning_tree.html minimum_spanning_tree]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Java: [http://jgrapht.org JGraphT] (класс &amp;lt;code&amp;gt;[http://jgrapht.org/javadoc/org/jgrapht/alg/KruskalMinimumSpanningTree.html KruskalMinimumSpanningTree]&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Daryin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9A%D1%80%D1%83%D1%81%D0%BA%D0%B0%D0%BB%D0%B0&amp;diff=2082</id>
		<title>Алгоритм Крускала</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9A%D1%80%D1%83%D1%81%D0%BA%D0%B0%D0%BB%D0%B0&amp;diff=2082"/>
		<updated>2015-07-08T12:40:54Z</updated>

		<summary type="html">&lt;p&gt;Daryin: /* Общее описание алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Свойства и структура алгоритмов ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм Крускала&#039;&#039;&#039;&amp;lt;ref&amp;gt;Kruskal, Joseph B. “On the Shortest Spanning Subtree of a Graph and the Traveling Salesman Problem.” Proceedings of the American Mathematical Society 7, no. 1 (January 1956): 48–50. doi:10.1090/S0002-9939-1956-0078686-7.&amp;lt;/ref&amp;gt; предназначен для решения [[Построение минимального остовного дерева (MST)|задачи о построении минимального остовного дерева]] во взвешенном неориентированном графе. Последовательная сложность алгоритма &amp;lt;math&amp;gt;O(m \ln n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В отличие от алгоритмов [[Алгоритм Прима|Прима]] и [[Алгоритм Борувки|Борувки]], алгоритм Крускала не требует информации о рёбрах конкретной вершины, вместо этого на его вход подаётся общий список рёбер графа в произвольном порядке. Кроме этого, каждое (ненаправленное) ребро достаточно представить лишь одной из его направленных дуг, что на практике означает в два раза меньший объём вычислений. Последовательная версия алгоритма Крускала работает, как правило, быстрее последовательной версии алгоритма Борувки, а при условии предварительной сортировки списка рёбер по весу сложность алгоритма снижается до &amp;lt;math&amp;gt;O(m \alpha(m, n))&amp;lt;/math&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;math&amp;gt;m \ln m&amp;lt;/math&amp;gt;, затем просматриваются в порядке увеличения веса за время &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt; (например, с помощью быстрой сортировки), при этом для хранения информации о текущих фрагментах используется [[система непересекающихся множеств]]&amp;lt;ref&amp;gt;Tarjan, Robert Endre. “Efficiency of a Good but Not Linear Set Union Algorithm.” Journal of the ACM 22, no. 2 (April 1975): 215–25. doi:10.1145/321879.321884.&amp;lt;/ref&amp;gt; с общим временем работы &amp;lt;math&amp;gt;O(m \alpha(m, n))&amp;lt;/math&amp;gt;. Итоговая сложность алгоритма &amp;lt;math&amp;gt;O(m \ln m)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Как видно, наибольшую сложность имеет этап сортировки, при этом большая часть рёбер сортируется напрасно: они всё равно будут отброшены, как принадлежащие одному фрагменту. Использование инкрементальной быстрой сортировки&amp;lt;ref&amp;gt;Navarro, Gonzalo, and Rodrigo Paredes. “On Sorting, Heaps, and Minimum Spanning Trees.” Algorithmica 57, no. 4 (March 23, 2010): 585–620. doi:10.1007/s00453-010-9400-6.&amp;lt;/ref&amp;gt; (англ. IQS: Incremental Quick Sort) позволяет снизить затраты на сортировку, так что среднее время работы алгоритма составляет &amp;lt;math&amp;gt;O(m + n \ln^2 n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В случае, если рёбра графа изначально отсортированы по весу рёбер, сложность алгоритма снижается до &amp;lt;math&amp;gt;O(m \alpha(m, n))&amp;lt;/math&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;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
* C++: [http://www.boost.org/libs/graph/doc/ Boost Graph Library] (функция &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph/doc/kruskal_min_spanning_tree.html kruskal_min_spanning_tree]&amp;lt;/code&amp;gt;), сложность &amp;lt;math&amp;gt;O(m \ln m)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* C++, MPI: [http://www.boost.org/libs/graph_parallel/doc/html/index.html Parallel Boost Graph Library]&lt;br /&gt;
** функция &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#merge-local-minimum-spanning-trees merge_local_minimum_spanning_trees]&amp;lt;/code&amp;gt; реализует алгоритм Крускала;&lt;br /&gt;
** функции &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#dense-boruvka-minimum-spanning-tree dense_boruvka_minimum_spanning_tree]&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#boruvka-then-merge boruvka_then_merge]&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dehne_gotz_min_spanning_tree.html#boruvka-mixed-merge boruvka_mixed_merge]&amp;lt;/code&amp;gt; сочетают [[алгоритм Борувки]] и алгоритм Крускала.&lt;br /&gt;
* Python: [https://networkx.github.io NetworkX] (функция &amp;lt;code&amp;gt;[http://networkx.github.io/documentation/networkx-1.9.1/reference/generated/networkx.algorithms.mst.minimum_spanning_tree.html minimum_spanning_tree]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Java: [http://jgrapht.org JGraphT] (класс &amp;lt;code&amp;gt;[http://jgrapht.org/javadoc/org/jgrapht/alg/KruskalMinimumSpanningTree.html KruskalMinimumSpanningTree]&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Daryin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A2%D0%B0%D1%80%D1%8C%D1%8F%D0%BD%D0%B0_%D0%BF%D0%BE%D0%B8%D1%81%D0%BA%D0%B0_%C2%AB%D0%BC%D0%BE%D1%81%D1%82%D0%BE%D0%B2%C2%BB_%D0%B2_%D0%B3%D1%80%D0%B0%D1%84%D0%B5&amp;diff=2057</id>
		<title>Алгоритм Тарьяна поиска «мостов» в графе</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A2%D0%B0%D1%80%D1%8C%D1%8F%D0%BD%D0%B0_%D0%BF%D0%BE%D0%B8%D1%81%D0%BA%D0%B0_%C2%AB%D0%BC%D0%BE%D1%81%D1%82%D0%BE%D0%B2%C2%BB_%D0%B2_%D0%B3%D1%80%D0%B0%D1%84%D0%B5&amp;diff=2057"/>
		<updated>2015-07-07T09:30:20Z</updated>

		<summary type="html">&lt;p&gt;Daryin: /* Описание ресурса параллелизма алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Свойства и структура алгоритмов ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм Тарьяна&#039;&#039;&#039; &amp;lt;ref&amp;gt;Tarjan, R Endre. “A Note on Finding the Bridges of a Graph.” Information Processing Letters 2, no. 6 (April 1974): 160–61. doi:10.1016/0020-0190(74)90003-9.&amp;lt;/ref&amp;gt; находит [[Связность в графах|мосты]] в неориентированном графе за время &amp;lt;math&amp;gt;O(m)&amp;lt;/math&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;math&amp;gt;O(m)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
=== Описание ресурса параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм Тарьяна может работать с любым остовным деревом, поэтому можно применить эффективно параллелизуемый [[Поиск в ширину (BFS)|поиск в ширину]]. Последующие вычисления также могут быть параллелизованы.&lt;br /&gt;
&lt;br /&gt;
Параллельный [[Алгоритм Тарьяна-Вишкина поиска компонент двусвязности|алгоритм Тарьяна-Вишкина]]&amp;lt;ref&amp;gt;Tarjan, Robert Endre, and Uzi Vishkin. “An Efficient Parallel Biconnectivity Algorithm.” SIAM Journal on Computing 14, no. 4 (1985): 862–74.&amp;lt;/ref&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;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Daryin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA_%D0%B8%D0%B7%D0%BE%D0%BC%D0%BE%D1%80%D1%84%D0%BD%D1%8B%D1%85_%D0%BF%D0%BE%D0%B4%D0%B3%D1%80%D0%B0%D1%84%D0%BE%D0%B2&amp;diff=2056</id>
		<title>Поиск изоморфных подграфов</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA_%D0%B8%D0%B7%D0%BE%D0%BC%D0%BE%D1%80%D1%84%D0%BD%D1%8B%D1%85_%D0%BF%D0%BE%D0%B4%D0%B3%D1%80%D0%B0%D1%84%D0%BE%D0%B2&amp;diff=2056"/>
		<updated>2015-07-07T09:13:46Z</updated>

		<summary type="html">&lt;p&gt;Daryin: /* Постановка задачи */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Постановка задачи ==&lt;br /&gt;
&lt;br /&gt;
Пусть заданы два графа &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;H&amp;lt;/math&amp;gt;. &#039;&#039;&#039;Задача поиска изоморфных подграфов&#039;&#039;&#039; состоит в том, чтобы определить, существует ли у графа &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt; подграф, изоморфный &amp;lt;math&amp;gt;H&amp;lt;/math&amp;gt;, и в случае положительного ответа – предъявить хотя бы один такой подграф.&lt;br /&gt;
&lt;br /&gt;
== Свойства задачи ==&lt;br /&gt;
&lt;br /&gt;
Задача поиска изоморфных подграфов является NP-полной, поэтому не существует известных алгоритмов, решающих её за полиномиальное время.&lt;br /&gt;
&lt;br /&gt;
== Алгоритмы решения задачи ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;[[Алгоритм Ульмана]]&#039;&#039;&#039;&amp;lt;ref&amp;gt;Ullmann, Julian R. “An Algorithm for Subgraph Isomorphism.” Journal of the ACM 23, no. 1 (January 1976): 31–42. doi:10.1145/321921.321925.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Ullmann, Julian R. “Bit-Vector Algorithms for Binary Constraint Satisfaction and Subgraph Isomorphism.” Journal of Experimental Algorithmics 15 (March 2010): 1.1. doi:10.1145/1671970.1921702.&amp;lt;/ref&amp;gt; решает задачу поиска изоморфных подграфов за экспоненциальное время, при этом&lt;br /&gt;
* для фиксированного графа &amp;lt;math&amp;gt;H&amp;lt;/math&amp;gt; время полиномиальное;&lt;br /&gt;
* для планарного графа &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt; время работы линейное (при фиксированном графе &amp;lt;math&amp;gt;H&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;[[Алгоритм VF2]]&#039;&#039;&#039;&amp;lt;ref&amp;gt;Cordella, L P, P Foggia, C Sansone, and M Vento. “A (Sub)Graph Isomorphism Algorithm for Matching Large Graphs.” IEEE Transactions on Pattern Analysis and Machine Intelligence 26, no. 10 (October 2004): 1367–72. doi:10.1109/TPAMI.2004.75.&amp;lt;/ref&amp;gt; разработан специально для применения на больших графах.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Daryin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%94%D0%B6%D0%BE%D0%BD%D1%81%D0%BE%D0%BD%D0%B0&amp;diff=2008</id>
		<title>Алгоритм Джонсона</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%94%D0%B6%D0%BE%D0%BD%D1%81%D0%BE%D0%BD%D0%B0&amp;diff=2008"/>
		<updated>2015-06-30T20:03:55Z</updated>

		<summary type="html">&lt;p&gt;Daryin: /* Существующие реализации алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Свойства и структура алгоритмов ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм Джонсона&#039;&#039;&#039;&amp;lt;ref&amp;gt;Johnson, Donald B. “Efficient Algorithms for Shortest Paths in Sparse Networks.” Journal of the ACM 24, no. 1 (January 1977): 1–13. doi:10.1145/321992.321993.&amp;lt;/ref&amp;gt; предназначен для решения [[Поиск кратчайшего пути для всех пар вершин (APSP)|задачи поиска всех кратчайших путей на графе]]. Для заданного ориентированного взвешенного графа алгоритм находит кратчайшие расстояния между всеми парами вершин за время &amp;lt;math&amp;gt;O(mn + n^2 \ln n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В случае разреженных графов алгоритм Джонсона эффективнее [[Алгоритм Флойда-Уоршелла|алгоритма Флойда-Уоршела]], имеющего сложность &amp;lt;math&amp;gt;O(n^3)&amp;lt;/math&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;
&lt;br /&gt;
На этапе предварительной обработки выполняется алгоритм Беллмана-Форда сложностью &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt;. Далее для каждой вершины графа выполняется алгоритм Дейкстры сложностью &amp;lt;math&amp;gt;O(m + n \ln n)&amp;lt;/math&amp;gt;. Итоговая сложность алгоритма Джонсона &amp;lt;math&amp;gt;O(mn + n^2 \ln n)&amp;lt;/math&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;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
* C++: [http://www.boost.org/libs/graph/doc/ Boost Graph Library] (функция &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph/doc/johnson_all_pairs_shortest.html johnson_all_pairs_shortest_paths]&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Daryin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%94%D0%B6%D0%BE%D0%BD%D1%81%D0%BE%D0%BD%D0%B0&amp;diff=2007</id>
		<title>Алгоритм Джонсона</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%94%D0%B6%D0%BE%D0%BD%D1%81%D0%BE%D0%BD%D0%B0&amp;diff=2007"/>
		<updated>2015-06-30T19:53:14Z</updated>

		<summary type="html">&lt;p&gt;Daryin: Опечатки&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Свойства и структура алгоритмов ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм Джонсона&#039;&#039;&#039;&amp;lt;ref&amp;gt;Johnson, Donald B. “Efficient Algorithms for Shortest Paths in Sparse Networks.” Journal of the ACM 24, no. 1 (January 1977): 1–13. doi:10.1145/321992.321993.&amp;lt;/ref&amp;gt; предназначен для решения [[Поиск кратчайшего пути для всех пар вершин (APSP)|задачи поиска всех кратчайших путей на графе]]. Для заданного ориентированного взвешенного графа алгоритм находит кратчайшие расстояния между всеми парами вершин за время &amp;lt;math&amp;gt;O(mn + n^2 \ln n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В случае разреженных графов алгоритм Джонсона эффективнее [[Алгоритм Флойда-Уоршелла|алгоритма Флойда-Уоршела]], имеющего сложность &amp;lt;math&amp;gt;O(n^3)&amp;lt;/math&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;
&lt;br /&gt;
На этапе предварительной обработки выполняется алгоритм Беллмана-Форда сложностью &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt;. Далее для каждой вершины графа выполняется алгоритм Дейкстры сложностью &amp;lt;math&amp;gt;O(m + n \ln n)&amp;lt;/math&amp;gt;. Итоговая сложность алгоритма Джонсона &amp;lt;math&amp;gt;O(mn + n^2 \ln n)&amp;lt;/math&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;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Daryin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%94%D0%B6%D0%BE%D0%BD%D1%81%D0%BE%D0%BD%D0%B0&amp;diff=2006</id>
		<title>Алгоритм Джонсона</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%94%D0%B6%D0%BE%D0%BD%D1%81%D0%BE%D0%BD%D0%B0&amp;diff=2006"/>
		<updated>2015-06-30T19:51:41Z</updated>

		<summary type="html">&lt;p&gt;Daryin: Общее описание алгоритма&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Свойства и структура алгоритмов ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм Джонсона&#039;&#039;&#039;&amp;lt;ref&amp;gt;Johnson, Donald B. “Efficient Algorithms for Shortest Paths in Sparse Networks.” Journal of the ACM 24, no. 1 (January 1977): 1–13. doi:10.1145/321992.321993.&amp;lt;/ref&amp;gt; предназначен для решения [[Поиск кратчайшего пути для всех пар вершин (APSP)|задачи поиска всех кратчайших путей на графе]]. Для заданного ориентированного взвешенного графа алгоритм находит кратчайшие расстояния между всеми парами вершин за время &amp;lt;math&amp;gt;O(mn + n^2 \ln n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В случае разреженных графов алгоритм Джонсона эффективнее [[Алгоритм Флойда-Уоршелла|алгоритма Флойда-Уоршела]], имеющего сложность &amp;lt;math&amp;gt;O(n^3)&amp;lt;/math&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;
&lt;br /&gt;
На этапе предварительной обработки выполняется алгоритм Беллмана-Форда сложностью &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt;. Далее для каждой вершины графа выполняется алгоритм Дейкстры сложностью &amp;lt;math&amp;gt;O(m + n \ln n)&amp;lt;/math&amp;gt;. Итоговая сложность алгоритма Джонсона &amp;lt;math&amp;gt;O(mn + n^2 \ln n)&amp;lt;/math&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;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Daryin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9A%D0%BB%D0%B0%D1%81%D1%81%D0%B8%D1%84%D0%B8%D0%BA%D0%B0%D1%86%D0%B8%D1%8F_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%BE%D0%B2&amp;diff=2005</id>
		<title>Классификация алгоритмов</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9A%D0%BB%D0%B0%D1%81%D1%81%D0%B8%D1%84%D0%B8%D0%BA%D0%B0%D1%86%D0%B8%D1%8F_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%BE%D0%B2&amp;diff=2005"/>
		<updated>2015-06-30T19:39:49Z</updated>

		<summary type="html">&lt;p&gt;Daryin: /* Алгоритмы на графах */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;# &amp;lt;div id=&amp;quot;Векторные операции&amp;quot;&amp;gt;&#039;&#039;&#039;Векторные операции&#039;&#039;&#039;&amp;lt;/div&amp;gt;&lt;br /&gt;
## &#039;&#039;Суммирование сдваиванием&#039;&#039;&lt;br /&gt;
### [[Нахождение суммы элементов массива сдваиванием]]&lt;br /&gt;
### [[Нахождение частных сумм элементов массива сдваиванием]]&lt;br /&gt;
## [[Равномерная норма вектора, вещественная версия, последовательно-параллельный вариант]]&lt;br /&gt;
## [[Скалярное произведение векторов, вещественная версия, последовательно-параллельный вариант]]&lt;br /&gt;
## [[Последовательно-параллельный метод суммирования]]&lt;br /&gt;
# &amp;lt;div id=&amp;quot;Умножение матрицы на вектор&amp;quot;&amp;gt;&#039;&#039;&#039;Умножение матрицы на вектор&#039;&#039;&#039;&amp;lt;/div&amp;gt;&lt;br /&gt;
## [[Умножение плотной матрицы на вектор]]&lt;br /&gt;
# &amp;lt;div id=&amp;quot;Матричные операции&amp;quot;&amp;gt;&#039;&#039;&#039;Матричные операции&#039;&#039;&#039;&amp;lt;/div&amp;gt;&lt;br /&gt;
## [[Умножение плотных матриц]]&lt;br /&gt;
# &amp;lt;div id=&amp;quot;Разложения матриц&amp;quot;&amp;gt;&#039;&#039;&#039;Разложения матриц&#039;&#039;&#039;&amp;lt;/div&amp;gt;&lt;br /&gt;
## &#039;&#039;Треугольные разложения&#039;&#039;&lt;br /&gt;
### Метод Гаусса (накождение LU-разложения)&lt;br /&gt;
#### Метод Гаусса без перестановок&lt;br /&gt;
##### LU-разложение методом Гаусса&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;
## &#039;&#039;Унитарно-треугольные разложения&#039;&#039;&lt;br /&gt;
### [[Метод Гивенса (вращений) QR-разложения матрицы]]&lt;br /&gt;
### [[Метод Хаусхолдера (отражений) QR-разложения матрицы]]&lt;br /&gt;
## &#039;&#039;Разложения на унитарные и хессенберговы матрицы&#039;&#039;&lt;br /&gt;
### [[Метод Хаусхолдера (отражений) приведения матрицы к хессенберговой (двухдиагональной) форме]]&lt;br /&gt;
## &#039;&#039;Разложения на унитарные и диагональные матрицы&#039;&#039;&lt;br /&gt;
### [[Спектральное разложение (нахождение собственных значений и векторов)]]&lt;br /&gt;
### [[Сингулярное разложение (нахождение сингулярных значений и векторов)]]&lt;br /&gt;
# &amp;lt;div id=&amp;quot;Решение систем линейных уравнений&amp;quot;&amp;gt;&#039;&#039;&#039;Решение систем линейных уравнений&#039;&#039;&#039;&amp;lt;/div&amp;gt;&lt;br /&gt;
## [[High Performance Conjugate Gradient (HPCG) benchmark]]&lt;br /&gt;
## [[Linpack benchmark]]&lt;br /&gt;
## [[Метод Гаусса решения СЛАУ (прямой ход)]]&lt;br /&gt;
## [[Обратная подстановка (вещественный вариант)|Обратная подстановка]]&lt;br /&gt;
# &amp;lt;div id=&amp;quot;Тесты производительности компьютеров&amp;quot;&amp;gt;&#039;&#039;&#039;Тесты производительности компьютеров&#039;&#039;&#039;&amp;lt;/div&amp;gt;&lt;br /&gt;
## [[High Performance Conjugate Gradient (HPCG) benchmark]]&lt;br /&gt;
## [[Linpack benchmark]]&lt;br /&gt;
# &amp;lt;div id=&amp;quot;Преобразование Фурье&amp;quot;&amp;gt;&#039;&#039;&#039;Преобразование Фурье&#039;&#039;&#039;&amp;lt;/div&amp;gt;&lt;br /&gt;
## [[Быстрое преобразование Фурье для степеней двойки]]&lt;br /&gt;
# &amp;lt;div id=&amp;quot;Алгебра многочленов&amp;quot;&amp;gt;&#039;&#039;&#039;Алгебра многочленов&#039;&#039;&#039;&amp;lt;/div&amp;gt;&lt;br /&gt;
## [[Схема Горнера, вещественная версия, последовательный вариант|Схема Горнера]]&lt;br /&gt;
# &amp;lt;div id=&amp;quot;Численные методы интегрирования&amp;quot;&amp;gt;&#039;&#039;&#039;Численные методы интегрирования&#039;&#039;&#039;&amp;lt;/div&amp;gt;&lt;br /&gt;
## [[Квадратурные (кубатурные) методы численного интегрирования по отрезку (многомерному кубу)]]&lt;br /&gt;
### [[Квадратурные_(кубатурные)_методы_численного_интегрирования_по_отрезку_(многомерному_кубу)#Математическое описание|Метод прямоугольников]]&lt;br /&gt;
### [[Квадратурные_(кубатурные)_методы_численного_интегрирования_по_отрезку_(многомерному_кубу)#Математическое описание|Метод трапеций]]&lt;br /&gt;
### [[Квадратурные_(кубатурные)_методы_численного_интегрирования_по_отрезку_(многомерному_кубу)#Математическое описание|Метод парабол (метод Симпсона)]]&lt;br /&gt;
### [[Квадратурные_(кубатурные)_методы_численного_интегрирования_по_отрезку_(многомерному_кубу)#Математическое описание|Метод Гаусса]]&lt;br /&gt;
# &amp;lt;div id=&amp;quot;Алгоритмы на графах&amp;quot;&amp;gt;&#039;&#039;&#039;Алгоритмы на графах&#039;&#039;&#039;&amp;lt;/div&amp;gt;&lt;br /&gt;
## Обход графа&lt;br /&gt;
### [[Поиск в ширину (BFS)]]&lt;br /&gt;
### [[Поиск в глубину (DFS)]]&lt;br /&gt;
## [[Поиск кратчайшего пути от одной вершины (SSSP)]]&lt;br /&gt;
### [[Поиск в ширину (BFS)]] (для невзвешенных графов)&lt;br /&gt;
### [[Алгоритм Дейкстры]]&lt;br /&gt;
### [[Алгоритм Беллмана-Форда]]&lt;br /&gt;
### [[Алгоритм Δ-шагания]]&lt;br /&gt;
## [[Поиск кратчайшего пути для всех пар вершин (APSP)]]&lt;br /&gt;
### [[Алгоритм Джонсона]]&lt;br /&gt;
### [[Алгоритм Флойда-Уоршелла]]&lt;br /&gt;
## [[Поиск транзитивного замыкания орграфа]]&lt;br /&gt;
### [[Алгоритм Пурдома]]&lt;br /&gt;
## [[Определение диаметра графа]]&lt;br /&gt;
## [[Построение минимального остовного дерева (MST)]]&lt;br /&gt;
### [[Алгоритм Борувки]]&lt;br /&gt;
### [[Алгоритм Крускала]]&lt;br /&gt;
### [[Алгоритм Прима]]&lt;br /&gt;
### [[Алгоритм GHS]]&lt;br /&gt;
## [[Поиск изоморфных подграфов]]&lt;br /&gt;
### [[Алгоритм Ульмана]]&lt;br /&gt;
### [[Алгоритм VF2]]&lt;br /&gt;
## [[Связность в графах]]&lt;br /&gt;
### [[Алгоритм Шилоаха-Вишкина поиска компонент связности]]&lt;br /&gt;
### [[Система непересекающихся множеств]]&lt;br /&gt;
### [[Алгоритм Тарьяна поиска компонент сильной связности]]&lt;br /&gt;
### [[Алгоритм DCSC поиска компонент сильной связности]]&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;
### [[Алгоритм Гопкрофта-Карпа]]&lt;br /&gt;
## [[Вычисление betweenness centrality|Вычисление центральности вершин]]&lt;br /&gt;
# &amp;lt;div id=&amp;quot;Алгоритмы поиска&amp;quot;&amp;gt;&#039;&#039;&#039;Алгоритмы поиска&#039;&#039;&#039;&amp;lt;/div&amp;gt;&lt;br /&gt;
## [[Двоичный поиск - находит элемент в отсортированном списке]], &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt;&lt;br /&gt;
# &amp;lt;div id=&amp;quot;Алгоритмы сортировки&amp;quot;&amp;gt;&#039;&#039;&#039;Алгоритмы сортировки&#039;&#039;&#039;&amp;lt;/div&amp;gt;&lt;br /&gt;
## [[Сортировка с помощью двоичного дерева]]&lt;br /&gt;
## [[Сортировка пузырьком]]&lt;br /&gt;
## [[Сортировка слиянием (последовательный и параллельный варианты)]]&lt;br /&gt;
# &amp;lt;div id=&amp;quot;Вычислительная геометрия&amp;quot;&amp;gt;&#039;&#039;&#039;Вычислительная геометрия&#039;&#039;&#039;&amp;lt;/div&amp;gt;&lt;br /&gt;
## [[Поиск диаметра множества точек]]&lt;br /&gt;
## [[Построение выпуклой оболочки набора точек]]&lt;br /&gt;
## [[Триангуляция Делоне]]&lt;br /&gt;
## [[Диаграмма Вороного]]&lt;br /&gt;
## [[Принадлежность точки многоугольнику]]&lt;br /&gt;
## [[Пересечения выпуклых многоугольников]] - трудоёмкость &amp;lt;math&amp;gt;O(n_1 + n_2)&amp;lt;/math&amp;gt;&lt;br /&gt;
## [[Пересечение звёздных многоугольников]] - трудоёмкость &amp;lt;math&amp;gt;O(n_1 * n_2)&amp;lt;/math&amp;gt;&lt;br /&gt;
# &amp;lt;div id=&amp;quot;Компьютерная графика&amp;quot;&amp;gt;&#039;&#039;&#039;Компьютерная графика&#039;&#039;&#039;&amp;lt;/div&amp;gt;&lt;br /&gt;
## [[Алгоритмы построения отрезка - алгоритмы для аппроксимации отрезка на дискретной графической поверхности]]&lt;br /&gt;
## [[Алгоритм определения видимых частей трёхмерной сцены]]&lt;br /&gt;
## [[Трассировка лучей - рендеринг реалистичных изображений]]&lt;br /&gt;
## [[Глобальное освещение - рассматривает прямое освещение и отражение от других объектов]]&lt;br /&gt;
# &amp;lt;div id=&amp;quot;Криптографические алгоритмы&amp;quot;&amp;gt;&#039;&#039;&#039;Криптографические алгоритмы&#039;&#039;&#039;&amp;lt;/div&amp;gt;&lt;br /&gt;
# &amp;lt;div id=&amp;quot;Нейронные сети&amp;quot;&amp;gt;&#039;&#039;&#039;Нейронные сети&#039;&#039;&#039;&amp;lt;/div&amp;gt;&lt;br /&gt;
# &amp;lt;div id=&amp;quot;Алгоритмы оптимизации&amp;quot;&amp;gt;&#039;&#039;&#039;Алгоритмы оптимизации&#039;&#039;&#039;&amp;lt;/div&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;div id=&amp;quot;Алгоритмы теории игр&amp;quot;&amp;gt;&#039;&#039;&#039;Алгоритмы теории игр&#039;&#039;&#039;&amp;lt;/div&amp;gt;&lt;br /&gt;
# &amp;lt;div id=&amp;quot;Алгоритмы моделирования квантовых систем&amp;quot;&amp;gt;&#039;&#039;&#039;Алгоритмы моделирования квантовых систем&#039;&#039;&#039;&amp;lt;/div&amp;gt;&lt;br /&gt;
## &#039;&#039;Алгоритмы моделирования квантовых вычислений&#039;&#039;&lt;br /&gt;
### [[Однокубитное преобразование вектора-состояния]]&lt;br /&gt;
### [[Двухкубитное преобразование вектора-состояния]]&lt;br /&gt;
### [[Моделирование квантового преобразования Фурье]]&lt;br /&gt;
# &amp;lt;div id=&amp;quot;Алгоритмы решения уравнений математической физики&amp;quot;&amp;gt;&#039;&#039;&#039;Алгоритмы решения уравнений математической физики&#039;&#039;&#039;&amp;lt;/div&amp;gt;&lt;br /&gt;
## [[Уравнение Пуассона, решение дискретным преобразованием Фурье]]&lt;br /&gt;
# &amp;lt;div id=&amp;quot;Другие алгоритмы&amp;quot;&amp;gt;&#039;&#039;&#039;Другие алгоритмы&#039;&#039;&#039;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[en:Algorithm classification]]&lt;/div&gt;</summary>
		<author><name>Daryin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA_%D0%BA%D1%80%D0%B0%D1%82%D1%87%D0%B0%D0%B9%D1%88%D0%B5%D0%B3%D0%BE_%D0%BF%D1%83%D1%82%D0%B8_%D0%B4%D0%BB%D1%8F_%D0%B2%D1%81%D0%B5%D1%85_%D0%BF%D0%B0%D1%80_%D0%B2%D0%B5%D1%80%D1%88%D0%B8%D0%BD_(APSP)&amp;diff=2004</id>
		<title>Поиск кратчайшего пути для всех пар вершин (APSP)</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA_%D0%BA%D1%80%D0%B0%D1%82%D1%87%D0%B0%D0%B9%D1%88%D0%B5%D0%B3%D0%BE_%D0%BF%D1%83%D1%82%D0%B8_%D0%B4%D0%BB%D1%8F_%D0%B2%D1%81%D0%B5%D1%85_%D0%BF%D0%B0%D1%80_%D0%B2%D0%B5%D1%80%D1%88%D0%B8%D0%BD_(APSP)&amp;diff=2004"/>
		<updated>2015-06-30T19:38:59Z</updated>

		<summary type="html">&lt;p&gt;Daryin: /* Алгоритмы решения задачи */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Постановка задачи ==&lt;br /&gt;
&lt;br /&gt;
Пусть задан граф &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; с весами рёбер &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;e \in E&amp;lt;/math&amp;gt;. Последовательность &amp;lt;math&amp;gt;P(u, v)&amp;lt;/math&amp;gt; рёбер &amp;lt;math&amp;gt;e_1 = (u, w_1)&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;e_2 = (w_1, w_2)&amp;lt;/math&amp;gt;, …, &amp;lt;math&amp;gt;e_k = (w_{k-1}, v)&amp;lt;/math&amp;gt; называется &#039;&#039;путём&#039;&#039;, идущим от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к вершине &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;. Вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; называется &#039;&#039;достижимой&#039;&#039; из вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, если существует хотя бы один путь &amp;lt;math&amp;gt;P(u, v)&amp;lt;/math&amp;gt;. Суммарный вес этого пути равен&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
	f(P(u, v)) = \sum_{i = 1}^k f(e_i).&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
Требуется для каждой пары вершины &amp;lt;math&amp;gt;(u, v)&amp;lt;/math&amp;gt;, для которой вершина &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; достижима из вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;, указать путь &amp;lt;math&amp;gt;P^*(u, v)&amp;lt;/math&amp;gt;, имеющий наименьший возможный суммарный вес:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
	f^*(u, v) = f(P^*(u, v)) = \min f(P(u, v)).&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
Название задачи на английском языке – «All Pairs Shortest Path» (APSP).&lt;br /&gt;
&lt;br /&gt;
== Варианты задачи ==&lt;br /&gt;
&lt;br /&gt;
Если требуется найти кратчайшие пути лишь от одной, а не от всех вершин – см. [[Поиск кратчайшего пути от одной вершины (SSSP)|поиск кратчайшего пути от одной вершины]].&lt;br /&gt;
&lt;br /&gt;
Задача может быть поставлена как для ориентированного, так и для неориентированного графа. Приведённая постановка задачи применима в обоих случаях.&lt;br /&gt;
&lt;br /&gt;
В зависимости от ограничений на возможные значения весов различают следующие случаи.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Единичные веса&#039;&#039;&#039;. В этом случае задача существенно упрощается и может быть решена за квадратичное время [[Поиск в ширину (BFS)|алгоритмом поиска вширь]].&lt;br /&gt;
* &#039;&#039;&#039;Положительные целые веса&#039;&#039;&#039;. Задача также может быть решена за квадратичное время&amp;lt;ref name=thorup&amp;gt;Thorup, Mikkel. “Undirected Single-Source Shortest Paths with Positive Integer Weights in Linear Time.” Journal of the ACM 46, no. 3 (May 1, 1999): 362–94. doi:10.1145/316542.316548.&amp;lt;/ref&amp;gt; (хотя и с большей константой).&lt;br /&gt;
* &#039;&#039;&#039;Неотрицательные веса&#039;&#039;&#039;. Часто встречающееся на практике ограничение, когда вес ребра соответствует времени или стоимости прохождения участка маршрута и не может быть отрицательным.&lt;br /&gt;
* &#039;&#039;&#039;Произвольные веса&#039;&#039;&#039;. Наиболее общая постановка. Граф не должен содержать циклов с отрицательным суммарным весом, иначе кратчайшего пути не существует.&lt;br /&gt;
&lt;br /&gt;
В зависимости от способа организации вычислений возможны следующие варианты:&lt;br /&gt;
&lt;br /&gt;
* статический граф, полное вычисление всех кратчайших путей (классический подход);&lt;br /&gt;
* вычисление онлайн&amp;lt;ref&amp;gt;Djidjev, Hristo N, Grammati E Pantziou, and Christos D Zaroliagis. “On-Line and Dynamic Algorithms for Shortest Path Problems.” In Proceedings of STACS 95, 193–204. Lecture Notes in Computer Science Vol. 900, Berlin, Heidelberg: Springer, 1995. doi:10.1007/3-540-59042-0_73.&amp;lt;/ref&amp;gt;: производятся предварительные вычисления, далее путь между парой вершин вычисляется по запросу (такой подход уместен, если в реальности потребуется вычислить кратчайшие пути только для некоторых пар, но заранее набор этих пар неизвестен);&lt;br /&gt;
* динамический граф&amp;lt;ref&amp;gt;Demetrescu, Camil, and Giuseppe F Italiano. “Fully Dynamic All Pairs Shortest Paths with Real Edge Weights.” Journal of Computer and System Sciences 72, no. 5 (August 2006): 813–37. doi:10.1016/j.jcss.2005.05.005.&amp;lt;/ref&amp;gt;: рёбра и вершины могут добавляться и удаляться, при этом происходит пересчёт кратчайших расстояний (вариант подходит для постоянно меняющихся графов, например, для социальных сетей).&lt;br /&gt;
&lt;br /&gt;
== Описание входных и выходных данных ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: взвешенный граф &amp;lt;math&amp;gt;(V, E, W)&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин &amp;lt;math&amp;gt;v_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; рёбер &amp;lt;math&amp;gt;e_j = (v^{(1)}_{j}, v^{(2)}_{j})&amp;lt;/math&amp;gt; с весами &amp;lt;math&amp;gt;f_j&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(m + n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039; (возможные варианты):&lt;br /&gt;
# для каждой пары вершины &amp;lt;math&amp;gt;(u, v)&amp;lt;/math&amp;gt; исходного графа – последнее ребро &amp;lt;math&amp;gt;e^*_{uv} = (w, v)&amp;lt;/math&amp;gt;, лежащее на кратчайшем пути от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, или соответствующая вершина &amp;lt;math&amp;gt;w&amp;lt;/math&amp;gt;;&lt;br /&gt;
# для каждой пары вершин &amp;lt;math&amp;gt;(u, v)&amp;lt;/math&amp;gt; исходного графа – суммарный вес &amp;lt;math&amp;gt;f^*(u, v)&amp;lt;/math&amp;gt; кратчайшего пути от от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
	&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Преобразование выходных данных и поиск кратчайшего пути ===&lt;br /&gt;
&lt;br /&gt;
Кратчайший путь от &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; может быть восстановлен за время &amp;lt;math&amp;gt;O(\left |P^*(u, v) \right |)&amp;lt;/math&amp;gt;, если, начиная с вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, проходить рёбра &amp;lt;math&amp;gt;e^*_{uw}&amp;lt;/math&amp;gt; в обратном направлении до тех пор, пока не будет посещена вершина &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Рёбра &amp;lt;math&amp;gt;e^*_{uv}&amp;lt;/math&amp;gt; могут быть восстановлены за время &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt; перебором рёбер для каждой вершины:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
    e^*_{uv} \in \{ (w, v) \in E \mid f^*(u, v) = f^*(u, w) + f((v, w)) \}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
Перебор может осуществляться параллельно.&lt;br /&gt;
&lt;br /&gt;
Для поиска кратчайших расстояний &amp;lt;math&amp;gt;f^*(u, v)&amp;lt;/math&amp;gt; по набору рёбер &amp;lt;math&amp;gt;e^*_{uv}&amp;lt;/math&amp;gt;, необходимо для данной вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; построить дерево из рёбер &amp;lt;math&amp;gt;e^*_{uv}&amp;lt;/math&amp;gt; и выполнить на нём [[Поиск в ширину (BFS)|поиск в ширину]] за время &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt; (с возможностью параллелизации), общее время для всех пар вершин – &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Алгоритмы решения задачи ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;[[Алгоритм Джонсона]]&#039;&#039;&#039;&amp;lt;ref&amp;gt;Johnson, Donald B. “Efficient Algorithms for Shortest Paths in Sparse Networks.” Journal of the ACM 24, no. 1 (January 1977): 1–13. doi:10.1145/321992.321993.&amp;lt;/ref&amp;gt; для ориентированных графов с произвольными весами, сложность &amp;lt;math&amp;gt;O(mn+ n^2 \ln n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;[[Алгоритм Флойда-Уоршелла]]&#039;&#039;&#039;&amp;lt;ref&amp;gt;Roy, Bernard. “Transitivité Et Connexité.” Comptes Rendus De l&#039;Académie Des Sciences 249 (1959): 216–218.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Warshall, Stephen. “A Theorem on Boolean Matrices.” Journal of the ACM 9, no. 1 (January 1, 1962): 11–12. doi:10.1145/321105.321107.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Floyd, Robert W. “Algorithm 97: Shortest Path.” Communications of the ACM 5, no. 6 (June 1, 1962): 345. doi:10.1145/367766.368168.&amp;lt;/ref&amp;gt; для ориентированных графов с произвольными весами, сложность &amp;lt;math&amp;gt;O(n^3)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Многократное применение любого алгоритма [[Поиск кратчайшего пути от одной вершины (SSSP)|поиска кратчайшего пути от одной вершины]]:&lt;br /&gt;
** &#039;&#039;&#039;[[Поиск в ширину (BFS)]]&#039;&#039;&#039; для ориентированных невзвешенных графов, сложность &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt;.&lt;br /&gt;
** &#039;&#039;&#039;[[Алгоритм Дейкстры]]&#039;&#039;&#039;&amp;lt;ref&amp;gt;Dijkstra, E W. “A Note on Two Problems in Connexion with Graphs.” Numerische Mathematik 1, no. 1 (December 1959): 269–71. doi:10.1007/BF01386390.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Fredman, Michael L, and Robert Endre Tarjan. “Fibonacci Heaps and Their Uses in Improved Network Optimization Algorithms.” Journal of the ACM 34, no. 3 (July 1, 1987): 596–615. doi:10.1145/28869.28874.&amp;lt;/ref&amp;gt; для ориентированных графов с неотрицательными весами, сложность &amp;lt;math&amp;gt;O(mn + n^2 \ln n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
** &#039;&#039;&#039;[[Алгоритм Беллмана-Форда]]&#039;&#039;&#039;&amp;lt;ref&amp;gt;Bellman, Richard. “On a Routing Problem.” Quarterly of Applied Mathematics 16 (1958): 87–90.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Ford, L R. Network Flow Theory. Rand.org, RAND Corporation, 1958.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Moore, Edward F. “The Shortest Path Through a Maze,” International Symposium on the Theory of Switching, 285–92, 1959.&amp;lt;/ref&amp;gt; для ориентированных графов с произвольными весами, сложность &amp;lt;math&amp;gt;O(mn^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
** &#039;&#039;&#039;[[Алгоритм Δ-шагания]]&#039;&#039;&#039;&amp;lt;ref&amp;gt;Meyer, U, and P Sanders. “Δ-Stepping: a Parallelizable Shortest Path Algorithm.” Journal of Algorithms 49, no. 1 (October 2003): 114–52. doi:10.1016/S0196-6774(03)00076-2.&amp;lt;/ref&amp;gt; для ориентированных графов с неотрицательными весами, средняя сложность на графах со случайными весами &amp;lt;math&amp;gt;O(n(n + m + dL))&amp;lt;/math&amp;gt;, параллельная сложность &amp;lt;math&amp;gt;O((dL + \ln n) n \ln n&amp;lt;/math&amp;gt; со средней работой &amp;lt;math&amp;gt;O(n (n + m + dL \ln n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
** &#039;&#039;&#039;Алгоритм Торупа&#039;&#039;&#039;&amp;lt;ref name=thorup /&amp;gt; представляет собой теоретическое доказательство возможности решения задачи для неориентированных графов с положительными целыми весами за линейное время &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt;.&lt;br /&gt;
** В ориентированном ациклическом графе с произвольными весами алгоритм основан на топологической сортировке графа, сложность &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Обозначения: &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; – число рёбер, &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; – число вершин, &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; – максимальная степень вершины, &amp;lt;math&amp;gt;L&amp;lt;/math&amp;gt; – максимальный суммарный вес кратчайшего пути.&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма ==&lt;br /&gt;
&lt;br /&gt;
Задача может быть решена путём применения любого алгоритма [[Поиск кратчайшего пути от одной вершины (SSSP)|поиска кратчайшего пути от одной вершины]] для всех вершин графа, при этом вычисления для различных вершин независимы и могут выполняться параллельно.&lt;br /&gt;
&lt;br /&gt;
В связи с квадратичной сложностью выходных данных и более чем квадратичной сложностью вычислений, имеет смысл проводить предварительную обработку данных и использовать различные декомпозиции графа, сводя задачу к параллельному нахождению кратчайших путей на подграфах меньших размеров&amp;lt;ref&amp;gt;Banerjee, Dip Sankar, Ashutosh Kumar, Meher Chaitanya, Shashank Sharma, and Kishore Kothapalli. “Work Efficient Parallel Algorithms for Large Graph Exploration on Emerging Heterogeneous Architectures.” Journal of Parallel and Distributed Computing, December 2014. doi:10.1016/j.jpdc.2014.11.006.&amp;lt;/ref&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;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Daryin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%94%D0%B5%D0%B9%D0%BA%D1%81%D1%82%D1%80%D1%8B&amp;diff=2001</id>
		<title>Алгоритм Дейкстры</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%94%D0%B5%D0%B9%D0%BA%D1%81%D1%82%D1%80%D1%8B&amp;diff=2001"/>
		<updated>2015-06-26T12:15:35Z</updated>

		<summary type="html">&lt;p&gt;Daryin: /* Описание схемы реализации последовательного алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Свойства и структура алгоритмов ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм Дейкстры&#039;&#039;&#039;&amp;lt;ref&amp;gt;Dijkstra, E W. “A Note on Two Problems in Connexion with Graphs.” Numerische Mathematik 1, no. 1 (December 1959): 269–71. doi:10.1007/BF01386390.&amp;lt;/ref&amp;gt; предназначен для решения [[Поиск кратчайшего пути от одной вершины (SSSP)|задачи поиска кратчайшего пути на графе]]. Для заданного ориентированного взвешенного графа с неотрицательными весами алгоритм находит кратчайшие расстояния от выделенной вершины-источника до всех остальных вершин графа.&lt;br /&gt;
Алгоритм Дейкстры (с использованием фибоначчиевой кучи&amp;lt;ref name=FibHeap&amp;gt;Fredman, Michael L, and Robert Endre Tarjan. “Fibonacci Heaps and Their Uses in Improved Network Optimization Algorithms.” Journal of the ACM 34, no. 3 (July 1987): 596–615. doi:10.1145/28869.28874.&amp;lt;/ref&amp;gt;) выполняется за время &amp;lt;math&amp;gt;O(m + n \ln n)&amp;lt;/math&amp;gt; и является асимптотически быстрейшим из известных последовательных алгоритмов для данного класса задач.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание ===&lt;br /&gt;
&lt;br /&gt;
Пусть задан граф &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; с весами рёбер &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt; и выделенной вершиной-источником &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;. Обозначим через &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt; кратчайшее расстояние от источника &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; до вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Пусть уже вычислены все расстояния, не превосходящие некоторого числа &amp;lt;math&amp;gt;r&amp;lt;/math&amp;gt;, то есть расстояния до вершин из множества &amp;lt;math&amp;gt;V_r = \{ v \in V \mid d(v) \le r \}&amp;lt;/math&amp;gt;. Пусть&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
        (v, w) \in \arg\min \{ d(v) + f(e) \mid  v \in V, e = (v, w) \in E \}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
Тогда &amp;lt;math&amp;gt;d(w) = d(v) + f(e)&amp;lt;/math&amp;gt;, и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; лежит на кратчайшем пути от &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;w&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Величины &amp;lt;math&amp;gt;d^+(w) = d(v) + f(e)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;v \in V_r&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;e = (v, w) \in E&amp;lt;/math&amp;gt;, называются &#039;&#039;предполагаемыми расстояниями&#039;&#039; и являются оценкой сверху для настоящих расстояний: &amp;lt;math&amp;gt;d(w) \le d^+(w)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Алгоритм Дейкстры на каждом шаге находит вершину с наименьшим предполагаемым расстоянием, помечает её как посещённую и обновляет предполагаемые расстояния для всех концов рёбер, исходящих из неё.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основные вычисления связаны с операциями над очередью с приоритетом:&lt;br /&gt;
* извлечение минимального элемента (&amp;lt;code&amp;gt;delete_min&amp;lt;/code&amp;gt;);&lt;br /&gt;
* уменьшение приоритета элемента (&amp;lt;code&amp;gt;decrease_key&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
Псевдокод алгоритма:&lt;br /&gt;
&lt;br /&gt;
 &#039;&#039;&#039;Входные данные&#039;&#039;&#039;:&lt;br /&gt;
   граф с вершинами &#039;&#039;V&#039;&#039;, рёбрами &#039;&#039;E&#039;&#039; с весами &#039;&#039;f&#039;&#039;(&#039;&#039;e&#039;&#039;);&lt;br /&gt;
   вершина-источник &#039;&#039;u&#039;&#039;.&lt;br /&gt;
 &#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: расстояния &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) до каждой вершины &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039; от вершины &#039;&#039;u&#039;&#039;.&lt;br /&gt;
 &lt;br /&gt;
 &#039;&#039;Q&#039;&#039; := &#039;&#039;&#039;new&#039;&#039;&#039; priority queue&lt;br /&gt;
 &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039;:&lt;br /&gt;
     &#039;&#039;&#039;if&#039;&#039;&#039; &#039;&#039;v&#039;&#039; = &#039;&#039;u&#039;&#039; &#039;&#039;&#039;then&#039;&#039;&#039; &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) := 0 &#039;&#039;&#039;else&#039;&#039;&#039; &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) := ∞ &lt;br /&gt;
     &#039;&#039;Q&#039;&#039;.insert(&#039;&#039;v&#039;&#039;, &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;))&lt;br /&gt;
 &lt;br /&gt;
 &#039;&#039;&#039;while&#039;&#039;&#039; &#039;&#039;Q&#039;&#039; ≠ ∅:&lt;br /&gt;
     &#039;&#039;v&#039;&#039; := &#039;&#039;Q&#039;&#039;.delete_min()&lt;br /&gt;
     &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;e&#039;&#039; = (&#039;&#039;v&#039;&#039;, &#039;&#039;w&#039;&#039;) ∈ &#039;&#039;E&#039;&#039;:&lt;br /&gt;
         &#039;&#039;&#039;if&#039;&#039;&#039; &#039;&#039;d&#039;&#039;(&#039;&#039;w&#039;&#039;) &amp;gt; &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) + &#039;&#039;f&#039;&#039;(&#039;&#039;e&#039;&#039;):&lt;br /&gt;
             &#039;&#039;d&#039;&#039;(&#039;&#039;w&#039;&#039;) := &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) + &#039;&#039;f&#039;&#039;(&#039;&#039;e&#039;&#039;)&lt;br /&gt;
             &#039;&#039;Q&#039;&#039;.decrease_key(&#039;&#039;w&#039;&#039;, &#039;&#039;d&#039;&#039;(&#039;&#039;w&#039;&#039;))&lt;br /&gt;
&lt;br /&gt;
=== Описание схемы реализации последовательного алгоритма ===&lt;br /&gt;
Конкретная реализация алгоритма Дейкстры определяется выбором используемого алгоритма очереди с приоритетом. В простейшем случае это может быть массив или список, поиск минимума в котором требует просмотра всех вершин. Более эффективным является использование кучи; наилучшую известную оценку сложности имеет вариант с использованием фибоначчиевой кучи&amp;lt;ref name=FibHeap /&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Возможен вариант реализации, когда вершины добавляются в очередь не на этапе инициализации, а в момент первого посещения.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Последовательная сложность алгоритма равна &amp;lt;math&amp;gt;O(C_1 m + C_2n)&amp;lt;/math&amp;gt;, где&lt;br /&gt;
* &amp;lt;math&amp;gt;C_1&amp;lt;/math&amp;gt; – количество операций уменьшения расстояния до вершины;&lt;br /&gt;
* &amp;lt;math&amp;gt;C_2&amp;lt;/math&amp;gt; – количество операций вычисления минимума.&lt;br /&gt;
&lt;br /&gt;
Оригинальный алгоритм Дейкстры использовал в качестве внутренней структуры данных списки, для которых &amp;lt;math&amp;gt;C_1 = O(1)&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;C_2 = O(n)&amp;lt;/math&amp;gt;, так что общая сложность составляла &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
При использовании фибоначчиевой кучи&amp;lt;ref name=FibHeap /&amp;gt; время вычисления минимума сокращается до &amp;lt;math&amp;gt;C_2 = O(\ln n)&amp;lt;/math&amp;gt;, так что общая сложность равна &amp;lt;math&amp;gt;O(m + n \ln n)&amp;lt;/math&amp;gt;, что является асимптотически наилучшим известным результатом для данного класса задач.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
=== Описание ресурса параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм Дейкстры допускает эффективную параллелизацию&amp;lt;ref&amp;gt;Crauser, A, K Mehlhorn, U Meyer, and P Sanders. “A Parallelization of Dijkstra&#039;s Shortest Path Algorithm,” Proceedings of Mathematical Foundations of Computer Science / Lecture Notes in Computer Science, 1450:722–31, Berlin, Heidelberg: Springer, 1998. doi:10.1007/BFb0055823.&amp;lt;/ref&amp;gt;, среднее время работы &amp;lt;math&amp;gt;O(n^{1/3}\ln n)&amp;lt;/math&amp;gt; с объёмом вычислений &amp;lt;math&amp;gt;O(n \ln n + m)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Алгоритм Δ-шагания]] может рассматриваться как параллельная версия алгоритма Дейкстры.&lt;br /&gt;
&lt;br /&gt;
=== Описание входных и выходных данных ===&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: взвешенный граф &amp;lt;math&amp;gt;(V, E, W)&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин &amp;lt;math&amp;gt;v_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; рёбер &amp;lt;math&amp;gt;e_j = (v^{(1)}_{j}, v^{(2)}_{j})&amp;lt;/math&amp;gt; с весами &amp;lt;math&amp;gt;f_j&amp;lt;/math&amp;gt;), вершина-источник &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(m + n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039; (возможные варианты):&lt;br /&gt;
# для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного графа – последнее ребро &amp;lt;math&amp;gt;e^*_v = (w, v)&amp;lt;/math&amp;gt;, лежащее на кратчайшем пути от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, или соответствующая вершина &amp;lt;math&amp;gt;w&amp;lt;/math&amp;gt;;&lt;br /&gt;
# для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного графа – суммарный вес &amp;lt;math&amp;gt;f^*(v)&amp;lt;/math&amp;gt; кратчайшего пути от от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
	&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(n)&amp;lt;/math&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;
* C++: [http://www.boost.org/libs/graph/doc/ Boost Graph Library] (функции &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph/doc/dijkstra_shortest_paths.html dijkstra_shortest_paths]&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph/doc/dijkstra_shortest_paths_no_color_map.html dijkstra_shortest_paths_no_color_map]&amp;lt;/code&amp;gt;), сложность &amp;lt;math&amp;gt;O(m + n \ln n)&amp;lt;/math&amp;gt;. &lt;br /&gt;
* C++, MPI: [http://www.boost.org/libs/graph_parallel/doc/html/index.html Parallel Boost Graph Library]:&lt;br /&gt;
** функция &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dijkstra_shortest_paths.html#eager-dijkstra-s-algorithm eager_dijkstra_shortest_paths]&amp;lt;/code&amp;gt; – непосредственная реализация алгоритма Дейкстры;&lt;br /&gt;
** функция &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dijkstra_shortest_paths.html#crauser-et-al-s-algorithm crauser_et_al_shortest_paths]&amp;lt;/code&amp;gt; – реализация алгоритма Дейкстры в виде алгоритма из статьи Краузера и др.&amp;lt;ref&amp;gt;Crauser, A, K Mehlhorn, U Meyer, and P Sanders. “A Parallelization of Dijkstra&#039;s Shortest Path Algorithm,” Proceedings of Mathematical Foundations of Computer Science / Lecture Notes in Computer Science, 1450:722–31, Berlin, Heidelberg: Springer, 1998. doi:10.1007/BFb0055823.&amp;lt;/ref&amp;gt;&lt;br /&gt;
* Python: [https://networkx.github.io NetworkX] (функция &amp;lt;code&amp;gt;[http://networkx.github.io/documentation/networkx-1.9.1/reference/generated/networkx.algorithms.shortest_paths.weighted.single_source_dijkstra.html single_source_dijkstra]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Python/C++: [https://networkit.iti.kit.edu NetworKit] (класс &amp;lt;code&amp;gt;[https://networkit.iti.kit.edu/data/uploads/docs/NetworKit-Doc/python/html/graph.html#networkit.graph.Dijkstra networkit.graph.Dijkstra]&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Daryin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%94%D0%B5%D0%B9%D0%BA%D1%81%D1%82%D1%80%D1%8B&amp;diff=2000</id>
		<title>Алгоритм Дейкстры</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%94%D0%B5%D0%B9%D0%BA%D1%81%D1%82%D1%80%D1%8B&amp;diff=2000"/>
		<updated>2015-06-26T11:58:57Z</updated>

		<summary type="html">&lt;p&gt;Daryin: /* Макроструктура алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Свойства и структура алгоритмов ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм Дейкстры&#039;&#039;&#039;&amp;lt;ref&amp;gt;Dijkstra, E W. “A Note on Two Problems in Connexion with Graphs.” Numerische Mathematik 1, no. 1 (December 1959): 269–71. doi:10.1007/BF01386390.&amp;lt;/ref&amp;gt; предназначен для решения [[Поиск кратчайшего пути от одной вершины (SSSP)|задачи поиска кратчайшего пути на графе]]. Для заданного ориентированного взвешенного графа с неотрицательными весами алгоритм находит кратчайшие расстояния от выделенной вершины-источника до всех остальных вершин графа.&lt;br /&gt;
Алгоритм Дейкстры (с использованием фибоначчиевой кучи&amp;lt;ref name=FibHeap&amp;gt;Fredman, Michael L, and Robert Endre Tarjan. “Fibonacci Heaps and Their Uses in Improved Network Optimization Algorithms.” Journal of the ACM 34, no. 3 (July 1987): 596–615. doi:10.1145/28869.28874.&amp;lt;/ref&amp;gt;) выполняется за время &amp;lt;math&amp;gt;O(m + n \ln n)&amp;lt;/math&amp;gt; и является асимптотически быстрейшим из известных последовательных алгоритмов для данного класса задач.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание ===&lt;br /&gt;
&lt;br /&gt;
Пусть задан граф &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; с весами рёбер &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt; и выделенной вершиной-источником &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;. Обозначим через &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt; кратчайшее расстояние от источника &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; до вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Пусть уже вычислены все расстояния, не превосходящие некоторого числа &amp;lt;math&amp;gt;r&amp;lt;/math&amp;gt;, то есть расстояния до вершин из множества &amp;lt;math&amp;gt;V_r = \{ v \in V \mid d(v) \le r \}&amp;lt;/math&amp;gt;. Пусть&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
        (v, w) \in \arg\min \{ d(v) + f(e) \mid  v \in V, e = (v, w) \in E \}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
Тогда &amp;lt;math&amp;gt;d(w) = d(v) + f(e)&amp;lt;/math&amp;gt;, и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; лежит на кратчайшем пути от &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;w&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Величины &amp;lt;math&amp;gt;d^+(w) = d(v) + f(e)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;v \in V_r&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;e = (v, w) \in E&amp;lt;/math&amp;gt;, называются &#039;&#039;предполагаемыми расстояниями&#039;&#039; и являются оценкой сверху для настоящих расстояний: &amp;lt;math&amp;gt;d(w) \le d^+(w)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Алгоритм Дейкстры на каждом шаге находит вершину с наименьшим предполагаемым расстоянием, помечает её как посещённую и обновляет предполагаемые расстояния для всех концов рёбер, исходящих из неё.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основные вычисления связаны с операциями над очередью с приоритетом:&lt;br /&gt;
* извлечение минимального элемента (&amp;lt;code&amp;gt;delete_min&amp;lt;/code&amp;gt;);&lt;br /&gt;
* уменьшение приоритета элемента (&amp;lt;code&amp;gt;decrease_key&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
Псевдокод алгоритма:&lt;br /&gt;
&lt;br /&gt;
 &#039;&#039;&#039;Входные данные&#039;&#039;&#039;:&lt;br /&gt;
   граф с вершинами &#039;&#039;V&#039;&#039;, рёбрами &#039;&#039;E&#039;&#039; с весами &#039;&#039;f&#039;&#039;(&#039;&#039;e&#039;&#039;);&lt;br /&gt;
   вершина-источник &#039;&#039;u&#039;&#039;.&lt;br /&gt;
 &#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: расстояния &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) до каждой вершины &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039; от вершины &#039;&#039;u&#039;&#039;.&lt;br /&gt;
 &lt;br /&gt;
 &#039;&#039;Q&#039;&#039; := &#039;&#039;&#039;new&#039;&#039;&#039; priority queue&lt;br /&gt;
 &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039;:&lt;br /&gt;
     &#039;&#039;&#039;if&#039;&#039;&#039; &#039;&#039;v&#039;&#039; = &#039;&#039;u&#039;&#039; &#039;&#039;&#039;then&#039;&#039;&#039; &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) := 0 &#039;&#039;&#039;else&#039;&#039;&#039; &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) := ∞ &lt;br /&gt;
     &#039;&#039;Q&#039;&#039;.insert(&#039;&#039;v&#039;&#039;, &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;))&lt;br /&gt;
 &lt;br /&gt;
 &#039;&#039;&#039;while&#039;&#039;&#039; &#039;&#039;Q&#039;&#039; ≠ ∅:&lt;br /&gt;
     &#039;&#039;v&#039;&#039; := &#039;&#039;Q&#039;&#039;.delete_min()&lt;br /&gt;
     &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;e&#039;&#039; = (&#039;&#039;v&#039;&#039;, &#039;&#039;w&#039;&#039;) ∈ &#039;&#039;E&#039;&#039;:&lt;br /&gt;
         &#039;&#039;&#039;if&#039;&#039;&#039; &#039;&#039;d&#039;&#039;(&#039;&#039;w&#039;&#039;) &amp;gt; &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) + &#039;&#039;f&#039;&#039;(&#039;&#039;e&#039;&#039;):&lt;br /&gt;
             &#039;&#039;d&#039;&#039;(&#039;&#039;w&#039;&#039;) := &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) + &#039;&#039;f&#039;&#039;(&#039;&#039;e&#039;&#039;)&lt;br /&gt;
             &#039;&#039;Q&#039;&#039;.decrease_key(&#039;&#039;w&#039;&#039;, &#039;&#039;d&#039;&#039;(&#039;&#039;w&#039;&#039;))&lt;br /&gt;
&lt;br /&gt;
=== Описание схемы реализации последовательного алгоритма ===&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Последовательная сложность алгоритма равна &amp;lt;math&amp;gt;O(C_1 m + C_2n)&amp;lt;/math&amp;gt;, где&lt;br /&gt;
* &amp;lt;math&amp;gt;C_1&amp;lt;/math&amp;gt; – количество операций уменьшения расстояния до вершины;&lt;br /&gt;
* &amp;lt;math&amp;gt;C_2&amp;lt;/math&amp;gt; – количество операций вычисления минимума.&lt;br /&gt;
&lt;br /&gt;
Оригинальный алгоритм Дейкстры использовал в качестве внутренней структуры данных списки, для которых &amp;lt;math&amp;gt;C_1 = O(1)&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;C_2 = O(n)&amp;lt;/math&amp;gt;, так что общая сложность составляла &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
При использовании фибоначчиевой кучи&amp;lt;ref name=FibHeap /&amp;gt; время вычисления минимума сокращается до &amp;lt;math&amp;gt;C_2 = O(\ln n)&amp;lt;/math&amp;gt;, так что общая сложность равна &amp;lt;math&amp;gt;O(m + n \ln n)&amp;lt;/math&amp;gt;, что является асимптотически наилучшим известным результатом для данного класса задач.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
=== Описание ресурса параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм Дейкстры допускает эффективную параллелизацию&amp;lt;ref&amp;gt;Crauser, A, K Mehlhorn, U Meyer, and P Sanders. “A Parallelization of Dijkstra&#039;s Shortest Path Algorithm,” Proceedings of Mathematical Foundations of Computer Science / Lecture Notes in Computer Science, 1450:722–31, Berlin, Heidelberg: Springer, 1998. doi:10.1007/BFb0055823.&amp;lt;/ref&amp;gt;, среднее время работы &amp;lt;math&amp;gt;O(n^{1/3}\ln n)&amp;lt;/math&amp;gt; с объёмом вычислений &amp;lt;math&amp;gt;O(n \ln n + m)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Алгоритм Δ-шагания]] может рассматриваться как параллельная версия алгоритма Дейкстры.&lt;br /&gt;
&lt;br /&gt;
=== Описание входных и выходных данных ===&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: взвешенный граф &amp;lt;math&amp;gt;(V, E, W)&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин &amp;lt;math&amp;gt;v_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; рёбер &amp;lt;math&amp;gt;e_j = (v^{(1)}_{j}, v^{(2)}_{j})&amp;lt;/math&amp;gt; с весами &amp;lt;math&amp;gt;f_j&amp;lt;/math&amp;gt;), вершина-источник &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(m + n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039; (возможные варианты):&lt;br /&gt;
# для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного графа – последнее ребро &amp;lt;math&amp;gt;e^*_v = (w, v)&amp;lt;/math&amp;gt;, лежащее на кратчайшем пути от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, или соответствующая вершина &amp;lt;math&amp;gt;w&amp;lt;/math&amp;gt;;&lt;br /&gt;
# для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного графа – суммарный вес &amp;lt;math&amp;gt;f^*(v)&amp;lt;/math&amp;gt; кратчайшего пути от от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
	&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(n)&amp;lt;/math&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;
* C++: [http://www.boost.org/libs/graph/doc/ Boost Graph Library] (функции &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph/doc/dijkstra_shortest_paths.html dijkstra_shortest_paths]&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph/doc/dijkstra_shortest_paths_no_color_map.html dijkstra_shortest_paths_no_color_map]&amp;lt;/code&amp;gt;), сложность &amp;lt;math&amp;gt;O(m + n \ln n)&amp;lt;/math&amp;gt;. &lt;br /&gt;
* C++, MPI: [http://www.boost.org/libs/graph_parallel/doc/html/index.html Parallel Boost Graph Library]:&lt;br /&gt;
** функция &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dijkstra_shortest_paths.html#eager-dijkstra-s-algorithm eager_dijkstra_shortest_paths]&amp;lt;/code&amp;gt; – непосредственная реализация алгоритма Дейкстры;&lt;br /&gt;
** функция &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dijkstra_shortest_paths.html#crauser-et-al-s-algorithm crauser_et_al_shortest_paths]&amp;lt;/code&amp;gt; – реализация алгоритма Дейкстры в виде алгоритма из статьи Краузера и др.&amp;lt;ref&amp;gt;Crauser, A, K Mehlhorn, U Meyer, and P Sanders. “A Parallelization of Dijkstra&#039;s Shortest Path Algorithm,” Proceedings of Mathematical Foundations of Computer Science / Lecture Notes in Computer Science, 1450:722–31, Berlin, Heidelberg: Springer, 1998. doi:10.1007/BFb0055823.&amp;lt;/ref&amp;gt;&lt;br /&gt;
* Python: [https://networkx.github.io NetworkX] (функция &amp;lt;code&amp;gt;[http://networkx.github.io/documentation/networkx-1.9.1/reference/generated/networkx.algorithms.shortest_paths.weighted.single_source_dijkstra.html single_source_dijkstra]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Python/C++: [https://networkit.iti.kit.edu NetworKit] (класс &amp;lt;code&amp;gt;[https://networkit.iti.kit.edu/data/uploads/docs/NetworKit-Doc/python/html/graph.html#networkit.graph.Dijkstra networkit.graph.Dijkstra]&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Daryin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%94%D0%B5%D0%B9%D0%BA%D1%81%D1%82%D1%80%D1%8B&amp;diff=1999</id>
		<title>Алгоритм Дейкстры</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%94%D0%B5%D0%B9%D0%BA%D1%81%D1%82%D1%80%D1%8B&amp;diff=1999"/>
		<updated>2015-06-26T11:57:45Z</updated>

		<summary type="html">&lt;p&gt;Daryin: /* Макроструктура алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Свойства и структура алгоритмов ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм Дейкстры&#039;&#039;&#039;&amp;lt;ref&amp;gt;Dijkstra, E W. “A Note on Two Problems in Connexion with Graphs.” Numerische Mathematik 1, no. 1 (December 1959): 269–71. doi:10.1007/BF01386390.&amp;lt;/ref&amp;gt; предназначен для решения [[Поиск кратчайшего пути от одной вершины (SSSP)|задачи поиска кратчайшего пути на графе]]. Для заданного ориентированного взвешенного графа с неотрицательными весами алгоритм находит кратчайшие расстояния от выделенной вершины-источника до всех остальных вершин графа.&lt;br /&gt;
Алгоритм Дейкстры (с использованием фибоначчиевой кучи&amp;lt;ref name=FibHeap&amp;gt;Fredman, Michael L, and Robert Endre Tarjan. “Fibonacci Heaps and Their Uses in Improved Network Optimization Algorithms.” Journal of the ACM 34, no. 3 (July 1987): 596–615. doi:10.1145/28869.28874.&amp;lt;/ref&amp;gt;) выполняется за время &amp;lt;math&amp;gt;O(m + n \ln n)&amp;lt;/math&amp;gt; и является асимптотически быстрейшим из известных последовательных алгоритмов для данного класса задач.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание ===&lt;br /&gt;
&lt;br /&gt;
Пусть задан граф &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; с весами рёбер &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt; и выделенной вершиной-источником &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;. Обозначим через &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt; кратчайшее расстояние от источника &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; до вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Пусть уже вычислены все расстояния, не превосходящие некоторого числа &amp;lt;math&amp;gt;r&amp;lt;/math&amp;gt;, то есть расстояния до вершин из множества &amp;lt;math&amp;gt;V_r = \{ v \in V \mid d(v) \le r \}&amp;lt;/math&amp;gt;. Пусть&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
        (v, w) \in \arg\min \{ d(v) + f(e) \mid  v \in V, e = (v, w) \in E \}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
Тогда &amp;lt;math&amp;gt;d(w) = d(v) + f(e)&amp;lt;/math&amp;gt;, и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; лежит на кратчайшем пути от &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;w&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Величины &amp;lt;math&amp;gt;d^+(w) = d(v) + f(e)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;v \in V_r&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;e = (v, w) \in E&amp;lt;/math&amp;gt;, называются &#039;&#039;предполагаемыми расстояниями&#039;&#039; и являются оценкой сверху для настоящих расстояний: &amp;lt;math&amp;gt;d(w) \le d^+(w)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Алгоритм Дейкстры на каждом шаге находит вершину с наименьшим предполагаемым расстоянием, помечает её как посещённую и обновляет предполагаемые расстояния для всех концов рёбер, исходящих из неё.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основные вычисления связаны с операциями над очередью с приоритетом:&lt;br /&gt;
* извлечение минимального элемента (&amp;lt;code&amp;gt;delete_min&amp;lt;/code&amp;gt;);&lt;br /&gt;
* уменьшение приоритета элемента (&amp;lt;code&amp;gt;decrease_key&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
Псевдокод алгоритма:&lt;br /&gt;
&lt;br /&gt;
 &#039;&#039;&#039;Входные данные&#039;&#039;&#039;:&lt;br /&gt;
   граф с вершинами &#039;&#039;V&#039;&#039;, рёбрами &#039;&#039;E&#039;&#039; с весами &#039;&#039;f&#039;&#039;(&#039;&#039;e&#039;&#039;);&lt;br /&gt;
   вершина-источник &#039;&#039;u&#039;&#039;.&lt;br /&gt;
 &#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: расстояния &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) до каждой вершины &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039; от вершины &#039;&#039;u&#039;&#039;.&lt;br /&gt;
 &lt;br /&gt;
 &#039;&#039;Q&#039;&#039; := &#039;&#039;&#039;new&#039;&#039;&#039; priority queue&lt;br /&gt;
 &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039; &#039;&#039;&#039;do&#039;&#039;&#039;&lt;br /&gt;
     &#039;&#039;&#039;if&#039;&#039;&#039; &#039;&#039;v&#039;&#039; = &#039;&#039;u&#039;&#039; &#039;&#039;&#039;then&#039;&#039;&#039; &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) := 0 &#039;&#039;&#039;else&#039;&#039;&#039; &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) := ∞ &lt;br /&gt;
     &#039;&#039;Q&#039;&#039;.insert(&#039;&#039;v&#039;&#039;, &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;))&lt;br /&gt;
 &lt;br /&gt;
 &#039;&#039;&#039;while&#039;&#039;&#039; &#039;&#039;Q&#039;&#039; ≠ ∅:&lt;br /&gt;
     &#039;&#039;v&#039;&#039; := &#039;&#039;Q&#039;&#039;.delete_min()&lt;br /&gt;
     &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;e&#039;&#039; = (&#039;&#039;v&#039;&#039;, &#039;&#039;w&#039;&#039;) ∈ &#039;&#039;E&#039;&#039;:&lt;br /&gt;
         &#039;&#039;&#039;if&#039;&#039;&#039; &#039;&#039;d&#039;&#039;(&#039;&#039;w&#039;&#039;) &amp;gt; &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) + &#039;&#039;f&#039;&#039;(&#039;&#039;e&#039;&#039;):&lt;br /&gt;
             &#039;&#039;d&#039;&#039;(&#039;&#039;w&#039;&#039;) := &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) + &#039;&#039;f&#039;&#039;(&#039;&#039;e&#039;&#039;)&lt;br /&gt;
             &#039;&#039;Q&#039;&#039;.decrease_key(&#039;&#039;w&#039;&#039;, &#039;&#039;d&#039;&#039;(&#039;&#039;w&#039;&#039;))&lt;br /&gt;
&lt;br /&gt;
=== Описание схемы реализации последовательного алгоритма ===&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Последовательная сложность алгоритма равна &amp;lt;math&amp;gt;O(C_1 m + C_2n)&amp;lt;/math&amp;gt;, где&lt;br /&gt;
* &amp;lt;math&amp;gt;C_1&amp;lt;/math&amp;gt; – количество операций уменьшения расстояния до вершины;&lt;br /&gt;
* &amp;lt;math&amp;gt;C_2&amp;lt;/math&amp;gt; – количество операций вычисления минимума.&lt;br /&gt;
&lt;br /&gt;
Оригинальный алгоритм Дейкстры использовал в качестве внутренней структуры данных списки, для которых &amp;lt;math&amp;gt;C_1 = O(1)&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;C_2 = O(n)&amp;lt;/math&amp;gt;, так что общая сложность составляла &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
При использовании фибоначчиевой кучи&amp;lt;ref name=FibHeap /&amp;gt; время вычисления минимума сокращается до &amp;lt;math&amp;gt;C_2 = O(\ln n)&amp;lt;/math&amp;gt;, так что общая сложность равна &amp;lt;math&amp;gt;O(m + n \ln n)&amp;lt;/math&amp;gt;, что является асимптотически наилучшим известным результатом для данного класса задач.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
=== Описание ресурса параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм Дейкстры допускает эффективную параллелизацию&amp;lt;ref&amp;gt;Crauser, A, K Mehlhorn, U Meyer, and P Sanders. “A Parallelization of Dijkstra&#039;s Shortest Path Algorithm,” Proceedings of Mathematical Foundations of Computer Science / Lecture Notes in Computer Science, 1450:722–31, Berlin, Heidelberg: Springer, 1998. doi:10.1007/BFb0055823.&amp;lt;/ref&amp;gt;, среднее время работы &amp;lt;math&amp;gt;O(n^{1/3}\ln n)&amp;lt;/math&amp;gt; с объёмом вычислений &amp;lt;math&amp;gt;O(n \ln n + m)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Алгоритм Δ-шагания]] может рассматриваться как параллельная версия алгоритма Дейкстры.&lt;br /&gt;
&lt;br /&gt;
=== Описание входных и выходных данных ===&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: взвешенный граф &amp;lt;math&amp;gt;(V, E, W)&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин &amp;lt;math&amp;gt;v_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; рёбер &amp;lt;math&amp;gt;e_j = (v^{(1)}_{j}, v^{(2)}_{j})&amp;lt;/math&amp;gt; с весами &amp;lt;math&amp;gt;f_j&amp;lt;/math&amp;gt;), вершина-источник &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(m + n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039; (возможные варианты):&lt;br /&gt;
# для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного графа – последнее ребро &amp;lt;math&amp;gt;e^*_v = (w, v)&amp;lt;/math&amp;gt;, лежащее на кратчайшем пути от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, или соответствующая вершина &amp;lt;math&amp;gt;w&amp;lt;/math&amp;gt;;&lt;br /&gt;
# для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного графа – суммарный вес &amp;lt;math&amp;gt;f^*(v)&amp;lt;/math&amp;gt; кратчайшего пути от от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
	&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(n)&amp;lt;/math&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;
* C++: [http://www.boost.org/libs/graph/doc/ Boost Graph Library] (функции &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph/doc/dijkstra_shortest_paths.html dijkstra_shortest_paths]&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph/doc/dijkstra_shortest_paths_no_color_map.html dijkstra_shortest_paths_no_color_map]&amp;lt;/code&amp;gt;), сложность &amp;lt;math&amp;gt;O(m + n \ln n)&amp;lt;/math&amp;gt;. &lt;br /&gt;
* C++, MPI: [http://www.boost.org/libs/graph_parallel/doc/html/index.html Parallel Boost Graph Library]:&lt;br /&gt;
** функция &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dijkstra_shortest_paths.html#eager-dijkstra-s-algorithm eager_dijkstra_shortest_paths]&amp;lt;/code&amp;gt; – непосредственная реализация алгоритма Дейкстры;&lt;br /&gt;
** функция &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dijkstra_shortest_paths.html#crauser-et-al-s-algorithm crauser_et_al_shortest_paths]&amp;lt;/code&amp;gt; – реализация алгоритма Дейкстры в виде алгоритма из статьи Краузера и др.&amp;lt;ref&amp;gt;Crauser, A, K Mehlhorn, U Meyer, and P Sanders. “A Parallelization of Dijkstra&#039;s Shortest Path Algorithm,” Proceedings of Mathematical Foundations of Computer Science / Lecture Notes in Computer Science, 1450:722–31, Berlin, Heidelberg: Springer, 1998. doi:10.1007/BFb0055823.&amp;lt;/ref&amp;gt;&lt;br /&gt;
* Python: [https://networkx.github.io NetworkX] (функция &amp;lt;code&amp;gt;[http://networkx.github.io/documentation/networkx-1.9.1/reference/generated/networkx.algorithms.shortest_paths.weighted.single_source_dijkstra.html single_source_dijkstra]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Python/C++: [https://networkit.iti.kit.edu NetworKit] (класс &amp;lt;code&amp;gt;[https://networkit.iti.kit.edu/data/uploads/docs/NetworKit-Doc/python/html/graph.html#networkit.graph.Dijkstra networkit.graph.Dijkstra]&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Daryin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%94%D0%B5%D0%B9%D0%BA%D1%81%D1%82%D1%80%D1%8B&amp;diff=1998</id>
		<title>Алгоритм Дейкстры</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%94%D0%B5%D0%B9%D0%BA%D1%81%D1%82%D1%80%D1%8B&amp;diff=1998"/>
		<updated>2015-06-26T11:54:35Z</updated>

		<summary type="html">&lt;p&gt;Daryin: /* Вычислительное ядро алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Свойства и структура алгоритмов ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм Дейкстры&#039;&#039;&#039;&amp;lt;ref&amp;gt;Dijkstra, E W. “A Note on Two Problems in Connexion with Graphs.” Numerische Mathematik 1, no. 1 (December 1959): 269–71. doi:10.1007/BF01386390.&amp;lt;/ref&amp;gt; предназначен для решения [[Поиск кратчайшего пути от одной вершины (SSSP)|задачи поиска кратчайшего пути на графе]]. Для заданного ориентированного взвешенного графа с неотрицательными весами алгоритм находит кратчайшие расстояния от выделенной вершины-источника до всех остальных вершин графа.&lt;br /&gt;
Алгоритм Дейкстры (с использованием фибоначчиевой кучи&amp;lt;ref name=FibHeap&amp;gt;Fredman, Michael L, and Robert Endre Tarjan. “Fibonacci Heaps and Their Uses in Improved Network Optimization Algorithms.” Journal of the ACM 34, no. 3 (July 1987): 596–615. doi:10.1145/28869.28874.&amp;lt;/ref&amp;gt;) выполняется за время &amp;lt;math&amp;gt;O(m + n \ln n)&amp;lt;/math&amp;gt; и является асимптотически быстрейшим из известных последовательных алгоритмов для данного класса задач.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание ===&lt;br /&gt;
&lt;br /&gt;
Пусть задан граф &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; с весами рёбер &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt; и выделенной вершиной-источником &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;. Обозначим через &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt; кратчайшее расстояние от источника &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; до вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Пусть уже вычислены все расстояния, не превосходящие некоторого числа &amp;lt;math&amp;gt;r&amp;lt;/math&amp;gt;, то есть расстояния до вершин из множества &amp;lt;math&amp;gt;V_r = \{ v \in V \mid d(v) \le r \}&amp;lt;/math&amp;gt;. Пусть&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
        (v, w) \in \arg\min \{ d(v) + f(e) \mid  v \in V, e = (v, w) \in E \}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
Тогда &amp;lt;math&amp;gt;d(w) = d(v) + f(e)&amp;lt;/math&amp;gt;, и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; лежит на кратчайшем пути от &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;w&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Величины &amp;lt;math&amp;gt;d^+(w) = d(v) + f(e)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;v \in V_r&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;e = (v, w) \in E&amp;lt;/math&amp;gt;, называются &#039;&#039;предполагаемыми расстояниями&#039;&#039; и являются оценкой сверху для настоящих расстояний: &amp;lt;math&amp;gt;d(w) \le d^+(w)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Алгоритм Дейкстры на каждом шаге находит вершину с наименьшим предполагаемым расстоянием, помечает её как посещённую и обновляет предполагаемые расстояния для всех концов рёбер, исходящих из неё.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основные вычисления связаны с операциями над очередью с приоритетом:&lt;br /&gt;
* извлечение минимального элемента (&amp;lt;code&amp;gt;delete_min&amp;lt;/code&amp;gt;);&lt;br /&gt;
* уменьшение приоритета элемента (&amp;lt;code&amp;gt;decrease_key&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
=== Описание схемы реализации последовательного алгоритма ===&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Последовательная сложность алгоритма равна &amp;lt;math&amp;gt;O(C_1 m + C_2n)&amp;lt;/math&amp;gt;, где&lt;br /&gt;
* &amp;lt;math&amp;gt;C_1&amp;lt;/math&amp;gt; – количество операций уменьшения расстояния до вершины;&lt;br /&gt;
* &amp;lt;math&amp;gt;C_2&amp;lt;/math&amp;gt; – количество операций вычисления минимума.&lt;br /&gt;
&lt;br /&gt;
Оригинальный алгоритм Дейкстры использовал в качестве внутренней структуры данных списки, для которых &amp;lt;math&amp;gt;C_1 = O(1)&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;C_2 = O(n)&amp;lt;/math&amp;gt;, так что общая сложность составляла &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
При использовании фибоначчиевой кучи&amp;lt;ref name=FibHeap /&amp;gt; время вычисления минимума сокращается до &amp;lt;math&amp;gt;C_2 = O(\ln n)&amp;lt;/math&amp;gt;, так что общая сложность равна &amp;lt;math&amp;gt;O(m + n \ln n)&amp;lt;/math&amp;gt;, что является асимптотически наилучшим известным результатом для данного класса задач.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
=== Описание ресурса параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм Дейкстры допускает эффективную параллелизацию&amp;lt;ref&amp;gt;Crauser, A, K Mehlhorn, U Meyer, and P Sanders. “A Parallelization of Dijkstra&#039;s Shortest Path Algorithm,” Proceedings of Mathematical Foundations of Computer Science / Lecture Notes in Computer Science, 1450:722–31, Berlin, Heidelberg: Springer, 1998. doi:10.1007/BFb0055823.&amp;lt;/ref&amp;gt;, среднее время работы &amp;lt;math&amp;gt;O(n^{1/3}\ln n)&amp;lt;/math&amp;gt; с объёмом вычислений &amp;lt;math&amp;gt;O(n \ln n + m)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Алгоритм Δ-шагания]] может рассматриваться как параллельная версия алгоритма Дейкстры.&lt;br /&gt;
&lt;br /&gt;
=== Описание входных и выходных данных ===&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: взвешенный граф &amp;lt;math&amp;gt;(V, E, W)&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин &amp;lt;math&amp;gt;v_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; рёбер &amp;lt;math&amp;gt;e_j = (v^{(1)}_{j}, v^{(2)}_{j})&amp;lt;/math&amp;gt; с весами &amp;lt;math&amp;gt;f_j&amp;lt;/math&amp;gt;), вершина-источник &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(m + n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039; (возможные варианты):&lt;br /&gt;
# для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного графа – последнее ребро &amp;lt;math&amp;gt;e^*_v = (w, v)&amp;lt;/math&amp;gt;, лежащее на кратчайшем пути от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, или соответствующая вершина &amp;lt;math&amp;gt;w&amp;lt;/math&amp;gt;;&lt;br /&gt;
# для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного графа – суммарный вес &amp;lt;math&amp;gt;f^*(v)&amp;lt;/math&amp;gt; кратчайшего пути от от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
	&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(n)&amp;lt;/math&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;
* C++: [http://www.boost.org/libs/graph/doc/ Boost Graph Library] (функции &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph/doc/dijkstra_shortest_paths.html dijkstra_shortest_paths]&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph/doc/dijkstra_shortest_paths_no_color_map.html dijkstra_shortest_paths_no_color_map]&amp;lt;/code&amp;gt;), сложность &amp;lt;math&amp;gt;O(m + n \ln n)&amp;lt;/math&amp;gt;. &lt;br /&gt;
* C++, MPI: [http://www.boost.org/libs/graph_parallel/doc/html/index.html Parallel Boost Graph Library]:&lt;br /&gt;
** функция &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dijkstra_shortest_paths.html#eager-dijkstra-s-algorithm eager_dijkstra_shortest_paths]&amp;lt;/code&amp;gt; – непосредственная реализация алгоритма Дейкстры;&lt;br /&gt;
** функция &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dijkstra_shortest_paths.html#crauser-et-al-s-algorithm crauser_et_al_shortest_paths]&amp;lt;/code&amp;gt; – реализация алгоритма Дейкстры в виде алгоритма из статьи Краузера и др.&amp;lt;ref&amp;gt;Crauser, A, K Mehlhorn, U Meyer, and P Sanders. “A Parallelization of Dijkstra&#039;s Shortest Path Algorithm,” Proceedings of Mathematical Foundations of Computer Science / Lecture Notes in Computer Science, 1450:722–31, Berlin, Heidelberg: Springer, 1998. doi:10.1007/BFb0055823.&amp;lt;/ref&amp;gt;&lt;br /&gt;
* Python: [https://networkx.github.io NetworkX] (функция &amp;lt;code&amp;gt;[http://networkx.github.io/documentation/networkx-1.9.1/reference/generated/networkx.algorithms.shortest_paths.weighted.single_source_dijkstra.html single_source_dijkstra]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Python/C++: [https://networkit.iti.kit.edu NetworKit] (класс &amp;lt;code&amp;gt;[https://networkit.iti.kit.edu/data/uploads/docs/NetworKit-Doc/python/html/graph.html#networkit.graph.Dijkstra networkit.graph.Dijkstra]&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Daryin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%94%D0%B5%D0%B9%D0%BA%D1%81%D1%82%D1%80%D1%8B&amp;diff=1997</id>
		<title>Алгоритм Дейкстры</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%94%D0%B5%D0%B9%D0%BA%D1%81%D1%82%D1%80%D1%8B&amp;diff=1997"/>
		<updated>2015-06-26T11:39:41Z</updated>

		<summary type="html">&lt;p&gt;Daryin: /* Вычислительное ядро алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Свойства и структура алгоритмов ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм Дейкстры&#039;&#039;&#039;&amp;lt;ref&amp;gt;Dijkstra, E W. “A Note on Two Problems in Connexion with Graphs.” Numerische Mathematik 1, no. 1 (December 1959): 269–71. doi:10.1007/BF01386390.&amp;lt;/ref&amp;gt; предназначен для решения [[Поиск кратчайшего пути от одной вершины (SSSP)|задачи поиска кратчайшего пути на графе]]. Для заданного ориентированного взвешенного графа с неотрицательными весами алгоритм находит кратчайшие расстояния от выделенной вершины-источника до всех остальных вершин графа.&lt;br /&gt;
Алгоритм Дейкстры (с использованием фибоначчиевой кучи&amp;lt;ref name=FibHeap&amp;gt;Fredman, Michael L, and Robert Endre Tarjan. “Fibonacci Heaps and Their Uses in Improved Network Optimization Algorithms.” Journal of the ACM 34, no. 3 (July 1987): 596–615. doi:10.1145/28869.28874.&amp;lt;/ref&amp;gt;) выполняется за время &amp;lt;math&amp;gt;O(m + n \ln n)&amp;lt;/math&amp;gt; и является асимптотически быстрейшим из известных последовательных алгоритмов для данного класса задач.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание ===&lt;br /&gt;
&lt;br /&gt;
Пусть задан граф &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; с весами рёбер &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt; и выделенной вершиной-источником &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;. Обозначим через &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt; кратчайшее расстояние от источника &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; до вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Пусть уже вычислены все расстояния, не превосходящие некоторого числа &amp;lt;math&amp;gt;r&amp;lt;/math&amp;gt;, то есть расстояния до вершин из множества &amp;lt;math&amp;gt;V_r = \{ v \in V \mid d(v) \le r \}&amp;lt;/math&amp;gt;. Пусть&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
        (v, w) \in \arg\min \{ d(v) + f(e) \mid  v \in V, e = (v, w) \in E \}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
Тогда &amp;lt;math&amp;gt;d(w) = d(v) + f(e)&amp;lt;/math&amp;gt;, и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; лежит на кратчайшем пути от &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;w&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Величины &amp;lt;math&amp;gt;d^+(w) = d(v) + f(e)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;v \in V_r&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;e = (v, w) \in E&amp;lt;/math&amp;gt;, называются &#039;&#039;предполагаемыми расстояниями&#039;&#039; и являются оценкой сверху для настоящих расстояний: &amp;lt;math&amp;gt;d(w) \le d^+(w)&amp;lt;/math&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;math&amp;gt;O(C_1 m + C_2n)&amp;lt;/math&amp;gt;, где&lt;br /&gt;
* &amp;lt;math&amp;gt;C_1&amp;lt;/math&amp;gt; – количество операций уменьшения расстояния до вершины;&lt;br /&gt;
* &amp;lt;math&amp;gt;C_2&amp;lt;/math&amp;gt; – количество операций вычисления минимума.&lt;br /&gt;
&lt;br /&gt;
Оригинальный алгоритм Дейкстры использовал в качестве внутренней структуры данных списки, для которых &amp;lt;math&amp;gt;C_1 = O(1)&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;C_2 = O(n)&amp;lt;/math&amp;gt;, так что общая сложность составляла &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
При использовании фибоначчиевой кучи&amp;lt;ref name=FibHeap /&amp;gt; время вычисления минимума сокращается до &amp;lt;math&amp;gt;C_2 = O(\ln n)&amp;lt;/math&amp;gt;, так что общая сложность равна &amp;lt;math&amp;gt;O(m + n \ln n)&amp;lt;/math&amp;gt;, что является асимптотически наилучшим известным результатом для данного класса задач.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
=== Описание ресурса параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм Дейкстры допускает эффективную параллелизацию&amp;lt;ref&amp;gt;Crauser, A, K Mehlhorn, U Meyer, and P Sanders. “A Parallelization of Dijkstra&#039;s Shortest Path Algorithm,” Proceedings of Mathematical Foundations of Computer Science / Lecture Notes in Computer Science, 1450:722–31, Berlin, Heidelberg: Springer, 1998. doi:10.1007/BFb0055823.&amp;lt;/ref&amp;gt;, среднее время работы &amp;lt;math&amp;gt;O(n^{1/3}\ln n)&amp;lt;/math&amp;gt; с объёмом вычислений &amp;lt;math&amp;gt;O(n \ln n + m)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Алгоритм Δ-шагания]] может рассматриваться как параллельная версия алгоритма Дейкстры.&lt;br /&gt;
&lt;br /&gt;
=== Описание входных и выходных данных ===&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: взвешенный граф &amp;lt;math&amp;gt;(V, E, W)&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин &amp;lt;math&amp;gt;v_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; рёбер &amp;lt;math&amp;gt;e_j = (v^{(1)}_{j}, v^{(2)}_{j})&amp;lt;/math&amp;gt; с весами &amp;lt;math&amp;gt;f_j&amp;lt;/math&amp;gt;), вершина-источник &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(m + n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039; (возможные варианты):&lt;br /&gt;
# для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного графа – последнее ребро &amp;lt;math&amp;gt;e^*_v = (w, v)&amp;lt;/math&amp;gt;, лежащее на кратчайшем пути от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, или соответствующая вершина &amp;lt;math&amp;gt;w&amp;lt;/math&amp;gt;;&lt;br /&gt;
# для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного графа – суммарный вес &amp;lt;math&amp;gt;f^*(v)&amp;lt;/math&amp;gt; кратчайшего пути от от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
	&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(n)&amp;lt;/math&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;
* C++: [http://www.boost.org/libs/graph/doc/ Boost Graph Library] (функции &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph/doc/dijkstra_shortest_paths.html dijkstra_shortest_paths]&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph/doc/dijkstra_shortest_paths_no_color_map.html dijkstra_shortest_paths_no_color_map]&amp;lt;/code&amp;gt;), сложность &amp;lt;math&amp;gt;O(m + n \ln n)&amp;lt;/math&amp;gt;. &lt;br /&gt;
* C++, MPI: [http://www.boost.org/libs/graph_parallel/doc/html/index.html Parallel Boost Graph Library]:&lt;br /&gt;
** функция &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dijkstra_shortest_paths.html#eager-dijkstra-s-algorithm eager_dijkstra_shortest_paths]&amp;lt;/code&amp;gt; – непосредственная реализация алгоритма Дейкстры;&lt;br /&gt;
** функция &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dijkstra_shortest_paths.html#crauser-et-al-s-algorithm crauser_et_al_shortest_paths]&amp;lt;/code&amp;gt; – реализация алгоритма Дейкстры в виде алгоритма из статьи Краузера и др.&amp;lt;ref&amp;gt;Crauser, A, K Mehlhorn, U Meyer, and P Sanders. “A Parallelization of Dijkstra&#039;s Shortest Path Algorithm,” Proceedings of Mathematical Foundations of Computer Science / Lecture Notes in Computer Science, 1450:722–31, Berlin, Heidelberg: Springer, 1998. doi:10.1007/BFb0055823.&amp;lt;/ref&amp;gt;&lt;br /&gt;
* Python: [https://networkx.github.io NetworkX] (функция &amp;lt;code&amp;gt;[http://networkx.github.io/documentation/networkx-1.9.1/reference/generated/networkx.algorithms.shortest_paths.weighted.single_source_dijkstra.html single_source_dijkstra]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Python/C++: [https://networkit.iti.kit.edu NetworKit] (класс &amp;lt;code&amp;gt;[https://networkit.iti.kit.edu/data/uploads/docs/NetworKit-Doc/python/html/graph.html#networkit.graph.Dijkstra networkit.graph.Dijkstra]&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Daryin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%CE%94-%D1%88%D0%B0%D0%B3%D0%B0%D0%BD%D0%B8%D1%8F&amp;diff=1996</id>
		<title>Алгоритм Δ-шагания</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%CE%94-%D1%88%D0%B0%D0%B3%D0%B0%D0%BD%D0%B8%D1%8F&amp;diff=1996"/>
		<updated>2015-06-25T14:17:59Z</updated>

		<summary type="html">&lt;p&gt;Daryin: /* Описание входных и выходных данных */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Свойства и структура алгоритмов ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм дельта-шагания&#039;&#039;&#039;&amp;lt;ref&amp;gt;Meyer, U, and P Sanders. “Δ-Stepping: a Parallelizable Shortest Path Algorithm.” Journal of Algorithms 49, no. 1 (October 2003): 114–52. doi:10.1016/S0196-6774(03)00076-2.&amp;lt;/ref&amp;gt; (англ. Δ-Stepping) предназначен для решения [[Поиск кратчайшего пути от одной вершины (SSSP)|задачи поиска кратчайшего пути на графе]]. Для заданного ориентированного взвешенного графа с неотрицательными весами алгоритм находит кратчайшие расстояния от выделенной вершины-источника до всех остальных вершин графа. Алгоритм изначально проектировался с целью эффективной параллелизации.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание ===&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
=== Описание схемы реализации последовательного алгоритма ===&lt;br /&gt;
Псевдокод последовательного алгоритма:&lt;br /&gt;
 &lt;br /&gt;
 &#039;&#039;&#039;Входные данные&#039;&#039;&#039;:&lt;br /&gt;
   граф с вершинами &#039;&#039;V&#039;&#039;, рёбрами &#039;&#039;E&#039;&#039; с весами &#039;&#039;W&#039;&#039;;&lt;br /&gt;
   вершина-источник &#039;&#039;u&#039;&#039;;&lt;br /&gt;
   параметр &#039;&#039;Δ&#039;&#039; &amp;gt; 0.&lt;br /&gt;
 &#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: расстояния &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) до каждой вершины &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039; от вершины &#039;&#039;u&#039;&#039;.&lt;br /&gt;
 &lt;br /&gt;
 &#039;&#039;&#039;procedure&#039;&#039;&#039; DeltaStepping(&#039;&#039;V&#039;&#039;, &#039;&#039;E&#039;&#039;, &#039;&#039;W&#039;&#039;, &#039;&#039;u&#039;&#039;, &#039;&#039;Δ&#039;&#039;):&lt;br /&gt;
      &#039;&#039;LightEdges&#039;&#039; := { e ∈ E | W(e) ≤ Δ }&lt;br /&gt;
      &#039;&#039;HeavyEdges&#039;&#039; := { e ∈ E | W(e) &amp;gt; Δ }&lt;br /&gt;
      &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039; &#039;&#039;&#039;do&#039;&#039;&#039; &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) := ∞&lt;br /&gt;
      Relax(&#039;&#039;u&#039;&#039;, 0)&lt;br /&gt;
      &#039;&#039;&#039;while&#039;&#039;&#039; any({ &#039;&#039;Buckets&#039;&#039;(i) ≠ ∅ }):&lt;br /&gt;
          &#039;&#039;Bucket&#039;&#039; := first({ &#039;&#039;Buckets&#039;&#039;(i) ≠ ∅ })&lt;br /&gt;
          &#039;&#039;Deleted&#039;&#039; := ∅&lt;br /&gt;
          &#039;&#039;&#039;while&#039;&#039;&#039; &#039;&#039;Bucket&#039;&#039; ≠ ∅:&lt;br /&gt;
              &#039;&#039;Requests&#039;&#039; := FindRequests(&#039;&#039;Bucket&#039;&#039;, &#039;&#039;LightEdges&#039;&#039;)&lt;br /&gt;
              &#039;&#039;Deleted&#039;&#039; := &#039;&#039;Deleted&#039;&#039; ∪ &#039;&#039;Bucket&#039;&#039;&lt;br /&gt;
              &#039;&#039;Bucket&#039;&#039; := ∅&lt;br /&gt;
              RelaxAll(&#039;&#039;Requests&#039;&#039;)&lt;br /&gt;
          RelaxAll(FindRequests(&#039;&#039;Deleted&#039;&#039;, &#039;&#039;HeavyEdges&#039;&#039;))&lt;br /&gt;
          &lt;br /&gt;
 &#039;&#039;&#039;procedure&#039;&#039;&#039; Relax(&#039;&#039;v&#039;&#039;, &#039;&#039;x&#039;&#039;):&lt;br /&gt;
     &#039;&#039;&#039;if&#039;&#039;&#039; &#039;&#039;x&#039;&#039; &amp;lt; &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;):&lt;br /&gt;
         &#039;&#039;OldBucket&#039;&#039; := B(⌊&#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) / &#039;&#039;Δ&#039;&#039;⌋)&lt;br /&gt;
         &#039;&#039;NewBucket&#039;&#039; := B(⌊&#039;&#039;x&#039;&#039; / &#039;&#039;Δ&#039;&#039;⌋)&lt;br /&gt;
         &#039;&#039;OldBucket&#039;&#039; := &#039;&#039;OldBucket&#039;&#039; \ {&#039;&#039;v&#039;&#039;}&lt;br /&gt;
         &#039;&#039;NewBucket&#039;&#039; := &#039;&#039;NewBucket&#039;&#039; ∪ {&#039;&#039;v&#039;&#039;}&lt;br /&gt;
         &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) := &#039;&#039;x&#039;&#039;&lt;br /&gt;
         &lt;br /&gt;
 &#039;&#039;&#039;procedure&#039;&#039;&#039; RelaxAll(&#039;&#039;R&#039;&#039;):&lt;br /&gt;
     &#039;&#039;&#039;for each&#039;&#039;&#039; (&#039;&#039;v&#039;&#039;, &#039;&#039;x&#039;&#039;) ∈ &#039;&#039;R&#039;&#039; &#039;&#039;&#039;do&#039;&#039;&#039; Relax(&#039;&#039;v&#039;&#039;, &#039;&#039;x&#039;&#039;)&lt;br /&gt;
     &lt;br /&gt;
 &#039;&#039;&#039;function&#039;&#039;&#039; FindRequests(&#039;&#039;V&amp;lt;nowiki&amp;gt;&#039;&amp;lt;/nowiki&amp;gt;&#039;&#039;, &#039;&#039;E&amp;lt;nowiki&amp;gt;&#039;&amp;lt;/nowiki&amp;gt;&#039;&#039;):&lt;br /&gt;
     &#039;&#039;&#039;return&#039;&#039;&#039; { (&#039;&#039;w&#039;&#039;, &#039;&#039;d&#039;&#039;(&#039;&#039;w&#039;&#039;) + &#039;&#039;W&#039;&#039;(&#039;&#039;v&#039;&#039;, &#039;&#039;w&#039;&#039;)) | &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&amp;lt;nowiki&amp;gt;&#039;&amp;lt;/nowiki&amp;gt;&#039;&#039; and (&#039;&#039;v&#039;&#039;, &#039;&#039;w&#039;&#039;) ∈ &#039;&#039;E&amp;lt;nowiki&amp;gt;&#039;&amp;lt;/nowiki&amp;gt;&#039;&#039;}&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Средняя последовательная сложность алгоритма на графах со случайными весами &amp;lt;math&amp;gt;O(n + m + dL)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;L&amp;lt;/math&amp;gt; – максимальный суммарный вес кратчайшего пути, &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; – максимальная длина кратчайшего пути.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
=== Описание ресурса параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Средняя время работы параллельного алгоритма на графах со случайными весами на &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; процессорах составляет &amp;lt;math&amp;gt;O((dL + \ln n) \ln n)&amp;lt;/math&amp;gt; со средней работой &amp;lt;math&amp;gt;O(n + m + dL \ln n)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Описание входных и выходных данных ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: взвешенный граф &amp;lt;math&amp;gt;(V, E, W)&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин &amp;lt;math&amp;gt;v_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; рёбер &amp;lt;math&amp;gt;e_j = (v^{(1)}_{j}, v^{(2)}_{j})&amp;lt;/math&amp;gt; с весами &amp;lt;math&amp;gt;f_j&amp;lt;/math&amp;gt;), вершина-источник &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(m + n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039; (возможные варианты):&lt;br /&gt;
# для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного графа – последнее ребро &amp;lt;math&amp;gt;e^*_v = (w, v)&amp;lt;/math&amp;gt;, лежащее на кратчайшем пути от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, или соответствующая вершина &amp;lt;math&amp;gt;w&amp;lt;/math&amp;gt;;&lt;br /&gt;
# для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного графа – суммарный вес &amp;lt;math&amp;gt;f^*(v)&amp;lt;/math&amp;gt; кратчайшего пути от от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
	&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма===&lt;br /&gt;
&lt;br /&gt;
Предельные случаи алгоритма Δ-шагания:&lt;br /&gt;
* при &amp;lt;math&amp;gt;\Delta \to 0&amp;lt;/math&amp;gt;: [[алгоритм Дейкстры]];&lt;br /&gt;
* при &amp;lt;math&amp;gt;\Delta \to \infty&amp;lt;/math&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;
* C++, MPI: [http://www.boost.org/libs/graph_parallel/doc/html/index.html Parallel Boost Graph Library] (функция &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dijkstra_shortest_paths.html#delta-stepping-algorithm delta_stepping_shortest_paths]&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Daryin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%94%D0%B5%D0%B9%D0%BA%D1%81%D1%82%D1%80%D1%8B&amp;diff=1995</id>
		<title>Алгоритм Дейкстры</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%94%D0%B5%D0%B9%D0%BA%D1%81%D1%82%D1%80%D1%8B&amp;diff=1995"/>
		<updated>2015-06-25T14:17:34Z</updated>

		<summary type="html">&lt;p&gt;Daryin: /* Описание входных и выходных данных */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Свойства и структура алгоритмов ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм Дейкстры&#039;&#039;&#039;&amp;lt;ref&amp;gt;Dijkstra, E W. “A Note on Two Problems in Connexion with Graphs.” Numerische Mathematik 1, no. 1 (December 1959): 269–71. doi:10.1007/BF01386390.&amp;lt;/ref&amp;gt; предназначен для решения [[Поиск кратчайшего пути от одной вершины (SSSP)|задачи поиска кратчайшего пути на графе]]. Для заданного ориентированного взвешенного графа с неотрицательными весами алгоритм находит кратчайшие расстояния от выделенной вершины-источника до всех остальных вершин графа.&lt;br /&gt;
Алгоритм Дейкстры (с использованием фибоначчиевой кучи&amp;lt;ref name=FibHeap&amp;gt;Fredman, Michael L, and Robert Endre Tarjan. “Fibonacci Heaps and Their Uses in Improved Network Optimization Algorithms.” Journal of the ACM 34, no. 3 (July 1987): 596–615. doi:10.1145/28869.28874.&amp;lt;/ref&amp;gt;) выполняется за время &amp;lt;math&amp;gt;O(m + n \ln n)&amp;lt;/math&amp;gt; и является асимптотически быстрейшим из известных последовательных алгоритмов для данного класса задач.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание ===&lt;br /&gt;
&lt;br /&gt;
Пусть задан граф &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; с весами рёбер &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt; и выделенной вершиной-источником &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;. Обозначим через &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt; кратчайшее расстояние от источника &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; до вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Пусть уже вычислены все расстояния, не превосходящие некоторого числа &amp;lt;math&amp;gt;r&amp;lt;/math&amp;gt;, то есть расстояния до вершин из множества &amp;lt;math&amp;gt;V_r = \{ v \in V \mid d(v) \le r \}&amp;lt;/math&amp;gt;. Пусть&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
        (v, w) \in \arg\min \{ d(v) + f(e) \mid  v \in V, e = (v, w) \in E \}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
Тогда &amp;lt;math&amp;gt;d(w) = d(v) + f(e)&amp;lt;/math&amp;gt;, и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; лежит на кратчайшем пути от &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;w&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Величины &amp;lt;math&amp;gt;d^+(w) = d(v) + f(e)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;v \in V_r&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;e = (v, w) \in E&amp;lt;/math&amp;gt;, называются &#039;&#039;предполагаемыми расстояниями&#039;&#039; и являются оценкой сверху для настоящих расстояний: &amp;lt;math&amp;gt;d(w) \le d^+(w)&amp;lt;/math&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;
Последовательная сложность алгоритма равна &amp;lt;math&amp;gt;O(C_1 m + C_2n)&amp;lt;/math&amp;gt;, где&lt;br /&gt;
* &amp;lt;math&amp;gt;C_1&amp;lt;/math&amp;gt; – количество операций уменьшения расстояния до вершины;&lt;br /&gt;
* &amp;lt;math&amp;gt;C_2&amp;lt;/math&amp;gt; – количество операций вычисления минимума.&lt;br /&gt;
&lt;br /&gt;
Оригинальный алгоритм Дейкстры использовал в качестве внутренней структуры данных списки, для которых &amp;lt;math&amp;gt;C_1 = O(1)&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;C_2 = O(n)&amp;lt;/math&amp;gt;, так что общая сложность составляла &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
При использовании фибоначчиевой кучи&amp;lt;ref name=FibHeap /&amp;gt; время вычисления минимума сокращается до &amp;lt;math&amp;gt;C_2 = O(\ln n)&amp;lt;/math&amp;gt;, так что общая сложность равна &amp;lt;math&amp;gt;O(m + n \ln n)&amp;lt;/math&amp;gt;, что является асимптотически наилучшим известным результатом для данного класса задач.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
=== Описание ресурса параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм Дейкстры допускает эффективную параллелизацию&amp;lt;ref&amp;gt;Crauser, A, K Mehlhorn, U Meyer, and P Sanders. “A Parallelization of Dijkstra&#039;s Shortest Path Algorithm,” Proceedings of Mathematical Foundations of Computer Science / Lecture Notes in Computer Science, 1450:722–31, Berlin, Heidelberg: Springer, 1998. doi:10.1007/BFb0055823.&amp;lt;/ref&amp;gt;, среднее время работы &amp;lt;math&amp;gt;O(n^{1/3}\ln n)&amp;lt;/math&amp;gt; с объёмом вычислений &amp;lt;math&amp;gt;O(n \ln n + m)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Алгоритм Δ-шагания]] может рассматриваться как параллельная версия алгоритма Дейкстры.&lt;br /&gt;
&lt;br /&gt;
=== Описание входных и выходных данных ===&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: взвешенный граф &amp;lt;math&amp;gt;(V, E, W)&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин &amp;lt;math&amp;gt;v_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; рёбер &amp;lt;math&amp;gt;e_j = (v^{(1)}_{j}, v^{(2)}_{j})&amp;lt;/math&amp;gt; с весами &amp;lt;math&amp;gt;f_j&amp;lt;/math&amp;gt;), вершина-источник &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(m + n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039; (возможные варианты):&lt;br /&gt;
# для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного графа – последнее ребро &amp;lt;math&amp;gt;e^*_v = (w, v)&amp;lt;/math&amp;gt;, лежащее на кратчайшем пути от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, или соответствующая вершина &amp;lt;math&amp;gt;w&amp;lt;/math&amp;gt;;&lt;br /&gt;
# для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного графа – суммарный вес &amp;lt;math&amp;gt;f^*(v)&amp;lt;/math&amp;gt; кратчайшего пути от от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
	&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(n)&amp;lt;/math&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;
* C++: [http://www.boost.org/libs/graph/doc/ Boost Graph Library] (функции &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph/doc/dijkstra_shortest_paths.html dijkstra_shortest_paths]&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph/doc/dijkstra_shortest_paths_no_color_map.html dijkstra_shortest_paths_no_color_map]&amp;lt;/code&amp;gt;), сложность &amp;lt;math&amp;gt;O(m + n \ln n)&amp;lt;/math&amp;gt;. &lt;br /&gt;
* C++, MPI: [http://www.boost.org/libs/graph_parallel/doc/html/index.html Parallel Boost Graph Library]:&lt;br /&gt;
** функция &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dijkstra_shortest_paths.html#eager-dijkstra-s-algorithm eager_dijkstra_shortest_paths]&amp;lt;/code&amp;gt; – непосредственная реализация алгоритма Дейкстры;&lt;br /&gt;
** функция &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph_parallel/doc/html/dijkstra_shortest_paths.html#crauser-et-al-s-algorithm crauser_et_al_shortest_paths]&amp;lt;/code&amp;gt; – реализация алгоритма Дейкстры в виде алгоритма из статьи Краузера и др.&amp;lt;ref&amp;gt;Crauser, A, K Mehlhorn, U Meyer, and P Sanders. “A Parallelization of Dijkstra&#039;s Shortest Path Algorithm,” Proceedings of Mathematical Foundations of Computer Science / Lecture Notes in Computer Science, 1450:722–31, Berlin, Heidelberg: Springer, 1998. doi:10.1007/BFb0055823.&amp;lt;/ref&amp;gt;&lt;br /&gt;
* Python: [https://networkx.github.io NetworkX] (функция &amp;lt;code&amp;gt;[http://networkx.github.io/documentation/networkx-1.9.1/reference/generated/networkx.algorithms.shortest_paths.weighted.single_source_dijkstra.html single_source_dijkstra]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Python/C++: [https://networkit.iti.kit.edu NetworKit] (класс &amp;lt;code&amp;gt;[https://networkit.iti.kit.edu/data/uploads/docs/NetworKit-Doc/python/html/graph.html#networkit.graph.Dijkstra networkit.graph.Dijkstra]&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Daryin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%91%D0%B5%D0%BB%D0%BB%D0%BC%D0%B0%D0%BD%D0%B0-%D0%A4%D0%BE%D1%80%D0%B4%D0%B0&amp;diff=1994</id>
		<title>Алгоритм Беллмана-Форда</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%91%D0%B5%D0%BB%D0%BB%D0%BC%D0%B0%D0%BD%D0%B0-%D0%A4%D0%BE%D1%80%D0%B4%D0%B0&amp;diff=1994"/>
		<updated>2015-06-25T14:17:10Z</updated>

		<summary type="html">&lt;p&gt;Daryin: /* Описание входных и выходных данных */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Свойства и структура алгоритмов ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм Беллмана-Форда&#039;&#039;&#039;&amp;lt;ref&amp;gt;Bellman, Richard. “On a Routing Problem.” Quarterly of Applied Mathematics 16 (1958): 87–90.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Ford, L R. Network Flow Theory. Rand.org, RAND Corporation, 1958.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Moore, Edward F. “The Shortest Path Through a Maze,” International Symposium on the Theory of Switching, 285–92, 1959.&amp;lt;/ref&amp;gt; предназначен для решения [[Поиск кратчайшего пути от одной вершины (SSSP)|задачи поиска кратчайшего пути на графе]]. Для заданного ориентированного взвешенного графа алгоритм находит кратчайшие расстояния от выделенной вершины-источника до всех остальных вершин графа. Алгоритм Беллмана-Форда масштабируется хуже других алгоритмов решения указанной задачи (сложность &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt; против &amp;lt;math&amp;gt;O(m + n\ln n)&amp;lt;/math&amp;gt; у [[Алгоритм Дейкстры|алгоритма Дейкстры]]), однако его отличительной особенностью является применимость к графам с произвольными, в том числе отрицательными, весами.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание ===&lt;br /&gt;
Пусть задан граф &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; с весами рёбер &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt; и выделенной вершиной-источником &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;. Обозначим через &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt; кратчайшее расстояние от источника &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; до вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Алгоритм Беллмана-Форда ищет функцию &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt; как единственное решение уравнения&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
        d(v) = \min \{ d(w) + f(e) \mid e = (w, v) \in E \}, \quad \forall v \ne u,&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
с начальным условием &amp;lt;math&amp;gt;d(u) = 0&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основной операцией алгоритма является релаксация ребра: если &amp;lt;math&amp;gt;e = (w, v) \in E&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;d(v) &amp;gt; d(w) + f(e)&amp;lt;/math&amp;gt;, то производится присваивание &amp;lt;math&amp;gt;d(v) \leftarrow d(w) + f(e)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
Алгоритм последовательно уточняет значения функции &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* В самом начале производится присваивание &amp;lt;math&amp;gt;d(u) = 0&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;d(v) = \infty&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\forall v \ne u&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Далее происходит &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; итерация, в ходе каждой из которых производится релаксация всех рёбер графа.&lt;br /&gt;
&lt;br /&gt;
=== Описание схемы реализации последовательного алгоритма ===&lt;br /&gt;
Последовательный алгоритм реализуется следующим псевдокодом:&lt;br /&gt;
&lt;br /&gt;
 &#039;&#039;&#039;Входные данные&#039;&#039;&#039;:&lt;br /&gt;
   граф с вершинами &#039;&#039;V&#039;&#039;, рёбрами &#039;&#039;E&#039;&#039; с весами &#039;&#039;f&#039;&#039;(&#039;&#039;e&#039;&#039;);&lt;br /&gt;
   вершина-источник &#039;&#039;u&#039;&#039;.&lt;br /&gt;
 &#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: расстояния &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) до каждой вершины &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039; от вершины &#039;&#039;u&#039;&#039;.&lt;br /&gt;
 &lt;br /&gt;
 &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039; &#039;&#039;&#039;do&#039;&#039;&#039; &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) := ∞&lt;br /&gt;
 &#039;&#039;d&#039;&#039;(&#039;&#039;u&#039;&#039;) = 0&lt;br /&gt;
 &lt;br /&gt;
 &#039;&#039;&#039;for&#039;&#039;&#039; &#039;&#039;i&#039;&#039; &#039;&#039;&#039;from&#039;&#039;&#039; 1 &#039;&#039;&#039;to&#039;&#039;&#039; |&#039;&#039;V&#039;&#039;| - 1:&lt;br /&gt;
     &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;e&#039;&#039; = (&#039;&#039;w&#039;&#039;, &#039;&#039;v&#039;&#039;) ∈ &#039;&#039;E&#039;&#039;:&lt;br /&gt;
         &#039;&#039;&#039;if&#039;&#039;&#039; &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) &amp;gt; &#039;&#039;d&#039;&#039;(&#039;&#039;w&#039;&#039;) + &#039;&#039;f&#039;&#039;(&#039;&#039;e&#039;&#039;):&lt;br /&gt;
             &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) := &#039;&#039;d&#039;&#039;(&#039;&#039;w&#039;&#039;) + &#039;&#039;f&#039;&#039;(&#039;&#039;e&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Алгоритм выполняет &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; итерацию, на каждой из которых происходит релаксация &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; рёбер. Таким образом, общий объём работы составляет &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&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;
При использовании атомарных операций для вычисления минимума релаксация рёбер может производится параллельно. В этом случае потребуется &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; шагов при использовании &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt; процессоров.&lt;br /&gt;
&lt;br /&gt;
[[Алгоритм Δ-шагания]] может рассматриваться как параллельная версия алгоритма Беллмана-Форда.&lt;br /&gt;
&lt;br /&gt;
=== Описание входных и выходных данных ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: взвешенный граф &amp;lt;math&amp;gt;(V, E, W)&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин &amp;lt;math&amp;gt;v_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; рёбер &amp;lt;math&amp;gt;e_j = (v^{(1)}_{j}, v^{(2)}_{j})&amp;lt;/math&amp;gt; с весами &amp;lt;math&amp;gt;f_j&amp;lt;/math&amp;gt;), вершина-источник &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(m + n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039; (возможные варианты):&lt;br /&gt;
# для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного графа – последнее ребро &amp;lt;math&amp;gt;e^*_v = (w, v)&amp;lt;/math&amp;gt;, лежащее на кратчайшем пути от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;, или соответствующая вершина &amp;lt;math&amp;gt;w&amp;lt;/math&amp;gt;;&lt;br /&gt;
# для каждой вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; исходного графа – суммарный вес &amp;lt;math&amp;gt;f^*(v)&amp;lt;/math&amp;gt; кратчайшего пути от от вершины &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
	&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма===&lt;br /&gt;
&lt;br /&gt;
Алгоритм может распознавать наличие отрицательных циклов в графе. Ребро &amp;lt;math&amp;gt;e = (v, w)&amp;lt;/math&amp;gt; лежит на таком цикле, если вычисленные алгоритмом кратчайшие расстояния &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt; удовлетворяют условию&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
        d(v) + f(e) &amp;lt; d(w),&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
где &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt; – вес ребра &amp;lt;math&amp;gt;e&amp;lt;/math&amp;gt;. Условие может быть проверено для всех рёбер графа за время &amp;lt;math&amp;gt;O(m)&amp;lt;/math&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;
* C++: [http://www.boost.org/libs/graph/doc/ Boost Graph Library] (функция &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph/doc/bellman_ford_shortest.html bellman_ford_shortest]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Python: [https://networkx.github.io NetworkX] (функция &amp;lt;code&amp;gt;[http://networkx.github.io/documentation/networkx-1.9.1/reference/generated/networkx.algorithms.shortest_paths.weighted.bellman_ford.html bellman_ford]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Java: [http://jgrapht.org JGraphT] (класс &amp;lt;code&amp;gt;[http://jgrapht.org/javadoc/org/jgrapht/alg/BellmanFordShortestPath.html BellmanFordShortestPath]&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Daryin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%91%D0%B5%D0%BB%D0%BB%D0%BC%D0%B0%D0%BD%D0%B0-%D0%A4%D0%BE%D1%80%D0%B4%D0%B0&amp;diff=1993</id>
		<title>Алгоритм Беллмана-Форда</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%91%D0%B5%D0%BB%D0%BB%D0%BC%D0%B0%D0%BD%D0%B0-%D0%A4%D0%BE%D1%80%D0%B4%D0%B0&amp;diff=1993"/>
		<updated>2015-06-25T14:16:24Z</updated>

		<summary type="html">&lt;p&gt;Daryin: /* Описание ресурса параллелизма алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Свойства и структура алгоритмов ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм Беллмана-Форда&#039;&#039;&#039;&amp;lt;ref&amp;gt;Bellman, Richard. “On a Routing Problem.” Quarterly of Applied Mathematics 16 (1958): 87–90.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Ford, L R. Network Flow Theory. Rand.org, RAND Corporation, 1958.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Moore, Edward F. “The Shortest Path Through a Maze,” International Symposium on the Theory of Switching, 285–92, 1959.&amp;lt;/ref&amp;gt; предназначен для решения [[Поиск кратчайшего пути от одной вершины (SSSP)|задачи поиска кратчайшего пути на графе]]. Для заданного ориентированного взвешенного графа алгоритм находит кратчайшие расстояния от выделенной вершины-источника до всех остальных вершин графа. Алгоритм Беллмана-Форда масштабируется хуже других алгоритмов решения указанной задачи (сложность &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt; против &amp;lt;math&amp;gt;O(m + n\ln n)&amp;lt;/math&amp;gt; у [[Алгоритм Дейкстры|алгоритма Дейкстры]]), однако его отличительной особенностью является применимость к графам с произвольными, в том числе отрицательными, весами.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание ===&lt;br /&gt;
Пусть задан граф &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; с весами рёбер &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt; и выделенной вершиной-источником &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;. Обозначим через &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt; кратчайшее расстояние от источника &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; до вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Алгоритм Беллмана-Форда ищет функцию &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt; как единственное решение уравнения&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
        d(v) = \min \{ d(w) + f(e) \mid e = (w, v) \in E \}, \quad \forall v \ne u,&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
с начальным условием &amp;lt;math&amp;gt;d(u) = 0&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основной операцией алгоритма является релаксация ребра: если &amp;lt;math&amp;gt;e = (w, v) \in E&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;d(v) &amp;gt; d(w) + f(e)&amp;lt;/math&amp;gt;, то производится присваивание &amp;lt;math&amp;gt;d(v) \leftarrow d(w) + f(e)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
Алгоритм последовательно уточняет значения функции &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* В самом начале производится присваивание &amp;lt;math&amp;gt;d(u) = 0&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;d(v) = \infty&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\forall v \ne u&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Далее происходит &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; итерация, в ходе каждой из которых производится релаксация всех рёбер графа.&lt;br /&gt;
&lt;br /&gt;
=== Описание схемы реализации последовательного алгоритма ===&lt;br /&gt;
Последовательный алгоритм реализуется следующим псевдокодом:&lt;br /&gt;
&lt;br /&gt;
 &#039;&#039;&#039;Входные данные&#039;&#039;&#039;:&lt;br /&gt;
   граф с вершинами &#039;&#039;V&#039;&#039;, рёбрами &#039;&#039;E&#039;&#039; с весами &#039;&#039;f&#039;&#039;(&#039;&#039;e&#039;&#039;);&lt;br /&gt;
   вершина-источник &#039;&#039;u&#039;&#039;.&lt;br /&gt;
 &#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: расстояния &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) до каждой вершины &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039; от вершины &#039;&#039;u&#039;&#039;.&lt;br /&gt;
 &lt;br /&gt;
 &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039; &#039;&#039;&#039;do&#039;&#039;&#039; &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) := ∞&lt;br /&gt;
 &#039;&#039;d&#039;&#039;(&#039;&#039;u&#039;&#039;) = 0&lt;br /&gt;
 &lt;br /&gt;
 &#039;&#039;&#039;for&#039;&#039;&#039; &#039;&#039;i&#039;&#039; &#039;&#039;&#039;from&#039;&#039;&#039; 1 &#039;&#039;&#039;to&#039;&#039;&#039; |&#039;&#039;V&#039;&#039;| - 1:&lt;br /&gt;
     &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;e&#039;&#039; = (&#039;&#039;w&#039;&#039;, &#039;&#039;v&#039;&#039;) ∈ &#039;&#039;E&#039;&#039;:&lt;br /&gt;
         &#039;&#039;&#039;if&#039;&#039;&#039; &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) &amp;gt; &#039;&#039;d&#039;&#039;(&#039;&#039;w&#039;&#039;) + &#039;&#039;f&#039;&#039;(&#039;&#039;e&#039;&#039;):&lt;br /&gt;
             &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) := &#039;&#039;d&#039;&#039;(&#039;&#039;w&#039;&#039;) + &#039;&#039;f&#039;&#039;(&#039;&#039;e&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Алгоритм выполняет &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; итерацию, на каждой из которых происходит релаксация &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; рёбер. Таким образом, общий объём работы составляет &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&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;
При использовании атомарных операций для вычисления минимума релаксация рёбер может производится параллельно. В этом случае потребуется &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; шагов при использовании &amp;lt;math&amp;gt;O(m)&amp;lt;/math&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;math&amp;gt;e = (v, w)&amp;lt;/math&amp;gt; лежит на таком цикле, если вычисленные алгоритмом кратчайшие расстояния &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt; удовлетворяют условию&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
        d(v) + f(e) &amp;lt; d(w),&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
где &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt; – вес ребра &amp;lt;math&amp;gt;e&amp;lt;/math&amp;gt;. Условие может быть проверено для всех рёбер графа за время &amp;lt;math&amp;gt;O(m)&amp;lt;/math&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;
* C++: [http://www.boost.org/libs/graph/doc/ Boost Graph Library] (функция &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph/doc/bellman_ford_shortest.html bellman_ford_shortest]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Python: [https://networkx.github.io NetworkX] (функция &amp;lt;code&amp;gt;[http://networkx.github.io/documentation/networkx-1.9.1/reference/generated/networkx.algorithms.shortest_paths.weighted.bellman_ford.html bellman_ford]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Java: [http://jgrapht.org JGraphT] (класс &amp;lt;code&amp;gt;[http://jgrapht.org/javadoc/org/jgrapht/alg/BellmanFordShortestPath.html BellmanFordShortestPath]&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Daryin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%91%D0%B5%D0%BB%D0%BB%D0%BC%D0%B0%D0%BD%D0%B0-%D0%A4%D0%BE%D1%80%D0%B4%D0%B0&amp;diff=1992</id>
		<title>Алгоритм Беллмана-Форда</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%91%D0%B5%D0%BB%D0%BB%D0%BC%D0%B0%D0%BD%D0%B0-%D0%A4%D0%BE%D1%80%D0%B4%D0%B0&amp;diff=1992"/>
		<updated>2015-06-25T14:14:18Z</updated>

		<summary type="html">&lt;p&gt;Daryin: /* Описание схемы реализации последовательного алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Свойства и структура алгоритмов ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм Беллмана-Форда&#039;&#039;&#039;&amp;lt;ref&amp;gt;Bellman, Richard. “On a Routing Problem.” Quarterly of Applied Mathematics 16 (1958): 87–90.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Ford, L R. Network Flow Theory. Rand.org, RAND Corporation, 1958.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Moore, Edward F. “The Shortest Path Through a Maze,” International Symposium on the Theory of Switching, 285–92, 1959.&amp;lt;/ref&amp;gt; предназначен для решения [[Поиск кратчайшего пути от одной вершины (SSSP)|задачи поиска кратчайшего пути на графе]]. Для заданного ориентированного взвешенного графа алгоритм находит кратчайшие расстояния от выделенной вершины-источника до всех остальных вершин графа. Алгоритм Беллмана-Форда масштабируется хуже других алгоритмов решения указанной задачи (сложность &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt; против &amp;lt;math&amp;gt;O(m + n\ln n)&amp;lt;/math&amp;gt; у [[Алгоритм Дейкстры|алгоритма Дейкстры]]), однако его отличительной особенностью является применимость к графам с произвольными, в том числе отрицательными, весами.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание ===&lt;br /&gt;
Пусть задан граф &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; с весами рёбер &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt; и выделенной вершиной-источником &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;. Обозначим через &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt; кратчайшее расстояние от источника &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; до вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Алгоритм Беллмана-Форда ищет функцию &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt; как единственное решение уравнения&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
        d(v) = \min \{ d(w) + f(e) \mid e = (w, v) \in E \}, \quad \forall v \ne u,&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
с начальным условием &amp;lt;math&amp;gt;d(u) = 0&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основной операцией алгоритма является релаксация ребра: если &amp;lt;math&amp;gt;e = (w, v) \in E&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;d(v) &amp;gt; d(w) + f(e)&amp;lt;/math&amp;gt;, то производится присваивание &amp;lt;math&amp;gt;d(v) \leftarrow d(w) + f(e)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
Алгоритм последовательно уточняет значения функции &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* В самом начале производится присваивание &amp;lt;math&amp;gt;d(u) = 0&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;d(v) = \infty&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\forall v \ne u&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Далее происходит &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; итерация, в ходе каждой из которых производится релаксация всех рёбер графа.&lt;br /&gt;
&lt;br /&gt;
=== Описание схемы реализации последовательного алгоритма ===&lt;br /&gt;
Последовательный алгоритм реализуется следующим псевдокодом:&lt;br /&gt;
&lt;br /&gt;
 &#039;&#039;&#039;Входные данные&#039;&#039;&#039;:&lt;br /&gt;
   граф с вершинами &#039;&#039;V&#039;&#039;, рёбрами &#039;&#039;E&#039;&#039; с весами &#039;&#039;f&#039;&#039;(&#039;&#039;e&#039;&#039;);&lt;br /&gt;
   вершина-источник &#039;&#039;u&#039;&#039;.&lt;br /&gt;
 &#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: расстояния &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) до каждой вершины &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039; от вершины &#039;&#039;u&#039;&#039;.&lt;br /&gt;
 &lt;br /&gt;
 &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039; &#039;&#039;&#039;do&#039;&#039;&#039; &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) := ∞&lt;br /&gt;
 &#039;&#039;d&#039;&#039;(&#039;&#039;u&#039;&#039;) = 0&lt;br /&gt;
 &lt;br /&gt;
 &#039;&#039;&#039;for&#039;&#039;&#039; &#039;&#039;i&#039;&#039; &#039;&#039;&#039;from&#039;&#039;&#039; 1 &#039;&#039;&#039;to&#039;&#039;&#039; |&#039;&#039;V&#039;&#039;| - 1:&lt;br /&gt;
     &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;e&#039;&#039; = (&#039;&#039;w&#039;&#039;, &#039;&#039;v&#039;&#039;) ∈ &#039;&#039;E&#039;&#039;:&lt;br /&gt;
         &#039;&#039;&#039;if&#039;&#039;&#039; &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) &amp;gt; &#039;&#039;d&#039;&#039;(&#039;&#039;w&#039;&#039;) + &#039;&#039;f&#039;&#039;(&#039;&#039;e&#039;&#039;):&lt;br /&gt;
             &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) := &#039;&#039;d&#039;&#039;(&#039;&#039;w&#039;&#039;) + &#039;&#039;f&#039;&#039;(&#039;&#039;e&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Алгоритм выполняет &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; итерацию, на каждой из которых происходит релаксация &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; рёбер. Таким образом, общий объём работы составляет &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&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;
=== Свойства алгоритма===&lt;br /&gt;
&lt;br /&gt;
Алгоритм может распознавать наличие отрицательных циклов в графе. Ребро &amp;lt;math&amp;gt;e = (v, w)&amp;lt;/math&amp;gt; лежит на таком цикле, если вычисленные алгоритмом кратчайшие расстояния &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt; удовлетворяют условию&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
        d(v) + f(e) &amp;lt; d(w),&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
где &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt; – вес ребра &amp;lt;math&amp;gt;e&amp;lt;/math&amp;gt;. Условие может быть проверено для всех рёбер графа за время &amp;lt;math&amp;gt;O(m)&amp;lt;/math&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;
* C++: [http://www.boost.org/libs/graph/doc/ Boost Graph Library] (функция &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph/doc/bellman_ford_shortest.html bellman_ford_shortest]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Python: [https://networkx.github.io NetworkX] (функция &amp;lt;code&amp;gt;[http://networkx.github.io/documentation/networkx-1.9.1/reference/generated/networkx.algorithms.shortest_paths.weighted.bellman_ford.html bellman_ford]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Java: [http://jgrapht.org JGraphT] (класс &amp;lt;code&amp;gt;[http://jgrapht.org/javadoc/org/jgrapht/alg/BellmanFordShortestPath.html BellmanFordShortestPath]&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Daryin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%91%D0%B5%D0%BB%D0%BB%D0%BC%D0%B0%D0%BD%D0%B0-%D0%A4%D0%BE%D1%80%D0%B4%D0%B0&amp;diff=1991</id>
		<title>Алгоритм Беллмана-Форда</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%91%D0%B5%D0%BB%D0%BB%D0%BC%D0%B0%D0%BD%D0%B0-%D0%A4%D0%BE%D1%80%D0%B4%D0%B0&amp;diff=1991"/>
		<updated>2015-06-25T14:12:53Z</updated>

		<summary type="html">&lt;p&gt;Daryin: /* Последовательная сложность алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Свойства и структура алгоритмов ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм Беллмана-Форда&#039;&#039;&#039;&amp;lt;ref&amp;gt;Bellman, Richard. “On a Routing Problem.” Quarterly of Applied Mathematics 16 (1958): 87–90.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Ford, L R. Network Flow Theory. Rand.org, RAND Corporation, 1958.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Moore, Edward F. “The Shortest Path Through a Maze,” International Symposium on the Theory of Switching, 285–92, 1959.&amp;lt;/ref&amp;gt; предназначен для решения [[Поиск кратчайшего пути от одной вершины (SSSP)|задачи поиска кратчайшего пути на графе]]. Для заданного ориентированного взвешенного графа алгоритм находит кратчайшие расстояния от выделенной вершины-источника до всех остальных вершин графа. Алгоритм Беллмана-Форда масштабируется хуже других алгоритмов решения указанной задачи (сложность &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt; против &amp;lt;math&amp;gt;O(m + n\ln n)&amp;lt;/math&amp;gt; у [[Алгоритм Дейкстры|алгоритма Дейкстры]]), однако его отличительной особенностью является применимость к графам с произвольными, в том числе отрицательными, весами.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание ===&lt;br /&gt;
Пусть задан граф &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; с весами рёбер &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt; и выделенной вершиной-источником &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;. Обозначим через &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt; кратчайшее расстояние от источника &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; до вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Алгоритм Беллмана-Форда ищет функцию &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt; как единственное решение уравнения&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
        d(v) = \min \{ d(w) + f(e) \mid e = (w, v) \in E \}, \quad \forall v \ne u,&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
с начальным условием &amp;lt;math&amp;gt;d(u) = 0&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основной операцией алгоритма является релаксация ребра: если &amp;lt;math&amp;gt;e = (w, v) \in E&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;d(v) &amp;gt; d(w) + f(e)&amp;lt;/math&amp;gt;, то производится присваивание &amp;lt;math&amp;gt;d(v) \leftarrow d(w) + f(e)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
Алгоритм последовательно уточняет значения функции &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* В самом начале производится присваивание &amp;lt;math&amp;gt;d(u) = 0&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;d(v) = \infty&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\forall v \ne u&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Далее происходит &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; итерация, в ходе каждой из которых производится релаксация всех рёбер графа.&lt;br /&gt;
&lt;br /&gt;
=== Описание схемы реализации последовательного алгоритма ===&lt;br /&gt;
Последовательный алгоритм реализуется следующим псевдокодом:&lt;br /&gt;
&lt;br /&gt;
 &#039;&#039;&#039;Входные данные&#039;&#039;&#039;:&lt;br /&gt;
   граф с вершинами &#039;&#039;V&#039;&#039;, рёбрами &#039;&#039;E&#039;&#039; с весами &#039;&#039;f&#039;&#039;(&#039;&#039;e&#039;&#039;);&lt;br /&gt;
   вершина-источник &#039;&#039;u&#039;&#039;.&lt;br /&gt;
 &#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: расстояния &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) до каждой вершины &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039; от вершины &#039;&#039;u&#039;&#039;.&lt;br /&gt;
 &lt;br /&gt;
 &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039; &#039;&#039;&#039;do&#039;&#039;&#039; &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) := ∞&lt;br /&gt;
 &#039;&#039;d&#039;&#039;(&#039;&#039;u&#039;&#039;) = 0&lt;br /&gt;
 &lt;br /&gt;
 &#039;&#039;&#039;for&#039;&#039;&#039; &#039;&#039;i&#039;&#039; &#039;&#039;&#039;from&#039;&#039;&#039; 1 &#039;&#039;&#039;to&#039;&#039;&#039; |&#039;&#039;V&#039;&#039;| - 1:&lt;br /&gt;
     &#039;&#039;&#039;for&#039;&#039;&#039; &#039;&#039;e&#039;&#039; = (&#039;&#039;w&#039;&#039;, &#039;&#039;v&#039;&#039;) ∈ &#039;&#039;E&#039;&#039;:&lt;br /&gt;
         &#039;&#039;&#039;if&#039;&#039;&#039; &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) &amp;gt; &#039;&#039;d&#039;&#039;(&#039;&#039;w&#039;&#039;) + &#039;&#039;f&#039;&#039;(&#039;&#039;e&#039;&#039;):&lt;br /&gt;
             &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) := &#039;&#039;d&#039;&#039;(&#039;&#039;w&#039;&#039;) + &#039;&#039;f&#039;&#039;(&#039;&#039;e&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Алгоритм выполняет &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; итерацию, на каждой из которых происходит релаксация &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; рёбер. Таким образом, общий объём работы составляет &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&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;
=== Свойства алгоритма===&lt;br /&gt;
&lt;br /&gt;
Алгоритм может распознавать наличие отрицательных циклов в графе. Ребро &amp;lt;math&amp;gt;e = (v, w)&amp;lt;/math&amp;gt; лежит на таком цикле, если вычисленные алгоритмом кратчайшие расстояния &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt; удовлетворяют условию&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
        d(v) + f(e) &amp;lt; d(w),&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
где &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt; – вес ребра &amp;lt;math&amp;gt;e&amp;lt;/math&amp;gt;. Условие может быть проверено для всех рёбер графа за время &amp;lt;math&amp;gt;O(m)&amp;lt;/math&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;
* C++: [http://www.boost.org/libs/graph/doc/ Boost Graph Library] (функция &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph/doc/bellman_ford_shortest.html bellman_ford_shortest]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Python: [https://networkx.github.io NetworkX] (функция &amp;lt;code&amp;gt;[http://networkx.github.io/documentation/networkx-1.9.1/reference/generated/networkx.algorithms.shortest_paths.weighted.bellman_ford.html bellman_ford]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Java: [http://jgrapht.org JGraphT] (класс &amp;lt;code&amp;gt;[http://jgrapht.org/javadoc/org/jgrapht/alg/BellmanFordShortestPath.html BellmanFordShortestPath]&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Daryin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%91%D0%B5%D0%BB%D0%BB%D0%BC%D0%B0%D0%BD%D0%B0-%D0%A4%D0%BE%D1%80%D0%B4%D0%B0&amp;diff=1990</id>
		<title>Алгоритм Беллмана-Форда</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%91%D0%B5%D0%BB%D0%BB%D0%BC%D0%B0%D0%BD%D0%B0-%D0%A4%D0%BE%D1%80%D0%B4%D0%B0&amp;diff=1990"/>
		<updated>2015-06-25T14:07:06Z</updated>

		<summary type="html">&lt;p&gt;Daryin: /* Описание схемы реализации последовательного алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Свойства и структура алгоритмов ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм Беллмана-Форда&#039;&#039;&#039;&amp;lt;ref&amp;gt;Bellman, Richard. “On a Routing Problem.” Quarterly of Applied Mathematics 16 (1958): 87–90.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Ford, L R. Network Flow Theory. Rand.org, RAND Corporation, 1958.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Moore, Edward F. “The Shortest Path Through a Maze,” International Symposium on the Theory of Switching, 285–92, 1959.&amp;lt;/ref&amp;gt; предназначен для решения [[Поиск кратчайшего пути от одной вершины (SSSP)|задачи поиска кратчайшего пути на графе]]. Для заданного ориентированного взвешенного графа алгоритм находит кратчайшие расстояния от выделенной вершины-источника до всех остальных вершин графа. Алгоритм Беллмана-Форда масштабируется хуже других алгоритмов решения указанной задачи (сложность &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt; против &amp;lt;math&amp;gt;O(m + n\ln n)&amp;lt;/math&amp;gt; у [[Алгоритм Дейкстры|алгоритма Дейкстры]]), однако его отличительной особенностью является применимость к графам с произвольными, в том числе отрицательными, весами.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание ===&lt;br /&gt;
Пусть задан граф &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; с весами рёбер &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt; и выделенной вершиной-источником &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;. Обозначим через &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt; кратчайшее расстояние от источника &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; до вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Алгоритм Беллмана-Форда ищет функцию &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt; как единственное решение уравнения&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
        d(v) = \min \{ d(w) + f(e) \mid e = (w, v) \in E \}, \quad \forall v \ne u,&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
с начальным условием &amp;lt;math&amp;gt;d(u) = 0&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основной операцией алгоритма является релаксация ребра: если &amp;lt;math&amp;gt;e = (w, v) \in E&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;d(v) &amp;gt; d(w) + f(e)&amp;lt;/math&amp;gt;, то производится присваивание &amp;lt;math&amp;gt;d(v) \leftarrow d(w) + f(e)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
Алгоритм последовательно уточняет значения функции &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* В самом начале производится присваивание &amp;lt;math&amp;gt;d(u) = 0&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;d(v) = \infty&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\forall v \ne u&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Далее происходит &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; итерация, в ходе каждой из которых производится релаксация всех рёбер графа.&lt;br /&gt;
&lt;br /&gt;
=== Описание схемы реализации последовательного алгоритма ===&lt;br /&gt;
Последовательный алгоритм реализуется следующим псевдокодом:&lt;br /&gt;
&lt;br /&gt;
 &#039;&#039;&#039;Входные данные&#039;&#039;&#039;:&lt;br /&gt;
   граф с вершинами &#039;&#039;V&#039;&#039;, рёбрами &#039;&#039;E&#039;&#039; с весами &#039;&#039;f&#039;&#039;(&#039;&#039;e&#039;&#039;);&lt;br /&gt;
   вершина-источник &#039;&#039;u&#039;&#039;.&lt;br /&gt;
 &#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: расстояния &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) до каждой вершины &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039; от вершины &#039;&#039;u&#039;&#039;.&lt;br /&gt;
 &lt;br /&gt;
 &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039; &#039;&#039;&#039;do&#039;&#039;&#039; &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) := ∞&lt;br /&gt;
 &#039;&#039;d&#039;&#039;(&#039;&#039;u&#039;&#039;) = 0&lt;br /&gt;
 &lt;br /&gt;
 &#039;&#039;&#039;for&#039;&#039;&#039; &#039;&#039;i&#039;&#039; &#039;&#039;&#039;from&#039;&#039;&#039; 1 &#039;&#039;&#039;to&#039;&#039;&#039; |&#039;&#039;V&#039;&#039;| - 1:&lt;br /&gt;
     &#039;&#039;&#039;for&#039;&#039;&#039; &#039;&#039;e&#039;&#039; = (&#039;&#039;w&#039;&#039;, &#039;&#039;v&#039;&#039;) ∈ &#039;&#039;E&#039;&#039;:&lt;br /&gt;
         &#039;&#039;&#039;if&#039;&#039;&#039; &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) &amp;gt; &#039;&#039;d&#039;&#039;(&#039;&#039;w&#039;&#039;) + &#039;&#039;f&#039;&#039;(&#039;&#039;e&#039;&#039;):&lt;br /&gt;
             &#039;&#039;d&#039;&#039;(&#039;&#039;v&#039;&#039;) := &#039;&#039;d&#039;&#039;(&#039;&#039;w&#039;&#039;) + &#039;&#039;f&#039;&#039;(&#039;&#039;e&#039;&#039;)&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;math&amp;gt;e = (v, w)&amp;lt;/math&amp;gt; лежит на таком цикле, если вычисленные алгоритмом кратчайшие расстояния &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt; удовлетворяют условию&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
        d(v) + f(e) &amp;lt; d(w),&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
где &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt; – вес ребра &amp;lt;math&amp;gt;e&amp;lt;/math&amp;gt;. Условие может быть проверено для всех рёбер графа за время &amp;lt;math&amp;gt;O(m)&amp;lt;/math&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;
* C++: [http://www.boost.org/libs/graph/doc/ Boost Graph Library] (функция &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph/doc/bellman_ford_shortest.html bellman_ford_shortest]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Python: [https://networkx.github.io NetworkX] (функция &amp;lt;code&amp;gt;[http://networkx.github.io/documentation/networkx-1.9.1/reference/generated/networkx.algorithms.shortest_paths.weighted.bellman_ford.html bellman_ford]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Java: [http://jgrapht.org JGraphT] (класс &amp;lt;code&amp;gt;[http://jgrapht.org/javadoc/org/jgrapht/alg/BellmanFordShortestPath.html BellmanFordShortestPath]&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Daryin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%91%D0%B5%D0%BB%D0%BB%D0%BC%D0%B0%D0%BD%D0%B0-%D0%A4%D0%BE%D1%80%D0%B4%D0%B0&amp;diff=1989</id>
		<title>Алгоритм Беллмана-Форда</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%91%D0%B5%D0%BB%D0%BB%D0%BC%D0%B0%D0%BD%D0%B0-%D0%A4%D0%BE%D1%80%D0%B4%D0%B0&amp;diff=1989"/>
		<updated>2015-06-25T13:57:46Z</updated>

		<summary type="html">&lt;p&gt;Daryin: /* Макроструктура алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Свойства и структура алгоритмов ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм Беллмана-Форда&#039;&#039;&#039;&amp;lt;ref&amp;gt;Bellman, Richard. “On a Routing Problem.” Quarterly of Applied Mathematics 16 (1958): 87–90.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Ford, L R. Network Flow Theory. Rand.org, RAND Corporation, 1958.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Moore, Edward F. “The Shortest Path Through a Maze,” International Symposium on the Theory of Switching, 285–92, 1959.&amp;lt;/ref&amp;gt; предназначен для решения [[Поиск кратчайшего пути от одной вершины (SSSP)|задачи поиска кратчайшего пути на графе]]. Для заданного ориентированного взвешенного графа алгоритм находит кратчайшие расстояния от выделенной вершины-источника до всех остальных вершин графа. Алгоритм Беллмана-Форда масштабируется хуже других алгоритмов решения указанной задачи (сложность &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt; против &amp;lt;math&amp;gt;O(m + n\ln n)&amp;lt;/math&amp;gt; у [[Алгоритм Дейкстры|алгоритма Дейкстры]]), однако его отличительной особенностью является применимость к графам с произвольными, в том числе отрицательными, весами.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание ===&lt;br /&gt;
Пусть задан граф &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; с весами рёбер &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt; и выделенной вершиной-источником &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;. Обозначим через &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt; кратчайшее расстояние от источника &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; до вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Алгоритм Беллмана-Форда ищет функцию &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt; как единственное решение уравнения&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
        d(v) = \min \{ d(w) + f(e) \mid e = (w, v) \in E \}, \quad \forall v \ne u,&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
с начальным условием &amp;lt;math&amp;gt;d(u) = 0&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основной операцией алгоритма является релаксация ребра: если &amp;lt;math&amp;gt;e = (w, v) \in E&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;d(v) &amp;gt; d(w) + f(e)&amp;lt;/math&amp;gt;, то производится присваивание &amp;lt;math&amp;gt;d(v) \leftarrow d(w) + f(e)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
Алгоритм последовательно уточняет значения функции &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* В самом начале производится присваивание &amp;lt;math&amp;gt;d(u) = 0&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;d(v) = \infty&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\forall v \ne u&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Далее происходит &amp;lt;math&amp;gt;n-1&amp;lt;/math&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;
Алгоритм может распознавать наличие отрицательных циклов в графе. Ребро &amp;lt;math&amp;gt;e = (v, w)&amp;lt;/math&amp;gt; лежит на таком цикле, если вычисленные алгоритмом кратчайшие расстояния &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt; удовлетворяют условию&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
        d(v) + f(e) &amp;lt; d(w),&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
где &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt; – вес ребра &amp;lt;math&amp;gt;e&amp;lt;/math&amp;gt;. Условие может быть проверено для всех рёбер графа за время &amp;lt;math&amp;gt;O(m)&amp;lt;/math&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;
* C++: [http://www.boost.org/libs/graph/doc/ Boost Graph Library] (функция &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph/doc/bellman_ford_shortest.html bellman_ford_shortest]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Python: [https://networkx.github.io NetworkX] (функция &amp;lt;code&amp;gt;[http://networkx.github.io/documentation/networkx-1.9.1/reference/generated/networkx.algorithms.shortest_paths.weighted.bellman_ford.html bellman_ford]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Java: [http://jgrapht.org JGraphT] (класс &amp;lt;code&amp;gt;[http://jgrapht.org/javadoc/org/jgrapht/alg/BellmanFordShortestPath.html BellmanFordShortestPath]&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Daryin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%91%D0%B5%D0%BB%D0%BB%D0%BC%D0%B0%D0%BD%D0%B0-%D0%A4%D0%BE%D1%80%D0%B4%D0%B0&amp;diff=1988</id>
		<title>Алгоритм Беллмана-Форда</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%91%D0%B5%D0%BB%D0%BB%D0%BC%D0%B0%D0%BD%D0%B0-%D0%A4%D0%BE%D1%80%D0%B4%D0%B0&amp;diff=1988"/>
		<updated>2015-06-25T13:54:12Z</updated>

		<summary type="html">&lt;p&gt;Daryin: /* Вычислительное ядро алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Свойства и структура алгоритмов ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм Беллмана-Форда&#039;&#039;&#039;&amp;lt;ref&amp;gt;Bellman, Richard. “On a Routing Problem.” Quarterly of Applied Mathematics 16 (1958): 87–90.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Ford, L R. Network Flow Theory. Rand.org, RAND Corporation, 1958.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Moore, Edward F. “The Shortest Path Through a Maze,” International Symposium on the Theory of Switching, 285–92, 1959.&amp;lt;/ref&amp;gt; предназначен для решения [[Поиск кратчайшего пути от одной вершины (SSSP)|задачи поиска кратчайшего пути на графе]]. Для заданного ориентированного взвешенного графа алгоритм находит кратчайшие расстояния от выделенной вершины-источника до всех остальных вершин графа. Алгоритм Беллмана-Форда масштабируется хуже других алгоритмов решения указанной задачи (сложность &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt; против &amp;lt;math&amp;gt;O(m + n\ln n)&amp;lt;/math&amp;gt; у [[Алгоритм Дейкстры|алгоритма Дейкстры]]), однако его отличительной особенностью является применимость к графам с произвольными, в том числе отрицательными, весами.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание ===&lt;br /&gt;
Пусть задан граф &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; с весами рёбер &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt; и выделенной вершиной-источником &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;. Обозначим через &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt; кратчайшее расстояние от источника &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; до вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Алгоритм Беллмана-Форда ищет функцию &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt; как единственное решение уравнения&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
        d(v) = \min \{ d(w) + f(e) \mid e = (w, v) \in E \}, \quad \forall v \ne u,&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
с начальным условием &amp;lt;math&amp;gt;d(u) = 0&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основной операцией алгоритма является релаксация ребра: если &amp;lt;math&amp;gt;e = (w, v) \in E&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;d(v) &amp;gt; d(w) + f(e)&amp;lt;/math&amp;gt;, то производится присваивание &amp;lt;math&amp;gt;d(v) \leftarrow d(w) + f(e)&amp;lt;/math&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;math&amp;gt;e = (v, w)&amp;lt;/math&amp;gt; лежит на таком цикле, если вычисленные алгоритмом кратчайшие расстояния &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt; удовлетворяют условию&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
        d(v) + f(e) &amp;lt; d(w),&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
где &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt; – вес ребра &amp;lt;math&amp;gt;e&amp;lt;/math&amp;gt;. Условие может быть проверено для всех рёбер графа за время &amp;lt;math&amp;gt;O(m)&amp;lt;/math&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;
* C++: [http://www.boost.org/libs/graph/doc/ Boost Graph Library] (функция &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph/doc/bellman_ford_shortest.html bellman_ford_shortest]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Python: [https://networkx.github.io NetworkX] (функция &amp;lt;code&amp;gt;[http://networkx.github.io/documentation/networkx-1.9.1/reference/generated/networkx.algorithms.shortest_paths.weighted.bellman_ford.html bellman_ford]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Java: [http://jgrapht.org JGraphT] (класс &amp;lt;code&amp;gt;[http://jgrapht.org/javadoc/org/jgrapht/alg/BellmanFordShortestPath.html BellmanFordShortestPath]&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Daryin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%91%D0%B5%D0%BB%D0%BB%D0%BC%D0%B0%D0%BD%D0%B0-%D0%A4%D0%BE%D1%80%D0%B4%D0%B0&amp;diff=1987</id>
		<title>Алгоритм Беллмана-Форда</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%91%D0%B5%D0%BB%D0%BB%D0%BC%D0%B0%D0%BD%D0%B0-%D0%A4%D0%BE%D1%80%D0%B4%D0%B0&amp;diff=1987"/>
		<updated>2015-06-25T13:49:57Z</updated>

		<summary type="html">&lt;p&gt;Daryin: /* Вычислительное ядро алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Свойства и структура алгоритмов ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм Беллмана-Форда&#039;&#039;&#039;&amp;lt;ref&amp;gt;Bellman, Richard. “On a Routing Problem.” Quarterly of Applied Mathematics 16 (1958): 87–90.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Ford, L R. Network Flow Theory. Rand.org, RAND Corporation, 1958.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Moore, Edward F. “The Shortest Path Through a Maze,” International Symposium on the Theory of Switching, 285–92, 1959.&amp;lt;/ref&amp;gt; предназначен для решения [[Поиск кратчайшего пути от одной вершины (SSSP)|задачи поиска кратчайшего пути на графе]]. Для заданного ориентированного взвешенного графа алгоритм находит кратчайшие расстояния от выделенной вершины-источника до всех остальных вершин графа. Алгоритм Беллмана-Форда масштабируется хуже других алгоритмов решения указанной задачи (сложность &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt; против &amp;lt;math&amp;gt;O(m + n\ln n)&amp;lt;/math&amp;gt; у [[Алгоритм Дейкстры|алгоритма Дейкстры]]), однако его отличительной особенностью является применимость к графам с произвольными, в том числе отрицательными, весами.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание ===&lt;br /&gt;
Пусть задан граф &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; с весами рёбер &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt; и выделенной вершиной-источником &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;. Обозначим через &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt; кратчайшее расстояние от источника &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; до вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Алгоритм Беллмана-Форда ищет функцию &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt; как единственное решение уравнения&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
        d(v) = \min \{ d(w) + f(e) \mid e = (w, v) \in E \}, \quad \forall v \ne u,&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
с начальным условием &amp;lt;math&amp;gt;d(u) = 0&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основной операцией алгоритма является релаксация ядра: если &amp;lt;math&amp;gt;e = (w, v) \in E&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;d(v) &amp;gt; d(w) + f(e)&amp;lt;/math&amp;gt;, то производится присваивание &amp;lt;math&amp;gt;d(v) \leftarrow d(w) + f(e)&amp;lt;/math&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;math&amp;gt;e = (v, w)&amp;lt;/math&amp;gt; лежит на таком цикле, если вычисленные алгоритмом кратчайшие расстояния &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt; удовлетворяют условию&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
        d(v) + f(e) &amp;lt; d(w),&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
где &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt; – вес ребра &amp;lt;math&amp;gt;e&amp;lt;/math&amp;gt;. Условие может быть проверено для всех рёбер графа за время &amp;lt;math&amp;gt;O(m)&amp;lt;/math&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;
* C++: [http://www.boost.org/libs/graph/doc/ Boost Graph Library] (функция &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph/doc/bellman_ford_shortest.html bellman_ford_shortest]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Python: [https://networkx.github.io NetworkX] (функция &amp;lt;code&amp;gt;[http://networkx.github.io/documentation/networkx-1.9.1/reference/generated/networkx.algorithms.shortest_paths.weighted.bellman_ford.html bellman_ford]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Java: [http://jgrapht.org JGraphT] (класс &amp;lt;code&amp;gt;[http://jgrapht.org/javadoc/org/jgrapht/alg/BellmanFordShortestPath.html BellmanFordShortestPath]&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Daryin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%91%D0%B5%D0%BB%D0%BB%D0%BC%D0%B0%D0%BD%D0%B0-%D0%A4%D0%BE%D1%80%D0%B4%D0%B0&amp;diff=1986</id>
		<title>Алгоритм Беллмана-Форда</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%91%D0%B5%D0%BB%D0%BB%D0%BC%D0%B0%D0%BD%D0%B0-%D0%A4%D0%BE%D1%80%D0%B4%D0%B0&amp;diff=1986"/>
		<updated>2015-06-25T13:45:51Z</updated>

		<summary type="html">&lt;p&gt;Daryin: /* Математическое описание */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Свойства и структура алгоритмов ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм Беллмана-Форда&#039;&#039;&#039;&amp;lt;ref&amp;gt;Bellman, Richard. “On a Routing Problem.” Quarterly of Applied Mathematics 16 (1958): 87–90.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Ford, L R. Network Flow Theory. Rand.org, RAND Corporation, 1958.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Moore, Edward F. “The Shortest Path Through a Maze,” International Symposium on the Theory of Switching, 285–92, 1959.&amp;lt;/ref&amp;gt; предназначен для решения [[Поиск кратчайшего пути от одной вершины (SSSP)|задачи поиска кратчайшего пути на графе]]. Для заданного ориентированного взвешенного графа алгоритм находит кратчайшие расстояния от выделенной вершины-источника до всех остальных вершин графа. Алгоритм Беллмана-Форда масштабируется хуже других алгоритмов решения указанной задачи (сложность &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt; против &amp;lt;math&amp;gt;O(m + n\ln n)&amp;lt;/math&amp;gt; у [[Алгоритм Дейкстры|алгоритма Дейкстры]]), однако его отличительной особенностью является применимость к графам с произвольными, в том числе отрицательными, весами.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание ===&lt;br /&gt;
Пусть задан граф &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; с весами рёбер &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt; и выделенной вершиной-источником &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt;. Обозначим через &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt; кратчайшее расстояние от источника &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; до вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Алгоритм Беллмана-Форда ищет функцию &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt; как единственное решение уравнения&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
        d(v) = \min \{ d(w) + f(e) \mid e = (w, v) \in E \}, \quad \forall v \ne u,&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
с начальным условием &amp;lt;math&amp;gt;d(u) = 0&amp;lt;/math&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;
&lt;br /&gt;
Алгоритм может распознавать наличие отрицательных циклов в графе. Ребро &amp;lt;math&amp;gt;e = (v, w)&amp;lt;/math&amp;gt; лежит на таком цикле, если вычисленные алгоритмом кратчайшие расстояния &amp;lt;math&amp;gt;d(v)&amp;lt;/math&amp;gt; удовлетворяют условию&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
        d(v) + f(e) &amp;lt; d(w),&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
где &amp;lt;math&amp;gt;f(e)&amp;lt;/math&amp;gt; – вес ребра &amp;lt;math&amp;gt;e&amp;lt;/math&amp;gt;. Условие может быть проверено для всех рёбер графа за время &amp;lt;math&amp;gt;O(m)&amp;lt;/math&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;
* C++: [http://www.boost.org/libs/graph/doc/ Boost Graph Library] (функция &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph/doc/bellman_ford_shortest.html bellman_ford_shortest]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Python: [https://networkx.github.io NetworkX] (функция &amp;lt;code&amp;gt;[http://networkx.github.io/documentation/networkx-1.9.1/reference/generated/networkx.algorithms.shortest_paths.weighted.bellman_ford.html bellman_ford]&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Java: [http://jgrapht.org JGraphT] (класс &amp;lt;code&amp;gt;[http://jgrapht.org/javadoc/org/jgrapht/alg/BellmanFordShortestPath.html BellmanFordShortestPath]&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Daryin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%93%D0%BE%D0%BF%D0%BA%D1%80%D0%BE%D1%84%D1%82%D0%B0-%D0%9A%D0%B0%D1%80%D0%BF%D0%B0&amp;diff=1985</id>
		<title>Алгоритм Гопкрофта-Карпа</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%93%D0%BE%D0%BF%D0%BA%D1%80%D0%BE%D1%84%D1%82%D0%B0-%D0%9A%D0%B0%D1%80%D0%BF%D0%B0&amp;diff=1985"/>
		<updated>2015-06-25T12:19:19Z</updated>

		<summary type="html">&lt;p&gt;Daryin: /* Описание схемы реализации последовательного алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Свойства и структура алгоритмов ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм Гопкрофта-Карпа&#039;&#039;&#039;&amp;lt;ref&amp;gt;Hopcroft, John E, and Richard M Karp. “An $N^{5/2} $ Algorithm for Maximum Matchings in Bipartite Graphs.” SIAM Journal on Computing 2, no. 4 (1973): 225–31. doi:10.1137/0202019.&amp;lt;/ref&amp;gt; предназначен для решения [[Задача о назначениях|задачи о назначениях]] в случае единичных цен. По заданному двудольному графу алгоритм находит максимальное паросочетание за время &amp;lt;math&amp;gt;O(m \sqrt{n})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание ===&lt;br /&gt;
Граф &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; называется &#039;&#039;двудольным&#039;&#039;, если его вершины можно разделить на две части &amp;lt;math&amp;gt;V = X \sqcup Y&amp;lt;/math&amp;gt;, так что каждое ребро &amp;lt;math&amp;gt;e \in E&amp;lt;/math&amp;gt; соединяет вершины из &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt;. Набор рёбер &amp;lt;math&amp;gt;M \subseteq E&amp;lt;/math&amp;gt; называется &#039;&#039;паросочетанием&#039;&#039;, если каждая вершина &amp;lt;math&amp;gt;v \in V&amp;lt;/math&amp;gt; принадлежит не более чем одному ребру из &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt;. Требуется найти &#039;&#039;&#039;максимальное паросочетание&#039;&#039;&#039; – паросочетание с максимально возможным числом рёбер.&lt;br /&gt;
&lt;br /&gt;
Вершина &amp;lt;math&amp;gt;v \in V&amp;lt;/math&amp;gt; называется свободной, если она не принадлежит ни одному ребру из &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt;. Простой путь&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
        P: e_1 = (v_1, v_2), e_2 = (v_2, v_3), \ldots, e_{2k-1} = (v_{2k-1}, v_{2k})&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
называется &#039;&#039;увеличивающей цепью&#039;&#039;, если его концы &amp;lt;math&amp;gt;v_1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v_{2k}&amp;lt;/math&amp;gt; – свободные вершины, а каждое второе ребро принадлежит текущему паросочетанию:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
        e_2, e_4, \ldots, e_{2k-2} \in M.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
Паросочетание &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; является максимальным, если для него не существует увеличивающей цепи.&lt;br /&gt;
&lt;br /&gt;
Множество &amp;lt;math&amp;gt;M&#039;&amp;lt;/math&amp;gt;, полученное заменой в &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; рёбер &amp;lt;math&amp;gt;e_2, e_4, \ldots, e_{2k-2}&amp;lt;/math&amp;gt; на рёбра &amp;lt;math&amp;gt;e_1, e_3, \ldots, e_{2k-1}&amp;lt;/math&amp;gt;, является паросочетанием и содержит на одно ребро больше, чем &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt;. Множество &amp;lt;math&amp;gt;M&#039; = M \mathop{\Delta} P&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\mathop{\Delta}&amp;lt;/math&amp;gt; обозначает операцию симметрической разности множеств.&lt;br /&gt;
&lt;br /&gt;
На каждом шаге алгоритма Гопкрофта-Карпа строится максимальное по включению множество кратчайших увеличивающих цепей &amp;lt;math&amp;gt;\{ P_1, \ldots, P_q \}&amp;lt;/math&amp;gt;, не пересекающихся по вершинам, и текущее паросочетание &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; заменяется на &amp;lt;math&amp;gt;M = M \mathop{\Delta} P_1 \mathop{\Delta} P_2 \mathop{\Delta} \dots \mathop{\Delta} P_q&amp;lt;/math&amp;gt;. Гарантируется, что число шагов не превышает &amp;lt;math&amp;gt;2 (\sqrt{\mu} + 1) = O(\sqrt{n})&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\mu&amp;lt;/math&amp;gt; – число рёбер максимального паросочетания.&lt;br /&gt;
&lt;br /&gt;
Для поиска максимального множества кратчайших увеличивающих цепей применяется [[Поиск в ширину (BFS)|поиск в ширину]] с последующим [[Поиск в глубину (DFS)|поиском в глубину]].&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основная вычислительная сложность алгоритма приходится на операции [[Поиск в ширину (BFS)|поиска в ширину]] и [[Поиск в глубину (DFS)|поиска в глубину]].&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
# Выбрать начальное паросочетание &amp;lt;math&amp;gt;M_0&amp;lt;/math&amp;gt; (например, &amp;lt;math&amp;gt;M_0 = \emptyset&amp;lt;/math&amp;gt; или паросочетание, полученное жадным алгоритмом).&lt;br /&gt;
# Вычислить максимальное по включение множество кратчайших увеличивающих цепей &amp;lt;math&amp;gt;\{ P_1, \ldots, P_q \}&amp;lt;/math&amp;gt;, не пересекающихся по вершинам. Если улучшающих цепей не существует, остановиться: текущее паросочетание &amp;lt;math&amp;gt;M_i&amp;lt;/math&amp;gt; является максимальным.&lt;br /&gt;
# Составить новое паросочетание &amp;lt;math&amp;gt;M_{i + 1} = M_i \mathop{\Delta} P_1 \mathop{\Delta} P_2 \mathop{\Delta} \dots \mathop{\Delta} P_q&amp;lt;/math&amp;gt; и перейти к шагу 2.&lt;br /&gt;
&lt;br /&gt;
=== Описание схемы реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Опишем, каким образом ищется максимальное множество кратчайших увеличивающих цепей на втором шаге алгоритма.&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; – двудольный граф с разбиением вершин &amp;lt;math&amp;gt;V = X \sqcup Y&amp;lt;/math&amp;gt;, а &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; – текущее паросочетание в нём. Обозначим через &amp;lt;math&amp;gt;X_f \subseteq X&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;Y_f \subseteq Y&amp;lt;/math&amp;gt; соответствующие множества свободных вершин.&lt;br /&gt;
&lt;br /&gt;
# Если одно из множеств &amp;lt;math&amp;gt;X_f&amp;lt;/math&amp;gt; или &amp;lt;math&amp;gt;Y_f&amp;lt;/math&amp;gt; пусто, то увеличивающих цепей не существует.&lt;br /&gt;
# Ориентируем рёбра &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt; следующим образом:&lt;br /&gt;
#* рёбра из &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; идут из &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt;;&lt;br /&gt;
#* все остальные рёбра идут из &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Выполним один [[Поиск в ширину (BFS)|поиск в ширину]] на полученном ориентированном графе, двигаясь от множества вершин &amp;lt;math&amp;gt;X_f&amp;lt;/math&amp;gt; в обратном направлении.&lt;br /&gt;
#* Поиск останавливается на том шаге, на котором будет посещена хотя бы одна из вершин в &amp;lt;math&amp;gt;Y_f&amp;lt;/math&amp;gt;.&lt;br /&gt;
#* Если поиск будет завершён без посещения одной из вершин &amp;lt;math&amp;gt;Y_f&amp;lt;/math&amp;gt;, то увеличивающих цепей не существует.&lt;br /&gt;
#* Пометим вершины и рёбра, посещённые в ходе поиска в ширину.&lt;br /&gt;
# Для каждой из свободных вершин &amp;lt;math&amp;gt;y \in Y_f&amp;lt;/math&amp;gt; выполним [[Поиск в глубину (DFS)|поиск в глубину]] в прямом направлении, двигаясь только по помеченным рёбрам и вершинам.&lt;br /&gt;
#* Снимем пометку с посещённых рёбер и вершин.&lt;br /&gt;
#* Если на очередном шаге посещена вершина &amp;lt;math&amp;gt;x \in X_f&amp;lt;/math&amp;gt;, то поиск останавливается, а текущее содержимое стека определяет кратчайшую увеличивающую цепь от &amp;lt;math&amp;gt;y&amp;lt;/math&amp;gt; к &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt;.&lt;br /&gt;
#* Если поиск в глубину завершается без посещения множества &amp;lt;math&amp;gt;X_f&amp;lt;/math&amp;gt;, то для данной вершины &amp;lt;math&amp;gt;y&amp;lt;/math&amp;gt; увеличивающей цепи не существует, и необходимо перейти к следующей свободной вершине.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Алгоритм выполняет не более &amp;lt;math&amp;gt;O(\sqrt{n})&amp;lt;/math&amp;gt; шагов, на каждом из которых производятся поиск в ширину и поиск в глубину сложностью &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt;. Таким образом, общая сложность составляет &amp;lt;math&amp;gt;O(m \sqrt{n})&amp;lt;/math&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;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
* Java: [http://jgrapht.org JGraphT] (класс &amp;lt;code&amp;gt;[http://jgrapht.org/javadoc/org/jgrapht/alg/HopcroftKarpBipartiteMatching.html HopcroftKarpBipartiteMatching]&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Daryin</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%93%D0%BE%D0%BF%D0%BA%D1%80%D0%BE%D1%84%D1%82%D0%B0-%D0%9A%D0%B0%D1%80%D0%BF%D0%B0&amp;diff=1984</id>
		<title>Алгоритм Гопкрофта-Карпа</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%93%D0%BE%D0%BF%D0%BA%D1%80%D0%BE%D1%84%D1%82%D0%B0-%D0%9A%D0%B0%D1%80%D0%BF%D0%B0&amp;diff=1984"/>
		<updated>2015-06-25T11:26:09Z</updated>

		<summary type="html">&lt;p&gt;Daryin: /* Макроструктура алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Свойства и структура алгоритмов ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм Гопкрофта-Карпа&#039;&#039;&#039;&amp;lt;ref&amp;gt;Hopcroft, John E, and Richard M Karp. “An $N^{5/2} $ Algorithm for Maximum Matchings in Bipartite Graphs.” SIAM Journal on Computing 2, no. 4 (1973): 225–31. doi:10.1137/0202019.&amp;lt;/ref&amp;gt; предназначен для решения [[Задача о назначениях|задачи о назначениях]] в случае единичных цен. По заданному двудольному графу алгоритм находит максимальное паросочетание за время &amp;lt;math&amp;gt;O(m \sqrt{n})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание ===&lt;br /&gt;
Граф &amp;lt;math&amp;gt;G = (V, E)&amp;lt;/math&amp;gt; называется &#039;&#039;двудольным&#039;&#039;, если его вершины можно разделить на две части &amp;lt;math&amp;gt;V = X \sqcup Y&amp;lt;/math&amp;gt;, так что каждое ребро &amp;lt;math&amp;gt;e \in E&amp;lt;/math&amp;gt; соединяет вершины из &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt;. Набор рёбер &amp;lt;math&amp;gt;M \subseteq E&amp;lt;/math&amp;gt; называется &#039;&#039;паросочетанием&#039;&#039;, если каждая вершина &amp;lt;math&amp;gt;v \in V&amp;lt;/math&amp;gt; принадлежит не более чем одному ребру из &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt;. Требуется найти &#039;&#039;&#039;максимальное паросочетание&#039;&#039;&#039; – паросочетание с максимально возможным числом рёбер.&lt;br /&gt;
&lt;br /&gt;
Вершина &amp;lt;math&amp;gt;v \in V&amp;lt;/math&amp;gt; называется свободной, если она не принадлежит ни одному ребру из &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt;. Простой путь&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
        P: e_1 = (v_1, v_2), e_2 = (v_2, v_3), \ldots, e_{2k-1} = (v_{2k-1}, v_{2k})&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
называется &#039;&#039;увеличивающей цепью&#039;&#039;, если его концы &amp;lt;math&amp;gt;v_1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v_{2k}&amp;lt;/math&amp;gt; – свободные вершины, а каждое второе ребро принадлежит текущему паросочетанию:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
        e_2, e_4, \ldots, e_{2k-2} \in M.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
Паросочетание &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; является максимальным, если для него не существует увеличивающей цепи.&lt;br /&gt;
&lt;br /&gt;
Множество &amp;lt;math&amp;gt;M&#039;&amp;lt;/math&amp;gt;, полученное заменой в &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; рёбер &amp;lt;math&amp;gt;e_2, e_4, \ldots, e_{2k-2}&amp;lt;/math&amp;gt; на рёбра &amp;lt;math&amp;gt;e_1, e_3, \ldots, e_{2k-1}&amp;lt;/math&amp;gt;, является паросочетанием и содержит на одно ребро больше, чем &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt;. Множество &amp;lt;math&amp;gt;M&#039; = M \mathop{\Delta} P&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\mathop{\Delta}&amp;lt;/math&amp;gt; обозначает операцию симметрической разности множеств.&lt;br /&gt;
&lt;br /&gt;
На каждом шаге алгоритма Гопкрофта-Карпа строится максимальное по включению множество кратчайших увеличивающих цепей &amp;lt;math&amp;gt;\{ P_1, \ldots, P_q \}&amp;lt;/math&amp;gt;, не пересекающихся по вершинам, и текущее паросочетание &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; заменяется на &amp;lt;math&amp;gt;M = M \mathop{\Delta} P_1 \mathop{\Delta} P_2 \mathop{\Delta} \dots \mathop{\Delta} P_q&amp;lt;/math&amp;gt;. Гарантируется, что число шагов не превышает &amp;lt;math&amp;gt;2 (\sqrt{\mu} + 1) = O(\sqrt{n})&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\mu&amp;lt;/math&amp;gt; – число рёбер максимального паросочетания.&lt;br /&gt;
&lt;br /&gt;
Для поиска максимального множества кратчайших увеличивающих цепей применяется [[Поиск в ширину (BFS)|поиск в ширину]] с последующим [[Поиск в глубину (DFS)|поиском в глубину]].&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Основная вычислительная сложность алгоритма приходится на операции [[Поиск в ширину (BFS)|поиска в ширину]] и [[Поиск в глубину (DFS)|поиска в глубину]].&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
# Выбрать начальное паросочетание &amp;lt;math&amp;gt;M_0&amp;lt;/math&amp;gt; (например, &amp;lt;math&amp;gt;M_0 = \emptyset&amp;lt;/math&amp;gt; или паросочетание, полученное жадным алгоритмом).&lt;br /&gt;
# Вычислить максимальное по включение множество кратчайших увеличивающих цепей &amp;lt;math&amp;gt;\{ P_1, \ldots, P_q \}&amp;lt;/math&amp;gt;, не пересекающихся по вершинам. Если улучшающих цепей не существует, остановиться: текущее паросочетание &amp;lt;math&amp;gt;M_i&amp;lt;/math&amp;gt; является максимальным.&lt;br /&gt;
# Составить новое паросочетание &amp;lt;math&amp;gt;M_{i + 1} = M_i \mathop{\Delta} P_1 \mathop{\Delta} P_2 \mathop{\Delta} \dots \mathop{\Delta} P_q&amp;lt;/math&amp;gt; и перейти к шагу 2.&lt;br /&gt;
&lt;br /&gt;
=== Описание схемы реализации последовательного алгоритма ===&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Алгоритм выполняет не более &amp;lt;math&amp;gt;O(\sqrt{n})&amp;lt;/math&amp;gt; шагов, на каждом из которых производятся поиск в ширину и поиск в глубину сложностью &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt;. Таким образом, общая сложность составляет &amp;lt;math&amp;gt;O(m \sqrt{n})&amp;lt;/math&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;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
* Java: [http://jgrapht.org JGraphT] (класс &amp;lt;code&amp;gt;[http://jgrapht.org/javadoc/org/jgrapht/alg/HopcroftKarpBipartiteMatching.html HopcroftKarpBipartiteMatching]&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Daryin</name></author>
	</entry>
</feed>