<?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=Allocator</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=Allocator"/>
	<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/Allocator"/>
	<updated>2026-10-22T18:03:00Z</updated>
	<subtitle>Вклад</subtitle>
	<generator>MediaWiki 1.43.5</generator>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Allocator/%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D1%80%D0%B5%D0%BA%D1%83%D1%80%D1%81%D0%B8%D0%B2%D0%BD%D0%BE%D0%B9_%D0%BA%D0%BE%D0%BE%D1%80%D0%B4%D0%B8%D0%BD%D0%B0%D1%82%D0%BD%D0%BE%D0%B9_%D0%B1%D0%B8%D1%81%D0%B5%D0%BA%D1%86%D0%B8%D0%B8&amp;diff=22387</id>
		<title>Участник:Allocator/Метод рекурсивной координатной бисекции</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Allocator/%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D1%80%D0%B5%D0%BA%D1%83%D1%80%D1%81%D0%B8%D0%B2%D0%BD%D0%BE%D0%B9_%D0%BA%D0%BE%D0%BE%D1%80%D0%B4%D0%B8%D0%BD%D0%B0%D1%82%D0%BD%D0%BE%D0%B9_%D0%B1%D0%B8%D1%81%D0%B5%D0%BA%D1%86%D0%B8%D0%B8&amp;diff=22387"/>
		<updated>2017-02-07T11:17:27Z</updated>

		<summary type="html">&lt;p&gt;Allocator: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Авторы: Долгов Борис (624), Галуза Дмитрий (624)&lt;br /&gt;
= Свойства и структура алгоритмов =&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
Декомпозиция графа — это разбиение множества вершин графа на подмножества, для обработки каждого из подмножеств на отдельном вычислительном узле.&lt;br /&gt;
Декомпозиция больших графов (то есть графов, объём описания которых превышает память, доступную одному узлу) происходит в два этапа: выполнение первоначального разбиения вершин графа по вычислительным процессорам, затем — выполнение одного из параллельных алгоритмов декомпозиции. Алгоритм координатной рекурсивной бисекции даёт хорошее первичное распределение узлов сетки по сравнению с простым распределением узлов по номерам и, как правило, используется как первый шаг более сложных алгоритмов &amp;lt;ref&amp;gt;Якобовский М.В. Введение в параллельные методы решения задач: Учебное пособие / Предисл.: В. А. Садовничий. – М.: Издательство Московского университета, 2012. – 328 с., илл. – (Серия «Суперкомпьютерное образование»), ISBN 978-5-211-06382-2 URL: http://lira.imamod.ru/ITTPMOPS/ &amp;lt;/ref&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Данный алгоритм использует стратегию рекурсивной бисекции. В соответствии с ней, граф последовательно за k-1 шаг делится на k частей. На первом шаге исходный граф делится на 2 части, на каждом следующем шаге – одна из ранее полученных частей делится ещё на две части. В качестве способа деления на две части используется разделение по координатам вершин. Для этого вершины сортируется вдоль выбранной координаты, и в отсортированном массиве разбиваются на две непрерывные части в заданной пропорции. &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&amp;lt;/math&amp;gt; -- множество вершин, &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt; -- множество рёбер. Каждой вершине соответствует целочисленный координатный вектор &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt;, вес &amp;lt;math&amp;gt;v_i&amp;lt;/math&amp;gt;. Кроме того, задано желаемое количество доменов &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt;.&lt;br /&gt;
Требуется разбить множество &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; на p непересекающихся подмножеств (доменов) &amp;lt;math&amp;gt;S_1, \ldots, S_p&amp;lt;/math&amp;gt;, при этом должны быть выполнены следующие требования:&lt;br /&gt;
Домены не пересекаются: &amp;lt;math&amp;gt;S_i \cap S_j = \emptyset&amp;lt;/math&amp;gt;&lt;br /&gt;
Каждая вершина находится в домене: &amp;lt;math&amp;gt; \cup _i S_i = V&amp;lt;/math&amp;gt;&lt;br /&gt;
Распределение вершин по доменам примерно равномерное: &amp;lt;math&amp;gt;|S_i| \approx |V| / p&amp;lt;/math&amp;gt;&lt;br /&gt;
Минимизирован вес разрезанных рёбер: &amp;lt;math&amp;gt;\sum_{i \neq j} \sum_{v_1 \in S_i} \sum_{v2_ \in S_j} e_{v_1, v_2} \rightarrow min&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;O(n 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;
* После сортировки, начальные K вершин относятся к первому домену, остальные -- ко второму (K выбирается исходя из общего количества вершин и требуемой пропорции деления)&lt;br /&gt;
* При необходимости, каждый из кластеров может быть разделён на подкластеры рекурсивным вызовом этого же алгоритма&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
&amp;lt;source lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Шаг алгоритма, принимающий решение о разделении вершин.&lt;br /&gt;
# Разбивает граф, передаваемый в переменной graf и имеющий n вершин, на два подграфа&lt;br /&gt;
# subgraf1 и subgraf2, имеющих n1 и n2 вершин соответственно.&lt;br /&gt;
Bisect(graf, n, subgraf1, n1, subgraf2, n2) {&lt;br /&gt;
  # Найти самую протяжённую координату, то есть такое i, для которого разница&lt;br /&gt;
  # между максимальным и минимальным значением данной координаты по всем вершинам наибольшая.&lt;br /&gt;
  dim = argmax(i, max(j, graf[j][i]) - min(j, graf[j][i]));&lt;br /&gt;
  # Отсортировать вершины, используя в качестве ключа значение этой координаты&lt;br /&gt;
  sort(graf, dim);&lt;br /&gt;
  # Разбить граф на два домена, при этом первые n1 вершин попадут в первый домен, оставшиеся -- во второй.&lt;br /&gt;
  subgraf1 = graf[0..n1-1];&lt;br /&gt;
  subgraf2 = graf[n1..n-1];&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# Схема рекурсивной бисекции -- общая часть всех алгоритмов рекурсивной бисекции&lt;br /&gt;
# Разбивает граф, передаваемый в аргументе graf и имеющий n вершин&lt;br /&gt;
# на k примерно равных доменов, возвращаемых в переменной domains.&lt;br /&gt;
RecursiveBisect(graf, n, k, domains) {&lt;br /&gt;
  # Найти количество конечных доменов, присутствующих в первой и второй части разбиения на текущем шаге.&lt;br /&gt;
  k1 = (k+1)/2;&lt;br /&gt;
  k2 = k-k1;&lt;br /&gt;
  # Найти количество вершин в первой и второй части разбиения.&lt;br /&gt;
  n1=n*k1/k;&lt;br /&gt;
  n2=n-n1;&lt;br /&gt;
  # Выполнить разбиение на две части в соответствии с алгоритмом координатной бисекции.&lt;br /&gt;
  Bisect(graf, n, subgraf1, n1, subgraf2, n2);&lt;br /&gt;
  # В случае, если в части присутствует больше одного конечного домена, рекурсивно повторить алгоритм.&lt;br /&gt;
  # В противном случае, найденный домен будет входить в ответ данного алгоритма.&lt;br /&gt;
  If (k1&amp;gt;1) RecursiveBisect(subgraf1, n1, k1); else domains += subgraf1;&lt;br /&gt;
  If (k2&amp;gt;1) RecursiveBisect(subgraf2, n2, k2); else domains += subgraf2;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Как было описано ранее, для разбиения графа размером n на p кластеров потребуется p рекурсивных шагов. Таким образом, при использовании оптимальной стратегии разбиения, время работы можно выразить рекуррентным соотношением &amp;lt;math&amp;gt;T(N) = O(N*log_2(N)) + 2 * T(N/2)&amp;lt;/math&amp;gt;. Согласно основной теореме о рекуррентных соотношениях &amp;lt;ref&amp;gt; Основная теорема о рекуррентных соотношениях URL: https://ru.wikipedia.org/wiki/%D0%9E%D1%81%D0%BD%D0%BE%D0%B2%D0%BD%D0%B0%D1%8F_%D1%82%D0%B5%D0%BE%D1%80%D0%B5%D0%BC%D0%B0_%D0%BE_%D1%80%D0%B5%D0%BA%D1%83%D1%80%D1%80%D0%B5%D0%BD%D1%82%D0%BD%D1%8B%D1%85_%D1%81%D0%BE%D0%BE%D1%82%D0%BD%D0%BE%D1%88%D0%B5%D0%BD%D0%B8%D1%8F%D1%85#.D0.92.D0.B0.D1.80.D0.B8.D0.B0.D0.BD.D1.82_2&amp;lt;/ref&amp;gt; , &amp;lt;math&amp;gt;T(N) = O(N*log_2(N)*log_2(N))&amp;lt;/math&amp;gt; в худшем случае (при N = P). При меньшем количестве кластеров, глубина рекурсии будет ограничена &amp;lt;math&amp;gt;log_2(P)&amp;lt;/math&amp;gt;, следовательно, время работы алгоритма составит &amp;lt;math&amp;gt;O(log_2(p) * N * log_2(N))&amp;lt;/math&amp;gt;&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
Шаг алгоритма заключается в разделении входного множества вершин на два домена. Получается, что граф исполнения шагов представляет из себя бинарное дерево. Каждый шаг инициирует исполнение еще двух шагов.&lt;br /&gt;
[[File:allocator_graph.png|thumb|center|800px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
При этом на каждом шаге выполняются действия, описанные в п 1.2.&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Так как алгоритм работает с координатной сеткой – он обладает координатным параллелизмом.&lt;br /&gt;
Ярусно-параллельная форма алгоритма имеет высоту в &amp;lt;math&amp;gt;log_2 k&amp;lt;/math&amp;gt; уровней, где k – требуемое значение конечных доменов. Соответственно ширина яруса i будет равна &amp;lt;math&amp;gt;2^i&amp;lt;/math&amp;gt;. Так как ширина i-го яруса равна &amp;lt;math&amp;gt;2^i&amp;lt;/math&amp;gt;, а общее количество вершин примем за n – то в кажом домене на i-м ярусе будет по &amp;lt;math&amp;gt;n / 2^i&amp;lt;/math&amp;gt; вершин. Если на каждом шаге использовать  параллельную сортировку c хорошей сложностью &amp;lt;math&amp;gt;(n/p)(log_2(n/p)+log_2^2(p))&amp;lt;/math&amp;gt; (где n - количество сортируемых элементов, p - количество процессоров), то получаем итоговую сложность алгоритма как сумму всех ярусов, на которых параллельно запускается сортировка.&lt;br /&gt;
А именно:&lt;br /&gt;
&amp;lt;math&amp;gt;\sum_{i=0}^{log_2 k} ((n / 2^i) / (p / 2^i)) (log_2((n / 2^i) / (p / 2^i)) + log_2^2(p / 2^i))  = O((n/p) * (log_2 (n/p) + log_2^2(p)) * log_2(k))&amp;lt;/math&amp;gt;&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: требуемое количество доменов в разбиении, вершины графа. Каждой вершине соответствуют некоторые многомерные числовые координаты. Вершины могут быть произвольно разбиты по вычислительным узлам. Таким образом, &#039;&#039;&#039;объём входных данных&#039;&#039;&#039; пропорционален размеру графа V и количеству измерений N: &amp;lt;math&amp;gt;|V|*N+|E|&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: p доменов, разбивающих граф; при этом вершины этих доменов хранятся последовательно в исходном массиве. При количестве узлов, используемых для декомпозиции, совпадающим с количеством узлов, используемых для дальнейших вычислений, это автоматически размещает всю информацию о вершинах на вычислительных узлах, которые будут обрабатывать эти вершины. Таким образом, &#039;&#039;&#039;объём выходных данных&#039;&#039;&#039; пропорционален количеству вершин в исходном графе: |V|.&lt;br /&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; данного алгоритма может быть достигнута, если использовать устойчивый алгоритм сортировки.&lt;br /&gt;
&lt;br /&gt;
Так как процессы обрабатывают примерно равное количество вершин, полученный алгоритм является &#039;&#039;&#039;сбалансированным&#039;&#039;&#039; по числу операций на каждом процессе.&lt;br /&gt;
&lt;br /&gt;
Вычислительная мощность алгоритма, равная отношению количества операций к количеству входных и выходных данных равна &amp;lt;math&amp;gt;((|V|/P) * (log_2 (|V|/P) + log_2^2(P)) * log_2(K) / (|V|N+|E|) = &amp;lt;/math&amp;gt;. Если принять количество вычислительных узлов P и количество измерений N за константы, а количество ребёр пропорциональным количеству вершин (что верно для координатных сеток), вычислительная мощность будет равна &amp;lt;math&amp;gt;O(log_2(|V|) * log_2(K))&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;
Оценка масштабируемости метода рекурсивной координатной бисекции проводилась на основе существующей реализации. Из существующих реализаций была выбрана наиболее широко используемая на данный момент реализация из программной библиотеки для распределения нагрузки Zoltan. &lt;br /&gt;
&lt;br /&gt;
Входными данными были двумерные регулярные сетки, которые генерировались непосредственно внутри самой программы. Исходная реализация предполагала чтение с файла, но в данном случае накладные расходы на чтение оказываются неоправданны.&lt;br /&gt;
&lt;br /&gt;
Запуск производился на суперкомпьютере ВМК МГУ BlueGene/P и осуществлялся следующей программой на языке BASH&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
for size in 1024 2048 4096; do&lt;br /&gt;
  for procs in 1 2 16 64 128 256 512; do&lt;br /&gt;
    mpisubmit.bg -n $procs -w 3:00 -m smp --stdin /dev/null --stderr ~/output2/$size-$procs.log --stdout /dev/null ./simpleRCB.exe -- $size&lt;br /&gt;
  done&lt;br /&gt;
done&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Результаты запуска при разбиении на 16 доменов показаны ниже.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Allocator_split16.png|thumb|center|800px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Динамические характеристики и эффективность реализации алгоритма ==&lt;br /&gt;
&lt;br /&gt;
== Выводы для классов архитектур ==&lt;br /&gt;
&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Открытые параллельные и последовательные реализации существуют в пакете алгоритмов декомпозиции METIS и ParMETIS: http://glaros.dtc.umn.edu/gkhome/software&lt;br /&gt;
&lt;br /&gt;
Кроме того, он пристутсвует в библиотеках Zoltan (https://www.cs.sandia.gov/zoltan/), Scotch и PT-Scotch (https://www.labri.fr/perso/pelegrin/scotch/).&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;/div&gt;</summary>
		<author><name>Allocator</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Allocator/%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D1%80%D0%B5%D0%BA%D1%83%D1%80%D1%81%D0%B8%D0%B2%D0%BD%D0%BE%D0%B9_%D0%BA%D0%BE%D0%BE%D1%80%D0%B4%D0%B8%D0%BD%D0%B0%D1%82%D0%BD%D0%BE%D0%B9_%D0%B1%D0%B8%D1%81%D0%B5%D0%BA%D1%86%D0%B8%D0%B8&amp;diff=22386</id>
		<title>Участник:Allocator/Метод рекурсивной координатной бисекции</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Allocator/%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D1%80%D0%B5%D0%BA%D1%83%D1%80%D1%81%D0%B8%D0%B2%D0%BD%D0%BE%D0%B9_%D0%BA%D0%BE%D0%BE%D1%80%D0%B4%D0%B8%D0%BD%D0%B0%D1%82%D0%BD%D0%BE%D0%B9_%D0%B1%D0%B8%D1%81%D0%B5%D0%BA%D1%86%D0%B8%D0%B8&amp;diff=22386"/>
		<updated>2017-02-07T11:14:28Z</updated>

		<summary type="html">&lt;p&gt;Allocator: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Авторы: Долгов Борис (624), Галуза Дмитрий (624)&lt;br /&gt;
= Свойства и структура алгоритмов =&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
Декомпозиция графа — это разбиение множества вершин графа на подмножества, для обработки каждого из подмножеств на отдельном вычислительном узле.&lt;br /&gt;
Декомпозиция больших графов (то есть графов, объём описания которых превышает память, доступную одному узлу) происходит в два этапа: выполнение первоначального разбиения вершин графа по вычислительным процессорам, затем — выполнение одного из параллельных алгоритмов декомпозиции. Алгоритм координатной рекурсивной бисекции даёт хорошее первичное распределение узлов сетки по сравнению с простым распределением узлов по номерам и, как правило, используется как первый шаг более сложных алгоритмов &amp;lt;ref&amp;gt;Якобовский М.В. Введение в параллельные методы решения задач: Учебное пособие / Предисл.: В. А. Садовничий. – М.: Издательство Московского университета, 2012. – 328 с., илл. – (Серия «Суперкомпьютерное образование»), ISBN 978-5-211-06382-2 URL: http://lira.imamod.ru/ITTPMOPS/ &amp;lt;/ref&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Данный алгоритм использует стратегию рекурсивной бисекции. В соответствии с ней, граф последовательно за k-1 шаг делится на k частей. На первом шаге исходный граф делится на 2 части, на каждом следующем шаге – одна из ранее полученных частей делится ещё на две части. В качестве способа деления на две части используется разделение по координатам вершин. Для этого вершины сортируется вдоль выбранной координаты, и в отсортированном массиве разбиваются на две непрерывные части в заданной пропорции. &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&amp;lt;/math&amp;gt; -- множество вершин, &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt; -- множество рёбер. Каждой вершине соответствует целочисленный координатный вектор &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt;, вес &amp;lt;math&amp;gt;v_i&amp;lt;/math&amp;gt;. Кроме того, задано желаемое количество доменов &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt;.&lt;br /&gt;
Требуется разбить множество &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; на p непересекающихся подмножеств (доменов) &amp;lt;math&amp;gt;S_1, \ldots, S_p&amp;lt;/math&amp;gt;, при этом должны быть выполнены следующие требования:&lt;br /&gt;
Домены не пересекаются: &amp;lt;math&amp;gt;S_i \cap S_j = \emptyset&amp;lt;/math&amp;gt;&lt;br /&gt;
Каждая вершина находится в домене: &amp;lt;math&amp;gt; \cup _i S_i = V&amp;lt;/math&amp;gt;&lt;br /&gt;
Распределение вершин по доменам примерно равномерное: &amp;lt;math&amp;gt;|S_i| \approx |V| / p&amp;lt;/math&amp;gt;&lt;br /&gt;
Минимизирован вес разрезанных рёбер: &amp;lt;math&amp;gt;\sum_{i \neq j} \sum_{v_1 \in S_i} \sum_{v2_ \in S_j} e_{v_1, v_2} \rightarrow min&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;O(n 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;
* После сортировки, начальные K вершин относятся к первому домену, остальные -- ко второму (K выбирается исходя из общего количества вершин и требуемой пропорции деления)&lt;br /&gt;
* При необходимости, каждый из кластеров может быть разделён на подкластеры рекурсивным вызовом этого же алгоритма&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
&amp;lt;source lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Шаг алгоритма, принимающий решение о разделении вершин.&lt;br /&gt;
# Разбивает граф, передаваемый в переменной graf и имеющий n вершин, на два подграфа&lt;br /&gt;
# subgraf1 и subgraf2, имеющих n1 и n2 вершин соответственно.&lt;br /&gt;
Bisect(graf, n, subgraf1, n1, subgraf2, n2) {&lt;br /&gt;
  # Найти самую протяжённую координату, то есть такое i, для которого разница&lt;br /&gt;
  # между максимальным и минимальным значением данной координаты по всем вершинам наибольшая.&lt;br /&gt;
  dim = argmax(i, max(j, graf[j][i]) - min(j, graf[j][i]));&lt;br /&gt;
  # Отсортировать вершины, используя в качестве ключа значение этой координаты&lt;br /&gt;
  sort(graf, dim);&lt;br /&gt;
  # Разбить граф на два домена, при этом первые n1 вершин попадут в первый домен, оставшиеся -- во второй.&lt;br /&gt;
  subgraf1 = graf[0..n1-1];&lt;br /&gt;
  subgraf2 = graf[n1..n-1];&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# Схема рекурсивной бисекции -- общая часть всех алгоритмов рекурсивной бисекции&lt;br /&gt;
# Разбивает граф, передаваемый в аргументе graf и имеющий n вершин&lt;br /&gt;
# на k примерно равных доменов, возвращаемых в переменной domains.&lt;br /&gt;
RecursiveBisect(graf, n, k, domains) {&lt;br /&gt;
  # Найти количество конечных доменов, присутствующих в первой и второй части разбиения на текущем шаге.&lt;br /&gt;
  k1 = (k+1)/2;&lt;br /&gt;
  k2 = k-k1;&lt;br /&gt;
  # Найти количество вершин в первой и второй части разбиения.&lt;br /&gt;
  n1=n*k1/k;&lt;br /&gt;
  n2=n-n1;&lt;br /&gt;
  # Выполнить разбиение на две части в соответствии с алгоритмом координатной бисекции.&lt;br /&gt;
  Bisect(graf, n, subgraf1, n1, subgraf2, n2);&lt;br /&gt;
  # В случае, если в части присутствует больше одного конечного домена, рекурсивно повторить алгоритм.&lt;br /&gt;
  # В противном случае, найденный домен будет входить в ответ данного алгоритма.&lt;br /&gt;
  If (k1&amp;gt;1) RecursiveBisect(subgraf1, n1, k1); else domains += subgraf1;&lt;br /&gt;
  If (k2&amp;gt;1) RecursiveBisect(subgraf2, n2, k2); else domains += subgraf2;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Как было описано ранее, для разбиения графа размером n на p кластеров потребуется p рекурсивных шагов. Таким образом, при использовании оптимальной стратегии разбиения, время работы можно выразить рекуррентным соотношением &amp;lt;math&amp;gt;T(N) = O(N*log_2(N)) + 2 * T(N/2)&amp;lt;/math&amp;gt;. Согласно основной теореме о рекуррентных соотношениях &amp;lt;ref&amp;gt; Основная теорема о рекуррентных соотношениях URL: https://ru.wikipedia.org/wiki/%D0%9E%D1%81%D0%BD%D0%BE%D0%B2%D0%BD%D0%B0%D1%8F_%D1%82%D0%B5%D0%BE%D1%80%D0%B5%D0%BC%D0%B0_%D0%BE_%D1%80%D0%B5%D0%BA%D1%83%D1%80%D1%80%D0%B5%D0%BD%D1%82%D0%BD%D1%8B%D1%85_%D1%81%D0%BE%D0%BE%D1%82%D0%BD%D0%BE%D1%88%D0%B5%D0%BD%D0%B8%D1%8F%D1%85#.D0.92.D0.B0.D1.80.D0.B8.D0.B0.D0.BD.D1.82_2&amp;lt;/ref&amp;gt; , &amp;lt;math&amp;gt;T(N) = O(N*log_2(N)*log_2(N))&amp;lt;/math&amp;gt; в худшем случае (при N = P). При меньшем количестве кластеров, глубина рекурсии будет ограничена &amp;lt;math&amp;gt;log_2(P)&amp;lt;/math&amp;gt;, следовательно, время работы алгоритма составит &amp;lt;math&amp;gt;O(log_2(p) * N * log_2(N))&amp;lt;/math&amp;gt;&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
Шаг алгоритма заключается в разделении входного множества вершин на два домена. Получается, что граф исполнения шагов представляет из себя бинарное дерево. Каждый шаг инициирует исполнение еще двух шагов.&lt;br /&gt;
[[File:allocator_graph.png|thumb|center|800px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
При этом на каждом шаге выполняются действия, описанные в п 1.2.&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Так как алгоритм работает с координатной сеткой – он обладает координатным параллелизмом.&lt;br /&gt;
Ярусно-параллельная форма алгоритма имеет высоту в &amp;lt;math&amp;gt;log_2 k&amp;lt;/math&amp;gt; уровней, где k – требуемое значение конечных доменов. Соответственно ширина яруса i будет равна &amp;lt;math&amp;gt;2^i&amp;lt;/math&amp;gt;. Так как ширина i-го яруса равна &amp;lt;math&amp;gt;2^i&amp;lt;/math&amp;gt;, а общее количество вершин примем за n – то в кажом домене на i-м ярусе будет по &amp;lt;math&amp;gt;n / 2^i&amp;lt;/math&amp;gt; вершин. Если на каждом шаге использовать  параллельную сортировку c хорошей сложностью &amp;lt;math&amp;gt;(n/p)(log_2(n/p)+log_2^2(p))&amp;lt;/math&amp;gt; (где n - количество сортируемых элементов, p - количество процессоров), то получаем итоговую сложность алгоритма как сумму всех ярусов, на которых параллельно запускается сортировка.&lt;br /&gt;
А именно:&lt;br /&gt;
&amp;lt;math&amp;gt;\sum_{i=0}^{log_2 k} ((n / 2^i) / (p / 2^i)) (log_2((n / 2^i) / (p / 2^i)) + log_2^2(p / 2^i))  = O((n/p) * (log_2 (n/p) + log_2^2(p)) * log_2(k))&amp;lt;/math&amp;gt;&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: требуемое количество доменов в разбиении, вершины графа. Каждой вершине соответствуют некоторые многомерные числовые координаты. Вершины могут быть произвольно разбиты по вычислительным узлам. Таким образом, &#039;&#039;&#039;объём входных данных&#039;&#039;&#039; пропорционален размеру графа V и количеству измерений N: &amp;lt;math&amp;gt;|V|*N+|E|&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: p доменов, разбивающих граф; при этом вершины этих доменов хранятся последовательно в исходном массиве. При количестве узлов, используемых для декомпозиции, совпадающим с количеством узлов, используемых для дальнейших вычислений, это автоматически размещает всю информацию о вершинах на вычислительных узлах, которые будут обрабатывать эти вершины. Таким образом, &#039;&#039;&#039;объём выходных данных&#039;&#039;&#039; пропорционален количеству вершин в исходном графе: |V|.&lt;br /&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; данного алгоритма может быть достигнута, если использовать устойчивый алгоритм сортировки.&lt;br /&gt;
&lt;br /&gt;
Так как процессы обрабатывают примерно равное количество вершин, полученный алгоритм является &#039;&#039;&#039;сбалансированным&#039;&#039;&#039; по числу операций на каждом процессе.&lt;br /&gt;
&lt;br /&gt;
Вычислительная мощность алгоритма, равная отношению количества операций к количеству входных и выходных данных равна &amp;lt;math&amp;gt;((|V|/P) * (log_2 (|V|/P) + log_2^2(P)) * log_2(K) / (|V|N+|E|) = &amp;lt;/math&amp;gt;. Если принять количество вычислительных узлов P и количество измерений N за константы, а количество ребёр пропорциональным количеству вершин (что верно для координатных сеток), вычислительная мощность будет равна &amp;lt;math&amp;gt;O(log_2(|V|) * log_2(K))&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;
Оценка масштабируемости метода рекурсивной координатной бисекции проводилась на основе существующей реализации. Из существующих реализаций была выбрана наиболее широко используемая на данный момент реализация из программной библиотеки для распределения нагрузки Zoltan. &lt;br /&gt;
&lt;br /&gt;
Входными данными были двумерные регулярные сетки, которые генерировались непосредственно внутри самой программы. Исходная реализация предполагала чтение с файла, но в данном случае накладные расходы на чтение оказываются неоправданны.&lt;br /&gt;
&lt;br /&gt;
Запуск производился на суперкомпьютере ВМК МГУ BlueGene/P и осуществлялся следующей программой на языке BASH&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
for size in 1024 2048 4096; do&lt;br /&gt;
  for procs in 1 2 16 64 128 256 512; do&lt;br /&gt;
    mpisubmit.bg -n $procs -w 3:00 -m smp --stdin /dev/null --stderr ~/output2/$size-$procs.log --stdout /dev/null ./simpleRCB.exe -- $size&lt;br /&gt;
  done&lt;br /&gt;
done&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Результаты запуска при разбиении на 16 доменов показаны ниже.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Allocator_split16.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Динамические характеристики и эффективность реализации алгоритма ==&lt;br /&gt;
&lt;br /&gt;
== Выводы для классов архитектур ==&lt;br /&gt;
&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Открытые параллельные и последовательные реализации существуют в пакете алгоритмов декомпозиции METIS и ParMETIS: http://glaros.dtc.umn.edu/gkhome/software&lt;br /&gt;
&lt;br /&gt;
Кроме того, он пристутсвует в библиотеках Zoltan (https://www.cs.sandia.gov/zoltan/), Scotch и PT-Scotch (https://www.labri.fr/perso/pelegrin/scotch/).&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;/div&gt;</summary>
		<author><name>Allocator</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Allocator/%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D1%80%D0%B5%D0%BA%D1%83%D1%80%D1%81%D0%B8%D0%B2%D0%BD%D0%BE%D0%B9_%D0%BA%D0%BE%D0%BE%D1%80%D0%B4%D0%B8%D0%BD%D0%B0%D1%82%D0%BD%D0%BE%D0%B9_%D0%B1%D0%B8%D1%81%D0%B5%D0%BA%D1%86%D0%B8%D0%B8&amp;diff=22291</id>
		<title>Участник:Allocator/Метод рекурсивной координатной бисекции</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Allocator/%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D1%80%D0%B5%D0%BA%D1%83%D1%80%D1%81%D0%B8%D0%B2%D0%BD%D0%BE%D0%B9_%D0%BA%D0%BE%D0%BE%D1%80%D0%B4%D0%B8%D0%BD%D0%B0%D1%82%D0%BD%D0%BE%D0%B9_%D0%B1%D0%B8%D1%81%D0%B5%D0%BA%D1%86%D0%B8%D0%B8&amp;diff=22291"/>
		<updated>2017-02-06T15:15:35Z</updated>

		<summary type="html">&lt;p&gt;Allocator: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Авторы: Долгов Борис (624), Галуза Дмитрий (624)&lt;br /&gt;
= Свойства и структура алгоритмов =&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
Декомпозиция графа — это разбиение множества вершин графа на подмножества, для обработки каждого из подмножеств на отдельном вычислительном узле.&lt;br /&gt;
Декомпозиция больших графов (то есть графов, объём описания которых превышает память, доступную одному узлу) происходит в два этапа: выполнение первоначального разбиения вершин графа по вычислительным процессорам, затем — выполнение одного из параллельных алгоритмов декомпозиции. Алгоритм координатной рекурсивной бисекции даёт хорошее первичное распределение узлов сетки по сравнению с простым распределением узлов по номерам и, как правило, используется как первый шаг более сложных алгоритмов &amp;lt;ref&amp;gt;Якобовский М.В. Введение в параллельные методы решения задач: Учебное пособие / Предисл.: В. А. Садовничий. – М.: Издательство Московского университета, 2012. – 328 с., илл. – (Серия «Суперкомпьютерное образование»), ISBN 978-5-211-06382-2 URL: http://lira.imamod.ru/ITTPMOPS/ &amp;lt;/ref&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Данный алгоритм использует стратегию рекурсивной бисекции. В соответствии с ней, граф последовательно за k-1 шаг делится на k частей. На первом шаге исходный граф делится на 2 части, на каждом следующем шаге – одна из ранее полученных частей делится ещё на две части. В качестве способа деления на две части используется разделение по координатам вершин. Для этого вершины сортируется вдоль выбранной координаты, и в отсортированном массиве разбиваются на две непрерывные части в заданной пропорции. &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&amp;lt;/math&amp;gt; -- множество вершин, &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt; -- множество рёбер. Каждой вершине соответствует целочисленный координатный вектор &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt;, вес &amp;lt;math&amp;gt;v_i&amp;lt;/math&amp;gt;. Кроме того, задано желаемое количество доменов &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt;.&lt;br /&gt;
Требуется разбить множество &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; на p непересекающихся подмножеств (доменов) &amp;lt;math&amp;gt;S_1, \ldots, S_p&amp;lt;/math&amp;gt;, при этом должны быть выполнены следующие требования:&lt;br /&gt;
Домены не пересекаются: &amp;lt;math&amp;gt;S_i \cap S_j = \emptyset&amp;lt;/math&amp;gt;&lt;br /&gt;
Каждая вершина находится в домене: &amp;lt;math&amp;gt; \cup _i S_i = V&amp;lt;/math&amp;gt;&lt;br /&gt;
Распределение вершин по доменам примерно равномерное: &amp;lt;math&amp;gt;|S_i| \approx |V| / p&amp;lt;/math&amp;gt;&lt;br /&gt;
Минимизирован вес разрезанных рёбер: &amp;lt;math&amp;gt;\sum_{i \neq j} \sum_{v_1 \in S_i} \sum_{v2_ \in S_j} e_{v_1, v_2} \rightarrow min&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;O(n 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;
* После сортировки, начальные K вершин относятся к первому домену, остальные -- ко второму (K выбирается исходя из общего количества вершин и требуемой пропорции деления)&lt;br /&gt;
* При необходимости, каждый из кластеров может быть разделён на подкластеры рекурсивным вызовом этого же алгоритма&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
&amp;lt;source lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Шаг алгоритма, принимающий решение о разделении вершин.&lt;br /&gt;
# Разбивает граф, передаваемый в переменной graf и имеющий n вершин, на два подграфа&lt;br /&gt;
# subgraf1 и subgraf2, имеющих n1 и n2 вершин соответственно.&lt;br /&gt;
Bisect(graf, n, subgraf1, n1, subgraf2, n2) {&lt;br /&gt;
  # Найти самую протяжённую координату, то есть такое i, для которого разница&lt;br /&gt;
  # между максимальным и минимальным значением данной координаты по всем вершинам наибольшая.&lt;br /&gt;
  dim = argmax(i, max(j, graf[j][i]) - min(j, graf[j][i]));&lt;br /&gt;
  # Отсортировать вершины, используя в качестве ключа значение этой координаты&lt;br /&gt;
  sort(graf, dim);&lt;br /&gt;
  # Разбить граф на два домена, при этом первые n1 вершин попадут в первый домен, оставшиеся -- во второй.&lt;br /&gt;
  subgraf1 = graf[0..n1-1];&lt;br /&gt;
  subgraf2 = graf[n1..n-1];&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# Схема рекурсивной бисекции -- общая часть всех алгоритмов рекурсивной бисекции&lt;br /&gt;
# Разбивает граф, передаваемый в аргументе graf и имеющий n вершин&lt;br /&gt;
# на k примерно равных доменов, возвращаемых в переменной domains.&lt;br /&gt;
RecursiveBisect(graf, n, k, domains) {&lt;br /&gt;
  # Найти количество конечных доменов, присутствующих в первой и второй части разбиения на текущем шаге.&lt;br /&gt;
  k1 = (k+1)/2;&lt;br /&gt;
  k2 = k-k1;&lt;br /&gt;
  # Найти количество вершин в первой и второй части разбиения.&lt;br /&gt;
  n1=n*k1/k;&lt;br /&gt;
  n2=n-n1;&lt;br /&gt;
  # Выполнить разбиение на две части в соответствии с алгоритмом координатной бисекции.&lt;br /&gt;
  Bisect(graf, n, subgraf1, n1, subgraf2, n2);&lt;br /&gt;
  # В случае, если в части присутствует больше одного конечного домена, рекурсивно повторить алгоритм.&lt;br /&gt;
  # В противном случае, найденный домен будет входить в ответ данного алгоритма.&lt;br /&gt;
  If (k1&amp;gt;1) RecursiveBisect(subgraf1, n1, k1); else domains += subgraf1;&lt;br /&gt;
  If (k2&amp;gt;1) RecursiveBisect(subgraf2, n2, k2); else domains += subgraf2;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Как было описано ранее, для разбиения графа размером n на p кластеров потребуется p рекурсивных шагов. Таким образом, при использовании оптимальной стратегии разбиения, время работы можно выразить рекуррентным соотношением &amp;lt;math&amp;gt;T(N) = O(N*log_2(N)) + 2 * T(N/2)&amp;lt;/math&amp;gt;. Согласно основной теореме о рекуррентных соотношениях &amp;lt;ref&amp;gt; Основная теорема о рекуррентных соотношениях URL: https://ru.wikipedia.org/wiki/%D0%9E%D1%81%D0%BD%D0%BE%D0%B2%D0%BD%D0%B0%D1%8F_%D1%82%D0%B5%D0%BE%D1%80%D0%B5%D0%BC%D0%B0_%D0%BE_%D1%80%D0%B5%D0%BA%D1%83%D1%80%D1%80%D0%B5%D0%BD%D1%82%D0%BD%D1%8B%D1%85_%D1%81%D0%BE%D0%BE%D1%82%D0%BD%D0%BE%D1%88%D0%B5%D0%BD%D0%B8%D1%8F%D1%85#.D0.92.D0.B0.D1.80.D0.B8.D0.B0.D0.BD.D1.82_2&amp;lt;/ref&amp;gt; , &amp;lt;math&amp;gt;T(N) = O(N*log_2(N)*log_2(N))&amp;lt;/math&amp;gt; в худшем случае (при N = P). При меньшем количестве кластеров, глубина рекурсии будет ограничена &amp;lt;math&amp;gt;log_2(P)&amp;lt;/math&amp;gt;, следовательно, время работы алгоритма составит &amp;lt;math&amp;gt;O(log_2(p) * N * log_2(N))&amp;lt;/math&amp;gt;&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
Шаг алгоритма заключается в разделении входного множества вершин на два домена. Получается, что граф исполнения шагов представляет из себя бинарное дерево. Каждый шаг инициирует исполнение еще двух шагов.&lt;br /&gt;
[[File:allocator_graph.png|thumb|center|800px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
При этом на каждом шаге выполняются действия, описанные в п 1.2.&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Так как алгоритм работает с координатной сеткой – он обладает координатным параллелизмом.&lt;br /&gt;
Ярусно-параллельная форма алгоритма имеет высоту в &amp;lt;math&amp;gt;log_2 k&amp;lt;/math&amp;gt; уровней, где k – требуемое значение конечных доменов. Соответственно ширина яруса i будет равна &amp;lt;math&amp;gt;2^i&amp;lt;/math&amp;gt;. Так как ширина i-го яруса равна &amp;lt;math&amp;gt;2^i&amp;lt;/math&amp;gt;, а общее количество вершин примем за n – то в кажом домене на i-м ярусе будет по &amp;lt;math&amp;gt;n / 2^i&amp;lt;/math&amp;gt; вершин. Если на каждом шаге использовать  параллельную сортировку c хорошей сложностью &amp;lt;math&amp;gt;(n/p)(log_2(n/p)+log_2^2(p))&amp;lt;/math&amp;gt; (где n - количество сортируемых элементов, p - количество процессоров), то получаем итоговую сложность алгоритма как сумму всех ярусов, на которых параллельно запускается сортировка.&lt;br /&gt;
А именно:&lt;br /&gt;
&amp;lt;math&amp;gt;\sum_{i=0}^{log_2 k} ((n / 2^i) / (p / 2^i)) (log_2((n / 2^i) / (p / 2^i)) + log_2^2(p / 2^i))  = O((n/p) * (log_2 (n/p) + log_2^2(p)) * log_2(k))&amp;lt;/math&amp;gt;&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: требуемое количество доменов в разбиении, вершины графа. Каждой вершине соответствуют некоторые многомерные числовые координаты. Вершины могут быть произвольно разбиты по вычислительным узлам. Таким образом, &#039;&#039;&#039;объём входных данных&#039;&#039;&#039; пропорционален размеру графа V и количеству измерений N: &amp;lt;math&amp;gt;|V|*N+|E|&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: p доменов, разбивающих граф; при этом вершины этих доменов хранятся последовательно в исходном массиве. При количестве узлов, используемых для декомпозиции, совпадающим с количеством узлов, используемых для дальнейших вычислений, это автоматически размещает всю информацию о вершинах на вычислительных узлах, которые будут обрабатывать эти вершины. Таким образом, &#039;&#039;&#039;объём выходных данных&#039;&#039;&#039; пропорционален количеству вершин в исходном графе: |V|.&lt;br /&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; данного алгоритма может быть достигнута, если использовать устойчивый алгоритм сортировки.&lt;br /&gt;
&lt;br /&gt;
Так как процессы обрабатывают примерно равное количество вершин, полученный алгоритм является &#039;&#039;&#039;сбалансированным&#039;&#039;&#039; по числу операций на каждом процессе.&lt;br /&gt;
&lt;br /&gt;
Вычислительная мощность алгоритма, равная отношению количества операций к количеству входных и выходных данных равна &amp;lt;math&amp;gt;((|V|/P) * (log_2 (|V|/P) + log_2^2(P)) * log_2(K) / (|V|N+|E|) = &amp;lt;/math&amp;gt;. Если принять количество вычислительных узлов P и количество измерений N за константы, а количество ребёр пропорциональным количеству вершин (что верно для координатных сеток), вычислительная мощность будет равна &amp;lt;math&amp;gt;O(log_2(|V|) * log_2(K))&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;
Открытые параллельные и последовательные реализации существуют в пакете алгоритмов декомпозиции METIS и ParMETIS: http://glaros.dtc.umn.edu/gkhome/software&lt;br /&gt;
&lt;br /&gt;
Кроме того, он пристутсвует в библиотеках Zoltan (https://www.cs.sandia.gov/zoltan/), Scotch и PT-Scotch (https://www.labri.fr/perso/pelegrin/scotch/).&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;/div&gt;</summary>
		<author><name>Allocator</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Allocator/%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D1%80%D0%B5%D0%BA%D1%83%D1%80%D1%81%D0%B8%D0%B2%D0%BD%D0%BE%D0%B9_%D0%BA%D0%BE%D0%BE%D1%80%D0%B4%D0%B8%D0%BD%D0%B0%D1%82%D0%BD%D0%BE%D0%B9_%D0%B1%D0%B8%D1%81%D0%B5%D0%BA%D1%86%D0%B8%D0%B8&amp;diff=22288</id>
		<title>Участник:Allocator/Метод рекурсивной координатной бисекции</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Allocator/%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D1%80%D0%B5%D0%BA%D1%83%D1%80%D1%81%D0%B8%D0%B2%D0%BD%D0%BE%D0%B9_%D0%BA%D0%BE%D0%BE%D1%80%D0%B4%D0%B8%D0%BD%D0%B0%D1%82%D0%BD%D0%BE%D0%B9_%D0%B1%D0%B8%D1%81%D0%B5%D0%BA%D1%86%D0%B8%D0%B8&amp;diff=22288"/>
		<updated>2017-02-06T15:08:38Z</updated>

		<summary type="html">&lt;p&gt;Allocator: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Авторы: Долгов Борис (624), Галуза Дмитрий (624)&lt;br /&gt;
= Свойства и структура алгоритмов =&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
Декомпозиция графа — это разбиение множества вершин графа на подмножества, для обработки каждого из подмножеств на отдельном вычислительном узле.&lt;br /&gt;
Декомпозиция больших графов (то есть графов, объём описания которых превышает память, доступную одному узлу) происходит в два этапа: выполнение первоначального разбиения вершин графа по вычислительным процессорам, затем — выполнение одного из параллельных алгоритмов декомпозиции. Алгоритм координатной рекурсивной бисекции даёт хорошее первичное распределение узлов сетки по сравнению с простым распределением узлов по номерам и, как правило, используется как первый шаг более сложных алгоритмов &amp;lt;ref&amp;gt;Якобовский М.В. Введение в параллельные методы решения задач: Учебное пособие / Предисл.: В. А. Садовничий. – М.: Издательство Московского университета, 2012. – 328 с., илл. – (Серия «Суперкомпьютерное образование»), ISBN 978-5-211-06382-2 URL: http://lira.imamod.ru/ITTPMOPS/ &amp;lt;/ref&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Данный алгоритм использует стратегию рекурсивной бисекции. В соответствии с ней, граф последовательно за k-1 шаг делится на k частей. На первом шаге исходный граф делится на 2 части, на каждом следующем шаге – одна из ранее полученных частей делится ещё на две части. В качестве способа деления на две части используется разделение по координатам вершин. Для этого вершины сортируется вдоль выбранной координаты, и в отсортированном массиве разбиваются на две непрерывные части в заданной пропорции. &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&amp;lt;/math&amp;gt; -- множество вершин, &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt; -- множество рёбер. Каждой вершине соответствует целочисленный координатный вектор &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt;, вес &amp;lt;math&amp;gt;v_i&amp;lt;/math&amp;gt;. Кроме того, задано желаемое количество доменов &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt;.&lt;br /&gt;
Требуется разбить множество &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; на p непересекающихся подмножеств (доменов) &amp;lt;math&amp;gt;S_1, \ldots, S_p&amp;lt;/math&amp;gt;, при этом должны быть выполнены следующие требования:&lt;br /&gt;
Домены не пересекаются: &amp;lt;math&amp;gt;S_i \cap S_j = \emptyset&amp;lt;/math&amp;gt;&lt;br /&gt;
Каждая вершина находится в домене: &amp;lt;math&amp;gt; \cup _i S_i = V&amp;lt;/math&amp;gt;&lt;br /&gt;
Распределение вершин по доменам примерно равномерное: &amp;lt;math&amp;gt;|S_i| \approx |V| / p&amp;lt;/math&amp;gt;&lt;br /&gt;
Минимизирован вес разрезанных рёбер: &amp;lt;math&amp;gt;\sum_{i \neq j} \sum_{v_1 \in S_i} \sum_{v2_ \in S_j} e_{v_1, v_2} \rightarrow min&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;O(n 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;
* После сортировки, начальные K вершин относятся к первому домену, остальные -- ко второму (K выбирается исходя из общего количества вершин и требуемой пропорции деления)&lt;br /&gt;
* При необходимости, каждый из кластеров может быть разделён на подкластеры рекурсивным вызовом этого же алгоритма&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
&amp;lt;source lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Шаг алгоритма, принимающий решение о разделении вершин.&lt;br /&gt;
# Разбивает граф, передаваемый в переменной graf и имеющий n вершин, на два подграфа&lt;br /&gt;
# subgraf1 и subgraf2, имеющих n1 и n2 вершин соответственно.&lt;br /&gt;
Bisect(graf, n, subgraf1, n1, subgraf2, n2) {&lt;br /&gt;
  # Найти самую протяжённую координату, то есть такое i, для которого разница&lt;br /&gt;
  # между максимальным и минимальным значением данной координаты по всем вершинам наибольшая.&lt;br /&gt;
  dim = argmax(i, max(j, graf[j][i]) - min(j, graf[j][i]));&lt;br /&gt;
  # Отсортировать вершины, используя в качестве ключа значение этой координаты&lt;br /&gt;
  sort(graf, dim);&lt;br /&gt;
  # Разбить граф на два домена, при этом первые n1 вершин попадут в первый домен, оставшиеся -- во второй.&lt;br /&gt;
  subgraf1 = graf[0..n1-1];&lt;br /&gt;
  subgraf2 = graf[n1..n-1];&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# Схема рекурсивной бисекции -- общая часть всех алгоритмов рекурсивной бисекции&lt;br /&gt;
# Разбивает граф, передаваемый в аргументе graf и имеющий n вершин&lt;br /&gt;
# на k примерно равных доменов, возвращаемых в переменной domains.&lt;br /&gt;
RecursiveBisect(graf, n, k, domains) {&lt;br /&gt;
  # Найти количество конечных доменов, присутствующих в первой и второй части разбиения на текущем шаге.&lt;br /&gt;
  k1 = (k+1)/2;&lt;br /&gt;
  k2 = k-k1;&lt;br /&gt;
  # Найти количество вершин в первой и второй части разбиения.&lt;br /&gt;
  n1=n*k1/k;&lt;br /&gt;
  n2=n-n1;&lt;br /&gt;
  # Выполнить разбиение на две части в соответствии с алгоритмом координатной бисекции.&lt;br /&gt;
  Bisect(graf, n, subgraf1, n1, subgraf2, n2);&lt;br /&gt;
  # В случае, если в части присутствует больше одного конечного домена, рекурсивно повторить алгоритм.&lt;br /&gt;
  # В противном случае, найденный домен будет входить в ответ данного алгоритма.&lt;br /&gt;
  If (k1&amp;gt;1) RecursiveBisect(subgraf1, n1, k1); else domains += subgraf1;&lt;br /&gt;
  If (k2&amp;gt;1) RecursiveBisect(subgraf2, n2, k2); else domains += subgraf2;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Как было описано ранее, для разбиения графа размером n на p кластеров потребуется p рекурсивных шагов. Таким образом, при использовании оптимальной стратегии разбиения, время работы можно выразить рекуррентным соотношением &amp;lt;math&amp;gt;T(N) = O(N*log_2(N)) + 2 * T(N/2)&amp;lt;/math&amp;gt;. Согласно основной теореме о рекуррентных соотношениях &amp;lt;ref&amp;gt; Основная теорема о рекуррентных соотношениях URL: https://ru.wikipedia.org/wiki/%D0%9E%D1%81%D0%BD%D0%BE%D0%B2%D0%BD%D0%B0%D1%8F_%D1%82%D0%B5%D0%BE%D1%80%D0%B5%D0%BC%D0%B0_%D0%BE_%D1%80%D0%B5%D0%BA%D1%83%D1%80%D1%80%D0%B5%D0%BD%D1%82%D0%BD%D1%8B%D1%85_%D1%81%D0%BE%D0%BE%D1%82%D0%BD%D0%BE%D1%88%D0%B5%D0%BD%D0%B8%D1%8F%D1%85#.D0.92.D0.B0.D1.80.D0.B8.D0.B0.D0.BD.D1.82_2&amp;lt;/ref&amp;gt; , &amp;lt;math&amp;gt;T(N) = O(N*log_2(N)*log_2(N))&amp;lt;/math&amp;gt; в худшем случае (при N = P). При меньшем количестве кластеров, глубина рекурсии будет ограничена &amp;lt;math&amp;gt;log_2(P)&amp;lt;/math&amp;gt;, следовательно, время работы алгоритма составит &amp;lt;math&amp;gt;O(log_2(p) * N * log_2(N))&amp;lt;/math&amp;gt;&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
Шаг алгоритма заключается в разделении входного множества вершин на два домена. Получается, что граф исполнения шагов представляет из себя бинарное дерево. Каждый шаг инициирует исполнение еще двух шагов.&lt;br /&gt;
[[File:allocator_graph.png|thumb|center|800px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
При этом на каждом шаге выполняются действия, описанные в п 1.2.&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Так как алгоритм работает с координатной сеткой – он обладает координатным параллелизмом.&lt;br /&gt;
Ярусно-параллельная форма алгоритма имеет высоту в &amp;lt;math&amp;gt;log_2 k&amp;lt;/math&amp;gt; уровней, где k – требуемое значение конечных доменов. Соответственно ширина яруса i будет равна &amp;lt;math&amp;gt;2^i&amp;lt;/math&amp;gt;. Так как ширина i-го яруса равна &amp;lt;math&amp;gt;2^i&amp;lt;/math&amp;gt;, а общее количество вершин примем за n – то в кажом домене на i-м ярусе будет по &amp;lt;math&amp;gt;n / 2^i&amp;lt;/math&amp;gt; вершин. Если на каждом шаге использовать  параллельную сортировку c хорошей сложностью &amp;lt;math&amp;gt;(n/p)(log_2(n/p)+log_2^2(p))&amp;lt;/math&amp;gt; (где n - количество сортируемых элементов, p - количество процессоров), то получаем итоговую сложность алгоритма как сумму всех ярусов, на которых параллельно запускается сортировка.&lt;br /&gt;
А именно:&lt;br /&gt;
&amp;lt;math&amp;gt;\sum_{i=0}^{log_2 k} ((n / 2^i) / (p / 2^i)) (log_2((n / 2^i) / (p / 2^i)) + log_2^2(p / 2^i))  = O((n/p) * (log_2 (n/p) + log_2^2(p)) * log_2 k)&amp;lt;/math&amp;gt;&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: требуемое количество доменов в разбиении, вершины графа. Каждой вершине соответствуют некоторые многомерные числовые координаты. Вершины могут быть произвольно разбиты по вычислительным узлам. Таким образом, &#039;&#039;&#039;объём входных данных&#039;&#039;&#039; пропорционален размеру графа V и количеству измерений N: |V|*N.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: p доменов, разбивающих граф; при этом вершины этих доменов хранятся последовательно в исходном массиве. При количестве узлов, используемых для декомпозиции, совпадающим с количеством узлов, используемых для дальнейших вычислений, это автоматически размещает всю информацию о вершинах на вычислительных узлах, которые будут обрабатывать эти вершины. Таким образом, &#039;&#039;&#039;объём выходных данных&#039;&#039;&#039; пропорционален количеству вершин в исходном графе: |V|.&lt;br /&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; данного алгоритма может быть достигнута, если использовать устойчивый алгоритм сортировки.&lt;br /&gt;
&lt;br /&gt;
Так как процессы обрабатывают примерно равное количество вершин, полученный алгоритм является &#039;&#039;&#039;сбалансированным&#039;&#039;&#039; по числу операций на каждом процессе.&lt;br /&gt;
&lt;br /&gt;
Вычислительная мощность алгоритма, равная отношению количества операций к количеству входных и выходных данных равна &amp;lt;math&amp;gt;(|V|/P+P) * log_2(|V|/P + P) * log_2(K) / (|V|N) = O(P * log_2(|V|/P + P) * log_2(K)) / N&amp;lt;/math&amp;gt;. Если принять количество вычислительных узлов P и количество измерений N за константы, вычислительная мощность будет равна &amp;lt;math&amp;gt;O(log_2(|V|) * log_2(K))&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;
Открытые параллельные и последовательные реализации существуют в пакете алгоритмов декомпозиции METIS и ParMETIS: http://glaros.dtc.umn.edu/gkhome/software&lt;br /&gt;
&lt;br /&gt;
Кроме того, он пристутсвует в библиотеках Zoltan (https://www.cs.sandia.gov/zoltan/), Scotch и PT-Scotch (https://www.labri.fr/perso/pelegrin/scotch/).&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;/div&gt;</summary>
		<author><name>Allocator</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Allocator_graph.png&amp;diff=22242</id>
		<title>Файл:Allocator graph.png</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Allocator_graph.png&amp;diff=22242"/>
		<updated>2017-02-06T13:14:18Z</updated>

		<summary type="html">&lt;p&gt;Allocator: Allocator загрузил новую версию Файл:Allocator graph.png&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Граф исполнения метода рекурсивной координатной бисекции&lt;/div&gt;</summary>
		<author><name>Allocator</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Allocator/%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D1%80%D0%B5%D0%BA%D1%83%D1%80%D1%81%D0%B8%D0%B2%D0%BD%D0%BE%D0%B9_%D0%BA%D0%BE%D0%BE%D1%80%D0%B4%D0%B8%D0%BD%D0%B0%D1%82%D0%BD%D0%BE%D0%B9_%D0%B1%D0%B8%D1%81%D0%B5%D0%BA%D1%86%D0%B8%D0%B8&amp;diff=22235</id>
		<title>Участник:Allocator/Метод рекурсивной координатной бисекции</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Allocator/%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D1%80%D0%B5%D0%BA%D1%83%D1%80%D1%81%D0%B8%D0%B2%D0%BD%D0%BE%D0%B9_%D0%BA%D0%BE%D0%BE%D1%80%D0%B4%D0%B8%D0%BD%D0%B0%D1%82%D0%BD%D0%BE%D0%B9_%D0%B1%D0%B8%D1%81%D0%B5%D0%BA%D1%86%D0%B8%D0%B8&amp;diff=22235"/>
		<updated>2017-02-06T12:42:03Z</updated>

		<summary type="html">&lt;p&gt;Allocator: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Авторы: Долгов Борис (624), Галуза Дмитрий (624)&lt;br /&gt;
= Свойства и структура алгоритмов =&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
Декомпозиция графа — это разбиение множества вершин графа на подмножества, для обработки каждого из подмножеств на отдельном вычислительном узле.&lt;br /&gt;
Декомпозиция больших графов (то есть графов, объём описания которых превышает память, доступную одному узлу) происходит в два этапа: выполнение первоначального разбиения вершин графа по вычислительным процессорам, затем — выполнение одного из параллельных алгоритмов декомпозиции. Алгоритм координатной рекурсивной бисекции даёт хорошее первичное распределение узлов сетки по сравнению с простым распределением узлов по номерам и, как правило, используется как первый шаг более сложных алгоритмов &amp;lt;ref&amp;gt;Якобовский М.В. Введение в параллельные методы решения задач: Учебное пособие / Предисл.: В. А. Садовничий. – М.: Издательство Московского университета, 2012. – 328 с., илл. – (Серия «Суперкомпьютерное образование»), ISBN 978-5-211-06382-2 URL: http://lira.imamod.ru/ITTPMOPS/ &amp;lt;/ref&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Данный алгоритм использует стратегию рекурсивной бисекции. В соответствии с ней, граф последовательно за k-1 шаг делится на k частей. На первом шаге исходный граф делится на 2 части, на каждом следующем шаге – одна из ранее полученных частей делится ещё на две части. В качестве способа деления на две части используется разделение по координатам вершин. Для этого вершины сортируется вдоль выбранной координаты, и в отсортированном массиве разбиваются на две непрерывные части в заданной пропорции. &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&amp;lt;/math&amp;gt; -- множество вершин, &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt; -- множество рёбер. Каждой вершине соответствует целочисленный координатный вектор &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt;, вес &amp;lt;math&amp;gt;v_i&amp;lt;/math&amp;gt;. Кроме того, задано желаемое количество доменов &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt;.&lt;br /&gt;
Требуется разбить множество &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; на p непересекающихся подмножеств (доменов) &amp;lt;math&amp;gt;S_1, \ldots, S_p&amp;lt;/math&amp;gt;, при этом должны быть выполнены следующие требования:&lt;br /&gt;
Домены не пересекаются: &amp;lt;math&amp;gt;S_i \cap S_j = \emptyset&amp;lt;/math&amp;gt;&lt;br /&gt;
Каждая вершина находится в домене: &amp;lt;math&amp;gt; \cup _i S_i = V&amp;lt;/math&amp;gt;&lt;br /&gt;
Распределение вершин по доменам примерно равномерное: &amp;lt;math&amp;gt;|S_i| \approx |V| / p&amp;lt;/math&amp;gt;&lt;br /&gt;
Минимизирован вес разрезанных рёбер: &amp;lt;math&amp;gt;\sum_{i \neq j} \sum_{v_1 \in S_i} \sum_{v2_ \in S_j} e_{i, j} \rightarrow min&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;O(n 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;
* После сортировки, начальные K вершин относятся к первому домену, остальные -- ко второму (K выбирается исходя из общего количества вершин и требуемой пропорции деления)&lt;br /&gt;
* При необходимости, каждый из кластеров может быть разделён на подкластеры рекурсивным вызовом этого же алгоритма&lt;br /&gt;
&lt;br /&gt;
При использовании алгоритма сортировки со сложностью &amp;lt;math&amp;gt;S(N)&amp;lt;/math&amp;gt;, сложность алгоритма составит &amp;lt;math&amp;gt;p * (S(N) + N)&amp;lt;/math&amp;gt;&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
&amp;lt;source lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Шаг алгоритма, принимающий решение о разделении вершин.&lt;br /&gt;
# Разбивает граф, передаваемый в переменной graf и имеющий n вершин, на два подграфа&lt;br /&gt;
# subgraf1 и subgraf2, имеющих n1 и n2 вершин соответственно.&lt;br /&gt;
Bisect(graf, n, subgraf1, n1, subgraf2, n2) {&lt;br /&gt;
  # Найти самую протяжённую координату, то есть такое i, для которого разница&lt;br /&gt;
  # между максимальным и минимальным значением данной координаты по всем вершинам наибольшая.&lt;br /&gt;
  dim = argmax(i, max(j, graf[j][i]) - min(j, graf[j][i]));&lt;br /&gt;
  # Отсортировать вершины, используя в качестве ключа значение этой координаты&lt;br /&gt;
  sort(graf, dim);&lt;br /&gt;
  # Разбить граф на два домена, при этом первые n1 вершин попадут в первый домен, оставшиеся -- во второй.&lt;br /&gt;
  subgraf1 = graf[0..n1-1];&lt;br /&gt;
  subgraf2 = graf[n1..n-1];&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# Схема рекурсивной бисекции -- общая часть всех алгоритмов рекурсивной бисекции&lt;br /&gt;
# Разбивает граф, передаваемый в аргументе graf и имеющий n вершин&lt;br /&gt;
# на k примерно равных доменов, возвращаемых в переменной domains.&lt;br /&gt;
RecursiveBisect(graf, n, k, domains) {&lt;br /&gt;
  # Найти количество конечных доменов, присутствующих в первой и второй части разбиения на текущем шаге.&lt;br /&gt;
  k1 = (k+1)/2;&lt;br /&gt;
  k2 = k-k1;&lt;br /&gt;
  # Найти количество вершин в первой и второй части разбиения.&lt;br /&gt;
  n1=n*k1/k;&lt;br /&gt;
  n2=n-n1;&lt;br /&gt;
  # Выполнить разбиение на две части в соответствии с алгоритмом координатной бисекции.&lt;br /&gt;
  Bisect(graf, n, subgraf1, n1, subgraf2, n2);&lt;br /&gt;
  # В случае, если в части присутствует больше одного конечного домена, рекурсивно повторить алгоритм.&lt;br /&gt;
  # В противном случае, найденный домен будет входить в ответ данного алгоритма.&lt;br /&gt;
  If (k1&amp;gt;1) RecursiveBisect(subgraf1, n1, k1); else domains += subgraf1;&lt;br /&gt;
  If (k2&amp;gt;1) RecursiveBisect(subgraf2, n2, k2); else domains += subgraf2;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Как было описано ранее, для разбиения графа размером n на p кластеров потребуется p рекурсивных шагов. Таким образом, при использовании оптимальной стратегии разбиения, время работы можно выразить рекуррентным соотношением &amp;lt;math&amp;gt;T(N) = O(N*log_2(N)) + 2 * T(N/2)&amp;lt;/math&amp;gt;. Согласно основной теореме о рекуррентных соотношениях &amp;lt;ref&amp;gt; Основная теорема о рекуррентных соотношениях URL: https://ru.wikipedia.org/wiki/%D0%9E%D1%81%D0%BD%D0%BE%D0%B2%D0%BD%D0%B0%D1%8F_%D1%82%D0%B5%D0%BE%D1%80%D0%B5%D0%BC%D0%B0_%D0%BE_%D1%80%D0%B5%D0%BA%D1%83%D1%80%D1%80%D0%B5%D0%BD%D1%82%D0%BD%D1%8B%D1%85_%D1%81%D0%BE%D0%BE%D1%82%D0%BD%D0%BE%D1%88%D0%B5%D0%BD%D0%B8%D1%8F%D1%85#.D0.92.D0.B0.D1.80.D0.B8.D0.B0.D0.BD.D1.82_2&amp;lt;/ref&amp;gt; , &amp;lt;math&amp;gt;T(N) = O(N*log_2(N)*log_2(N))&amp;lt;/math&amp;gt; в худшем случае (при N = P). При меньшем количестве кластеров, глубина рекурсии будет ограничена &amp;lt;math&amp;gt;log_2(P)&amp;lt;/math&amp;gt;, следовательно, время работы алгоритма составит &amp;lt;math&amp;gt;O(log_2(p) * N * log_2(N))&amp;lt;/math&amp;gt;&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
Шаг алгоритма заключается в разделении входного множества вершин на два домена. Получается, что граф исполнения шагов представляет из себя бинарное дерево. Каждый шаг инициирует исполнение еще двух шагов.&lt;br /&gt;
[[File:allocator_graph.png|thumb|center|800px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
При этом на каждом шаге выполняются действия, описанные в п 1.2.&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Так как алгоритм работает с координатной сеткой – он обладает координатным параллелизмом.&lt;br /&gt;
Ярусно-параллельная форма алгоритма имеет высоту в &amp;lt;math&amp;gt;log_2 k&amp;lt;/math&amp;gt; уровней, где k – требуемое значение конечных доменов. Соответственно ширина яруса i будет равна &amp;lt;math&amp;gt;2^i&amp;lt;/math&amp;gt;. Так как ширина i-го яруса равна &amp;lt;math&amp;gt;2^i&amp;lt;/math&amp;gt;, а общее количество вершин примем за n – то в кажом домене на i-м ярусе будет по &amp;lt;math&amp;gt;n / 2^i&amp;lt;/math&amp;gt; вершин. Если на каждом шаге использовать оптимальную параллельную сортировку &amp;lt;ref&amp;gt;URL: Supercomputer Software Department, Математическое моделирование: параллельные алгоритмы, Параллельные алгоритмы сортировки URL: http://ssd.sscc.ru/ru/content/%D0%BF%D0%B0%D1%80%D0%B0%D0%BB%D0%BB%D0%B5%D0%BB%D1%8C%D0%BD%D1%8B%D0%B5-%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D1%8B-%D1%81%D0%BE%D1%80%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%BA%D0%B8&amp;lt;/ref&amp;gt;, сложность которой равна &amp;lt;math&amp;gt;(n/p+p)*log_2 (n/p)&amp;lt;/math&amp;gt; (где n - количество сортируемых элементов, p - количество процессоров), то получаем итоговую сложность алгоритма как сумму всех ярусов, на которых параллельно запускается оптимальная сортировка.&lt;br /&gt;
А именно:&lt;br /&gt;
&amp;lt;math&amp;gt;\sum_{i=0}^{log_2 k} (n / p + p) / 2^i * log_2 ((n / p + p) / 2^i) = O((n/p + p) * log_2 (n/p + p) * log_2 k)&amp;lt;/math&amp;gt;&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: требуемое количество доменов в разбиении, вершины графа. Каждой вершине соответствуют некоторые многомерные числовые координаты. Вершины могут быть произвольно разбиты по вычислительным узлам. Таким образом, &#039;&#039;&#039;объём входных данных&#039;&#039;&#039; пропорционален размеру графа V и количеству измерений N: |V|*N.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: p доменов, разбивающих граф; при этом вершины этих доменов хранятся последовательно в исходном массиве. При количестве узлов, используемых для декомпозиции, совпадающим с количеством узлов, используемых для дальнейших вычислений, это автоматически размещает всю информацию о вершинах на вычислительных узлах, которые будут обрабатывать эти вершины. Таким образом, &#039;&#039;&#039;объём выходных данных&#039;&#039;&#039; пропорционален количеству вершин в исходном графе: |V|.&lt;br /&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; данного алгоритма может быть достигнута, если использовать устойчивый алгоритм сортировки.&lt;br /&gt;
&lt;br /&gt;
Так как процессы обрабатывают примерно равное количество вершин, полученный алгоритм является &#039;&#039;&#039;сбалансированным&#039;&#039;&#039; по числу операций на каждом процессе.&lt;br /&gt;
&lt;br /&gt;
Вычислительная мощность алгоритма, равная отношению количества операций к количеству входных и выходных данных равна &amp;lt;math&amp;gt;(|V|/P+P) * log_2(|V|/P + P) * log_2(K) / (|V|N) = O(P * log_2(|V|/P + P) * log_2(K)) / N&amp;lt;/math&amp;gt;. Если принять количество вычислительных узлов P и количество измерений N за константы, вычислительная мощность будет равна &amp;lt;math&amp;gt;O(log_2(|V|) * log_2(K))&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;
Открытые параллельные и последовательные реализации существуют в пакете алгоритмов декомпозиции METIS и ParMETIS: http://glaros.dtc.umn.edu/gkhome/software&lt;br /&gt;
&lt;br /&gt;
Кроме того, он пристутсвует в библиотеках Zoltan (https://www.cs.sandia.gov/zoltan/), Scotch и PT-Scotch (https://www.labri.fr/perso/pelegrin/scotch/).&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;/div&gt;</summary>
		<author><name>Allocator</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Allocator/%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D1%80%D0%B5%D0%BA%D1%83%D1%80%D1%81%D0%B8%D0%B2%D0%BD%D0%BE%D0%B9_%D0%BA%D0%BE%D0%BE%D1%80%D0%B4%D0%B8%D0%BD%D0%B0%D1%82%D0%BD%D0%BE%D0%B9_%D0%B1%D0%B8%D1%81%D0%B5%D0%BA%D1%86%D0%B8%D0%B8&amp;diff=22120</id>
		<title>Участник:Allocator/Метод рекурсивной координатной бисекции</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Allocator/%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D1%80%D0%B5%D0%BA%D1%83%D1%80%D1%81%D0%B8%D0%B2%D0%BD%D0%BE%D0%B9_%D0%BA%D0%BE%D0%BE%D1%80%D0%B4%D0%B8%D0%BD%D0%B0%D1%82%D0%BD%D0%BE%D0%B9_%D0%B1%D0%B8%D1%81%D0%B5%D0%BA%D1%86%D0%B8%D0%B8&amp;diff=22120"/>
		<updated>2017-02-03T17:43:23Z</updated>

		<summary type="html">&lt;p&gt;Allocator: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment}}&lt;br /&gt;
Авторы: Долгов Борис (624), Галуза Дмитрий (624)&lt;br /&gt;
= Свойства и структура алгоритмов =&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
Декомпозиция графа — это разбиение множества вершин графа на подмножества, для обработки каждого из подмножеств на отдельном вычислительном узле.&lt;br /&gt;
Декомпозиция больших графов (то есть графов, объём описания которых превышает память, доступную одному узлу) происходит в два этапа: выполнение первоначального разбиения вершин графа по вычислительным процессорам, затем — выполнение одного из параллельных алгоритмов декомпозиции. Алгоритм координатной рекурсивной бисекции даёт хорошее первичное распределение узлов сетки по сравнению с простым распределением узлов по номерам и, как правило, используется как первый шаг более сложных алгоритмов &amp;lt;ref&amp;gt;Якобовский М.В. Введение в параллельные методы решения задач: Учебное пособие / Предисл.: В. А. Садовничий. – М.: Издательство Московского университета, 2012. – 328 с., илл. – (Серия «Суперкомпьютерное образование»), ISBN 978-5-211-06382-2 URL: http://lira.imamod.ru/ITTPMOPS/ &amp;lt;/ref&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Данный алгоритм использует стратегию рекурсивной бисекции. В соответствии с ней, граф последовательно за k-1 шаг делится на k частей. На первом шаге исходный граф делится на 2 части, на каждом следующем шаге – одна из ранее полученных частей делится ещё на две части. В качестве способа деления на две части используется разделение по координатам вершин. Для этого вершины сортируется вдоль выбранной координаты, и в отсортированном массиве разбиваются на две непрерывные части в заданной пропорции. &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&amp;lt;/math&amp;gt; -- множество вершин, &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt; -- множество рёбер. Каждой вершине соответствует целочисленный координатный вектор &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt;, вес &amp;lt;math&amp;gt;v_i&amp;lt;/math&amp;gt;. Кроме того, задано желаемое количество доменов &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt;.&lt;br /&gt;
Требуется разбить множество &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; на p непересекающихся подмножеств (доменов) &amp;lt;math&amp;gt;S_1, \ldots, S_p&amp;lt;/math&amp;gt;, при этом должны быть выполнены следующие требования:&lt;br /&gt;
Домены не пересекаются: &amp;lt;math&amp;gt;S_i \cap S_j = \emptyset&amp;lt;/math&amp;gt;&lt;br /&gt;
Каждая вершина находится в домене: &amp;lt;math&amp;gt; \cup _i S_i = V&amp;lt;/math&amp;gt;&lt;br /&gt;
Распределение вершин по доменам примерно равномерное: &amp;lt;math&amp;gt;|S_i| \approx |V| / p&amp;lt;/math&amp;gt;&lt;br /&gt;
Минимизирован вес разрезанных рёбер: &amp;lt;math&amp;gt;\sum_{i \neq j} \sum_{v_1 \in S_i} \sum_{v2_ \in S_j} e_{i, j} \rightarrow min&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;O(n log_2{n})&amp;lt;/math&amp;gt;. Поиск максимально протяжённой координатной оси выполняется за линейное время, а разбиение вершин на два домена – за константное.&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Алгоритм декомпозиции на каждом шаге может использовать любой из параллельных алгоритмов сортировки.&lt;br /&gt;
&lt;br /&gt;
При использовании алгоритма сортировки со сложностью &amp;lt;math&amp;gt;S(N)&amp;lt;/math&amp;gt;, сложность алгоритма составит &amp;lt;math&amp;gt;p * (S(N) + N)&amp;lt;/math&amp;gt;&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
&amp;lt;source lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Шаг алгоритма&lt;br /&gt;
Bisect(graf, n, subgraf1, n1, subgraf2, n2) {&lt;br /&gt;
  dim = argmax(i, max(j, graf[j][i]) - min(j, graf[j][i]));&lt;br /&gt;
  sort(graf, dim);&lt;br /&gt;
  subgraf1 = graf[0..n1];&lt;br /&gt;
  subgraf2 = graf[n1+1..n-1];&lt;br /&gt;
}&lt;br /&gt;
# Схема рекурсивной бисекции&lt;br /&gt;
RecursiveBisect(graf, n, k) {&lt;br /&gt;
  k1 = (k1+1)/2;&lt;br /&gt;
  k2 = k-k1;&lt;br /&gt;
  n1=n*k1/k;&lt;br /&gt;
  n2=n-n1;&lt;br /&gt;
  Bisect(graf, n, subgraf1, n1, subgraf2, n2);&lt;br /&gt;
  If (k1&amp;gt;1) RecursiveBisect(subgraf1, n1, k1);&lt;br /&gt;
  If (k2&amp;gt;1) RecursiveBisect(subgraf2, n2, k2);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Как было описано ранее, для разбиения графа размером n на p кластеров потребуется p рекурсивных шагов. Таким образом, при использовании оптимальной стратегии разбиения, время работы можно выразить рекуррентным соотношением &amp;lt;math&amp;gt;T(N) = O(N*log_2(N)) + 2 * T(N/2)&amp;lt;/math&amp;gt;. Согласно основной теореме о рекуррентных соотношениях &amp;lt;ref&amp;gt; Основная теорема о рекуррентных соотношениях URL: https://ru.wikipedia.org/wiki/%D0%9E%D1%81%D0%BD%D0%BE%D0%B2%D0%BD%D0%B0%D1%8F_%D1%82%D0%B5%D0%BE%D1%80%D0%B5%D0%BC%D0%B0_%D0%BE_%D1%80%D0%B5%D0%BA%D1%83%D1%80%D1%80%D0%B5%D0%BD%D1%82%D0%BD%D1%8B%D1%85_%D1%81%D0%BE%D0%BE%D1%82%D0%BD%D0%BE%D1%88%D0%B5%D0%BD%D0%B8%D1%8F%D1%85#.D0.92.D0.B0.D1.80.D0.B8.D0.B0.D0.BD.D1.82_2&amp;lt;/ref&amp;gt; , &amp;lt;math&amp;gt;T(N) = O(N*log_2(N)*log_2(N))&amp;lt;/math&amp;gt; в худшем случае (при N = P). При меньшем количестве кластеров, глубина рекурсии будет ограничена &amp;lt;math&amp;gt;log_2(P)&amp;lt;/math&amp;gt;, следовательно, время работы алгоритма составит &amp;lt;math&amp;gt;O(log_2(p) * N * log_2(N))&amp;lt;/math&amp;gt;&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
Шаг алгоритма заключается в разделении входного множества вершин на два домена. Получается, что граф исполнения шагов представляет из себя бинарное дерево. Каждый шаг инициирует исполнение еще двух шагов.&lt;br /&gt;
[[File:allocator_graph.png|thumb|center|800px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
При этом на каждом шаге выполняются действия, описанные в п 1.2.&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Так как алгоритм работает с координатной сеткой – он обладает координатным параллелизмом.&lt;br /&gt;
Ярусно-параллельная форма алгоритма имеет высоту в &amp;lt;math&amp;gt;log_2 k&amp;lt;/math&amp;gt; уровней, где k – требуемое значение конечных доменов. Соответственно ширина яруса i будет равна &amp;lt;math&amp;gt;2^i&amp;lt;/math&amp;gt;. Так как ширина i-го яруса равна &amp;lt;math&amp;gt;2^i&amp;lt;/math&amp;gt;, а общее количество вершин примем за n – то в кажом домене на i-м ярусе будет по &amp;lt;math&amp;gt;n / 2^i&amp;lt;/math&amp;gt; вершин. Если на каждом шаге использовать оптимальную параллельную сортировку &amp;lt;ref&amp;gt;URL: Supercomputer Software Department, Математическое моделирование: параллельные алгоритмы, Параллельные алгоритмы сортировки URL: http://ssd.sscc.ru/ru/content/%D0%BF%D0%B0%D1%80%D0%B0%D0%BB%D0%BB%D0%B5%D0%BB%D1%8C%D0%BD%D1%8B%D0%B5-%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D1%8B-%D1%81%D0%BE%D1%80%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%BA%D0%B8&amp;lt;/ref&amp;gt;, сложность которой равна &amp;lt;math&amp;gt;(n/p+p)*log_2 (n/p)&amp;lt;/math&amp;gt; (где n - количество сортируемых элементов, p - количество процессоров), то получаем итоговую сложность алгоритма как сумму всех ярусов, на которых параллельно запускается оптимальная сортировка.&lt;br /&gt;
А именно:&lt;br /&gt;
&amp;lt;math&amp;gt;\sum_{i=0}^{log_2 k} (n / p + p) / 2^i * log_2 ((n / p + p) / 2^i) =O((n/p + p) * log_2 (n/p + p) * log_2 k)&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;: p доменов, разбивающих граф; при этом вершины этих доменов хранятся последовательно в исходном массиве. При количестве узлов, используемых для декомпозиции, совпадающим с количеством узлов, используемых для дальнейших вычислений, это автоматически размещает всю информацию о вершинах на вычислительных узлах, которые будут обрабатывать эти вершины.&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
Приведенный алгоритм является &#039;&#039;&#039;устойчивым&#039;&#039;&#039; по построению, так как отсутствуют операции, которые могут нарушить устойчивость (например, вычисления с вещественными числами с плавающей точкой). Работа ведется с целочисленными координатами, что приводит к исключению ошибок округления и прочих побочным эффектам.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Детерминированность&#039;&#039;&#039; данного алгоритма может быть достигнута, если использовать устойчивый алгоритм сортировки.&lt;br /&gt;
&lt;br /&gt;
Так как процессы обрабатывают примерно равное количество вершин, полученный алгоритм является &#039;&#039;&#039;сбалансированным&#039;&#039;&#039; по числу операций на каждом процессе.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;/div&gt;</summary>
		<author><name>Allocator</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Allocator/%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D1%80%D0%B5%D0%BA%D1%83%D1%80%D1%81%D0%B8%D0%B2%D0%BD%D0%BE%D0%B9_%D0%BA%D0%BE%D0%BE%D1%80%D0%B4%D0%B8%D0%BD%D0%B0%D1%82%D0%BD%D0%BE%D0%B9_%D0%B1%D0%B8%D1%81%D0%B5%D0%BA%D1%86%D0%B8%D0%B8&amp;diff=22119</id>
		<title>Участник:Allocator/Метод рекурсивной координатной бисекции</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Allocator/%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D1%80%D0%B5%D0%BA%D1%83%D1%80%D1%81%D0%B8%D0%B2%D0%BD%D0%BE%D0%B9_%D0%BA%D0%BE%D0%BE%D1%80%D0%B4%D0%B8%D0%BD%D0%B0%D1%82%D0%BD%D0%BE%D0%B9_%D0%B1%D0%B8%D1%81%D0%B5%D0%BA%D1%86%D0%B8%D0%B8&amp;diff=22119"/>
		<updated>2017-02-03T17:21:06Z</updated>

		<summary type="html">&lt;p&gt;Allocator: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment}}&lt;br /&gt;
Авторы: Долгов Борис (624), Галуза Дмитрий (624)&lt;br /&gt;
= Свойства и структура алгоритмов =&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
Декомпозиция графа — это разбиение множества вершин графа на подмножества, для обработки каждого из подмножеств на отдельном вычислительном узле.&lt;br /&gt;
Декомпозиция больших графов (то есть графов, объём описания которых превышает память, доступную одному узлу) происходит в два этапа: выполнение первоначального разбиения вершин графа по вычислительным процессорам, затем — выполнение одного из параллельных алгоритмов декомпозиции. Алгоритм координатной рекурсивной бисекции даёт хорошее первичное распределение узлов сетки по сравнению с простым распределением узлов по номерам и, как правило, используется как первый шаг более сложных алгоритмов.&lt;br /&gt;
&lt;br /&gt;
Данный алгоритм использует стратегию рекурсивной бисекции. В соответствии с ней, граф последовательно за k-1 шаг делится на k частей. На первом шаге исходный граф делится на 2 части, на каждом следующем шаге – одна из ранее полученных частей делится ещё на две части. В качестве способа деления на две части используется разделение по координатам вершин. Для этого вершины сортируется вдоль выбранной координаты, и в отсортированном массиве разбиваются на две непрерывные части в заданной пропорции. &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&amp;lt;/math&amp;gt; -- множество вершин, &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt; -- множество рёбер. Каждой вершине соответствует целочисленный координатный вектор &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt;, вес &amp;lt;math&amp;gt;v_i&amp;lt;/math&amp;gt;. Кроме того, задано желаемое количество доменов &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt;.&lt;br /&gt;
Требуется разбить множество &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; на p непересекающихся подмножеств (доменов) &amp;lt;math&amp;gt;S_1, \ldots, S_p&amp;lt;/math&amp;gt;, при этом должны быть выполнены следующие требования:&lt;br /&gt;
Домены не пересекаются: &amp;lt;math&amp;gt;S_i \cap S_j = \emptyset&amp;lt;/math&amp;gt;&lt;br /&gt;
Каждая вершина находится в домене: &amp;lt;math&amp;gt; \cup _i S_i = V&amp;lt;/math&amp;gt;&lt;br /&gt;
Распределение вершин по доменам примерно равномерное: &amp;lt;math&amp;gt;|S_i| \approx |V| / p&amp;lt;/math&amp;gt;&lt;br /&gt;
Минимизирован вес разрезанных рёбер: &amp;lt;math&amp;gt;\sum_{i \neq j} \sum_{v_1 \in S_i} \sum_{v2_ \in S_j} e_{i, j} \rightarrow min&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;O(n log_2{n})&amp;lt;/math&amp;gt;. Поиск максимально протяжённой координатной оси выполняется за линейное время, а разбиение вершин на два домена – за константное.&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Алгоритм декомпозиции на каждом шаге может использовать любой из параллельных алгоритмов сортировки.&lt;br /&gt;
&lt;br /&gt;
При использовании алгоритма сортировки со сложностью &amp;lt;math&amp;gt;S(N)&amp;lt;/math&amp;gt;, сложность алгоритма составит &amp;lt;math&amp;gt;p * (S(N) + N)&amp;lt;/math&amp;gt;&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
&amp;lt;source lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Шаг алгоритма&lt;br /&gt;
Bisect(graf, n, subgraf1, n1, subgraf2, n2) {&lt;br /&gt;
  dim = argmax(i, max(j, graf[j][i]) - min(j, graf[j][i]));&lt;br /&gt;
  sort(graf, dim);&lt;br /&gt;
  subgraf1 = graf[0..n1];&lt;br /&gt;
  subgraf2 = graf[n1+1..n-1];&lt;br /&gt;
}&lt;br /&gt;
# Схема рекурсивной бисекции&lt;br /&gt;
RecursiveBisect(graf, n, k) {&lt;br /&gt;
  k1 = (k1+1)/2;&lt;br /&gt;
  k2 = k-k1;&lt;br /&gt;
  n1=n*k1/k;&lt;br /&gt;
  n2=n-n1;&lt;br /&gt;
  Bisect(graf, n, subgraf1, n1, subgraf2, n2);&lt;br /&gt;
  If (k1&amp;gt;1) RecursiveBisect(subgraf1, n1, k1);&lt;br /&gt;
  If (k2&amp;gt;1) RecursiveBisect(subgraf2, n2, k2);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Как было описано ранее, для разбиения графа размером n на p кластеров потребуется p рекурсивных шагов. Таким образом, при использовании оптимальной стратегии разбиения, время работы можно выразить рекуррентным соотношением &amp;lt;math&amp;gt;T(N) = O(N*log_2(N)) + 2 * T(N/2)&amp;lt;/math&amp;gt;. Согласно основной теореме о рекуррентных соотношениях ссылка , &amp;lt;math&amp;gt;T(N) = O(N*log_2(N)*log_2(N))&amp;lt;/math&amp;gt; в худшем случае (при N = P). При меньшем количестве кластеров, глубина рекурсии будет ограничена &amp;lt;math&amp;gt;log_2(P)&amp;lt;/math&amp;gt;, следовательно, время работы алгоритма составит &amp;lt;math&amp;gt;O(log_2(p) * N * log_2(N))&amp;lt;/math&amp;gt;&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
Шаг алгоритма заключается в разделении входного множества вершин на два домена. Получается, что граф исполнения шагов представляет из себя бинарное дерево. Каждый шаг инициирует исполнение еще двух шагов.&lt;br /&gt;
[[File:allocator_graph.png|thumb|center|800px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
При этом на каждом шаге выполняются действия, описанные в п 1.2.&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Так как алгоритм работает с координатной сеткой – он обладает координатным параллелизмом.&lt;br /&gt;
Ярусно-параллельная форма алгоритма имеет высоту в &amp;lt;math&amp;gt;log_2 k&amp;lt;/math&amp;gt; уровней, где k – требуемое значение конечных доменов. Соответственно ширина яруса i будет равна &amp;lt;math&amp;gt;2^i&amp;lt;/math&amp;gt;. Так как ширина i-го яруса равна &amp;lt;math&amp;gt;2^i&amp;lt;/math&amp;gt;, а общее количество вершин примем за n – то в кажом домене на i-м ярусе будет по &amp;lt;math&amp;gt;n / 2^i&amp;lt;/math&amp;gt; вершин. Если на каждом шаге использовать оптимальную параллельную сортировку, сложность которой равна &amp;lt;math&amp;gt;(n/p+p)*log_2 (n/p)&amp;lt;/math&amp;gt; (где n - количество сортируемых элементов, p - количество процессоров), то получаем итоговую сложность алгоритма как сумму всех ярусов, на которых параллельно запускается оптимальная сортировка.&lt;br /&gt;
А именно:&lt;br /&gt;
&amp;lt;math&amp;gt;\sum_{i=0}^{log_2 k} (n / p + p) / 2^i * log_2 ((n / p + p) / 2^i) =O((n/p + p) * log_2 (n/p + p) * log_2 k)&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;: p доменов, разбивающих граф; при этом вершины этих доменов хранятся последовательно в исходном массиве. При количестве узлов, используемых для декомпозиции, совпадающим с количеством узлов, используемых для дальнейших вычислений, это автоматически размещает всю информацию о вершинах на вычислительных узлах, которые будут обрабатывать эти вершины.&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
Приведенный алгоритм является &#039;&#039;&#039;устойчивым&#039;&#039;&#039; по построению, так как отсутствуют операции, которые могут нарушить устойчивость (например, вычисления с вещественными числами с плавающей точкой). Работа ведется с целочисленными координатами, что приводит к исключению ошибок округления и прочих побочным эффектам.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Детерминированность&#039;&#039;&#039; данного алгоритма может быть достигнута, если использовать устойчивый алгоритм сортировки.&lt;br /&gt;
&lt;br /&gt;
Так как процессы обрабатывают примерно равное количество вершин, полученный алгоритм является &#039;&#039;&#039;сбалансированным&#039;&#039;&#039; по числу операций на каждом процессе.&lt;/div&gt;</summary>
		<author><name>Allocator</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Allocator/%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D1%80%D0%B5%D0%BA%D1%83%D1%80%D1%81%D0%B8%D0%B2%D0%BD%D0%BE%D0%B9_%D0%BA%D0%BE%D0%BE%D1%80%D0%B4%D0%B8%D0%BD%D0%B0%D1%82%D0%BD%D0%BE%D0%B9_%D0%B1%D0%B8%D1%81%D0%B5%D0%BA%D1%86%D0%B8%D0%B8&amp;diff=22118</id>
		<title>Участник:Allocator/Метод рекурсивной координатной бисекции</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Allocator/%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D1%80%D0%B5%D0%BA%D1%83%D1%80%D1%81%D0%B8%D0%B2%D0%BD%D0%BE%D0%B9_%D0%BA%D0%BE%D0%BE%D1%80%D0%B4%D0%B8%D0%BD%D0%B0%D1%82%D0%BD%D0%BE%D0%B9_%D0%B1%D0%B8%D1%81%D0%B5%D0%BA%D1%86%D0%B8%D0%B8&amp;diff=22118"/>
		<updated>2017-02-03T17:19:08Z</updated>

		<summary type="html">&lt;p&gt;Allocator: Новая страница: «Авторы: Долгов Борис (624), Галуза Дмитрий (624) = Свойства и структура алгоритмов = == Общее оп…»&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Авторы: Долгов Борис (624), Галуза Дмитрий (624)&lt;br /&gt;
= Свойства и структура алгоритмов =&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
Декомпозиция графа — это разбиение множества вершин графа на подмножества, для обработки каждого из подмножеств на отдельном вычислительном узле.&lt;br /&gt;
Декомпозиция больших графов (то есть графов, объём описания которых превышает память, доступную одному узлу) происходит в два этапа: выполнение первоначального разбиения вершин графа по вычислительным процессорам, затем — выполнение одного из параллельных алгоритмов декомпозиции. Алгоритм координатной рекурсивной бисекции даёт хорошее первичное распределение узлов сетки по сравнению с простым распределением узлов по номерам и, как правило, используется как первый шаг более сложных алгоритмов.&lt;br /&gt;
&lt;br /&gt;
Данный алгоритм использует стратегию рекурсивной бисекции. В соответствии с ней, граф последовательно за k-1 шаг делится на k частей. На первом шаге исходный граф делится на 2 части, на каждом следующем шаге – одна из ранее полученных частей делится ещё на две части. В качестве способа деления на две части используется разделение по координатам вершин. Для этого вершины сортируется вдоль выбранной координаты, и в отсортированном массиве разбиваются на две непрерывные части в заданной пропорции. &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&amp;lt;/math&amp;gt; -- множество вершин, &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt; -- множество рёбер. Каждой вершине соответствует целочисленный координатный вектор &amp;lt;math&amp;gt;c_i&amp;lt;/math&amp;gt;, вес &amp;lt;math&amp;gt;v_i&amp;lt;/math&amp;gt;. Кроме того, задано желаемое количество доменов &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt;.&lt;br /&gt;
Требуется разбить множество &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; на p непересекающихся подмножеств (доменов) &amp;lt;math&amp;gt;S_1, \ldots, S_p&amp;lt;/math&amp;gt;, при этом должны быть выполнены следующие требования:&lt;br /&gt;
Домены не пересекаются: &amp;lt;math&amp;gt;S_i \cap S_j = \emptyset&amp;lt;/math&amp;gt;&lt;br /&gt;
Каждая вершина находится в домене: &amp;lt;math&amp;gt; \cup _i S_i = V&amp;lt;/math&amp;gt;&lt;br /&gt;
Распределение вершин по доменам примерно равномерное: &amp;lt;math&amp;gt;|S_i| \approx |V| / p&amp;lt;/math&amp;gt;&lt;br /&gt;
Минимизирован вес разрезанных рёбер: &amp;lt;math&amp;gt;\sum_{i \neq j} \sum_{v_1 \in S_i} \sum_{v2_ \in S_j} e_{i, j} \rightarrow min&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;O(n log_2{n})&amp;lt;/math&amp;gt;. Поиск максимально протяжённой координатной оси выполняется за линейное время, а разбиение вершин на два домена – за константное.&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Алгоритм декомпозиции на каждом шаге может использовать любой из параллельных алгоритмов сортировки.&lt;br /&gt;
&lt;br /&gt;
При использовании алгоритма сортировки со сложностью &amp;lt;math&amp;gt;S(N)&amp;lt;/math&amp;gt;, сложность алгоритма составит &amp;lt;math&amp;gt;p * (S(N) + N)&amp;lt;/math&amp;gt;&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
&amp;lt;source lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Шаг алгоритма&lt;br /&gt;
Bisect(graf, n, subgraf1, n1, subgraf2, n2) {&lt;br /&gt;
  dim = argmax(i, max(j, graf[j][i]) - min(j, graf[j][i]));&lt;br /&gt;
  sort(graf, dim);&lt;br /&gt;
  subgraf1 = graf[0..n1];&lt;br /&gt;
  subgraf2 = graf[n1+1..n-1];&lt;br /&gt;
}&lt;br /&gt;
# Схема рекурсивной бисекции&lt;br /&gt;
RecursiveBisect(graf, n, k) {&lt;br /&gt;
  k1 = (k1+1)/2;&lt;br /&gt;
  k2 = k-k1;&lt;br /&gt;
  n1=n*k1/k;&lt;br /&gt;
  n2=n-n1;&lt;br /&gt;
  Bisect(graf, n, subgraf1, n1, subgraf2, n2);&lt;br /&gt;
  If (k1&amp;gt;1) RecursiveBisect(subgraf1, n1, k1);&lt;br /&gt;
  If (k2&amp;gt;1) RecursiveBisect(subgraf2, n2, k2);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Как было описано ранее, для разбиения графа размером n на p кластеров потребуется p рекурсивных шагов. Таким образом, при использовании оптимальной стратегии разбиения, время работы можно выразить рекуррентным соотношением &amp;lt;math&amp;gt;T(N) = O(N*log_2(N)) + 2 * T(N/2)&amp;lt;/math&amp;gt;. Согласно основной теореме о рекуррентных соотношениях ссылка , &amp;lt;math&amp;gt;T(N) = O(N*log_2(N)*log_2(N))&amp;lt;/math&amp;gt; в худшем случае (при N = P). При меньшем количестве кластеров, глубина рекурсии будет ограничена &amp;lt;math&amp;gt;log_2(P)&amp;lt;/math&amp;gt;, следовательно, время работы алгоритма составит &amp;lt;math&amp;gt;O(log_2(p) * N * log_2(N))&amp;lt;/math&amp;gt;&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
Шаг алгоритма заключается в разделении входного множества вершин на два домена. Получается, что граф исполнения шагов представляет из себя бинарное дерево. Каждый шаг инициирует исполнение еще двух шагов.&lt;br /&gt;
[[File:allocator_graph.png|thumb|center|800px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
При этом на каждом шаге выполняются действия, описанные в п 1.2.&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Так как алгоритм работает с координатной сеткой – он обладает координатным параллелизмом.&lt;br /&gt;
Ярусно-параллельная форма алгоритма имеет высоту в &amp;lt;math&amp;gt;log_2 k&amp;lt;/math&amp;gt; уровней, где k – требуемое значение конечных доменов. Соответственно ширина яруса i будет равна &amp;lt;math&amp;gt;2^i&amp;lt;/math&amp;gt;. Так как ширина i-го яруса равна &amp;lt;math&amp;gt;2^i&amp;lt;/math&amp;gt;, а общее количество вершин примем за n – то в кажом домене на i-м ярусе будет по &amp;lt;math&amp;gt;n / 2^i&amp;lt;/math&amp;gt; вершин. Если на каждом шаге использовать оптимальную параллельную сортировку, сложность которой равна &amp;lt;math&amp;gt;(n/p+p)*log_2 (n/p)&amp;lt;/math&amp;gt; (где n - количество сортируемых элементов, p - количество процессоров), то получаем итоговую сложность алгоритма как сумму всех ярусов, на которых параллельно запускается оптимальная сортировка.&lt;br /&gt;
А именно:&lt;br /&gt;
&amp;lt;math&amp;gt;\sum_{i=0}^{log_2 k} (n / p + p) / 2^i * log_2 ((n / p + p) / 2^i) =O((n/p + p) * log_2 (n/p + p) * log_2 k)&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;: p доменов, разбивающих граф; при этом вершины этих доменов хранятся последовательно в исходном массиве. При количестве узлов, используемых для декомпозиции, совпадающим с количеством узлов, используемых для дальнейших вычислений, это автоматически размещает всю информацию о вершинах на вычислительных узлах, которые будут обрабатывать эти вершины.&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
Приведенный алгоритм является &#039;&#039;&#039;устойчивым&#039;&#039;&#039; по построению, так как отсутствуют операции, которые могут нарушить устойчивость (например, вычисления с вещественными числами с плавающей точкой). Работа ведется с целочисленными координатами, что приводит к исключению ошибок округления и прочих побочным эффектам.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Детерминированность&#039;&#039;&#039; данного алгоритма может быть достигнута, если использовать устойчивый алгоритм сортировки.&lt;br /&gt;
&lt;br /&gt;
Так как процессы обрабатывают примерно равное количество вершин, полученный алгоритм является &#039;&#039;&#039;сбалансированным&#039;&#039;&#039; по числу операций на каждом процессе.&lt;/div&gt;</summary>
		<author><name>Allocator</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Allocator_graph.png&amp;diff=22117</id>
		<title>Файл:Allocator graph.png</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Allocator_graph.png&amp;diff=22117"/>
		<updated>2017-02-03T16:37:19Z</updated>

		<summary type="html">&lt;p&gt;Allocator: Граф исполнения метода рекурсивной координатной бисекции&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Граф исполнения метода рекурсивной координатной бисекции&lt;/div&gt;</summary>
		<author><name>Allocator</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Allocator&amp;diff=22116</id>
		<title>Участник:Allocator</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Allocator&amp;diff=22116"/>
		<updated>2017-02-03T16:18:16Z</updated>

		<summary type="html">&lt;p&gt;Allocator: Новая страница: «Студент группы 624 кафедры АЯ ВМК МГУ»&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Студент группы 624 кафедры АЯ ВМК МГУ&lt;/div&gt;</summary>
		<author><name>Allocator</name></author>
	</entry>
</feed>