<?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=Teplov</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=Teplov"/>
	<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/Teplov"/>
	<updated>2026-10-22T16:10:30Z</updated>
	<subtitle>Вклад</subtitle>
	<generator>MediaWiki 1.43.5</generator>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%91%D0%B5%D0%BB%D0%BB%D0%BC%D0%B0%D0%BD%D0%B0-%D0%A4%D0%BE%D1%80%D0%B4%D0%B0&amp;diff=21739</id>
		<title>Алгоритм Беллмана-Форда</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%91%D0%B5%D0%BB%D0%BB%D0%BC%D0%B0%D0%BD%D0%B0-%D0%A4%D0%BE%D1%80%D0%B4%D0%B0&amp;diff=21739"/>
		<updated>2017-01-25T12:04:16Z</updated>

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

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

		<summary type="html">&lt;p&gt;Teplov: /* Динамические характеристики и эффективность реализации алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Приведение симметричной вещественной матрицы к трёхдиагональному виду методом Хаусхолдера (отражений)&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(n^3)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;2n^2+O(n)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;n^2/2&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;(n^2+n)/2&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;(n^2+3n)/2&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
Основные авторы описания: [[Участник:Frolov|А.В.Фролов]]&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Метод Хаусхолдера&#039;&#039;&#039; (в советской математической литературе чаще называется &#039;&#039;&#039;методом отражений&#039;&#039;&#039;) используется для приведения симметричных вещественных матриц к трёхдиагональному виду, или, что то же самое, для разложения &amp;lt;math&amp;gt;A=QTQ^T&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; - ортогональная, &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; — симметричная трёхдиагональная матрица)&amp;lt;ref&amp;gt;В.В.Воеводин, Ю.А.Кузнецов. Матрицы и вычисления. М.: Наука, 1984.&amp;lt;/ref&amp;gt;. При этом матрица &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; хранится и используется не в своём явном виде, а в виде произведения матриц отражения&amp;lt;ref name=&amp;quot;VOLA&amp;quot;&amp;gt;Воеводин В.В. Вычислительные основы линейной алгебры. М.: Наука, 1977.&amp;lt;/ref&amp;gt;. Каждая из матриц отражения может быть определена одним вектором. Это позволяет в классическом исполнении метода отражений хранить результаты разложения на месте матрицы A с использованием одномерного дополнительного массива. &lt;br /&gt;
&lt;br /&gt;
В данной статье рассматривается именно классическое исполнение, в котором не используются приёмы типа сдваивания при вычислениях скалярных произведений.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В методе Хаусхолдера для выполнения &amp;lt;math&amp;gt;QTQ^T&amp;lt;/math&amp;gt;-разложения матрицы используются умножения слева её текущих модификаций на матрицы Хаусхолдера (отражений) с последующим умножением на те же матрицы справа. &lt;br /&gt;
&lt;br /&gt;
{{Шаблон:Матрица отражений}}&lt;br /&gt;
&lt;br /&gt;
На &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м шаге метода с помощью преобразования отражения &amp;quot;убираются&amp;quot; ненулевые поддиагональные элементы, начиная с &amp;lt;math&amp;gt;i+2&amp;lt;/math&amp;gt;-го в &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м столбце. После умножения на эту же матрицу отражения справа автоматически убираются и ненулевые наддиагональные элементы, начиная с &amp;lt;math&amp;gt;i+2&amp;lt;/math&amp;gt;-го в &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-й строке, а полученная модификация снова приобретает симметричный вид. &lt;br /&gt;
&lt;br /&gt;
На каждом из шагов метода матрицу отражений обычно представляют не в стандартном виде, а в виде &amp;lt;math&amp;gt;U=E-\frac{1}{\gamma}vv^*&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; находится через координаты текущего &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; - вектор размерности &amp;lt;math&amp;gt;n+1-i&amp;lt;/math&amp;gt;, составленный из элементов &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца, начиная с &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;(s,s)=0&amp;lt;/math&amp;gt;, то &amp;lt;math&amp;gt;v=e_{i}&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\gamma = \frac{1}{2}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В остальных случаях по алгоритму вычисляется &amp;lt;math&amp;gt;u = \frac{1}{\sqrt{(s,s)}}s&amp;lt;/math&amp;gt;, и далее &amp;lt;math&amp;gt;v_{j}=0&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;j&amp;lt;i&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;v_{j}=u_{j-i+1}&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;j&amp;gt;i&amp;lt;/math&amp;gt;, а &amp;lt;math&amp;gt;v_{i}=1&amp;lt;/math&amp;gt;, если &amp;lt;math&amp;gt;u_{1}=0&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v_{i}=\frac{u_{1}}{|u_{1}|}(1+|u_{1}|)&amp;lt;/math&amp;gt; для остальных значений. При этом &amp;lt;math&amp;gt;\gamma = 1+|u_{1}|=|v_{i}|&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
После вычисления вектора &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; подстолбцы справа от ведущего модифицируются по формулам &amp;lt;math&amp;gt;x&#039;=x-\frac{(x,v)}{\gamma}v&amp;lt;/math&amp;gt;. Потом по аналогичным формулам модифицируются строки ниже ведущей. Благодаря ассоциативности этих операций после вычисления вектора &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; можно сразу выписать формулы модификации всех элементов справа и снизу от ведущих столбца и строки. Оказывается, что если для каждого столбца матрицы &amp;lt;math&amp;gt;x^{(j)}&amp;lt;/math&amp;gt; с номером &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; известно &amp;lt;math&amp;gt;\beta_{j}=\frac{(x^{(j)},v)}{\gamma}&amp;lt;/math&amp;gt;, то для модифицируемого элемента матрицы &amp;lt;math&amp;gt;y&amp;lt;/math&amp;gt; в позиции &amp;lt;math&amp;gt;(i,j)&amp;lt;/math&amp;gt; выполняется модификация &amp;lt;math&amp;gt;y&#039; = y - \beta_{i} v_{i} - \beta_{j} v_{j} - \Delta v_{i} v_{j}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\Delta = \frac{(\beta,v)}{\gamma}&amp;lt;/math&amp;gt;. При этом все эти модификации на каждом конкретном шаге алгоритма для разных пар &amp;lt;math&amp;gt;(i,j)&amp;lt;/math&amp;gt; можно выполнять независимо друг от друга, в том числе и параллельно.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
&lt;br /&gt;
[[File:HausLeftSym1.png|thumb|right|800px|Рисунок 1. Граф первой половины шага алгоритма приведения к трёхдиагональному виду с отображением входных данных. Изображён граф первой половины шага с номером &amp;lt;math&amp;gt;n-4&amp;lt;/math&amp;gt;. Квадраты - результаты выполнения предыдущего шага. Если шаг первый, то это входные данные. Зелёные кружки - операция вида &amp;lt;math&amp;gt;a+b^2&amp;lt;/math&amp;gt;, салатовые - операция вида &amp;lt;math&amp;gt;a+bc&amp;lt;/math&amp;gt;. Синий кружок - вычисление параметров матрицы отражения, светло-красные - вычисление коэффициентов &amp;lt;math&amp;gt;\beta_i&amp;lt;/math&amp;gt;для следующего полушага, жёлтые - вычисление вектора &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.]]&lt;br /&gt;
[[File:HousLeftSym2.png|thumb|right|600px|Рисунок 2. Граф второй половины шага алгоритма приведения к трёхдиагональному виду с отображением входных данных. Изображён граф первой половины шага с номером &amp;lt;math&amp;gt;n-3&amp;lt;/math&amp;gt;. Квадраты - результаты выполнения предыдущего шага. Если шаг первый, то это входные данные. Синий, жёлтые и светло-красные кружки выполняются на первом полушаге (см. Рисунок 1). Голубые кружки -  операции &amp;lt;math&amp;gt;a+bc&amp;lt;/math&amp;gt;, зелёное - умножение, чёрные - операции &amp;lt;math&amp;gt;a+bc+de+fce&amp;lt;/math&amp;gt;.]]&lt;br /&gt;
&lt;br /&gt;
Основную часть алгоритма составляют вычисления на каждом шагу скалярных произведений &amp;lt;math&amp;gt;(s,s)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(x,v)&amp;lt;/math&amp;gt; для всех подстолбцов &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt;справа от текущего, а также проводимые над нижним правым квадратом матрицы операции вида &amp;lt;math&amp;gt;y&#039;=y-ab-cd-fbd&amp;lt;/math&amp;gt;, с учётом симметрии матрицы.&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Как уже сказано в описании ядра, основная часть - вычисления на каждом шагу скалярных произведений &amp;lt;math&amp;gt;(s,s)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(x,v)&amp;lt;/math&amp;gt; для всех подстолбцов &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; справа от текущего, а также массовые покомпонентные операции &amp;lt;math&amp;gt;y&#039;=y-ab-cd-fbd&amp;lt;/math&amp;gt;. При этом, однако, строгая последовательность выполнения первых двух подшагов не обязательна, в силу связи получаемых векторов &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; можно одновременно с &amp;lt;math&amp;gt;(s,s)&amp;lt;/math&amp;gt; вычислять и произведения &amp;lt;math&amp;gt;(x,s)&amp;lt;/math&amp;gt; с последующим выражением через них коэффициентов модификации. Это позволяет почти вдвое уменьшать критический путь графа алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Последовательность выполнения алгоритма обычно записывается как последовательное &amp;quot;обнуление&amp;quot; поддиагональных элементов столбцов, начиная с 1-го столбца и заканчивая предпоследним &amp;lt;math&amp;gt;(n-1)&amp;lt;/math&amp;gt;-м.&lt;br /&gt;
&lt;br /&gt;
При этом в каждом &amp;quot;обнуляемом&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м столбце &amp;quot;обнуляются&amp;quot; сразу все его поддиагональные элементы одновременно, с &amp;lt;math&amp;gt;(i+1)&amp;lt;/math&amp;gt;-го до &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;-го. &lt;br /&gt;
&lt;br /&gt;
Каждое &amp;quot;обнуление&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца состоит из двух шагов: &lt;br /&gt;
а) вычисление параметров матрицы отражения &amp;lt;math&amp;gt;U_{i}&amp;lt;/math&amp;gt; такой, чтобы при умножении на неё слева &amp;quot;обнулились&amp;quot; все поддиагональные его элементы;&lt;br /&gt;
б) одновременное умножение слева матрицы отражения &amp;lt;math&amp;gt;U_{i}&amp;lt;/math&amp;gt; и справа  матрицы отражения &amp;lt;math&amp;gt;U_{i}^T&amp;lt;/math&amp;gt;на текущую версию матрицы.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В последовательной версии основная сложность алгоритма определяется прежде всего вычислениями скалярных произведений векторов, а также массовых модификаций элементов вида &amp;lt;math&amp;gt;y&#039;=y-\alpha v - \beta w - \Delta vw&amp;lt;/math&amp;gt;. Они, если не учитывать возможную разреженность, составляют (в главном члене) по &amp;lt;math&amp;gt;O(n^3)&amp;lt;/math&amp;gt; операций действительного умножения и сложения/вычитания.&lt;br /&gt;
&lt;br /&gt;
При классификации по последовательной сложности, таким образом, метод Хаусхолдера относится к алгоритмам &#039;&#039;с кубической сложностью&#039;&#039;.&lt;br /&gt;
&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;n&amp;lt;/math&amp;gt; к трёхдиагональной методом Хаусхолдера нужно рассмотреть критический путь графа. &lt;br /&gt;
&lt;br /&gt;
Как видно из описания разных вершин, вычисления при &amp;quot;обнулении&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца параметров отражения и скалярных произведений состоят из основной части - ветви длиной по &amp;lt;math&amp;gt;2(n-i)&amp;lt;/math&amp;gt; умножений и сложений - и коррекции вычислений, которые составляют &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; операций. &lt;br /&gt;
&lt;br /&gt;
Поэтому по грубой (без членов низших порядков) оценке критический путь метода Хаусхолдера будет идти через &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt; умножений и &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt; сложений/вычитаний. &lt;br /&gt;
 &lt;br /&gt;
Поэтому в параллельном варианте, как и в последовательном, основную долю требуемого для выполнения алгоритма времени будут определять операции вида &amp;lt;math&amp;gt;a+bc&amp;lt;/math&amp;gt;.  &lt;br /&gt;
&lt;br /&gt;
При классификации по высоте ЯПФ, таким образом, метод Хаусхолдера относится к алгоритмам &#039;&#039;с квадратичной сложностью&#039;&#039;. При классификации по ширине ЯПФ его сложность будет также &#039;&#039;квадратичной&#039;&#039; (без расширения ряда ярусов, связанных с векторными операциями сложения, пришлось бы увеличить вдвое длину критического пути).&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: плотная симметричная квадратная матрица &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; (элементы &amp;lt;math&amp;gt;a_{ij}&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;(n^2+n)/2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: трёхдиагональная матрица &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; (ненулевые элементы &amp;lt;math&amp;gt;r_{ij}&amp;lt;/math&amp;gt; в последовательном варианте хранятся в элементах исходной матрицы &amp;lt;math&amp;gt;a_{ij}&amp;lt;/math&amp;gt;), унитарная (ортогональная) матрица Q - как произведение матриц Хаусхолдера (отражения) (их вектора нормалей к плоскостям отражения в последовательном варианте хранятся в поддиагональных элементах исходной матрицы &amp;lt;math&amp;gt;a_{ij}&amp;lt;/math&amp;gt; и в одном дополнительном столбце размерности n).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;(n^2+3n)/2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Соотношение последовательной и параллельной сложности, как хорошо видно, является &#039;&#039;линейным&#039;&#039;, что даёт определённый стимул для распараллеливания. Однако у наискорейшей ЯПФ ширина &#039;&#039;квадратична&#039;&#039;, что указывает на дисбаланс между загруженностями устройств при попытке её реально запрограммировать. Поэтому более практично даже при хорошей (быстрой) вычислительной сети оставить количество устройств (например, узлов кластера) &#039;&#039;линейным&#039;&#039; по размеру матрицы, что удвоит критический путь реализуемой ЯПФ. &lt;br /&gt;
&lt;br /&gt;
При этом вычислительная мощность алгоритма, как отношение числа операций к суммарному объему входных и выходных данных, &#039;&#039;линейна&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Алгоритм в рамках выбранной версии полностью детерминирован.&lt;br /&gt;
&lt;br /&gt;
Вычислительная погрешность в методе отражений (Хаусхолдера) растет &#039;&#039;линейно&#039;&#039;, как и в методе Гивенса (вращений).&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
В варианте с кратчайшим критическим путём графа алгоритма (с использованием зависимости между обнуляемым вектором и направляющим вектором отражения) метод Хаусхолдера (отражений) приведения квадратной симметричной вещественной матрицы к трёхдиагональному виду на Фортране 77 можно записать так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;fortran&amp;quot;&amp;gt;&lt;br /&gt;
	DO  I = 1, N-2&lt;br /&gt;
&lt;br /&gt;
            DO K = I, N&lt;br /&gt;
              SX(K)=A(N,I)*A(N,K)&lt;br /&gt;
            END DO&lt;br /&gt;
            DO J = N-1, I+1, -1&lt;br /&gt;
                 SX(I)=SX(I)+A(J,I)*A(J,I)&lt;br /&gt;
            END DO &lt;br /&gt;
            DO K = I+1, N&lt;br /&gt;
              DO J = N-1, K, -1&lt;br /&gt;
                 SX(K)=SX(K)+A(J,I)*A(J,K)&lt;br /&gt;
              END DO&lt;br /&gt;
              DO J = K-1,I+1,-1&lt;br /&gt;
                 SX(K)=SX(K)+A(J,I)*A(K,J)&lt;br /&gt;
              END DO&lt;br /&gt;
            END DO&lt;br /&gt;
&lt;br /&gt;
               ALPHA = SQRT (SX(I))&lt;br /&gt;
               IF (A(I+1,I).NE.0.) THEN&lt;br /&gt;
                  BETA = 1./ALPHA&lt;br /&gt;
                  DO J = I+2, N&lt;br /&gt;
                    A(J,I)=A(J,I)*BETA&lt;br /&gt;
                  END DO&lt;br /&gt;
                  SX(I) = A(I+1,I)*BETA+SIGN(1.,A(I+1,I))                     &lt;br /&gt;
                  A(I+1,I)=ALPHA&lt;br /&gt;
                  G=1./ABS(SX(I)) ! 1/gamma&lt;br /&gt;
                  SX2=0.&lt;br /&gt;
                  DO K = I+2, N&lt;br /&gt;
                     SX(K)=SX(K)*BETA*G+SIGN(A(K,I+1),SX(I))&lt;br /&gt;
                     SX2=SX(K)*A(K,I)+SX2                     &lt;br /&gt;
                  END DO&lt;br /&gt;
                  SX2=G*SX2&lt;br /&gt;
                  DO K = I+2, N&lt;br /&gt;
                     A(K,K) = A(K,K)-2*A(K,I)*SX(K)+SX2*A(K,I)**2&lt;br /&gt;
                     DO J = K+1, N&lt;br /&gt;
                       A (J,K) = A(J,K)-A(J,I)*SX(K)-A(K,I)*SX(I)+SX2*A(J,I)*A(K,I)&lt;br /&gt;
                     END DO&lt;br /&gt;
                  END DO&lt;br /&gt;
               ELSE&lt;br /&gt;
                  IF (ALPHA.NE.0.) THEN&lt;br /&gt;
                     BETA = 1./ALPHA&lt;br /&gt;
                     DO J = I+2, N&lt;br /&gt;
                       A(J,I)=A(J,I)*BETA&lt;br /&gt;
                     END DO&lt;br /&gt;
                     SX(I) = -1. &lt;br /&gt;
                     A(I+1,I)=ALPHA&lt;br /&gt;
                     G=1.! 1/gamma&lt;br /&gt;
                     SX2=0.&lt;br /&gt;
                     DO K = I+2, N&lt;br /&gt;
                        SX(K)=SX(K)*BETA*G+SIGN(A(K,I+1),SX(I))&lt;br /&gt;
                        SX2=SX(K)*A(K,I)+SX2                 &lt;br /&gt;
                     END DO&lt;br /&gt;
                     SX2=G*SX2&lt;br /&gt;
                     DO K = I+2, N  &lt;br /&gt;
                        A(K,K) = A(K,K)-2*A(K,I)*SX(K)+SX2*A(K,I)**2                   &lt;br /&gt;
                        DO J = K+1, N&lt;br /&gt;
                          A (J,K) = A(J,K)-A(J,I)*SX(K)-A(K,I)*SX(I)+SX2*A(J,I)*A(K,I)&lt;br /&gt;
                        END DO&lt;br /&gt;
                     END DO&lt;br /&gt;
                  ELSE&lt;br /&gt;
                     SX(I)=1.&lt;br /&gt;
                  END IF&lt;br /&gt;
               END IF               &lt;br /&gt;
&lt;br /&gt;
		&lt;br /&gt;
	END DO&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Здесь симметричная трёхдиагональная матрица хранится в диагонали и нижней кодиагонали массива A, вектора v - в поддиагональной части массива, за исключением первых их элементов, для которых выделен массив SX.&lt;br /&gt;
&lt;br /&gt;
Обычно же в последовательных версиях коэффициенты модификаций столбцов вычисляются целиком через скалярные произведения после вычислений параметров матрицы отражения. При этом схема чуть проще. Удлиняется критический путь графа, но для последовательных реализаций это неважно.&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
К сожалению, в графе, как видно по рисунку, в наличии пучки рассылок, в них неизбежно часть дуг остаются длинными, что отрицательно влияет на локальность вычислений по пространству.  &lt;br /&gt;
&lt;br /&gt;
==== Локальность реализации алгоритма ====&lt;br /&gt;
===== Структура обращений в память и качественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qtq_1.png|thumb|center|700px|Рисунок 3. Метод Хаусхолдера (отражений) для приведения симметричных матриц к трёхдиагональному виду. Общий профиль обращений в память]]&lt;br /&gt;
&lt;br /&gt;
На рис. 3 представлен профиль обращений в память для реализации метода Хаусхолдера (отражений) для приведения симметричных матриц к трёхдиагональному виду. Данный профиль обладает явной итерационной структурой, при этом видно, что итерации очень похожи. Основное их отличие заключается в наборе используемых данных – на каждой следующей итерации несколько первых элементов отбрасываются их рассмотрения, то есть чем позже итерация, тем меньше данных в ней задействовано. Также можно отметить, что число обращений в каждой последующей итерации немного уменьшается. Для того чтобы проанализировать общий профиль, в таком случае чаще всего достаточно изучить одну итерацию. Рассмотрим самую первую из них более детально.&lt;br /&gt;
&lt;br /&gt;
На рис. 4 показан набор обращений в рамках первой итерации (выделен на рис. 3 зеленым). Его можно разбить на несколько фрагментов и рассмотреть их отдельно. Строение фрагментов 2-5 можно оценить по общему графику для итерации. Фрагмент 2 представляет собой набор последовательных переборов в обратном порядке с небольшим шагом, причем число элементов в переборе постепенно уменьшается. Такое строение характеризуется достаточно высокой пространственной локальностью, поскольку часто происходит обращений к близко расположенным данным, однако низкой временной, поскольку данные повторно не используются. То же самое верно и для фрагмента 4, основное отличие которого заключается в том, что переборы выполняются в прямом порядке. Отметим, что некоторое искривление данного фрагмента связано не со строением самого фрагмента, а с разным числом параллельно выполняющихся обращений (см., например, правую область фрагмента 6), чего не наблюдается при выполнении фрагмента 2.&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qtq_2.png|thumb|center|700px|Рисунок 4. Профиль обращений, одна итерация]]&lt;br /&gt;
&lt;br /&gt;
Далее, фрагменты  3 и 5 состоят из небольшого числа обращений к данным, расположенным далеко друг от друга. Такие фрагменты характеризуются низкой пространственной и временной локальностью.&lt;br /&gt;
&lt;br /&gt;
Оставшиеся фрагменты требуют более детального рассмотрения. Перейдем к изучению фрагмента 1, который целиком представлен на рис. 5. Здесь мы можем видеть несколько этапов, каждый из которых представляет собой последовательный перебор элементов с шагом по памяти 1, причем в некоторых случаях данные много раз используются повторно (особенно это заметно в правой нижней области рисунка, где выполняется множество обращений к одному и тому же элементу). При условии, что в данном фрагменте задействовано всего около 40 элементов, можно говорить, что данный набор обращений обладает высокой пространственной и временной локальностью. &lt;br /&gt;
&lt;br /&gt;
[[file:householder_qtq_3.png|thumb|center|500px|Рисунок 5. Профиль обращений, одна итерация, фрагмент 1]]&lt;br /&gt;
&lt;br /&gt;
Далее рассмотрим детально фрагмент 6 (рис. 6). Здесь можно увидеть, что повторные обращения к данным выполняются не подряд, а через некоторые промежутки, однако число задействованных элементов также очень невелико, так что и в этом случае локальность (и пространственная, и временная) будет высокой.&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qtq_4.png|thumb|center|500px|Рисунок 6. Профиль обращений, одна итерация, фрагмент 6]]&lt;br /&gt;
&lt;br /&gt;
В целом можно сказать, что рассматриваемая итерация обладает достаточно высокой пространственной и временной локальностью. Поскольку остальные итерации устроены подобным образом, данные вывод можно перенести и на общий профиль обращений.&lt;br /&gt;
&lt;br /&gt;
===== Количественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
Оценка выполняется на основе характеристики daps, которая оценивает число выполненных обращений (чтений и записей) в память в секунду. Данная характеристика является аналогом оценки flops применительно к работе с памятью и является в большей степени оценкой производительности взаимодействия с памятью, чем оценкой локальности. Однако она служит хорошим источником информации, в том числе для сравнения с результатами по следующей характеристике cvg.&lt;br /&gt;
&lt;br /&gt;
На рисунке 7 приведены значения daps для реализаций распространенных алгоритмов, отсортированные по возрастанию (чем больше daps, тем в общем случае выше производительность). Можно увидеть, что производительность работы с памятью в данном случае достаточно высока – значение daps лишь немногим уступает тесту Linpack, который обладает высокой эффективностью работы с памятью, и немного превосходит, например, реализацию метода Якоби или метода Гаусса.&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qtq_daps.png|thumb|center|700px|Рисунок 7. Сравнение значений оценки daps]]&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
==== Масштабируемость алгоритма ====&lt;br /&gt;
==== Масштабируемость реализации алгоритма ====&lt;br /&gt;
Проведём исследование масштабируемости вширь реализации согласно [[Scalability methodology|методике]]. Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов-2&amp;quot; [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Набор и границы значений изменяемых [[Глоссарий#Параметры запуска|параметров запуска]] реализации алгоритма: &lt;br /&gt;
&lt;br /&gt;
*	число процессоров [16:81] с шагом n^2;&lt;br /&gt;
*	размер матрицы [1000 : 16000] с шагом 1000.&lt;br /&gt;
&lt;br /&gt;
В результате проведённых экспериментов был получен следующий диапазон [[Глоссарий#Эффективность реализации|эффективности реализации]] алгоритма:&lt;br /&gt;
&lt;br /&gt;
*	минимальная эффективность реализации 4.763e-05%;&lt;br /&gt;
*	максимальная эффективность реализации 0.011%.&lt;br /&gt;
&lt;br /&gt;
На следующих рисунках приведены графики [[Глоссарий#Производительность|производительности]] и эффективности выбранной реализации алгоритма в процедуре PDSYTRD из Scalapack в зависимости от изменяемых параметров запуска.&lt;br /&gt;
&lt;br /&gt;
[[file:PDSYTRD Perf2.png|thumb|center|700px|Рисунок 8. Параллельная реализация метода Хаусхолдера QR-разложения. Изменение производительности в зависимости от числа процессоров и размера матрицы.]]&lt;br /&gt;
[[file:PDSYTRD eff2.png|thumb|center|700px|Рисунок 9. Параллельная реализация метода Хаусхолдера QR-разложения. Изменение эффективности в зависимости от числа процессоров и размера матрицы.]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
Для проведения экспериментов использовалась реализация метода Хаусхолдера, представленная в пакете SCALAPACK библиотеки Intel MKL (метод pdgehrd).  Все результаты получены на суперкомпьютере «Ломоносов-2». Использовались процессоры Intel Xeon E5-2697v3, сеть FDR Infiniband а также компилятор Intel с опциями, рекомнедованными Intel MKL Advisor. &lt;br /&gt;
На рисунках показана эффективность реализации метода Хаусхолдера (отражений) для приведения симметричных матриц к трёхдиагональному виду для размерности матрицы 15000, запуск проводился на 81 ядре.&lt;br /&gt;
&lt;br /&gt;
[[file:PDGETRD cpu.png|thumb|center|700px|Рисунок 9. График загрузки CPU при выполнении метода Хаусхолдера (отражений) приведения матрицы к трёхдиагональному виду]]&lt;br /&gt;
&lt;br /&gt;
На графике загрузки процессора видно, что почти все время работы программы средний уровень загрузки составляет около 90%. Это хорошая картина для программ, запущенных c использованием технологии Hyper Threading т.е. всех виртуальных ядер доступных для данной архитектуры. К концу итерации виден более высокий уровень интенсивности вычислений в срванении со началом и серединой вычислений.&lt;br /&gt;
&lt;br /&gt;
[[file:PDGETRD LoadAvg.png|thumb|center|700px|Рисунок 10. График числа процессов, ожидающих вхождения в стадию счета (Loadavg), при работе метода Хаусхолдера (отражений) приведения матрицы к трёхдиагональному виду]]&lt;br /&gt;
&lt;br /&gt;
На графике числа процессов, ожидающих вхождения в стадию счета (Loadavg), видно, что на протяжении всей работы программы значение этого параметра постоянно и приблизительно равняется 140 и только к концу итерации снижается до 80. Это свидетельствует о стабильной работе программы на протяжении всего выполнения со 140 нитями на одном узле или около 10 нитей на 1 физическое ядро на каждом узле. Это указывает на статичную загрузку аппаратных ресурсов процессами, однако их число для узла слишком велико, что с одной стороны может указывать на не очень рациональные опции компиляции, который могут приводить к снижению производительности из-за накладных расходов на переключение контекста между нитями.&lt;br /&gt;
&lt;br /&gt;
 [[file:PDGETRD L1.png|thumb|center|700px|Рисунок 11. График кэш-промахов L1 в секунду при работе метода Хаусхолдера (отражений) приведения матрицы к трёхдиагональному виду]]&lt;br /&gt;
 &lt;br /&gt;
На графике кэш-промахов первого уровня видно, что число промахов достаточно низкое и находится на уровне 5 млн/сек в среднем по всем узлам. Интересен факт снижения числа промахов к концу итерации, хотя снижение и не очень значительное, но вполне заметное.&lt;br /&gt;
&lt;br /&gt;
 [[file:PDGETRD L2.png|thumb|center|700px|Рисунок 12. График кэш-промахов L1 в секунду при работе метода Хаусхолдера (отражений) приведения матрицы к трёхдиагональному виду]]&lt;br /&gt;
 &lt;br /&gt;
На графике кэш-промахов второго уровня видно, что число промахов достаточно тоже низкое и находится на уровне 1 млн/сек в среднем по всем узлам. На графике промахов второго уровня факт снижения числа промахов к концу итерации проявляется более явно и снижение и более значительное и заметное (с 1.2 млн до 0.5 млн.).&lt;br /&gt;
&lt;br /&gt;
 [[file:PDGETRD L3.png|thumb|center|700px|Рисунок 13. График кэш-промахов L3 в секунду при работе метода Хаусхолдера (отражений) приведения матрицы к трёхдиагональному виду]]&lt;br /&gt;
&lt;br /&gt;
На графике кэш-промахов последнего уровня видно, что число промахов достаточно небольшое и быстро убывает с уровня 90 тыс/сек до 0 в среднем по всем узлам. Это указывает на то, что задача достаточно хорошо укладывается в кэш-память третьего уровня, особенно к концу итерации.&lt;br /&gt;
[[file:PDGETRD Inf data.png|thumb|center|700px|Рисунок 13. График скорости передачи по сети Infiniband в байт/сек при работе метода Хаусхолдера (отражений) приведения матрицы к трёхдиагональному виду]]&lt;br /&gt;
&lt;br /&gt;
На графике скорости передачи данных по сети Infiniband наблюдается достаточно низкая интенсивность использования коммуникационной сети на каждой итерации. Причем к концу каждой итерации интенсивность передачи данных сильно снижается. Это указывает на бОльшую необходимость в обмене данными между процессами в начале итерации и большую локальность вычислений к концу итерации.&lt;br /&gt;
 [[file:PDGETRD Inf pckts.png|thumb|center|700px|Рисунок 14. График скорости передачи по сети Infiniband в пакетах/сек при работе метода Хаусхолдера (отражений) приведения матрицы к трёхдиагональному виду]]&lt;br /&gt;
&lt;br /&gt;
На графике скорости передачи данных в пакетах в секунду наблюдается большая «кучность» показаний максимального минимального и среднего значений в сравнении с графиком скорости передачи в байт/сек. Это говорит о том, что, вероятно, процессы обмениваются сообщениями различной длины, что указывает на неравномерное распределение данных. Также наблюдается рост интенсивности использования сети к концу каждой итерации. Особенно это различие заметно к концу итерации.&lt;br /&gt;
 &lt;br /&gt;
В целом, по данным системного мониторинга работы программы можно сделать вывод о том, что программа работала стабильно и эффективно использовала память. Использование памяти и коммуникационной среды не достаточно интенсивное, что может стать указывать на факторы снижения эффективности и увеличении производительности при существенном росте размера задачи.&lt;br /&gt;
Для существующей в SCALAPACK параллельной реализаций характерно сильное снижение числа кэш-промахов и обменов к концу итерации&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
В сравнении с методом Гивенса, который имеет естественное двумерное блочное разбиение на основе точечного метода, метод Хаусхолдера из-за худших характеристик локальности (наличие пучков рассылок) и меньшего количества независимых обобщённых развёрток графа не так хорош для реализаций на системах с распределённой памятью, как для систем с общей памятью. Поэтому на массово параллельных системах с распределённой памятью следует применять метод Хаусхолдера (если уж именно его нужно реализовать) не в точечной версии, а в разрабатываемых исследователями блочных вариантах. Следует отметить, что эти варианты - не блочная нарезка описанного метода, а самостоятельные методы. Особенно их применение рекомендуется в случаях с большой разрежённостью матрицы.&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Большинство пакетов от LINPACKа и LAPACKa до SCALAPACKa используют для QR-разложения матриц именно метод Хаусхолдера, правда, в различных модификациях (обычно с использованием BLAS). Существует большая подборка исследовательских работ по блочным версиям.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Статьи в работе]]&lt;br /&gt;
[[Категория:Разложения матриц]]&lt;/div&gt;</summary>
		<author><name>Teplov</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D0%A5%D0%B0%D1%83%D1%81%D1%85%D0%BE%D0%BB%D0%B4%D0%B5%D1%80%D0%B0_(%D0%BE%D1%82%D1%80%D0%B0%D0%B6%D0%B5%D0%BD%D0%B8%D0%B9)_QR-%D1%80%D0%B0%D0%B7%D0%BB%D0%BE%D0%B6%D0%B5%D0%BD%D0%B8%D1%8F_%D0%BA%D0%B2%D0%B0%D0%B4%D1%80%D0%B0%D1%82%D0%BD%D0%BE%D0%B9_%D0%BC%D0%B0%D1%82%D1%80%D0%B8%D1%86%D1%8B,_%D0%B2%D0%B5%D1%89%D0%B5%D1%81%D1%82%D0%B2%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D1%82%D0%BE%D1%87%D0%B5%D1%87%D0%BD%D1%8B%D0%B9_%D0%B2%D0%B0%D1%80%D0%B8%D0%B0%D0%BD%D1%82&amp;diff=21583</id>
		<title>Метод Хаусхолдера (отражений) QR-разложения квадратной матрицы, вещественный точечный вариант</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D0%A5%D0%B0%D1%83%D1%81%D1%85%D0%BE%D0%BB%D0%B4%D0%B5%D1%80%D0%B0_(%D0%BE%D1%82%D1%80%D0%B0%D0%B6%D0%B5%D0%BD%D0%B8%D0%B9)_QR-%D1%80%D0%B0%D0%B7%D0%BB%D0%BE%D0%B6%D0%B5%D0%BD%D0%B8%D1%8F_%D0%BA%D0%B2%D0%B0%D0%B4%D1%80%D0%B0%D1%82%D0%BD%D0%BE%D0%B9_%D0%BC%D0%B0%D1%82%D1%80%D0%B8%D1%86%D1%8B,_%D0%B2%D0%B5%D1%89%D0%B5%D1%81%D1%82%D0%B2%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D1%82%D0%BE%D1%87%D0%B5%D1%87%D0%BD%D1%8B%D0%B9_%D0%B2%D0%B0%D1%80%D0%B8%D0%B0%D0%BD%D1%82&amp;diff=21583"/>
		<updated>2017-01-19T12:25:04Z</updated>

		<summary type="html">&lt;p&gt;Teplov: /* Динамические характеристики и эффективность реализации алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = QR-разложение методом Хаусхолдера (отражений)&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;\frac{4 n^3}{3}&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;n(n + 1)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
Основные авторы описания: [[Участник:Frolov|А.В.Фролов]]&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Метод Хаусхолдера&#039;&#039;&#039; (в советской математической литературе чаще называется &#039;&#039;&#039;методом отражений&#039;&#039;&#039;) используется для разложения  матриц в виде &amp;lt;math&amp;gt;A=QR&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; - унитарная, &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; — правая треугольная матрица)&amp;lt;ref&amp;gt;В.В.Воеводин, Ю.А.Кузнецов. Матрицы и вычисления. М.: Наука, 1984.&amp;lt;/ref&amp;gt;. При этом матрица &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; хранится и используется не в своём явном виде, а в виде произведения матриц отражения&amp;lt;ref name=&amp;quot;VOLA&amp;quot;&amp;gt;Воеводин В.В. Вычислительные основы линейной алгебры. М.: Наука, 1977.&amp;lt;/ref&amp;gt;. Каждая из матриц отражения может быть определена одним вектором. Это позволяет в классическом исполнении метода отражений хранить результаты разложения на месте матрицы A с использованием минимального одномерного дополнительного массива. &lt;br /&gt;
&lt;br /&gt;
В данной статье рассматривается именно классическое исполнение, в котором не используются приёмы типа сдваивания при вычислениях скалярных произведений.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В методе Хаусхолдера для выполнения &amp;lt;math&amp;gt;QR&amp;lt;/math&amp;gt;-разложения матрицы используются умножения слева её текущих модификаций на матрицы Хаусхолдера (отражений). &lt;br /&gt;
&lt;br /&gt;
{{Шаблон:Матрица отражений}}&lt;br /&gt;
&lt;br /&gt;
На &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м шаге метода с помощью преобразования отражения &amp;quot;убираются&amp;quot; ненулевые поддиагональные элементы в &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м столбце. Таким образом, после &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; шагов преобразований получается матрица &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; из &amp;lt;math&amp;gt;QR&amp;lt;/math&amp;gt;-разложения.&lt;br /&gt;
&lt;br /&gt;
На каждом из шагов метода матрицу отражений обычно представляют не в стандартном виде, а в виде &amp;lt;math&amp;gt;U=E-\frac{1}{\gamma}vv^*&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; находится через координаты текущего &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; - вектор размерности &amp;lt;math&amp;gt;n+1-i&amp;lt;/math&amp;gt;, составленный из элементов &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца, начиная с &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;(s,s)=0&amp;lt;/math&amp;gt;, то &amp;lt;math&amp;gt;v=e_{i}&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\gamma = \frac{1}{2}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В остальных случаях по алгоритму вычисляется &amp;lt;math&amp;gt;u = \frac{1}{\sqrt{(s,s)}}s&amp;lt;/math&amp;gt;, и далее &amp;lt;math&amp;gt;v_{j}=0&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;j&amp;lt;i&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;v_{j}=u_{j-i+1}&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;j&amp;gt;i&amp;lt;/math&amp;gt;, а &amp;lt;math&amp;gt;v_{i}=1&amp;lt;/math&amp;gt;, если &amp;lt;math&amp;gt;u_{1}=0&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v_{i}=\frac{u_{1}}{|u_{1}|}(1+|u_{1}|)&amp;lt;/math&amp;gt; для остальных значений. При этом &amp;lt;math&amp;gt;\gamma =1+|u_{1}|=|v_{i}|&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
После вычисления вектора &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; подстолбцы справа от ведущего модифицируются по формулам &amp;lt;math&amp;gt;x&#039;=x-\frac{(x,v)}{\gamma}v&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Основную часть алгоритма составляют вычисления на каждом шагу скалярных произведений &amp;lt;math&amp;gt;(s,s)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(x,v)&amp;lt;/math&amp;gt; для всех подстолбцов &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt;справа от текущего, а также векторные операции &amp;lt;math&amp;gt;x&#039;=x-\frac{(x,v)}{\gamma}v&amp;lt;/math&amp;gt;. Это используется при программировании метода во многих библиотеках для его конструирования из стандартных подпрограмм (например, из BLAS).&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
[[File:HausLeft1.png|thumb|right|600px|Рисунок 1. Граф шага (обнуление &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;го столбца) алгоритма. Квадратики - входные данные шага (берутся с предыдущего или из входных данных), кружки - операции. Зелёным выделены операции типа a+bb, салатовым и голубым - типа a+bc, тёмно-синим - вычисление &amp;lt;math&amp;gt;\gamma , v_{1}&amp;lt;/math&amp;gt;, жёлтым - умножения (или деления). Обведённая группа операций повторяется независимо n-i раз. Результаты синего, красных и жёлтых кружков, а на последнем шаге и голубого - выходные для алгоритма.]]&lt;br /&gt;
&lt;br /&gt;
Как уже сказано в описании ядра, основная часть - вычисления на каждом шагу скалярных произведений &amp;lt;math&amp;gt;(s,s)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(x,v)&amp;lt;/math&amp;gt; для всех подстолбцов &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; справа от текущего, а также векторные операции &amp;lt;math&amp;gt;x&#039;=x-\frac{(x,v)}{\gamma}v&amp;lt;/math&amp;gt;. При этом, однако, строгая последовательность выполнения этих трёх подшагов не обязательна, в силу связи получаемых векторов &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; можно одновременно с &amp;lt;math&amp;gt;(s,s)&amp;lt;/math&amp;gt; вычислять и произведения &amp;lt;math&amp;gt;(x,s)&amp;lt;/math&amp;gt; с последующим выражением через них &amp;lt;math&amp;gt;(x,v)&amp;lt;/math&amp;gt;. Это позволяет почти вдвое уменьшать критический путь графа алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Последовательность выполнения алгоритма обычно записывается как последовательное &amp;quot;обнуление&amp;quot; поддиагональных элементов столбцов, начиная с 1-го столбца и заканчивая предпоследним &amp;lt;math&amp;gt;(n-1)&amp;lt;/math&amp;gt;-м.&lt;br /&gt;
&lt;br /&gt;
При этом в каждом &amp;quot;обнуляемом&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м столбце &amp;quot;обнуляются&amp;quot; сразу все его поддиагональные элементы одновременно, с &amp;lt;math&amp;gt;(i+1)&amp;lt;/math&amp;gt;-го до &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;-го. &lt;br /&gt;
&lt;br /&gt;
Каждое &amp;quot;обнуление&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца состоит из двух шагов: &lt;br /&gt;
а) вычисление параметров матрицы отражения &amp;lt;math&amp;gt;U_{i}&amp;lt;/math&amp;gt; такой, чтобы при умножении на неё слева &amp;quot;обнулились&amp;quot; все поддиагональные его элементы;&lt;br /&gt;
б) умножение слева матрицы отражения &amp;lt;math&amp;gt;U_{i}&amp;lt;/math&amp;gt; на текущую версию матрицы.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В последовательной версии основная сложность алгоритма определяется прежде всего вычислениями скалярных произведений векторов, а также модификаций векторов вида &amp;lt;math&amp;gt;x&#039;=x-\alpha v&amp;lt;/math&amp;gt;, причем над векторами убывающей по ходу алгоритма размерности. Они, если не учитывать возможную разреженность, составляют (в главном члене) по &amp;lt;math&amp;gt;2n^3/3&amp;lt;/math&amp;gt; операций действительного умножения и сложения/вычитания.&lt;br /&gt;
&lt;br /&gt;
При классификации по последовательной сложности, таким образом, метод Хаусхолдера относится к алгоритмам &#039;&#039;с кубической сложностью&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
На Рисунке 1 приведён шаг графа алгоритма метода Хаусхолдера в наиболее его быстром (с параллельной точки зрения) варианте, использующем то, что с точностью до множителя ведущий вектор матрицы отражения отличается отличается от подстолбца, где выполняется очередное исключение, только одним элементом. Операции привязаны к обрабатываемым элементам матрицы. Для получения полного графа графы шагов нужно положить друг на друга последовательными слоями, при этом правые нижние углы должны быть друг над другом.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Для понимания ресурса параллелизма в разложении матрицы порядка &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; методом Хаусхолдера нужно рассмотреть критический путь графа. &lt;br /&gt;
&lt;br /&gt;
Как видно из описания разных вершин, вычисления при &amp;quot;обнулении&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца параметров отражения и скалярных произведений состоят из основной части - ветви длиной по &amp;lt;math&amp;gt;n-i&amp;lt;/math&amp;gt; умножений и сложений - и коррекции вычислений, которые составляют &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; операций.&lt;br /&gt;
&lt;br /&gt;
Поэтому по грубой (без членов низших порядков) оценке критический путь метода Хаусхолдера будет идти через &amp;lt;math&amp;gt;\frac{n^2}{2}&amp;lt;/math&amp;gt; умножений и &amp;lt;math&amp;gt;\frac{n^2}{2}&amp;lt;/math&amp;gt; сложений/вычитаний. &lt;br /&gt;
 &lt;br /&gt;
Поэтому в параллельном варианте, как и в последовательном, основную долю требуемого для выполнения алгоритма времени будут определять операции вида &amp;lt;math&amp;gt;a+bc&amp;lt;/math&amp;gt;.  &lt;br /&gt;
&lt;br /&gt;
При классификации по высоте ЯПФ, таким образом, метод Хаусхолдера относится к алгоритмам &#039;&#039;с квадратичной сложностью&#039;&#039;. При классификации по ширине ЯПФ его сложность будет также &#039;&#039;квадратичной&#039;&#039; (без расширения ряда ярусов, связанных с векторными операциями сложения, пришлось бы увеличить вдвое длину критического пути; при таком расширении сложность по ширине ЯПФ станет &#039;&#039;линейной&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
Надо сказать, что здесь в оценках речь идёт именно о классическом способе реализации метода Хаусхолдера. Даже использование схем сдваивания или последовательно-параллельных для вычисления скалярных произведений уменьшает критический путь с квадратичного до &#039;&#039;степени 3/2&#039;&#039; или &#039;&#039;линейно-логарифмического&#039;&#039;. Однако все эти широко распространённые методы пока не дали возможности снизить критический путь метода Хаусхолдера до &#039;&#039;линейного&#039;&#039; (как, скажем, у метода Гивенса).&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: плотная квадратная матрица &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; (элементы &amp;lt;math&amp;gt;a_{ij}&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: правая треугольная матрица &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; (ненулевые элементы &amp;lt;math&amp;gt;r_{ij}&amp;lt;/math&amp;gt; в последовательном варианте хранятся в элементах исходной матрицы &amp;lt;math&amp;gt;a_{ij}&amp;lt;/math&amp;gt;), унитарная (ортогональная) матрица Q - как произведение матриц Хаусхолдера (отражения) (их вектора нормалей к плоскостям отражения в последовательном варианте хранятся в поддиагональных элементах исходной матрицы &amp;lt;math&amp;gt;a_{ij}&amp;lt;/math&amp;gt; и в одном дополнительном столбце размерности n).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;n^2+n&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Соотношение последовательной и параллельной сложности, как хорошо видно, является &#039;&#039;линейным&#039;&#039;, что даёт определённый стимул для распараллеливания. Однако у наискорейшей ЯПФ ширина &#039;&#039;квадратична&#039;&#039;, что указывает на дисбаланс между загруженностями устройств при попытке её реально запрограммировать. Поэтому более практично даже при хорошей (быстрой) вычислительной сети оставить количество устройств (например, узлов кластера) &#039;&#039;линейным&#039;&#039; по размеру матрицы, что удвоит критический путь реализуемой ЯПФ. &lt;br /&gt;
&lt;br /&gt;
При этом вычислительная мощность алгоритма, как отношение числа операций к суммарному объему входных и выходных данных, &#039;&#039;линейна&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Алгоритм в рамках выбранной версии полностью детерминирован.&lt;br /&gt;
&lt;br /&gt;
Вычислительная погрешность в методе отражений (Хаусхолдера) растет &#039;&#039;линейно&#039;&#039;, как и в методе Гивенса (вращений).&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В варианте с кратчайшим критическим путём графа алгоритма (с использованием зависимости между обнуляемым вектором и направляющим вектором отражения) метод Хаусхолдера (отражений) QR-разложения квадратной вещественной матрицы на Фортране 77 можно записать так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;fortran&amp;quot;&amp;gt;&lt;br /&gt;
	DO  I = 1, N-1&lt;br /&gt;
            DO K = I, N&lt;br /&gt;
              SX(K)=A(N,I)*A(N,K)&lt;br /&gt;
            END DO&lt;br /&gt;
            DO J = N-1, I, -1&lt;br /&gt;
               DO K = I, N&lt;br /&gt;
                  SX(K)=SX(K)+A(J,I)*A(J,K)&lt;br /&gt;
               END DO&lt;br /&gt;
            END DO&lt;br /&gt;
&lt;br /&gt;
               ALPHA = SQRT (SX(I))&lt;br /&gt;
               IF (A(I,I).NE.0.) THEN&lt;br /&gt;
                  BETA = 1./ALPHA&lt;br /&gt;
                  DO J = I+1, N&lt;br /&gt;
                    A(J,I)=A(J,I)*BETA&lt;br /&gt;
                  END DO&lt;br /&gt;
                  SX(I) = A(I,I)*BETA+SIGN(1.,A(I,I))                     &lt;br /&gt;
                  A(I,I)=ALPHA&lt;br /&gt;
                  G=1./ABS(SX(I)) ! 1/gamma&lt;br /&gt;
                  DO K = I+1, N&lt;br /&gt;
                     SX(K)=SX(K)*BETA*G+SIGN(A(I,K),SX(I))&lt;br /&gt;
                     A(I,K) = A(I,K)+SX(K)*SX(I)&lt;br /&gt;
                     DO J = I+1, N&lt;br /&gt;
                       A (J,K) = A(J,K)-A(J,I)*SX(K)&lt;br /&gt;
                     END DO&lt;br /&gt;
                  END DO&lt;br /&gt;
               ELSE&lt;br /&gt;
                  IF (ALPHA.NE.0.) THEN&lt;br /&gt;
                     BETA = 1./ALPHA&lt;br /&gt;
                     DO J = I+1, N&lt;br /&gt;
                       A(J,I)=A(J,I)*BETA&lt;br /&gt;
                     END DO&lt;br /&gt;
                     SX(I) = -1. &lt;br /&gt;
                     A(I,I)=ALPHA&lt;br /&gt;
                     G=1.! 1/gamma&lt;br /&gt;
                     DO K = I+1, N&lt;br /&gt;
                        SX(K)=SX(K)*BETA*G+SIGN(A(I,K),SX(I))&lt;br /&gt;
                        A(I,K) = A(I,K)+SX(K)*SX(I)&lt;br /&gt;
                        DO J = I+1, N&lt;br /&gt;
                          A (J,K) = A(J,K)-A(J,I)*SX(K)&lt;br /&gt;
                        END DO&lt;br /&gt;
                     END DO&lt;br /&gt;
                  ELSE&lt;br /&gt;
                     SX(I)=1&lt;br /&gt;
                     G=2.&lt;br /&gt;
                     DO K = I+1, N&lt;br /&gt;
                        SX(K)=2.&lt;br /&gt;
                        A(I,K) = A(I,K)-SX(K)&lt;br /&gt;
                     END DO&lt;br /&gt;
                  END IF&lt;br /&gt;
               END IF               &lt;br /&gt;
&lt;br /&gt;
		&lt;br /&gt;
	END DO&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В этом варианте R расположена в верхнем правом треугольнике массива A, направляющие вектора матриц отражений размещены в поддиагональных элементах соответствующих столбцов, а их первые элементы - в элементах массива SX.&lt;br /&gt;
&lt;br /&gt;
Обычно же в последовательных версиях коэффициенты модификаций столбцов вычисляются целиком через скалярные произведения после вычислений параметров матрицы отражения. При этом схема чуть проще. Удлиняется критический путь графа, но для последовательных реализаций это неважно.&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
К сожалению, в графе, как видно по рисунку, в наличии пучки рассылок, в них неизбежно часть дуг остаются длинными, что отрицательно влияет на локальность вычислений по пространству.  &lt;br /&gt;
&lt;br /&gt;
==== Локальность реализации алгоритма ====&lt;br /&gt;
===== Структура обращений в память и качественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qr_1.png|thumb|center|700px|Рисунок 2. Метод Хаусхолдера (отражений) QR-разложения квадратной матрицы, вещественный вариант. Общий профиль обращений в память]]&lt;br /&gt;
&lt;br /&gt;
На рис. 2 представлен профиль обращений в память для реализации вещественного варианта метода Хаусхолдера (отражений) QR-разложения квадратной матрицы. Из данного рисунка можно сделать несколько выводов. Во-первых, видно, что в среднем к каждому элементу выполняется примерно 90 обращений (отношение общего числа обращений к числу задействованных данных), что является достаточно хорошим показателем в плане повторного использования данных. Далее, явно видна итерационная структура профиля, причем на каждой следующей итерации обращения к небольшой части первых элементов перестают выполняться. Это говорит о том, что ближе к концу профиля задействовано меньше данных, что хорошо сказывается на пространственной локальности. Однако в то же время видно, что на каждой следующей итерации выполняется меньше обращений к одним и тем же локальным областям, то есть временная локальность, по всей вероятности, становится ниже. Рассмотрим подробнее первую итерацию, что более детально оценить их строение.&lt;br /&gt;
&lt;br /&gt;
На рис. 3 представлена одна итерация общего профиля (выделенный зеленый фрагмент на рис. 2). Ее можно условно разбить на 5 фрагментов, каждую из которых рассмотреть отдельно. Фрагменты 2 и 5 устроены просто – элементы перебираются с большим шагом по памяти, при достижении конца данных перебор повторяется, только с небольшим сдвигом. Про такие фрагменты можно сказать, что они обладают низкой пространственной локальностью, поскольку шаг по памяти достаточно большой, и низкой временной локальностью, так как данные повторно не используются. Однако отметим, что данные фрагменты состоят из небольшого числа обращений, что означает, что они не так сильно будут снижать общую локальность профиля.&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qr_2.png|thumb|center|700px|Рисунок 3. Профиль обращений, одна итерация]]&lt;br /&gt;
&lt;br /&gt;
Далее, фрагмент 1 и 3 устроены практически идентично (основное отличие – два основных этапа переставлены местами), поэтому рассмотрим только один из них. На рис. 4 представлен фрагмент 3. Здесь хорошо видно, как устроены эти этапы. На первом этапе выполняется перебор элементов в обратном порядке с шагом 1, при этом к каждому элементу подряд обращаются несколько раз. Такой фрагмент обладает очень высокой пространственной локальностью и достаточно высокой временной локальностью. &lt;br /&gt;
&lt;br /&gt;
[[file:householder_qr_3.png|thumb|center|500px|Рисунок 4. Профиль обращений, одна итерация, фрагмент 3]]&lt;br /&gt;
&lt;br /&gt;
На втором этапе также выполняется последовательный перебор, но уже с небольшим шагом, и к каждому элементу обращаются только по 1 разу на каждой итерации. Однако, поскольку в каждой небольшой итерации на втором этапе всего около 30 обращений, повторное обращение к каждому элементу происходит недалеко от предыдущего, что говорит о достаточно высокой временной локальности. Пространственная локальность также высока, поскольку шаг по памяти невелик.&lt;br /&gt;
&lt;br /&gt;
Далее рассмотрим фрагмент 4. Чтобы лучше понять локальную структуру, детально изучим небольшую его часть (рис. 5). Из данного рисунка хорошо видно, что здесь профиль устроен очень просто и представляет собой обычный последовательный перебор с шагом 1 (с небольшими и нечастыми сдвигами, которые не влияют на локальность этого фрагмента). Такой фрагмент обладает высокой пространственной, но низкой временной локальностью.&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qr_4.png|thumb|center|500px|Рисунок 5. Профиль обращений, одна итерация, часть фрагмента 4]]&lt;br /&gt;
&lt;br /&gt;
В целом можно сказать, что одна итерация обладает высокой пространственной и средней временной локальностью, хотя она несколько снижается из-за наличия фрагментов 2, 4 и 5. При условии, что итерации подобны, выводы относительно локальности одной итерации можно перенести и на общий профиль.&lt;br /&gt;
&lt;br /&gt;
===== Количественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
Оценка выполняется на основе характеристики daps, которая оценивает число выполненных обращений (чтений и записей) в память в секунду. Данная характеристика является аналогом оценки flops применительно к работе с памятью и является в большей степени оценкой производительности взаимодействия с памятью, чем оценкой локальности. Однако она служит хорошим источником информации, в том числе для сравнения с результатами по следующей характеристике cvg.&lt;br /&gt;
&lt;br /&gt;
На рисунке 6 приведены значения daps для реализаций распространенных алгоритмов, отсортированные по возрастанию (чем больше daps, тем в общем случае выше производительность). Можно увидеть, что производительность работы с памятью в данном примере высока. Значение daps для него находится на одном уровне с тестом Linpack и лишь немногим ниже, чем, например, самые эффективные варианты перемножения плотных матриц. Это означает, что негативное влияние фрагментов 2 и 5 оказывается достаточно слабым, вероятно (как и предполагалось ранее) из-за небольшого их размера.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qr_daps.png|thumb|center|700px|Рисунок 6. Сравнение значений оценки daps]]&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
==== Масштабируемость алгоритма ====&lt;br /&gt;
==== Масштабируемость реализации алгоритма ====&lt;br /&gt;
Проведём исследование масштабируемости вширь реализации согласно [[Scalability methodology|методике]]. Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов-2&amp;quot; [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Набор и границы значений изменяемых [[Глоссарий#Параметры запуска|параметров запуска]] реализации алгоритма: &lt;br /&gt;
&lt;br /&gt;
*	число процессоров [16:100] с шагом n^2;&lt;br /&gt;
*	размер матрицы [1000 : 16000] с шагом 1000.&lt;br /&gt;
&lt;br /&gt;
В результате проведённых экспериментов был получен следующий диапазон [[Глоссарий#Эффективность реализации|эффективности реализации]] алгоритма:&lt;br /&gt;
&lt;br /&gt;
*	минимальная эффективность реализации 0.00012%;&lt;br /&gt;
*	максимальная эффективность реализации 0.0416%.&lt;br /&gt;
&lt;br /&gt;
На следующих рисунках приведены графики [[Глоссарий#Производительность|производительности]] и эффективности выбранной реализации алгоритма в процедуре PDGEQRF из Scalapack в зависимости от изменяемых параметров запуска.&lt;br /&gt;
&lt;br /&gt;
[[file:PDGEQRF perf2.png|thumb|center|700px|Рисунок 7. Параллельная реализация метода Хаусхолдера QR-разложения. Изменение производительности в зависимости от числа процессоров и размера матрицы.]]&lt;br /&gt;
[[file:PDGEQRF eff2.png|thumb|center|700px|Рисунок 8. Параллельная реализация метода Хаусхолдера QR-разложения. Изменение эффективности в зависимости от числа процессоров и размера матрицы.]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
Для проведения экспериментов использовалась реализация метода Хаусхолдера, представленная в пакете SCALAPACK библиотеки Intel MKL (метод pdgehrd).  Все результаты получены на суперкомпьютере «Ломоносов-2». Использовались процессоры Intel Xeon E5-2697v3, сеть FDR Infiniband а также компилятор Intel с опциями, рекомнедованными Intel MKL Advisor. &lt;br /&gt;
На рисунках показана эффективность реализации метода Хаусхолдера (отражений) QR-разложения квадратной матрицы для размерности матрицы 20000, запуск проводился на 100 ядрах, выполнялось 11 итераций.&lt;br /&gt;
[[file:PDGEQRF cpu.png|thumb|center|700px|Рисунок 9. График загрузки CPU при выполнении метода Хаусхолдера (отражений) QR-разложения квадратной матрицы]]&lt;br /&gt;
&lt;br /&gt;
На графике загрузки процессора видно, что почти все время работы программы средний уровень загрузки составляет около 85%. Это хорошая картина для программ, запущенных c использованием технологии Hyper Threading т.е. всех виртуальных ядер доступных для данной архитектуры. К концу итерации виден более высокий уровень интенсивности вычислений в срванении со началом и серединой вычислений.&lt;br /&gt;
&lt;br /&gt;
[[file:PDGEQRF LoadAvg.png|thumb|center|700px|Рисунок 10. График числа процессов, ожидающих вхождения в стадию счета (Loadavg), при работе метода Хаусхолдера (отражений) QR-разложения квадратной матрицы]]&lt;br /&gt;
&lt;br /&gt;
На графике числа процессов, ожидающих вхождения в стадию счета (Loadavg), видно, что на протяжении всей работы программы значение этого параметра постоянно снижается со среднего значения 100 и к концу итерации снижается до 80. Это указывает на изменяющуюся загрузку аппаратных ресурсов процессами, однако их число для узла слишком велико, что с одной стороны может указывать на не очень рациональные опции компиляции, которые могут приводить к снижению производительности из-за накладных расходов на переключение контекста между нитями.&lt;br /&gt;
&lt;br /&gt;
 [[file:PDGEQRF L1.png|thumb|center|700px|Рисунок 11. График кэш-промахов L1 в секунду при работе метода Хаусхолдера (отражений) QR-разложения квадратной матрицы]]&lt;br /&gt;
 &lt;br /&gt;
На графике кэш-промахов первого уровня видно, что число промахов достаточно низкое и находится на уровне 5 млн/сек в среднем по всем узлам. Интересен факт снижения числа промахов к концу каждой итерации, хотя снижение и не очень значительное, но вполне заметное.&lt;br /&gt;
&lt;br /&gt;
 [[file:PDGEQRF L2.png|thumb|center|700px|Рисунок 12. График кэш-промахов L1 в секунду при работе метода Хаусхолдера (отражений) QR-разложения квадратной матрицы]]&lt;br /&gt;
 &lt;br /&gt;
На графике кэш-промахов второго уровня видно, что число промахов достаточно тоже низкое и находится на уровне 1 млн/сек в среднем по всем узлам. На графике промахов второго уровня факт снижения числа промахов к концу итерации проявляется более явно и снижение и более значительное и заметное (с 1.2 млн до 0.5 млн.).&lt;br /&gt;
&lt;br /&gt;
 [[file:PDGEQRF L3.png|thumb|center|700px|Рисунок 13. График кэш-промахов L3 в секунду при работе метода Хаусхолдера (отражений) QR-разложения квадратной матрицы]]&lt;br /&gt;
&lt;br /&gt;
На графике кэш-промахов последнего уровня видно, что число промахов достаточно небольшое и быстро убывает с уровня 75 тыс/сек до 20 тыс/сек в среднем по всем узлам. Это указывает на то, что задача достаточно хорошо укладывается в кэш-память третьего уровня, особенно к концу итерации.&lt;br /&gt;
&lt;br /&gt;
[[file:PDGEQRF Inf data.png|thumb|center|700px|Рисунок 13. График скорости передачи по сети Infiniband в байт/сек при работе метода Хаусхолдера (отражений) QR-разложения квадратной матрицы]]&lt;br /&gt;
&lt;br /&gt;
На графике скорости передачи данных по сети Infiniband наблюдается достаточно низкая интенсивность использования коммуникационной сети на каждой итерации. Причем к концу каждой итерации интенсивность передачи данных сильно снижается. Это указывает на бОльшую необходимость в обмене данными между процессами в начале итерации и большую локальность вычислений к концу итерации. Наблюдается так же разбалансированность пересылок между максимальной и минимальной интенсивностью среди использованных узлов.&lt;br /&gt;
&lt;br /&gt;
 [[file:PDGEQRF Inf pckts.png|thumb|center|700px|Рисунок 14. График скорости передачи по сети Infiniband в пакетах/сек при работе метода Хаусхолдера (отражений) QR-разложения квадратной матрицы]]&lt;br /&gt;
&lt;br /&gt;
На графике скорости передачи данных в пакетах в секунду наблюдается большая «кучность» показаний максимального минимального и среднего значений в сравнении с графиком скорости передачи в байт/сек. Это говорит о том, что, вероятно, процессы обмениваются сообщениями различной длины, что указывает на неравномерное распределение данных. Также наблюдается рост интенсивности использования сети к концу каждой итерации. Особенно это различие заметно к концу итерации.&lt;br /&gt;
 &lt;br /&gt;
В целом, по данным системного мониторинга работы программы можно сделать вывод о том, что программа работала стабильно и эффективно использовала память. Использование памяти и коммуникационной среды не достаточно интенсивное, что может стать указывать на факторы снижения эффективности и увеличении производительности при существенном росте размера задачи.&lt;br /&gt;
Для существующей в SCALAPACK параллельной реализаций характерно сильное снижение числа кэш-промахов и обменов к концу итерации&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
В сравнении с методом Гивенса, который имеет естественное двумерное блочное разбиение на основе точечного метода, метод Хаусхолдера из-за худших характеристик локальности (наличие пучков рассылок) и меньшего количества независимых обобщённых развёрток графа не так хорош для реализаций на системах с распределённой памятью, как для систем с общей памятью. Поэтому на массово параллельных системах с распределённой памятью следует применять метод Хаусхолдера (если уж именно его нужно реализовать) не в точечной версии, а в разрабатываемых исследователями блочных вариантах. Следует отметить, что эти варианты - не блочная нарезка описанного метода, а самостоятельные методы. Особенно их применение рекомендуется в случаях с большой разрежённостью матрицы.&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Большинство пакетов от LINPACKа и LAPACKa до SCALAPACKa используют для QR-разложения матриц именно метод Хаусхолдера, правда, в различных модификациях (обычно с использованием BLAS). Существует большая подборка исследовательских работ по блочным версиям.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Статьи в работе]]&lt;br /&gt;
[[Категория:Разложения матриц]]&lt;/div&gt;</summary>
		<author><name>Teplov</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D0%A5%D0%B0%D1%83%D1%81%D1%85%D0%BE%D0%BB%D0%B4%D0%B5%D1%80%D0%B0_(%D0%BE%D1%82%D1%80%D0%B0%D0%B6%D0%B5%D0%BD%D0%B8%D0%B9)_%D0%B4%D0%BB%D1%8F_%D0%BF%D1%80%D0%B8%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D1%8F_%D1%81%D0%B8%D0%BC%D0%BC%D0%B5%D1%82%D1%80%D0%B8%D1%87%D0%BD%D1%8B%D1%85_%D0%BC%D0%B0%D1%82%D1%80%D0%B8%D1%86_%D0%BA_%D1%82%D1%80%D1%91%D1%85%D0%B4%D0%B8%D0%B0%D0%B3%D0%BE%D0%BD%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%BC%D1%83_%D0%B2%D0%B8%D0%B4%D1%83&amp;diff=21549</id>
		<title>Метод Хаусхолдера (отражений) для приведения симметричных матриц к трёхдиагональному виду</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D0%A5%D0%B0%D1%83%D1%81%D1%85%D0%BE%D0%BB%D0%B4%D0%B5%D1%80%D0%B0_(%D0%BE%D1%82%D1%80%D0%B0%D0%B6%D0%B5%D0%BD%D0%B8%D0%B9)_%D0%B4%D0%BB%D1%8F_%D0%BF%D1%80%D0%B8%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D1%8F_%D1%81%D0%B8%D0%BC%D0%BC%D0%B5%D1%82%D1%80%D0%B8%D1%87%D0%BD%D1%8B%D1%85_%D0%BC%D0%B0%D1%82%D1%80%D0%B8%D1%86_%D0%BA_%D1%82%D1%80%D1%91%D1%85%D0%B4%D0%B8%D0%B0%D0%B3%D0%BE%D0%BD%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%BC%D1%83_%D0%B2%D0%B8%D0%B4%D1%83&amp;diff=21549"/>
		<updated>2017-01-17T22:40:05Z</updated>

		<summary type="html">&lt;p&gt;Teplov: /* Динамические характеристики и эффективность реализации алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Приведение симметричной вещественной матрицы к трёхдиагональному виду методом Хаусхолдера (отражений)&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(n^3)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;2n^2+O(n)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;n^2/2&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;(n^2+n)/2&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;(n^2+3n)/2&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
Основные авторы описания: [[Участник:Frolov|А.В.Фролов]]&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Метод Хаусхолдера&#039;&#039;&#039; (в советской математической литературе чаще называется &#039;&#039;&#039;методом отражений&#039;&#039;&#039;) используется для приведения симметричных вещественных матриц к трёхдиагональному виду, или, что то же самое, для разложения &amp;lt;math&amp;gt;A=QTQ^T&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; - ортогональная, &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; — симметричная трёхдиагональная матрица)&amp;lt;ref&amp;gt;В.В.Воеводин, Ю.А.Кузнецов. Матрицы и вычисления. М.: Наука, 1984.&amp;lt;/ref&amp;gt;. При этом матрица &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; хранится и используется не в своём явном виде, а в виде произведения матриц отражения&amp;lt;ref name=&amp;quot;VOLA&amp;quot;&amp;gt;Воеводин В.В. Вычислительные основы линейной алгебры. М.: Наука, 1977.&amp;lt;/ref&amp;gt;. Каждая из матриц отражения может быть определена одним вектором. Это позволяет в классическом исполнении метода отражений хранить результаты разложения на месте матрицы A с использованием одномерного дополнительного массива. &lt;br /&gt;
&lt;br /&gt;
В данной статье рассматривается именно классическое исполнение, в котором не используются приёмы типа сдваивания при вычислениях скалярных произведений.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В методе Хаусхолдера для выполнения &amp;lt;math&amp;gt;QTQ^T&amp;lt;/math&amp;gt;-разложения матрицы используются умножения слева её текущих модификаций на матрицы Хаусхолдера (отражений) с последующим умножением на те же матрицы справа. &lt;br /&gt;
&lt;br /&gt;
{{Шаблон:Матрица отражений}}&lt;br /&gt;
&lt;br /&gt;
На &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м шаге метода с помощью преобразования отражения &amp;quot;убираются&amp;quot; ненулевые поддиагональные элементы, начиная с &amp;lt;math&amp;gt;i+2&amp;lt;/math&amp;gt;-го в &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м столбце. После умножения на эту же матрицу отражения справа автоматически убираются и ненулевые наддиагональные элементы, начиная с &amp;lt;math&amp;gt;i+2&amp;lt;/math&amp;gt;-го в &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-й строке, а полученная модификация снова приобретает симметричный вид. &lt;br /&gt;
&lt;br /&gt;
На каждом из шагов метода матрицу отражений обычно представляют не в стандартном виде, а в виде &amp;lt;math&amp;gt;U=E-\frac{1}{\gamma}vv^*&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; находится через координаты текущего &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; - вектор размерности &amp;lt;math&amp;gt;n+1-i&amp;lt;/math&amp;gt;, составленный из элементов &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца, начиная с &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;(s,s)=0&amp;lt;/math&amp;gt;, то &amp;lt;math&amp;gt;v=e_{i}&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\gamma = \frac{1}{2}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В остальных случаях по алгоритму вычисляется &amp;lt;math&amp;gt;u = \frac{1}{\sqrt{(s,s)}}s&amp;lt;/math&amp;gt;, и далее &amp;lt;math&amp;gt;v_{j}=0&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;j&amp;lt;i&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;v_{j}=u_{j-i+1}&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;j&amp;gt;i&amp;lt;/math&amp;gt;, а &amp;lt;math&amp;gt;v_{i}=1&amp;lt;/math&amp;gt;, если &amp;lt;math&amp;gt;u_{1}=0&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v_{i}=\frac{u_{1}}{|u_{1}|}(1+|u_{1}|)&amp;lt;/math&amp;gt; для остальных значений. При этом &amp;lt;math&amp;gt;\gamma = 1+|u_{1}|=|v_{i}|&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
После вычисления вектора &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; подстолбцы справа от ведущего модифицируются по формулам &amp;lt;math&amp;gt;x&#039;=x-\frac{(x,v)}{\gamma}v&amp;lt;/math&amp;gt;. Потом по аналогичным формулам модифицируются строки ниже ведущей. Благодаря ассоциативности этих операций после вычисления вектора &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; можно сразу выписать формулы модификации всех элементов справа и снизу от ведущих столбца и строки. Оказывается, что если для каждого столбца матрицы &amp;lt;math&amp;gt;x^{(j)}&amp;lt;/math&amp;gt; с номером &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; известно &amp;lt;math&amp;gt;\beta_{j}=\frac{(x^{(j)},v)}{\gamma}&amp;lt;/math&amp;gt;, то для модифицируемого элемента матрицы &amp;lt;math&amp;gt;y&amp;lt;/math&amp;gt; в позиции &amp;lt;math&amp;gt;(i,j)&amp;lt;/math&amp;gt; выполняется модификация &amp;lt;math&amp;gt;y&#039; = y - \beta_{i} v_{i} - \beta_{j} v_{j} - \Delta v_{i} v_{j}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\Delta = \frac{(\beta,v)}{\gamma}&amp;lt;/math&amp;gt;. При этом все эти модификации на каждом конкретном шаге алгоритма для разных пар &amp;lt;math&amp;gt;(i,j)&amp;lt;/math&amp;gt; можно выполнять независимо друг от друга, в том числе и параллельно.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
&lt;br /&gt;
[[File:HausLeftSym1.png|thumb|right|800px|Рисунок 1. Граф первой половины шага алгоритма приведения к трёхдиагональному виду с отображением входных данных. Изображён граф первой половины шага с номером &amp;lt;math&amp;gt;n-4&amp;lt;/math&amp;gt;. Квадраты - результаты выполнения предыдущего шага. Если шаг первый, то это входные данные. Зелёные кружки - операция вида &amp;lt;math&amp;gt;a+b^2&amp;lt;/math&amp;gt;, салатовые - операция вида &amp;lt;math&amp;gt;a+bc&amp;lt;/math&amp;gt;. Синий кружок - вычисление параметров матрицы отражения, светло-красные - вычисление коэффициентов &amp;lt;math&amp;gt;\beta_i&amp;lt;/math&amp;gt;для следующего полушага, жёлтые - вычисление вектора &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.]]&lt;br /&gt;
[[File:HousLeftSym2.png|thumb|right|600px|Рисунок 2. Граф второй половины шага алгоритма приведения к трёхдиагональному виду с отображением входных данных. Изображён граф первой половины шага с номером &amp;lt;math&amp;gt;n-3&amp;lt;/math&amp;gt;. Квадраты - результаты выполнения предыдущего шага. Если шаг первый, то это входные данные. Синий, жёлтые и светло-красные кружки выполняются на первом полушаге (см. Рисунок 1). Голубые кружки -  операции &amp;lt;math&amp;gt;a+bc&amp;lt;/math&amp;gt;, зелёное - умножение, чёрные - операции &amp;lt;math&amp;gt;a+bc+de+fce&amp;lt;/math&amp;gt;.]]&lt;br /&gt;
&lt;br /&gt;
Основную часть алгоритма составляют вычисления на каждом шагу скалярных произведений &amp;lt;math&amp;gt;(s,s)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(x,v)&amp;lt;/math&amp;gt; для всех подстолбцов &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt;справа от текущего, а также проводимые над нижним правым квадратом матрицы операции вида &amp;lt;math&amp;gt;y&#039;=y-ab-cd-fbd&amp;lt;/math&amp;gt;, с учётом симметрии матрицы.&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Как уже сказано в описании ядра, основная часть - вычисления на каждом шагу скалярных произведений &amp;lt;math&amp;gt;(s,s)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(x,v)&amp;lt;/math&amp;gt; для всех подстолбцов &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; справа от текущего, а также массовые покомпонентные операции &amp;lt;math&amp;gt;y&#039;=y-ab-cd-fbd&amp;lt;/math&amp;gt;. При этом, однако, строгая последовательность выполнения первых двух подшагов не обязательна, в силу связи получаемых векторов &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; можно одновременно с &amp;lt;math&amp;gt;(s,s)&amp;lt;/math&amp;gt; вычислять и произведения &amp;lt;math&amp;gt;(x,s)&amp;lt;/math&amp;gt; с последующим выражением через них коэффициентов модификации. Это позволяет почти вдвое уменьшать критический путь графа алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Последовательность выполнения алгоритма обычно записывается как последовательное &amp;quot;обнуление&amp;quot; поддиагональных элементов столбцов, начиная с 1-го столбца и заканчивая предпоследним &amp;lt;math&amp;gt;(n-1)&amp;lt;/math&amp;gt;-м.&lt;br /&gt;
&lt;br /&gt;
При этом в каждом &amp;quot;обнуляемом&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м столбце &amp;quot;обнуляются&amp;quot; сразу все его поддиагональные элементы одновременно, с &amp;lt;math&amp;gt;(i+1)&amp;lt;/math&amp;gt;-го до &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;-го. &lt;br /&gt;
&lt;br /&gt;
Каждое &amp;quot;обнуление&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца состоит из двух шагов: &lt;br /&gt;
а) вычисление параметров матрицы отражения &amp;lt;math&amp;gt;U_{i}&amp;lt;/math&amp;gt; такой, чтобы при умножении на неё слева &amp;quot;обнулились&amp;quot; все поддиагональные его элементы;&lt;br /&gt;
б) одновременное умножение слева матрицы отражения &amp;lt;math&amp;gt;U_{i}&amp;lt;/math&amp;gt; и справа  матрицы отражения &amp;lt;math&amp;gt;U_{i}^T&amp;lt;/math&amp;gt;на текущую версию матрицы.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В последовательной версии основная сложность алгоритма определяется прежде всего вычислениями скалярных произведений векторов, а также массовых модификаций элементов вида &amp;lt;math&amp;gt;y&#039;=y-\alpha v - \beta w - \Delta vw&amp;lt;/math&amp;gt;. Они, если не учитывать возможную разреженность, составляют (в главном члене) по &amp;lt;math&amp;gt;O(n^3)&amp;lt;/math&amp;gt; операций действительного умножения и сложения/вычитания.&lt;br /&gt;
&lt;br /&gt;
При классификации по последовательной сложности, таким образом, метод Хаусхолдера относится к алгоритмам &#039;&#039;с кубической сложностью&#039;&#039;.&lt;br /&gt;
&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;n&amp;lt;/math&amp;gt; к трёхдиагональной методом Хаусхолдера нужно рассмотреть критический путь графа. &lt;br /&gt;
&lt;br /&gt;
Как видно из описания разных вершин, вычисления при &amp;quot;обнулении&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца параметров отражения и скалярных произведений состоят из основной части - ветви длиной по &amp;lt;math&amp;gt;2(n-i)&amp;lt;/math&amp;gt; умножений и сложений - и коррекции вычислений, которые составляют &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; операций. &lt;br /&gt;
&lt;br /&gt;
Поэтому по грубой (без членов низших порядков) оценке критический путь метода Хаусхолдера будет идти через &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt; умножений и &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt; сложений/вычитаний. &lt;br /&gt;
 &lt;br /&gt;
Поэтому в параллельном варианте, как и в последовательном, основную долю требуемого для выполнения алгоритма времени будут определять операции вида &amp;lt;math&amp;gt;a+bc&amp;lt;/math&amp;gt;.  &lt;br /&gt;
&lt;br /&gt;
При классификации по высоте ЯПФ, таким образом, метод Хаусхолдера относится к алгоритмам &#039;&#039;с квадратичной сложностью&#039;&#039;. При классификации по ширине ЯПФ его сложность будет также &#039;&#039;квадратичной&#039;&#039; (без расширения ряда ярусов, связанных с векторными операциями сложения, пришлось бы увеличить вдвое длину критического пути).&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: плотная симметричная квадратная матрица &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; (элементы &amp;lt;math&amp;gt;a_{ij}&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;(n^2+n)/2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: трёхдиагональная матрица &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; (ненулевые элементы &amp;lt;math&amp;gt;r_{ij}&amp;lt;/math&amp;gt; в последовательном варианте хранятся в элементах исходной матрицы &amp;lt;math&amp;gt;a_{ij}&amp;lt;/math&amp;gt;), унитарная (ортогональная) матрица Q - как произведение матриц Хаусхолдера (отражения) (их вектора нормалей к плоскостям отражения в последовательном варианте хранятся в поддиагональных элементах исходной матрицы &amp;lt;math&amp;gt;a_{ij}&amp;lt;/math&amp;gt; и в одном дополнительном столбце размерности n).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;(n^2+3n)/2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Соотношение последовательной и параллельной сложности, как хорошо видно, является &#039;&#039;линейным&#039;&#039;, что даёт определённый стимул для распараллеливания. Однако у наискорейшей ЯПФ ширина &#039;&#039;квадратична&#039;&#039;, что указывает на дисбаланс между загруженностями устройств при попытке её реально запрограммировать. Поэтому более практично даже при хорошей (быстрой) вычислительной сети оставить количество устройств (например, узлов кластера) &#039;&#039;линейным&#039;&#039; по размеру матрицы, что удвоит критический путь реализуемой ЯПФ. &lt;br /&gt;
&lt;br /&gt;
При этом вычислительная мощность алгоритма, как отношение числа операций к суммарному объему входных и выходных данных, &#039;&#039;линейна&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Алгоритм в рамках выбранной версии полностью детерминирован.&lt;br /&gt;
&lt;br /&gt;
Вычислительная погрешность в методе отражений (Хаусхолдера) растет &#039;&#039;линейно&#039;&#039;, как и в методе Гивенса (вращений).&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
В варианте с кратчайшим критическим путём графа алгоритма (с использованием зависимости между обнуляемым вектором и направляющим вектором отражения) метод Хаусхолдера (отражений) приведения квадратной симметричной вещественной матрицы к трёхдиагональному виду на Фортране 77 можно записать так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;fortran&amp;quot;&amp;gt;&lt;br /&gt;
	DO  I = 1, N-2&lt;br /&gt;
&lt;br /&gt;
            DO K = I, N&lt;br /&gt;
              SX(K)=A(N,I)*A(N,K)&lt;br /&gt;
            END DO&lt;br /&gt;
            DO J = N-1, I+1, -1&lt;br /&gt;
                 SX(I)=SX(I)+A(J,I)*A(J,I)&lt;br /&gt;
            END DO &lt;br /&gt;
            DO K = I+1, N&lt;br /&gt;
              DO J = N-1, K, -1&lt;br /&gt;
                 SX(K)=SX(K)+A(J,I)*A(J,K)&lt;br /&gt;
              END DO&lt;br /&gt;
              DO J = K-1,I+1,-1&lt;br /&gt;
                 SX(K)=SX(K)+A(J,I)*A(K,J)&lt;br /&gt;
              END DO&lt;br /&gt;
            END DO&lt;br /&gt;
&lt;br /&gt;
               ALPHA = SQRT (SX(I))&lt;br /&gt;
               IF (A(I+1,I).NE.0.) THEN&lt;br /&gt;
                  BETA = 1./ALPHA&lt;br /&gt;
                  DO J = I+2, N&lt;br /&gt;
                    A(J,I)=A(J,I)*BETA&lt;br /&gt;
                  END DO&lt;br /&gt;
                  SX(I) = A(I+1,I)*BETA+SIGN(1.,A(I+1,I))                     &lt;br /&gt;
                  A(I+1,I)=ALPHA&lt;br /&gt;
                  G=1./ABS(SX(I)) ! 1/gamma&lt;br /&gt;
                  SX2=0.&lt;br /&gt;
                  DO K = I+2, N&lt;br /&gt;
                     SX(K)=SX(K)*BETA*G+SIGN(A(K,I+1),SX(I))&lt;br /&gt;
                     SX2=SX(K)*A(K,I)+SX2                     &lt;br /&gt;
                  END DO&lt;br /&gt;
                  SX2=G*SX2&lt;br /&gt;
                  DO K = I+2, N&lt;br /&gt;
                     A(K,K) = A(K,K)-2*A(K,I)*SX(K)+SX2*A(K,I)**2&lt;br /&gt;
                     DO J = K+1, N&lt;br /&gt;
                       A (J,K) = A(J,K)-A(J,I)*SX(K)-A(K,I)*SX(I)+SX2*A(J,I)*A(K,I)&lt;br /&gt;
                     END DO&lt;br /&gt;
                  END DO&lt;br /&gt;
               ELSE&lt;br /&gt;
                  IF (ALPHA.NE.0.) THEN&lt;br /&gt;
                     BETA = 1./ALPHA&lt;br /&gt;
                     DO J = I+2, N&lt;br /&gt;
                       A(J,I)=A(J,I)*BETA&lt;br /&gt;
                     END DO&lt;br /&gt;
                     SX(I) = -1. &lt;br /&gt;
                     A(I+1,I)=ALPHA&lt;br /&gt;
                     G=1.! 1/gamma&lt;br /&gt;
                     SX2=0.&lt;br /&gt;
                     DO K = I+2, N&lt;br /&gt;
                        SX(K)=SX(K)*BETA*G+SIGN(A(K,I+1),SX(I))&lt;br /&gt;
                        SX2=SX(K)*A(K,I)+SX2                 &lt;br /&gt;
                     END DO&lt;br /&gt;
                     SX2=G*SX2&lt;br /&gt;
                     DO K = I+2, N  &lt;br /&gt;
                        A(K,K) = A(K,K)-2*A(K,I)*SX(K)+SX2*A(K,I)**2                   &lt;br /&gt;
                        DO J = K+1, N&lt;br /&gt;
                          A (J,K) = A(J,K)-A(J,I)*SX(K)-A(K,I)*SX(I)+SX2*A(J,I)*A(K,I)&lt;br /&gt;
                        END DO&lt;br /&gt;
                     END DO&lt;br /&gt;
                  ELSE&lt;br /&gt;
                     SX(I)=1.&lt;br /&gt;
                  END IF&lt;br /&gt;
               END IF               &lt;br /&gt;
&lt;br /&gt;
		&lt;br /&gt;
	END DO&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Здесь симметричная трёхдиагональная матрица хранится в диагонали и нижней кодиагонали массива A, вектора v - в поддиагональной части массива, за исключением первых их элементов, для которых выделен массив SX.&lt;br /&gt;
&lt;br /&gt;
Обычно же в последовательных версиях коэффициенты модификаций столбцов вычисляются целиком через скалярные произведения после вычислений параметров матрицы отражения. При этом схема чуть проще. Удлиняется критический путь графа, но для последовательных реализаций это неважно.&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
К сожалению, в графе, как видно по рисунку, в наличии пучки рассылок, в них неизбежно часть дуг остаются длинными, что отрицательно влияет на локальность вычислений по пространству.  &lt;br /&gt;
&lt;br /&gt;
==== Локальность реализации алгоритма ====&lt;br /&gt;
===== Структура обращений в память и качественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qtq_1.png|thumb|center|700px|Рисунок 3. Метод Хаусхолдера (отражений) для приведения симметричных матриц к трёхдиагональному виду. Общий профиль обращений в память]]&lt;br /&gt;
&lt;br /&gt;
На рис. 3 представлен профиль обращений в память для реализации метода Хаусхолдера (отражений) для приведения симметричных матриц к трёхдиагональному виду. Данный профиль обладает явной итерационной структурой, при этом видно, что итерации очень похожи. Основное их отличие заключается в наборе используемых данных – на каждой следующей итерации несколько первых элементов отбрасываются их рассмотрения, то есть чем позже итерация, тем меньше данных в ней задействовано. Также можно отметить, что число обращений в каждой последующей итерации немного уменьшается. Для того чтобы проанализировать общий профиль, в таком случае чаще всего достаточно изучить одну итерацию. Рассмотрим самую первую из них более детально.&lt;br /&gt;
&lt;br /&gt;
На рис. 4 показан набор обращений в рамках первой итерации (выделен на рис. 3 зеленым). Его можно разбить на несколько фрагментов и рассмотреть их отдельно. Строение фрагментов 2-5 можно оценить по общему графику для итерации. Фрагмент 2 представляет собой набор последовательных переборов в обратном порядке с небольшим шагом, причем число элементов в переборе постепенно уменьшается. Такое строение характеризуется достаточно высокой пространственной локальностью, поскольку часто происходит обращений к близко расположенным данным, однако низкой временной, поскольку данные повторно не используются. То же самое верно и для фрагмента 4, основное отличие которого заключается в том, что переборы выполняются в прямом порядке. Отметим, что некоторое искривление данного фрагмента связано не со строением самого фрагмента, а с разным числом параллельно выполняющихся обращений (см., например, правую область фрагмента 6), чего не наблюдается при выполнении фрагмента 2.&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qtq_2.png|thumb|center|700px|Рисунок 4. Профиль обращений, одна итерация]]&lt;br /&gt;
&lt;br /&gt;
Далее, фрагменты  3 и 5 состоят из небольшого числа обращений к данным, расположенным далеко друг от друга. Такие фрагменты характеризуются низкой пространственной и временной локальностью.&lt;br /&gt;
&lt;br /&gt;
Оставшиеся фрагменты требуют более детального рассмотрения. Перейдем к изучению фрагмента 1, который целиком представлен на рис. 5. Здесь мы можем видеть несколько этапов, каждый из которых представляет собой последовательный перебор элементов с шагом по памяти 1, причем в некоторых случаях данные много раз используются повторно (особенно это заметно в правой нижней области рисунка, где выполняется множество обращений к одному и тому же элементу). При условии, что в данном фрагменте задействовано всего около 40 элементов, можно говорить, что данный набор обращений обладает высокой пространственной и временной локальностью. &lt;br /&gt;
&lt;br /&gt;
[[file:householder_qtq_3.png|thumb|center|500px|Рисунок 5. Профиль обращений, одна итерация, фрагмент 1]]&lt;br /&gt;
&lt;br /&gt;
Далее рассмотрим детально фрагмент 6 (рис. 6). Здесь можно увидеть, что повторные обращения к данным выполняются не подряд, а через некоторые промежутки, однако число задействованных элементов также очень невелико, так что и в этом случае локальность (и пространственная, и временная) будет высокой.&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qtq_4.png|thumb|center|500px|Рисунок 6. Профиль обращений, одна итерация, фрагмент 6]]&lt;br /&gt;
&lt;br /&gt;
В целом можно сказать, что рассматриваемая итерация обладает достаточно высокой пространственной и временной локальностью. Поскольку остальные итерации устроены подобным образом, данные вывод можно перенести и на общий профиль обращений.&lt;br /&gt;
&lt;br /&gt;
===== Количественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
Оценка выполняется на основе характеристики daps, которая оценивает число выполненных обращений (чтений и записей) в память в секунду. Данная характеристика является аналогом оценки flops применительно к работе с памятью и является в большей степени оценкой производительности взаимодействия с памятью, чем оценкой локальности. Однако она служит хорошим источником информации, в том числе для сравнения с результатами по следующей характеристике cvg.&lt;br /&gt;
&lt;br /&gt;
На рисунке 7 приведены значения daps для реализаций распространенных алгоритмов, отсортированные по возрастанию (чем больше daps, тем в общем случае выше производительность). Можно увидеть, что производительность работы с памятью в данном случае достаточно высока – значение daps лишь немногим уступает тесту Linpack, который обладает высокой эффективностью работы с памятью, и немного превосходит, например, реализацию метода Якоби или метода Гаусса.&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qtq_daps.png|thumb|center|700px|Рисунок 7. Сравнение значений оценки daps]]&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
==== Масштабируемость алгоритма ====&lt;br /&gt;
==== Масштабируемость реализации алгоритма ====&lt;br /&gt;
Проведём исследование масштабируемости вширь реализации согласно [[Scalability methodology|методике]]. Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов-2&amp;quot; [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Набор и границы значений изменяемых [[Глоссарий#Параметры запуска|параметров запуска]] реализации алгоритма: &lt;br /&gt;
&lt;br /&gt;
*	число процессоров [16:81] с шагом n^2;&lt;br /&gt;
*	размер матрицы [1000 : 16000] с шагом 1000.&lt;br /&gt;
&lt;br /&gt;
В результате проведённых экспериментов был получен следующий диапазон [[Глоссарий#Эффективность реализации|эффективности реализации]] алгоритма:&lt;br /&gt;
&lt;br /&gt;
*	минимальная эффективность реализации 4.763e-05%;&lt;br /&gt;
*	максимальная эффективность реализации 0.011%.&lt;br /&gt;
&lt;br /&gt;
На следующих рисунках приведены графики [[Глоссарий#Производительность|производительности]] и эффективности выбранной реализации алгоритма в процедуре PDSYTRD из Scalapack в зависимости от изменяемых параметров запуска.&lt;br /&gt;
&lt;br /&gt;
[[file:PDSYTRD Perf2.png|thumb|center|700px|Рисунок 8. Параллельная реализация метода Хаусхолдера QR-разложения. Изменение производительности в зависимости от числа процессоров и размера матрицы.]]&lt;br /&gt;
[[file:PDSYTRD eff2.png|thumb|center|700px|Рисунок 9. Параллельная реализация метода Хаусхолдера QR-разложения. Изменение эффективности в зависимости от числа процессоров и размера матрицы.]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
Для проведения экспериментов использовалась реализация Метод Хаусхолдера, представленная в пакете SCALAPACK библиотеки Intel MKL (метод pdgehrd).  Все результаты получены на суперкомпьютере «Ломоносов-2». Использовались процессоры Intel Xeon E5-2697v3, сеть FDR Infiniband а также компилятор Intel с опциями, рекомнедованными Intel MKL Advisor. &lt;br /&gt;
На рисунках показана эффективность реализации Метод Хаусхолдера (отражений) для приведения симметричных матриц к трёхдиагональному виду для размерности матрицы 15000, запуск проводился на 81 ядре.&lt;br /&gt;
&lt;br /&gt;
[[file:PDGETRD cpu.png|thumb|center|700px|Рисунок 9. График загрузки CPU при выполнении метода Хаусхолдера (отражений) приведения матрицы к трёхдиагональному виду]]&lt;br /&gt;
&lt;br /&gt;
На графике загрузки процессора видно, что почти все время работы программы средний уровень загрузки составляет около 90%. Это хорошая картина для программ, запущенных c использованием технологии Hyper Threading т.е. всех виртуальных ядер доступных для данной архитектуры. К концу итерации виден более высокий уровень интенсивности вычислений в срванении со началом и серединой вычислений.&lt;br /&gt;
&lt;br /&gt;
[[file:PDGETRD LoadAvg.png|thumb|center|700px|Рисунок 10. График числа процессов, ожидающих вхождения в стадию счета (Loadavg), при работе метода Хаусхолдера (отражений) приведения матрицы к трёхдиагональному виду]]&lt;br /&gt;
&lt;br /&gt;
На графике числа процессов, ожидающих вхождения в стадию счета (Loadavg), видно, что на протяжении всей работы программы значение этого параметра постоянно и приблизительно равняется 140 и только к концу итерации снижается до 80. Это свидетельствует о стабильной работе программы на протяжении всего выполнения со 140 нитями на одном узле или около 10 нитей на 1 физическое ядро на каждом узле. Это указывает на статичную загрузку аппаратных ресурсов процессами, однако их число для узла слишком велико, что с одной стороны может указывать на не очень рациональные опции компиляции, который могут приводить к снижению производительности из-за накладных расходов на переключение контекста между нитями.&lt;br /&gt;
&lt;br /&gt;
 [[file:PDGETRD L1.png|thumb|center|700px|Рисунок 11. График кэш-промахов L1 в секунду при работе метода Хаусхолдера (отражений) приведения матрицы к трёхдиагональному виду]]&lt;br /&gt;
 &lt;br /&gt;
На графике кэш-промахов первого уровня видно, что число промахов достаточно низкое и находится на уровне 5 млн/сек в среднем по всем узлам. Интересен факт снижения числа промахов к концу итерации, хотя снижение и не очень значительное, но вполне заметное.&lt;br /&gt;
&lt;br /&gt;
 [[file:PDGETRD L2.png|thumb|center|700px|Рисунок 12. График кэш-промахов L1 в секунду при работе метода Хаусхолдера (отражений) приведения матрицы к трёхдиагональному виду]]&lt;br /&gt;
 &lt;br /&gt;
На графике кэш-промахов второго уровня видно, что число промахов достаточно тоже низкое и находится на уровне 1 млн/сек в среднем по всем узлам. На графике промахов второго уровня факт снижения числа промахов к концу итерации проявляется более явно и снижение и более значительное и заметное (с 1.2 млн до 0.5 млн.).&lt;br /&gt;
&lt;br /&gt;
 [[file:PDGETRD L3.png|thumb|center|700px|Рисунок 13. График кэш-промахов L3 в секунду при работе метода Хаусхолдера (отражений) приведения матрицы к трёхдиагональному виду]]&lt;br /&gt;
&lt;br /&gt;
На графике кэш-промахов последнего уровня видно, что число промахов достаточно небольшое и быстро убывает с уровня 90 тыс/сек до 0 в среднем по всем узлам. Это указывает на то, что задача достаточно хорошо укладывается в кэш-память третьего уровня, особенно к концу итерации.&lt;br /&gt;
[[file:PDGETRD Inf data.png|thumb|center|700px|Рисунок 13. График скорости передачи по сети Infiniband в байт/сек при работе метода Хаусхолдера (отражений) приведения матрицы к трёхдиагональному виду]]&lt;br /&gt;
&lt;br /&gt;
На графике скорости передачи данных по сети Infiniband наблюдается достаточно низкая интенсивность использования коммуникационной сети на каждой итерации. Причем к концу каждой итерации интенсивность передачи данных сильно снижается. Это указывает на бОльшую необходимость в обмене данными между процессами в начале итерации и большую локальность вычислений к концу итерации.&lt;br /&gt;
 [[file:PDGETRD Inf pckts.png|thumb|center|700px|Рисунок 14. График скорости передачи по сети Infiniband в пакетах/сек при работе метода Хаусхолдера (отражений) приведения матрицы к трёхдиагональному виду]]&lt;br /&gt;
&lt;br /&gt;
На графике скорости передачи данных в пакетах в секунду наблюдается большая «кучность» показаний максимального минимального и среднего значений в сравнении с графиком скорости передачи в байт/сек. Это говорит о том, что, вероятно, процессы обмениваются сообщениями различной длины, что указывает на неравномерное распределение данных. Также наблюдается рост интенсивности использования сети к концу каждой итерации. Особенно это различие заметно к концу итерации.&lt;br /&gt;
 &lt;br /&gt;
В целом, по данным системного мониторинга работы программы можно сделать вывод о том, что программа работала стабильно и эффективно использовала память. Использование памяти и коммуникационной среды не достаточно интенсивное, что может стать указывать на факторы снижения эффективности и увеличении производительности при существенном росте размера задачи.&lt;br /&gt;
Для существующей в SCALAPACK параллельной реализаций характерно сильное снижение числа кэш-промахов и обменов к концу итерации&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
В сравнении с методом Гивенса, который имеет естественное двумерное блочное разбиение на основе точечного метода, метод Хаусхолдера из-за худших характеристик локальности (наличие пучков рассылок) и меньшего количества независимых обобщённых развёрток графа не так хорош для реализаций на системах с распределённой памятью, как для систем с общей памятью. Поэтому на массово параллельных системах с распределённой памятью следует применять метод Хаусхолдера (если уж именно его нужно реализовать) не в точечной версии, а в разрабатываемых исследователями блочных вариантах. Следует отметить, что эти варианты - не блочная нарезка описанного метода, а самостоятельные методы. Особенно их применение рекомендуется в случаях с большой разрежённостью матрицы.&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Большинство пакетов от LINPACKа и LAPACKa до SCALAPACKa используют для QR-разложения матриц именно метод Хаусхолдера, правда, в различных модификациях (обычно с использованием BLAS). Существует большая подборка исследовательских работ по блочным версиям.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Статьи в работе]]&lt;br /&gt;
[[Категория:Разложения матриц]]&lt;/div&gt;</summary>
		<author><name>Teplov</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D0%A5%D0%B0%D1%83%D1%81%D1%85%D0%BE%D0%BB%D0%B4%D0%B5%D1%80%D0%B0_(%D0%BE%D1%82%D1%80%D0%B0%D0%B6%D0%B5%D0%BD%D0%B8%D0%B9)_%D0%BF%D1%80%D0%B8%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D1%8F_%D0%BC%D0%B0%D1%82%D1%80%D0%B8%D1%86%D1%8B_%D0%BA_%D0%B4%D0%B2%D1%83%D1%85%D0%B4%D0%B8%D0%B0%D0%B3%D0%BE%D0%BD%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%B9_%D1%84%D0%BE%D1%80%D0%BC%D0%B5&amp;diff=21547</id>
		<title>Метод Хаусхолдера (отражений) приведения матрицы к двухдиагональной форме</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D0%A5%D0%B0%D1%83%D1%81%D1%85%D0%BE%D0%BB%D0%B4%D0%B5%D1%80%D0%B0_(%D0%BE%D1%82%D1%80%D0%B0%D0%B6%D0%B5%D0%BD%D0%B8%D0%B9)_%D0%BF%D1%80%D0%B8%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D1%8F_%D0%BC%D0%B0%D1%82%D1%80%D0%B8%D1%86%D1%8B_%D0%BA_%D0%B4%D0%B2%D1%83%D1%85%D0%B4%D0%B8%D0%B0%D0%B3%D0%BE%D0%BD%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%B9_%D1%84%D0%BE%D1%80%D0%BC%D0%B5&amp;diff=21547"/>
		<updated>2017-01-17T14:19:29Z</updated>

		<summary type="html">&lt;p&gt;Teplov: /* Динамические характеристики и эффективность реализации алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Приведение матрицы к двудиагональному виду методом Хаусхолдера (отражений)&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;\frac{8 n^3}{3}+O(n^2)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;2n^2+O(n)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;n(n + 2)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
Основные авторы описания: [[Участник:Frolov|А.В.Фролов]]&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Метод Хаусхолдера&#039;&#039;&#039; (в советской математической литературе чаще называется &#039;&#039;&#039;методом отражений&#039;&#039;&#039;) используется для приведения симметричных вещественных матриц к двухдиагональному виду, или, что то же самое, для разложения &amp;lt;math&amp;gt;A=QDU^T&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;Q, U&amp;lt;/math&amp;gt; - ортогональные, &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; — правая двухдиагональная матрица)&amp;lt;ref&amp;gt;В.В.Воеводин, Ю.А.Кузнецов. Матрицы и вычисления. М.: Наука, 1984.&amp;lt;/ref&amp;gt;. При этом матрицы &amp;lt;math&amp;gt;Q, U&amp;lt;/math&amp;gt; хранятся и используются не в своём явном виде, а в виде произведения матриц отражения&amp;lt;ref name=&amp;quot;VOLA&amp;quot;&amp;gt;Воеводин В.В. Вычислительные основы линейной алгебры. М.: Наука, 1977.&amp;lt;/ref&amp;gt;. Каждая из матриц отражения может быть определена одним вектором. Это позволяет в классическом исполнении метода отражений хранить результаты разложения на месте матрицы A с использованием двух одномерных дополнительных массивов. &lt;br /&gt;
&lt;br /&gt;
В данной статье рассматривается именно классическое исполнение, в котором не используются приёмы типа сдваивания при вычислениях скалярных произведений.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В методе Хаусхолдера для выполнения разложения матрицы в произведение двухдиагональной и двух ортогональных используются попеременные умножения слева и справа её текущих модификаций на матрицы Хаусхолдера (отражений). &lt;br /&gt;
&lt;br /&gt;
{{Шаблон:Матрица отражений}}&lt;br /&gt;
&lt;br /&gt;
На &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м шаге метода с помощью преобразования отражения &amp;quot;убираются&amp;quot; ненулевые поддиагональные элементы в &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м столбце, а потом (кроме шага с номером &amp;lt;math&amp;gt;i=n-1&amp;lt;/math&amp;gt;) с помощью преобразования отражения &amp;quot;убираются&amp;quot; ненулевые наддиагональные элементы в &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м столбце, кроме самого левого из них. Таким образом, после &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; шагов преобразований получается правая двудиагональная &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; из разложения матрицы в произведение двудиагональной и двух ортогональных.&lt;br /&gt;
&lt;br /&gt;
На каждом из шагов метода матрицы отражений обычно представляют не в стандартном виде, а в виде &amp;lt;math&amp;gt;U=E-\frac{1}{\gamma}vv^*&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; находится для левых матриц отражения через координаты текущего &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; - вектор размерности &amp;lt;math&amp;gt;n+1-i&amp;lt;/math&amp;gt;, составленный из элементов &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца, начиная с &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;(s,s)=0&amp;lt;/math&amp;gt;, то &amp;lt;math&amp;gt;v=e_{i}&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\gamma = \frac{1}{2}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В остальных случаях по алгоритму вычисляется &amp;lt;math&amp;gt;u = \frac{1}{\sqrt{(s,s)}}s&amp;lt;/math&amp;gt;, и далее &amp;lt;math&amp;gt;v_{j}=0&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;j&amp;lt;i&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;v_{j}=u_{j-i+1}&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;j&amp;gt;i&amp;lt;/math&amp;gt;, а &amp;lt;math&amp;gt;v_{i}=1&amp;lt;/math&amp;gt;, если &amp;lt;math&amp;gt;u_{1}=0&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v_{i}=\frac{u_{1}}{|u_{1}|}(1+|u_{1}|)&amp;lt;/math&amp;gt; для остальных значений. При этом &amp;lt;math&amp;gt;\gamma = 1+|u_{1}|=|v_{i}|&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
После вычисления вектора &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; подстолбцы справа от ведущего модифицируются по формулам &amp;lt;math&amp;gt;x&#039;=x-\frac{(x,v)}{\gamma}v&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Аналогично для правых матриц отражений &amp;lt;math&amp;gt;U=E-\frac{1}{\gamma}vv^*&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; находится через координаты текущей &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-й строки так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; - вектор размерности &amp;lt;math&amp;gt;n-i&amp;lt;/math&amp;gt;, составленный из элементов &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-й строки, начиная с &amp;lt;math&amp;gt;i+1&amp;lt;/math&amp;gt;-го элемента.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;(s,s)=0&amp;lt;/math&amp;gt;, то &amp;lt;math&amp;gt;v=e_{i+1}&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\gamma = \frac{1}{2}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В остальных случаях по алгоритму вычисляется &amp;lt;math&amp;gt;u = \frac{1}{\sqrt{(s,s)}}s&amp;lt;/math&amp;gt;, и далее &amp;lt;math&amp;gt;v_{j}=0&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;j&amp;lt;i+1&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;v_{j}=u_{j-i+2}&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;j&amp;gt;i+1&amp;lt;/math&amp;gt;, а &amp;lt;math&amp;gt;v_{i+1}=1&amp;lt;/math&amp;gt;, если &amp;lt;math&amp;gt;u_{1}=0&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v_{i+1}=\frac{u_{1}}{|u_{1}|}(1+|u_{1}|)&amp;lt;/math&amp;gt; для остальных значений. При этом &amp;lt;math&amp;gt;\gamma = 1+|u_{1}|=|v_{i+1}|&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Основную часть алгоритма составляют вычисления на каждом шагу скалярных произведений &amp;lt;math&amp;gt;(s,s)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(x,v)&amp;lt;/math&amp;gt; для всех подстолбцов &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; справа от текущего, а также векторные операции &amp;lt;math&amp;gt;x&#039;=x-\frac{(x,v)}{\gamma}v&amp;lt;/math&amp;gt;, и затем вычисления на каждом шагу (кроме шага с номером &amp;lt;math&amp;gt;i=n-1&amp;lt;/math&amp;gt;) скалярных произведений &amp;lt;math&amp;gt;(s,s)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(x,v)&amp;lt;/math&amp;gt; для всех подстрок &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; снизу от текущей, а также векторные операции &amp;lt;math&amp;gt;x&#039;=x-\frac{(x,v)}{\gamma}v&amp;lt;/math&amp;gt;. Это используется при программировании метода во многих библиотеках для его конструирования из стандартных подпрограмм (например, из BLAS).&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Всё приведение состоит из &amp;lt;math&amp;gt;2n-3&amp;lt;/math&amp;gt; полушагов. Каждый из них состоит из вычисления некоторой матрицы отражений (Хаусхолдера) и умножения на неё матрицы. Строгая последовательность выполнения этих частей полушагов не обязательна, в силу связи получаемых векторов &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; можно одновременно с &amp;lt;math&amp;gt;(s,s)&amp;lt;/math&amp;gt; вычислять и произведения &amp;lt;math&amp;gt;(x,s)&amp;lt;/math&amp;gt; с последующим выражением через них &amp;lt;math&amp;gt;(x,v)&amp;lt;/math&amp;gt;. Это позволяет почти вдвое уменьшать критический путь графа алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Последовательность выполнения алгоритма обычно записывается как последовательное &amp;quot;обнуление&amp;quot; поддиагональных элементов столбцов, начиная с 1-го столбца, заканчивая &amp;lt;math&amp;gt;(n-1)&amp;lt;/math&amp;gt;-м, попеременно с последовательным &amp;quot;обнулением&amp;quot; наддиагональных (кроме первой кодиагонали) элементов строк, начиная с 1й строки и заканчивая &amp;lt;math&amp;gt;(n-2)&amp;lt;/math&amp;gt;-й &lt;br /&gt;
&lt;br /&gt;
При этом в каждом &amp;quot;обнуляемом&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м столбце &amp;quot;обнуляются&amp;quot; сразу все его поддиагональные элементы одновременно, с &amp;lt;math&amp;gt;(i+1)&amp;lt;/math&amp;gt;-го до &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;-го. &lt;br /&gt;
&lt;br /&gt;
Каждое &amp;quot;обнуление&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца состоит из двух шагов: &lt;br /&gt;
а) вычисление параметров матрицы отражения &amp;lt;math&amp;gt;U_{2i-1}&amp;lt;/math&amp;gt; такой, чтобы при умножении на неё слева &amp;quot;обнулились&amp;quot; все поддиагональные его элементы;&lt;br /&gt;
б) умножение слева матрицы отражения &amp;lt;math&amp;gt;U_{2i-1}&amp;lt;/math&amp;gt; на текущую версию матрицы.&lt;br /&gt;
&lt;br /&gt;
В каждой &amp;quot;обнуляемой&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-й строке &amp;quot;обнуляются&amp;quot; сразу все его наддиагональные элементы одновременно, с &amp;lt;math&amp;gt;(i+2)&amp;lt;/math&amp;gt;-го до &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;-го. &lt;br /&gt;
&lt;br /&gt;
Каждое &amp;quot;обнуление&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-й строки состоит из двух шагов: &lt;br /&gt;
а) вычисление параметров матрицы отражения &amp;lt;math&amp;gt;U_{2i}&amp;lt;/math&amp;gt; такой, чтобы при умножении на неё справа &amp;quot;обнулились&amp;quot; все (кроме первого) наддиагональные её элементы;&lt;br /&gt;
б) умножение справа матрицы отражения &amp;lt;math&amp;gt;U_{2i}&amp;lt;/math&amp;gt; на текущую версию матрицы.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В последовательной версии основная сложность алгоритма определяется прежде всего вычислениями скалярных произведений векторов, а также модификаций векторов вида &amp;lt;math&amp;gt;x&#039;=x-\alpha v&amp;lt;/math&amp;gt;, причем над векторами убывающей по ходу алгоритма размерности. Они, если не учитывать возможную разреженность, составляют (в главном члене) по &amp;lt;math&amp;gt;4n^3/3&amp;lt;/math&amp;gt; операций действительного умножения и сложения/вычитания.&lt;br /&gt;
&lt;br /&gt;
При классификации по последовательной сложности, таким образом, метод Хаусхолдера относится к алгоритмам &#039;&#039;с кубической сложностью&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
[[File:HausLeft1.png|thumb|right|600px|Рисунок 1. Граф нечётного полушага (обнуление &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;го столбца) алгоритма. Квадратики - входные данные шага (берутся с предыдущего полушага), кружки - операции. Зелёным выделены операции типа a+bb, салатовым и голубым - типа a+bc, тёмно-синим - вычисление &amp;lt;math&amp;gt;\gamma , v_{1}&amp;lt;/math&amp;gt;, жёлтым - умножения (или деления). Обведённая группа операций повторяется независимо n-i раз. Результаты синего и жёлтых кружков - выходные для алгоритма.]]&lt;br /&gt;
&lt;br /&gt;
На рисунке 1 изображён граф нечётного (&amp;lt;math&amp;gt;(2i-1)&amp;lt;/math&amp;gt;го) полушага в привязке к элементам обрабатываемой матрицы. Граф чётного (&amp;lt;math&amp;gt;2i&amp;lt;/math&amp;gt;го) полушага почти аналогичен, только длина ветвей (нарисована слева) будет меньше на 1 (&amp;lt;math&amp;gt;n-i&amp;lt;/math&amp;gt;), и для привязки к элементам матрицы его надо отразить относительно диагонали матрицы. После этого отражения графы полушагов можно положить друг на друга слоями, при этом правые нижние углы должны быть друг над другом.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Для понимания ресурса параллелизма в разложении матрицы порядка &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; методом Хаусхолдера нужно рассмотреть критический путь графа. &lt;br /&gt;
&lt;br /&gt;
Как видно из описания разных вершин, вычисления при &amp;quot;обнулении&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца параметров отражения и скалярных произведений состоят из основной части - ветви длиной по &amp;lt;math&amp;gt;n-i&amp;lt;/math&amp;gt; умножений и сложений - и коррекции вычислений, которые составляют &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; операций. Вычисления при &amp;quot;обнулении&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-й строки параметров отражения и скалярных произведений состоят из основной части - ветви длиной по &amp;lt;math&amp;gt;n-i-1&amp;lt;/math&amp;gt; умножений и сложений - и коррекции вычислений, которые составляют &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; операций.&lt;br /&gt;
&lt;br /&gt;
Поэтому по грубой (без членов низших порядков) оценке критический путь метода Хаусхолдера будет идти через &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt; умножений и &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt; сложений/вычитаний. &lt;br /&gt;
 &lt;br /&gt;
Поэтому в параллельном варианте, как и в последовательном, основную долю требуемого для выполнения алгоритма времени будут определять операции вида &amp;lt;math&amp;gt;a+bc&amp;lt;/math&amp;gt;.  &lt;br /&gt;
&lt;br /&gt;
При классификации по высоте ЯПФ, таким образом, метод Хаусхолдера относится к алгоритмам &#039;&#039;с квадратичной сложностью&#039;&#039;. При классификации по ширине ЯПФ его сложность будет также &#039;&#039;квадратичной&#039;&#039; (без расширения ряда ярусов, связанных с векторными операциями сложения, пришлось бы увеличить вдвое длину критического пути; при таком расширении сложность по ширине ЯПФ станет &#039;&#039;линейной&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
Надо сказать, что здесь в оценках речь идёт именно о классическом способе реализации метода Хаусхолдера. Даже использование схем сдваивания или последовательно-параллельных для вычисления скалярных произведений уменьшает критический путь с квадратичного до &#039;&#039;степени 3/2&#039;&#039; или &#039;&#039;линейно-логарифмического&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: плотная квадратная матрица &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; (элементы &amp;lt;math&amp;gt;a_{ij}&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;n^2+2n&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Соотношение последовательной и параллельной сложности, как хорошо видно, является &#039;&#039;линейным&#039;&#039;, что даёт определённый стимул для распараллеливания. Однако у наискорейшей ЯПФ ширина &#039;&#039;квадратична&#039;&#039;, что указывает на дисбаланс между загруженностями устройств при попытке её реально запрограммировать. Поэтому более практично даже при хорошей (быстрой) вычислительной сети оставить количество устройств (например, узлов кластера) &#039;&#039;линейным&#039;&#039; по размеру матрицы, что удвоит критический путь реализуемой ЯПФ. &lt;br /&gt;
&lt;br /&gt;
При этом вычислительная мощность алгоритма, как отношение числа операций к суммарному объему входных и выходных данных, &#039;&#039;линейна&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Алгоритм в рамках выбранной версии полностью детерминирован.&lt;br /&gt;
&lt;br /&gt;
Вычислительная погрешность в методе отражений (Хаусхолдера) растет &#039;&#039;линейно&#039;&#039;, как и в методе Гивенса (вращений).&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В варианте с кратчайшим критическим путём графа алгоритма (с использованием зависимости между обнуляемым вектором и направляющим вектором отражения) метод Хаусхолдера (отражений) приведения квадратной вещественной матрицы к двудиагональной форме на Фортране 77 можно записать так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;fortran&amp;quot;&amp;gt;&lt;br /&gt;
	DO  I = 1, N-1&lt;br /&gt;
            DO K = I, N&lt;br /&gt;
              SX(K)=A(N,I)*A(N,K)&lt;br /&gt;
            END DO&lt;br /&gt;
            DO J = N-1, I, -1&lt;br /&gt;
               DO K = I, N&lt;br /&gt;
                  SX(K)=SX(K)+A(J,I)*A(J,K)&lt;br /&gt;
               END DO&lt;br /&gt;
            END DO&lt;br /&gt;
&lt;br /&gt;
               ALPHA = SQRT (SX(I))&lt;br /&gt;
               IF (A(I,I).NE.0.) THEN&lt;br /&gt;
                  BETA = 1./ALPHA&lt;br /&gt;
                  DO J = I+1, N&lt;br /&gt;
                    A(J,I)=A(J,I)*BETA&lt;br /&gt;
                  END DO&lt;br /&gt;
                  SX(I) = A(I,I)*BETA+SIGN(1.,A(I,I))                     &lt;br /&gt;
                  A(I,I)=ALPHA&lt;br /&gt;
                  G=1./ABS(SX(I)) ! 1/gamma&lt;br /&gt;
                  DO K = I+1, N&lt;br /&gt;
                     SX(K)=SX(K)*BETA*G+SIGN(A(I,K),SX(I))&lt;br /&gt;
                     A(I,K) = A(I,K)+SX(K)*SX(I)&lt;br /&gt;
                     DO J = I+1, N&lt;br /&gt;
                       A (J,K) = A(J,K)-A(J,I)*SX(K)&lt;br /&gt;
                     END DO&lt;br /&gt;
                  END DO&lt;br /&gt;
               ELSE&lt;br /&gt;
                  IF (ALPHA.NE.0.) THEN&lt;br /&gt;
                     BETA = 1./ALPHA&lt;br /&gt;
                     DO J = I+1, N&lt;br /&gt;
                       A(J,I)=A(J,I)*BETA&lt;br /&gt;
                     END DO&lt;br /&gt;
                     SX(I) = -1. &lt;br /&gt;
                     A(I,I)=ALPHA&lt;br /&gt;
                     G=1.! 1/gamma&lt;br /&gt;
                     DO K = I+1, N&lt;br /&gt;
                        SX(K)=SX(K)*BETA*G+SIGN(A(I,K),SX(I))&lt;br /&gt;
                        A(I,K) = A(I,K)+SX(K)*SX(I)&lt;br /&gt;
                        DO J = I+1, N&lt;br /&gt;
                          A (J,K) = A(J,K)-A(J,I)*SX(K)&lt;br /&gt;
                        END DO&lt;br /&gt;
                     END DO&lt;br /&gt;
                  ELSE&lt;br /&gt;
                     SX(I)=1&lt;br /&gt;
                     G=2&lt;br /&gt;
                     DO K = I+1, N&lt;br /&gt;
                        SX(K)=2&lt;br /&gt;
                        A(I,K) = A(I,K)-SX(K)&lt;br /&gt;
                     END DO&lt;br /&gt;
                  END IF&lt;br /&gt;
               END IF               &lt;br /&gt;
&lt;br /&gt;
              IF (I.LT.N-1) THEN&lt;br /&gt;
&lt;br /&gt;
            DO K = I, N&lt;br /&gt;
              SL(K)=A(I,N)*A(K,N)&lt;br /&gt;
            END DO&lt;br /&gt;
            DO J = N-1, I+1, -1&lt;br /&gt;
               DO K = I, N&lt;br /&gt;
                  SL(K)=SL(K)+A(I,J)*A(K,J)&lt;br /&gt;
               END DO&lt;br /&gt;
            END DO&lt;br /&gt;
&lt;br /&gt;
               ALPHA = SQRT (SL(I))&lt;br /&gt;
               IF (A(I,I+1).NE.0.) THEN&lt;br /&gt;
                  BETA = 1./ALPHA&lt;br /&gt;
                  DO J = I+2, N&lt;br /&gt;
                    A(I,J)=A(I,J)*BETA&lt;br /&gt;
                  END DO&lt;br /&gt;
                  SL(I) = A(I,I+1)*BETA+SIGN(1.,A(I,I+1))                     &lt;br /&gt;
                  A(I,I+1)=ALPHA&lt;br /&gt;
                  G=1./ABS(SL(I)) ! 1/gamma&lt;br /&gt;
                  DO K = I+1, N&lt;br /&gt;
                     SL(K)=SL(K)*BETA*G+SIGN(A(K,I+1),SL(I))&lt;br /&gt;
                     A(K,I+1) = A(K,I+1)+SL(K)*SX(I)&lt;br /&gt;
                     DO J = I+2, N&lt;br /&gt;
                       A (K,J) = A(K,J)-A(I,J)*SL(K)&lt;br /&gt;
                     END DO&lt;br /&gt;
                  END DO&lt;br /&gt;
               ELSE&lt;br /&gt;
                  IF (ALPHA.NE.0.) THEN&lt;br /&gt;
                     BETA = 1./ALPHA&lt;br /&gt;
                     DO J = I+2, N&lt;br /&gt;
                       A(I,J)=A(I,J)*BETA&lt;br /&gt;
                     END DO&lt;br /&gt;
                     SL(I) = -1. &lt;br /&gt;
                     A(I,I+1)=ALPHA&lt;br /&gt;
                     G=1. ! 1/gamma&lt;br /&gt;
                     DO K = I+1, N&lt;br /&gt;
                        SL(K)=SL(K)*BETA*G+SIGN(A(K,I+1),SL(I))&lt;br /&gt;
                        A(K,I+1) = A(K,I+1)+SL(K)*SL(I)&lt;br /&gt;
                        DO J = I+2, N&lt;br /&gt;
                          A (K,J) = A(K,J)-A(I,J)*SL(K)&lt;br /&gt;
                        END DO&lt;br /&gt;
                     END DO&lt;br /&gt;
                  ELSE&lt;br /&gt;
                     SL(I)=1&lt;br /&gt;
                     G=2.&lt;br /&gt;
                     DO K = I+1, N&lt;br /&gt;
                        SL(K)=2.&lt;br /&gt;
                        A(K,I+1) = A(K,I+1)-SL(K)&lt;br /&gt;
                     END DO&lt;br /&gt;
                  END IF&lt;br /&gt;
               END IF   &lt;br /&gt;
&lt;br /&gt;
              END IF&lt;br /&gt;
	END DO&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В этом варианте D расположена в диагонали и верхней кодиагонали массива A, направляющие вектора матриц отражений размещены в поддиагональных элементах соответствующих столбцов или надддиагональных - строк, а их первые элементы - в элементах массивов SX и SL.&lt;br /&gt;
&lt;br /&gt;
Обычно же в последовательных версиях коэффициенты модификаций столбцов вычисляются целиком через скалярные произведения после вычислений параметров матрицы отражения. При этом схема чуть проще. Удлиняется критический путь графа, но для последовательных реализаций это неважно.&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
К сожалению, в графе, как видно по рисунку, в наличии пучки рассылок, в них неизбежно часть дуг остаются длинными, что отрицательно влияет на локальность вычислений по пространству.  &lt;br /&gt;
&lt;br /&gt;
==== Локальность реализации алгоритма ====&lt;br /&gt;
===== Структура обращений в память и качественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qdu_1.png|thumb|center|700px|Рисунок 2. Метод Хаусхолдера (отражений) приведения матрицы к двухдиагональной форме. Общий профиль обращений в память]]&lt;br /&gt;
&lt;br /&gt;
На рис. 2 представлен профиль обращений в память для реализации метода Хаусхолдера (отражений) приведения матрицы к двухдиагональной форме. Данный профиль обладает явной итерационной структурой, причем видно, что на каждой следующей итерации из рассмотрения отбрасывается небольшая часть элементов из начала массива данных. Также можно заметить, что основная часть данных используется достаточно равномерно в рамках одной итерации, однако в нижней и верхней частях итерации видны небольшие блоки с очень высокой локальностью обращений. Итерации явно устроены по одному принципу, поэтому стоит рассмотреть одну из них более детально.&lt;br /&gt;
&lt;br /&gt;
На рис. 3 показаны обращения в рамках первой итерации общего профиля (выделена на рис. 2 зеленым). Эти обращения можно условно разбить на несколько фрагментов. Фрагменты 1, 2 и 6 практически идентичны (различие заключается только в перестановке местами двух частей этих фрагментов), рассмотрим один из них.&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qdu_2.png|thumb|center|700px|Рисунок 3. Профиль обращений, одна итерация]]&lt;br /&gt;
&lt;br /&gt;
На рис. 4 показан отдельно фрагмент 6, выделенный на рис. 3. Теперь можно определить структуру каждой из двух частей более точно. Первая часть представляет собой последовательный перебор элементов в обратном порядке, причем к каждому элементу выполняется несколько обращений подряд. Во второй части также наблюдается последовательный перебор, однако с другими свойствами: элементы перебираются в прямом порядке, к каждому элементу выполняется только 1 обращение, однако данный перебор повторяется несколько раз подряд, причем каждый раз задействованы одни и те же элементы. Все это позволяет говорить о высокой пространственной локальности фрагмента (поскольку шаг по памяти каждый раз небольшой) и достаточно высокой временной локальности (поскольку данные часто используются повторно).&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qdu_3.png|thumb|center|500px|Рисунок 4. Профиль обращений, одна итерация, фрагмент 6]]&lt;br /&gt;
&lt;br /&gt;
Свойства локальности во фрагментах 3 и 5 можно оценить и без более детального рассмотрения. Данные в обоих фрагментах перебираются с достаточно большим шагом по памяти, при этом повторно они почти не используются, что говорит о низкой локальности в обоих случаях (хотя надо отметить, что локальность фрагмента 5 немного выше из-за более высокой пространственной локальности).&lt;br /&gt;
&lt;br /&gt;
Теперь перейдем к рассмотрению фрагмента 4, расположенного в центре рис. 3. Поскольку он обладает регулярной структурой, достаточно более детально изучить небольшой фрагмент (представлен на рис. 5, выделен на рис. 3 зеленым). Здесь можно увидеть, что первая часть фрагмента представляет собой практически стандартный последовательный перебор (с небольшими нечастыми сдвигами, которые не влияют на локальность). Такой набор обращений обладает высокой пространственной и низкой временной локальностью. Вторая часть данного фрагмента устроена несколько сложнее – она состоит из L-образных наборов обращений. Одна его область (часть 1 на рис. 5) по строению очень похожа на первую часть; вторая (часть 2 на рис. 5) представляет собой набор обращений к одному и тому же элементу, что достаточно сильно повышает локальность данного фрагмента. Из этого можно сделать вывод, что фрагмент 4 обладает высокой пространственной и средней временной локальностью.&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qdu_4.png|thumb|center|500px|Рисунок 5. Профиль обращений, одна итерация, часть фрагмент 4]]&lt;br /&gt;
&lt;br /&gt;
Суммируя выше сказанное, можно сказать, что фрагменты 1, 2 и 6 обладают высокой локальностью; фрагменты 3 и 5 – низкой локальностью; фрагмент 4 – высокой пространственной и средней временной локальностью. Можно предположить, что в итоге локальность общего профиля будет не очень высокой, в основном из-за более низкой временной локальности. Однако такое достаточно сложное строение общего профиля затрудняет оценку локальности общего профиля на основе визуального анализа.&lt;br /&gt;
&lt;br /&gt;
===== Количественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
Оценка выполняется на основе характеристики daps, которая оценивает число выполненных обращений (чтений и записей) в память в секунду. Данная характеристика является аналогом оценки flops применительно к работе с памятью и является в большей степени оценкой производительности взаимодействия с памятью, чем оценкой локальности. Однако она служит хорошим источником информации, в том числе для сравнения с результатами по следующей характеристике cvg.&lt;br /&gt;
&lt;br /&gt;
На рисунке 6 приведены значения daps для реализаций распространенных алгоритмов, отсортированные по возрастанию (чем больше daps, тем в общем случае выше производительность). Можно увидеть, что оценка производительности в данном случае не очень высока, что подтверждает сделанные ранее выводы. В частности, значение daps для этого примера показывает более низкую производительность по сравнению с реализациями других методов Хаусхолдера (householder_qr householder_qtq) и находится примерно на уровне оценки для реализации метода Холецкого.&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qdu_daps.png|thumb|center|700px|Рисунок 6. Сравнение значений оценки daps]]&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
==== Масштабируемость алгоритма ====&lt;br /&gt;
==== Масштабируемость реализации алгоритма ====&lt;br /&gt;
Проведём исследование масштабируемости вширь реализации согласно [[Scalability methodology|методике]]. Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов-2&amp;quot; [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Набор и границы значений изменяемых [[Глоссарий#Параметры запуска|параметров запуска]] реализации алгоритма: &lt;br /&gt;
&lt;br /&gt;
*	число процессоров [16:100] с шагом n^2;&lt;br /&gt;
*	размер матрицы [1000 : 16000] с шагом 1000.&lt;br /&gt;
&lt;br /&gt;
В результате проведённых экспериментов был получен следующий диапазон [[Глоссарий#Эффективность реализации|эффективности реализации]] алгоритма:&lt;br /&gt;
&lt;br /&gt;
*	минимальная эффективность реализации 5.033e-05%;&lt;br /&gt;
*	максимальная эффективность реализации 0.0135%.&lt;br /&gt;
&lt;br /&gt;
На следующих рисунках приведены графики [[Глоссарий#Производительность|производительности]] и эффективности выбранной реализации алгоритма в процедуре PDGEHRD из Scalapack в зависимости от изменяемых параметров запуска.&lt;br /&gt;
&lt;br /&gt;
[[file:PDGEHRD perf2.png|thumb|center|700px|Рисунок 7. Параллельная реализация метода Хаусхолдера для приведения матриц к двухдиагональному виду. Изменение производительности в зависимости от числа процессоров и размера матрицы.]]&lt;br /&gt;
[[file:PDGEHRD eff2.png|thumb|center|700px|Рисунок 8. Параллельная реализация метода Хаусхолдера для приведения матриц к двухдиагональному виду. Изменение эффективности в зависимости от числа процессоров и размера матрицы.]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
Для проведения экспериментов использовалась реализация Метод Хаусхолдера, представленная в пакете SCALAPACK библиотеки Intel MKL (метод pdgehrd).  Все результаты получены на суперкомпьютере «Ломоносов-2». Использовались процессоры Intel Xeon E5-2697v3, сеть FDR Infiniband а также компилятор Intel с опциями, рекомнедованными Intel MKL Advisor. &lt;br /&gt;
На рисунках показана эффективность реализации метода Хаусхолдера (отражений) приведения матрицы к двухдиагональной форме для размерности матрицы 16000, запуск проводился на 100 ядрах.&lt;br /&gt;
&lt;br /&gt;
[[file:PDGEHRD CPU.png|thumb|center|700px|Рисунок 9. График загрузки CPU при выполнении метода Хаусхолдера (отражений) приведения матрицы к двухдиагональной форме]]&lt;br /&gt;
&lt;br /&gt;
На графике загрузки процессора видно, что почти все время работы программы уровень загрузки составляет около 90%. Это хорошая картина для программ, запущенных c использованием технологии Hyper Threading т.е. всех виртуальных ядер доступных для данной архитектуры. &lt;br /&gt;
&lt;br /&gt;
[[file:PDGEHRD Loadavg.png|thumb|center|700px|Рисунок 10. График числа процессов, ожидающих вхождения в стадию счета (Loadavg), при работе метода Хаусхолдера (отражений) приведения матрицы к двухдиагональной форме]]&lt;br /&gt;
&lt;br /&gt;
На графике числа процессов, ожидающих вхождения в стадию счета (Loadavg), видно, что на протяжении всей работы программы значение этого параметра постоянно и приблизительно равняется 140. Это свидетельствует о стабильной работе программы на протяжении всего выполнения со 140 нитями на одном узле или около 10 нитей на 1 физическое ядро на каждом узле. Это указывает на статичную загрузку аппаратных ресурсов процессами, однако их число для узла слишком велико, что с одной стороны может указывать на не очень рациональные опции компиляции, который могут приводить к снижению производительности из-за накладных расходов на переключение контекста между нитями.&lt;br /&gt;
&lt;br /&gt;
 [[file:PDGEHRD L1.png|thumb|center|700px|Рисунок 11. График кэш-промахов L1 в секунду при работе метода Хаусхолдера (отражений) приведения матрицы к двухдиагональной формеy]]&lt;br /&gt;
 &lt;br /&gt;
На графике кэш-промахов первого уровня видно, что число промахов достаточно низкое и находится на уровне 4 млн/сек в среднем по всем узлам. Интересен факт снижения числа промахов к концу итерации, хотя снижение и не очень значительное, но вполне заметное.&lt;br /&gt;
&lt;br /&gt;
 [[file:PDGEHRD L2.png|thumb|center|700px|Рисунок 12. График кэш-промахов L1 в секунду при работе метода Хаусхолдера (отражений) приведения матрицы к двухдиагональной форме]]&lt;br /&gt;
 &lt;br /&gt;
На графике кэш-промахов второго уровня видно, что число промахов достаточно тоже низкое и находится на уровне 1 млн/сек в среднем по всем узлам. На графике промахов второго уровня факт снижения числа промахов к концу итерации проявляется более явно и снижение и более значительное и заметное (с 1.4 млн до 0.8 млн.).&lt;br /&gt;
&lt;br /&gt;
 [[file:PDGEHRD L3.png|thumb|center|700px|Рисунок 13. График кэш-промахов L3 в секунду при работе метода Хаусхолдера (отражений) приведения матрицы к двухдиагональной форме]]&lt;br /&gt;
&lt;br /&gt;
На графике кэш-промахов последнего уровня видно, что число промахов достаточно небольшое и быстро убывает с уровня 127 тыс/сек до 0 в среднем по всем узлам. Это указывает на то, что задача достаточно хорошо укладывается в кэш-память третьего уровня, особенно к концу итерации.&lt;br /&gt;
[[file:PDGEHRD inf data.png|thumb|center|700px|Рисунок 13. График скорости передачи по сети Infiniband в байт/сек при работе метода Хаусхолдера (отражений) приведения матрицы к двухдиагональной форме]]&lt;br /&gt;
&lt;br /&gt;
На графике скорости передачи данных по сети Infiniband наблюдается достаточно низкая интенсивность использования коммуникационной сети на каждой итерации. Причем к концу каждой итерации интенсивность передачи данных сильно снижается. Это указывает на бОльшую необходимость в обмене данными между процессами в начале итерации и большую локальность вычислений к концу итерации.&lt;br /&gt;
 [[file:PDGEHRD inf pckts.png|thumb|center|700px|Рисунок 14. График скорости передачи по сети Infiniband в пакетах/сек при работе метода Хаусхолдера (отражений) приведения матрицы к двухдиагональной форме]]&lt;br /&gt;
&lt;br /&gt;
На графике скорости передачи данных в пакетах в секунду наблюдается большая «кучность» показаний максимального минимального и среднего значений в сравнении с графиком скорости передачи в байт/сек. Это говорит о том, что, вероятно, процессы обмениваются сообщениями различной длины, что указывает на неравномерное распределение данных. Также наблюдается рост интенсивности использования сети к концу каждой итерации. Особенно это различие заметно к концу итерации.&lt;br /&gt;
 &lt;br /&gt;
В целом, по данным системного мониторинга работы программы можно сделать вывод о том, что программа работала стабильно и эффективно использовала память. Использование памяти и коммуникационной среды не достаточно интенсивное, что может стать указывать на факторы снижения эффективности и увеличении производительности при существенном росте размера задачи.&lt;br /&gt;
Для существующей в SCALAPACK параллельной реализаций характерно сильное снижение числа кэш-промахов и обменов к концу итерации&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
В сравнении с методом Гивенса, который имеет естественное двумерное блочное разбиение на основе точечного метода, метод Хаусхолдера из-за худших характеристик локальности (наличие пучков рассылок) и меньшего количества независимых обобщённых развёрток графа не так хорош для реализаций на системах с распределённой памятью, как для систем с общей памятью. Поэтому на массово параллельных системах с распределённой памятью следует применять метод Хаусхолдера (если уж именно его нужно реализовать) не в точечной версии, а в разрабатываемых исследователями блочных вариантах. Следует отметить, что эти варианты - не блочная нарезка описанного метода, а самостоятельные методы. Особенно их применение рекомендуется в случаях с большой разрежённостью матрицы.&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Большинство пакетов от LINPACKа и LAPACKa до SCALAPACKa используют для приведения матриц к двудиагональному виду именно метод Хаусхолдера, правда, в различных модификациях (обычно с использованием BLAS). Существует большая подборка исследовательских работ по блочным версиям.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Статьи в работе]]&lt;br /&gt;
[[Категория:Разложения матриц]]&lt;/div&gt;</summary>
		<author><name>Teplov</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A0%D0%B0%D0%B7%D0%BB%D0%BE%D0%B6%D0%B5%D0%BD%D0%B8%D0%B5_%D0%A5%D0%BE%D0%BB%D0%B5%D1%86%D0%BA%D0%BE%D0%B3%D0%BE_(%D0%BC%D0%B5%D1%82%D0%BE%D0%B4_%D0%BA%D0%B2%D0%B0%D0%B4%D1%80%D0%B0%D1%82%D0%BD%D0%BE%D0%B3%D0%BE_%D0%BA%D0%BE%D1%80%D0%BD%D1%8F)&amp;diff=21546</id>
		<title>Разложение Холецкого (метод квадратного корня)</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A0%D0%B0%D0%B7%D0%BB%D0%BE%D0%B6%D0%B5%D0%BD%D0%B8%D0%B5_%D0%A5%D0%BE%D0%BB%D0%B5%D1%86%D0%BA%D0%BE%D0%B3%D0%BE_(%D0%BC%D0%B5%D1%82%D0%BE%D0%B4_%D0%BA%D0%B2%D0%B0%D0%B4%D1%80%D0%B0%D1%82%D0%BD%D0%BE%D0%B3%D0%BE_%D0%BA%D0%BE%D1%80%D0%BD%D1%8F)&amp;diff=21546"/>
		<updated>2017-01-17T13:43:11Z</updated>

		<summary type="html">&lt;p&gt;Teplov: /* Динамические характеристики и эффективность реализации алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Разложение Холецкого&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(n^3)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;\frac{n (n + 1)}{2}&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;\frac{n (n + 1)}{2}&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Основные авторы описания: [[Участник:Frolov|А.В.Фролов]], [[Участник:VadimVV|Вад.В.Воеводин]] ([[#Описание локальности данных и вычислений|раздел 2.2]]), [[Участник:Teplov|А.М.Теплов]] (разделы [[#Масштабируемость алгоритма и его реализации|2.4]], [[#Динамические характеристики и эффективность реализации алгоритма|2.5]])&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Разложение Холецкого&#039;&#039;&#039; впервые предложено французским офицером и математиком Андре-Луи Холецким в конце Первой Мировой войны, незадолго до его гибели в бою в августе 1918 г. Идея этого разложения была опубликована в 1924 г. его сослуживцем&amp;lt;ref&amp;gt;Commandant Benoit, Note sur une méthode de résolution des équations normales provenant de l&#039;application de la méthode des moindres carrés à un système d&#039;équations linéaires en nombre inférieur à celui des inconnues (Procédé du Commandant Cholesky), Bulletin Géodésique 2 (1924), 67-77.&amp;lt;/ref&amp;gt;.  Потом оно было использовано поляком Т. Банашевичем&amp;lt;ref&amp;gt;Banachiewicz T. Principles d&#039;une nouvelle technique de la méthode des moindres carrês. Bull. Intern. Acad. Polon. Sci. A., 1938,  134-135.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Banachiewicz T. Méthode de résoltution numérique des équations linéaires, du calcul des déterminants et des inverses et de réduction des formes quardatiques. Bull. Intern. Acad. Polon. Sci. A., 1938, 393-401.&amp;lt;/ref&amp;gt; в 1938 г. В советской математической литературе называется также методом квадратного корня&amp;lt;ref&amp;gt;Воеводин В.В. Вычислительные основы линейной алгебры. М.: Наука, 1977.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Воеводин В.В., Кузнецов Ю.А. Матрицы и вычисления. М.: Наука, 1984.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Фаддеев Д.К., Фаддева В.Н. Вычислительные основы линейной алгебры. М.-Л.: Физматгиз, 1963.&amp;lt;/ref&amp;gt;; название связано с характерными операциями, отсутствующими в родственном разложении Гаусса. &lt;br /&gt;
&lt;br /&gt;
Первоначально разложение Холецкого использовалось исключительно для плотных симметричных положительно определенных матриц. В настоящее время его использование гораздо шире. Оно может быть применено также, например, к эрмитовым матрицам. Для  повышения производительности вычислений часто применяется блочная версия разложения.&lt;br /&gt;
&lt;br /&gt;
Для разреженных матриц разложение Холецкого также широко применяется в качестве основного этапа прямого метода решения линейных систем. В этом случае используют специальные упорядочивания для уменьшения ширины профиля исключения, а следовательно и уменьшения количества арифметических операций. Другие упорядочивания используются для выделения независимых блоков вычислений при работе на системах с параллельной организацией.&lt;br /&gt;
&lt;br /&gt;
Варианты разложения Холецкого нашли успешные применения и в итерационных методах для построения переобусловливателей разреженных симметричных положительно определенных матриц. В неполном треугольном разложении («по позициям») элементы переобусловливателя вычисляются только в заранее заданных позициях, например, в позициях ненулевых элементов исходной матрицы (так называемое разложение IC0). Для получения же более точного разложения применяется приближение, в котором фильтрация малых элементов производится «по значениям». В зависимости от используемого порога фильтрации можно получить более точное, но и более заполненное разложение. Существует и алгоритм разложения второго порядка точности&amp;lt;ref&amp;gt;Kaporin I.E. High quality preconditioning of a general symmetric positive definite matrix based on its UTU + UTR + RTU-decomposition. Numer. Lin. Algebra Appl. (1998) Vol. 5, No. 6, 483-509.&amp;lt;/ref&amp;gt;. В нём при таком же заполнении множителей разложения удается улучшить точность. Для такого разложения в параллельном режиме используется специальный вариант аддитивного переобуславливания на основе разложения второго порядка&amp;lt;ref&amp;gt;Капорин И.Е., Коньшин И.Н. Параллельное решение симметричных положительно-определенных систем на основе перекрывающегося разбиения на блоки. Ж. вычисл. матем. и матем. физ.,  2001, Т, 41, N. 4, C. 515–528.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
На этой странице представлено исходное разложение Холецкого с новых позиций нашего суперкомпьютерного века. Приведено описание конкретной версии разложения Холецкого — для плотных вещественных симметричных положительно определённых матриц, но структура для ряда других версий, например, для комплексного случая, почти такая же, различия состоят в замене большинства вещественных операций на комплексные. Список остальных основных вариантов разложения можно посмотреть на странице [[Метод Холецкого (нахождение симметричного треугольного разложения)]].&lt;br /&gt;
&lt;br /&gt;
Используется для разложения положительно определённых эрмитовых (&#039;&#039;в вещественном случае - симметрических&#039;&#039;) матриц в виде &amp;lt;math&amp;gt;A = L L^*&amp;lt;/math&amp;gt;,  &amp;lt;math&amp;gt;L&amp;lt;/math&amp;gt; — нижняя треугольная матрица, &lt;br /&gt;
{{Шаблон:LCommon}}&lt;br /&gt;
или в виде &amp;lt;math&amp;gt;A = U^* U&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; — верхняя треугольная матрица,&lt;br /&gt;
{{Шаблон:UCommon}}&lt;br /&gt;
Эти разложения совершенно эквивалентны друг другу по вычислениям и различаются только способом представления данных). Он заключается в реализации формул для элементов матрицы &amp;lt;math&amp;gt;L&amp;lt;/math&amp;gt;, получающихся из вышеприведённого равенства единственным образом. Получило широкое распространение благодаря следующим особенностям.&lt;br /&gt;
&lt;br /&gt;
==== Симметричность и положительная определённость матрицы ====&lt;br /&gt;
&lt;br /&gt;
Симметричность матрицы позволяет хранить и вычислять только чуть больше половины её элементов, что почти вдвое экономит как необходимые для вычислений объёмы памяти, так и количество операций в сравнении с, например, разложением по методу Гаусса. При этом альтернативное (без вычисления квадратных корней) LU-разложение, использующее симметрию матрицы, всё же несколько быстрее метода Холецкого (не использует извлечение квадратных корней), но требует хранения всей матрицы. &lt;br /&gt;
Благодаря тому, что разлагаемая матрица не только симметрична, но и положительно определена, её LU-разложения, в том числе и разложение методом Холецкого, имеют наименьшее &#039;&#039;[[Глоссарий#Эквивалентное возмущение|эквивалентное возмущение]]&#039;&#039; из всех известных разложений матриц.&lt;br /&gt;
&lt;br /&gt;
==== Режим накопления ====&lt;br /&gt;
&lt;br /&gt;
Алгоритм позволяет использовать так называемый &#039;&#039;режим накопления&#039;&#039;, обусловленный тем, что значительную часть вычислений составляют &#039;&#039;вычисления скалярных произведений&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Исходные данные: положительно определённая симметрическая матрица &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; (элементы &amp;lt;math&amp;gt;a_{ij}&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Вычисляемые данные: нижняя треугольная матрица &amp;lt;math&amp;gt;L&amp;lt;/math&amp;gt; (элементы &amp;lt;math&amp;gt;l_{ij}&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Формулы метода:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
\begin{align}&lt;br /&gt;
l_{11} &amp;amp; = \sqrt{a_{11}}, \\&lt;br /&gt;
l_{j1} &amp;amp; = \frac{a_{j1}}{l_{11}}, \quad j \in [2, n], \\&lt;br /&gt;
l_{ii} &amp;amp; = \sqrt{a_{ii} - \sum_{p = 1}^{i - 1} l_{ip}^2}, \quad i \in [2, n], \\&lt;br /&gt;
l_{ji} &amp;amp; = \left (a_{ji} - \sum_{p = 1}^{i - 1} l_{ip} l_{jp} \right ) / l_{ii}, \quad i \in [2, n - 1], j \in [i + 1, n].&lt;br /&gt;
\end{align}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Существует также блочная версия метода, однако в данном описании разобран только точечный метод.&lt;br /&gt;
&lt;br /&gt;
В ряде реализаций деление на диагональный элемент выполняется в два этапа: вычисление &amp;lt;math&amp;gt;\frac{1}{l_{ii}}&amp;lt;/math&amp;gt; и затем умножение на него всех (видоизменённых) &amp;lt;math&amp;gt;a_{ji}&amp;lt;/math&amp;gt; . Здесь мы этот вариант алгоритма не рассматриваем. Заметим только, что он имеет худшие параллельные характеристики, чем представленный.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Вычислительное ядро последовательной версии метода Холецкого можно составить из множественных (всего их &amp;lt;math&amp;gt;\frac{n (n - 1)}{2}&amp;lt;/math&amp;gt;) вычислений скалярных произведений строк матрицы:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;\sum_{p = 1}^{i - 1} l_{ip} l_{jp}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
в режиме накопления или без него, в зависимости от требований задачи. Во многих последовательных реализациях упомянутый способ представления не используется. Дело в том, что в них вычисление сумм типа&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;a_{ji} - \sum_{p = 1}^{i - 1} l_{ip} l_{jp}&amp;lt;/math&amp;gt;&amp;lt;nowiki/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
в которых и встречаются скалярные произведения, ведутся не в порядке «вычислили скалярное произведение, а потом вычли его из элемента», а путём вычитания из элемента покомпонентных произведений, являющихся частями скалярных произведений. Поэтому следует считать вычислительным ядром метода не вычисления скалярных произведений, а вычисления выражений&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;a_{ji} - \sum_{p = 1}^{i - 1} l_{ip} l_{jp}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
в &#039;&#039;режиме накопления&#039;&#039; или без него.&lt;br /&gt;
&lt;br /&gt;
Тем не менее, в популярных зарубежных реализациях точечного метода Холецкого, в частности, в библиотеках LINPACK и LAPACK, основанных на BLAS, используются именно вычисления скалярных произведений в виде вызова соответствующих подпрограмм BLAS (конкретно — функции SDOT). На последовательном уровне это влечёт за собой дополнительную операцию суммирования на каждый из &amp;lt;math&amp;gt;\frac{n (n + 1)}{2}&amp;lt;/math&amp;gt; вычисляемый элемент матрицы &amp;lt;math&amp;gt;L&amp;lt;/math&amp;gt; и некоторое замедление работы программы (о других следствиях рассказано ниже в разделе «[[#Существующие реализации алгоритма|Существующие реализации алгоритма]]»). Поэтому в данных вариантах ядром метода Холецкого будут вычисления этих скалярных произведений.&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Как записано и в [[#Вычислительное ядро алгоритма|описании ядра алгоритма]], основную часть метода составляют множественные (всего &amp;lt;math&amp;gt;\frac{n (n - 1)}{2}&amp;lt;/math&amp;gt;) вычисления сумм&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;a_{ji}-\sum_{p=1}^{i-1}l_{ip} l_{jp}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
в режиме накопления или без него.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Последовательность исполнения метода следующая:&lt;br /&gt;
&lt;br /&gt;
1. &amp;lt;math&amp;gt;l_{11}= \sqrt{a_{11}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2. &amp;lt;math&amp;gt;l_{j1}= \frac{a_{j1}}{l_{11}}&amp;lt;/math&amp;gt; (при &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; от &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; до &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Далее для всех &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; от &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; до &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; по нарастанию выполняются &lt;br /&gt;
&lt;br /&gt;
3. &amp;lt;math&amp;gt;l_{ii} = \sqrt{a_{ii} - \sum_{p = 1}^{i - 1} l_{ip}^2}&amp;lt;/math&amp;gt; и&lt;br /&gt;
&lt;br /&gt;
4. (кроме &amp;lt;math&amp;gt;i = n&amp;lt;/math&amp;gt;): &amp;lt;nowiki/&amp;gt;&amp;lt;math&amp;gt;l_{ji} = \left (a_{ji} - \sum_{p = 1}^{i - 1} l_{ip} l_{jp} \right ) / l_{ii}&amp;lt;/math&amp;gt; (для всех &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; от &amp;lt;math&amp;gt;i + 1&amp;lt;/math&amp;gt; до &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
После этого (если &amp;lt;math&amp;gt;i &amp;lt; n&amp;lt;/math&amp;gt;) происходит переход к шагу 3 с бо́льшим &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Особо отметим, что вычисления сумм вида &amp;lt;math&amp;gt;a_{ji} - \sum_{p = 1}^{i - 1} l_{ip} l_{jp}&amp;lt;/math&amp;gt; в обеих формулах производят в режиме накопления вычитанием из &amp;lt;math&amp;gt;a_{ji}&amp;lt;/math&amp;gt; произведений &amp;lt;math&amp;gt;l_{ip} l_{jp}&amp;lt;/math&amp;gt; для &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; от &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; до &amp;lt;math&amp;gt;i - 1&amp;lt;/math&amp;gt;, c нарастанием &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Для разложения матрицы порядка n методом Холецкого в последовательном (наиболее быстром) варианте требуется:&lt;br /&gt;
	&lt;br /&gt;
* &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вычислений квадратного корня,&lt;br /&gt;
* &amp;lt;math&amp;gt;\frac{n(n-1)}{2}&amp;lt;/math&amp;gt; делений,&lt;br /&gt;
* &amp;lt;math&amp;gt;\frac{n^3-n}{6}&amp;lt;/math&amp;gt; сложений (вычитаний),&lt;br /&gt;
* &amp;lt;math&amp;gt;\frac{n^3-n}{6}&amp;lt;/math&amp;gt; умножений.&lt;br /&gt;
&lt;br /&gt;
Умножения и сложения (вычитания) составляют &#039;&#039;основную часть алгоритма&#039;&#039;.&lt;br /&gt;
 &lt;br /&gt;
При этом использование режима накопления требует совершения умножений и вычитаний в режиме двойной точности (или использования функции вроде DPROD в Фортране), что ещё больше увеличивает долю умножений и сложений/вычитаний во времени, требуемом для выполнения метода Холецкого.&lt;br /&gt;
&lt;br /&gt;
При классификации по последовательной сложности, таким образом, метод Холецкого относится к алгоритмам &#039;&#039;с кубической сложностью&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
Опишем [[глоссарий#Граф алгоритма|граф алгоритма]]&amp;lt;ref&amp;gt;Воеводин В.В.  Математические основы параллельных вычислений// М.: Изд. Моск. ун-та, 1991. 345 с.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Воеводин В.В., Воеводин Вл.В. Параллельные вычисления. – СПб.: БХВ - Петербург, 2002. – 608 с.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Фролов А.В.. Принципы построения и описание языка Сигма. Препринт ОВМ АН N 236. М.: ОВМ АН СССР, 1989.&amp;lt;/ref&amp;gt; как аналитически, так и в виде рисунка. &lt;br /&gt;
&lt;br /&gt;
Граф алгоритма состоит из трёх групп вершин, расположенных в целочисленных узлах трёх областей разной размерности.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первая&#039;&#039;&#039; группа вершин расположена в одномерной области, соответствующая ей операция вычисляет функцию SQRT. &lt;br /&gt;
Единственная координата каждой из вершин &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; меняется в диапазоне от &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; до &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;, принимая все целочисленные значения.&lt;br /&gt;
&lt;br /&gt;
Аргумент этой функции &lt;br /&gt;
	&lt;br /&gt;
* при &amp;lt;math&amp;gt;i = 1&amp;lt;/math&amp;gt; — элемент &#039;&#039;входных данных&#039;&#039;, а именно  &amp;lt;math&amp;gt;a_{11}&amp;lt;/math&amp;gt;; &lt;br /&gt;
* при &amp;lt;math&amp;gt;i &amp;gt; 1&amp;lt;/math&amp;gt; — результат срабатывания операции, соответствующей вершине из третьей группы, с координатами &amp;lt;math&amp;gt;i - 1&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;i - 1&amp;lt;/math&amp;gt;.&lt;br /&gt;
Результат срабатывания операции является &#039;&#039;выходным данным&#039;&#039; &amp;lt;math&amp;gt;l_{ii}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вторая&#039;&#039;&#039; группа вершин расположена в двумерной области, соответствующая ей операция &amp;lt;math&amp;gt;a / b&amp;lt;/math&amp;gt;. &lt;br /&gt;
Естественно введённые координаты области таковы: &lt;br /&gt;
* &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; — меняется в диапазоне от &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; до &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt;, принимая все целочисленные значения;&lt;br /&gt;
* &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; — меняется в диапазоне от &amp;lt;math&amp;gt;i+1&amp;lt;/math&amp;gt; до &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;, принимая все целочисленные значения.&lt;br /&gt;
&lt;br /&gt;
Аргументы операции следующие:&lt;br /&gt;
*&amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt;: &lt;br /&gt;
** при &amp;lt;math&amp;gt;i = 1&amp;lt;/math&amp;gt; — элементы &#039;&#039;входных данных&#039;&#039;, а именно &amp;lt;math&amp;gt;a_{j1}&amp;lt;/math&amp;gt;;&lt;br /&gt;
** при &amp;lt;math&amp;gt;i &amp;gt; 1&amp;lt;/math&amp;gt; — результат срабатывания операции, соответствующей вершине из третьей группы, с координатами &amp;lt;math&amp;gt;i - 1, j, i - 1&amp;lt;/math&amp;gt;;&lt;br /&gt;
* &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; — результат срабатывания операции, соответствующей вершине из первой группы, с координатой &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Результат срабатывания операции является &#039;&#039;выходным данным&#039;&#039; &amp;lt;math&amp;gt;l_{ji}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Третья&#039;&#039;&#039; группа вершин расположена в трёхмерной области, соответствующая ей операция  &amp;lt;math&amp;gt;a - b * c&amp;lt;/math&amp;gt;. &lt;br /&gt;
Естественно введённые координаты области таковы: &lt;br /&gt;
* &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; — меняется в диапазоне от &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt; до &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;, принимая все целочисленные значения;&lt;br /&gt;
* &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; — меняется в диапазоне от &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; до &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;, принимая все целочисленные значения;&lt;br /&gt;
* &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; — меняется в диапазоне  от &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; до &amp;lt;math&amp;gt;i - 1&amp;lt;/math&amp;gt;, принимая все целочисленные значения.&lt;br /&gt;
&lt;br /&gt;
Аргументы операции следующие:&lt;br /&gt;
*&amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt;:&lt;br /&gt;
** при &amp;lt;math&amp;gt;p = 1&amp;lt;/math&amp;gt; элемент входных данных &amp;lt;math&amp;gt;a_{ji}&amp;lt;/math&amp;gt;; &lt;br /&gt;
** при &amp;lt;math&amp;gt;p &amp;gt; 1&amp;lt;/math&amp;gt; — результат срабатывания операции, соответствующей вершине из третьей группы, с координатами &amp;lt;math&amp;gt;i, j, p - 1&amp;lt;/math&amp;gt;;&lt;br /&gt;
*&amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; — результат срабатывания операции, соответствующей вершине из второй группы, с координатами &amp;lt;math&amp;gt;p, i&amp;lt;/math&amp;gt;;&lt;br /&gt;
*&amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; — результат срабатывания операции, соответствующей вершине из второй группы, с координатами &amp;lt;math&amp;gt;p, j&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
Результат срабатывания операции является &#039;&#039;промежуточным данным&#039;&#039; алгоритма.&lt;br /&gt;
&lt;br /&gt;
Описанный граф можно посмотреть на рис.1 и рис.2, выполненных для случая &amp;lt;math&amp;gt;n = 4&amp;lt;/math&amp;gt;. Здесь вершины первой группы обозначены жёлтым цветом и буквосочетанием sq, вершины второй — зелёным цветом и знаком деления, третьей — красным цветом и буквой f. Вершины, соответствующие операциям, производящим выходные данные алгоритма, выполнены более крупно. Дублирующие друг друга дуги даны как одна. На рис.1 показан граф алгоритма согласно классическому определению , на рис.2 к графу алгоритма добавлены вершины , соответствующие входным (обозначены синим цветом) и выходным (обозначены розовым цветом) данным.&lt;br /&gt;
&lt;br /&gt;
[[file:Cholesky full.png|thumb|center|1400px|Рисунок 1. Граф алгоритма без отображения входных и выходных данных. SQ - вычисление квадратного корня, F - операция a-bc, Div - деление.]]&lt;br /&gt;
[[file:Cholesky full_in_out.png|thumb|center|1400px|Рисунок 2. Граф алгоритма с отображением входных и выходных данных. SQ - вычисление квадратного корня, F - операция a-bc, Div - деление, In - входные данные, Out - результаты.]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
{{#widget:Iframe&lt;br /&gt;
|url=https://algowiki-project.org/html/Algo_viewer_json/cholesky/Algo_viewer_example_2.html&lt;br /&gt;
|width=800&lt;br /&gt;
|height=600&lt;br /&gt;
|border=1&lt;br /&gt;
}}&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
Интерактивное изображение графа алгоритма без входных и выходных данных для n = 4.&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Для разложения матрицы порядка &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; методом Холецкого в параллельном варианте требуется последовательно выполнить следующие ярусы:&lt;br /&gt;
* &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; ярусов с вычислением квадратного корня (единичные вычисления в каждом из ярусов),&lt;br /&gt;
* &amp;lt;math&amp;gt;n - 1&amp;lt;/math&amp;gt; ярус делений (в каждом из ярусов линейное количество делений, в зависимости от яруса — от &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; до &amp;lt;math&amp;gt;n - 1&amp;lt;/math&amp;gt;),&lt;br /&gt;
* по &amp;lt;math&amp;gt;n - 1&amp;lt;/math&amp;gt; ярусов умножений и сложений/вычитаний (в каждом из ярусов — квадратичное количество операций, от &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; до &amp;lt;math&amp;gt;\frac{n^2 - n}{2}&amp;lt;/math&amp;gt;.&lt;br /&gt;
 &lt;br /&gt;
Таким образом, в параллельном варианте, в отличие от последовательного, вычисления квадратных корней и делений будут определять довольно значительную долю требуемого времени. При реализации на конкретных архитектурах наличие в отдельных ярусах [[глоссарий#Ярусно-параллельная форма графа алгоритма|ЯПФ]] отдельных вычислений квадратных корней может породить и другие проблемы. Например, при реализации на ПЛИСах остальные вычисления (деления и тем более умножения и сложения/вычитания) могут быть конвейеризованы, что даёт экономию и по ресурсам на программируемых платах; вычисления же квадратных корней из-за их изолированности приведут к занятию ресурсов на платах, которые будут простаивать большую часть времени. Таким образом, общая экономия в 2 раза, из-за которой метод Холецкого предпочитают в случае симметричных задач тому же методу Гаусса, в параллельном случае уже имеет место вовсе не по всем ресурсам, и главное - не по требуемому времени.&lt;br /&gt;
&lt;br /&gt;
При этом использование режима накопления требует совершения умножений и вычитаний в режиме двойной точности, а в параллельном варианте это означает, что практически все промежуточные вычисления для выполнения метода Холецкого в режиме накопления должны быть двойной точности. В отличие от последовательного варианта это означает увеличение требуемой памяти почти в 2 раза.&lt;br /&gt;
&lt;br /&gt;
При классификации по высоте ЯПФ, таким образом, метод Холецкого относится к алгоритмам со сложностью &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;. При классификации по ширине ЯПФ его сложность будет &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: плотная матрица &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; (элементы &amp;lt;math&amp;gt;a_{ij}&amp;lt;/math&amp;gt;).&lt;br /&gt;
Дополнительные ограничения:&lt;br /&gt;
* &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; – симметрическая матрица, т. е. &amp;lt;math&amp;gt;a_{ij}= a_{ji}, i, j = 1, \ldots, n&amp;lt;/math&amp;gt;.&lt;br /&gt;
* &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; – положительно определённая матрица, т. е. для любых ненулевых векторов &amp;lt;math&amp;gt;\vec{x}&amp;lt;/math&amp;gt; выполняется &amp;lt;math&amp;gt;\vec{x}^T A \vec{x} &amp;gt; 0&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;\frac{n (n + 1)}{2}&amp;lt;/math&amp;gt; (в силу симметричности достаточно хранить только диагональ и над/поддиагональные элементы). В разных реализациях эта экономия хранения может быть выполнена разным образом. Например, в библиотеке, реализованной в НИВЦ МГУ, матрица A хранилась в одномерном массиве длины &amp;lt;math&amp;gt;\frac{n (n + 1)}{2}&amp;lt;/math&amp;gt; по строкам своего нижнего треугольника. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: нижняя треугольная матрица &amp;lt;math&amp;gt;L&amp;lt;/math&amp;gt; (элементы &amp;lt;math&amp;gt;l_{ij}&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;\frac{n (n + 1)}{2}&amp;lt;/math&amp;gt;  (в силу треугольности достаточно хранить только ненулевые элементы). В разных реализациях эта экономия хранения может быть выполнена разным образом. Например, в той же  библиотеке, созданной в НИВЦ МГУ, матрица &amp;lt;math&amp;gt;L&amp;lt;/math&amp;gt; хранилась в одномерном массиве длины &amp;lt;math&amp;gt;\frac{n (n + 1)}{2}&amp;lt;/math&amp;gt; по строкам своей нижней части.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Соотношение последовательной и параллельной сложности в случае неограниченных ресурсов, как хорошо видно, является &#039;&#039;квадратичным&#039;&#039; (отношение кубической к линейной). &lt;br /&gt;
&lt;br /&gt;
При этом вычислительная мощность алгоритма, как отношение числа операций к суммарному объему входных и выходных данных – всего лишь &#039;&#039;линейна&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
При этом алгоритм почти полностью детерминирован, это гарантируется теоремой о единственности разложения. Использование другого порядка выполнения ассоциативных операций может привести к накоплению ошибок округления, однако это влияние в используемых вариантах алгоритма не так велико, как, скажем, отказ от использования режима накопления.&lt;br /&gt;
&lt;br /&gt;
Дуги информационного графа, исходящие из вершин, соответствующих операциям квадратного корня и деления, образуют пучки т. н. рассылок линейной мощности (то есть степень исхода этих вершин и мощность работы с этими данными — линейная функция от порядка матрицы и координат этих вершин). При этом естественно наличие в этих пучках «длинных» дуг. Остальные дуги локальны. &lt;br /&gt;
&lt;br /&gt;
Наиболее известной является компактная укладка графа — его проекция на треугольник матрицы, который перевычисляется укладываемыми операциями. При этом «длинные» дуги можно убрать, заменив более дальнюю пересылку комбинацией нескольких ближних (к соседям).&lt;br /&gt;
&lt;br /&gt;
[[Глоссарий#Эквивалентное возмущение|Эквивалентное возмущение]] &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; у метода Холецкого всего вдвое больше, чем возмущение &amp;lt;math&amp;gt;\delta A&amp;lt;/math&amp;gt;, вносимое в матрицу при вводе чисел в компьютер:&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
||M||_{E} \leq 2||\delta A||_{E}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Это явление обусловлено положительной определённостью матрицы. Среди всех используемых разложений матриц это наименьшее из эквивалентных возмущений.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В простейшем (без перестановок суммирования) варианте метод Холецкого на Фортране можно записать так:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;fortran&amp;quot;&amp;gt;&lt;br /&gt;
	DO  I = 1, N&lt;br /&gt;
		S = A(I,I)&lt;br /&gt;
		DO  IP=1, I-1&lt;br /&gt;
			S = S - DPROD(A(I,IP), A(I,IP))&lt;br /&gt;
		END DO&lt;br /&gt;
		A(I,I) = SQRT (S)&lt;br /&gt;
		DO  J = I+1, N&lt;br /&gt;
			S = A(J,I)&lt;br /&gt;
			DO  IP=1, I-1&lt;br /&gt;
				S = S - DPROD(A(I,IP), A(J,IP))&lt;br /&gt;
			END DO&lt;br /&gt;
			A(J,I) = S/A(I,I)&lt;br /&gt;
		END DO&lt;br /&gt;
	END DO&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
При этом для реализации режима накопления переменная &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; должна быть двойной точности. &lt;br /&gt;
&lt;br /&gt;
Отдельно следует обратить внимание на используемую в такой реализации функцию DPROD. Её появление как раз связано с тем, как математики могли использовать режим накопления вычислений. Дело в том, что, как правило, при выполнении умножения двух чисел с плавающей запятой сначала результат получается с удвоенными длинами мантиссы и порядка, и только при выполнении присваивания переменной одинарной точности результат округляется. Эта возможность даёт выполнять умножение действительных чисел с двойной точностью без предварительного приведения их к типу двойной точности. На ранних этапах развития вычислительных библиотек снятие результата без округление реализовали вставками специального кода в фортран-программы, но уже в 70-х гг. XX века в ряде трансляторов Фортрана появилась функция DPROD, реализующая это без обращения программиста к машинным кодам. Она была закреплена среди стандартных функций в стандарте Фортран 77, и реализована во всех современных трансляторах Фортрана. &lt;br /&gt;
&lt;br /&gt;
Для метода Холецкого существует блочная версия, которая отличается от точечной не тем, что операции над числами заменены на аналоги этих операций над блоками; её построение основано на том, что практически все циклы точечной версии имеют тип SchedDo в терминах методологии, основанной на исследовании информационного графа и, следовательно, могут быть развёрнуты. Тем не менее, обычно блочную версию метода Холецкого записывают не в виде программы с развёрнутыми и переставленными циклами, а в виде программы, подобной реализации точечного метода, в которой вместо соответствующих скалярных операций присутствуют операции над блоками.&lt;br /&gt;
&lt;br /&gt;
Особенностью размещения массивов в Фортране является хранение их &amp;quot;по столбцам&amp;quot; (быстрее всего меняется первый индекс). Поэтому для обеспечения локальности работы с памятью представляется более эффективной такая схема метода Холецкого (полностью эквивалентная описанной), когда исходная матрица и её разложение хранятся не в нижнем, а в верхнем треугольнике. Тогда при вычислениях скалярных произведений программа будет &amp;quot;идти&amp;quot; по последовательным ячейкам памяти компьютера.&lt;br /&gt;
&lt;br /&gt;
Есть и другой вариант точечной схемы: использовать вычисляемые элементы матрицы &amp;lt;math&amp;gt;L&amp;lt;/math&amp;gt; в качестве аргументов непосредственно «сразу после» их вычисления. Такая программа будет выглядеть так:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;fortran&amp;quot;&amp;gt;&lt;br /&gt;
	DO  I = 1, N&lt;br /&gt;
		A(I,I) = SQRT (A(I, I))&lt;br /&gt;
		DO  J = I+1, N&lt;br /&gt;
			A(J,I) = A(J,I)/A(I,I)&lt;br /&gt;
		END DO&lt;br /&gt;
		DO  K=I+1,N&lt;br /&gt;
			DO J = K, N&lt;br /&gt;
				A(J,K) = A(J,K) - A(J,I)*A(K,I)&lt;br /&gt;
			END DO	&lt;br /&gt;
		END DO&lt;br /&gt;
	END DO&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Как видно, в этом варианте для реализации режима накопления одинарной точности мы должны будем объявить двойную точность для массива, хранящего исходные данные и результат. Подчеркнём, что [[глоссарий#Граф алгоритма|граф алгоритма]] обеих схем - один и тот же (из п.1.7), если не считать изменением замену умножения на функцию DPROD!&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
==== Локальность реализации алгоритма ====&lt;br /&gt;
&lt;br /&gt;
===== Структура обращений в память и качественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
[[file:Cholesky_locality1.jpg|thumb|center|700px|Рисунок 3. Реализация метода Холецкого. Общий профиль обращений в память]]&lt;br /&gt;
&lt;br /&gt;
На рис.3 представлен профиль обращений в память&amp;lt;ref&amp;gt;Воеводин Вад. В. Визуализация и анализ профиля обращений в память // Вестник Южно-Уральского государственного университета. Серия Математическое моделирование и про-граммирование. — 2011. — Т. 17, № 234. — С. 76–84.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Воеводин Вл. В., Воеводин Вад. В. Спасительная локальность суперкомпьютеров // Открытые системы. — 2013. — № 9. — С. 12–15.&amp;lt;/ref&amp;gt; для реализации метода Холецкого. В программе задействован только 1 массив, поэтому в данном случае обращения в профиле происходят только к элементам этого массива. Программа состоит из одного основного этапа, который, в свою очередь, состоит из последовательности подобных итераций. Пример одной итерации выделен зеленым цветом.&lt;br /&gt;
&lt;br /&gt;
Видно, что на каждой &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-й итерации используются все адреса, начиная с некоторого, при этом адрес первого обрабатываемого элемента увеличивается. Также можно заметить, что число обращений в память на каждой итерации растет примерно до середины работы программы, после чего уменьшается вплоть до завершения работы. Это позволяет говорить о том, что данные в программе используются неравномерно, при этом многие итерации, особенно в начале выполнения программы, задействуют большой объем данных, что приводит к ухудшению локальности.&lt;br /&gt;
&lt;br /&gt;
Однако в данном случае основным фактором, влияющим на локальность работы с памятью, является строение итерации. Рассмотрим фрагмент профиля, соответствующий нескольким первым итерациям.&lt;br /&gt;
&lt;br /&gt;
[[file:Cholesky_locality2.jpg|thumb|center|700px|Рисунок 4. Реализация метода Холецкого. Фрагмент профиля (несколько первых итераций)]]&lt;br /&gt;
&lt;br /&gt;
Исходя из рис.4 видно, что каждая итерация состоит из двух различных фрагментов. Фрагмент 1 – последовательный перебор (с некоторым шагом) всех адресов, начиная с некоторого начального. При этом к каждому адресу происходит мало обращений. Такой фрагмент обладает достаточно неплохой пространственной локальностью, так как шаг по памяти между соседними обращениями невелик, но плохой временно́й локальностью, поскольку данные редко используются повторно.&lt;br /&gt;
&lt;br /&gt;
Фрагмент 2 устроен гораздо лучше с точки зрения локальности. В рамках этого фрагмента выполняется большое число обращений подряд к одним и тем же данным, что обеспечивает гораздо более высокую степень как пространственной, так и временно́й локальности по сравнению с фрагментом 1.&lt;br /&gt;
&lt;br /&gt;
После рассмотрения фрагмента профиля на рис.4 можно оценить общую локальность двух фрагментов на каждой итерации. Однако стоит рассмотреть более подробно, как устроен каждый из фрагментов. &lt;br /&gt;
&lt;br /&gt;
[[file:Cholesky_locality3.jpg|thumb|center|700px|Рисунок 5. Реализация метода Холецкого. Фрагмент профиля (часть одной итерации)]]&lt;br /&gt;
&lt;br /&gt;
Рис.5, на котором представлена часть одной итерации общего профиля (см. рис.3), позволяет отметить достаточно интересный факт: строение каждого из фрагментов на самом деле заметно сложнее, чем это выглядит на рис.4. В частности, каждый шаг фрагмента 1 состоит из нескольких обращений к соседним адресам, причем выполняется не последовательный перебор. Также можно увидеть, что фрагмент 2 на самом деле в свою очередь состоит из повторяющихся итераций, при этом видно, что каждый шаг фрагмента 1 соответствует одной итерации фрагмента 2 (выделено зеленым на рис.5). Это лишний раз говорит о том, что для точного понимания локальной структуры профиля необходимо его рассмотреть на уровне отдельных обращений.&lt;br /&gt;
&lt;br /&gt;
Стоит отметить, что выводы на основе рис.5 просто дополняют общее представлении о строении профиля обращений; сделанные на основе рис.4 выводы относительно общей локальности двух фрагментов остаются верны.&lt;br /&gt;
&lt;br /&gt;
===== Количественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
Основной фрагмент реализации, на основе которого были получены количественные оценки, приведен [http://git.algowiki-project.org/Voevodin/locality/blob/master/benchmarks/holecky/holecky.h здесь] (функция Kernel). Условия запуска описаны [http://git.algowiki-project.org/Voevodin/locality/blob/master/README.md здесь].&lt;br /&gt;
&lt;br /&gt;
Первая оценка выполняется на основе характеристики daps, которая оценивает число выполненных обращений (чтений и записей) в память в секунду. Данная характеристика является аналогом оценки flops применительно к работе с памятью и является в большей степени оценкой производительности взаимодействия с памятью, чем оценкой локальности. Однако она служит хорошим источником информации, в том числе для сравнения с результатами по следующей характеристике cvg.&lt;br /&gt;
&lt;br /&gt;
[[file:Cholesky_locality4.jpg|thumb|center|700px|Рисунок 6. Сравнение значений оценки daps]]&lt;br /&gt;
&lt;br /&gt;
На рис.6 приведены значения daps для реализаций распространенных алгоритмов, отсортированные по возрастанию (чем больше daps, тем в общем случае выше производительность). Можно увидеть, что реализация метода Холецкого характеризуется достаточно высокой скоростью взаимодействия с памятью, однако ниже, чем, например, у теста Линпак или реализации метода Якоби.&lt;br /&gt;
&lt;br /&gt;
Вторая характеристика – cvg – предназначена для получения более машинно-независимой оценки локальности. Она определяет, насколько часто в программе необходимо подтягивать данные в кэш-память. Соответственно, чем меньше значение cvg, тем реже это нужно делать, тем лучше локальность. &lt;br /&gt;
&lt;br /&gt;
[[file:Cholesky_locality5.jpg|thumb|center|700px|Рисунок 7. Сравнение значений оценки cvg]]&lt;br /&gt;
&lt;br /&gt;
На рис.7 приведены значения cvg для того же набора реализаций, отсортированные по убыванию (чем меньше cvg, тем в общем случае выше локальность). Можно увидеть, что, согласно данной оценке, реализация метода Холецкого оказалась ниже в списке по сравнению с оценкой daps.&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Как нетрудно видеть по структуре графа алгоритма, вариантов распараллеливания алгоритма довольно много. Например, во втором варианте (см. раздел «[[#Особенности реализации последовательного алгоритма|Особенности реализации последовательного алгоритма]]») все внутренние циклы параллельны, в первом — параллелен цикл по &amp;lt;math&amp;gt;J&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;
Проведём исследование масштабируемости параллельной реализации разложения Холецкого согласно [[Scalability methodology|методике]]. Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов&amp;quot;&amp;lt;ref name=&amp;quot;Lom&amp;quot;&amp;gt;Воеводин Вл., Жуматий С., Соболев С., Антонов А., Брызгалов П., Никитенко Д., Стефанов К., Воеводин Вад. Практика суперкомпьютера «Ломоносов» // Открытые системы, 2012, N 7, С. 36-39.&amp;lt;/ref&amp;gt; [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Набор и границы значений изменяемых [[Глоссарий#Параметры запуска|параметров запуска]] реализации алгоритма: &lt;br /&gt;
&lt;br /&gt;
*	число процессоров [4 : 256] с шагом 4;&lt;br /&gt;
*	размер матрицы [1024 : 5120].&lt;br /&gt;
&lt;br /&gt;
В результате проведённых экспериментов был получен следующий диапазон [[Глоссарий#Эффективность реализации|эффективности реализации]] алгоритма:&lt;br /&gt;
&lt;br /&gt;
*	минимальная эффективность реализации 0,11%;&lt;br /&gt;
*	максимальная эффективность реализации 2,65%.&lt;br /&gt;
&lt;br /&gt;
На следующих рисунках приведены графики [[Глоссарий#Производительность|производительности]] и эффективности выбранной реализации разложения Холецкого в зависимости от изменяемых параметров запуска.&lt;br /&gt;
&lt;br /&gt;
[[file:Масштабируемость Параллельной реализации метода Холецкого Производительность3.png|thumb|center|700px|Рисунок 8. Параллельная реализация метода Холецкого. Изменение производительности в зависимости от числа процессоров и размера матрицы.]]&lt;br /&gt;
[[file:Холецкий масштабируемость эффективность2.png|thumb|center|700px|Рисунок 9. Параллельная реализация метода Холецкого. Изменение эффективности в зависимости от числа процессоров и размера матрицы.]]&lt;br /&gt;
&lt;br /&gt;
Построим оценки масштабируемости выбранной реализации разложения Холецкого:&lt;br /&gt;
*	По числу процессов: -0,000593. При увеличении числа процессов эффективность на рассмотренной области изменений параметров запуска уменьшается, однако в целом уменьшение не очень быстрое. Малая интенсивность изменения объясняется крайне низкой общей эффективностью работы приложения с максимумом в 2,65%, и значение эффективности на рассмотренной области значений быстро доходит до десятых долей процента. Это свидетельствует о том, что на большей части области значений нет интенсивного снижения эффективности. Это объясняется также тем, что с ростом [[Глоссарий#Вычислительная сложность|вычислительной сложности]] падение эффективности становится не таким быстрым. Уменьшение эффективности на рассмотренной области работы параллельной программы объясняется быстрым ростом накладных расходов на организацию параллельного выполнения. С ростом вычислительной сложности задачи эффективность снижается так же быстро, но при больших значениях числа процессов. Это подтверждает предположение о том, что накладные расходы начинают сильно превалировать над вычислениями.&lt;br /&gt;
*	По размеру задачи: 0,06017. При увеличении размера задачи эффективность возрастает. Эффективность возрастает тем быстрее, чем большее число процессов используется для выполнения. Это подтверждает предположение о том, что размер задачи сильно влияет на эффективность выполнения приложения. Оценка показывает, что с ростом размера задачи эффективность на рассмотренной области значений параметров запуска сильно увеличивается. Также, учитывая разницу максимальной и минимальной эффективности в 2,5%, можно сделать вывод, что рост эффективности при увеличении размера задачи наблюдается на большей части рассмотренной области значений.&lt;br /&gt;
*	По двум направлениям: 0,000403. При рассмотрении увеличения как вычислительной сложности, так и числа процессов на всей рассмотренной области значений эффективность увеличивается, однако скорость увеличения эффективности небольшая. В совокупности с тем фактом, что разница между максимальной и минимальной эффективностью на рассмотренной области значений параметров небольшая, эффективность с увеличением масштабов возрастает, но медленно и с небольшими перепадами.&lt;br /&gt;
&lt;br /&gt;
[http://git.algowiki-project.org/Teplov/Scalability/tree/master/cholesky-decomposition-master Исследованная параллельная реализация на языке C]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Для проведения экспериментов использовалась реализация разложения Холецкого, представленная в пакете SCALAPACK библиотеки Intel MKL (метод pdpotrf).  Все результаты получены на суперкомпьютере «Ломоносов»&amp;lt;ref name=&amp;quot;Lom&amp;quot; /&amp;gt;. Использовались процессоры Intel Xeon X5570 с пиковой производительностью в 94 Гфлопс, а также компилятор Intel с опцией –O2. &lt;br /&gt;
На рисунках показана эффективность реализации разложения Холецкого (случай использования нижних треугольников матриц) для размерности матрицы 80000, запуск проводился на 256 процессах.&lt;br /&gt;
&lt;br /&gt;
[[file:Cholesky CPU.png|thumb|center|700px|Рисунок 10. График загрузки CPU при выполнении разложения Холецкого]]&lt;br /&gt;
&lt;br /&gt;
На графике загрузки процессора видно, что почти все время работы программы уровень загрузки составляет около 50%. Это хорошая картина для программ, запущенных без использования технологии Hyper Threading. &lt;br /&gt;
&lt;br /&gt;
[[file:Cholesky FLOPS.png|thumb|center|700px|Рисунок 11. График операций с плавающей точкой в секунду при выполнении разложения Холецкого]]&lt;br /&gt;
&lt;br /&gt;
На Рисунке 11 показан график количества операций с плавающей точкой в секунду. Видно, что к концу каждой итерации число операций возрастает.&lt;br /&gt;
 [[file:Cholesky L1.png|thumb|center|700px|Рисунок 12. График кэш-промахов L1 в секунду при работе разложения Холецкого]]&lt;br /&gt;
 &lt;br /&gt;
На графике кэш-промахов первого уровня видно, что число промахов достаточно большое и находится на уровне 25 млн/сек в среднем по всем узлам.&lt;br /&gt;
 [[file:Cholesky L3.png|thumb|center|700px|Рисунок 13. График кэш-промахов L3 в секунду при работе разложения Холецкого]]&lt;br /&gt;
&lt;br /&gt;
На графике кэш-промахов третьего уровня видно, что число промахов все еще достаточно большое и находится на уровне 1,5 млн/сек в среднем по всем узлам. Это указывает на то, что задача достаточно большая, и данные плохо укладываются в кэш-память.&lt;br /&gt;
[[file:Cholesky MemRead.png|thumb|center|700px|Рисунок 14. График количества чтений из оперативной памяти при работе разложения Холецкого]]&lt;br /&gt;
&lt;br /&gt;
На графике чтений из памяти на протяжении всего времени работы программы наблюдается достаточно интенсивная и не сильно изменяющаяся работа с памятью.&lt;br /&gt;
 [[file:Cholesky MemWrite.png|thumb|center|700px|Рисунок 15. График количества записей в оперативную память при работе разложения Холецкого]]&lt;br /&gt;
&lt;br /&gt;
На графике записей в память видна периодичность: на каждой итерации к концу выполнения число записей в память достаточно сильно падает. Это коррелирует с возрастанием числа операций с плавающей точкой и может объясняться тем, что при меньшем числе записей в память программа уменьшает накладные расходы и увеличивает эффективность.&lt;br /&gt;
 [[file:Cholesky Inf Bps.png|thumb|center|700px|Рисунок 16. График скорости передачи по сети Infiniband в байт/сек при работе разложения Холецкого]]&lt;br /&gt;
&lt;br /&gt;
На графике скорости передачи данных по сети Infiniband наблюдается достаточно интенсивное использование коммуникационной сети на каждой итерации. Причем к концу каждой итерации интенсивность передачи данных сильно возрастает. Это указывает на большую необходимость в обмене данными между процессами к концу итерации.&lt;br /&gt;
 [[file:Cholesky Inf Pps.png|thumb|center|700px|Рисунок 17. График скорости передачи по сети Infiniband в пакетах/сек при работе разложения Холецкого]]&lt;br /&gt;
&lt;br /&gt;
На графике скорости передачи данных в пакетах в секунду наблюдается большая «кучность» показаний максимального минимального и среднего значений в сравнении с графиком скорости передачи в байт/сек. Это говорит о том, что, вероятно, процессы обмениваются сообщениями различной длины, что указывает на неравномерное распределение данных. Также наблюдается рост интенсивности использования сети к концу каждой итерации.&lt;br /&gt;
 [[file:Cholesky LoadAVG.png|thumb|center|700px|Рисунок 18. График числа процессов, ожидающих вхождения в стадию счета (Loadavg), при работе разложения Холецкого]]&lt;br /&gt;
На графике числа процессов, ожидающих вхождения в стадию счета (Loadavg), видно, что на протяжении всей работы программы значение этого параметра постоянно и приблизительно равняется 8. Это свидетельствует о стабильной работе программы с восьмью процессами на каждом узле. Это указывает на рациональную и статичную загрузку аппаратных ресурсов процессами.&lt;br /&gt;
В целом, по данным системного мониторинга работы программы можно сделать вывод о том, что программа работала достаточно эффективно и стабильно. Использование памяти и коммуникационной среды достаточно интенсивное, что может стать фактором снижения эффективности при существенном росте размера задачи или же числа процессоров.&lt;br /&gt;
Для существующих параллельных реализаций характерно отнесение всего ресурса параллелизма на блочный уровень. Относительно низкая эффективность работы связана с проблемами внутри одного узла, следующим фактором является неоптимальное соотношение между «арифметикой» и обменами. Видно, что при некотором (довольно большом) оптимальном размере блока обмены влияют не так уж сильно.&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
Как видно по показателям SCALAPACK на суперкомпьютерах, обмены при большом n хоть и уменьшают эффективность расчётов, но слабее, чем неоптимальность организации расчётов внутри одного узла. Поэтому, видимо, следует сначала оптимизировать не блочный алгоритм, а подпрограммы, используемые на отдельных процессорах: точечный метод Холецкого, перемножения матриц и др. подпрограммы. [[#Существующие реализации алгоритма|Ниже]] содержится информация о возможном направлении такой оптимизации.&lt;br /&gt;
&lt;br /&gt;
Вообще эффективность метода Холецкого для параллельных архитектур не может быть высокой. Это связано со следующим свойством информационной структуры алгоритма: если операции деления или вычисления выражений &amp;lt;math&amp;gt;a - bc&amp;lt;/math&amp;gt; являются не только массовыми, но и параллельными, и потому их вычисления сравнительно легко выстраивать в конвейеры или распределять по устройствам, то операции извлечения квадратных корней являются узким местом алгоритма. Поэтому для эффективной реализации  алгоритмов, столь же хороших по вычислительным характеристикам, как и метод квадратного корня, следует использовать не метод Холецкого, а его давно известную модификацию без извлечения квадратных корней — [[%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D0%A5%D0%BE%D0%BB%D0%B5%D1%86%D0%BA%D0%BE%D0%B3%D0%BE_(%D0%BD%D0%B0%D1%85%D0%BE%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D1%81%D0%B8%D0%BC%D0%BC%D0%B5%D1%82%D1%80%D0%B8%D1%87%D0%BD%D0%BE%D0%B3%D0%BE_%D1%82%D1%80%D0%B5%D1%83%D0%B3%D0%BE%D0%BB%D1%8C%D0%BD%D0%BE%D0%B3%D0%BE_%D1%80%D0%B0%D0%B7%D0%BB%D0%BE%D0%B6%D0%B5%D0%BD%D0%B8%D1%8F)#.5C.28LDL.5ET.5C.29_.D1.80.D0.B0.D0.B7.D0.BB.D0.BE.D0.B6.D0.B5.D0.BD.D0.B8.D0.B5|разложение матрицы в произведение &amp;lt;math&amp;gt;L D L^T&amp;lt;/math&amp;gt;]]&amp;lt;ref&amp;gt;Krishnamoorthy A., Menon D. Matrix Inversion Using Cholesky Decomposition. 2013. eprint arXiv:1111.4144&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Точечный метод Холецкого реализован как в основных библиотеках программ отечественных организаций, так и в западных пакетах LINPACK, LAPACK, SCALAPACK и др. &lt;br /&gt;
	&lt;br /&gt;
При этом в отечественных реализациях, как правило, выполнены стандартные требования к методу с точки зрения ошибок округления, то есть, реализован режим накопления, и обычно нет лишних операций. Ряд старых отечественных реализаций использует для экономии памяти упаковку матриц &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;L&amp;lt;/math&amp;gt; в одномерный массив. &lt;br /&gt;
	&lt;br /&gt;
Реализация точечного метода Холецкого в современных западных пакетах обычно происходит из одной и той же реализации метода в LINPACK, а та использует пакет BLAS. В BLAS скалярное произведение реализовано без режима накопления, но это легко исправляется при желании. &lt;br /&gt;
&lt;br /&gt;
Интересно, что в крупнейших библиотеках алгоритмов до сих пор предлагается именно разложение Холецкого, а более быстрый алгоритм LU-разложения без извлечения квадратных корней используется только в особых случаях (например, для трёхдиагональных матриц), в которых количество диагональных элементов уже сравнимо с количеством внедиагональных.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references \&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[en:Cholesky decomposition]]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Законченные статьи]]&lt;br /&gt;
[[Категория:Разложения матриц]]&lt;/div&gt;</summary>
		<author><name>Teplov</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D0%A5%D0%B0%D1%83%D1%81%D1%85%D0%BE%D0%BB%D0%B4%D0%B5%D1%80%D0%B0_(%D0%BE%D1%82%D1%80%D0%B0%D0%B6%D0%B5%D0%BD%D0%B8%D0%B9)_QR-%D1%80%D0%B0%D0%B7%D0%BB%D0%BE%D0%B6%D0%B5%D0%BD%D0%B8%D1%8F_%D0%BA%D0%B2%D0%B0%D0%B4%D1%80%D0%B0%D1%82%D0%BD%D0%BE%D0%B9_%D0%BC%D0%B0%D1%82%D1%80%D0%B8%D1%86%D1%8B,_%D0%B2%D0%B5%D1%89%D0%B5%D1%81%D1%82%D0%B2%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D1%82%D0%BE%D1%87%D0%B5%D1%87%D0%BD%D1%8B%D0%B9_%D0%B2%D0%B0%D1%80%D0%B8%D0%B0%D0%BD%D1%82&amp;diff=21533</id>
		<title>Метод Хаусхолдера (отражений) QR-разложения квадратной матрицы, вещественный точечный вариант</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D0%A5%D0%B0%D1%83%D1%81%D1%85%D0%BE%D0%BB%D0%B4%D0%B5%D1%80%D0%B0_(%D0%BE%D1%82%D1%80%D0%B0%D0%B6%D0%B5%D0%BD%D0%B8%D0%B9)_QR-%D1%80%D0%B0%D0%B7%D0%BB%D0%BE%D0%B6%D0%B5%D0%BD%D0%B8%D1%8F_%D0%BA%D0%B2%D0%B0%D0%B4%D1%80%D0%B0%D1%82%D0%BD%D0%BE%D0%B9_%D0%BC%D0%B0%D1%82%D1%80%D0%B8%D1%86%D1%8B,_%D0%B2%D0%B5%D1%89%D0%B5%D1%81%D1%82%D0%B2%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D1%82%D0%BE%D1%87%D0%B5%D1%87%D0%BD%D1%8B%D0%B9_%D0%B2%D0%B0%D1%80%D0%B8%D0%B0%D0%BD%D1%82&amp;diff=21533"/>
		<updated>2017-01-10T13:14:03Z</updated>

		<summary type="html">&lt;p&gt;Teplov: /* Масштабируемость реализации алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = QR-разложение методом Хаусхолдера (отражений)&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;\frac{4 n^3}{3}&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;n(n + 1)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
Основные авторы описания: [[Участник:Frolov|А.В.Фролов]]&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Метод Хаусхолдера&#039;&#039;&#039; (в советской математической литературе чаще называется &#039;&#039;&#039;методом отражений&#039;&#039;&#039;) используется для разложения  матриц в виде &amp;lt;math&amp;gt;A=QR&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; - унитарная, &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; — правая треугольная матрица)&amp;lt;ref&amp;gt;В.В.Воеводин, Ю.А.Кузнецов. Матрицы и вычисления. М.: Наука, 1984.&amp;lt;/ref&amp;gt;. При этом матрица &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; хранится и используется не в своём явном виде, а в виде произведения матриц отражения&amp;lt;ref name=&amp;quot;VOLA&amp;quot;&amp;gt;Воеводин В.В. Вычислительные основы линейной алгебры. М.: Наука, 1977.&amp;lt;/ref&amp;gt;. Каждая из матриц отражения может быть определена одним вектором. Это позволяет в классическом исполнении метода отражений хранить результаты разложения на месте матрицы A с использованием минимального одномерного дополнительного массива. &lt;br /&gt;
&lt;br /&gt;
В данной статье рассматривается именно классическое исполнение, в котором не используются приёмы типа сдваивания при вычислениях скалярных произведений.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В методе Хаусхолдера для выполнения &amp;lt;math&amp;gt;QR&amp;lt;/math&amp;gt;-разложения матрицы используются умножения слева её текущих модификаций на матрицы Хаусхолдера (отражений). &lt;br /&gt;
&lt;br /&gt;
{{Шаблон:Матрица отражений}}&lt;br /&gt;
&lt;br /&gt;
На &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м шаге метода с помощью преобразования отражения &amp;quot;убираются&amp;quot; ненулевые поддиагональные элементы в &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м столбце. Таким образом, после &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; шагов преобразований получается матрица &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; из &amp;lt;math&amp;gt;QR&amp;lt;/math&amp;gt;-разложения.&lt;br /&gt;
&lt;br /&gt;
На каждом из шагов метода матрицу отражений обычно представляют не в стандартном виде, а в виде &amp;lt;math&amp;gt;U=E-\frac{1}{\gamma}vv^*&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; находится через координаты текущего &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; - вектор размерности &amp;lt;math&amp;gt;n+1-i&amp;lt;/math&amp;gt;, составленный из элементов &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца, начиная с &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;(s,s)=0&amp;lt;/math&amp;gt;, то &amp;lt;math&amp;gt;v=e_{i}&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\gamma = \frac{1}{2}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В остальных случаях по алгоритму вычисляется &amp;lt;math&amp;gt;u = \frac{1}{\sqrt{(s,s)}}s&amp;lt;/math&amp;gt;, и далее &amp;lt;math&amp;gt;v_{j}=0&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;j&amp;lt;i&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;v_{j}=u_{j-i+1}&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;j&amp;gt;i&amp;lt;/math&amp;gt;, а &amp;lt;math&amp;gt;v_{i}=1&amp;lt;/math&amp;gt;, если &amp;lt;math&amp;gt;u_{1}=0&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v_{i}=\frac{u_{1}}{|u_{1}|}(1+|u_{1}|)&amp;lt;/math&amp;gt; для остальных значений. При этом &amp;lt;math&amp;gt;\gamma =1+|u_{1}|=|v_{i}|&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
После вычисления вектора &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; подстолбцы справа от ведущего модифицируются по формулам &amp;lt;math&amp;gt;x&#039;=x-\frac{(x,v)}{\gamma}v&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Основную часть алгоритма составляют вычисления на каждом шагу скалярных произведений &amp;lt;math&amp;gt;(s,s)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(x,v)&amp;lt;/math&amp;gt; для всех подстолбцов &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt;справа от текущего, а также векторные операции &amp;lt;math&amp;gt;x&#039;=x-\frac{(x,v)}{\gamma}v&amp;lt;/math&amp;gt;. Это используется при программировании метода во многих библиотеках для его конструирования из стандартных подпрограмм (например, из BLAS).&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
[[File:HausLeft1.png|thumb|right|600px|Рисунок 1. Граф шага (обнуление &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;го столбца) алгоритма. Квадратики - входные данные шага (берутся с предыдущего или из входных данных), кружки - операции. Зелёным выделены операции типа a+bb, салатовым и голубым - типа a+bc, тёмно-синим - вычисление &amp;lt;math&amp;gt;\gamma , v_{1}&amp;lt;/math&amp;gt;, жёлтым - умножения (или деления). Обведённая группа операций повторяется независимо n-i раз. Результаты синего, красных и жёлтых кружков, а на последнем шаге и голубого - выходные для алгоритма.]]&lt;br /&gt;
&lt;br /&gt;
Как уже сказано в описании ядра, основная часть - вычисления на каждом шагу скалярных произведений &amp;lt;math&amp;gt;(s,s)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(x,v)&amp;lt;/math&amp;gt; для всех подстолбцов &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; справа от текущего, а также векторные операции &amp;lt;math&amp;gt;x&#039;=x-\frac{(x,v)}{\gamma}v&amp;lt;/math&amp;gt;. При этом, однако, строгая последовательность выполнения этих трёх подшагов не обязательна, в силу связи получаемых векторов &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; можно одновременно с &amp;lt;math&amp;gt;(s,s)&amp;lt;/math&amp;gt; вычислять и произведения &amp;lt;math&amp;gt;(x,s)&amp;lt;/math&amp;gt; с последующим выражением через них &amp;lt;math&amp;gt;(x,v)&amp;lt;/math&amp;gt;. Это позволяет почти вдвое уменьшать критический путь графа алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Последовательность выполнения алгоритма обычно записывается как последовательное &amp;quot;обнуление&amp;quot; поддиагональных элементов столбцов, начиная с 1-го столбца и заканчивая предпоследним &amp;lt;math&amp;gt;(n-1)&amp;lt;/math&amp;gt;-м.&lt;br /&gt;
&lt;br /&gt;
При этом в каждом &amp;quot;обнуляемом&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м столбце &amp;quot;обнуляются&amp;quot; сразу все его поддиагональные элементы одновременно, с &amp;lt;math&amp;gt;(i+1)&amp;lt;/math&amp;gt;-го до &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;-го. &lt;br /&gt;
&lt;br /&gt;
Каждое &amp;quot;обнуление&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца состоит из двух шагов: &lt;br /&gt;
а) вычисление параметров матрицы отражения &amp;lt;math&amp;gt;U_{i}&amp;lt;/math&amp;gt; такой, чтобы при умножении на неё слева &amp;quot;обнулились&amp;quot; все поддиагональные его элементы;&lt;br /&gt;
б) умножение слева матрицы отражения &amp;lt;math&amp;gt;U_{i}&amp;lt;/math&amp;gt; на текущую версию матрицы.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В последовательной версии основная сложность алгоритма определяется прежде всего вычислениями скалярных произведений векторов, а также модификаций векторов вида &amp;lt;math&amp;gt;x&#039;=x-\alpha v&amp;lt;/math&amp;gt;, причем над векторами убывающей по ходу алгоритма размерности. Они, если не учитывать возможную разреженность, составляют (в главном члене) по &amp;lt;math&amp;gt;2n^3/3&amp;lt;/math&amp;gt; операций действительного умножения и сложения/вычитания.&lt;br /&gt;
&lt;br /&gt;
При классификации по последовательной сложности, таким образом, метод Хаусхолдера относится к алгоритмам &#039;&#039;с кубической сложностью&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
На Рисунке 1 приведён шаг графа алгоритма метода Хаусхолдера в наиболее его быстром (с параллельной точки зрения) варианте, использующем то, что с точностью до множителя ведущий вектор матрицы отражения отличается отличается от подстолбца, где выполняется очередное исключение, только одним элементом. Операции привязаны к обрабатываемым элементам матрицы. Для получения полного графа графы шагов нужно положить друг на друга последовательными слоями, при этом правые нижние углы должны быть друг над другом.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Для понимания ресурса параллелизма в разложении матрицы порядка &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; методом Хаусхолдера нужно рассмотреть критический путь графа. &lt;br /&gt;
&lt;br /&gt;
Как видно из описания разных вершин, вычисления при &amp;quot;обнулении&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца параметров отражения и скалярных произведений состоят из основной части - ветви длиной по &amp;lt;math&amp;gt;n-i&amp;lt;/math&amp;gt; умножений и сложений - и коррекции вычислений, которые составляют &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; операций.&lt;br /&gt;
&lt;br /&gt;
Поэтому по грубой (без членов низших порядков) оценке критический путь метода Хаусхолдера будет идти через &amp;lt;math&amp;gt;\frac{n^2}{2}&amp;lt;/math&amp;gt; умножений и &amp;lt;math&amp;gt;\frac{n^2}{2}&amp;lt;/math&amp;gt; сложений/вычитаний. &lt;br /&gt;
 &lt;br /&gt;
Поэтому в параллельном варианте, как и в последовательном, основную долю требуемого для выполнения алгоритма времени будут определять операции вида &amp;lt;math&amp;gt;a+bc&amp;lt;/math&amp;gt;.  &lt;br /&gt;
&lt;br /&gt;
При классификации по высоте ЯПФ, таким образом, метод Хаусхолдера относится к алгоритмам &#039;&#039;с квадратичной сложностью&#039;&#039;. При классификации по ширине ЯПФ его сложность будет также &#039;&#039;квадратичной&#039;&#039; (без расширения ряда ярусов, связанных с векторными операциями сложения, пришлось бы увеличить вдвое длину критического пути; при таком расширении сложность по ширине ЯПФ станет &#039;&#039;линейной&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
Надо сказать, что здесь в оценках речь идёт именно о классическом способе реализации метода Хаусхолдера. Даже использование схем сдваивания или последовательно-параллельных для вычисления скалярных произведений уменьшает критический путь с квадратичного до &#039;&#039;степени 3/2&#039;&#039; или &#039;&#039;линейно-логарифмического&#039;&#039;. Однако все эти широко распространённые методы пока не дали возможности снизить критический путь метода Хаусхолдера до &#039;&#039;линейного&#039;&#039; (как, скажем, у метода Гивенса).&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: плотная квадратная матрица &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; (элементы &amp;lt;math&amp;gt;a_{ij}&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: правая треугольная матрица &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; (ненулевые элементы &amp;lt;math&amp;gt;r_{ij}&amp;lt;/math&amp;gt; в последовательном варианте хранятся в элементах исходной матрицы &amp;lt;math&amp;gt;a_{ij}&amp;lt;/math&amp;gt;), унитарная (ортогональная) матрица Q - как произведение матриц Хаусхолдера (отражения) (их вектора нормалей к плоскостям отражения в последовательном варианте хранятся в поддиагональных элементах исходной матрицы &amp;lt;math&amp;gt;a_{ij}&amp;lt;/math&amp;gt; и в одном дополнительном столбце размерности n).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;n^2+n&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Соотношение последовательной и параллельной сложности, как хорошо видно, является &#039;&#039;линейным&#039;&#039;, что даёт определённый стимул для распараллеливания. Однако у наискорейшей ЯПФ ширина &#039;&#039;квадратична&#039;&#039;, что указывает на дисбаланс между загруженностями устройств при попытке её реально запрограммировать. Поэтому более практично даже при хорошей (быстрой) вычислительной сети оставить количество устройств (например, узлов кластера) &#039;&#039;линейным&#039;&#039; по размеру матрицы, что удвоит критический путь реализуемой ЯПФ. &lt;br /&gt;
&lt;br /&gt;
При этом вычислительная мощность алгоритма, как отношение числа операций к суммарному объему входных и выходных данных, &#039;&#039;линейна&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Алгоритм в рамках выбранной версии полностью детерминирован.&lt;br /&gt;
&lt;br /&gt;
Вычислительная погрешность в методе отражений (Хаусхолдера) растет &#039;&#039;линейно&#039;&#039;, как и в методе Гивенса (вращений).&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В варианте с кратчайшим критическим путём графа алгоритма (с использованием зависимости между обнуляемым вектором и направляющим вектором отражения) метод Хаусхолдера (отражений) QR-разложения квадратной вещественной матрицы на Фортране 77 можно записать так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;fortran&amp;quot;&amp;gt;&lt;br /&gt;
	DO  I = 1, N-1&lt;br /&gt;
            DO K = I, N&lt;br /&gt;
              SX(K)=A(N,I)*A(N,K)&lt;br /&gt;
            END DO&lt;br /&gt;
            DO J = N-1, I, -1&lt;br /&gt;
               DO K = I, N&lt;br /&gt;
                  SX(K)=SX(K)+A(J,I)*A(J,K)&lt;br /&gt;
               END DO&lt;br /&gt;
            END DO&lt;br /&gt;
&lt;br /&gt;
               ALPHA = SQRT (SX(I))&lt;br /&gt;
               IF (A(I,I).NE.0.) THEN&lt;br /&gt;
                  BETA = 1./ALPHA&lt;br /&gt;
                  DO J = I+1, N&lt;br /&gt;
                    A(J,I)=A(J,I)*BETA&lt;br /&gt;
                  END DO&lt;br /&gt;
                  SX(I) = A(I,I)*BETA+SIGN(1.,A(I,I))                     &lt;br /&gt;
                  A(I,I)=ALPHA&lt;br /&gt;
                  G=1./ABS(SX(I)) ! 1/gamma&lt;br /&gt;
                  DO K = I+1, N&lt;br /&gt;
                     SX(K)=SX(K)*BETA*G+SIGN(A(I,K),SX(I))&lt;br /&gt;
                     A(I,K) = A(I,K)+SX(K)*SX(I)&lt;br /&gt;
                     DO J = I+1, N&lt;br /&gt;
                       A (J,K) = A(J,K)-A(J,I)*SX(K)&lt;br /&gt;
                     END DO&lt;br /&gt;
                  END DO&lt;br /&gt;
               ELSE&lt;br /&gt;
                  IF (ALPHA.NE.0.) THEN&lt;br /&gt;
                     BETA = 1./ALPHA&lt;br /&gt;
                     DO J = I+1, N&lt;br /&gt;
                       A(J,I)=A(J,I)*BETA&lt;br /&gt;
                     END DO&lt;br /&gt;
                     SX(I) = -1. &lt;br /&gt;
                     A(I,I)=ALPHA&lt;br /&gt;
                     G=1.! 1/gamma&lt;br /&gt;
                     DO K = I+1, N&lt;br /&gt;
                        SX(K)=SX(K)*BETA*G+SIGN(A(I,K),SX(I))&lt;br /&gt;
                        A(I,K) = A(I,K)+SX(K)*SX(I)&lt;br /&gt;
                        DO J = I+1, N&lt;br /&gt;
                          A (J,K) = A(J,K)-A(J,I)*SX(K)&lt;br /&gt;
                        END DO&lt;br /&gt;
                     END DO&lt;br /&gt;
                  ELSE&lt;br /&gt;
                     SX(I)=1&lt;br /&gt;
                     G=2.&lt;br /&gt;
                     DO K = I+1, N&lt;br /&gt;
                        SX(K)=2.&lt;br /&gt;
                        A(I,K) = A(I,K)-SX(K)&lt;br /&gt;
                     END DO&lt;br /&gt;
                  END IF&lt;br /&gt;
               END IF               &lt;br /&gt;
&lt;br /&gt;
		&lt;br /&gt;
	END DO&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В этом варианте R расположена в верхнем правом треугольнике массива A, направляющие вектора матриц отражений размещены в поддиагональных элементах соответствующих столбцов, а их первые элементы - в элементах массива SX.&lt;br /&gt;
&lt;br /&gt;
Обычно же в последовательных версиях коэффициенты модификаций столбцов вычисляются целиком через скалярные произведения после вычислений параметров матрицы отражения. При этом схема чуть проще. Удлиняется критический путь графа, но для последовательных реализаций это неважно.&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
К сожалению, в графе, как видно по рисунку, в наличии пучки рассылок, в них неизбежно часть дуг остаются длинными, что отрицательно влияет на локальность вычислений по пространству.  &lt;br /&gt;
&lt;br /&gt;
==== Локальность реализации алгоритма ====&lt;br /&gt;
===== Структура обращений в память и качественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qr_1.png|thumb|center|700px|Рисунок 2. Метод Хаусхолдера (отражений) QR-разложения квадратной матрицы, вещественный вариант. Общий профиль обращений в память]]&lt;br /&gt;
&lt;br /&gt;
На рис. 2 представлен профиль обращений в память для реализации вещественного варианта метода Хаусхолдера (отражений) QR-разложения квадратной матрицы. Из данного рисунка можно сделать несколько выводов. Во-первых, видно, что в среднем к каждому элементу выполняется примерно 90 обращений (отношение общего числа обращений к числу задействованных данных), что является достаточно хорошим показателем в плане повторного использования данных. Далее, явно видна итерационная структура профиля, причем на каждой следующей итерации обращения к небольшой части первых элементов перестают выполняться. Это говорит о том, что ближе к концу профиля задействовано меньше данных, что хорошо сказывается на пространственной локальности. Однако в то же время видно, что на каждой следующей итерации выполняется меньше обращений к одним и тем же локальным областям, то есть временная локальность, по всей вероятности, становится ниже. Рассмотрим подробнее первую итерацию, что более детально оценить их строение.&lt;br /&gt;
&lt;br /&gt;
На рис. 3 представлена одна итерация общего профиля (выделенный зеленый фрагмент на рис. 2). Ее можно условно разбить на 5 фрагментов, каждую из которых рассмотреть отдельно. Фрагменты 2 и 5 устроены просто – элементы перебираются с большим шагом по памяти, при достижении конца данных перебор повторяется, только с небольшим сдвигом. Про такие фрагменты можно сказать, что они обладают низкой пространственной локальностью, поскольку шаг по памяти достаточно большой, и низкой временной локальностью, так как данные повторно не используются. Однако отметим, что данные фрагменты состоят из небольшого числа обращений, что означает, что они не так сильно будут снижать общую локальность профиля.&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qr_2.png|thumb|center|700px|Рисунок 3. Профиль обращений, одна итерация]]&lt;br /&gt;
&lt;br /&gt;
Далее, фрагмент 1 и 3 устроены практически идентично (основное отличие – два основных этапа переставлены местами), поэтому рассмотрим только один из них. На рис. 4 представлен фрагмент 3. Здесь хорошо видно, как устроены эти этапы. На первом этапе выполняется перебор элементов в обратном порядке с шагом 1, при этом к каждому элементу подряд обращаются несколько раз. Такой фрагмент обладает очень высокой пространственной локальностью и достаточно высокой временной локальностью. &lt;br /&gt;
&lt;br /&gt;
[[file:householder_qr_3.png|thumb|center|500px|Рисунок 4. Профиль обращений, одна итерация, фрагмент 3]]&lt;br /&gt;
&lt;br /&gt;
На втором этапе также выполняется последовательный перебор, но уже с небольшим шагом, и к каждому элементу обращаются только по 1 разу на каждой итерации. Однако, поскольку в каждой небольшой итерации на втором этапе всего около 30 обращений, повторное обращение к каждому элементу происходит недалеко от предыдущего, что говорит о достаточно высокой временной локальности. Пространственная локальность также высока, поскольку шаг по памяти невелик.&lt;br /&gt;
&lt;br /&gt;
Далее рассмотрим фрагмент 4. Чтобы лучше понять локальную структуру, детально изучим небольшую его часть (рис. 5). Из данного рисунка хорошо видно, что здесь профиль устроен очень просто и представляет собой обычный последовательный перебор с шагом 1 (с небольшими и нечастыми сдвигами, которые не влияют на локальность этого фрагмента). Такой фрагмент обладает высокой пространственной, но низкой временной локальностью.&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qr_4.png|thumb|center|500px|Рисунок 5. Профиль обращений, одна итерация, часть фрагмента 4]]&lt;br /&gt;
&lt;br /&gt;
В целом можно сказать, что одна итерация обладает высокой пространственной и средней временной локальностью, хотя она несколько снижается из-за наличия фрагментов 2, 4 и 5. При условии, что итерации подобны, выводы относительно локальности одной итерации можно перенести и на общий профиль.&lt;br /&gt;
&lt;br /&gt;
===== Количественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
Оценка выполняется на основе характеристики daps, которая оценивает число выполненных обращений (чтений и записей) в память в секунду. Данная характеристика является аналогом оценки flops применительно к работе с памятью и является в большей степени оценкой производительности взаимодействия с памятью, чем оценкой локальности. Однако она служит хорошим источником информации, в том числе для сравнения с результатами по следующей характеристике cvg.&lt;br /&gt;
&lt;br /&gt;
На рисунке 6 приведены значения daps для реализаций распространенных алгоритмов, отсортированные по возрастанию (чем больше daps, тем в общем случае выше производительность). Можно увидеть, что производительность работы с памятью в данном примере высока. Значение daps для него находится на одном уровне с тестом Linpack и лишь немногим ниже, чем, например, самые эффективные варианты перемножения плотных матриц. Это означает, что негативное влияние фрагментов 2 и 5 оказывается достаточно слабым, вероятно (как и предполагалось ранее) из-за небольшого их размера.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qr_daps.png|thumb|center|700px|Рисунок 6. Сравнение значений оценки daps]]&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
==== Масштабируемость алгоритма ====&lt;br /&gt;
==== Масштабируемость реализации алгоритма ====&lt;br /&gt;
Проведём исследование масштабируемости вширь реализации согласно [[Scalability methodology|методике]]. Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов-2&amp;quot; [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Набор и границы значений изменяемых [[Глоссарий#Параметры запуска|параметров запуска]] реализации алгоритма: &lt;br /&gt;
&lt;br /&gt;
*	число процессоров [16:100] с шагом n^2;&lt;br /&gt;
*	размер матрицы [1000 : 16000] с шагом 1000.&lt;br /&gt;
&lt;br /&gt;
В результате проведённых экспериментов был получен следующий диапазон [[Глоссарий#Эффективность реализации|эффективности реализации]] алгоритма:&lt;br /&gt;
&lt;br /&gt;
*	минимальная эффективность реализации 0.00012%;&lt;br /&gt;
*	максимальная эффективность реализации 0.0416%.&lt;br /&gt;
&lt;br /&gt;
На следующих рисунках приведены графики [[Глоссарий#Производительность|производительности]] и эффективности выбранной реализации алгоритма в процедуре PDGEQRF из Scalapack в зависимости от изменяемых параметров запуска.&lt;br /&gt;
&lt;br /&gt;
[[file:PDGEQRF perf2.png|thumb|center|700px|Рисунок 7. Параллельная реализация метода Хаусхолдера QR-разложения. Изменение производительности в зависимости от числа процессоров и размера матрицы.]]&lt;br /&gt;
[[file:PDGEQRF eff2.png|thumb|center|700px|Рисунок 8. Параллельная реализация метода Хаусхолдера QR-разложения. Изменение эффективности в зависимости от числа процессоров и размера матрицы.]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
В сравнении с методом Гивенса, который имеет естественное двумерное блочное разбиение на основе точечного метода, метод Хаусхолдера из-за худших характеристик локальности (наличие пучков рассылок) и меньшего количества независимых обобщённых развёрток графа не так хорош для реализаций на системах с распределённой памятью, как для систем с общей памятью. Поэтому на массово параллельных системах с распределённой памятью следует применять метод Хаусхолдера (если уж именно его нужно реализовать) не в точечной версии, а в разрабатываемых исследователями блочных вариантах. Следует отметить, что эти варианты - не блочная нарезка описанного метода, а самостоятельные методы. Особенно их применение рекомендуется в случаях с большой разрежённостью матрицы.&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Большинство пакетов от LINPACKа и LAPACKa до SCALAPACKa используют для QR-разложения матриц именно метод Хаусхолдера, правда, в различных модификациях (обычно с использованием BLAS). Существует большая подборка исследовательских работ по блочным версиям.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Статьи в работе]]&lt;br /&gt;
[[Категория:Разложения матриц]]&lt;/div&gt;</summary>
		<author><name>Teplov</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D0%A5%D0%B0%D1%83%D1%81%D1%85%D0%BE%D0%BB%D0%B4%D0%B5%D1%80%D0%B0_(%D0%BE%D1%82%D1%80%D0%B0%D0%B6%D0%B5%D0%BD%D0%B8%D0%B9)_QR-%D1%80%D0%B0%D0%B7%D0%BB%D0%BE%D0%B6%D0%B5%D0%BD%D0%B8%D1%8F_%D0%BA%D0%B2%D0%B0%D0%B4%D1%80%D0%B0%D1%82%D0%BD%D0%BE%D0%B9_%D0%BC%D0%B0%D1%82%D1%80%D0%B8%D1%86%D1%8B,_%D0%B2%D0%B5%D1%89%D0%B5%D1%81%D1%82%D0%B2%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D1%82%D0%BE%D1%87%D0%B5%D1%87%D0%BD%D1%8B%D0%B9_%D0%B2%D0%B0%D1%80%D0%B8%D0%B0%D0%BD%D1%82&amp;diff=21532</id>
		<title>Метод Хаусхолдера (отражений) QR-разложения квадратной матрицы, вещественный точечный вариант</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D0%A5%D0%B0%D1%83%D1%81%D1%85%D0%BE%D0%BB%D0%B4%D0%B5%D1%80%D0%B0_(%D0%BE%D1%82%D1%80%D0%B0%D0%B6%D0%B5%D0%BD%D0%B8%D0%B9)_QR-%D1%80%D0%B0%D0%B7%D0%BB%D0%BE%D0%B6%D0%B5%D0%BD%D0%B8%D1%8F_%D0%BA%D0%B2%D0%B0%D0%B4%D1%80%D0%B0%D1%82%D0%BD%D0%BE%D0%B9_%D0%BC%D0%B0%D1%82%D1%80%D0%B8%D1%86%D1%8B,_%D0%B2%D0%B5%D1%89%D0%B5%D1%81%D1%82%D0%B2%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D1%82%D0%BE%D1%87%D0%B5%D1%87%D0%BD%D1%8B%D0%B9_%D0%B2%D0%B0%D1%80%D0%B8%D0%B0%D0%BD%D1%82&amp;diff=21532"/>
		<updated>2017-01-10T13:13:27Z</updated>

		<summary type="html">&lt;p&gt;Teplov: /* Масштабируемость реализации алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = QR-разложение методом Хаусхолдера (отражений)&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;\frac{4 n^3}{3}&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;n(n + 1)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
Основные авторы описания: [[Участник:Frolov|А.В.Фролов]]&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Метод Хаусхолдера&#039;&#039;&#039; (в советской математической литературе чаще называется &#039;&#039;&#039;методом отражений&#039;&#039;&#039;) используется для разложения  матриц в виде &amp;lt;math&amp;gt;A=QR&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; - унитарная, &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; — правая треугольная матрица)&amp;lt;ref&amp;gt;В.В.Воеводин, Ю.А.Кузнецов. Матрицы и вычисления. М.: Наука, 1984.&amp;lt;/ref&amp;gt;. При этом матрица &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; хранится и используется не в своём явном виде, а в виде произведения матриц отражения&amp;lt;ref name=&amp;quot;VOLA&amp;quot;&amp;gt;Воеводин В.В. Вычислительные основы линейной алгебры. М.: Наука, 1977.&amp;lt;/ref&amp;gt;. Каждая из матриц отражения может быть определена одним вектором. Это позволяет в классическом исполнении метода отражений хранить результаты разложения на месте матрицы A с использованием минимального одномерного дополнительного массива. &lt;br /&gt;
&lt;br /&gt;
В данной статье рассматривается именно классическое исполнение, в котором не используются приёмы типа сдваивания при вычислениях скалярных произведений.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В методе Хаусхолдера для выполнения &amp;lt;math&amp;gt;QR&amp;lt;/math&amp;gt;-разложения матрицы используются умножения слева её текущих модификаций на матрицы Хаусхолдера (отражений). &lt;br /&gt;
&lt;br /&gt;
{{Шаблон:Матрица отражений}}&lt;br /&gt;
&lt;br /&gt;
На &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м шаге метода с помощью преобразования отражения &amp;quot;убираются&amp;quot; ненулевые поддиагональные элементы в &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м столбце. Таким образом, после &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; шагов преобразований получается матрица &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; из &amp;lt;math&amp;gt;QR&amp;lt;/math&amp;gt;-разложения.&lt;br /&gt;
&lt;br /&gt;
На каждом из шагов метода матрицу отражений обычно представляют не в стандартном виде, а в виде &amp;lt;math&amp;gt;U=E-\frac{1}{\gamma}vv^*&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; находится через координаты текущего &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; - вектор размерности &amp;lt;math&amp;gt;n+1-i&amp;lt;/math&amp;gt;, составленный из элементов &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца, начиная с &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;(s,s)=0&amp;lt;/math&amp;gt;, то &amp;lt;math&amp;gt;v=e_{i}&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\gamma = \frac{1}{2}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В остальных случаях по алгоритму вычисляется &amp;lt;math&amp;gt;u = \frac{1}{\sqrt{(s,s)}}s&amp;lt;/math&amp;gt;, и далее &amp;lt;math&amp;gt;v_{j}=0&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;j&amp;lt;i&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;v_{j}=u_{j-i+1}&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;j&amp;gt;i&amp;lt;/math&amp;gt;, а &amp;lt;math&amp;gt;v_{i}=1&amp;lt;/math&amp;gt;, если &amp;lt;math&amp;gt;u_{1}=0&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v_{i}=\frac{u_{1}}{|u_{1}|}(1+|u_{1}|)&amp;lt;/math&amp;gt; для остальных значений. При этом &amp;lt;math&amp;gt;\gamma =1+|u_{1}|=|v_{i}|&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
После вычисления вектора &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; подстолбцы справа от ведущего модифицируются по формулам &amp;lt;math&amp;gt;x&#039;=x-\frac{(x,v)}{\gamma}v&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Основную часть алгоритма составляют вычисления на каждом шагу скалярных произведений &amp;lt;math&amp;gt;(s,s)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(x,v)&amp;lt;/math&amp;gt; для всех подстолбцов &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt;справа от текущего, а также векторные операции &amp;lt;math&amp;gt;x&#039;=x-\frac{(x,v)}{\gamma}v&amp;lt;/math&amp;gt;. Это используется при программировании метода во многих библиотеках для его конструирования из стандартных подпрограмм (например, из BLAS).&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
[[File:HausLeft1.png|thumb|right|600px|Рисунок 1. Граф шага (обнуление &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;го столбца) алгоритма. Квадратики - входные данные шага (берутся с предыдущего или из входных данных), кружки - операции. Зелёным выделены операции типа a+bb, салатовым и голубым - типа a+bc, тёмно-синим - вычисление &amp;lt;math&amp;gt;\gamma , v_{1}&amp;lt;/math&amp;gt;, жёлтым - умножения (или деления). Обведённая группа операций повторяется независимо n-i раз. Результаты синего, красных и жёлтых кружков, а на последнем шаге и голубого - выходные для алгоритма.]]&lt;br /&gt;
&lt;br /&gt;
Как уже сказано в описании ядра, основная часть - вычисления на каждом шагу скалярных произведений &amp;lt;math&amp;gt;(s,s)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(x,v)&amp;lt;/math&amp;gt; для всех подстолбцов &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; справа от текущего, а также векторные операции &amp;lt;math&amp;gt;x&#039;=x-\frac{(x,v)}{\gamma}v&amp;lt;/math&amp;gt;. При этом, однако, строгая последовательность выполнения этих трёх подшагов не обязательна, в силу связи получаемых векторов &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; можно одновременно с &amp;lt;math&amp;gt;(s,s)&amp;lt;/math&amp;gt; вычислять и произведения &amp;lt;math&amp;gt;(x,s)&amp;lt;/math&amp;gt; с последующим выражением через них &amp;lt;math&amp;gt;(x,v)&amp;lt;/math&amp;gt;. Это позволяет почти вдвое уменьшать критический путь графа алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Последовательность выполнения алгоритма обычно записывается как последовательное &amp;quot;обнуление&amp;quot; поддиагональных элементов столбцов, начиная с 1-го столбца и заканчивая предпоследним &amp;lt;math&amp;gt;(n-1)&amp;lt;/math&amp;gt;-м.&lt;br /&gt;
&lt;br /&gt;
При этом в каждом &amp;quot;обнуляемом&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м столбце &amp;quot;обнуляются&amp;quot; сразу все его поддиагональные элементы одновременно, с &amp;lt;math&amp;gt;(i+1)&amp;lt;/math&amp;gt;-го до &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;-го. &lt;br /&gt;
&lt;br /&gt;
Каждое &amp;quot;обнуление&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца состоит из двух шагов: &lt;br /&gt;
а) вычисление параметров матрицы отражения &amp;lt;math&amp;gt;U_{i}&amp;lt;/math&amp;gt; такой, чтобы при умножении на неё слева &amp;quot;обнулились&amp;quot; все поддиагональные его элементы;&lt;br /&gt;
б) умножение слева матрицы отражения &amp;lt;math&amp;gt;U_{i}&amp;lt;/math&amp;gt; на текущую версию матрицы.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В последовательной версии основная сложность алгоритма определяется прежде всего вычислениями скалярных произведений векторов, а также модификаций векторов вида &amp;lt;math&amp;gt;x&#039;=x-\alpha v&amp;lt;/math&amp;gt;, причем над векторами убывающей по ходу алгоритма размерности. Они, если не учитывать возможную разреженность, составляют (в главном члене) по &amp;lt;math&amp;gt;2n^3/3&amp;lt;/math&amp;gt; операций действительного умножения и сложения/вычитания.&lt;br /&gt;
&lt;br /&gt;
При классификации по последовательной сложности, таким образом, метод Хаусхолдера относится к алгоритмам &#039;&#039;с кубической сложностью&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
На Рисунке 1 приведён шаг графа алгоритма метода Хаусхолдера в наиболее его быстром (с параллельной точки зрения) варианте, использующем то, что с точностью до множителя ведущий вектор матрицы отражения отличается отличается от подстолбца, где выполняется очередное исключение, только одним элементом. Операции привязаны к обрабатываемым элементам матрицы. Для получения полного графа графы шагов нужно положить друг на друга последовательными слоями, при этом правые нижние углы должны быть друг над другом.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Для понимания ресурса параллелизма в разложении матрицы порядка &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; методом Хаусхолдера нужно рассмотреть критический путь графа. &lt;br /&gt;
&lt;br /&gt;
Как видно из описания разных вершин, вычисления при &amp;quot;обнулении&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца параметров отражения и скалярных произведений состоят из основной части - ветви длиной по &amp;lt;math&amp;gt;n-i&amp;lt;/math&amp;gt; умножений и сложений - и коррекции вычислений, которые составляют &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; операций.&lt;br /&gt;
&lt;br /&gt;
Поэтому по грубой (без членов низших порядков) оценке критический путь метода Хаусхолдера будет идти через &amp;lt;math&amp;gt;\frac{n^2}{2}&amp;lt;/math&amp;gt; умножений и &amp;lt;math&amp;gt;\frac{n^2}{2}&amp;lt;/math&amp;gt; сложений/вычитаний. &lt;br /&gt;
 &lt;br /&gt;
Поэтому в параллельном варианте, как и в последовательном, основную долю требуемого для выполнения алгоритма времени будут определять операции вида &amp;lt;math&amp;gt;a+bc&amp;lt;/math&amp;gt;.  &lt;br /&gt;
&lt;br /&gt;
При классификации по высоте ЯПФ, таким образом, метод Хаусхолдера относится к алгоритмам &#039;&#039;с квадратичной сложностью&#039;&#039;. При классификации по ширине ЯПФ его сложность будет также &#039;&#039;квадратичной&#039;&#039; (без расширения ряда ярусов, связанных с векторными операциями сложения, пришлось бы увеличить вдвое длину критического пути; при таком расширении сложность по ширине ЯПФ станет &#039;&#039;линейной&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
Надо сказать, что здесь в оценках речь идёт именно о классическом способе реализации метода Хаусхолдера. Даже использование схем сдваивания или последовательно-параллельных для вычисления скалярных произведений уменьшает критический путь с квадратичного до &#039;&#039;степени 3/2&#039;&#039; или &#039;&#039;линейно-логарифмического&#039;&#039;. Однако все эти широко распространённые методы пока не дали возможности снизить критический путь метода Хаусхолдера до &#039;&#039;линейного&#039;&#039; (как, скажем, у метода Гивенса).&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: плотная квадратная матрица &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; (элементы &amp;lt;math&amp;gt;a_{ij}&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: правая треугольная матрица &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; (ненулевые элементы &amp;lt;math&amp;gt;r_{ij}&amp;lt;/math&amp;gt; в последовательном варианте хранятся в элементах исходной матрицы &amp;lt;math&amp;gt;a_{ij}&amp;lt;/math&amp;gt;), унитарная (ортогональная) матрица Q - как произведение матриц Хаусхолдера (отражения) (их вектора нормалей к плоскостям отражения в последовательном варианте хранятся в поддиагональных элементах исходной матрицы &amp;lt;math&amp;gt;a_{ij}&amp;lt;/math&amp;gt; и в одном дополнительном столбце размерности n).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;n^2+n&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Соотношение последовательной и параллельной сложности, как хорошо видно, является &#039;&#039;линейным&#039;&#039;, что даёт определённый стимул для распараллеливания. Однако у наискорейшей ЯПФ ширина &#039;&#039;квадратична&#039;&#039;, что указывает на дисбаланс между загруженностями устройств при попытке её реально запрограммировать. Поэтому более практично даже при хорошей (быстрой) вычислительной сети оставить количество устройств (например, узлов кластера) &#039;&#039;линейным&#039;&#039; по размеру матрицы, что удвоит критический путь реализуемой ЯПФ. &lt;br /&gt;
&lt;br /&gt;
При этом вычислительная мощность алгоритма, как отношение числа операций к суммарному объему входных и выходных данных, &#039;&#039;линейна&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Алгоритм в рамках выбранной версии полностью детерминирован.&lt;br /&gt;
&lt;br /&gt;
Вычислительная погрешность в методе отражений (Хаусхолдера) растет &#039;&#039;линейно&#039;&#039;, как и в методе Гивенса (вращений).&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В варианте с кратчайшим критическим путём графа алгоритма (с использованием зависимости между обнуляемым вектором и направляющим вектором отражения) метод Хаусхолдера (отражений) QR-разложения квадратной вещественной матрицы на Фортране 77 можно записать так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;fortran&amp;quot;&amp;gt;&lt;br /&gt;
	DO  I = 1, N-1&lt;br /&gt;
            DO K = I, N&lt;br /&gt;
              SX(K)=A(N,I)*A(N,K)&lt;br /&gt;
            END DO&lt;br /&gt;
            DO J = N-1, I, -1&lt;br /&gt;
               DO K = I, N&lt;br /&gt;
                  SX(K)=SX(K)+A(J,I)*A(J,K)&lt;br /&gt;
               END DO&lt;br /&gt;
            END DO&lt;br /&gt;
&lt;br /&gt;
               ALPHA = SQRT (SX(I))&lt;br /&gt;
               IF (A(I,I).NE.0.) THEN&lt;br /&gt;
                  BETA = 1./ALPHA&lt;br /&gt;
                  DO J = I+1, N&lt;br /&gt;
                    A(J,I)=A(J,I)*BETA&lt;br /&gt;
                  END DO&lt;br /&gt;
                  SX(I) = A(I,I)*BETA+SIGN(1.,A(I,I))                     &lt;br /&gt;
                  A(I,I)=ALPHA&lt;br /&gt;
                  G=1./ABS(SX(I)) ! 1/gamma&lt;br /&gt;
                  DO K = I+1, N&lt;br /&gt;
                     SX(K)=SX(K)*BETA*G+SIGN(A(I,K),SX(I))&lt;br /&gt;
                     A(I,K) = A(I,K)+SX(K)*SX(I)&lt;br /&gt;
                     DO J = I+1, N&lt;br /&gt;
                       A (J,K) = A(J,K)-A(J,I)*SX(K)&lt;br /&gt;
                     END DO&lt;br /&gt;
                  END DO&lt;br /&gt;
               ELSE&lt;br /&gt;
                  IF (ALPHA.NE.0.) THEN&lt;br /&gt;
                     BETA = 1./ALPHA&lt;br /&gt;
                     DO J = I+1, N&lt;br /&gt;
                       A(J,I)=A(J,I)*BETA&lt;br /&gt;
                     END DO&lt;br /&gt;
                     SX(I) = -1. &lt;br /&gt;
                     A(I,I)=ALPHA&lt;br /&gt;
                     G=1.! 1/gamma&lt;br /&gt;
                     DO K = I+1, N&lt;br /&gt;
                        SX(K)=SX(K)*BETA*G+SIGN(A(I,K),SX(I))&lt;br /&gt;
                        A(I,K) = A(I,K)+SX(K)*SX(I)&lt;br /&gt;
                        DO J = I+1, N&lt;br /&gt;
                          A (J,K) = A(J,K)-A(J,I)*SX(K)&lt;br /&gt;
                        END DO&lt;br /&gt;
                     END DO&lt;br /&gt;
                  ELSE&lt;br /&gt;
                     SX(I)=1&lt;br /&gt;
                     G=2.&lt;br /&gt;
                     DO K = I+1, N&lt;br /&gt;
                        SX(K)=2.&lt;br /&gt;
                        A(I,K) = A(I,K)-SX(K)&lt;br /&gt;
                     END DO&lt;br /&gt;
                  END IF&lt;br /&gt;
               END IF               &lt;br /&gt;
&lt;br /&gt;
		&lt;br /&gt;
	END DO&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В этом варианте R расположена в верхнем правом треугольнике массива A, направляющие вектора матриц отражений размещены в поддиагональных элементах соответствующих столбцов, а их первые элементы - в элементах массива SX.&lt;br /&gt;
&lt;br /&gt;
Обычно же в последовательных версиях коэффициенты модификаций столбцов вычисляются целиком через скалярные произведения после вычислений параметров матрицы отражения. При этом схема чуть проще. Удлиняется критический путь графа, но для последовательных реализаций это неважно.&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
К сожалению, в графе, как видно по рисунку, в наличии пучки рассылок, в них неизбежно часть дуг остаются длинными, что отрицательно влияет на локальность вычислений по пространству.  &lt;br /&gt;
&lt;br /&gt;
==== Локальность реализации алгоритма ====&lt;br /&gt;
===== Структура обращений в память и качественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qr_1.png|thumb|center|700px|Рисунок 2. Метод Хаусхолдера (отражений) QR-разложения квадратной матрицы, вещественный вариант. Общий профиль обращений в память]]&lt;br /&gt;
&lt;br /&gt;
На рис. 2 представлен профиль обращений в память для реализации вещественного варианта метода Хаусхолдера (отражений) QR-разложения квадратной матрицы. Из данного рисунка можно сделать несколько выводов. Во-первых, видно, что в среднем к каждому элементу выполняется примерно 90 обращений (отношение общего числа обращений к числу задействованных данных), что является достаточно хорошим показателем в плане повторного использования данных. Далее, явно видна итерационная структура профиля, причем на каждой следующей итерации обращения к небольшой части первых элементов перестают выполняться. Это говорит о том, что ближе к концу профиля задействовано меньше данных, что хорошо сказывается на пространственной локальности. Однако в то же время видно, что на каждой следующей итерации выполняется меньше обращений к одним и тем же локальным областям, то есть временная локальность, по всей вероятности, становится ниже. Рассмотрим подробнее первую итерацию, что более детально оценить их строение.&lt;br /&gt;
&lt;br /&gt;
На рис. 3 представлена одна итерация общего профиля (выделенный зеленый фрагмент на рис. 2). Ее можно условно разбить на 5 фрагментов, каждую из которых рассмотреть отдельно. Фрагменты 2 и 5 устроены просто – элементы перебираются с большим шагом по памяти, при достижении конца данных перебор повторяется, только с небольшим сдвигом. Про такие фрагменты можно сказать, что они обладают низкой пространственной локальностью, поскольку шаг по памяти достаточно большой, и низкой временной локальностью, так как данные повторно не используются. Однако отметим, что данные фрагменты состоят из небольшого числа обращений, что означает, что они не так сильно будут снижать общую локальность профиля.&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qr_2.png|thumb|center|700px|Рисунок 3. Профиль обращений, одна итерация]]&lt;br /&gt;
&lt;br /&gt;
Далее, фрагмент 1 и 3 устроены практически идентично (основное отличие – два основных этапа переставлены местами), поэтому рассмотрим только один из них. На рис. 4 представлен фрагмент 3. Здесь хорошо видно, как устроены эти этапы. На первом этапе выполняется перебор элементов в обратном порядке с шагом 1, при этом к каждому элементу подряд обращаются несколько раз. Такой фрагмент обладает очень высокой пространственной локальностью и достаточно высокой временной локальностью. &lt;br /&gt;
&lt;br /&gt;
[[file:householder_qr_3.png|thumb|center|500px|Рисунок 4. Профиль обращений, одна итерация, фрагмент 3]]&lt;br /&gt;
&lt;br /&gt;
На втором этапе также выполняется последовательный перебор, но уже с небольшим шагом, и к каждому элементу обращаются только по 1 разу на каждой итерации. Однако, поскольку в каждой небольшой итерации на втором этапе всего около 30 обращений, повторное обращение к каждому элементу происходит недалеко от предыдущего, что говорит о достаточно высокой временной локальности. Пространственная локальность также высока, поскольку шаг по памяти невелик.&lt;br /&gt;
&lt;br /&gt;
Далее рассмотрим фрагмент 4. Чтобы лучше понять локальную структуру, детально изучим небольшую его часть (рис. 5). Из данного рисунка хорошо видно, что здесь профиль устроен очень просто и представляет собой обычный последовательный перебор с шагом 1 (с небольшими и нечастыми сдвигами, которые не влияют на локальность этого фрагмента). Такой фрагмент обладает высокой пространственной, но низкой временной локальностью.&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qr_4.png|thumb|center|500px|Рисунок 5. Профиль обращений, одна итерация, часть фрагмента 4]]&lt;br /&gt;
&lt;br /&gt;
В целом можно сказать, что одна итерация обладает высокой пространственной и средней временной локальностью, хотя она несколько снижается из-за наличия фрагментов 2, 4 и 5. При условии, что итерации подобны, выводы относительно локальности одной итерации можно перенести и на общий профиль.&lt;br /&gt;
&lt;br /&gt;
===== Количественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
Оценка выполняется на основе характеристики daps, которая оценивает число выполненных обращений (чтений и записей) в память в секунду. Данная характеристика является аналогом оценки flops применительно к работе с памятью и является в большей степени оценкой производительности взаимодействия с памятью, чем оценкой локальности. Однако она служит хорошим источником информации, в том числе для сравнения с результатами по следующей характеристике cvg.&lt;br /&gt;
&lt;br /&gt;
На рисунке 6 приведены значения daps для реализаций распространенных алгоритмов, отсортированные по возрастанию (чем больше daps, тем в общем случае выше производительность). Можно увидеть, что производительность работы с памятью в данном примере высока. Значение daps для него находится на одном уровне с тестом Linpack и лишь немногим ниже, чем, например, самые эффективные варианты перемножения плотных матриц. Это означает, что негативное влияние фрагментов 2 и 5 оказывается достаточно слабым, вероятно (как и предполагалось ранее) из-за небольшого их размера.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qr_daps.png|thumb|center|700px|Рисунок 6. Сравнение значений оценки daps]]&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
==== Масштабируемость алгоритма ====&lt;br /&gt;
==== Масштабируемость реализации алгоритма ====&lt;br /&gt;
Проведём исследование масштабируемости вширь реализации согласно [[Scalability methodology|методике]]. Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов-2&amp;quot; [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Набор и границы значений изменяемых [[Глоссарий#Параметры запуска|параметров запуска]] реализации алгоритма: &lt;br /&gt;
&lt;br /&gt;
*	число процессоров [16:100] с шагом n^2;&lt;br /&gt;
*	размер матрицы [1000 : 16000] с шагом 1000.&lt;br /&gt;
&lt;br /&gt;
В результате проведённых экспериментов был получен следующий диапазон [[Глоссарий#Эффективность реализации|эффективности реализации]] алгоритма:&lt;br /&gt;
&lt;br /&gt;
*	минимальная эффективность реализации 0.00012%;&lt;br /&gt;
*	максимальная эффективность реализации 0.0416%.&lt;br /&gt;
&lt;br /&gt;
На следующих рисунках приведены графики [[Глоссарий#Производительность|производительности]] и эффективности выбранной реализации алгоритма в процедуре PDGEQRF из Scalapack в зависимости от изменяемых параметров запуска.&lt;br /&gt;
&lt;br /&gt;
[[file:PDGEQRF perf2.png|thumb|center|700px|Рисунок 8. Параллельная реализация метода Хаусхолдера QR-разложения. Изменение производительности в зависимости от числа процессоров и размера матрицы.]]&lt;br /&gt;
[[file:PDGEQRF eff2.png|thumb|center|700px|Рисунок 9. Параллельная реализация метода Хаусхолдера QR-разложения. Изменение эффективности в зависимости от числа процессоров и размера матрицы.]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
В сравнении с методом Гивенса, который имеет естественное двумерное блочное разбиение на основе точечного метода, метод Хаусхолдера из-за худших характеристик локальности (наличие пучков рассылок) и меньшего количества независимых обобщённых развёрток графа не так хорош для реализаций на системах с распределённой памятью, как для систем с общей памятью. Поэтому на массово параллельных системах с распределённой памятью следует применять метод Хаусхолдера (если уж именно его нужно реализовать) не в точечной версии, а в разрабатываемых исследователями блочных вариантах. Следует отметить, что эти варианты - не блочная нарезка описанного метода, а самостоятельные методы. Особенно их применение рекомендуется в случаях с большой разрежённостью матрицы.&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Большинство пакетов от LINPACKа и LAPACKa до SCALAPACKa используют для QR-разложения матриц именно метод Хаусхолдера, правда, в различных модификациях (обычно с использованием BLAS). Существует большая подборка исследовательских работ по блочным версиям.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Статьи в работе]]&lt;br /&gt;
[[Категория:Разложения матриц]]&lt;/div&gt;</summary>
		<author><name>Teplov</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D0%A5%D0%B0%D1%83%D1%81%D1%85%D0%BE%D0%BB%D0%B4%D0%B5%D1%80%D0%B0_(%D0%BE%D1%82%D1%80%D0%B0%D0%B6%D0%B5%D0%BD%D0%B8%D0%B9)_QR-%D1%80%D0%B0%D0%B7%D0%BB%D0%BE%D0%B6%D0%B5%D0%BD%D0%B8%D1%8F_%D0%BA%D0%B2%D0%B0%D0%B4%D1%80%D0%B0%D1%82%D0%BD%D0%BE%D0%B9_%D0%BC%D0%B0%D1%82%D1%80%D0%B8%D1%86%D1%8B,_%D0%B2%D0%B5%D1%89%D0%B5%D1%81%D1%82%D0%B2%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D1%82%D0%BE%D1%87%D0%B5%D1%87%D0%BD%D1%8B%D0%B9_%D0%B2%D0%B0%D1%80%D0%B8%D0%B0%D0%BD%D1%82&amp;diff=21531</id>
		<title>Метод Хаусхолдера (отражений) QR-разложения квадратной матрицы, вещественный точечный вариант</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D0%A5%D0%B0%D1%83%D1%81%D1%85%D0%BE%D0%BB%D0%B4%D0%B5%D1%80%D0%B0_(%D0%BE%D1%82%D1%80%D0%B0%D0%B6%D0%B5%D0%BD%D0%B8%D0%B9)_QR-%D1%80%D0%B0%D0%B7%D0%BB%D0%BE%D0%B6%D0%B5%D0%BD%D0%B8%D1%8F_%D0%BA%D0%B2%D0%B0%D0%B4%D1%80%D0%B0%D1%82%D0%BD%D0%BE%D0%B9_%D0%BC%D0%B0%D1%82%D1%80%D0%B8%D1%86%D1%8B,_%D0%B2%D0%B5%D1%89%D0%B5%D1%81%D1%82%D0%B2%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D1%82%D0%BE%D1%87%D0%B5%D1%87%D0%BD%D1%8B%D0%B9_%D0%B2%D0%B0%D1%80%D0%B8%D0%B0%D0%BD%D1%82&amp;diff=21531"/>
		<updated>2017-01-10T13:12:08Z</updated>

		<summary type="html">&lt;p&gt;Teplov: /* Масштабируемость реализации алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = QR-разложение методом Хаусхолдера (отражений)&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;\frac{4 n^3}{3}&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;n(n + 1)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
Основные авторы описания: [[Участник:Frolov|А.В.Фролов]]&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Метод Хаусхолдера&#039;&#039;&#039; (в советской математической литературе чаще называется &#039;&#039;&#039;методом отражений&#039;&#039;&#039;) используется для разложения  матриц в виде &amp;lt;math&amp;gt;A=QR&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; - унитарная, &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; — правая треугольная матрица)&amp;lt;ref&amp;gt;В.В.Воеводин, Ю.А.Кузнецов. Матрицы и вычисления. М.: Наука, 1984.&amp;lt;/ref&amp;gt;. При этом матрица &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; хранится и используется не в своём явном виде, а в виде произведения матриц отражения&amp;lt;ref name=&amp;quot;VOLA&amp;quot;&amp;gt;Воеводин В.В. Вычислительные основы линейной алгебры. М.: Наука, 1977.&amp;lt;/ref&amp;gt;. Каждая из матриц отражения может быть определена одним вектором. Это позволяет в классическом исполнении метода отражений хранить результаты разложения на месте матрицы A с использованием минимального одномерного дополнительного массива. &lt;br /&gt;
&lt;br /&gt;
В данной статье рассматривается именно классическое исполнение, в котором не используются приёмы типа сдваивания при вычислениях скалярных произведений.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В методе Хаусхолдера для выполнения &amp;lt;math&amp;gt;QR&amp;lt;/math&amp;gt;-разложения матрицы используются умножения слева её текущих модификаций на матрицы Хаусхолдера (отражений). &lt;br /&gt;
&lt;br /&gt;
{{Шаблон:Матрица отражений}}&lt;br /&gt;
&lt;br /&gt;
На &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м шаге метода с помощью преобразования отражения &amp;quot;убираются&amp;quot; ненулевые поддиагональные элементы в &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м столбце. Таким образом, после &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; шагов преобразований получается матрица &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; из &amp;lt;math&amp;gt;QR&amp;lt;/math&amp;gt;-разложения.&lt;br /&gt;
&lt;br /&gt;
На каждом из шагов метода матрицу отражений обычно представляют не в стандартном виде, а в виде &amp;lt;math&amp;gt;U=E-\frac{1}{\gamma}vv^*&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; находится через координаты текущего &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; - вектор размерности &amp;lt;math&amp;gt;n+1-i&amp;lt;/math&amp;gt;, составленный из элементов &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца, начиная с &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;(s,s)=0&amp;lt;/math&amp;gt;, то &amp;lt;math&amp;gt;v=e_{i}&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\gamma = \frac{1}{2}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В остальных случаях по алгоритму вычисляется &amp;lt;math&amp;gt;u = \frac{1}{\sqrt{(s,s)}}s&amp;lt;/math&amp;gt;, и далее &amp;lt;math&amp;gt;v_{j}=0&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;j&amp;lt;i&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;v_{j}=u_{j-i+1}&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;j&amp;gt;i&amp;lt;/math&amp;gt;, а &amp;lt;math&amp;gt;v_{i}=1&amp;lt;/math&amp;gt;, если &amp;lt;math&amp;gt;u_{1}=0&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v_{i}=\frac{u_{1}}{|u_{1}|}(1+|u_{1}|)&amp;lt;/math&amp;gt; для остальных значений. При этом &amp;lt;math&amp;gt;\gamma =1+|u_{1}|=|v_{i}|&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
После вычисления вектора &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; подстолбцы справа от ведущего модифицируются по формулам &amp;lt;math&amp;gt;x&#039;=x-\frac{(x,v)}{\gamma}v&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Основную часть алгоритма составляют вычисления на каждом шагу скалярных произведений &amp;lt;math&amp;gt;(s,s)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(x,v)&amp;lt;/math&amp;gt; для всех подстолбцов &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt;справа от текущего, а также векторные операции &amp;lt;math&amp;gt;x&#039;=x-\frac{(x,v)}{\gamma}v&amp;lt;/math&amp;gt;. Это используется при программировании метода во многих библиотеках для его конструирования из стандартных подпрограмм (например, из BLAS).&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
[[File:HausLeft1.png|thumb|right|600px|Рисунок 1. Граф шага (обнуление &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;го столбца) алгоритма. Квадратики - входные данные шага (берутся с предыдущего или из входных данных), кружки - операции. Зелёным выделены операции типа a+bb, салатовым и голубым - типа a+bc, тёмно-синим - вычисление &amp;lt;math&amp;gt;\gamma , v_{1}&amp;lt;/math&amp;gt;, жёлтым - умножения (или деления). Обведённая группа операций повторяется независимо n-i раз. Результаты синего, красных и жёлтых кружков, а на последнем шаге и голубого - выходные для алгоритма.]]&lt;br /&gt;
&lt;br /&gt;
Как уже сказано в описании ядра, основная часть - вычисления на каждом шагу скалярных произведений &amp;lt;math&amp;gt;(s,s)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(x,v)&amp;lt;/math&amp;gt; для всех подстолбцов &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; справа от текущего, а также векторные операции &amp;lt;math&amp;gt;x&#039;=x-\frac{(x,v)}{\gamma}v&amp;lt;/math&amp;gt;. При этом, однако, строгая последовательность выполнения этих трёх подшагов не обязательна, в силу связи получаемых векторов &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; можно одновременно с &amp;lt;math&amp;gt;(s,s)&amp;lt;/math&amp;gt; вычислять и произведения &amp;lt;math&amp;gt;(x,s)&amp;lt;/math&amp;gt; с последующим выражением через них &amp;lt;math&amp;gt;(x,v)&amp;lt;/math&amp;gt;. Это позволяет почти вдвое уменьшать критический путь графа алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Последовательность выполнения алгоритма обычно записывается как последовательное &amp;quot;обнуление&amp;quot; поддиагональных элементов столбцов, начиная с 1-го столбца и заканчивая предпоследним &amp;lt;math&amp;gt;(n-1)&amp;lt;/math&amp;gt;-м.&lt;br /&gt;
&lt;br /&gt;
При этом в каждом &amp;quot;обнуляемом&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м столбце &amp;quot;обнуляются&amp;quot; сразу все его поддиагональные элементы одновременно, с &amp;lt;math&amp;gt;(i+1)&amp;lt;/math&amp;gt;-го до &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;-го. &lt;br /&gt;
&lt;br /&gt;
Каждое &amp;quot;обнуление&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца состоит из двух шагов: &lt;br /&gt;
а) вычисление параметров матрицы отражения &amp;lt;math&amp;gt;U_{i}&amp;lt;/math&amp;gt; такой, чтобы при умножении на неё слева &amp;quot;обнулились&amp;quot; все поддиагональные его элементы;&lt;br /&gt;
б) умножение слева матрицы отражения &amp;lt;math&amp;gt;U_{i}&amp;lt;/math&amp;gt; на текущую версию матрицы.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В последовательной версии основная сложность алгоритма определяется прежде всего вычислениями скалярных произведений векторов, а также модификаций векторов вида &amp;lt;math&amp;gt;x&#039;=x-\alpha v&amp;lt;/math&amp;gt;, причем над векторами убывающей по ходу алгоритма размерности. Они, если не учитывать возможную разреженность, составляют (в главном члене) по &amp;lt;math&amp;gt;2n^3/3&amp;lt;/math&amp;gt; операций действительного умножения и сложения/вычитания.&lt;br /&gt;
&lt;br /&gt;
При классификации по последовательной сложности, таким образом, метод Хаусхолдера относится к алгоритмам &#039;&#039;с кубической сложностью&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
На Рисунке 1 приведён шаг графа алгоритма метода Хаусхолдера в наиболее его быстром (с параллельной точки зрения) варианте, использующем то, что с точностью до множителя ведущий вектор матрицы отражения отличается отличается от подстолбца, где выполняется очередное исключение, только одним элементом. Операции привязаны к обрабатываемым элементам матрицы. Для получения полного графа графы шагов нужно положить друг на друга последовательными слоями, при этом правые нижние углы должны быть друг над другом.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Для понимания ресурса параллелизма в разложении матрицы порядка &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; методом Хаусхолдера нужно рассмотреть критический путь графа. &lt;br /&gt;
&lt;br /&gt;
Как видно из описания разных вершин, вычисления при &amp;quot;обнулении&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца параметров отражения и скалярных произведений состоят из основной части - ветви длиной по &amp;lt;math&amp;gt;n-i&amp;lt;/math&amp;gt; умножений и сложений - и коррекции вычислений, которые составляют &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; операций.&lt;br /&gt;
&lt;br /&gt;
Поэтому по грубой (без членов низших порядков) оценке критический путь метода Хаусхолдера будет идти через &amp;lt;math&amp;gt;\frac{n^2}{2}&amp;lt;/math&amp;gt; умножений и &amp;lt;math&amp;gt;\frac{n^2}{2}&amp;lt;/math&amp;gt; сложений/вычитаний. &lt;br /&gt;
 &lt;br /&gt;
Поэтому в параллельном варианте, как и в последовательном, основную долю требуемого для выполнения алгоритма времени будут определять операции вида &amp;lt;math&amp;gt;a+bc&amp;lt;/math&amp;gt;.  &lt;br /&gt;
&lt;br /&gt;
При классификации по высоте ЯПФ, таким образом, метод Хаусхолдера относится к алгоритмам &#039;&#039;с квадратичной сложностью&#039;&#039;. При классификации по ширине ЯПФ его сложность будет также &#039;&#039;квадратичной&#039;&#039; (без расширения ряда ярусов, связанных с векторными операциями сложения, пришлось бы увеличить вдвое длину критического пути; при таком расширении сложность по ширине ЯПФ станет &#039;&#039;линейной&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
Надо сказать, что здесь в оценках речь идёт именно о классическом способе реализации метода Хаусхолдера. Даже использование схем сдваивания или последовательно-параллельных для вычисления скалярных произведений уменьшает критический путь с квадратичного до &#039;&#039;степени 3/2&#039;&#039; или &#039;&#039;линейно-логарифмического&#039;&#039;. Однако все эти широко распространённые методы пока не дали возможности снизить критический путь метода Хаусхолдера до &#039;&#039;линейного&#039;&#039; (как, скажем, у метода Гивенса).&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: плотная квадратная матрица &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; (элементы &amp;lt;math&amp;gt;a_{ij}&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: правая треугольная матрица &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; (ненулевые элементы &amp;lt;math&amp;gt;r_{ij}&amp;lt;/math&amp;gt; в последовательном варианте хранятся в элементах исходной матрицы &amp;lt;math&amp;gt;a_{ij}&amp;lt;/math&amp;gt;), унитарная (ортогональная) матрица Q - как произведение матриц Хаусхолдера (отражения) (их вектора нормалей к плоскостям отражения в последовательном варианте хранятся в поддиагональных элементах исходной матрицы &amp;lt;math&amp;gt;a_{ij}&amp;lt;/math&amp;gt; и в одном дополнительном столбце размерности n).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;n^2+n&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Соотношение последовательной и параллельной сложности, как хорошо видно, является &#039;&#039;линейным&#039;&#039;, что даёт определённый стимул для распараллеливания. Однако у наискорейшей ЯПФ ширина &#039;&#039;квадратична&#039;&#039;, что указывает на дисбаланс между загруженностями устройств при попытке её реально запрограммировать. Поэтому более практично даже при хорошей (быстрой) вычислительной сети оставить количество устройств (например, узлов кластера) &#039;&#039;линейным&#039;&#039; по размеру матрицы, что удвоит критический путь реализуемой ЯПФ. &lt;br /&gt;
&lt;br /&gt;
При этом вычислительная мощность алгоритма, как отношение числа операций к суммарному объему входных и выходных данных, &#039;&#039;линейна&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Алгоритм в рамках выбранной версии полностью детерминирован.&lt;br /&gt;
&lt;br /&gt;
Вычислительная погрешность в методе отражений (Хаусхолдера) растет &#039;&#039;линейно&#039;&#039;, как и в методе Гивенса (вращений).&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В варианте с кратчайшим критическим путём графа алгоритма (с использованием зависимости между обнуляемым вектором и направляющим вектором отражения) метод Хаусхолдера (отражений) QR-разложения квадратной вещественной матрицы на Фортране 77 можно записать так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;fortran&amp;quot;&amp;gt;&lt;br /&gt;
	DO  I = 1, N-1&lt;br /&gt;
            DO K = I, N&lt;br /&gt;
              SX(K)=A(N,I)*A(N,K)&lt;br /&gt;
            END DO&lt;br /&gt;
            DO J = N-1, I, -1&lt;br /&gt;
               DO K = I, N&lt;br /&gt;
                  SX(K)=SX(K)+A(J,I)*A(J,K)&lt;br /&gt;
               END DO&lt;br /&gt;
            END DO&lt;br /&gt;
&lt;br /&gt;
               ALPHA = SQRT (SX(I))&lt;br /&gt;
               IF (A(I,I).NE.0.) THEN&lt;br /&gt;
                  BETA = 1./ALPHA&lt;br /&gt;
                  DO J = I+1, N&lt;br /&gt;
                    A(J,I)=A(J,I)*BETA&lt;br /&gt;
                  END DO&lt;br /&gt;
                  SX(I) = A(I,I)*BETA+SIGN(1.,A(I,I))                     &lt;br /&gt;
                  A(I,I)=ALPHA&lt;br /&gt;
                  G=1./ABS(SX(I)) ! 1/gamma&lt;br /&gt;
                  DO K = I+1, N&lt;br /&gt;
                     SX(K)=SX(K)*BETA*G+SIGN(A(I,K),SX(I))&lt;br /&gt;
                     A(I,K) = A(I,K)+SX(K)*SX(I)&lt;br /&gt;
                     DO J = I+1, N&lt;br /&gt;
                       A (J,K) = A(J,K)-A(J,I)*SX(K)&lt;br /&gt;
                     END DO&lt;br /&gt;
                  END DO&lt;br /&gt;
               ELSE&lt;br /&gt;
                  IF (ALPHA.NE.0.) THEN&lt;br /&gt;
                     BETA = 1./ALPHA&lt;br /&gt;
                     DO J = I+1, N&lt;br /&gt;
                       A(J,I)=A(J,I)*BETA&lt;br /&gt;
                     END DO&lt;br /&gt;
                     SX(I) = -1. &lt;br /&gt;
                     A(I,I)=ALPHA&lt;br /&gt;
                     G=1.! 1/gamma&lt;br /&gt;
                     DO K = I+1, N&lt;br /&gt;
                        SX(K)=SX(K)*BETA*G+SIGN(A(I,K),SX(I))&lt;br /&gt;
                        A(I,K) = A(I,K)+SX(K)*SX(I)&lt;br /&gt;
                        DO J = I+1, N&lt;br /&gt;
                          A (J,K) = A(J,K)-A(J,I)*SX(K)&lt;br /&gt;
                        END DO&lt;br /&gt;
                     END DO&lt;br /&gt;
                  ELSE&lt;br /&gt;
                     SX(I)=1&lt;br /&gt;
                     G=2.&lt;br /&gt;
                     DO K = I+1, N&lt;br /&gt;
                        SX(K)=2.&lt;br /&gt;
                        A(I,K) = A(I,K)-SX(K)&lt;br /&gt;
                     END DO&lt;br /&gt;
                  END IF&lt;br /&gt;
               END IF               &lt;br /&gt;
&lt;br /&gt;
		&lt;br /&gt;
	END DO&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В этом варианте R расположена в верхнем правом треугольнике массива A, направляющие вектора матриц отражений размещены в поддиагональных элементах соответствующих столбцов, а их первые элементы - в элементах массива SX.&lt;br /&gt;
&lt;br /&gt;
Обычно же в последовательных версиях коэффициенты модификаций столбцов вычисляются целиком через скалярные произведения после вычислений параметров матрицы отражения. При этом схема чуть проще. Удлиняется критический путь графа, но для последовательных реализаций это неважно.&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
К сожалению, в графе, как видно по рисунку, в наличии пучки рассылок, в них неизбежно часть дуг остаются длинными, что отрицательно влияет на локальность вычислений по пространству.  &lt;br /&gt;
&lt;br /&gt;
==== Локальность реализации алгоритма ====&lt;br /&gt;
===== Структура обращений в память и качественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qr_1.png|thumb|center|700px|Рисунок 2. Метод Хаусхолдера (отражений) QR-разложения квадратной матрицы, вещественный вариант. Общий профиль обращений в память]]&lt;br /&gt;
&lt;br /&gt;
На рис. 2 представлен профиль обращений в память для реализации вещественного варианта метода Хаусхолдера (отражений) QR-разложения квадратной матрицы. Из данного рисунка можно сделать несколько выводов. Во-первых, видно, что в среднем к каждому элементу выполняется примерно 90 обращений (отношение общего числа обращений к числу задействованных данных), что является достаточно хорошим показателем в плане повторного использования данных. Далее, явно видна итерационная структура профиля, причем на каждой следующей итерации обращения к небольшой части первых элементов перестают выполняться. Это говорит о том, что ближе к концу профиля задействовано меньше данных, что хорошо сказывается на пространственной локальности. Однако в то же время видно, что на каждой следующей итерации выполняется меньше обращений к одним и тем же локальным областям, то есть временная локальность, по всей вероятности, становится ниже. Рассмотрим подробнее первую итерацию, что более детально оценить их строение.&lt;br /&gt;
&lt;br /&gt;
На рис. 3 представлена одна итерация общего профиля (выделенный зеленый фрагмент на рис. 2). Ее можно условно разбить на 5 фрагментов, каждую из которых рассмотреть отдельно. Фрагменты 2 и 5 устроены просто – элементы перебираются с большим шагом по памяти, при достижении конца данных перебор повторяется, только с небольшим сдвигом. Про такие фрагменты можно сказать, что они обладают низкой пространственной локальностью, поскольку шаг по памяти достаточно большой, и низкой временной локальностью, так как данные повторно не используются. Однако отметим, что данные фрагменты состоят из небольшого числа обращений, что означает, что они не так сильно будут снижать общую локальность профиля.&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qr_2.png|thumb|center|700px|Рисунок 3. Профиль обращений, одна итерация]]&lt;br /&gt;
&lt;br /&gt;
Далее, фрагмент 1 и 3 устроены практически идентично (основное отличие – два основных этапа переставлены местами), поэтому рассмотрим только один из них. На рис. 4 представлен фрагмент 3. Здесь хорошо видно, как устроены эти этапы. На первом этапе выполняется перебор элементов в обратном порядке с шагом 1, при этом к каждому элементу подряд обращаются несколько раз. Такой фрагмент обладает очень высокой пространственной локальностью и достаточно высокой временной локальностью. &lt;br /&gt;
&lt;br /&gt;
[[file:householder_qr_3.png|thumb|center|500px|Рисунок 4. Профиль обращений, одна итерация, фрагмент 3]]&lt;br /&gt;
&lt;br /&gt;
На втором этапе также выполняется последовательный перебор, но уже с небольшим шагом, и к каждому элементу обращаются только по 1 разу на каждой итерации. Однако, поскольку в каждой небольшой итерации на втором этапе всего около 30 обращений, повторное обращение к каждому элементу происходит недалеко от предыдущего, что говорит о достаточно высокой временной локальности. Пространственная локальность также высока, поскольку шаг по памяти невелик.&lt;br /&gt;
&lt;br /&gt;
Далее рассмотрим фрагмент 4. Чтобы лучше понять локальную структуру, детально изучим небольшую его часть (рис. 5). Из данного рисунка хорошо видно, что здесь профиль устроен очень просто и представляет собой обычный последовательный перебор с шагом 1 (с небольшими и нечастыми сдвигами, которые не влияют на локальность этого фрагмента). Такой фрагмент обладает высокой пространственной, но низкой временной локальностью.&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qr_4.png|thumb|center|500px|Рисунок 5. Профиль обращений, одна итерация, часть фрагмента 4]]&lt;br /&gt;
&lt;br /&gt;
В целом можно сказать, что одна итерация обладает высокой пространственной и средней временной локальностью, хотя она несколько снижается из-за наличия фрагментов 2, 4 и 5. При условии, что итерации подобны, выводы относительно локальности одной итерации можно перенести и на общий профиль.&lt;br /&gt;
&lt;br /&gt;
===== Количественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
Оценка выполняется на основе характеристики daps, которая оценивает число выполненных обращений (чтений и записей) в память в секунду. Данная характеристика является аналогом оценки flops применительно к работе с памятью и является в большей степени оценкой производительности взаимодействия с памятью, чем оценкой локальности. Однако она служит хорошим источником информации, в том числе для сравнения с результатами по следующей характеристике cvg.&lt;br /&gt;
&lt;br /&gt;
На рисунке 6 приведены значения daps для реализаций распространенных алгоритмов, отсортированные по возрастанию (чем больше daps, тем в общем случае выше производительность). Можно увидеть, что производительность работы с памятью в данном примере высока. Значение daps для него находится на одном уровне с тестом Linpack и лишь немногим ниже, чем, например, самые эффективные варианты перемножения плотных матриц. Это означает, что негативное влияние фрагментов 2 и 5 оказывается достаточно слабым, вероятно (как и предполагалось ранее) из-за небольшого их размера.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qr_daps.png|thumb|center|700px|Рисунок 6. Сравнение значений оценки daps]]&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
==== Масштабируемость алгоритма ====&lt;br /&gt;
==== Масштабируемость реализации алгоритма ====&lt;br /&gt;
Проведём исследование масштабируемости вширь реализации согласно [[Scalability methodology|методике]]. Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов-2&amp;quot; [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Набор и границы значений изменяемых [[Глоссарий#Параметры запуска|параметров запуска]] реализации алгоритма: &lt;br /&gt;
&lt;br /&gt;
*	число процессоров [16:256] с шагом n^2;&lt;br /&gt;
*	размер матрицы [1000 : 32000] с шагом 1000.&lt;br /&gt;
&lt;br /&gt;
В результате проведённых экспериментов был получен следующий диапазон [[Глоссарий#Эффективность реализации|эффективности реализации]] алгоритма:&lt;br /&gt;
&lt;br /&gt;
*	минимальная эффективность реализации 7.511e-06%;&lt;br /&gt;
*	максимальная эффективность реализации 0.0746%.&lt;br /&gt;
&lt;br /&gt;
На следующих рисунках приведены графики [[Глоссарий#Производительность|производительности]] и эффективности выбранной реализации алгоритма в процедуре PDGEQRF из Scalapack в зависимости от изменяемых параметров запуска.&lt;br /&gt;
&lt;br /&gt;
[[file:PDGEQRF perf2.png|thumb|center|700px|Рисунок 8. Параллельная реализация метода Хаусхолдера QR-разложения. Изменение производительности в зависимости от числа процессоров и размера матрицы.]]&lt;br /&gt;
[[file:PDGEQRF eff2.png|thumb|center|700px|Рисунок 9. Параллельная реализация метода Хаусхолдера QR-разложения. Изменение эффективности в зависимости от числа процессоров и размера матрицы.]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
В сравнении с методом Гивенса, который имеет естественное двумерное блочное разбиение на основе точечного метода, метод Хаусхолдера из-за худших характеристик локальности (наличие пучков рассылок) и меньшего количества независимых обобщённых развёрток графа не так хорош для реализаций на системах с распределённой памятью, как для систем с общей памятью. Поэтому на массово параллельных системах с распределённой памятью следует применять метод Хаусхолдера (если уж именно его нужно реализовать) не в точечной версии, а в разрабатываемых исследователями блочных вариантах. Следует отметить, что эти варианты - не блочная нарезка описанного метода, а самостоятельные методы. Особенно их применение рекомендуется в случаях с большой разрежённостью матрицы.&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Большинство пакетов от LINPACKа и LAPACKa до SCALAPACKa используют для QR-разложения матриц именно метод Хаусхолдера, правда, в различных модификациях (обычно с использованием BLAS). Существует большая подборка исследовательских работ по блочным версиям.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Статьи в работе]]&lt;br /&gt;
[[Категория:Разложения матриц]]&lt;/div&gt;</summary>
		<author><name>Teplov</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D0%A5%D0%B0%D1%83%D1%81%D1%85%D0%BE%D0%BB%D0%B4%D0%B5%D1%80%D0%B0_(%D0%BE%D1%82%D1%80%D0%B0%D0%B6%D0%B5%D0%BD%D0%B8%D0%B9)_%D0%BF%D1%80%D0%B8%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D1%8F_%D0%BC%D0%B0%D1%82%D1%80%D0%B8%D1%86%D1%8B_%D0%BA_%D0%B4%D0%B2%D1%83%D1%85%D0%B4%D0%B8%D0%B0%D0%B3%D0%BE%D0%BD%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%B9_%D1%84%D0%BE%D1%80%D0%BC%D0%B5&amp;diff=21528</id>
		<title>Метод Хаусхолдера (отражений) приведения матрицы к двухдиагональной форме</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D0%A5%D0%B0%D1%83%D1%81%D1%85%D0%BE%D0%BB%D0%B4%D0%B5%D1%80%D0%B0_(%D0%BE%D1%82%D1%80%D0%B0%D0%B6%D0%B5%D0%BD%D0%B8%D0%B9)_%D0%BF%D1%80%D0%B8%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D1%8F_%D0%BC%D0%B0%D1%82%D1%80%D0%B8%D1%86%D1%8B_%D0%BA_%D0%B4%D0%B2%D1%83%D1%85%D0%B4%D0%B8%D0%B0%D0%B3%D0%BE%D0%BD%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%B9_%D1%84%D0%BE%D1%80%D0%BC%D0%B5&amp;diff=21528"/>
		<updated>2017-01-06T14:20:09Z</updated>

		<summary type="html">&lt;p&gt;Teplov: /* Масштабируемость реализации алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Приведение матрицы к двудиагональному виду методом Хаусхолдера (отражений)&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;\frac{8 n^3}{3}+O(n^2)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;2n^2+O(n)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;n(n + 2)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
Основные авторы описания: [[Участник:Frolov|А.В.Фролов]]&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Метод Хаусхолдера&#039;&#039;&#039; (в советской математической литературе чаще называется &#039;&#039;&#039;методом отражений&#039;&#039;&#039;) используется для приведения симметричных вещественных матриц к двухдиагональному виду, или, что то же самое, для разложения &amp;lt;math&amp;gt;A=QDU^T&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;Q, U&amp;lt;/math&amp;gt; - ортогональные, &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; — правая двухдиагональная матрица)&amp;lt;ref&amp;gt;В.В.Воеводин, Ю.А.Кузнецов. Матрицы и вычисления. М.: Наука, 1984.&amp;lt;/ref&amp;gt;. При этом матрицы &amp;lt;math&amp;gt;Q, U&amp;lt;/math&amp;gt; хранятся и используются не в своём явном виде, а в виде произведения матриц отражения&amp;lt;ref name=&amp;quot;VOLA&amp;quot;&amp;gt;Воеводин В.В. Вычислительные основы линейной алгебры. М.: Наука, 1977.&amp;lt;/ref&amp;gt;. Каждая из матриц отражения может быть определена одним вектором. Это позволяет в классическом исполнении метода отражений хранить результаты разложения на месте матрицы A с использованием двух одномерных дополнительных массивов. &lt;br /&gt;
&lt;br /&gt;
В данной статье рассматривается именно классическое исполнение, в котором не используются приёмы типа сдваивания при вычислениях скалярных произведений.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В методе Хаусхолдера для выполнения разложения матрицы в произведение двухдиагональной и двух ортогональных используются попеременные умножения слева и справа её текущих модификаций на матрицы Хаусхолдера (отражений). &lt;br /&gt;
&lt;br /&gt;
{{Шаблон:Матрица отражений}}&lt;br /&gt;
&lt;br /&gt;
На &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м шаге метода с помощью преобразования отражения &amp;quot;убираются&amp;quot; ненулевые поддиагональные элементы в &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м столбце, а потом (кроме шага с номером &amp;lt;math&amp;gt;i=n-1&amp;lt;/math&amp;gt;) с помощью преобразования отражения &amp;quot;убираются&amp;quot; ненулевые наддиагональные элементы в &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м столбце, кроме самого левого из них. Таким образом, после &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; шагов преобразований получается правая двудиагональная &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; из разложения матрицы в произведение двудиагональной и двух ортогональных.&lt;br /&gt;
&lt;br /&gt;
На каждом из шагов метода матрицы отражений обычно представляют не в стандартном виде, а в виде &amp;lt;math&amp;gt;U=E-\frac{1}{\gamma}vv^*&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; находится для левых матриц отражения через координаты текущего &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; - вектор размерности &amp;lt;math&amp;gt;n+1-i&amp;lt;/math&amp;gt;, составленный из элементов &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца, начиная с &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;(s,s)=0&amp;lt;/math&amp;gt;, то &amp;lt;math&amp;gt;v=e_{i}&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\gamma = \frac{1}{2}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В остальных случаях по алгоритму вычисляется &amp;lt;math&amp;gt;u = \frac{1}{\sqrt{(s,s)}}s&amp;lt;/math&amp;gt;, и далее &amp;lt;math&amp;gt;v_{j}=0&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;j&amp;lt;i&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;v_{j}=u_{j-i+1}&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;j&amp;gt;i&amp;lt;/math&amp;gt;, а &amp;lt;math&amp;gt;v_{i}=1&amp;lt;/math&amp;gt;, если &amp;lt;math&amp;gt;u_{1}=0&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v_{i}=\frac{u_{1}}{|u_{1}|}(1+|u_{1}|)&amp;lt;/math&amp;gt; для остальных значений. При этом &amp;lt;math&amp;gt;\gamma = 1+|u_{1}|=|v_{i}|&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
После вычисления вектора &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; подстолбцы справа от ведущего модифицируются по формулам &amp;lt;math&amp;gt;x&#039;=x-\frac{(x,v)}{\gamma}v&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Аналогично для правых матриц отражений &amp;lt;math&amp;gt;U=E-\frac{1}{\gamma}vv^*&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; находится через координаты текущей &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-й строки так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; - вектор размерности &amp;lt;math&amp;gt;n-i&amp;lt;/math&amp;gt;, составленный из элементов &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-й строки, начиная с &amp;lt;math&amp;gt;i+1&amp;lt;/math&amp;gt;-го элемента.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;(s,s)=0&amp;lt;/math&amp;gt;, то &amp;lt;math&amp;gt;v=e_{i+1}&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\gamma = \frac{1}{2}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В остальных случаях по алгоритму вычисляется &amp;lt;math&amp;gt;u = \frac{1}{\sqrt{(s,s)}}s&amp;lt;/math&amp;gt;, и далее &amp;lt;math&amp;gt;v_{j}=0&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;j&amp;lt;i+1&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;v_{j}=u_{j-i+2}&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;j&amp;gt;i+1&amp;lt;/math&amp;gt;, а &amp;lt;math&amp;gt;v_{i+1}=1&amp;lt;/math&amp;gt;, если &amp;lt;math&amp;gt;u_{1}=0&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v_{i+1}=\frac{u_{1}}{|u_{1}|}(1+|u_{1}|)&amp;lt;/math&amp;gt; для остальных значений. При этом &amp;lt;math&amp;gt;\gamma = 1+|u_{1}|=|v_{i+1}|&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Основную часть алгоритма составляют вычисления на каждом шагу скалярных произведений &amp;lt;math&amp;gt;(s,s)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(x,v)&amp;lt;/math&amp;gt; для всех подстолбцов &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; справа от текущего, а также векторные операции &amp;lt;math&amp;gt;x&#039;=x-\frac{(x,v)}{\gamma}v&amp;lt;/math&amp;gt;, и затем вычисления на каждом шагу (кроме шага с номером &amp;lt;math&amp;gt;i=n-1&amp;lt;/math&amp;gt;) скалярных произведений &amp;lt;math&amp;gt;(s,s)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(x,v)&amp;lt;/math&amp;gt; для всех подстрок &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; снизу от текущей, а также векторные операции &amp;lt;math&amp;gt;x&#039;=x-\frac{(x,v)}{\gamma}v&amp;lt;/math&amp;gt;. Это используется при программировании метода во многих библиотеках для его конструирования из стандартных подпрограмм (например, из BLAS).&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Всё приведение состоит из &amp;lt;math&amp;gt;2n-3&amp;lt;/math&amp;gt; полушагов. Каждый из них состоит из вычисления некоторой матрицы отражений (Хаусхолдера) и умножения на неё матрицы. Строгая последовательность выполнения этих частей полушагов не обязательна, в силу связи получаемых векторов &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; можно одновременно с &amp;lt;math&amp;gt;(s,s)&amp;lt;/math&amp;gt; вычислять и произведения &amp;lt;math&amp;gt;(x,s)&amp;lt;/math&amp;gt; с последующим выражением через них &amp;lt;math&amp;gt;(x,v)&amp;lt;/math&amp;gt;. Это позволяет почти вдвое уменьшать критический путь графа алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Последовательность выполнения алгоритма обычно записывается как последовательное &amp;quot;обнуление&amp;quot; поддиагональных элементов столбцов, начиная с 1-го столбца, заканчивая &amp;lt;math&amp;gt;(n-1)&amp;lt;/math&amp;gt;-м, попеременно с последовательным &amp;quot;обнулением&amp;quot; наддиагональных (кроме первой кодиагонали) элементов строк, начиная с 1й строки и заканчивая &amp;lt;math&amp;gt;(n-2)&amp;lt;/math&amp;gt;-й &lt;br /&gt;
&lt;br /&gt;
При этом в каждом &amp;quot;обнуляемом&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м столбце &amp;quot;обнуляются&amp;quot; сразу все его поддиагональные элементы одновременно, с &amp;lt;math&amp;gt;(i+1)&amp;lt;/math&amp;gt;-го до &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;-го. &lt;br /&gt;
&lt;br /&gt;
Каждое &amp;quot;обнуление&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца состоит из двух шагов: &lt;br /&gt;
а) вычисление параметров матрицы отражения &amp;lt;math&amp;gt;U_{2i-1}&amp;lt;/math&amp;gt; такой, чтобы при умножении на неё слева &amp;quot;обнулились&amp;quot; все поддиагональные его элементы;&lt;br /&gt;
б) умножение слева матрицы отражения &amp;lt;math&amp;gt;U_{2i-1}&amp;lt;/math&amp;gt; на текущую версию матрицы.&lt;br /&gt;
&lt;br /&gt;
В каждой &amp;quot;обнуляемой&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-й строке &amp;quot;обнуляются&amp;quot; сразу все его наддиагональные элементы одновременно, с &amp;lt;math&amp;gt;(i+2)&amp;lt;/math&amp;gt;-го до &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;-го. &lt;br /&gt;
&lt;br /&gt;
Каждое &amp;quot;обнуление&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-й строки состоит из двух шагов: &lt;br /&gt;
а) вычисление параметров матрицы отражения &amp;lt;math&amp;gt;U_{2i}&amp;lt;/math&amp;gt; такой, чтобы при умножении на неё справа &amp;quot;обнулились&amp;quot; все (кроме первого) наддиагональные её элементы;&lt;br /&gt;
б) умножение справа матрицы отражения &amp;lt;math&amp;gt;U_{2i}&amp;lt;/math&amp;gt; на текущую версию матрицы.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В последовательной версии основная сложность алгоритма определяется прежде всего вычислениями скалярных произведений векторов, а также модификаций векторов вида &amp;lt;math&amp;gt;x&#039;=x-\alpha v&amp;lt;/math&amp;gt;, причем над векторами убывающей по ходу алгоритма размерности. Они, если не учитывать возможную разреженность, составляют (в главном члене) по &amp;lt;math&amp;gt;4n^3/3&amp;lt;/math&amp;gt; операций действительного умножения и сложения/вычитания.&lt;br /&gt;
&lt;br /&gt;
При классификации по последовательной сложности, таким образом, метод Хаусхолдера относится к алгоритмам &#039;&#039;с кубической сложностью&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
[[File:HausLeft1.png|thumb|right|600px|Рисунок 1. Граф нечётного полушага (обнуление &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;го столбца) алгоритма. Квадратики - входные данные шага (берутся с предыдущего полушага), кружки - операции. Зелёным выделены операции типа a+bb, салатовым и голубым - типа a+bc, тёмно-синим - вычисление &amp;lt;math&amp;gt;\gamma , v_{1}&amp;lt;/math&amp;gt;, жёлтым - умножения (или деления). Обведённая группа операций повторяется независимо n-i раз. Результаты синего и жёлтых кружков - выходные для алгоритма.]]&lt;br /&gt;
&lt;br /&gt;
На рисунке 1 изображён граф нечётного (&amp;lt;math&amp;gt;(2i-1)&amp;lt;/math&amp;gt;го) полушага в привязке к элементам обрабатываемой матрицы. Граф чётного (&amp;lt;math&amp;gt;2i&amp;lt;/math&amp;gt;го) полушага почти аналогичен, только длина ветвей (нарисована слева) будет меньше на 1 (&amp;lt;math&amp;gt;n-i&amp;lt;/math&amp;gt;), и для привязки к элементам матрицы его надо отразить относительно диагонали матрицы. После этого отражения графы полушагов можно положить друг на друга слоями, при этом правые нижние углы должны быть друг над другом.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Для понимания ресурса параллелизма в разложении матрицы порядка &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; методом Хаусхолдера нужно рассмотреть критический путь графа. &lt;br /&gt;
&lt;br /&gt;
Как видно из описания разных вершин, вычисления при &amp;quot;обнулении&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца параметров отражения и скалярных произведений состоят из основной части - ветви длиной по &amp;lt;math&amp;gt;n-i&amp;lt;/math&amp;gt; умножений и сложений - и коррекции вычислений, которые составляют &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; операций. Вычисления при &amp;quot;обнулении&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-й строки параметров отражения и скалярных произведений состоят из основной части - ветви длиной по &amp;lt;math&amp;gt;n-i-1&amp;lt;/math&amp;gt; умножений и сложений - и коррекции вычислений, которые составляют &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; операций.&lt;br /&gt;
&lt;br /&gt;
Поэтому по грубой (без членов низших порядков) оценке критический путь метода Хаусхолдера будет идти через &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt; умножений и &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt; сложений/вычитаний. &lt;br /&gt;
 &lt;br /&gt;
Поэтому в параллельном варианте, как и в последовательном, основную долю требуемого для выполнения алгоритма времени будут определять операции вида &amp;lt;math&amp;gt;a+bc&amp;lt;/math&amp;gt;.  &lt;br /&gt;
&lt;br /&gt;
При классификации по высоте ЯПФ, таким образом, метод Хаусхолдера относится к алгоритмам &#039;&#039;с квадратичной сложностью&#039;&#039;. При классификации по ширине ЯПФ его сложность будет также &#039;&#039;квадратичной&#039;&#039; (без расширения ряда ярусов, связанных с векторными операциями сложения, пришлось бы увеличить вдвое длину критического пути; при таком расширении сложность по ширине ЯПФ станет &#039;&#039;линейной&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
Надо сказать, что здесь в оценках речь идёт именно о классическом способе реализации метода Хаусхолдера. Даже использование схем сдваивания или последовательно-параллельных для вычисления скалярных произведений уменьшает критический путь с квадратичного до &#039;&#039;степени 3/2&#039;&#039; или &#039;&#039;линейно-логарифмического&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: плотная квадратная матрица &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; (элементы &amp;lt;math&amp;gt;a_{ij}&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;n^2+2n&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Соотношение последовательной и параллельной сложности, как хорошо видно, является &#039;&#039;линейным&#039;&#039;, что даёт определённый стимул для распараллеливания. Однако у наискорейшей ЯПФ ширина &#039;&#039;квадратична&#039;&#039;, что указывает на дисбаланс между загруженностями устройств при попытке её реально запрограммировать. Поэтому более практично даже при хорошей (быстрой) вычислительной сети оставить количество устройств (например, узлов кластера) &#039;&#039;линейным&#039;&#039; по размеру матрицы, что удвоит критический путь реализуемой ЯПФ. &lt;br /&gt;
&lt;br /&gt;
При этом вычислительная мощность алгоритма, как отношение числа операций к суммарному объему входных и выходных данных, &#039;&#039;линейна&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Алгоритм в рамках выбранной версии полностью детерминирован.&lt;br /&gt;
&lt;br /&gt;
Вычислительная погрешность в методе отражений (Хаусхолдера) растет &#039;&#039;линейно&#039;&#039;, как и в методе Гивенса (вращений).&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В варианте с кратчайшим критическим путём графа алгоритма (с использованием зависимости между обнуляемым вектором и направляющим вектором отражения) метод Хаусхолдера (отражений) приведения квадратной вещественной матрицы к двудиагональной форме на Фортране 77 можно записать так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;fortran&amp;quot;&amp;gt;&lt;br /&gt;
	DO  I = 1, N-1&lt;br /&gt;
            DO K = I, N&lt;br /&gt;
              SX(K)=A(N,I)*A(N,K)&lt;br /&gt;
            END DO&lt;br /&gt;
            DO J = N-1, I, -1&lt;br /&gt;
               DO K = I, N&lt;br /&gt;
                  SX(K)=SX(K)+A(J,I)*A(J,K)&lt;br /&gt;
               END DO&lt;br /&gt;
            END DO&lt;br /&gt;
&lt;br /&gt;
               ALPHA = SQRT (SX(I))&lt;br /&gt;
               IF (A(I,I).NE.0.) THEN&lt;br /&gt;
                  BETA = 1./ALPHA&lt;br /&gt;
                  DO J = I+1, N&lt;br /&gt;
                    A(J,I)=A(J,I)*BETA&lt;br /&gt;
                  END DO&lt;br /&gt;
                  SX(I) = A(I,I)*BETA+SIGN(1.,A(I,I))                     &lt;br /&gt;
                  A(I,I)=ALPHA&lt;br /&gt;
                  G=1./ABS(SX(I)) ! 1/gamma&lt;br /&gt;
                  DO K = I+1, N&lt;br /&gt;
                     SX(K)=SX(K)*BETA*G+SIGN(A(I,K),SX(I))&lt;br /&gt;
                     A(I,K) = A(I,K)+SX(K)*SX(I)&lt;br /&gt;
                     DO J = I+1, N&lt;br /&gt;
                       A (J,K) = A(J,K)-A(J,I)*SX(K)&lt;br /&gt;
                     END DO&lt;br /&gt;
                  END DO&lt;br /&gt;
               ELSE&lt;br /&gt;
                  IF (ALPHA.NE.0.) THEN&lt;br /&gt;
                     BETA = 1./ALPHA&lt;br /&gt;
                     DO J = I+1, N&lt;br /&gt;
                       A(J,I)=A(J,I)*BETA&lt;br /&gt;
                     END DO&lt;br /&gt;
                     SX(I) = -1. &lt;br /&gt;
                     A(I,I)=ALPHA&lt;br /&gt;
                     G=1.! 1/gamma&lt;br /&gt;
                     DO K = I+1, N&lt;br /&gt;
                        SX(K)=SX(K)*BETA*G+SIGN(A(I,K),SX(I))&lt;br /&gt;
                        A(I,K) = A(I,K)+SX(K)*SX(I)&lt;br /&gt;
                        DO J = I+1, N&lt;br /&gt;
                          A (J,K) = A(J,K)-A(J,I)*SX(K)&lt;br /&gt;
                        END DO&lt;br /&gt;
                     END DO&lt;br /&gt;
                  ELSE&lt;br /&gt;
                     SX(I)=1&lt;br /&gt;
                     G=2&lt;br /&gt;
                     DO K = I+1, N&lt;br /&gt;
                        SX(K)=2&lt;br /&gt;
                        A(I,K) = A(I,K)-SX(K)&lt;br /&gt;
                     END DO&lt;br /&gt;
                  END IF&lt;br /&gt;
               END IF               &lt;br /&gt;
&lt;br /&gt;
              IF (I.LT.N-1) THEN&lt;br /&gt;
&lt;br /&gt;
            DO K = I, N&lt;br /&gt;
              SL(K)=A(I,N)*A(K,N)&lt;br /&gt;
            END DO&lt;br /&gt;
            DO J = N-1, I+1, -1&lt;br /&gt;
               DO K = I, N&lt;br /&gt;
                  SL(K)=SL(K)+A(I,J)*A(K,J)&lt;br /&gt;
               END DO&lt;br /&gt;
            END DO&lt;br /&gt;
&lt;br /&gt;
               ALPHA = SQRT (SL(I))&lt;br /&gt;
               IF (A(I,I+1).NE.0.) THEN&lt;br /&gt;
                  BETA = 1./ALPHA&lt;br /&gt;
                  DO J = I+2, N&lt;br /&gt;
                    A(I,J)=A(I,J)*BETA&lt;br /&gt;
                  END DO&lt;br /&gt;
                  SL(I) = A(I,I+1)*BETA+SIGN(1.,A(I,I+1))                     &lt;br /&gt;
                  A(I,I+1)=ALPHA&lt;br /&gt;
                  G=1./ABS(SL(I)) ! 1/gamma&lt;br /&gt;
                  DO K = I+1, N&lt;br /&gt;
                     SL(K)=SL(K)*BETA*G+SIGN(A(K,I+1),SL(I))&lt;br /&gt;
                     A(K,I+1) = A(K,I+1)+SL(K)*SX(I)&lt;br /&gt;
                     DO J = I+2, N&lt;br /&gt;
                       A (K,J) = A(K,J)-A(I,J)*SL(K)&lt;br /&gt;
                     END DO&lt;br /&gt;
                  END DO&lt;br /&gt;
               ELSE&lt;br /&gt;
                  IF (ALPHA.NE.0.) THEN&lt;br /&gt;
                     BETA = 1./ALPHA&lt;br /&gt;
                     DO J = I+2, N&lt;br /&gt;
                       A(I,J)=A(I,J)*BETA&lt;br /&gt;
                     END DO&lt;br /&gt;
                     SL(I) = -1. &lt;br /&gt;
                     A(I,I+1)=ALPHA&lt;br /&gt;
                     G=1. ! 1/gamma&lt;br /&gt;
                     DO K = I+1, N&lt;br /&gt;
                        SL(K)=SL(K)*BETA*G+SIGN(A(K,I+1),SL(I))&lt;br /&gt;
                        A(K,I+1) = A(K,I+1)+SL(K)*SL(I)&lt;br /&gt;
                        DO J = I+2, N&lt;br /&gt;
                          A (K,J) = A(K,J)-A(I,J)*SL(K)&lt;br /&gt;
                        END DO&lt;br /&gt;
                     END DO&lt;br /&gt;
                  ELSE&lt;br /&gt;
                     SL(I)=1&lt;br /&gt;
                     G=2.&lt;br /&gt;
                     DO K = I+1, N&lt;br /&gt;
                        SL(K)=2.&lt;br /&gt;
                        A(K,I+1) = A(K,I+1)-SL(K)&lt;br /&gt;
                     END DO&lt;br /&gt;
                  END IF&lt;br /&gt;
               END IF   &lt;br /&gt;
&lt;br /&gt;
              END IF&lt;br /&gt;
	END DO&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В этом варианте D расположена в диагонали и верхней кодиагонали массива A, направляющие вектора матриц отражений размещены в поддиагональных элементах соответствующих столбцов или надддиагональных - строк, а их первые элементы - в элементах массивов SX и SL.&lt;br /&gt;
&lt;br /&gt;
Обычно же в последовательных версиях коэффициенты модификаций столбцов вычисляются целиком через скалярные произведения после вычислений параметров матрицы отражения. При этом схема чуть проще. Удлиняется критический путь графа, но для последовательных реализаций это неважно.&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
К сожалению, в графе, как видно по рисунку, в наличии пучки рассылок, в них неизбежно часть дуг остаются длинными, что отрицательно влияет на локальность вычислений по пространству.  &lt;br /&gt;
&lt;br /&gt;
==== Локальность реализации алгоритма ====&lt;br /&gt;
===== Структура обращений в память и качественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qdu_1.png|thumb|center|700px|Рисунок 2. Метод Хаусхолдера (отражений) приведения матрицы к двухдиагональной форме. Общий профиль обращений в память]]&lt;br /&gt;
&lt;br /&gt;
На рис. 2 представлен профиль обращений в память для реализации метода Хаусхолдера (отражений) приведения матрицы к двухдиагональной форме. Данный профиль обладает явной итерационной структурой, причем видно, что на каждой следующей итерации из рассмотрения отбрасывается небольшая часть элементов из начала массива данных. Также можно заметить, что основная часть данных используется достаточно равномерно в рамках одной итерации, однако в нижней и верхней частях итерации видны небольшие блоки с очень высокой локальностью обращений. Итерации явно устроены по одному принципу, поэтому стоит рассмотреть одну из них более детально.&lt;br /&gt;
&lt;br /&gt;
На рис. 3 показаны обращения в рамках первой итерации общего профиля (выделена на рис. 2 зеленым). Эти обращения можно условно разбить на несколько фрагментов. Фрагменты 1, 2 и 6 практически идентичны (различие заключается только в перестановке местами двух частей этих фрагментов), рассмотрим один из них.&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qdu_2.png|thumb|center|700px|Рисунок 3. Профиль обращений, одна итерация]]&lt;br /&gt;
&lt;br /&gt;
На рис. 4 показан отдельно фрагмент 6, выделенный на рис. 3. Теперь можно определить структуру каждой из двух частей более точно. Первая часть представляет собой последовательный перебор элементов в обратном порядке, причем к каждому элементу выполняется несколько обращений подряд. Во второй части также наблюдается последовательный перебор, однако с другими свойствами: элементы перебираются в прямом порядке, к каждому элементу выполняется только 1 обращение, однако данный перебор повторяется несколько раз подряд, причем каждый раз задействованы одни и те же элементы. Все это позволяет говорить о высокой пространственной локальности фрагмента (поскольку шаг по памяти каждый раз небольшой) и достаточно высокой временной локальности (поскольку данные часто используются повторно).&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qdu_3.png|thumb|center|500px|Рисунок 4. Профиль обращений, одна итерация, фрагмент 6]]&lt;br /&gt;
&lt;br /&gt;
Свойства локальности во фрагментах 3 и 5 можно оценить и без более детального рассмотрения. Данные в обоих фрагментах перебираются с достаточно большим шагом по памяти, при этом повторно они почти не используются, что говорит о низкой локальности в обоих случаях (хотя надо отметить, что локальность фрагмента 5 немного выше из-за более высокой пространственной локальности).&lt;br /&gt;
&lt;br /&gt;
Теперь перейдем к рассмотрению фрагмента 4, расположенного в центре рис. 3. Поскольку он обладает регулярной структурой, достаточно более детально изучить небольшой фрагмент (представлен на рис. 5, выделен на рис. 3 зеленым). Здесь можно увидеть, что первая часть фрагмента представляет собой практически стандартный последовательный перебор (с небольшими нечастыми сдвигами, которые не влияют на локальность). Такой набор обращений обладает высокой пространственной и низкой временной локальностью. Вторая часть данного фрагмента устроена несколько сложнее – она состоит из L-образных наборов обращений. Одна его область (часть 1 на рис. 5) по строению очень похожа на первую часть; вторая (часть 2 на рис. 5) представляет собой набор обращений к одному и тому же элементу, что достаточно сильно повышает локальность данного фрагмента. Из этого можно сделать вывод, что фрагмент 4 обладает высокой пространственной и средней временной локальностью.&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qdu_4.png|thumb|center|500px|Рисунок 5. Профиль обращений, одна итерация, часть фрагмент 4]]&lt;br /&gt;
&lt;br /&gt;
Суммируя выше сказанное, можно сказать, что фрагменты 1, 2 и 6 обладают высокой локальностью; фрагменты 3 и 5 – низкой локальностью; фрагмент 4 – высокой пространственной и средней временной локальностью. Можно предположить, что в итоге локальность общего профиля будет не очень высокой, в основном из-за более низкой временной локальности. Однако такое достаточно сложное строение общего профиля затрудняет оценку локальности общего профиля на основе визуального анализа.&lt;br /&gt;
&lt;br /&gt;
===== Количественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
Оценка выполняется на основе характеристики daps, которая оценивает число выполненных обращений (чтений и записей) в память в секунду. Данная характеристика является аналогом оценки flops применительно к работе с памятью и является в большей степени оценкой производительности взаимодействия с памятью, чем оценкой локальности. Однако она служит хорошим источником информации, в том числе для сравнения с результатами по следующей характеристике cvg.&lt;br /&gt;
&lt;br /&gt;
На рисунке 6 приведены значения daps для реализаций распространенных алгоритмов, отсортированные по возрастанию (чем больше daps, тем в общем случае выше производительность). Можно увидеть, что оценка производительности в данном случае не очень высока, что подтверждает сделанные ранее выводы. В частности, значение daps для этого примера показывает более низкую производительность по сравнению с реализациями других методов Хаусхолдера (householder_qr householder_qtq) и находится примерно на уровне оценки для реализации метода Холецкого.&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qdu_daps.png|thumb|center|700px|Рисунок 6. Сравнение значений оценки daps]]&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
==== Масштабируемость алгоритма ====&lt;br /&gt;
==== Масштабируемость реализации алгоритма ====&lt;br /&gt;
Проведём исследование масштабируемости вширь реализации согласно [[Scalability methodology|методике]]. Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов-2&amp;quot; [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Набор и границы значений изменяемых [[Глоссарий#Параметры запуска|параметров запуска]] реализации алгоритма: &lt;br /&gt;
&lt;br /&gt;
*	число процессоров [16:100] с шагом n^2;&lt;br /&gt;
*	размер матрицы [1000 : 16000] с шагом 1000.&lt;br /&gt;
&lt;br /&gt;
В результате проведённых экспериментов был получен следующий диапазон [[Глоссарий#Эффективность реализации|эффективности реализации]] алгоритма:&lt;br /&gt;
&lt;br /&gt;
*	минимальная эффективность реализации 5.033e-05%;&lt;br /&gt;
*	максимальная эффективность реализации 0.0135%.&lt;br /&gt;
&lt;br /&gt;
На следующих рисунках приведены графики [[Глоссарий#Производительность|производительности]] и эффективности выбранной реализации алгоритма в процедуре PDGEHRD из Scalapack в зависимости от изменяемых параметров запуска.&lt;br /&gt;
&lt;br /&gt;
[[file:PDGEHRD perf2.png|thumb|center|700px|Рисунок 7. Параллельная реализация метода Хаусхолдера для приведения матриц к двухдиагональному виду. Изменение производительности в зависимости от числа процессоров и размера матрицы.]]&lt;br /&gt;
[[file:PDGEHRD eff2.png|thumb|center|700px|Рисунок 8. Параллельная реализация метода Хаусхолдера для приведения матриц к двухдиагональному виду. Изменение эффективности в зависимости от числа процессоров и размера матрицы.]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
В сравнении с методом Гивенса, который имеет естественное двумерное блочное разбиение на основе точечного метода, метод Хаусхолдера из-за худших характеристик локальности (наличие пучков рассылок) и меньшего количества независимых обобщённых развёрток графа не так хорош для реализаций на системах с распределённой памятью, как для систем с общей памятью. Поэтому на массово параллельных системах с распределённой памятью следует применять метод Хаусхолдера (если уж именно его нужно реализовать) не в точечной версии, а в разрабатываемых исследователями блочных вариантах. Следует отметить, что эти варианты - не блочная нарезка описанного метода, а самостоятельные методы. Особенно их применение рекомендуется в случаях с большой разрежённостью матрицы.&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Большинство пакетов от LINPACKа и LAPACKa до SCALAPACKa используют для приведения матриц к двудиагональному виду именно метод Хаусхолдера, правда, в различных модификациях (обычно с использованием BLAS). Существует большая подборка исследовательских работ по блочным версиям.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Статьи в работе]]&lt;br /&gt;
[[Категория:Разложения матриц]]&lt;/div&gt;</summary>
		<author><name>Teplov</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D0%A5%D0%B0%D1%83%D1%81%D1%85%D0%BE%D0%BB%D0%B4%D0%B5%D1%80%D0%B0_(%D0%BE%D1%82%D1%80%D0%B0%D0%B6%D0%B5%D0%BD%D0%B8%D0%B9)_%D0%BF%D1%80%D0%B8%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D1%8F_%D0%BC%D0%B0%D1%82%D1%80%D0%B8%D1%86%D1%8B_%D0%BA_%D0%B4%D0%B2%D1%83%D1%85%D0%B4%D0%B8%D0%B0%D0%B3%D0%BE%D0%BD%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%B9_%D1%84%D0%BE%D1%80%D0%BC%D0%B5&amp;diff=21527</id>
		<title>Метод Хаусхолдера (отражений) приведения матрицы к двухдиагональной форме</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D0%A5%D0%B0%D1%83%D1%81%D1%85%D0%BE%D0%BB%D0%B4%D0%B5%D1%80%D0%B0_(%D0%BE%D1%82%D1%80%D0%B0%D0%B6%D0%B5%D0%BD%D0%B8%D0%B9)_%D0%BF%D1%80%D0%B8%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D1%8F_%D0%BC%D0%B0%D1%82%D1%80%D0%B8%D1%86%D1%8B_%D0%BA_%D0%B4%D0%B2%D1%83%D1%85%D0%B4%D0%B8%D0%B0%D0%B3%D0%BE%D0%BD%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%B9_%D1%84%D0%BE%D1%80%D0%BC%D0%B5&amp;diff=21527"/>
		<updated>2017-01-06T14:17:30Z</updated>

		<summary type="html">&lt;p&gt;Teplov: /* Масштабируемость реализации алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Приведение матрицы к двудиагональному виду методом Хаусхолдера (отражений)&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;\frac{8 n^3}{3}+O(n^2)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;2n^2+O(n)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;n(n + 2)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
Основные авторы описания: [[Участник:Frolov|А.В.Фролов]]&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Метод Хаусхолдера&#039;&#039;&#039; (в советской математической литературе чаще называется &#039;&#039;&#039;методом отражений&#039;&#039;&#039;) используется для приведения симметричных вещественных матриц к двухдиагональному виду, или, что то же самое, для разложения &amp;lt;math&amp;gt;A=QDU^T&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;Q, U&amp;lt;/math&amp;gt; - ортогональные, &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; — правая двухдиагональная матрица)&amp;lt;ref&amp;gt;В.В.Воеводин, Ю.А.Кузнецов. Матрицы и вычисления. М.: Наука, 1984.&amp;lt;/ref&amp;gt;. При этом матрицы &amp;lt;math&amp;gt;Q, U&amp;lt;/math&amp;gt; хранятся и используются не в своём явном виде, а в виде произведения матриц отражения&amp;lt;ref name=&amp;quot;VOLA&amp;quot;&amp;gt;Воеводин В.В. Вычислительные основы линейной алгебры. М.: Наука, 1977.&amp;lt;/ref&amp;gt;. Каждая из матриц отражения может быть определена одним вектором. Это позволяет в классическом исполнении метода отражений хранить результаты разложения на месте матрицы A с использованием двух одномерных дополнительных массивов. &lt;br /&gt;
&lt;br /&gt;
В данной статье рассматривается именно классическое исполнение, в котором не используются приёмы типа сдваивания при вычислениях скалярных произведений.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В методе Хаусхолдера для выполнения разложения матрицы в произведение двухдиагональной и двух ортогональных используются попеременные умножения слева и справа её текущих модификаций на матрицы Хаусхолдера (отражений). &lt;br /&gt;
&lt;br /&gt;
{{Шаблон:Матрица отражений}}&lt;br /&gt;
&lt;br /&gt;
На &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м шаге метода с помощью преобразования отражения &amp;quot;убираются&amp;quot; ненулевые поддиагональные элементы в &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м столбце, а потом (кроме шага с номером &amp;lt;math&amp;gt;i=n-1&amp;lt;/math&amp;gt;) с помощью преобразования отражения &amp;quot;убираются&amp;quot; ненулевые наддиагональные элементы в &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м столбце, кроме самого левого из них. Таким образом, после &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; шагов преобразований получается правая двудиагональная &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; из разложения матрицы в произведение двудиагональной и двух ортогональных.&lt;br /&gt;
&lt;br /&gt;
На каждом из шагов метода матрицы отражений обычно представляют не в стандартном виде, а в виде &amp;lt;math&amp;gt;U=E-\frac{1}{\gamma}vv^*&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; находится для левых матриц отражения через координаты текущего &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; - вектор размерности &amp;lt;math&amp;gt;n+1-i&amp;lt;/math&amp;gt;, составленный из элементов &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца, начиная с &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;(s,s)=0&amp;lt;/math&amp;gt;, то &amp;lt;math&amp;gt;v=e_{i}&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\gamma = \frac{1}{2}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В остальных случаях по алгоритму вычисляется &amp;lt;math&amp;gt;u = \frac{1}{\sqrt{(s,s)}}s&amp;lt;/math&amp;gt;, и далее &amp;lt;math&amp;gt;v_{j}=0&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;j&amp;lt;i&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;v_{j}=u_{j-i+1}&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;j&amp;gt;i&amp;lt;/math&amp;gt;, а &amp;lt;math&amp;gt;v_{i}=1&amp;lt;/math&amp;gt;, если &amp;lt;math&amp;gt;u_{1}=0&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v_{i}=\frac{u_{1}}{|u_{1}|}(1+|u_{1}|)&amp;lt;/math&amp;gt; для остальных значений. При этом &amp;lt;math&amp;gt;\gamma = 1+|u_{1}|=|v_{i}|&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
После вычисления вектора &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; подстолбцы справа от ведущего модифицируются по формулам &amp;lt;math&amp;gt;x&#039;=x-\frac{(x,v)}{\gamma}v&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Аналогично для правых матриц отражений &amp;lt;math&amp;gt;U=E-\frac{1}{\gamma}vv^*&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; находится через координаты текущей &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-й строки так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; - вектор размерности &amp;lt;math&amp;gt;n-i&amp;lt;/math&amp;gt;, составленный из элементов &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-й строки, начиная с &amp;lt;math&amp;gt;i+1&amp;lt;/math&amp;gt;-го элемента.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;(s,s)=0&amp;lt;/math&amp;gt;, то &amp;lt;math&amp;gt;v=e_{i+1}&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\gamma = \frac{1}{2}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В остальных случаях по алгоритму вычисляется &amp;lt;math&amp;gt;u = \frac{1}{\sqrt{(s,s)}}s&amp;lt;/math&amp;gt;, и далее &amp;lt;math&amp;gt;v_{j}=0&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;j&amp;lt;i+1&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;v_{j}=u_{j-i+2}&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;j&amp;gt;i+1&amp;lt;/math&amp;gt;, а &amp;lt;math&amp;gt;v_{i+1}=1&amp;lt;/math&amp;gt;, если &amp;lt;math&amp;gt;u_{1}=0&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v_{i+1}=\frac{u_{1}}{|u_{1}|}(1+|u_{1}|)&amp;lt;/math&amp;gt; для остальных значений. При этом &amp;lt;math&amp;gt;\gamma = 1+|u_{1}|=|v_{i+1}|&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Основную часть алгоритма составляют вычисления на каждом шагу скалярных произведений &amp;lt;math&amp;gt;(s,s)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(x,v)&amp;lt;/math&amp;gt; для всех подстолбцов &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; справа от текущего, а также векторные операции &amp;lt;math&amp;gt;x&#039;=x-\frac{(x,v)}{\gamma}v&amp;lt;/math&amp;gt;, и затем вычисления на каждом шагу (кроме шага с номером &amp;lt;math&amp;gt;i=n-1&amp;lt;/math&amp;gt;) скалярных произведений &amp;lt;math&amp;gt;(s,s)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(x,v)&amp;lt;/math&amp;gt; для всех подстрок &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; снизу от текущей, а также векторные операции &amp;lt;math&amp;gt;x&#039;=x-\frac{(x,v)}{\gamma}v&amp;lt;/math&amp;gt;. Это используется при программировании метода во многих библиотеках для его конструирования из стандартных подпрограмм (например, из BLAS).&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Всё приведение состоит из &amp;lt;math&amp;gt;2n-3&amp;lt;/math&amp;gt; полушагов. Каждый из них состоит из вычисления некоторой матрицы отражений (Хаусхолдера) и умножения на неё матрицы. Строгая последовательность выполнения этих частей полушагов не обязательна, в силу связи получаемых векторов &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; можно одновременно с &amp;lt;math&amp;gt;(s,s)&amp;lt;/math&amp;gt; вычислять и произведения &amp;lt;math&amp;gt;(x,s)&amp;lt;/math&amp;gt; с последующим выражением через них &amp;lt;math&amp;gt;(x,v)&amp;lt;/math&amp;gt;. Это позволяет почти вдвое уменьшать критический путь графа алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Последовательность выполнения алгоритма обычно записывается как последовательное &amp;quot;обнуление&amp;quot; поддиагональных элементов столбцов, начиная с 1-го столбца, заканчивая &amp;lt;math&amp;gt;(n-1)&amp;lt;/math&amp;gt;-м, попеременно с последовательным &amp;quot;обнулением&amp;quot; наддиагональных (кроме первой кодиагонали) элементов строк, начиная с 1й строки и заканчивая &amp;lt;math&amp;gt;(n-2)&amp;lt;/math&amp;gt;-й &lt;br /&gt;
&lt;br /&gt;
При этом в каждом &amp;quot;обнуляемом&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м столбце &amp;quot;обнуляются&amp;quot; сразу все его поддиагональные элементы одновременно, с &amp;lt;math&amp;gt;(i+1)&amp;lt;/math&amp;gt;-го до &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;-го. &lt;br /&gt;
&lt;br /&gt;
Каждое &amp;quot;обнуление&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца состоит из двух шагов: &lt;br /&gt;
а) вычисление параметров матрицы отражения &amp;lt;math&amp;gt;U_{2i-1}&amp;lt;/math&amp;gt; такой, чтобы при умножении на неё слева &amp;quot;обнулились&amp;quot; все поддиагональные его элементы;&lt;br /&gt;
б) умножение слева матрицы отражения &amp;lt;math&amp;gt;U_{2i-1}&amp;lt;/math&amp;gt; на текущую версию матрицы.&lt;br /&gt;
&lt;br /&gt;
В каждой &amp;quot;обнуляемой&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-й строке &amp;quot;обнуляются&amp;quot; сразу все его наддиагональные элементы одновременно, с &amp;lt;math&amp;gt;(i+2)&amp;lt;/math&amp;gt;-го до &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;-го. &lt;br /&gt;
&lt;br /&gt;
Каждое &amp;quot;обнуление&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-й строки состоит из двух шагов: &lt;br /&gt;
а) вычисление параметров матрицы отражения &amp;lt;math&amp;gt;U_{2i}&amp;lt;/math&amp;gt; такой, чтобы при умножении на неё справа &amp;quot;обнулились&amp;quot; все (кроме первого) наддиагональные её элементы;&lt;br /&gt;
б) умножение справа матрицы отражения &amp;lt;math&amp;gt;U_{2i}&amp;lt;/math&amp;gt; на текущую версию матрицы.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В последовательной версии основная сложность алгоритма определяется прежде всего вычислениями скалярных произведений векторов, а также модификаций векторов вида &amp;lt;math&amp;gt;x&#039;=x-\alpha v&amp;lt;/math&amp;gt;, причем над векторами убывающей по ходу алгоритма размерности. Они, если не учитывать возможную разреженность, составляют (в главном члене) по &amp;lt;math&amp;gt;4n^3/3&amp;lt;/math&amp;gt; операций действительного умножения и сложения/вычитания.&lt;br /&gt;
&lt;br /&gt;
При классификации по последовательной сложности, таким образом, метод Хаусхолдера относится к алгоритмам &#039;&#039;с кубической сложностью&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
[[File:HausLeft1.png|thumb|right|600px|Рисунок 1. Граф нечётного полушага (обнуление &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;го столбца) алгоритма. Квадратики - входные данные шага (берутся с предыдущего полушага), кружки - операции. Зелёным выделены операции типа a+bb, салатовым и голубым - типа a+bc, тёмно-синим - вычисление &amp;lt;math&amp;gt;\gamma , v_{1}&amp;lt;/math&amp;gt;, жёлтым - умножения (или деления). Обведённая группа операций повторяется независимо n-i раз. Результаты синего и жёлтых кружков - выходные для алгоритма.]]&lt;br /&gt;
&lt;br /&gt;
На рисунке 1 изображён граф нечётного (&amp;lt;math&amp;gt;(2i-1)&amp;lt;/math&amp;gt;го) полушага в привязке к элементам обрабатываемой матрицы. Граф чётного (&amp;lt;math&amp;gt;2i&amp;lt;/math&amp;gt;го) полушага почти аналогичен, только длина ветвей (нарисована слева) будет меньше на 1 (&amp;lt;math&amp;gt;n-i&amp;lt;/math&amp;gt;), и для привязки к элементам матрицы его надо отразить относительно диагонали матрицы. После этого отражения графы полушагов можно положить друг на друга слоями, при этом правые нижние углы должны быть друг над другом.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Для понимания ресурса параллелизма в разложении матрицы порядка &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; методом Хаусхолдера нужно рассмотреть критический путь графа. &lt;br /&gt;
&lt;br /&gt;
Как видно из описания разных вершин, вычисления при &amp;quot;обнулении&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца параметров отражения и скалярных произведений состоят из основной части - ветви длиной по &amp;lt;math&amp;gt;n-i&amp;lt;/math&amp;gt; умножений и сложений - и коррекции вычислений, которые составляют &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; операций. Вычисления при &amp;quot;обнулении&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-й строки параметров отражения и скалярных произведений состоят из основной части - ветви длиной по &amp;lt;math&amp;gt;n-i-1&amp;lt;/math&amp;gt; умножений и сложений - и коррекции вычислений, которые составляют &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; операций.&lt;br /&gt;
&lt;br /&gt;
Поэтому по грубой (без членов низших порядков) оценке критический путь метода Хаусхолдера будет идти через &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt; умножений и &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt; сложений/вычитаний. &lt;br /&gt;
 &lt;br /&gt;
Поэтому в параллельном варианте, как и в последовательном, основную долю требуемого для выполнения алгоритма времени будут определять операции вида &amp;lt;math&amp;gt;a+bc&amp;lt;/math&amp;gt;.  &lt;br /&gt;
&lt;br /&gt;
При классификации по высоте ЯПФ, таким образом, метод Хаусхолдера относится к алгоритмам &#039;&#039;с квадратичной сложностью&#039;&#039;. При классификации по ширине ЯПФ его сложность будет также &#039;&#039;квадратичной&#039;&#039; (без расширения ряда ярусов, связанных с векторными операциями сложения, пришлось бы увеличить вдвое длину критического пути; при таком расширении сложность по ширине ЯПФ станет &#039;&#039;линейной&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
Надо сказать, что здесь в оценках речь идёт именно о классическом способе реализации метода Хаусхолдера. Даже использование схем сдваивания или последовательно-параллельных для вычисления скалярных произведений уменьшает критический путь с квадратичного до &#039;&#039;степени 3/2&#039;&#039; или &#039;&#039;линейно-логарифмического&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: плотная квадратная матрица &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; (элементы &amp;lt;math&amp;gt;a_{ij}&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;n^2+2n&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Соотношение последовательной и параллельной сложности, как хорошо видно, является &#039;&#039;линейным&#039;&#039;, что даёт определённый стимул для распараллеливания. Однако у наискорейшей ЯПФ ширина &#039;&#039;квадратична&#039;&#039;, что указывает на дисбаланс между загруженностями устройств при попытке её реально запрограммировать. Поэтому более практично даже при хорошей (быстрой) вычислительной сети оставить количество устройств (например, узлов кластера) &#039;&#039;линейным&#039;&#039; по размеру матрицы, что удвоит критический путь реализуемой ЯПФ. &lt;br /&gt;
&lt;br /&gt;
При этом вычислительная мощность алгоритма, как отношение числа операций к суммарному объему входных и выходных данных, &#039;&#039;линейна&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Алгоритм в рамках выбранной версии полностью детерминирован.&lt;br /&gt;
&lt;br /&gt;
Вычислительная погрешность в методе отражений (Хаусхолдера) растет &#039;&#039;линейно&#039;&#039;, как и в методе Гивенса (вращений).&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В варианте с кратчайшим критическим путём графа алгоритма (с использованием зависимости между обнуляемым вектором и направляющим вектором отражения) метод Хаусхолдера (отражений) приведения квадратной вещественной матрицы к двудиагональной форме на Фортране 77 можно записать так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;fortran&amp;quot;&amp;gt;&lt;br /&gt;
	DO  I = 1, N-1&lt;br /&gt;
            DO K = I, N&lt;br /&gt;
              SX(K)=A(N,I)*A(N,K)&lt;br /&gt;
            END DO&lt;br /&gt;
            DO J = N-1, I, -1&lt;br /&gt;
               DO K = I, N&lt;br /&gt;
                  SX(K)=SX(K)+A(J,I)*A(J,K)&lt;br /&gt;
               END DO&lt;br /&gt;
            END DO&lt;br /&gt;
&lt;br /&gt;
               ALPHA = SQRT (SX(I))&lt;br /&gt;
               IF (A(I,I).NE.0.) THEN&lt;br /&gt;
                  BETA = 1./ALPHA&lt;br /&gt;
                  DO J = I+1, N&lt;br /&gt;
                    A(J,I)=A(J,I)*BETA&lt;br /&gt;
                  END DO&lt;br /&gt;
                  SX(I) = A(I,I)*BETA+SIGN(1.,A(I,I))                     &lt;br /&gt;
                  A(I,I)=ALPHA&lt;br /&gt;
                  G=1./ABS(SX(I)) ! 1/gamma&lt;br /&gt;
                  DO K = I+1, N&lt;br /&gt;
                     SX(K)=SX(K)*BETA*G+SIGN(A(I,K),SX(I))&lt;br /&gt;
                     A(I,K) = A(I,K)+SX(K)*SX(I)&lt;br /&gt;
                     DO J = I+1, N&lt;br /&gt;
                       A (J,K) = A(J,K)-A(J,I)*SX(K)&lt;br /&gt;
                     END DO&lt;br /&gt;
                  END DO&lt;br /&gt;
               ELSE&lt;br /&gt;
                  IF (ALPHA.NE.0.) THEN&lt;br /&gt;
                     BETA = 1./ALPHA&lt;br /&gt;
                     DO J = I+1, N&lt;br /&gt;
                       A(J,I)=A(J,I)*BETA&lt;br /&gt;
                     END DO&lt;br /&gt;
                     SX(I) = -1. &lt;br /&gt;
                     A(I,I)=ALPHA&lt;br /&gt;
                     G=1.! 1/gamma&lt;br /&gt;
                     DO K = I+1, N&lt;br /&gt;
                        SX(K)=SX(K)*BETA*G+SIGN(A(I,K),SX(I))&lt;br /&gt;
                        A(I,K) = A(I,K)+SX(K)*SX(I)&lt;br /&gt;
                        DO J = I+1, N&lt;br /&gt;
                          A (J,K) = A(J,K)-A(J,I)*SX(K)&lt;br /&gt;
                        END DO&lt;br /&gt;
                     END DO&lt;br /&gt;
                  ELSE&lt;br /&gt;
                     SX(I)=1&lt;br /&gt;
                     G=2&lt;br /&gt;
                     DO K = I+1, N&lt;br /&gt;
                        SX(K)=2&lt;br /&gt;
                        A(I,K) = A(I,K)-SX(K)&lt;br /&gt;
                     END DO&lt;br /&gt;
                  END IF&lt;br /&gt;
               END IF               &lt;br /&gt;
&lt;br /&gt;
              IF (I.LT.N-1) THEN&lt;br /&gt;
&lt;br /&gt;
            DO K = I, N&lt;br /&gt;
              SL(K)=A(I,N)*A(K,N)&lt;br /&gt;
            END DO&lt;br /&gt;
            DO J = N-1, I+1, -1&lt;br /&gt;
               DO K = I, N&lt;br /&gt;
                  SL(K)=SL(K)+A(I,J)*A(K,J)&lt;br /&gt;
               END DO&lt;br /&gt;
            END DO&lt;br /&gt;
&lt;br /&gt;
               ALPHA = SQRT (SL(I))&lt;br /&gt;
               IF (A(I,I+1).NE.0.) THEN&lt;br /&gt;
                  BETA = 1./ALPHA&lt;br /&gt;
                  DO J = I+2, N&lt;br /&gt;
                    A(I,J)=A(I,J)*BETA&lt;br /&gt;
                  END DO&lt;br /&gt;
                  SL(I) = A(I,I+1)*BETA+SIGN(1.,A(I,I+1))                     &lt;br /&gt;
                  A(I,I+1)=ALPHA&lt;br /&gt;
                  G=1./ABS(SL(I)) ! 1/gamma&lt;br /&gt;
                  DO K = I+1, N&lt;br /&gt;
                     SL(K)=SL(K)*BETA*G+SIGN(A(K,I+1),SL(I))&lt;br /&gt;
                     A(K,I+1) = A(K,I+1)+SL(K)*SX(I)&lt;br /&gt;
                     DO J = I+2, N&lt;br /&gt;
                       A (K,J) = A(K,J)-A(I,J)*SL(K)&lt;br /&gt;
                     END DO&lt;br /&gt;
                  END DO&lt;br /&gt;
               ELSE&lt;br /&gt;
                  IF (ALPHA.NE.0.) THEN&lt;br /&gt;
                     BETA = 1./ALPHA&lt;br /&gt;
                     DO J = I+2, N&lt;br /&gt;
                       A(I,J)=A(I,J)*BETA&lt;br /&gt;
                     END DO&lt;br /&gt;
                     SL(I) = -1. &lt;br /&gt;
                     A(I,I+1)=ALPHA&lt;br /&gt;
                     G=1. ! 1/gamma&lt;br /&gt;
                     DO K = I+1, N&lt;br /&gt;
                        SL(K)=SL(K)*BETA*G+SIGN(A(K,I+1),SL(I))&lt;br /&gt;
                        A(K,I+1) = A(K,I+1)+SL(K)*SL(I)&lt;br /&gt;
                        DO J = I+2, N&lt;br /&gt;
                          A (K,J) = A(K,J)-A(I,J)*SL(K)&lt;br /&gt;
                        END DO&lt;br /&gt;
                     END DO&lt;br /&gt;
                  ELSE&lt;br /&gt;
                     SL(I)=1&lt;br /&gt;
                     G=2.&lt;br /&gt;
                     DO K = I+1, N&lt;br /&gt;
                        SL(K)=2.&lt;br /&gt;
                        A(K,I+1) = A(K,I+1)-SL(K)&lt;br /&gt;
                     END DO&lt;br /&gt;
                  END IF&lt;br /&gt;
               END IF   &lt;br /&gt;
&lt;br /&gt;
              END IF&lt;br /&gt;
	END DO&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В этом варианте D расположена в диагонали и верхней кодиагонали массива A, направляющие вектора матриц отражений размещены в поддиагональных элементах соответствующих столбцов или надддиагональных - строк, а их первые элементы - в элементах массивов SX и SL.&lt;br /&gt;
&lt;br /&gt;
Обычно же в последовательных версиях коэффициенты модификаций столбцов вычисляются целиком через скалярные произведения после вычислений параметров матрицы отражения. При этом схема чуть проще. Удлиняется критический путь графа, но для последовательных реализаций это неважно.&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
К сожалению, в графе, как видно по рисунку, в наличии пучки рассылок, в них неизбежно часть дуг остаются длинными, что отрицательно влияет на локальность вычислений по пространству.  &lt;br /&gt;
&lt;br /&gt;
==== Локальность реализации алгоритма ====&lt;br /&gt;
===== Структура обращений в память и качественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qdu_1.png|thumb|center|700px|Рисунок 2. Метод Хаусхолдера (отражений) приведения матрицы к двухдиагональной форме. Общий профиль обращений в память]]&lt;br /&gt;
&lt;br /&gt;
На рис. 2 представлен профиль обращений в память для реализации метода Хаусхолдера (отражений) приведения матрицы к двухдиагональной форме. Данный профиль обладает явной итерационной структурой, причем видно, что на каждой следующей итерации из рассмотрения отбрасывается небольшая часть элементов из начала массива данных. Также можно заметить, что основная часть данных используется достаточно равномерно в рамках одной итерации, однако в нижней и верхней частях итерации видны небольшие блоки с очень высокой локальностью обращений. Итерации явно устроены по одному принципу, поэтому стоит рассмотреть одну из них более детально.&lt;br /&gt;
&lt;br /&gt;
На рис. 3 показаны обращения в рамках первой итерации общего профиля (выделена на рис. 2 зеленым). Эти обращения можно условно разбить на несколько фрагментов. Фрагменты 1, 2 и 6 практически идентичны (различие заключается только в перестановке местами двух частей этих фрагментов), рассмотрим один из них.&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qdu_2.png|thumb|center|700px|Рисунок 3. Профиль обращений, одна итерация]]&lt;br /&gt;
&lt;br /&gt;
На рис. 4 показан отдельно фрагмент 6, выделенный на рис. 3. Теперь можно определить структуру каждой из двух частей более точно. Первая часть представляет собой последовательный перебор элементов в обратном порядке, причем к каждому элементу выполняется несколько обращений подряд. Во второй части также наблюдается последовательный перебор, однако с другими свойствами: элементы перебираются в прямом порядке, к каждому элементу выполняется только 1 обращение, однако данный перебор повторяется несколько раз подряд, причем каждый раз задействованы одни и те же элементы. Все это позволяет говорить о высокой пространственной локальности фрагмента (поскольку шаг по памяти каждый раз небольшой) и достаточно высокой временной локальности (поскольку данные часто используются повторно).&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qdu_3.png|thumb|center|500px|Рисунок 4. Профиль обращений, одна итерация, фрагмент 6]]&lt;br /&gt;
&lt;br /&gt;
Свойства локальности во фрагментах 3 и 5 можно оценить и без более детального рассмотрения. Данные в обоих фрагментах перебираются с достаточно большим шагом по памяти, при этом повторно они почти не используются, что говорит о низкой локальности в обоих случаях (хотя надо отметить, что локальность фрагмента 5 немного выше из-за более высокой пространственной локальности).&lt;br /&gt;
&lt;br /&gt;
Теперь перейдем к рассмотрению фрагмента 4, расположенного в центре рис. 3. Поскольку он обладает регулярной структурой, достаточно более детально изучить небольшой фрагмент (представлен на рис. 5, выделен на рис. 3 зеленым). Здесь можно увидеть, что первая часть фрагмента представляет собой практически стандартный последовательный перебор (с небольшими нечастыми сдвигами, которые не влияют на локальность). Такой набор обращений обладает высокой пространственной и низкой временной локальностью. Вторая часть данного фрагмента устроена несколько сложнее – она состоит из L-образных наборов обращений. Одна его область (часть 1 на рис. 5) по строению очень похожа на первую часть; вторая (часть 2 на рис. 5) представляет собой набор обращений к одному и тому же элементу, что достаточно сильно повышает локальность данного фрагмента. Из этого можно сделать вывод, что фрагмент 4 обладает высокой пространственной и средней временной локальностью.&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qdu_4.png|thumb|center|500px|Рисунок 5. Профиль обращений, одна итерация, часть фрагмент 4]]&lt;br /&gt;
&lt;br /&gt;
Суммируя выше сказанное, можно сказать, что фрагменты 1, 2 и 6 обладают высокой локальностью; фрагменты 3 и 5 – низкой локальностью; фрагмент 4 – высокой пространственной и средней временной локальностью. Можно предположить, что в итоге локальность общего профиля будет не очень высокой, в основном из-за более низкой временной локальности. Однако такое достаточно сложное строение общего профиля затрудняет оценку локальности общего профиля на основе визуального анализа.&lt;br /&gt;
&lt;br /&gt;
===== Количественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
Оценка выполняется на основе характеристики daps, которая оценивает число выполненных обращений (чтений и записей) в память в секунду. Данная характеристика является аналогом оценки flops применительно к работе с памятью и является в большей степени оценкой производительности взаимодействия с памятью, чем оценкой локальности. Однако она служит хорошим источником информации, в том числе для сравнения с результатами по следующей характеристике cvg.&lt;br /&gt;
&lt;br /&gt;
На рисунке 6 приведены значения daps для реализаций распространенных алгоритмов, отсортированные по возрастанию (чем больше daps, тем в общем случае выше производительность). Можно увидеть, что оценка производительности в данном случае не очень высока, что подтверждает сделанные ранее выводы. В частности, значение daps для этого примера показывает более низкую производительность по сравнению с реализациями других методов Хаусхолдера (householder_qr householder_qtq) и находится примерно на уровне оценки для реализации метода Холецкого.&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qdu_daps.png|thumb|center|700px|Рисунок 6. Сравнение значений оценки daps]]&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
==== Масштабируемость алгоритма ====&lt;br /&gt;
==== Масштабируемость реализации алгоритма ====&lt;br /&gt;
Проведём исследование масштабируемости вширь реализации согласно [[Scalability methodology|методике]]. Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов-2&amp;quot; [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Набор и границы значений изменяемых [[Глоссарий#Параметры запуска|параметров запуска]] реализации алгоритма: &lt;br /&gt;
&lt;br /&gt;
*	число процессоров [16:256] с шагом n^2;&lt;br /&gt;
*	размер матрицы [1000 : 32000] с шагом 1000.&lt;br /&gt;
&lt;br /&gt;
В результате проведённых экспериментов был получен следующий диапазон [[Глоссарий#Эффективность реализации|эффективности реализации]] алгоритма:&lt;br /&gt;
&lt;br /&gt;
*	минимальная эффективность реализации 7.511e-06%;&lt;br /&gt;
*	максимальная эффективность реализации 0.0746%.&lt;br /&gt;
&lt;br /&gt;
На следующих рисунках приведены графики [[Глоссарий#Производительность|производительности]] и эффективности выбранной реализации алгоритма в процедуре PDGEHRD из Scalapack в зависимости от изменяемых параметров запуска.&lt;br /&gt;
&lt;br /&gt;
[[file:PDGEHRD perf2.png|thumb|center|700px|Рисунок 7. Параллельная реализация метода Хаусхолдера для приведения матриц к двухдиагональному виду. Изменение производительности в зависимости от числа процессоров и размера матрицы.]]&lt;br /&gt;
[[file:PDGEHRD eff2.png|thumb|center|700px|Рисунок 8. Параллельная реализация метода Хаусхолдера для приведения матриц к двухдиагональному виду. Изменение эффективности в зависимости от числа процессоров и размера матрицы.]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
В сравнении с методом Гивенса, который имеет естественное двумерное блочное разбиение на основе точечного метода, метод Хаусхолдера из-за худших характеристик локальности (наличие пучков рассылок) и меньшего количества независимых обобщённых развёрток графа не так хорош для реализаций на системах с распределённой памятью, как для систем с общей памятью. Поэтому на массово параллельных системах с распределённой памятью следует применять метод Хаусхолдера (если уж именно его нужно реализовать) не в точечной версии, а в разрабатываемых исследователями блочных вариантах. Следует отметить, что эти варианты - не блочная нарезка описанного метода, а самостоятельные методы. Особенно их применение рекомендуется в случаях с большой разрежённостью матрицы.&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Большинство пакетов от LINPACKа и LAPACKa до SCALAPACKa используют для приведения матриц к двудиагональному виду именно метод Хаусхолдера, правда, в различных модификациях (обычно с использованием BLAS). Существует большая подборка исследовательских работ по блочным версиям.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Статьи в работе]]&lt;br /&gt;
[[Категория:Разложения матриц]]&lt;/div&gt;</summary>
		<author><name>Teplov</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D0%A5%D0%B0%D1%83%D1%81%D1%85%D0%BE%D0%BB%D0%B4%D0%B5%D1%80%D0%B0_(%D0%BE%D1%82%D1%80%D0%B0%D0%B6%D0%B5%D0%BD%D0%B8%D0%B9)_%D0%BF%D1%80%D0%B8%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D1%8F_%D0%BC%D0%B0%D1%82%D1%80%D0%B8%D1%86%D1%8B_%D0%BA_%D0%B4%D0%B2%D1%83%D1%85%D0%B4%D0%B8%D0%B0%D0%B3%D0%BE%D0%BD%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%B9_%D1%84%D0%BE%D1%80%D0%BC%D0%B5&amp;diff=21526</id>
		<title>Метод Хаусхолдера (отражений) приведения матрицы к двухдиагональной форме</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D0%A5%D0%B0%D1%83%D1%81%D1%85%D0%BE%D0%BB%D0%B4%D0%B5%D1%80%D0%B0_(%D0%BE%D1%82%D1%80%D0%B0%D0%B6%D0%B5%D0%BD%D0%B8%D0%B9)_%D0%BF%D1%80%D0%B8%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D1%8F_%D0%BC%D0%B0%D1%82%D1%80%D0%B8%D1%86%D1%8B_%D0%BA_%D0%B4%D0%B2%D1%83%D1%85%D0%B4%D0%B8%D0%B0%D0%B3%D0%BE%D0%BD%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%B9_%D1%84%D0%BE%D1%80%D0%BC%D0%B5&amp;diff=21526"/>
		<updated>2017-01-06T14:16:59Z</updated>

		<summary type="html">&lt;p&gt;Teplov: /* Масштабируемость реализации алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Приведение матрицы к двудиагональному виду методом Хаусхолдера (отражений)&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;\frac{8 n^3}{3}+O(n^2)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;2n^2+O(n)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;n(n + 2)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
Основные авторы описания: [[Участник:Frolov|А.В.Фролов]]&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Метод Хаусхолдера&#039;&#039;&#039; (в советской математической литературе чаще называется &#039;&#039;&#039;методом отражений&#039;&#039;&#039;) используется для приведения симметричных вещественных матриц к двухдиагональному виду, или, что то же самое, для разложения &amp;lt;math&amp;gt;A=QDU^T&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;Q, U&amp;lt;/math&amp;gt; - ортогональные, &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; — правая двухдиагональная матрица)&amp;lt;ref&amp;gt;В.В.Воеводин, Ю.А.Кузнецов. Матрицы и вычисления. М.: Наука, 1984.&amp;lt;/ref&amp;gt;. При этом матрицы &amp;lt;math&amp;gt;Q, U&amp;lt;/math&amp;gt; хранятся и используются не в своём явном виде, а в виде произведения матриц отражения&amp;lt;ref name=&amp;quot;VOLA&amp;quot;&amp;gt;Воеводин В.В. Вычислительные основы линейной алгебры. М.: Наука, 1977.&amp;lt;/ref&amp;gt;. Каждая из матриц отражения может быть определена одним вектором. Это позволяет в классическом исполнении метода отражений хранить результаты разложения на месте матрицы A с использованием двух одномерных дополнительных массивов. &lt;br /&gt;
&lt;br /&gt;
В данной статье рассматривается именно классическое исполнение, в котором не используются приёмы типа сдваивания при вычислениях скалярных произведений.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В методе Хаусхолдера для выполнения разложения матрицы в произведение двухдиагональной и двух ортогональных используются попеременные умножения слева и справа её текущих модификаций на матрицы Хаусхолдера (отражений). &lt;br /&gt;
&lt;br /&gt;
{{Шаблон:Матрица отражений}}&lt;br /&gt;
&lt;br /&gt;
На &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м шаге метода с помощью преобразования отражения &amp;quot;убираются&amp;quot; ненулевые поддиагональные элементы в &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м столбце, а потом (кроме шага с номером &amp;lt;math&amp;gt;i=n-1&amp;lt;/math&amp;gt;) с помощью преобразования отражения &amp;quot;убираются&amp;quot; ненулевые наддиагональные элементы в &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м столбце, кроме самого левого из них. Таким образом, после &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; шагов преобразований получается правая двудиагональная &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; из разложения матрицы в произведение двудиагональной и двух ортогональных.&lt;br /&gt;
&lt;br /&gt;
На каждом из шагов метода матрицы отражений обычно представляют не в стандартном виде, а в виде &amp;lt;math&amp;gt;U=E-\frac{1}{\gamma}vv^*&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; находится для левых матриц отражения через координаты текущего &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; - вектор размерности &amp;lt;math&amp;gt;n+1-i&amp;lt;/math&amp;gt;, составленный из элементов &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца, начиная с &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;(s,s)=0&amp;lt;/math&amp;gt;, то &amp;lt;math&amp;gt;v=e_{i}&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\gamma = \frac{1}{2}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В остальных случаях по алгоритму вычисляется &amp;lt;math&amp;gt;u = \frac{1}{\sqrt{(s,s)}}s&amp;lt;/math&amp;gt;, и далее &amp;lt;math&amp;gt;v_{j}=0&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;j&amp;lt;i&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;v_{j}=u_{j-i+1}&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;j&amp;gt;i&amp;lt;/math&amp;gt;, а &amp;lt;math&amp;gt;v_{i}=1&amp;lt;/math&amp;gt;, если &amp;lt;math&amp;gt;u_{1}=0&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v_{i}=\frac{u_{1}}{|u_{1}|}(1+|u_{1}|)&amp;lt;/math&amp;gt; для остальных значений. При этом &amp;lt;math&amp;gt;\gamma = 1+|u_{1}|=|v_{i}|&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
После вычисления вектора &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; подстолбцы справа от ведущего модифицируются по формулам &amp;lt;math&amp;gt;x&#039;=x-\frac{(x,v)}{\gamma}v&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Аналогично для правых матриц отражений &amp;lt;math&amp;gt;U=E-\frac{1}{\gamma}vv^*&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; находится через координаты текущей &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-й строки так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; - вектор размерности &amp;lt;math&amp;gt;n-i&amp;lt;/math&amp;gt;, составленный из элементов &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-й строки, начиная с &amp;lt;math&amp;gt;i+1&amp;lt;/math&amp;gt;-го элемента.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;(s,s)=0&amp;lt;/math&amp;gt;, то &amp;lt;math&amp;gt;v=e_{i+1}&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\gamma = \frac{1}{2}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В остальных случаях по алгоритму вычисляется &amp;lt;math&amp;gt;u = \frac{1}{\sqrt{(s,s)}}s&amp;lt;/math&amp;gt;, и далее &amp;lt;math&amp;gt;v_{j}=0&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;j&amp;lt;i+1&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;v_{j}=u_{j-i+2}&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;j&amp;gt;i+1&amp;lt;/math&amp;gt;, а &amp;lt;math&amp;gt;v_{i+1}=1&amp;lt;/math&amp;gt;, если &amp;lt;math&amp;gt;u_{1}=0&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v_{i+1}=\frac{u_{1}}{|u_{1}|}(1+|u_{1}|)&amp;lt;/math&amp;gt; для остальных значений. При этом &amp;lt;math&amp;gt;\gamma = 1+|u_{1}|=|v_{i+1}|&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Основную часть алгоритма составляют вычисления на каждом шагу скалярных произведений &amp;lt;math&amp;gt;(s,s)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(x,v)&amp;lt;/math&amp;gt; для всех подстолбцов &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; справа от текущего, а также векторные операции &amp;lt;math&amp;gt;x&#039;=x-\frac{(x,v)}{\gamma}v&amp;lt;/math&amp;gt;, и затем вычисления на каждом шагу (кроме шага с номером &amp;lt;math&amp;gt;i=n-1&amp;lt;/math&amp;gt;) скалярных произведений &amp;lt;math&amp;gt;(s,s)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(x,v)&amp;lt;/math&amp;gt; для всех подстрок &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; снизу от текущей, а также векторные операции &amp;lt;math&amp;gt;x&#039;=x-\frac{(x,v)}{\gamma}v&amp;lt;/math&amp;gt;. Это используется при программировании метода во многих библиотеках для его конструирования из стандартных подпрограмм (например, из BLAS).&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Всё приведение состоит из &amp;lt;math&amp;gt;2n-3&amp;lt;/math&amp;gt; полушагов. Каждый из них состоит из вычисления некоторой матрицы отражений (Хаусхолдера) и умножения на неё матрицы. Строгая последовательность выполнения этих частей полушагов не обязательна, в силу связи получаемых векторов &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; можно одновременно с &amp;lt;math&amp;gt;(s,s)&amp;lt;/math&amp;gt; вычислять и произведения &amp;lt;math&amp;gt;(x,s)&amp;lt;/math&amp;gt; с последующим выражением через них &amp;lt;math&amp;gt;(x,v)&amp;lt;/math&amp;gt;. Это позволяет почти вдвое уменьшать критический путь графа алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Последовательность выполнения алгоритма обычно записывается как последовательное &amp;quot;обнуление&amp;quot; поддиагональных элементов столбцов, начиная с 1-го столбца, заканчивая &amp;lt;math&amp;gt;(n-1)&amp;lt;/math&amp;gt;-м, попеременно с последовательным &amp;quot;обнулением&amp;quot; наддиагональных (кроме первой кодиагонали) элементов строк, начиная с 1й строки и заканчивая &amp;lt;math&amp;gt;(n-2)&amp;lt;/math&amp;gt;-й &lt;br /&gt;
&lt;br /&gt;
При этом в каждом &amp;quot;обнуляемом&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м столбце &amp;quot;обнуляются&amp;quot; сразу все его поддиагональные элементы одновременно, с &amp;lt;math&amp;gt;(i+1)&amp;lt;/math&amp;gt;-го до &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;-го. &lt;br /&gt;
&lt;br /&gt;
Каждое &amp;quot;обнуление&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца состоит из двух шагов: &lt;br /&gt;
а) вычисление параметров матрицы отражения &amp;lt;math&amp;gt;U_{2i-1}&amp;lt;/math&amp;gt; такой, чтобы при умножении на неё слева &amp;quot;обнулились&amp;quot; все поддиагональные его элементы;&lt;br /&gt;
б) умножение слева матрицы отражения &amp;lt;math&amp;gt;U_{2i-1}&amp;lt;/math&amp;gt; на текущую версию матрицы.&lt;br /&gt;
&lt;br /&gt;
В каждой &amp;quot;обнуляемой&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-й строке &amp;quot;обнуляются&amp;quot; сразу все его наддиагональные элементы одновременно, с &amp;lt;math&amp;gt;(i+2)&amp;lt;/math&amp;gt;-го до &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;-го. &lt;br /&gt;
&lt;br /&gt;
Каждое &amp;quot;обнуление&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-й строки состоит из двух шагов: &lt;br /&gt;
а) вычисление параметров матрицы отражения &amp;lt;math&amp;gt;U_{2i}&amp;lt;/math&amp;gt; такой, чтобы при умножении на неё справа &amp;quot;обнулились&amp;quot; все (кроме первого) наддиагональные её элементы;&lt;br /&gt;
б) умножение справа матрицы отражения &amp;lt;math&amp;gt;U_{2i}&amp;lt;/math&amp;gt; на текущую версию матрицы.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В последовательной версии основная сложность алгоритма определяется прежде всего вычислениями скалярных произведений векторов, а также модификаций векторов вида &amp;lt;math&amp;gt;x&#039;=x-\alpha v&amp;lt;/math&amp;gt;, причем над векторами убывающей по ходу алгоритма размерности. Они, если не учитывать возможную разреженность, составляют (в главном члене) по &amp;lt;math&amp;gt;4n^3/3&amp;lt;/math&amp;gt; операций действительного умножения и сложения/вычитания.&lt;br /&gt;
&lt;br /&gt;
При классификации по последовательной сложности, таким образом, метод Хаусхолдера относится к алгоритмам &#039;&#039;с кубической сложностью&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
[[File:HausLeft1.png|thumb|right|600px|Рисунок 1. Граф нечётного полушага (обнуление &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;го столбца) алгоритма. Квадратики - входные данные шага (берутся с предыдущего полушага), кружки - операции. Зелёным выделены операции типа a+bb, салатовым и голубым - типа a+bc, тёмно-синим - вычисление &amp;lt;math&amp;gt;\gamma , v_{1}&amp;lt;/math&amp;gt;, жёлтым - умножения (или деления). Обведённая группа операций повторяется независимо n-i раз. Результаты синего и жёлтых кружков - выходные для алгоритма.]]&lt;br /&gt;
&lt;br /&gt;
На рисунке 1 изображён граф нечётного (&amp;lt;math&amp;gt;(2i-1)&amp;lt;/math&amp;gt;го) полушага в привязке к элементам обрабатываемой матрицы. Граф чётного (&amp;lt;math&amp;gt;2i&amp;lt;/math&amp;gt;го) полушага почти аналогичен, только длина ветвей (нарисована слева) будет меньше на 1 (&amp;lt;math&amp;gt;n-i&amp;lt;/math&amp;gt;), и для привязки к элементам матрицы его надо отразить относительно диагонали матрицы. После этого отражения графы полушагов можно положить друг на друга слоями, при этом правые нижние углы должны быть друг над другом.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Для понимания ресурса параллелизма в разложении матрицы порядка &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; методом Хаусхолдера нужно рассмотреть критический путь графа. &lt;br /&gt;
&lt;br /&gt;
Как видно из описания разных вершин, вычисления при &amp;quot;обнулении&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца параметров отражения и скалярных произведений состоят из основной части - ветви длиной по &amp;lt;math&amp;gt;n-i&amp;lt;/math&amp;gt; умножений и сложений - и коррекции вычислений, которые составляют &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; операций. Вычисления при &amp;quot;обнулении&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-й строки параметров отражения и скалярных произведений состоят из основной части - ветви длиной по &amp;lt;math&amp;gt;n-i-1&amp;lt;/math&amp;gt; умножений и сложений - и коррекции вычислений, которые составляют &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; операций.&lt;br /&gt;
&lt;br /&gt;
Поэтому по грубой (без членов низших порядков) оценке критический путь метода Хаусхолдера будет идти через &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt; умножений и &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt; сложений/вычитаний. &lt;br /&gt;
 &lt;br /&gt;
Поэтому в параллельном варианте, как и в последовательном, основную долю требуемого для выполнения алгоритма времени будут определять операции вида &amp;lt;math&amp;gt;a+bc&amp;lt;/math&amp;gt;.  &lt;br /&gt;
&lt;br /&gt;
При классификации по высоте ЯПФ, таким образом, метод Хаусхолдера относится к алгоритмам &#039;&#039;с квадратичной сложностью&#039;&#039;. При классификации по ширине ЯПФ его сложность будет также &#039;&#039;квадратичной&#039;&#039; (без расширения ряда ярусов, связанных с векторными операциями сложения, пришлось бы увеличить вдвое длину критического пути; при таком расширении сложность по ширине ЯПФ станет &#039;&#039;линейной&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
Надо сказать, что здесь в оценках речь идёт именно о классическом способе реализации метода Хаусхолдера. Даже использование схем сдваивания или последовательно-параллельных для вычисления скалярных произведений уменьшает критический путь с квадратичного до &#039;&#039;степени 3/2&#039;&#039; или &#039;&#039;линейно-логарифмического&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: плотная квадратная матрица &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; (элементы &amp;lt;math&amp;gt;a_{ij}&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;n^2+2n&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Соотношение последовательной и параллельной сложности, как хорошо видно, является &#039;&#039;линейным&#039;&#039;, что даёт определённый стимул для распараллеливания. Однако у наискорейшей ЯПФ ширина &#039;&#039;квадратична&#039;&#039;, что указывает на дисбаланс между загруженностями устройств при попытке её реально запрограммировать. Поэтому более практично даже при хорошей (быстрой) вычислительной сети оставить количество устройств (например, узлов кластера) &#039;&#039;линейным&#039;&#039; по размеру матрицы, что удвоит критический путь реализуемой ЯПФ. &lt;br /&gt;
&lt;br /&gt;
При этом вычислительная мощность алгоритма, как отношение числа операций к суммарному объему входных и выходных данных, &#039;&#039;линейна&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Алгоритм в рамках выбранной версии полностью детерминирован.&lt;br /&gt;
&lt;br /&gt;
Вычислительная погрешность в методе отражений (Хаусхолдера) растет &#039;&#039;линейно&#039;&#039;, как и в методе Гивенса (вращений).&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В варианте с кратчайшим критическим путём графа алгоритма (с использованием зависимости между обнуляемым вектором и направляющим вектором отражения) метод Хаусхолдера (отражений) приведения квадратной вещественной матрицы к двудиагональной форме на Фортране 77 можно записать так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;fortran&amp;quot;&amp;gt;&lt;br /&gt;
	DO  I = 1, N-1&lt;br /&gt;
            DO K = I, N&lt;br /&gt;
              SX(K)=A(N,I)*A(N,K)&lt;br /&gt;
            END DO&lt;br /&gt;
            DO J = N-1, I, -1&lt;br /&gt;
               DO K = I, N&lt;br /&gt;
                  SX(K)=SX(K)+A(J,I)*A(J,K)&lt;br /&gt;
               END DO&lt;br /&gt;
            END DO&lt;br /&gt;
&lt;br /&gt;
               ALPHA = SQRT (SX(I))&lt;br /&gt;
               IF (A(I,I).NE.0.) THEN&lt;br /&gt;
                  BETA = 1./ALPHA&lt;br /&gt;
                  DO J = I+1, N&lt;br /&gt;
                    A(J,I)=A(J,I)*BETA&lt;br /&gt;
                  END DO&lt;br /&gt;
                  SX(I) = A(I,I)*BETA+SIGN(1.,A(I,I))                     &lt;br /&gt;
                  A(I,I)=ALPHA&lt;br /&gt;
                  G=1./ABS(SX(I)) ! 1/gamma&lt;br /&gt;
                  DO K = I+1, N&lt;br /&gt;
                     SX(K)=SX(K)*BETA*G+SIGN(A(I,K),SX(I))&lt;br /&gt;
                     A(I,K) = A(I,K)+SX(K)*SX(I)&lt;br /&gt;
                     DO J = I+1, N&lt;br /&gt;
                       A (J,K) = A(J,K)-A(J,I)*SX(K)&lt;br /&gt;
                     END DO&lt;br /&gt;
                  END DO&lt;br /&gt;
               ELSE&lt;br /&gt;
                  IF (ALPHA.NE.0.) THEN&lt;br /&gt;
                     BETA = 1./ALPHA&lt;br /&gt;
                     DO J = I+1, N&lt;br /&gt;
                       A(J,I)=A(J,I)*BETA&lt;br /&gt;
                     END DO&lt;br /&gt;
                     SX(I) = -1. &lt;br /&gt;
                     A(I,I)=ALPHA&lt;br /&gt;
                     G=1.! 1/gamma&lt;br /&gt;
                     DO K = I+1, N&lt;br /&gt;
                        SX(K)=SX(K)*BETA*G+SIGN(A(I,K),SX(I))&lt;br /&gt;
                        A(I,K) = A(I,K)+SX(K)*SX(I)&lt;br /&gt;
                        DO J = I+1, N&lt;br /&gt;
                          A (J,K) = A(J,K)-A(J,I)*SX(K)&lt;br /&gt;
                        END DO&lt;br /&gt;
                     END DO&lt;br /&gt;
                  ELSE&lt;br /&gt;
                     SX(I)=1&lt;br /&gt;
                     G=2&lt;br /&gt;
                     DO K = I+1, N&lt;br /&gt;
                        SX(K)=2&lt;br /&gt;
                        A(I,K) = A(I,K)-SX(K)&lt;br /&gt;
                     END DO&lt;br /&gt;
                  END IF&lt;br /&gt;
               END IF               &lt;br /&gt;
&lt;br /&gt;
              IF (I.LT.N-1) THEN&lt;br /&gt;
&lt;br /&gt;
            DO K = I, N&lt;br /&gt;
              SL(K)=A(I,N)*A(K,N)&lt;br /&gt;
            END DO&lt;br /&gt;
            DO J = N-1, I+1, -1&lt;br /&gt;
               DO K = I, N&lt;br /&gt;
                  SL(K)=SL(K)+A(I,J)*A(K,J)&lt;br /&gt;
               END DO&lt;br /&gt;
            END DO&lt;br /&gt;
&lt;br /&gt;
               ALPHA = SQRT (SL(I))&lt;br /&gt;
               IF (A(I,I+1).NE.0.) THEN&lt;br /&gt;
                  BETA = 1./ALPHA&lt;br /&gt;
                  DO J = I+2, N&lt;br /&gt;
                    A(I,J)=A(I,J)*BETA&lt;br /&gt;
                  END DO&lt;br /&gt;
                  SL(I) = A(I,I+1)*BETA+SIGN(1.,A(I,I+1))                     &lt;br /&gt;
                  A(I,I+1)=ALPHA&lt;br /&gt;
                  G=1./ABS(SL(I)) ! 1/gamma&lt;br /&gt;
                  DO K = I+1, N&lt;br /&gt;
                     SL(K)=SL(K)*BETA*G+SIGN(A(K,I+1),SL(I))&lt;br /&gt;
                     A(K,I+1) = A(K,I+1)+SL(K)*SX(I)&lt;br /&gt;
                     DO J = I+2, N&lt;br /&gt;
                       A (K,J) = A(K,J)-A(I,J)*SL(K)&lt;br /&gt;
                     END DO&lt;br /&gt;
                  END DO&lt;br /&gt;
               ELSE&lt;br /&gt;
                  IF (ALPHA.NE.0.) THEN&lt;br /&gt;
                     BETA = 1./ALPHA&lt;br /&gt;
                     DO J = I+2, N&lt;br /&gt;
                       A(I,J)=A(I,J)*BETA&lt;br /&gt;
                     END DO&lt;br /&gt;
                     SL(I) = -1. &lt;br /&gt;
                     A(I,I+1)=ALPHA&lt;br /&gt;
                     G=1. ! 1/gamma&lt;br /&gt;
                     DO K = I+1, N&lt;br /&gt;
                        SL(K)=SL(K)*BETA*G+SIGN(A(K,I+1),SL(I))&lt;br /&gt;
                        A(K,I+1) = A(K,I+1)+SL(K)*SL(I)&lt;br /&gt;
                        DO J = I+2, N&lt;br /&gt;
                          A (K,J) = A(K,J)-A(I,J)*SL(K)&lt;br /&gt;
                        END DO&lt;br /&gt;
                     END DO&lt;br /&gt;
                  ELSE&lt;br /&gt;
                     SL(I)=1&lt;br /&gt;
                     G=2.&lt;br /&gt;
                     DO K = I+1, N&lt;br /&gt;
                        SL(K)=2.&lt;br /&gt;
                        A(K,I+1) = A(K,I+1)-SL(K)&lt;br /&gt;
                     END DO&lt;br /&gt;
                  END IF&lt;br /&gt;
               END IF   &lt;br /&gt;
&lt;br /&gt;
              END IF&lt;br /&gt;
	END DO&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В этом варианте D расположена в диагонали и верхней кодиагонали массива A, направляющие вектора матриц отражений размещены в поддиагональных элементах соответствующих столбцов или надддиагональных - строк, а их первые элементы - в элементах массивов SX и SL.&lt;br /&gt;
&lt;br /&gt;
Обычно же в последовательных версиях коэффициенты модификаций столбцов вычисляются целиком через скалярные произведения после вычислений параметров матрицы отражения. При этом схема чуть проще. Удлиняется критический путь графа, но для последовательных реализаций это неважно.&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
К сожалению, в графе, как видно по рисунку, в наличии пучки рассылок, в них неизбежно часть дуг остаются длинными, что отрицательно влияет на локальность вычислений по пространству.  &lt;br /&gt;
&lt;br /&gt;
==== Локальность реализации алгоритма ====&lt;br /&gt;
===== Структура обращений в память и качественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qdu_1.png|thumb|center|700px|Рисунок 2. Метод Хаусхолдера (отражений) приведения матрицы к двухдиагональной форме. Общий профиль обращений в память]]&lt;br /&gt;
&lt;br /&gt;
На рис. 2 представлен профиль обращений в память для реализации метода Хаусхолдера (отражений) приведения матрицы к двухдиагональной форме. Данный профиль обладает явной итерационной структурой, причем видно, что на каждой следующей итерации из рассмотрения отбрасывается небольшая часть элементов из начала массива данных. Также можно заметить, что основная часть данных используется достаточно равномерно в рамках одной итерации, однако в нижней и верхней частях итерации видны небольшие блоки с очень высокой локальностью обращений. Итерации явно устроены по одному принципу, поэтому стоит рассмотреть одну из них более детально.&lt;br /&gt;
&lt;br /&gt;
На рис. 3 показаны обращения в рамках первой итерации общего профиля (выделена на рис. 2 зеленым). Эти обращения можно условно разбить на несколько фрагментов. Фрагменты 1, 2 и 6 практически идентичны (различие заключается только в перестановке местами двух частей этих фрагментов), рассмотрим один из них.&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qdu_2.png|thumb|center|700px|Рисунок 3. Профиль обращений, одна итерация]]&lt;br /&gt;
&lt;br /&gt;
На рис. 4 показан отдельно фрагмент 6, выделенный на рис. 3. Теперь можно определить структуру каждой из двух частей более точно. Первая часть представляет собой последовательный перебор элементов в обратном порядке, причем к каждому элементу выполняется несколько обращений подряд. Во второй части также наблюдается последовательный перебор, однако с другими свойствами: элементы перебираются в прямом порядке, к каждому элементу выполняется только 1 обращение, однако данный перебор повторяется несколько раз подряд, причем каждый раз задействованы одни и те же элементы. Все это позволяет говорить о высокой пространственной локальности фрагмента (поскольку шаг по памяти каждый раз небольшой) и достаточно высокой временной локальности (поскольку данные часто используются повторно).&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qdu_3.png|thumb|center|500px|Рисунок 4. Профиль обращений, одна итерация, фрагмент 6]]&lt;br /&gt;
&lt;br /&gt;
Свойства локальности во фрагментах 3 и 5 можно оценить и без более детального рассмотрения. Данные в обоих фрагментах перебираются с достаточно большим шагом по памяти, при этом повторно они почти не используются, что говорит о низкой локальности в обоих случаях (хотя надо отметить, что локальность фрагмента 5 немного выше из-за более высокой пространственной локальности).&lt;br /&gt;
&lt;br /&gt;
Теперь перейдем к рассмотрению фрагмента 4, расположенного в центре рис. 3. Поскольку он обладает регулярной структурой, достаточно более детально изучить небольшой фрагмент (представлен на рис. 5, выделен на рис. 3 зеленым). Здесь можно увидеть, что первая часть фрагмента представляет собой практически стандартный последовательный перебор (с небольшими нечастыми сдвигами, которые не влияют на локальность). Такой набор обращений обладает высокой пространственной и низкой временной локальностью. Вторая часть данного фрагмента устроена несколько сложнее – она состоит из L-образных наборов обращений. Одна его область (часть 1 на рис. 5) по строению очень похожа на первую часть; вторая (часть 2 на рис. 5) представляет собой набор обращений к одному и тому же элементу, что достаточно сильно повышает локальность данного фрагмента. Из этого можно сделать вывод, что фрагмент 4 обладает высокой пространственной и средней временной локальностью.&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qdu_4.png|thumb|center|500px|Рисунок 5. Профиль обращений, одна итерация, часть фрагмент 4]]&lt;br /&gt;
&lt;br /&gt;
Суммируя выше сказанное, можно сказать, что фрагменты 1, 2 и 6 обладают высокой локальностью; фрагменты 3 и 5 – низкой локальностью; фрагмент 4 – высокой пространственной и средней временной локальностью. Можно предположить, что в итоге локальность общего профиля будет не очень высокой, в основном из-за более низкой временной локальности. Однако такое достаточно сложное строение общего профиля затрудняет оценку локальности общего профиля на основе визуального анализа.&lt;br /&gt;
&lt;br /&gt;
===== Количественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
Оценка выполняется на основе характеристики daps, которая оценивает число выполненных обращений (чтений и записей) в память в секунду. Данная характеристика является аналогом оценки flops применительно к работе с памятью и является в большей степени оценкой производительности взаимодействия с памятью, чем оценкой локальности. Однако она служит хорошим источником информации, в том числе для сравнения с результатами по следующей характеристике cvg.&lt;br /&gt;
&lt;br /&gt;
На рисунке 6 приведены значения daps для реализаций распространенных алгоритмов, отсортированные по возрастанию (чем больше daps, тем в общем случае выше производительность). Можно увидеть, что оценка производительности в данном случае не очень высока, что подтверждает сделанные ранее выводы. В частности, значение daps для этого примера показывает более низкую производительность по сравнению с реализациями других методов Хаусхолдера (householder_qr householder_qtq) и находится примерно на уровне оценки для реализации метода Холецкого.&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qdu_daps.png|thumb|center|700px|Рисунок 6. Сравнение значений оценки daps]]&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
==== Масштабируемость алгоритма ====&lt;br /&gt;
==== Масштабируемость реализации алгоритма ====&lt;br /&gt;
Проведём исследование масштабируемости вширь реализации согласно [[Scalability methodology|методике]]. Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов-2&amp;quot; [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Набор и границы значений изменяемых [[Глоссарий#Параметры запуска|параметров запуска]] реализации алгоритма: &lt;br /&gt;
&lt;br /&gt;
*	число процессоров [16:256] с шагом n^2;&lt;br /&gt;
*	размер матрицы [1000 : 32000] с шагом 1000.&lt;br /&gt;
&lt;br /&gt;
В результате проведённых экспериментов был получен следующий диапазон [[Глоссарий#Эффективность реализации|эффективности реализации]] алгоритма:&lt;br /&gt;
&lt;br /&gt;
*	минимальная эффективность реализации 7.511e-06%;&lt;br /&gt;
*	максимальная эффективность реализации 0.0746%.&lt;br /&gt;
&lt;br /&gt;
На следующих рисунках приведены графики [[Глоссарий#Производительность|производительности]] и эффективности выбранной реализации алгоритма в процедуре PDGEHRD из Scalapack в зависимости от изменяемых параметров запуска.&lt;br /&gt;
&lt;br /&gt;
[[file:PDGEHRD perf2.png|thumb|center|700px|Рисунок 8. Параллельная реализация метода Хаусхолдера для приведения матриц к двухдиагональному виду. Изменение производительности в зависимости от числа процессоров и размера матрицы.]]&lt;br /&gt;
[[file:PDGEHRD eff2.png|thumb|center|700px|Рисунок 9. Параллельная реализация метода Хаусхолдера для приведения матриц к двухдиагональному виду. Изменение эффективности в зависимости от числа процессоров и размера матрицы.]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
В сравнении с методом Гивенса, который имеет естественное двумерное блочное разбиение на основе точечного метода, метод Хаусхолдера из-за худших характеристик локальности (наличие пучков рассылок) и меньшего количества независимых обобщённых развёрток графа не так хорош для реализаций на системах с распределённой памятью, как для систем с общей памятью. Поэтому на массово параллельных системах с распределённой памятью следует применять метод Хаусхолдера (если уж именно его нужно реализовать) не в точечной версии, а в разрабатываемых исследователями блочных вариантах. Следует отметить, что эти варианты - не блочная нарезка описанного метода, а самостоятельные методы. Особенно их применение рекомендуется в случаях с большой разрежённостью матрицы.&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Большинство пакетов от LINPACKа и LAPACKa до SCALAPACKa используют для приведения матриц к двудиагональному виду именно метод Хаусхолдера, правда, в различных модификациях (обычно с использованием BLAS). Существует большая подборка исследовательских работ по блочным версиям.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Статьи в работе]]&lt;br /&gt;
[[Категория:Разложения матриц]]&lt;/div&gt;</summary>
		<author><name>Teplov</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D0%A5%D0%B0%D1%83%D1%81%D1%85%D0%BE%D0%BB%D0%B4%D0%B5%D1%80%D0%B0_(%D0%BE%D1%82%D1%80%D0%B0%D0%B6%D0%B5%D0%BD%D0%B8%D0%B9)_%D0%B4%D0%BB%D1%8F_%D0%BF%D1%80%D0%B8%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D1%8F_%D1%81%D0%B8%D0%BC%D0%BC%D0%B5%D1%82%D1%80%D0%B8%D1%87%D0%BD%D1%8B%D1%85_%D0%BC%D0%B0%D1%82%D1%80%D0%B8%D1%86_%D0%BA_%D1%82%D1%80%D1%91%D1%85%D0%B4%D0%B8%D0%B0%D0%B3%D0%BE%D0%BD%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%BC%D1%83_%D0%B2%D0%B8%D0%B4%D1%83&amp;diff=21500</id>
		<title>Метод Хаусхолдера (отражений) для приведения симметричных матриц к трёхдиагональному виду</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D0%A5%D0%B0%D1%83%D1%81%D1%85%D0%BE%D0%BB%D0%B4%D0%B5%D1%80%D0%B0_(%D0%BE%D1%82%D1%80%D0%B0%D0%B6%D0%B5%D0%BD%D0%B8%D0%B9)_%D0%B4%D0%BB%D1%8F_%D0%BF%D1%80%D0%B8%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D1%8F_%D1%81%D0%B8%D0%BC%D0%BC%D0%B5%D1%82%D1%80%D0%B8%D1%87%D0%BD%D1%8B%D1%85_%D0%BC%D0%B0%D1%82%D1%80%D0%B8%D1%86_%D0%BA_%D1%82%D1%80%D1%91%D1%85%D0%B4%D0%B8%D0%B0%D0%B3%D0%BE%D0%BD%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%BC%D1%83_%D0%B2%D0%B8%D0%B4%D1%83&amp;diff=21500"/>
		<updated>2016-12-28T12:47:29Z</updated>

		<summary type="html">&lt;p&gt;Teplov: /* Масштабируемость реализации алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Приведение симметричной вещественной матрицы к трёхдиагональному виду методом Хаусхолдера (отражений)&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(n^3)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;2n^2+O(n)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;n^2/2&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;(n^2+n)/2&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;(n^2+3n)/2&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
Основные авторы описания: [[Участник:Frolov|А.В.Фролов]]&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Метод Хаусхолдера&#039;&#039;&#039; (в советской математической литературе чаще называется &#039;&#039;&#039;методом отражений&#039;&#039;&#039;) используется для приведения симметричных вещественных матриц к трёхдиагональному виду, или, что то же самое, для разложения &amp;lt;math&amp;gt;A=QTQ^T&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; - ортогональная, &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; — симметричная трёхдиагональная матрица)&amp;lt;ref&amp;gt;В.В.Воеводин, Ю.А.Кузнецов. Матрицы и вычисления. М.: Наука, 1984.&amp;lt;/ref&amp;gt;. При этом матрица &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; хранится и используется не в своём явном виде, а в виде произведения матриц отражения&amp;lt;ref name=&amp;quot;VOLA&amp;quot;&amp;gt;Воеводин В.В. Вычислительные основы линейной алгебры. М.: Наука, 1977.&amp;lt;/ref&amp;gt;. Каждая из матриц отражения может быть определена одним вектором. Это позволяет в классическом исполнении метода отражений хранить результаты разложения на месте матрицы A с использованием одномерного дополнительного массива. &lt;br /&gt;
&lt;br /&gt;
В данной статье рассматривается именно классическое исполнение, в котором не используются приёмы типа сдваивания при вычислениях скалярных произведений.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В методе Хаусхолдера для выполнения &amp;lt;math&amp;gt;QTQ^T&amp;lt;/math&amp;gt;-разложения матрицы используются умножения слева её текущих модификаций на матрицы Хаусхолдера (отражений) с последующим умножением на те же матрицы справа. &lt;br /&gt;
&lt;br /&gt;
{{Шаблон:Матрица отражений}}&lt;br /&gt;
&lt;br /&gt;
На &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м шаге метода с помощью преобразования отражения &amp;quot;убираются&amp;quot; ненулевые поддиагональные элементы, начиная с &amp;lt;math&amp;gt;i+2&amp;lt;/math&amp;gt;-го в &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м столбце. После умножения на эту же матрицу отражения справа автоматически убираются и ненулевые наддиагональные элементы, начиная с &amp;lt;math&amp;gt;i+2&amp;lt;/math&amp;gt;-го в &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-й строке, а полученная модификация снова приобретает симметричный вид. &lt;br /&gt;
&lt;br /&gt;
На каждом из шагов метода матрицу отражений обычно представляют не в стандартном виде, а в виде &amp;lt;math&amp;gt;U=E-\frac{1}{\gamma}vv^*&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; находится через координаты текущего &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; - вектор размерности &amp;lt;math&amp;gt;n+1-i&amp;lt;/math&amp;gt;, составленный из элементов &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца, начиная с &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;(s,s)=0&amp;lt;/math&amp;gt;, то &amp;lt;math&amp;gt;v=e_{i}&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\gamma = \frac{1}{2}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В остальных случаях по алгоритму вычисляется &amp;lt;math&amp;gt;u = \frac{1}{\sqrt{(s,s)}}s&amp;lt;/math&amp;gt;, и далее &amp;lt;math&amp;gt;v_{j}=0&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;j&amp;lt;i&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;v_{j}=u_{j-i+1}&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;j&amp;gt;i&amp;lt;/math&amp;gt;, а &amp;lt;math&amp;gt;v_{i}=1&amp;lt;/math&amp;gt;, если &amp;lt;math&amp;gt;u_{1}=0&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v_{i}=\frac{u_{1}}{|u_{1}|}(1+|u_{1}|)&amp;lt;/math&amp;gt; для остальных значений. При этом &amp;lt;math&amp;gt;\gamma = 1+|u_{1}|=|v_{i}|&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
После вычисления вектора &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; подстолбцы справа от ведущего модифицируются по формулам &amp;lt;math&amp;gt;x&#039;=x-\frac{(x,v)}{\gamma}v&amp;lt;/math&amp;gt;. Потом по аналогичным формулам модифицируются строки ниже ведущей. Благодаря ассоциативности этих операций после вычисления вектора &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; можно сразу выписать формулы модификации всех элементов справа и снизу от ведущих столбца и строки. Оказывается, что если для каждого столбца матрицы &amp;lt;math&amp;gt;x^{(j)}&amp;lt;/math&amp;gt; с номером &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; известно &amp;lt;math&amp;gt;\beta_{j}=\frac{(x^{(j)},v)}{\gamma}&amp;lt;/math&amp;gt;, то для модифицируемого элемента матрицы &amp;lt;math&amp;gt;y&amp;lt;/math&amp;gt; в позиции &amp;lt;math&amp;gt;(i,j)&amp;lt;/math&amp;gt; выполняется модификация &amp;lt;math&amp;gt;y&#039; = y - \beta_{i} v_{i} - \beta_{j} v_{j} - \Delta v_{i} v_{j}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\Delta = \frac{(\beta,v)}{\gamma}&amp;lt;/math&amp;gt;. При этом все эти модификации на каждом конкретном шаге алгоритма для разных пар &amp;lt;math&amp;gt;(i,j)&amp;lt;/math&amp;gt; можно выполнять независимо друг от друга, в том числе и параллельно.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
&lt;br /&gt;
[[File:HausLeftSym1.png|thumb|right|800px|Рисунок 1. Граф первой половины шага алгоритма приведения к трёхдиагональному виду с отображением входных данных. Изображён граф первой половины шага с номером &amp;lt;math&amp;gt;n-4&amp;lt;/math&amp;gt;. Квадраты - результаты выполнения предыдущего шага. Если шаг первый, то это входные данные. Зелёные кружки - операция вида &amp;lt;math&amp;gt;a+b^2&amp;lt;/math&amp;gt;, салатовые - операция вида &amp;lt;math&amp;gt;a+bc&amp;lt;/math&amp;gt;. Синий кружок - вычисление параметров матрицы отражения, светло-красные - вычисление коэффициентов &amp;lt;math&amp;gt;\beta_i&amp;lt;/math&amp;gt;для следующего полушага, жёлтые - вычисление вектора &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.]]&lt;br /&gt;
[[File:HousLeftSym2.png|thumb|right|600px|Рисунок 2. Граф второй половины шага алгоритма приведения к трёхдиагональному виду с отображением входных данных. Изображён граф первой половины шага с номером &amp;lt;math&amp;gt;n-3&amp;lt;/math&amp;gt;. Квадраты - результаты выполнения предыдущего шага. Если шаг первый, то это входные данные. Синий, жёлтые и светло-красные кружки выполняются на первом полушаге (см. Рисунок 1). Голубые кружки -  операции &amp;lt;math&amp;gt;a+bc&amp;lt;/math&amp;gt;, зелёное - умножение, чёрные - операции &amp;lt;math&amp;gt;a+bc+de+fce&amp;lt;/math&amp;gt;.]]&lt;br /&gt;
&lt;br /&gt;
Основную часть алгоритма составляют вычисления на каждом шагу скалярных произведений &amp;lt;math&amp;gt;(s,s)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(x,v)&amp;lt;/math&amp;gt; для всех подстолбцов &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt;справа от текущего, а также проводимые над нижним правым квадратом матрицы операции вида &amp;lt;math&amp;gt;y&#039;=y-ab-cd-fbd&amp;lt;/math&amp;gt;, с учётом симметрии матрицы.&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Как уже сказано в описании ядра, основная часть - вычисления на каждом шагу скалярных произведений &amp;lt;math&amp;gt;(s,s)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(x,v)&amp;lt;/math&amp;gt; для всех подстолбцов &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; справа от текущего, а также массовые покомпонентные операции &amp;lt;math&amp;gt;y&#039;=y-ab-cd-fbd&amp;lt;/math&amp;gt;. При этом, однако, строгая последовательность выполнения первых двух подшагов не обязательна, в силу связи получаемых векторов &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; можно одновременно с &amp;lt;math&amp;gt;(s,s)&amp;lt;/math&amp;gt; вычислять и произведения &amp;lt;math&amp;gt;(x,s)&amp;lt;/math&amp;gt; с последующим выражением через них коэффициентов модификации. Это позволяет почти вдвое уменьшать критический путь графа алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Последовательность выполнения алгоритма обычно записывается как последовательное &amp;quot;обнуление&amp;quot; поддиагональных элементов столбцов, начиная с 1-го столбца и заканчивая предпоследним &amp;lt;math&amp;gt;(n-1)&amp;lt;/math&amp;gt;-м.&lt;br /&gt;
&lt;br /&gt;
При этом в каждом &amp;quot;обнуляемом&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м столбце &amp;quot;обнуляются&amp;quot; сразу все его поддиагональные элементы одновременно, с &amp;lt;math&amp;gt;(i+1)&amp;lt;/math&amp;gt;-го до &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;-го. &lt;br /&gt;
&lt;br /&gt;
Каждое &amp;quot;обнуление&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца состоит из двух шагов: &lt;br /&gt;
а) вычисление параметров матрицы отражения &amp;lt;math&amp;gt;U_{i}&amp;lt;/math&amp;gt; такой, чтобы при умножении на неё слева &amp;quot;обнулились&amp;quot; все поддиагональные его элементы;&lt;br /&gt;
б) одновременное умножение слева матрицы отражения &amp;lt;math&amp;gt;U_{i}&amp;lt;/math&amp;gt; и справа  матрицы отражения &amp;lt;math&amp;gt;U_{i}^T&amp;lt;/math&amp;gt;на текущую версию матрицы.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В последовательной версии основная сложность алгоритма определяется прежде всего вычислениями скалярных произведений векторов, а также массовых модификаций элементов вида &amp;lt;math&amp;gt;y&#039;=y-\alpha v - \beta w - \Delta vw&amp;lt;/math&amp;gt;. Они, если не учитывать возможную разреженность, составляют (в главном члене) по &amp;lt;math&amp;gt;O(n^3)&amp;lt;/math&amp;gt; операций действительного умножения и сложения/вычитания.&lt;br /&gt;
&lt;br /&gt;
При классификации по последовательной сложности, таким образом, метод Хаусхолдера относится к алгоритмам &#039;&#039;с кубической сложностью&#039;&#039;.&lt;br /&gt;
&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;n&amp;lt;/math&amp;gt; к трёхдиагональной методом Хаусхолдера нужно рассмотреть критический путь графа. &lt;br /&gt;
&lt;br /&gt;
Как видно из описания разных вершин, вычисления при &amp;quot;обнулении&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца параметров отражения и скалярных произведений состоят из основной части - ветви длиной по &amp;lt;math&amp;gt;2(n-i)&amp;lt;/math&amp;gt; умножений и сложений - и коррекции вычислений, которые составляют &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; операций. &lt;br /&gt;
&lt;br /&gt;
Поэтому по грубой (без членов низших порядков) оценке критический путь метода Хаусхолдера будет идти через &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt; умножений и &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt; сложений/вычитаний. &lt;br /&gt;
 &lt;br /&gt;
Поэтому в параллельном варианте, как и в последовательном, основную долю требуемого для выполнения алгоритма времени будут определять операции вида &amp;lt;math&amp;gt;a+bc&amp;lt;/math&amp;gt;.  &lt;br /&gt;
&lt;br /&gt;
При классификации по высоте ЯПФ, таким образом, метод Хаусхолдера относится к алгоритмам &#039;&#039;с квадратичной сложностью&#039;&#039;. При классификации по ширине ЯПФ его сложность будет также &#039;&#039;квадратичной&#039;&#039; (без расширения ряда ярусов, связанных с векторными операциями сложения, пришлось бы увеличить вдвое длину критического пути).&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: плотная симметричная квадратная матрица &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; (элементы &amp;lt;math&amp;gt;a_{ij}&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;(n^2+n)/2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: трёхдиагональная матрица &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; (ненулевые элементы &amp;lt;math&amp;gt;r_{ij}&amp;lt;/math&amp;gt; в последовательном варианте хранятся в элементах исходной матрицы &amp;lt;math&amp;gt;a_{ij}&amp;lt;/math&amp;gt;), унитарная (ортогональная) матрица Q - как произведение матриц Хаусхолдера (отражения) (их вектора нормалей к плоскостям отражения в последовательном варианте хранятся в поддиагональных элементах исходной матрицы &amp;lt;math&amp;gt;a_{ij}&amp;lt;/math&amp;gt; и в одном дополнительном столбце размерности n).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;(n^2+3n)/2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Соотношение последовательной и параллельной сложности, как хорошо видно, является &#039;&#039;линейным&#039;&#039;, что даёт определённый стимул для распараллеливания. Однако у наискорейшей ЯПФ ширина &#039;&#039;квадратична&#039;&#039;, что указывает на дисбаланс между загруженностями устройств при попытке её реально запрограммировать. Поэтому более практично даже при хорошей (быстрой) вычислительной сети оставить количество устройств (например, узлов кластера) &#039;&#039;линейным&#039;&#039; по размеру матрицы, что удвоит критический путь реализуемой ЯПФ. &lt;br /&gt;
&lt;br /&gt;
При этом вычислительная мощность алгоритма, как отношение числа операций к суммарному объему входных и выходных данных, &#039;&#039;линейна&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Алгоритм в рамках выбранной версии полностью детерминирован.&lt;br /&gt;
&lt;br /&gt;
Вычислительная погрешность в методе отражений (Хаусхолдера) растет &#039;&#039;линейно&#039;&#039;, как и в методе Гивенса (вращений).&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
В варианте с кратчайшим критическим путём графа алгоритма (с использованием зависимости между обнуляемым вектором и направляющим вектором отражения) метод Хаусхолдера (отражений) приведения квадратной симметричной вещественной матрицы к трёхдиагональному виду на Фортране 77 можно записать так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;fortran&amp;quot;&amp;gt;&lt;br /&gt;
	DO  I = 1, N-2&lt;br /&gt;
&lt;br /&gt;
            DO K = I, N&lt;br /&gt;
              SX(K)=A(N,I)*A(N,K)&lt;br /&gt;
            END DO&lt;br /&gt;
            DO J = N-1, I+1, -1&lt;br /&gt;
                 SX(I)=SX(I)+A(J,I)*A(J,I)&lt;br /&gt;
            END DO &lt;br /&gt;
            DO K = I+1, N&lt;br /&gt;
              DO J = N-1, K, -1&lt;br /&gt;
                 SX(K)=SX(K)+A(J,I)*A(J,K)&lt;br /&gt;
              END DO&lt;br /&gt;
              DO J = K-1,I+1,-1&lt;br /&gt;
                 SX(K)=SX(K)+A(J,I)*A(K,J)&lt;br /&gt;
              END DO&lt;br /&gt;
            END DO&lt;br /&gt;
&lt;br /&gt;
               ALPHA = SQRT (SX(I))&lt;br /&gt;
               IF (A(I+1,I).NE.0.) THEN&lt;br /&gt;
                  BETA = 1./ALPHA&lt;br /&gt;
                  DO J = I+2, N&lt;br /&gt;
                    A(J,I)=A(J,I)*BETA&lt;br /&gt;
                  END DO&lt;br /&gt;
                  SX(I) = A(I+1,I)*BETA+SIGN(1.,A(I+1,I))                     &lt;br /&gt;
                  A(I+1,I)=ALPHA&lt;br /&gt;
                  G=1./ABS(SX(I)) ! 1/gamma&lt;br /&gt;
                  SX2=0.&lt;br /&gt;
                  DO K = I+2, N&lt;br /&gt;
                     SX(K)=SX(K)*BETA*G+SIGN(A(K,I+1),SX(I))&lt;br /&gt;
                     SX2=SX(K)*A(K,I)+SX2                     &lt;br /&gt;
                  END DO&lt;br /&gt;
                  SX2=G*SX2&lt;br /&gt;
                  DO K = I+2, N&lt;br /&gt;
                     A(K,K) = A(K,K)-2*A(K,I)*SX(K)+SX2*A(K,I)**2&lt;br /&gt;
                     DO J = K+1, N&lt;br /&gt;
                       A (J,K) = A(J,K)-A(J,I)*SX(K)-A(K,I)*SX(I)+SX2*A(J,I)*A(K,I)&lt;br /&gt;
                     END DO&lt;br /&gt;
                  END DO&lt;br /&gt;
               ELSE&lt;br /&gt;
                  IF (ALPHA.NE.0.) THEN&lt;br /&gt;
                     BETA = 1./ALPHA&lt;br /&gt;
                     DO J = I+2, N&lt;br /&gt;
                       A(J,I)=A(J,I)*BETA&lt;br /&gt;
                     END DO&lt;br /&gt;
                     SX(I) = -1. &lt;br /&gt;
                     A(I+1,I)=ALPHA&lt;br /&gt;
                     G=1.! 1/gamma&lt;br /&gt;
                     SX2=0.&lt;br /&gt;
                     DO K = I+2, N&lt;br /&gt;
                        SX(K)=SX(K)*BETA*G+SIGN(A(K,I+1),SX(I))&lt;br /&gt;
                        SX2=SX(K)*A(K,I)+SX2                 &lt;br /&gt;
                     END DO&lt;br /&gt;
                     SX2=G*SX2&lt;br /&gt;
                     DO K = I+2, N  &lt;br /&gt;
                        A(K,K) = A(K,K)-2*A(K,I)*SX(K)+SX2*A(K,I)**2                   &lt;br /&gt;
                        DO J = K+1, N&lt;br /&gt;
                          A (J,K) = A(J,K)-A(J,I)*SX(K)-A(K,I)*SX(I)+SX2*A(J,I)*A(K,I)&lt;br /&gt;
                        END DO&lt;br /&gt;
                     END DO&lt;br /&gt;
                  ELSE&lt;br /&gt;
                     SX(I)=1.&lt;br /&gt;
                  END IF&lt;br /&gt;
               END IF               &lt;br /&gt;
&lt;br /&gt;
		&lt;br /&gt;
	END DO&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Здесь симметричная трёхдиагональная матрица хранится в диагонали и нижней кодиагонали массива A, вектора v - в поддиагональной части массива, за исключением первых их элементов, для которых выделен массив SX.&lt;br /&gt;
&lt;br /&gt;
Обычно же в последовательных версиях коэффициенты модификаций столбцов вычисляются целиком через скалярные произведения после вычислений параметров матрицы отражения. При этом схема чуть проще. Удлиняется критический путь графа, но для последовательных реализаций это неважно.&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
К сожалению, в графе, как видно по рисунку, в наличии пучки рассылок, в них неизбежно часть дуг остаются длинными, что отрицательно влияет на локальность вычислений по пространству.  &lt;br /&gt;
&lt;br /&gt;
==== Локальность реализации алгоритма ====&lt;br /&gt;
===== Структура обращений в память и качественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qtq_1.png|thumb|center|700px|Рисунок 3. Метод Хаусхолдера (отражений) для приведения симметричных матриц к трёхдиагональному виду. Общий профиль обращений в память]]&lt;br /&gt;
&lt;br /&gt;
На рис. 3 представлен профиль обращений в память для реализации метода Хаусхолдера (отражений) для приведения симметричных матриц к трёхдиагональному виду. Данный профиль обладает явной итерационной структурой, при этом видно, что итерации очень похожи. Основное их отличие заключается в наборе используемых данных – на каждой следующей итерации несколько первых элементов отбрасываются их рассмотрения, то есть чем позже итерация, тем меньше данных в ней задействовано. Также можно отметить, что число обращений в каждой последующей итерации немного уменьшается. Для того чтобы проанализировать общий профиль, в таком случае чаще всего достаточно изучить одну итерацию. Рассмотрим самую первую из них более детально.&lt;br /&gt;
&lt;br /&gt;
На рис. 4 показан набор обращений в рамках первой итерации (выделен на рис. 3 зеленым). Его можно разбить на несколько фрагментов и рассмотреть их отдельно. Строение фрагментов 2-5 можно оценить по общему графику для итерации. Фрагмент 2 представляет собой набор последовательных переборов в обратном порядке с небольшим шагом, причем число элементов в переборе постепенно уменьшается. Такое строение характеризуется достаточно высокой пространственной локальностью, поскольку часто происходит обращений к близко расположенным данным, однако низкой временной, поскольку данные повторно не используются. То же самое верно и для фрагмента 4, основное отличие которого заключается в том, что переборы выполняются в прямом порядке. Отметим, что некоторое искривление данного фрагмента связано не со строением самого фрагмента, а с разным числом параллельно выполняющихся обращений (см., например, правую область фрагмента 6), чего не наблюдается при выполнении фрагмента 2.&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qtq_2.png|thumb|center|700px|Рисунок 4. Профиль обращений, одна итерация]]&lt;br /&gt;
&lt;br /&gt;
Далее, фрагменты  3 и 5 состоят из небольшого числа обращений к данным, расположенным далеко друг от друга. Такие фрагменты характеризуются низкой пространственной и временной локальностью.&lt;br /&gt;
&lt;br /&gt;
Оставшиеся фрагменты требуют более детального рассмотрения. Перейдем к изучению фрагмента 1, который целиком представлен на рис. 5. Здесь мы можем видеть несколько этапов, каждый из которых представляет собой последовательный перебор элементов с шагом по памяти 1, причем в некоторых случаях данные много раз используются повторно (особенно это заметно в правой нижней области рисунка, где выполняется множество обращений к одному и тому же элементу). При условии, что в данном фрагменте задействовано всего около 40 элементов, можно говорить, что данный набор обращений обладает высокой пространственной и временной локальностью. &lt;br /&gt;
&lt;br /&gt;
[[file:householder_qtq_3.png|thumb|center|500px|Рисунок 5. Профиль обращений, одна итерация, фрагмент 1]]&lt;br /&gt;
&lt;br /&gt;
Далее рассмотрим детально фрагмент 6 (рис. 6). Здесь можно увидеть, что повторные обращения к данным выполняются не подряд, а через некоторые промежутки, однако число задействованных элементов также очень невелико, так что и в этом случае локальность (и пространственная, и временная) будет высокой.&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qtq_4.png|thumb|center|500px|Рисунок 6. Профиль обращений, одна итерация, фрагмент 6]]&lt;br /&gt;
&lt;br /&gt;
В целом можно сказать, что рассматриваемая итерация обладает достаточно высокой пространственной и временной локальностью. Поскольку остальные итерации устроены подобным образом, данные вывод можно перенести и на общий профиль обращений.&lt;br /&gt;
&lt;br /&gt;
===== Количественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
Оценка выполняется на основе характеристики daps, которая оценивает число выполненных обращений (чтений и записей) в память в секунду. Данная характеристика является аналогом оценки flops применительно к работе с памятью и является в большей степени оценкой производительности взаимодействия с памятью, чем оценкой локальности. Однако она служит хорошим источником информации, в том числе для сравнения с результатами по следующей характеристике cvg.&lt;br /&gt;
&lt;br /&gt;
На рисунке 7 приведены значения daps для реализаций распространенных алгоритмов, отсортированные по возрастанию (чем больше daps, тем в общем случае выше производительность). Можно увидеть, что производительность работы с памятью в данном случае достаточно высока – значение daps лишь немногим уступает тесту Linpack, который обладает высокой эффективностью работы с памятью, и немного превосходит, например, реализацию метода Якоби или метода Гаусса.&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qtq_daps.png|thumb|center|700px|Рисунок 7. Сравнение значений оценки daps]]&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
==== Масштабируемость алгоритма ====&lt;br /&gt;
==== Масштабируемость реализации алгоритма ====&lt;br /&gt;
Проведём исследование масштабируемости вширь реализации согласно [[Scalability methodology|методике]]. Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов-2&amp;quot; [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Набор и границы значений изменяемых [[Глоссарий#Параметры запуска|параметров запуска]] реализации алгоритма: &lt;br /&gt;
&lt;br /&gt;
*	число процессоров [16:81] с шагом n^2;&lt;br /&gt;
*	размер матрицы [1000 : 16000] с шагом 1000.&lt;br /&gt;
&lt;br /&gt;
В результате проведённых экспериментов был получен следующий диапазон [[Глоссарий#Эффективность реализации|эффективности реализации]] алгоритма:&lt;br /&gt;
&lt;br /&gt;
*	минимальная эффективность реализации 4.763e-05%;&lt;br /&gt;
*	максимальная эффективность реализации 0.011%.&lt;br /&gt;
&lt;br /&gt;
На следующих рисунках приведены графики [[Глоссарий#Производительность|производительности]] и эффективности выбранной реализации алгоритма в процедуре PDSYTRD из Scalapack в зависимости от изменяемых параметров запуска.&lt;br /&gt;
&lt;br /&gt;
[[file:PDSYTRD Perf2.png|thumb|center|700px|Рисунок 8. Параллельная реализация метода Хаусхолдера QR-разложения. Изменение производительности в зависимости от числа процессоров и размера матрицы.]]&lt;br /&gt;
[[file:PDSYTRD eff2.png|thumb|center|700px|Рисунок 9. Параллельная реализация метода Хаусхолдера QR-разложения. Изменение эффективности в зависимости от числа процессоров и размера матрицы.]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
В сравнении с методом Гивенса, который имеет естественное двумерное блочное разбиение на основе точечного метода, метод Хаусхолдера из-за худших характеристик локальности (наличие пучков рассылок) и меньшего количества независимых обобщённых развёрток графа не так хорош для реализаций на системах с распределённой памятью, как для систем с общей памятью. Поэтому на массово параллельных системах с распределённой памятью следует применять метод Хаусхолдера (если уж именно его нужно реализовать) не в точечной версии, а в разрабатываемых исследователями блочных вариантах. Следует отметить, что эти варианты - не блочная нарезка описанного метода, а самостоятельные методы. Особенно их применение рекомендуется в случаях с большой разрежённостью матрицы.&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Большинство пакетов от LINPACKа и LAPACKa до SCALAPACKa используют для QR-разложения матриц именно метод Хаусхолдера, правда, в различных модификациях (обычно с использованием BLAS). Существует большая подборка исследовательских работ по блочным версиям.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Статьи в работе]]&lt;br /&gt;
[[Категория:Разложения матриц]]&lt;/div&gt;</summary>
		<author><name>Teplov</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D0%A5%D0%B0%D1%83%D1%81%D1%85%D0%BE%D0%BB%D0%B4%D0%B5%D1%80%D0%B0_(%D0%BE%D1%82%D1%80%D0%B0%D0%B6%D0%B5%D0%BD%D0%B8%D0%B9)_%D0%B4%D0%BB%D1%8F_%D0%BF%D1%80%D0%B8%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D1%8F_%D1%81%D0%B8%D0%BC%D0%BC%D0%B5%D1%82%D1%80%D0%B8%D1%87%D0%BD%D1%8B%D1%85_%D0%BC%D0%B0%D1%82%D1%80%D0%B8%D1%86_%D0%BA_%D1%82%D1%80%D1%91%D1%85%D0%B4%D0%B8%D0%B0%D0%B3%D0%BE%D0%BD%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%BC%D1%83_%D0%B2%D0%B8%D0%B4%D1%83&amp;diff=21499</id>
		<title>Метод Хаусхолдера (отражений) для приведения симметричных матриц к трёхдиагональному виду</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D0%A5%D0%B0%D1%83%D1%81%D1%85%D0%BE%D0%BB%D0%B4%D0%B5%D1%80%D0%B0_(%D0%BE%D1%82%D1%80%D0%B0%D0%B6%D0%B5%D0%BD%D0%B8%D0%B9)_%D0%B4%D0%BB%D1%8F_%D0%BF%D1%80%D0%B8%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D1%8F_%D1%81%D0%B8%D0%BC%D0%BC%D0%B5%D1%82%D1%80%D0%B8%D1%87%D0%BD%D1%8B%D1%85_%D0%BC%D0%B0%D1%82%D1%80%D0%B8%D1%86_%D0%BA_%D1%82%D1%80%D1%91%D1%85%D0%B4%D0%B8%D0%B0%D0%B3%D0%BE%D0%BD%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%BC%D1%83_%D0%B2%D0%B8%D0%B4%D1%83&amp;diff=21499"/>
		<updated>2016-12-28T12:44:57Z</updated>

		<summary type="html">&lt;p&gt;Teplov: /* Масштабируемость реализации алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Приведение симметричной вещественной матрицы к трёхдиагональному виду методом Хаусхолдера (отражений)&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(n^3)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;2n^2+O(n)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;n^2/2&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;(n^2+n)/2&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;(n^2+3n)/2&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
Основные авторы описания: [[Участник:Frolov|А.В.Фролов]]&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Метод Хаусхолдера&#039;&#039;&#039; (в советской математической литературе чаще называется &#039;&#039;&#039;методом отражений&#039;&#039;&#039;) используется для приведения симметричных вещественных матриц к трёхдиагональному виду, или, что то же самое, для разложения &amp;lt;math&amp;gt;A=QTQ^T&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; - ортогональная, &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; — симметричная трёхдиагональная матрица)&amp;lt;ref&amp;gt;В.В.Воеводин, Ю.А.Кузнецов. Матрицы и вычисления. М.: Наука, 1984.&amp;lt;/ref&amp;gt;. При этом матрица &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; хранится и используется не в своём явном виде, а в виде произведения матриц отражения&amp;lt;ref name=&amp;quot;VOLA&amp;quot;&amp;gt;Воеводин В.В. Вычислительные основы линейной алгебры. М.: Наука, 1977.&amp;lt;/ref&amp;gt;. Каждая из матриц отражения может быть определена одним вектором. Это позволяет в классическом исполнении метода отражений хранить результаты разложения на месте матрицы A с использованием одномерного дополнительного массива. &lt;br /&gt;
&lt;br /&gt;
В данной статье рассматривается именно классическое исполнение, в котором не используются приёмы типа сдваивания при вычислениях скалярных произведений.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В методе Хаусхолдера для выполнения &amp;lt;math&amp;gt;QTQ^T&amp;lt;/math&amp;gt;-разложения матрицы используются умножения слева её текущих модификаций на матрицы Хаусхолдера (отражений) с последующим умножением на те же матрицы справа. &lt;br /&gt;
&lt;br /&gt;
{{Шаблон:Матрица отражений}}&lt;br /&gt;
&lt;br /&gt;
На &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м шаге метода с помощью преобразования отражения &amp;quot;убираются&amp;quot; ненулевые поддиагональные элементы, начиная с &amp;lt;math&amp;gt;i+2&amp;lt;/math&amp;gt;-го в &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м столбце. После умножения на эту же матрицу отражения справа автоматически убираются и ненулевые наддиагональные элементы, начиная с &amp;lt;math&amp;gt;i+2&amp;lt;/math&amp;gt;-го в &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-й строке, а полученная модификация снова приобретает симметричный вид. &lt;br /&gt;
&lt;br /&gt;
На каждом из шагов метода матрицу отражений обычно представляют не в стандартном виде, а в виде &amp;lt;math&amp;gt;U=E-\frac{1}{\gamma}vv^*&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; находится через координаты текущего &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; - вектор размерности &amp;lt;math&amp;gt;n+1-i&amp;lt;/math&amp;gt;, составленный из элементов &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца, начиная с &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;(s,s)=0&amp;lt;/math&amp;gt;, то &amp;lt;math&amp;gt;v=e_{i}&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\gamma = \frac{1}{2}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В остальных случаях по алгоритму вычисляется &amp;lt;math&amp;gt;u = \frac{1}{\sqrt{(s,s)}}s&amp;lt;/math&amp;gt;, и далее &amp;lt;math&amp;gt;v_{j}=0&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;j&amp;lt;i&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;v_{j}=u_{j-i+1}&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;j&amp;gt;i&amp;lt;/math&amp;gt;, а &amp;lt;math&amp;gt;v_{i}=1&amp;lt;/math&amp;gt;, если &amp;lt;math&amp;gt;u_{1}=0&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v_{i}=\frac{u_{1}}{|u_{1}|}(1+|u_{1}|)&amp;lt;/math&amp;gt; для остальных значений. При этом &amp;lt;math&amp;gt;\gamma = 1+|u_{1}|=|v_{i}|&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
После вычисления вектора &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; подстолбцы справа от ведущего модифицируются по формулам &amp;lt;math&amp;gt;x&#039;=x-\frac{(x,v)}{\gamma}v&amp;lt;/math&amp;gt;. Потом по аналогичным формулам модифицируются строки ниже ведущей. Благодаря ассоциативности этих операций после вычисления вектора &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; можно сразу выписать формулы модификации всех элементов справа и снизу от ведущих столбца и строки. Оказывается, что если для каждого столбца матрицы &amp;lt;math&amp;gt;x^{(j)}&amp;lt;/math&amp;gt; с номером &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; известно &amp;lt;math&amp;gt;\beta_{j}=\frac{(x^{(j)},v)}{\gamma}&amp;lt;/math&amp;gt;, то для модифицируемого элемента матрицы &amp;lt;math&amp;gt;y&amp;lt;/math&amp;gt; в позиции &amp;lt;math&amp;gt;(i,j)&amp;lt;/math&amp;gt; выполняется модификация &amp;lt;math&amp;gt;y&#039; = y - \beta_{i} v_{i} - \beta_{j} v_{j} - \Delta v_{i} v_{j}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\Delta = \frac{(\beta,v)}{\gamma}&amp;lt;/math&amp;gt;. При этом все эти модификации на каждом конкретном шаге алгоритма для разных пар &amp;lt;math&amp;gt;(i,j)&amp;lt;/math&amp;gt; можно выполнять независимо друг от друга, в том числе и параллельно.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
&lt;br /&gt;
[[File:HausLeftSym1.png|thumb|right|800px|Рисунок 1. Граф первой половины шага алгоритма приведения к трёхдиагональному виду с отображением входных данных. Изображён граф первой половины шага с номером &amp;lt;math&amp;gt;n-4&amp;lt;/math&amp;gt;. Квадраты - результаты выполнения предыдущего шага. Если шаг первый, то это входные данные. Зелёные кружки - операция вида &amp;lt;math&amp;gt;a+b^2&amp;lt;/math&amp;gt;, салатовые - операция вида &amp;lt;math&amp;gt;a+bc&amp;lt;/math&amp;gt;. Синий кружок - вычисление параметров матрицы отражения, светло-красные - вычисление коэффициентов &amp;lt;math&amp;gt;\beta_i&amp;lt;/math&amp;gt;для следующего полушага, жёлтые - вычисление вектора &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.]]&lt;br /&gt;
[[File:HousLeftSym2.png|thumb|right|600px|Рисунок 2. Граф второй половины шага алгоритма приведения к трёхдиагональному виду с отображением входных данных. Изображён граф первой половины шага с номером &amp;lt;math&amp;gt;n-3&amp;lt;/math&amp;gt;. Квадраты - результаты выполнения предыдущего шага. Если шаг первый, то это входные данные. Синий, жёлтые и светло-красные кружки выполняются на первом полушаге (см. Рисунок 1). Голубые кружки -  операции &amp;lt;math&amp;gt;a+bc&amp;lt;/math&amp;gt;, зелёное - умножение, чёрные - операции &amp;lt;math&amp;gt;a+bc+de+fce&amp;lt;/math&amp;gt;.]]&lt;br /&gt;
&lt;br /&gt;
Основную часть алгоритма составляют вычисления на каждом шагу скалярных произведений &amp;lt;math&amp;gt;(s,s)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(x,v)&amp;lt;/math&amp;gt; для всех подстолбцов &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt;справа от текущего, а также проводимые над нижним правым квадратом матрицы операции вида &amp;lt;math&amp;gt;y&#039;=y-ab-cd-fbd&amp;lt;/math&amp;gt;, с учётом симметрии матрицы.&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Как уже сказано в описании ядра, основная часть - вычисления на каждом шагу скалярных произведений &amp;lt;math&amp;gt;(s,s)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(x,v)&amp;lt;/math&amp;gt; для всех подстолбцов &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; справа от текущего, а также массовые покомпонентные операции &amp;lt;math&amp;gt;y&#039;=y-ab-cd-fbd&amp;lt;/math&amp;gt;. При этом, однако, строгая последовательность выполнения первых двух подшагов не обязательна, в силу связи получаемых векторов &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; можно одновременно с &amp;lt;math&amp;gt;(s,s)&amp;lt;/math&amp;gt; вычислять и произведения &amp;lt;math&amp;gt;(x,s)&amp;lt;/math&amp;gt; с последующим выражением через них коэффициентов модификации. Это позволяет почти вдвое уменьшать критический путь графа алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Последовательность выполнения алгоритма обычно записывается как последовательное &amp;quot;обнуление&amp;quot; поддиагональных элементов столбцов, начиная с 1-го столбца и заканчивая предпоследним &amp;lt;math&amp;gt;(n-1)&amp;lt;/math&amp;gt;-м.&lt;br /&gt;
&lt;br /&gt;
При этом в каждом &amp;quot;обнуляемом&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м столбце &amp;quot;обнуляются&amp;quot; сразу все его поддиагональные элементы одновременно, с &amp;lt;math&amp;gt;(i+1)&amp;lt;/math&amp;gt;-го до &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;-го. &lt;br /&gt;
&lt;br /&gt;
Каждое &amp;quot;обнуление&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца состоит из двух шагов: &lt;br /&gt;
а) вычисление параметров матрицы отражения &amp;lt;math&amp;gt;U_{i}&amp;lt;/math&amp;gt; такой, чтобы при умножении на неё слева &amp;quot;обнулились&amp;quot; все поддиагональные его элементы;&lt;br /&gt;
б) одновременное умножение слева матрицы отражения &amp;lt;math&amp;gt;U_{i}&amp;lt;/math&amp;gt; и справа  матрицы отражения &amp;lt;math&amp;gt;U_{i}^T&amp;lt;/math&amp;gt;на текущую версию матрицы.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В последовательной версии основная сложность алгоритма определяется прежде всего вычислениями скалярных произведений векторов, а также массовых модификаций элементов вида &amp;lt;math&amp;gt;y&#039;=y-\alpha v - \beta w - \Delta vw&amp;lt;/math&amp;gt;. Они, если не учитывать возможную разреженность, составляют (в главном члене) по &amp;lt;math&amp;gt;O(n^3)&amp;lt;/math&amp;gt; операций действительного умножения и сложения/вычитания.&lt;br /&gt;
&lt;br /&gt;
При классификации по последовательной сложности, таким образом, метод Хаусхолдера относится к алгоритмам &#039;&#039;с кубической сложностью&#039;&#039;.&lt;br /&gt;
&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;n&amp;lt;/math&amp;gt; к трёхдиагональной методом Хаусхолдера нужно рассмотреть критический путь графа. &lt;br /&gt;
&lt;br /&gt;
Как видно из описания разных вершин, вычисления при &amp;quot;обнулении&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца параметров отражения и скалярных произведений состоят из основной части - ветви длиной по &amp;lt;math&amp;gt;2(n-i)&amp;lt;/math&amp;gt; умножений и сложений - и коррекции вычислений, которые составляют &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; операций. &lt;br /&gt;
&lt;br /&gt;
Поэтому по грубой (без членов низших порядков) оценке критический путь метода Хаусхолдера будет идти через &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt; умножений и &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt; сложений/вычитаний. &lt;br /&gt;
 &lt;br /&gt;
Поэтому в параллельном варианте, как и в последовательном, основную долю требуемого для выполнения алгоритма времени будут определять операции вида &amp;lt;math&amp;gt;a+bc&amp;lt;/math&amp;gt;.  &lt;br /&gt;
&lt;br /&gt;
При классификации по высоте ЯПФ, таким образом, метод Хаусхолдера относится к алгоритмам &#039;&#039;с квадратичной сложностью&#039;&#039;. При классификации по ширине ЯПФ его сложность будет также &#039;&#039;квадратичной&#039;&#039; (без расширения ряда ярусов, связанных с векторными операциями сложения, пришлось бы увеличить вдвое длину критического пути).&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: плотная симметричная квадратная матрица &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; (элементы &amp;lt;math&amp;gt;a_{ij}&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;(n^2+n)/2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: трёхдиагональная матрица &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; (ненулевые элементы &amp;lt;math&amp;gt;r_{ij}&amp;lt;/math&amp;gt; в последовательном варианте хранятся в элементах исходной матрицы &amp;lt;math&amp;gt;a_{ij}&amp;lt;/math&amp;gt;), унитарная (ортогональная) матрица Q - как произведение матриц Хаусхолдера (отражения) (их вектора нормалей к плоскостям отражения в последовательном варианте хранятся в поддиагональных элементах исходной матрицы &amp;lt;math&amp;gt;a_{ij}&amp;lt;/math&amp;gt; и в одном дополнительном столбце размерности n).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;(n^2+3n)/2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Соотношение последовательной и параллельной сложности, как хорошо видно, является &#039;&#039;линейным&#039;&#039;, что даёт определённый стимул для распараллеливания. Однако у наискорейшей ЯПФ ширина &#039;&#039;квадратична&#039;&#039;, что указывает на дисбаланс между загруженностями устройств при попытке её реально запрограммировать. Поэтому более практично даже при хорошей (быстрой) вычислительной сети оставить количество устройств (например, узлов кластера) &#039;&#039;линейным&#039;&#039; по размеру матрицы, что удвоит критический путь реализуемой ЯПФ. &lt;br /&gt;
&lt;br /&gt;
При этом вычислительная мощность алгоритма, как отношение числа операций к суммарному объему входных и выходных данных, &#039;&#039;линейна&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Алгоритм в рамках выбранной версии полностью детерминирован.&lt;br /&gt;
&lt;br /&gt;
Вычислительная погрешность в методе отражений (Хаусхолдера) растет &#039;&#039;линейно&#039;&#039;, как и в методе Гивенса (вращений).&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
В варианте с кратчайшим критическим путём графа алгоритма (с использованием зависимости между обнуляемым вектором и направляющим вектором отражения) метод Хаусхолдера (отражений) приведения квадратной симметричной вещественной матрицы к трёхдиагональному виду на Фортране 77 можно записать так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;fortran&amp;quot;&amp;gt;&lt;br /&gt;
	DO  I = 1, N-2&lt;br /&gt;
&lt;br /&gt;
            DO K = I, N&lt;br /&gt;
              SX(K)=A(N,I)*A(N,K)&lt;br /&gt;
            END DO&lt;br /&gt;
            DO J = N-1, I+1, -1&lt;br /&gt;
                 SX(I)=SX(I)+A(J,I)*A(J,I)&lt;br /&gt;
            END DO &lt;br /&gt;
            DO K = I+1, N&lt;br /&gt;
              DO J = N-1, K, -1&lt;br /&gt;
                 SX(K)=SX(K)+A(J,I)*A(J,K)&lt;br /&gt;
              END DO&lt;br /&gt;
              DO J = K-1,I+1,-1&lt;br /&gt;
                 SX(K)=SX(K)+A(J,I)*A(K,J)&lt;br /&gt;
              END DO&lt;br /&gt;
            END DO&lt;br /&gt;
&lt;br /&gt;
               ALPHA = SQRT (SX(I))&lt;br /&gt;
               IF (A(I+1,I).NE.0.) THEN&lt;br /&gt;
                  BETA = 1./ALPHA&lt;br /&gt;
                  DO J = I+2, N&lt;br /&gt;
                    A(J,I)=A(J,I)*BETA&lt;br /&gt;
                  END DO&lt;br /&gt;
                  SX(I) = A(I+1,I)*BETA+SIGN(1.,A(I+1,I))                     &lt;br /&gt;
                  A(I+1,I)=ALPHA&lt;br /&gt;
                  G=1./ABS(SX(I)) ! 1/gamma&lt;br /&gt;
                  SX2=0.&lt;br /&gt;
                  DO K = I+2, N&lt;br /&gt;
                     SX(K)=SX(K)*BETA*G+SIGN(A(K,I+1),SX(I))&lt;br /&gt;
                     SX2=SX(K)*A(K,I)+SX2                     &lt;br /&gt;
                  END DO&lt;br /&gt;
                  SX2=G*SX2&lt;br /&gt;
                  DO K = I+2, N&lt;br /&gt;
                     A(K,K) = A(K,K)-2*A(K,I)*SX(K)+SX2*A(K,I)**2&lt;br /&gt;
                     DO J = K+1, N&lt;br /&gt;
                       A (J,K) = A(J,K)-A(J,I)*SX(K)-A(K,I)*SX(I)+SX2*A(J,I)*A(K,I)&lt;br /&gt;
                     END DO&lt;br /&gt;
                  END DO&lt;br /&gt;
               ELSE&lt;br /&gt;
                  IF (ALPHA.NE.0.) THEN&lt;br /&gt;
                     BETA = 1./ALPHA&lt;br /&gt;
                     DO J = I+2, N&lt;br /&gt;
                       A(J,I)=A(J,I)*BETA&lt;br /&gt;
                     END DO&lt;br /&gt;
                     SX(I) = -1. &lt;br /&gt;
                     A(I+1,I)=ALPHA&lt;br /&gt;
                     G=1.! 1/gamma&lt;br /&gt;
                     SX2=0.&lt;br /&gt;
                     DO K = I+2, N&lt;br /&gt;
                        SX(K)=SX(K)*BETA*G+SIGN(A(K,I+1),SX(I))&lt;br /&gt;
                        SX2=SX(K)*A(K,I)+SX2                 &lt;br /&gt;
                     END DO&lt;br /&gt;
                     SX2=G*SX2&lt;br /&gt;
                     DO K = I+2, N  &lt;br /&gt;
                        A(K,K) = A(K,K)-2*A(K,I)*SX(K)+SX2*A(K,I)**2                   &lt;br /&gt;
                        DO J = K+1, N&lt;br /&gt;
                          A (J,K) = A(J,K)-A(J,I)*SX(K)-A(K,I)*SX(I)+SX2*A(J,I)*A(K,I)&lt;br /&gt;
                        END DO&lt;br /&gt;
                     END DO&lt;br /&gt;
                  ELSE&lt;br /&gt;
                     SX(I)=1.&lt;br /&gt;
                  END IF&lt;br /&gt;
               END IF               &lt;br /&gt;
&lt;br /&gt;
		&lt;br /&gt;
	END DO&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Здесь симметричная трёхдиагональная матрица хранится в диагонали и нижней кодиагонали массива A, вектора v - в поддиагональной части массива, за исключением первых их элементов, для которых выделен массив SX.&lt;br /&gt;
&lt;br /&gt;
Обычно же в последовательных версиях коэффициенты модификаций столбцов вычисляются целиком через скалярные произведения после вычислений параметров матрицы отражения. При этом схема чуть проще. Удлиняется критический путь графа, но для последовательных реализаций это неважно.&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
К сожалению, в графе, как видно по рисунку, в наличии пучки рассылок, в них неизбежно часть дуг остаются длинными, что отрицательно влияет на локальность вычислений по пространству.  &lt;br /&gt;
&lt;br /&gt;
==== Локальность реализации алгоритма ====&lt;br /&gt;
===== Структура обращений в память и качественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qtq_1.png|thumb|center|700px|Рисунок 3. Метод Хаусхолдера (отражений) для приведения симметричных матриц к трёхдиагональному виду. Общий профиль обращений в память]]&lt;br /&gt;
&lt;br /&gt;
На рис. 3 представлен профиль обращений в память для реализации метода Хаусхолдера (отражений) для приведения симметричных матриц к трёхдиагональному виду. Данный профиль обладает явной итерационной структурой, при этом видно, что итерации очень похожи. Основное их отличие заключается в наборе используемых данных – на каждой следующей итерации несколько первых элементов отбрасываются их рассмотрения, то есть чем позже итерация, тем меньше данных в ней задействовано. Также можно отметить, что число обращений в каждой последующей итерации немного уменьшается. Для того чтобы проанализировать общий профиль, в таком случае чаще всего достаточно изучить одну итерацию. Рассмотрим самую первую из них более детально.&lt;br /&gt;
&lt;br /&gt;
На рис. 4 показан набор обращений в рамках первой итерации (выделен на рис. 3 зеленым). Его можно разбить на несколько фрагментов и рассмотреть их отдельно. Строение фрагментов 2-5 можно оценить по общему графику для итерации. Фрагмент 2 представляет собой набор последовательных переборов в обратном порядке с небольшим шагом, причем число элементов в переборе постепенно уменьшается. Такое строение характеризуется достаточно высокой пространственной локальностью, поскольку часто происходит обращений к близко расположенным данным, однако низкой временной, поскольку данные повторно не используются. То же самое верно и для фрагмента 4, основное отличие которого заключается в том, что переборы выполняются в прямом порядке. Отметим, что некоторое искривление данного фрагмента связано не со строением самого фрагмента, а с разным числом параллельно выполняющихся обращений (см., например, правую область фрагмента 6), чего не наблюдается при выполнении фрагмента 2.&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qtq_2.png|thumb|center|700px|Рисунок 4. Профиль обращений, одна итерация]]&lt;br /&gt;
&lt;br /&gt;
Далее, фрагменты  3 и 5 состоят из небольшого числа обращений к данным, расположенным далеко друг от друга. Такие фрагменты характеризуются низкой пространственной и временной локальностью.&lt;br /&gt;
&lt;br /&gt;
Оставшиеся фрагменты требуют более детального рассмотрения. Перейдем к изучению фрагмента 1, который целиком представлен на рис. 5. Здесь мы можем видеть несколько этапов, каждый из которых представляет собой последовательный перебор элементов с шагом по памяти 1, причем в некоторых случаях данные много раз используются повторно (особенно это заметно в правой нижней области рисунка, где выполняется множество обращений к одному и тому же элементу). При условии, что в данном фрагменте задействовано всего около 40 элементов, можно говорить, что данный набор обращений обладает высокой пространственной и временной локальностью. &lt;br /&gt;
&lt;br /&gt;
[[file:householder_qtq_3.png|thumb|center|500px|Рисунок 5. Профиль обращений, одна итерация, фрагмент 1]]&lt;br /&gt;
&lt;br /&gt;
Далее рассмотрим детально фрагмент 6 (рис. 6). Здесь можно увидеть, что повторные обращения к данным выполняются не подряд, а через некоторые промежутки, однако число задействованных элементов также очень невелико, так что и в этом случае локальность (и пространственная, и временная) будет высокой.&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qtq_4.png|thumb|center|500px|Рисунок 6. Профиль обращений, одна итерация, фрагмент 6]]&lt;br /&gt;
&lt;br /&gt;
В целом можно сказать, что рассматриваемая итерация обладает достаточно высокой пространственной и временной локальностью. Поскольку остальные итерации устроены подобным образом, данные вывод можно перенести и на общий профиль обращений.&lt;br /&gt;
&lt;br /&gt;
===== Количественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
Оценка выполняется на основе характеристики daps, которая оценивает число выполненных обращений (чтений и записей) в память в секунду. Данная характеристика является аналогом оценки flops применительно к работе с памятью и является в большей степени оценкой производительности взаимодействия с памятью, чем оценкой локальности. Однако она служит хорошим источником информации, в том числе для сравнения с результатами по следующей характеристике cvg.&lt;br /&gt;
&lt;br /&gt;
На рисунке 7 приведены значения daps для реализаций распространенных алгоритмов, отсортированные по возрастанию (чем больше daps, тем в общем случае выше производительность). Можно увидеть, что производительность работы с памятью в данном случае достаточно высока – значение daps лишь немногим уступает тесту Linpack, который обладает высокой эффективностью работы с памятью, и немного превосходит, например, реализацию метода Якоби или метода Гаусса.&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qtq_daps.png|thumb|center|700px|Рисунок 7. Сравнение значений оценки daps]]&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
==== Масштабируемость алгоритма ====&lt;br /&gt;
==== Масштабируемость реализации алгоритма ====&lt;br /&gt;
Проведём исследование масштабируемости вширь реализации согласно [[Scalability methodology|методике]]. Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов-2&amp;quot; [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Набор и границы значений изменяемых [[Глоссарий#Параметры запуска|параметров запуска]] реализации алгоритма: &lt;br /&gt;
&lt;br /&gt;
*	число процессоров [16:256] с шагом n^2;&lt;br /&gt;
*	размер матрицы [1000 : 32000] с шагом 1000.&lt;br /&gt;
&lt;br /&gt;
В результате проведённых экспериментов был получен следующий диапазон [[Глоссарий#Эффективность реализации|эффективности реализации]] алгоритма:&lt;br /&gt;
&lt;br /&gt;
*	минимальная эффективность реализации 4.073e-06%;&lt;br /&gt;
*	максимальная эффективность реализации 0.0667%.&lt;br /&gt;
&lt;br /&gt;
На следующих рисунках приведены графики [[Глоссарий#Производительность|производительности]] и эффективности выбранной реализации алгоритма в процедуре PDSYTRD из Scalapack в зависимости от изменяемых параметров запуска.&lt;br /&gt;
&lt;br /&gt;
[[file:PDSYTRD Perf2.png|thumb|center|700px|Рисунок 8. Параллельная реализация метода Хаусхолдера QR-разложения. Изменение производительности в зависимости от числа процессоров и размера матрицы.]]&lt;br /&gt;
[[file:PDSYTRD eff2.png|thumb|center|700px|Рисунок 9. Параллельная реализация метода Хаусхолдера QR-разложения. Изменение эффективности в зависимости от числа процессоров и размера матрицы.]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
В сравнении с методом Гивенса, который имеет естественное двумерное блочное разбиение на основе точечного метода, метод Хаусхолдера из-за худших характеристик локальности (наличие пучков рассылок) и меньшего количества независимых обобщённых развёрток графа не так хорош для реализаций на системах с распределённой памятью, как для систем с общей памятью. Поэтому на массово параллельных системах с распределённой памятью следует применять метод Хаусхолдера (если уж именно его нужно реализовать) не в точечной версии, а в разрабатываемых исследователями блочных вариантах. Следует отметить, что эти варианты - не блочная нарезка описанного метода, а самостоятельные методы. Особенно их применение рекомендуется в случаях с большой разрежённостью матрицы.&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Большинство пакетов от LINPACKа и LAPACKa до SCALAPACKa используют для QR-разложения матриц именно метод Хаусхолдера, правда, в различных модификациях (обычно с использованием BLAS). Существует большая подборка исследовательских работ по блочным версиям.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Статьи в работе]]&lt;br /&gt;
[[Категория:Разложения матриц]]&lt;/div&gt;</summary>
		<author><name>Teplov</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D0%A5%D0%B0%D1%83%D1%81%D1%85%D0%BE%D0%BB%D0%B4%D0%B5%D1%80%D0%B0_(%D0%BE%D1%82%D1%80%D0%B0%D0%B6%D0%B5%D0%BD%D0%B8%D0%B9)_%D0%B4%D0%BB%D1%8F_%D0%BF%D1%80%D0%B8%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D1%8F_%D1%81%D0%B8%D0%BC%D0%BC%D0%B5%D1%82%D1%80%D0%B8%D1%87%D0%BD%D1%8B%D1%85_%D0%BC%D0%B0%D1%82%D1%80%D0%B8%D1%86_%D0%BA_%D1%82%D1%80%D1%91%D1%85%D0%B4%D0%B8%D0%B0%D0%B3%D0%BE%D0%BD%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%BC%D1%83_%D0%B2%D0%B8%D0%B4%D1%83&amp;diff=21359</id>
		<title>Метод Хаусхолдера (отражений) для приведения симметричных матриц к трёхдиагональному виду</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D0%A5%D0%B0%D1%83%D1%81%D1%85%D0%BE%D0%BB%D0%B4%D0%B5%D1%80%D0%B0_(%D0%BE%D1%82%D1%80%D0%B0%D0%B6%D0%B5%D0%BD%D0%B8%D0%B9)_%D0%B4%D0%BB%D1%8F_%D0%BF%D1%80%D0%B8%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D1%8F_%D1%81%D0%B8%D0%BC%D0%BC%D0%B5%D1%82%D1%80%D0%B8%D1%87%D0%BD%D1%8B%D1%85_%D0%BC%D0%B0%D1%82%D1%80%D0%B8%D1%86_%D0%BA_%D1%82%D1%80%D1%91%D1%85%D0%B4%D0%B8%D0%B0%D0%B3%D0%BE%D0%BD%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%BC%D1%83_%D0%B2%D0%B8%D0%B4%D1%83&amp;diff=21359"/>
		<updated>2016-12-19T12:42:25Z</updated>

		<summary type="html">&lt;p&gt;Teplov: /* Масштабируемость реализации алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Приведение симметричной вещественной матрицы к трёхдиагональному виду методом Хаусхолдера (отражений)&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(n^3)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;2n^2+O(n)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;n^2/2&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;(n^2+n)/2&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;(n^2+3n)/2&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
Основные авторы описания: [[Участник:Frolov|А.В.Фролов]]&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Метод Хаусхолдера&#039;&#039;&#039; (в советской математической литературе чаще называется &#039;&#039;&#039;методом отражений&#039;&#039;&#039;) используется для приведения симметричных вещественных матриц к трёхдиагональному виду, или, что то же самое, для разложения &amp;lt;math&amp;gt;A=QTQ^T&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; - ортогональная, &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; — симметричная трёхдиагональная матрица)&amp;lt;ref&amp;gt;В.В.Воеводин, Ю.А.Кузнецов. Матрицы и вычисления. М.: Наука, 1984.&amp;lt;/ref&amp;gt;. При этом матрица &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; хранится и используется не в своём явном виде, а в виде произведения матриц отражения&amp;lt;ref name=&amp;quot;VOLA&amp;quot;&amp;gt;Воеводин В.В. Вычислительные основы линейной алгебры. М.: Наука, 1977.&amp;lt;/ref&amp;gt;. Каждая из матриц отражения может быть определена одним вектором. Это позволяет в классическом исполнении метода отражений хранить результаты разложения на месте матрицы A с использованием одномерного дополнительного массива. &lt;br /&gt;
&lt;br /&gt;
В данной статье рассматривается именно классическое исполнение, в котором не используются приёмы типа сдваивания при вычислениях скалярных произведений.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В методе Хаусхолдера для выполнения &amp;lt;math&amp;gt;QTQ^T&amp;lt;/math&amp;gt;-разложения матрицы используются умножения слева её текущих модификаций на матрицы Хаусхолдера (отражений) с последующим умножением на те же матрицы справа. &lt;br /&gt;
&lt;br /&gt;
{{Шаблон:Матрица отражений}}&lt;br /&gt;
&lt;br /&gt;
На &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м шаге метода с помощью преобразования отражения &amp;quot;убираются&amp;quot; ненулевые поддиагональные элементы, начиная с &amp;lt;math&amp;gt;i+2&amp;lt;/math&amp;gt;-го в &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м столбце. После умножения на эту же матрицу отражения справа автоматически убираются и ненулевые наддиагональные элементы, начиная с &amp;lt;math&amp;gt;i+2&amp;lt;/math&amp;gt;-го в &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-й строке, а полученная модификация снова приобретает симметричный вид. &lt;br /&gt;
&lt;br /&gt;
На каждом из шагов метода матрицу отражений обычно представляют не в стандартном виде, а в виде &amp;lt;math&amp;gt;U=E-\frac{1}{\gamma}vv^*&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; находится через координаты текущего &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; - вектор размерности &amp;lt;math&amp;gt;n+1-i&amp;lt;/math&amp;gt;, составленный из элементов &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца, начиная с &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;(s,s)=0&amp;lt;/math&amp;gt;, то &amp;lt;math&amp;gt;v=e_{i}&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\gamma = \frac{1}{2}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В остальных случаях по алгоритму вычисляется &amp;lt;math&amp;gt;u = \frac{1}{\sqrt{(s,s)}}s&amp;lt;/math&amp;gt;, и далее &amp;lt;math&amp;gt;v_{j}=0&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;j&amp;lt;i&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;v_{j}=u_{j-i+1}&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;j&amp;gt;i&amp;lt;/math&amp;gt;, а &amp;lt;math&amp;gt;v_{i}=1&amp;lt;/math&amp;gt;, если &amp;lt;math&amp;gt;u_{1}=0&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v_{i}=\frac{u_{1}}{|u_{1}|}(1+|u_{1}|)&amp;lt;/math&amp;gt; для остальных значений. При этом &amp;lt;math&amp;gt;\gamma = 1+|u_{1}|=|v_{i}|&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
После вычисления вектора &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; подстолбцы справа от ведущего модифицируются по формулам &amp;lt;math&amp;gt;x&#039;=x-\frac{(x,v)}{\gamma}v&amp;lt;/math&amp;gt;. Потом по аналогичным формулам модифицируются строки ниже ведущей. Благодаря ассоциативности этих операций после вычисления вектора &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; можно сразу выписать формулы модификации всех элементов справа и снизу от ведущих столбца и строки. Оказывается, что если для каждого столбца матрицы &amp;lt;math&amp;gt;x^{(j)}&amp;lt;/math&amp;gt; с номером &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; известно &amp;lt;math&amp;gt;\beta_{j}=\frac{(x^{(j)},v)}{\gamma}&amp;lt;/math&amp;gt;, то для модифицируемого элемента матрицы &amp;lt;math&amp;gt;y&amp;lt;/math&amp;gt; в позиции &amp;lt;math&amp;gt;(i,j)&amp;lt;/math&amp;gt; выполняется модификация &amp;lt;math&amp;gt;y&#039; = y - \beta_{i} v_{i} - \beta_{j} v_{j} - \Delta v_{i} v_{j}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\Delta = \frac{(\beta,v)}{\gamma}&amp;lt;/math&amp;gt;. При этом все эти модификации на каждом конкретном шаге алгоритма для разных пар &amp;lt;math&amp;gt;(i,j)&amp;lt;/math&amp;gt; можно выполнять независимо друг от друга, в том числе и параллельно.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
&lt;br /&gt;
[[File:HausLeftSym1.png|thumb|right|800px|Рисунок 1. Граф первой половины шага алгоритма приведения к трёхдиагональному виду с отображением входных данных. Изображён граф первой половины шага с номером &amp;lt;math&amp;gt;n-4&amp;lt;/math&amp;gt;. Квадраты - результаты выполнения предыдущего шага. Если шаг первый, то это входные данные. Зелёные кружки - операция вида &amp;lt;math&amp;gt;a+b^2&amp;lt;/math&amp;gt;, салатовые - операция вида &amp;lt;math&amp;gt;a+bc&amp;lt;/math&amp;gt;. Синий кружок - вычисление параметров матрицы отражения, светло-красные - вычисление коэффициентов &amp;lt;math&amp;gt;\beta_i&amp;lt;/math&amp;gt;для следующего полушага, жёлтые - вычисление вектора &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.]]&lt;br /&gt;
[[File:HousLeftSym2.png|thumb|right|600px|Рисунок 2. Граф второй половины шага алгоритма приведения к трёхдиагональному виду с отображением входных данных. Изображён граф первой половины шага с номером &amp;lt;math&amp;gt;n-3&amp;lt;/math&amp;gt;. Квадраты - результаты выполнения предыдущего шага. Если шаг первый, то это входные данные. Синий, жёлтые и светло-красные кружки выполняются на первом полушаге (см. Рисунок 1). Голубые кружки -  операции &amp;lt;math&amp;gt;a+bc&amp;lt;/math&amp;gt;, зелёное - умножение, чёрные - операции &amp;lt;math&amp;gt;a+bc+de+fce&amp;lt;/math&amp;gt;.]]&lt;br /&gt;
&lt;br /&gt;
Основную часть алгоритма составляют вычисления на каждом шагу скалярных произведений &amp;lt;math&amp;gt;(s,s)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(x,v)&amp;lt;/math&amp;gt; для всех подстолбцов &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt;справа от текущего, а также проводимые над нижним правым квадратом матрицы операции вида &amp;lt;math&amp;gt;y&#039;=y-ab-cd-fbd&amp;lt;/math&amp;gt;, с учётом симметрии матрицы.&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Как уже сказано в описании ядра, основная часть - вычисления на каждом шагу скалярных произведений &amp;lt;math&amp;gt;(s,s)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(x,v)&amp;lt;/math&amp;gt; для всех подстолбцов &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; справа от текущего, а также массовые покомпонентные операции &amp;lt;math&amp;gt;y&#039;=y-ab-cd-fbd&amp;lt;/math&amp;gt;. При этом, однако, строгая последовательность выполнения первых двух подшагов не обязательна, в силу связи получаемых векторов &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; можно одновременно с &amp;lt;math&amp;gt;(s,s)&amp;lt;/math&amp;gt; вычислять и произведения &amp;lt;math&amp;gt;(x,s)&amp;lt;/math&amp;gt; с последующим выражением через них коэффициентов модификации. Это позволяет почти вдвое уменьшать критический путь графа алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Последовательность выполнения алгоритма обычно записывается как последовательное &amp;quot;обнуление&amp;quot; поддиагональных элементов столбцов, начиная с 1-го столбца и заканчивая предпоследним &amp;lt;math&amp;gt;(n-1)&amp;lt;/math&amp;gt;-м.&lt;br /&gt;
&lt;br /&gt;
При этом в каждом &amp;quot;обнуляемом&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м столбце &amp;quot;обнуляются&amp;quot; сразу все его поддиагональные элементы одновременно, с &amp;lt;math&amp;gt;(i+1)&amp;lt;/math&amp;gt;-го до &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;-го. &lt;br /&gt;
&lt;br /&gt;
Каждое &amp;quot;обнуление&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца состоит из двух шагов: &lt;br /&gt;
а) вычисление параметров матрицы отражения &amp;lt;math&amp;gt;U_{i}&amp;lt;/math&amp;gt; такой, чтобы при умножении на неё слева &amp;quot;обнулились&amp;quot; все поддиагональные его элементы;&lt;br /&gt;
б) одновременное умножение слева матрицы отражения &amp;lt;math&amp;gt;U_{i}&amp;lt;/math&amp;gt; и справа  матрицы отражения &amp;lt;math&amp;gt;U_{i}^T&amp;lt;/math&amp;gt;на текущую версию матрицы.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В последовательной версии основная сложность алгоритма определяется прежде всего вычислениями скалярных произведений векторов, а также массовых модификаций элементов вида &amp;lt;math&amp;gt;y&#039;=y-\alpha v - \beta w - \Delta vw&amp;lt;/math&amp;gt;. Они, если не учитывать возможную разреженность, составляют (в главном члене) по &amp;lt;math&amp;gt;O(n^3)&amp;lt;/math&amp;gt; операций действительного умножения и сложения/вычитания.&lt;br /&gt;
&lt;br /&gt;
При классификации по последовательной сложности, таким образом, метод Хаусхолдера относится к алгоритмам &#039;&#039;с кубической сложностью&#039;&#039;.&lt;br /&gt;
&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;n&amp;lt;/math&amp;gt; к трёхдиагональной методом Хаусхолдера нужно рассмотреть критический путь графа. &lt;br /&gt;
&lt;br /&gt;
Как видно из описания разных вершин, вычисления при &amp;quot;обнулении&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца параметров отражения и скалярных произведений состоят из основной части - ветви длиной по &amp;lt;math&amp;gt;2(n-i)&amp;lt;/math&amp;gt; умножений и сложений - и коррекции вычислений, которые составляют &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; операций. &lt;br /&gt;
&lt;br /&gt;
Поэтому по грубой (без членов низших порядков) оценке критический путь метода Хаусхолдера будет идти через &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt; умножений и &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt; сложений/вычитаний. &lt;br /&gt;
 &lt;br /&gt;
Поэтому в параллельном варианте, как и в последовательном, основную долю требуемого для выполнения алгоритма времени будут определять операции вида &amp;lt;math&amp;gt;a+bc&amp;lt;/math&amp;gt;.  &lt;br /&gt;
&lt;br /&gt;
При классификации по высоте ЯПФ, таким образом, метод Хаусхолдера относится к алгоритмам &#039;&#039;с квадратичной сложностью&#039;&#039;. При классификации по ширине ЯПФ его сложность будет также &#039;&#039;квадратичной&#039;&#039; (без расширения ряда ярусов, связанных с векторными операциями сложения, пришлось бы увеличить вдвое длину критического пути).&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: плотная симметричная квадратная матрица &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; (элементы &amp;lt;math&amp;gt;a_{ij}&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;(n^2+n)/2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: трёхдиагональная матрица &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; (ненулевые элементы &amp;lt;math&amp;gt;r_{ij}&amp;lt;/math&amp;gt; в последовательном варианте хранятся в элементах исходной матрицы &amp;lt;math&amp;gt;a_{ij}&amp;lt;/math&amp;gt;), унитарная (ортогональная) матрица Q - как произведение матриц Хаусхолдера (отражения) (их вектора нормалей к плоскостям отражения в последовательном варианте хранятся в поддиагональных элементах исходной матрицы &amp;lt;math&amp;gt;a_{ij}&amp;lt;/math&amp;gt; и в одном дополнительном столбце размерности n).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;(n^2+3n)/2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Соотношение последовательной и параллельной сложности, как хорошо видно, является &#039;&#039;линейным&#039;&#039;, что даёт определённый стимул для распараллеливания. Однако у наискорейшей ЯПФ ширина &#039;&#039;квадратична&#039;&#039;, что указывает на дисбаланс между загруженностями устройств при попытке её реально запрограммировать. Поэтому более практично даже при хорошей (быстрой) вычислительной сети оставить количество устройств (например, узлов кластера) &#039;&#039;линейным&#039;&#039; по размеру матрицы, что удвоит критический путь реализуемой ЯПФ. &lt;br /&gt;
&lt;br /&gt;
При этом вычислительная мощность алгоритма, как отношение числа операций к суммарному объему входных и выходных данных, &#039;&#039;линейна&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Алгоритм в рамках выбранной версии полностью детерминирован.&lt;br /&gt;
&lt;br /&gt;
Вычислительная погрешность в методе отражений (Хаусхолдера) растет &#039;&#039;линейно&#039;&#039;, как и в методе Гивенса (вращений).&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
В варианте с кратчайшим критическим путём графа алгоритма (с использованием зависимости между обнуляемым вектором и направляющим вектором отражения) метод Хаусхолдера (отражений) приведения квадратной симметричной вещественной матрицы к трёхдиагональному виду на Фортране 77 можно записать так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;fortran&amp;quot;&amp;gt;&lt;br /&gt;
	DO  I = 1, N-2&lt;br /&gt;
&lt;br /&gt;
            DO K = I, N&lt;br /&gt;
              SX(K)=A(N,I)*A(N,K)&lt;br /&gt;
            END DO&lt;br /&gt;
            DO J = N-1, I+1, -1&lt;br /&gt;
                 SX(I)=SX(I)+A(J,I)*A(J,I)&lt;br /&gt;
            END DO &lt;br /&gt;
            DO K = I+1, N&lt;br /&gt;
              DO J = N-1, K, -1&lt;br /&gt;
                 SX(K)=SX(K)+A(J,I)*A(J,K)&lt;br /&gt;
              END DO&lt;br /&gt;
              DO J = K-1,I+1,-1&lt;br /&gt;
                 SX(K)=SX(K)+A(J,I)*A(K,J)&lt;br /&gt;
              END DO&lt;br /&gt;
            END DO&lt;br /&gt;
&lt;br /&gt;
               ALPHA = SQRT (SX(I))&lt;br /&gt;
               IF (A(I+1,I).NE.0.) THEN&lt;br /&gt;
                  BETA = 1./ALPHA&lt;br /&gt;
                  DO J = I+2, N&lt;br /&gt;
                    A(J,I)=A(J,I)*BETA&lt;br /&gt;
                  END DO&lt;br /&gt;
                  SX(I) = A(I+1,I)*BETA+SIGN(1.,A(I+1,I))                     &lt;br /&gt;
                  A(I+1,I)=ALPHA&lt;br /&gt;
                  G=1./ABS(SX(I)) ! 1/gamma&lt;br /&gt;
                  SX2=0.&lt;br /&gt;
                  DO K = I+2, N&lt;br /&gt;
                     SX(K)=SX(K)*BETA*G+SIGN(A(K,I+1),SX(I))&lt;br /&gt;
                     SX2=SX(K)*A(K,I)+SX2                     &lt;br /&gt;
                  END DO&lt;br /&gt;
                  SX2=G*SX2&lt;br /&gt;
                  DO K = I+2, N&lt;br /&gt;
                     A(K,K) = A(K,K)-2*A(K,I)*SX(K)+SX2*A(K,I)**2&lt;br /&gt;
                     DO J = K+1, N&lt;br /&gt;
                       A (J,K) = A(J,K)-A(J,I)*SX(K)-A(K,I)*SX(I)+SX2*A(J,I)*A(K,I)&lt;br /&gt;
                     END DO&lt;br /&gt;
                  END DO&lt;br /&gt;
               ELSE&lt;br /&gt;
                  IF (ALPHA.NE.0.) THEN&lt;br /&gt;
                     BETA = 1./ALPHA&lt;br /&gt;
                     DO J = I+2, N&lt;br /&gt;
                       A(J,I)=A(J,I)*BETA&lt;br /&gt;
                     END DO&lt;br /&gt;
                     SX(I) = -1. &lt;br /&gt;
                     A(I+1,I)=ALPHA&lt;br /&gt;
                     G=1.! 1/gamma&lt;br /&gt;
                     SX2=0.&lt;br /&gt;
                     DO K = I+2, N&lt;br /&gt;
                        SX(K)=SX(K)*BETA*G+SIGN(A(K,I+1),SX(I))&lt;br /&gt;
                        SX2=SX(K)*A(K,I)+SX2                 &lt;br /&gt;
                     END DO&lt;br /&gt;
                     SX2=G*SX2&lt;br /&gt;
                     DO K = I+2, N  &lt;br /&gt;
                        A(K,K) = A(K,K)-2*A(K,I)*SX(K)+SX2*A(K,I)**2                   &lt;br /&gt;
                        DO J = K+1, N&lt;br /&gt;
                          A (J,K) = A(J,K)-A(J,I)*SX(K)-A(K,I)*SX(I)+SX2*A(J,I)*A(K,I)&lt;br /&gt;
                        END DO&lt;br /&gt;
                     END DO&lt;br /&gt;
                  ELSE&lt;br /&gt;
                     SX(I)=1.&lt;br /&gt;
                  END IF&lt;br /&gt;
               END IF               &lt;br /&gt;
&lt;br /&gt;
		&lt;br /&gt;
	END DO&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Здесь симметричная трёхдиагональная матрица хранится в диагонали и нижней кодиагонали массива A, вектора v - в поддиагональной части массива, за исключением первых их элементов, для которых выделен массив SX.&lt;br /&gt;
&lt;br /&gt;
Обычно же в последовательных версиях коэффициенты модификаций столбцов вычисляются целиком через скалярные произведения после вычислений параметров матрицы отражения. При этом схема чуть проще. Удлиняется критический путь графа, но для последовательных реализаций это неважно.&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
К сожалению, в графе, как видно по рисунку, в наличии пучки рассылок, в них неизбежно часть дуг остаются длинными, что отрицательно влияет на локальность вычислений по пространству.  &lt;br /&gt;
&lt;br /&gt;
==== Локальность реализации алгоритма ====&lt;br /&gt;
===== Структура обращений в память и качественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qtq_1.png|thumb|center|700px|Рисунок 3. Метод Хаусхолдера (отражений) для приведения симметричных матриц к трёхдиагональному виду. Общий профиль обращений в память]]&lt;br /&gt;
&lt;br /&gt;
На рис. 3 представлен профиль обращений в память для реализации метода Хаусхолдера (отражений) для приведения симметричных матриц к трёхдиагональному виду. Данный профиль обладает явной итерационной структурой, при этом видно, что итерации очень похожи. Основное их отличие заключается в наборе используемых данных – на каждой следующей итерации несколько первых элементов отбрасываются их рассмотрения, то есть чем позже итерация, тем меньше данных в ней задействовано. Также можно отметить, что число обращений в каждой последующей итерации немного уменьшается. Для того чтобы проанализировать общий профиль, в таком случае чаще всего достаточно изучить одну итерацию. Рассмотрим самую первую из них более детально.&lt;br /&gt;
&lt;br /&gt;
На рис. 4 показан набор обращений в рамках первой итерации (выделен на рис. 3 зеленым). Его можно разбить на несколько фрагментов и рассмотреть их отдельно. Строение фрагментов 2-5 можно оценить по общему графику для итерации. Фрагмент 2 представляет собой набор последовательных переборов в обратном порядке с небольшим шагом, причем число элементов в переборе постепенно уменьшается. Такое строение характеризуется достаточно высокой пространственной локальностью, поскольку часто происходит обращений к близко расположенным данным, однако низкой временной, поскольку данные повторно не используются. То же самое верно и для фрагмента 4, основное отличие которого заключается в том, что переборы выполняются в прямом порядке. Отметим, что некоторое искривление данного фрагмента связано не со строением самого фрагмента, а с разным числом параллельно выполняющихся обращений (см., например, правую область фрагмента 6), чего не наблюдается при выполнении фрагмента 2.&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qtq_2.png|thumb|center|700px|Рисунок 4. Профиль обращений, одна итерация]]&lt;br /&gt;
&lt;br /&gt;
Далее, фрагменты  3 и 5 состоят из небольшого числа обращений к данным, расположенным далеко друг от друга. Такие фрагменты характеризуются низкой пространственной и временной локальностью.&lt;br /&gt;
&lt;br /&gt;
Оставшиеся фрагменты требуют более детального рассмотрения. Перейдем к изучению фрагмента 1, который целиком представлен на рис. 5. Здесь мы можем видеть несколько этапов, каждый из которых представляет собой последовательный перебор элементов с шагом по памяти 1, причем в некоторых случаях данные много раз используются повторно (особенно это заметно в правой нижней области рисунка, где выполняется множество обращений к одному и тому же элементу). При условии, что в данном фрагменте задействовано всего около 40 элементов, можно говорить, что данный набор обращений обладает высокой пространственной и временной локальностью. &lt;br /&gt;
&lt;br /&gt;
[[file:householder_qtq_3.png|thumb|center|500px|Рисунок 5. Профиль обращений, одна итерация, фрагмент 1]]&lt;br /&gt;
&lt;br /&gt;
Далее рассмотрим детально фрагмент 6 (рис. 6). Здесь можно увидеть, что повторные обращения к данным выполняются не подряд, а через некоторые промежутки, однако число задействованных элементов также очень невелико, так что и в этом случае локальность (и пространственная, и временная) будет высокой.&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qtq_4.png|thumb|center|500px|Рисунок 6. Профиль обращений, одна итерация, фрагмент 6]]&lt;br /&gt;
&lt;br /&gt;
В целом можно сказать, что рассматриваемая итерация обладает достаточно высокой пространственной и временной локальностью. Поскольку остальные итерации устроены подобным образом, данные вывод можно перенести и на общий профиль обращений.&lt;br /&gt;
&lt;br /&gt;
===== Количественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
Оценка выполняется на основе характеристики daps, которая оценивает число выполненных обращений (чтений и записей) в память в секунду. Данная характеристика является аналогом оценки flops применительно к работе с памятью и является в большей степени оценкой производительности взаимодействия с памятью, чем оценкой локальности. Однако она служит хорошим источником информации, в том числе для сравнения с результатами по следующей характеристике cvg.&lt;br /&gt;
&lt;br /&gt;
На рисунке 7 приведены значения daps для реализаций распространенных алгоритмов, отсортированные по возрастанию (чем больше daps, тем в общем случае выше производительность). Можно увидеть, что производительность работы с памятью в данном случае достаточно высока – значение daps лишь немногим уступает тесту Linpack, который обладает высокой эффективностью работы с памятью, и немного превосходит, например, реализацию метода Якоби или метода Гаусса.&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qtq_daps.png|thumb|center|700px|Рисунок 7. Сравнение значений оценки daps]]&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
==== Масштабируемость алгоритма ====&lt;br /&gt;
==== Масштабируемость реализации алгоритма ====&lt;br /&gt;
Проведём исследование масштабируемости вширь реализации согласно [[Scalability methodology|методике]]. Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов-2&amp;quot; [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Набор и границы значений изменяемых [[Глоссарий#Параметры запуска|параметров запуска]] реализации алгоритма: &lt;br /&gt;
&lt;br /&gt;
*	число процессоров [16:256] с шагом n^2;&lt;br /&gt;
*	размер матрицы [1000 : 32000] с шагом 1000.&lt;br /&gt;
&lt;br /&gt;
В результате проведённых экспериментов был получен следующий диапазон [[Глоссарий#Эффективность реализации|эффективности реализации]] алгоритма:&lt;br /&gt;
&lt;br /&gt;
*	минимальная эффективность реализации 4.073e-06%;&lt;br /&gt;
*	максимальная эффективность реализации 0.0667%.&lt;br /&gt;
&lt;br /&gt;
На следующих рисунках приведены графики [[Глоссарий#Производительность|производительности]] и эффективности выбранной реализации алгоритма в процедуре PDSYTRD из Scalapack в зависимости от изменяемых параметров запуска.&lt;br /&gt;
&lt;br /&gt;
[[file:PDSYTRD PERF.png|thumb|center|700px|Рисунок 8. Параллельная реализация метода Хаусхолдера QR-разложения. Изменение производительности в зависимости от числа процессоров и размера матрицы.]]&lt;br /&gt;
[[file:PDSETRD EFF.png|thumb|center|700px|Рисунок 9. Параллельная реализация метода Хаусхолдера QR-разложения. Изменение эффективности в зависимости от числа процессоров и размера матрицы.]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
В сравнении с методом Гивенса, который имеет естественное двумерное блочное разбиение на основе точечного метода, метод Хаусхолдера из-за худших характеристик локальности (наличие пучков рассылок) и меньшего количества независимых обобщённых развёрток графа не так хорош для реализаций на системах с распределённой памятью, как для систем с общей памятью. Поэтому на массово параллельных системах с распределённой памятью следует применять метод Хаусхолдера (если уж именно его нужно реализовать) не в точечной версии, а в разрабатываемых исследователями блочных вариантах. Следует отметить, что эти варианты - не блочная нарезка описанного метода, а самостоятельные методы. Особенно их применение рекомендуется в случаях с большой разрежённостью матрицы.&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Большинство пакетов от LINPACKа и LAPACKa до SCALAPACKa используют для QR-разложения матриц именно метод Хаусхолдера, правда, в различных модификациях (обычно с использованием BLAS). Существует большая подборка исследовательских работ по блочным версиям.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Статьи в работе]]&lt;br /&gt;
[[Категория:Разложения матриц]]&lt;/div&gt;</summary>
		<author><name>Teplov</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D0%A5%D0%B0%D1%83%D1%81%D1%85%D0%BE%D0%BB%D0%B4%D0%B5%D1%80%D0%B0_(%D0%BE%D1%82%D1%80%D0%B0%D0%B6%D0%B5%D0%BD%D0%B8%D0%B9)_%D0%B4%D0%BB%D1%8F_%D0%BF%D1%80%D0%B8%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D1%8F_%D1%81%D0%B8%D0%BC%D0%BC%D0%B5%D1%82%D1%80%D0%B8%D1%87%D0%BD%D1%8B%D1%85_%D0%BC%D0%B0%D1%82%D1%80%D0%B8%D1%86_%D0%BA_%D1%82%D1%80%D1%91%D1%85%D0%B4%D0%B8%D0%B0%D0%B3%D0%BE%D0%BD%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%BC%D1%83_%D0%B2%D0%B8%D0%B4%D1%83&amp;diff=21358</id>
		<title>Метод Хаусхолдера (отражений) для приведения симметричных матриц к трёхдиагональному виду</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D0%A5%D0%B0%D1%83%D1%81%D1%85%D0%BE%D0%BB%D0%B4%D0%B5%D1%80%D0%B0_(%D0%BE%D1%82%D1%80%D0%B0%D0%B6%D0%B5%D0%BD%D0%B8%D0%B9)_%D0%B4%D0%BB%D1%8F_%D0%BF%D1%80%D0%B8%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D1%8F_%D1%81%D0%B8%D0%BC%D0%BC%D0%B5%D1%82%D1%80%D0%B8%D1%87%D0%BD%D1%8B%D1%85_%D0%BC%D0%B0%D1%82%D1%80%D0%B8%D1%86_%D0%BA_%D1%82%D1%80%D1%91%D1%85%D0%B4%D0%B8%D0%B0%D0%B3%D0%BE%D0%BD%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%BC%D1%83_%D0%B2%D0%B8%D0%B4%D1%83&amp;diff=21358"/>
		<updated>2016-12-19T12:41:38Z</updated>

		<summary type="html">&lt;p&gt;Teplov: /* Масштабируемость реализации алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Приведение симметричной вещественной матрицы к трёхдиагональному виду методом Хаусхолдера (отражений)&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(n^3)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;2n^2+O(n)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;n^2/2&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;(n^2+n)/2&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;(n^2+3n)/2&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
Основные авторы описания: [[Участник:Frolov|А.В.Фролов]]&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Метод Хаусхолдера&#039;&#039;&#039; (в советской математической литературе чаще называется &#039;&#039;&#039;методом отражений&#039;&#039;&#039;) используется для приведения симметричных вещественных матриц к трёхдиагональному виду, или, что то же самое, для разложения &amp;lt;math&amp;gt;A=QTQ^T&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; - ортогональная, &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; — симметричная трёхдиагональная матрица)&amp;lt;ref&amp;gt;В.В.Воеводин, Ю.А.Кузнецов. Матрицы и вычисления. М.: Наука, 1984.&amp;lt;/ref&amp;gt;. При этом матрица &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; хранится и используется не в своём явном виде, а в виде произведения матриц отражения&amp;lt;ref name=&amp;quot;VOLA&amp;quot;&amp;gt;Воеводин В.В. Вычислительные основы линейной алгебры. М.: Наука, 1977.&amp;lt;/ref&amp;gt;. Каждая из матриц отражения может быть определена одним вектором. Это позволяет в классическом исполнении метода отражений хранить результаты разложения на месте матрицы A с использованием одномерного дополнительного массива. &lt;br /&gt;
&lt;br /&gt;
В данной статье рассматривается именно классическое исполнение, в котором не используются приёмы типа сдваивания при вычислениях скалярных произведений.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В методе Хаусхолдера для выполнения &amp;lt;math&amp;gt;QTQ^T&amp;lt;/math&amp;gt;-разложения матрицы используются умножения слева её текущих модификаций на матрицы Хаусхолдера (отражений) с последующим умножением на те же матрицы справа. &lt;br /&gt;
&lt;br /&gt;
{{Шаблон:Матрица отражений}}&lt;br /&gt;
&lt;br /&gt;
На &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м шаге метода с помощью преобразования отражения &amp;quot;убираются&amp;quot; ненулевые поддиагональные элементы, начиная с &amp;lt;math&amp;gt;i+2&amp;lt;/math&amp;gt;-го в &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м столбце. После умножения на эту же матрицу отражения справа автоматически убираются и ненулевые наддиагональные элементы, начиная с &amp;lt;math&amp;gt;i+2&amp;lt;/math&amp;gt;-го в &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-й строке, а полученная модификация снова приобретает симметричный вид. &lt;br /&gt;
&lt;br /&gt;
На каждом из шагов метода матрицу отражений обычно представляют не в стандартном виде, а в виде &amp;lt;math&amp;gt;U=E-\frac{1}{\gamma}vv^*&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; находится через координаты текущего &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; - вектор размерности &amp;lt;math&amp;gt;n+1-i&amp;lt;/math&amp;gt;, составленный из элементов &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца, начиная с &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;(s,s)=0&amp;lt;/math&amp;gt;, то &amp;lt;math&amp;gt;v=e_{i}&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\gamma = \frac{1}{2}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В остальных случаях по алгоритму вычисляется &amp;lt;math&amp;gt;u = \frac{1}{\sqrt{(s,s)}}s&amp;lt;/math&amp;gt;, и далее &amp;lt;math&amp;gt;v_{j}=0&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;j&amp;lt;i&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;v_{j}=u_{j-i+1}&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;j&amp;gt;i&amp;lt;/math&amp;gt;, а &amp;lt;math&amp;gt;v_{i}=1&amp;lt;/math&amp;gt;, если &amp;lt;math&amp;gt;u_{1}=0&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v_{i}=\frac{u_{1}}{|u_{1}|}(1+|u_{1}|)&amp;lt;/math&amp;gt; для остальных значений. При этом &amp;lt;math&amp;gt;\gamma = 1+|u_{1}|=|v_{i}|&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
После вычисления вектора &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; подстолбцы справа от ведущего модифицируются по формулам &amp;lt;math&amp;gt;x&#039;=x-\frac{(x,v)}{\gamma}v&amp;lt;/math&amp;gt;. Потом по аналогичным формулам модифицируются строки ниже ведущей. Благодаря ассоциативности этих операций после вычисления вектора &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; можно сразу выписать формулы модификации всех элементов справа и снизу от ведущих столбца и строки. Оказывается, что если для каждого столбца матрицы &amp;lt;math&amp;gt;x^{(j)}&amp;lt;/math&amp;gt; с номером &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; известно &amp;lt;math&amp;gt;\beta_{j}=\frac{(x^{(j)},v)}{\gamma}&amp;lt;/math&amp;gt;, то для модифицируемого элемента матрицы &amp;lt;math&amp;gt;y&amp;lt;/math&amp;gt; в позиции &amp;lt;math&amp;gt;(i,j)&amp;lt;/math&amp;gt; выполняется модификация &amp;lt;math&amp;gt;y&#039; = y - \beta_{i} v_{i} - \beta_{j} v_{j} - \Delta v_{i} v_{j}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\Delta = \frac{(\beta,v)}{\gamma}&amp;lt;/math&amp;gt;. При этом все эти модификации на каждом конкретном шаге алгоритма для разных пар &amp;lt;math&amp;gt;(i,j)&amp;lt;/math&amp;gt; можно выполнять независимо друг от друга, в том числе и параллельно.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
&lt;br /&gt;
[[File:HausLeftSym1.png|thumb|right|800px|Рисунок 1. Граф первой половины шага алгоритма приведения к трёхдиагональному виду с отображением входных данных. Изображён граф первой половины шага с номером &amp;lt;math&amp;gt;n-4&amp;lt;/math&amp;gt;. Квадраты - результаты выполнения предыдущего шага. Если шаг первый, то это входные данные. Зелёные кружки - операция вида &amp;lt;math&amp;gt;a+b^2&amp;lt;/math&amp;gt;, салатовые - операция вида &amp;lt;math&amp;gt;a+bc&amp;lt;/math&amp;gt;. Синий кружок - вычисление параметров матрицы отражения, светло-красные - вычисление коэффициентов &amp;lt;math&amp;gt;\beta_i&amp;lt;/math&amp;gt;для следующего полушага, жёлтые - вычисление вектора &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.]]&lt;br /&gt;
[[File:HousLeftSym2.png|thumb|right|600px|Рисунок 2. Граф второй половины шага алгоритма приведения к трёхдиагональному виду с отображением входных данных. Изображён граф первой половины шага с номером &amp;lt;math&amp;gt;n-3&amp;lt;/math&amp;gt;. Квадраты - результаты выполнения предыдущего шага. Если шаг первый, то это входные данные. Синий, жёлтые и светло-красные кружки выполняются на первом полушаге (см. Рисунок 1). Голубые кружки -  операции &amp;lt;math&amp;gt;a+bc&amp;lt;/math&amp;gt;, зелёное - умножение, чёрные - операции &amp;lt;math&amp;gt;a+bc+de+fce&amp;lt;/math&amp;gt;.]]&lt;br /&gt;
&lt;br /&gt;
Основную часть алгоритма составляют вычисления на каждом шагу скалярных произведений &amp;lt;math&amp;gt;(s,s)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(x,v)&amp;lt;/math&amp;gt; для всех подстолбцов &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt;справа от текущего, а также проводимые над нижним правым квадратом матрицы операции вида &amp;lt;math&amp;gt;y&#039;=y-ab-cd-fbd&amp;lt;/math&amp;gt;, с учётом симметрии матрицы.&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Как уже сказано в описании ядра, основная часть - вычисления на каждом шагу скалярных произведений &amp;lt;math&amp;gt;(s,s)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(x,v)&amp;lt;/math&amp;gt; для всех подстолбцов &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; справа от текущего, а также массовые покомпонентные операции &amp;lt;math&amp;gt;y&#039;=y-ab-cd-fbd&amp;lt;/math&amp;gt;. При этом, однако, строгая последовательность выполнения первых двух подшагов не обязательна, в силу связи получаемых векторов &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; можно одновременно с &amp;lt;math&amp;gt;(s,s)&amp;lt;/math&amp;gt; вычислять и произведения &amp;lt;math&amp;gt;(x,s)&amp;lt;/math&amp;gt; с последующим выражением через них коэффициентов модификации. Это позволяет почти вдвое уменьшать критический путь графа алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Последовательность выполнения алгоритма обычно записывается как последовательное &amp;quot;обнуление&amp;quot; поддиагональных элементов столбцов, начиная с 1-го столбца и заканчивая предпоследним &amp;lt;math&amp;gt;(n-1)&amp;lt;/math&amp;gt;-м.&lt;br /&gt;
&lt;br /&gt;
При этом в каждом &amp;quot;обнуляемом&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м столбце &amp;quot;обнуляются&amp;quot; сразу все его поддиагональные элементы одновременно, с &amp;lt;math&amp;gt;(i+1)&amp;lt;/math&amp;gt;-го до &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;-го. &lt;br /&gt;
&lt;br /&gt;
Каждое &amp;quot;обнуление&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца состоит из двух шагов: &lt;br /&gt;
а) вычисление параметров матрицы отражения &amp;lt;math&amp;gt;U_{i}&amp;lt;/math&amp;gt; такой, чтобы при умножении на неё слева &amp;quot;обнулились&amp;quot; все поддиагональные его элементы;&lt;br /&gt;
б) одновременное умножение слева матрицы отражения &amp;lt;math&amp;gt;U_{i}&amp;lt;/math&amp;gt; и справа  матрицы отражения &amp;lt;math&amp;gt;U_{i}^T&amp;lt;/math&amp;gt;на текущую версию матрицы.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В последовательной версии основная сложность алгоритма определяется прежде всего вычислениями скалярных произведений векторов, а также массовых модификаций элементов вида &amp;lt;math&amp;gt;y&#039;=y-\alpha v - \beta w - \Delta vw&amp;lt;/math&amp;gt;. Они, если не учитывать возможную разреженность, составляют (в главном члене) по &amp;lt;math&amp;gt;O(n^3)&amp;lt;/math&amp;gt; операций действительного умножения и сложения/вычитания.&lt;br /&gt;
&lt;br /&gt;
При классификации по последовательной сложности, таким образом, метод Хаусхолдера относится к алгоритмам &#039;&#039;с кубической сложностью&#039;&#039;.&lt;br /&gt;
&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;n&amp;lt;/math&amp;gt; к трёхдиагональной методом Хаусхолдера нужно рассмотреть критический путь графа. &lt;br /&gt;
&lt;br /&gt;
Как видно из описания разных вершин, вычисления при &amp;quot;обнулении&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца параметров отражения и скалярных произведений состоят из основной части - ветви длиной по &amp;lt;math&amp;gt;2(n-i)&amp;lt;/math&amp;gt; умножений и сложений - и коррекции вычислений, которые составляют &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; операций. &lt;br /&gt;
&lt;br /&gt;
Поэтому по грубой (без членов низших порядков) оценке критический путь метода Хаусхолдера будет идти через &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt; умножений и &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt; сложений/вычитаний. &lt;br /&gt;
 &lt;br /&gt;
Поэтому в параллельном варианте, как и в последовательном, основную долю требуемого для выполнения алгоритма времени будут определять операции вида &amp;lt;math&amp;gt;a+bc&amp;lt;/math&amp;gt;.  &lt;br /&gt;
&lt;br /&gt;
При классификации по высоте ЯПФ, таким образом, метод Хаусхолдера относится к алгоритмам &#039;&#039;с квадратичной сложностью&#039;&#039;. При классификации по ширине ЯПФ его сложность будет также &#039;&#039;квадратичной&#039;&#039; (без расширения ряда ярусов, связанных с векторными операциями сложения, пришлось бы увеличить вдвое длину критического пути).&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: плотная симметричная квадратная матрица &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; (элементы &amp;lt;math&amp;gt;a_{ij}&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;(n^2+n)/2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: трёхдиагональная матрица &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; (ненулевые элементы &amp;lt;math&amp;gt;r_{ij}&amp;lt;/math&amp;gt; в последовательном варианте хранятся в элементах исходной матрицы &amp;lt;math&amp;gt;a_{ij}&amp;lt;/math&amp;gt;), унитарная (ортогональная) матрица Q - как произведение матриц Хаусхолдера (отражения) (их вектора нормалей к плоскостям отражения в последовательном варианте хранятся в поддиагональных элементах исходной матрицы &amp;lt;math&amp;gt;a_{ij}&amp;lt;/math&amp;gt; и в одном дополнительном столбце размерности n).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;(n^2+3n)/2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Соотношение последовательной и параллельной сложности, как хорошо видно, является &#039;&#039;линейным&#039;&#039;, что даёт определённый стимул для распараллеливания. Однако у наискорейшей ЯПФ ширина &#039;&#039;квадратична&#039;&#039;, что указывает на дисбаланс между загруженностями устройств при попытке её реально запрограммировать. Поэтому более практично даже при хорошей (быстрой) вычислительной сети оставить количество устройств (например, узлов кластера) &#039;&#039;линейным&#039;&#039; по размеру матрицы, что удвоит критический путь реализуемой ЯПФ. &lt;br /&gt;
&lt;br /&gt;
При этом вычислительная мощность алгоритма, как отношение числа операций к суммарному объему входных и выходных данных, &#039;&#039;линейна&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Алгоритм в рамках выбранной версии полностью детерминирован.&lt;br /&gt;
&lt;br /&gt;
Вычислительная погрешность в методе отражений (Хаусхолдера) растет &#039;&#039;линейно&#039;&#039;, как и в методе Гивенса (вращений).&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
В варианте с кратчайшим критическим путём графа алгоритма (с использованием зависимости между обнуляемым вектором и направляющим вектором отражения) метод Хаусхолдера (отражений) приведения квадратной симметричной вещественной матрицы к трёхдиагональному виду на Фортране 77 можно записать так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;fortran&amp;quot;&amp;gt;&lt;br /&gt;
	DO  I = 1, N-2&lt;br /&gt;
&lt;br /&gt;
            DO K = I, N&lt;br /&gt;
              SX(K)=A(N,I)*A(N,K)&lt;br /&gt;
            END DO&lt;br /&gt;
            DO J = N-1, I+1, -1&lt;br /&gt;
                 SX(I)=SX(I)+A(J,I)*A(J,I)&lt;br /&gt;
            END DO &lt;br /&gt;
            DO K = I+1, N&lt;br /&gt;
              DO J = N-1, K, -1&lt;br /&gt;
                 SX(K)=SX(K)+A(J,I)*A(J,K)&lt;br /&gt;
              END DO&lt;br /&gt;
              DO J = K-1,I+1,-1&lt;br /&gt;
                 SX(K)=SX(K)+A(J,I)*A(K,J)&lt;br /&gt;
              END DO&lt;br /&gt;
            END DO&lt;br /&gt;
&lt;br /&gt;
               ALPHA = SQRT (SX(I))&lt;br /&gt;
               IF (A(I+1,I).NE.0.) THEN&lt;br /&gt;
                  BETA = 1./ALPHA&lt;br /&gt;
                  DO J = I+2, N&lt;br /&gt;
                    A(J,I)=A(J,I)*BETA&lt;br /&gt;
                  END DO&lt;br /&gt;
                  SX(I) = A(I+1,I)*BETA+SIGN(1.,A(I+1,I))                     &lt;br /&gt;
                  A(I+1,I)=ALPHA&lt;br /&gt;
                  G=1./ABS(SX(I)) ! 1/gamma&lt;br /&gt;
                  SX2=0.&lt;br /&gt;
                  DO K = I+2, N&lt;br /&gt;
                     SX(K)=SX(K)*BETA*G+SIGN(A(K,I+1),SX(I))&lt;br /&gt;
                     SX2=SX(K)*A(K,I)+SX2                     &lt;br /&gt;
                  END DO&lt;br /&gt;
                  SX2=G*SX2&lt;br /&gt;
                  DO K = I+2, N&lt;br /&gt;
                     A(K,K) = A(K,K)-2*A(K,I)*SX(K)+SX2*A(K,I)**2&lt;br /&gt;
                     DO J = K+1, N&lt;br /&gt;
                       A (J,K) = A(J,K)-A(J,I)*SX(K)-A(K,I)*SX(I)+SX2*A(J,I)*A(K,I)&lt;br /&gt;
                     END DO&lt;br /&gt;
                  END DO&lt;br /&gt;
               ELSE&lt;br /&gt;
                  IF (ALPHA.NE.0.) THEN&lt;br /&gt;
                     BETA = 1./ALPHA&lt;br /&gt;
                     DO J = I+2, N&lt;br /&gt;
                       A(J,I)=A(J,I)*BETA&lt;br /&gt;
                     END DO&lt;br /&gt;
                     SX(I) = -1. &lt;br /&gt;
                     A(I+1,I)=ALPHA&lt;br /&gt;
                     G=1.! 1/gamma&lt;br /&gt;
                     SX2=0.&lt;br /&gt;
                     DO K = I+2, N&lt;br /&gt;
                        SX(K)=SX(K)*BETA*G+SIGN(A(K,I+1),SX(I))&lt;br /&gt;
                        SX2=SX(K)*A(K,I)+SX2                 &lt;br /&gt;
                     END DO&lt;br /&gt;
                     SX2=G*SX2&lt;br /&gt;
                     DO K = I+2, N  &lt;br /&gt;
                        A(K,K) = A(K,K)-2*A(K,I)*SX(K)+SX2*A(K,I)**2                   &lt;br /&gt;
                        DO J = K+1, N&lt;br /&gt;
                          A (J,K) = A(J,K)-A(J,I)*SX(K)-A(K,I)*SX(I)+SX2*A(J,I)*A(K,I)&lt;br /&gt;
                        END DO&lt;br /&gt;
                     END DO&lt;br /&gt;
                  ELSE&lt;br /&gt;
                     SX(I)=1.&lt;br /&gt;
                  END IF&lt;br /&gt;
               END IF               &lt;br /&gt;
&lt;br /&gt;
		&lt;br /&gt;
	END DO&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Здесь симметричная трёхдиагональная матрица хранится в диагонали и нижней кодиагонали массива A, вектора v - в поддиагональной части массива, за исключением первых их элементов, для которых выделен массив SX.&lt;br /&gt;
&lt;br /&gt;
Обычно же в последовательных версиях коэффициенты модификаций столбцов вычисляются целиком через скалярные произведения после вычислений параметров матрицы отражения. При этом схема чуть проще. Удлиняется критический путь графа, но для последовательных реализаций это неважно.&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
К сожалению, в графе, как видно по рисунку, в наличии пучки рассылок, в них неизбежно часть дуг остаются длинными, что отрицательно влияет на локальность вычислений по пространству.  &lt;br /&gt;
&lt;br /&gt;
==== Локальность реализации алгоритма ====&lt;br /&gt;
===== Структура обращений в память и качественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qtq_1.png|thumb|center|700px|Рисунок 3. Метод Хаусхолдера (отражений) для приведения симметричных матриц к трёхдиагональному виду. Общий профиль обращений в память]]&lt;br /&gt;
&lt;br /&gt;
На рис. 3 представлен профиль обращений в память для реализации метода Хаусхолдера (отражений) для приведения симметричных матриц к трёхдиагональному виду. Данный профиль обладает явной итерационной структурой, при этом видно, что итерации очень похожи. Основное их отличие заключается в наборе используемых данных – на каждой следующей итерации несколько первых элементов отбрасываются их рассмотрения, то есть чем позже итерация, тем меньше данных в ней задействовано. Также можно отметить, что число обращений в каждой последующей итерации немного уменьшается. Для того чтобы проанализировать общий профиль, в таком случае чаще всего достаточно изучить одну итерацию. Рассмотрим самую первую из них более детально.&lt;br /&gt;
&lt;br /&gt;
На рис. 4 показан набор обращений в рамках первой итерации (выделен на рис. 3 зеленым). Его можно разбить на несколько фрагментов и рассмотреть их отдельно. Строение фрагментов 2-5 можно оценить по общему графику для итерации. Фрагмент 2 представляет собой набор последовательных переборов в обратном порядке с небольшим шагом, причем число элементов в переборе постепенно уменьшается. Такое строение характеризуется достаточно высокой пространственной локальностью, поскольку часто происходит обращений к близко расположенным данным, однако низкой временной, поскольку данные повторно не используются. То же самое верно и для фрагмента 4, основное отличие которого заключается в том, что переборы выполняются в прямом порядке. Отметим, что некоторое искривление данного фрагмента связано не со строением самого фрагмента, а с разным числом параллельно выполняющихся обращений (см., например, правую область фрагмента 6), чего не наблюдается при выполнении фрагмента 2.&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qtq_2.png|thumb|center|700px|Рисунок 4. Профиль обращений, одна итерация]]&lt;br /&gt;
&lt;br /&gt;
Далее, фрагменты  3 и 5 состоят из небольшого числа обращений к данным, расположенным далеко друг от друга. Такие фрагменты характеризуются низкой пространственной и временной локальностью.&lt;br /&gt;
&lt;br /&gt;
Оставшиеся фрагменты требуют более детального рассмотрения. Перейдем к изучению фрагмента 1, который целиком представлен на рис. 5. Здесь мы можем видеть несколько этапов, каждый из которых представляет собой последовательный перебор элементов с шагом по памяти 1, причем в некоторых случаях данные много раз используются повторно (особенно это заметно в правой нижней области рисунка, где выполняется множество обращений к одному и тому же элементу). При условии, что в данном фрагменте задействовано всего около 40 элементов, можно говорить, что данный набор обращений обладает высокой пространственной и временной локальностью. &lt;br /&gt;
&lt;br /&gt;
[[file:householder_qtq_3.png|thumb|center|500px|Рисунок 5. Профиль обращений, одна итерация, фрагмент 1]]&lt;br /&gt;
&lt;br /&gt;
Далее рассмотрим детально фрагмент 6 (рис. 6). Здесь можно увидеть, что повторные обращения к данным выполняются не подряд, а через некоторые промежутки, однако число задействованных элементов также очень невелико, так что и в этом случае локальность (и пространственная, и временная) будет высокой.&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qtq_4.png|thumb|center|500px|Рисунок 6. Профиль обращений, одна итерация, фрагмент 6]]&lt;br /&gt;
&lt;br /&gt;
В целом можно сказать, что рассматриваемая итерация обладает достаточно высокой пространственной и временной локальностью. Поскольку остальные итерации устроены подобным образом, данные вывод можно перенести и на общий профиль обращений.&lt;br /&gt;
&lt;br /&gt;
===== Количественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
Оценка выполняется на основе характеристики daps, которая оценивает число выполненных обращений (чтений и записей) в память в секунду. Данная характеристика является аналогом оценки flops применительно к работе с памятью и является в большей степени оценкой производительности взаимодействия с памятью, чем оценкой локальности. Однако она служит хорошим источником информации, в том числе для сравнения с результатами по следующей характеристике cvg.&lt;br /&gt;
&lt;br /&gt;
На рисунке 7 приведены значения daps для реализаций распространенных алгоритмов, отсортированные по возрастанию (чем больше daps, тем в общем случае выше производительность). Можно увидеть, что производительность работы с памятью в данном случае достаточно высока – значение daps лишь немногим уступает тесту Linpack, который обладает высокой эффективностью работы с памятью, и немного превосходит, например, реализацию метода Якоби или метода Гаусса.&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qtq_daps.png|thumb|center|700px|Рисунок 7. Сравнение значений оценки daps]]&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
==== Масштабируемость алгоритма ====&lt;br /&gt;
==== Масштабируемость реализации алгоритма ====&lt;br /&gt;
Проведём исследование масштабируемости вширь реализации согласно [[Scalability methodology|методике]]. Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов-2&amp;quot; [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Набор и границы значений изменяемых [[Глоссарий#Параметры запуска|параметров запуска]] реализации алгоритма: &lt;br /&gt;
&lt;br /&gt;
*	число процессоров [16:256] с шагом n^2;&lt;br /&gt;
*	размер матрицы [1000 : 32000] с шагом 1000.&lt;br /&gt;
&lt;br /&gt;
В результате проведённых экспериментов был получен следующий диапазон [[Глоссарий#Эффективность реализации|эффективности реализации]] алгоритма:&lt;br /&gt;
&lt;br /&gt;
*	минимальная эффективность реализации 4.073e-06%;&lt;br /&gt;
*	максимальная эффективность реализации 0.0667%.&lt;br /&gt;
&lt;br /&gt;
На следующих рисунках приведены графики [[Глоссарий#Производительность|производительности]] и эффективности выбранной реализации алгоритма в процедуре PDSYTRD из Scalapack в зависимости от изменяемых параметров запуска.&lt;br /&gt;
&lt;br /&gt;
[[file:PDSYTRD PERF.png|thumb|center|700px|Рисунок 8. Параллельная реализация метода Хаусхолдера QR-разложения. Изменение производительности в зависимости от числа процессоров и размера матрицы.]]&lt;br /&gt;
[[file:PDSYTRD EFF.png|thumb|center|700px|Рисунок 9. Параллельная реализация метода Хаусхолдера QR-разложения. Изменение эффективности в зависимости от числа процессоров и размера матрицы.]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
В сравнении с методом Гивенса, который имеет естественное двумерное блочное разбиение на основе точечного метода, метод Хаусхолдера из-за худших характеристик локальности (наличие пучков рассылок) и меньшего количества независимых обобщённых развёрток графа не так хорош для реализаций на системах с распределённой памятью, как для систем с общей памятью. Поэтому на массово параллельных системах с распределённой памятью следует применять метод Хаусхолдера (если уж именно его нужно реализовать) не в точечной версии, а в разрабатываемых исследователями блочных вариантах. Следует отметить, что эти варианты - не блочная нарезка описанного метода, а самостоятельные методы. Особенно их применение рекомендуется в случаях с большой разрежённостью матрицы.&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Большинство пакетов от LINPACKа и LAPACKa до SCALAPACKa используют для QR-разложения матриц именно метод Хаусхолдера, правда, в различных модификациях (обычно с использованием BLAS). Существует большая подборка исследовательских работ по блочным версиям.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Статьи в работе]]&lt;br /&gt;
[[Категория:Разложения матриц]]&lt;/div&gt;</summary>
		<author><name>Teplov</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D0%A5%D0%B0%D1%83%D1%81%D1%85%D0%BE%D0%BB%D0%B4%D0%B5%D1%80%D0%B0_(%D0%BE%D1%82%D1%80%D0%B0%D0%B6%D0%B5%D0%BD%D0%B8%D0%B9)_%D0%B4%D0%BB%D1%8F_%D0%BF%D1%80%D0%B8%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D1%8F_%D1%81%D0%B8%D0%BC%D0%BC%D0%B5%D1%82%D1%80%D0%B8%D1%87%D0%BD%D1%8B%D1%85_%D0%BC%D0%B0%D1%82%D1%80%D0%B8%D1%86_%D0%BA_%D1%82%D1%80%D1%91%D1%85%D0%B4%D0%B8%D0%B0%D0%B3%D0%BE%D0%BD%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%BC%D1%83_%D0%B2%D0%B8%D0%B4%D1%83&amp;diff=21168</id>
		<title>Метод Хаусхолдера (отражений) для приведения симметричных матриц к трёхдиагональному виду</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D0%A5%D0%B0%D1%83%D1%81%D1%85%D0%BE%D0%BB%D0%B4%D0%B5%D1%80%D0%B0_(%D0%BE%D1%82%D1%80%D0%B0%D0%B6%D0%B5%D0%BD%D0%B8%D0%B9)_%D0%B4%D0%BB%D1%8F_%D0%BF%D1%80%D0%B8%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D1%8F_%D1%81%D0%B8%D0%BC%D0%BC%D0%B5%D1%82%D1%80%D0%B8%D1%87%D0%BD%D1%8B%D1%85_%D0%BC%D0%B0%D1%82%D1%80%D0%B8%D1%86_%D0%BA_%D1%82%D1%80%D1%91%D1%85%D0%B4%D0%B8%D0%B0%D0%B3%D0%BE%D0%BD%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%BC%D1%83_%D0%B2%D0%B8%D0%B4%D1%83&amp;diff=21168"/>
		<updated>2016-12-18T10:30:32Z</updated>

		<summary type="html">&lt;p&gt;Teplov: /* Масштабируемость реализации алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Приведение симметричной вещественной матрицы к трёхдиагональному виду методом Хаусхолдера (отражений)&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(n^3)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;2n^2+O(n)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;n^2/2&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;(n^2+n)/2&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;(n^2+3n)/2&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
Основные авторы описания: [[Участник:Frolov|А.В.Фролов]]&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Метод Хаусхолдера&#039;&#039;&#039; (в советской математической литературе чаще называется &#039;&#039;&#039;методом отражений&#039;&#039;&#039;) используется для приведения симметричных вещественных матриц к трёхдиагональному виду, или, что то же самое, для разложения &amp;lt;math&amp;gt;A=QTQ^T&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; - ортогональная, &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; — симметричная трёхдиагональная матрица)&amp;lt;ref&amp;gt;В.В.Воеводин, Ю.А.Кузнецов. Матрицы и вычисления. М.: Наука, 1984.&amp;lt;/ref&amp;gt;. При этом матрица &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; хранится и используется не в своём явном виде, а в виде произведения матриц отражения&amp;lt;ref name=&amp;quot;VOLA&amp;quot;&amp;gt;Воеводин В.В. Вычислительные основы линейной алгебры. М.: Наука, 1977.&amp;lt;/ref&amp;gt;. Каждая из матриц отражения может быть определена одним вектором. Это позволяет в классическом исполнении метода отражений хранить результаты разложения на месте матрицы A с использованием одномерного дополнительного массива. &lt;br /&gt;
&lt;br /&gt;
В данной статье рассматривается именно классическое исполнение, в котором не используются приёмы типа сдваивания при вычислениях скалярных произведений.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В методе Хаусхолдера для выполнения &amp;lt;math&amp;gt;QTQ^T&amp;lt;/math&amp;gt;-разложения матрицы используются умножения слева её текущих модификаций на матрицы Хаусхолдера (отражений) с последующим умножением на те же матрицы справа. &lt;br /&gt;
&lt;br /&gt;
{{Шаблон:Матрица отражений}}&lt;br /&gt;
&lt;br /&gt;
На &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м шаге метода с помощью преобразования отражения &amp;quot;убираются&amp;quot; ненулевые поддиагональные элементы, начиная с &amp;lt;math&amp;gt;i+2&amp;lt;/math&amp;gt;-го в &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м столбце. После умножения на эту же матрицу отражения справа автоматически убираются и ненулевые наддиагональные элементы, начиная с &amp;lt;math&amp;gt;i+2&amp;lt;/math&amp;gt;-го в &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-й строке, а полученная модификация снова приобретает симметричный вид. &lt;br /&gt;
&lt;br /&gt;
На каждом из шагов метода матрицу отражений обычно представляют не в стандартном виде, а в виде &amp;lt;math&amp;gt;U=E-\frac{1}{\gamma}vv^*&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; находится через координаты текущего &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; - вектор размерности &amp;lt;math&amp;gt;n+1-i&amp;lt;/math&amp;gt;, составленный из элементов &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца, начиная с &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;(s,s)=0&amp;lt;/math&amp;gt;, то &amp;lt;math&amp;gt;v=e_{i}&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\gamma = \frac{1}{2}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В остальных случаях по алгоритму вычисляется &amp;lt;math&amp;gt;u = \frac{1}{\sqrt{(s,s)}}s&amp;lt;/math&amp;gt;, и далее &amp;lt;math&amp;gt;v_{j}=0&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;j&amp;lt;i&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;v_{j}=u_{j-i+1}&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;j&amp;gt;i&amp;lt;/math&amp;gt;, а &amp;lt;math&amp;gt;v_{i}=1&amp;lt;/math&amp;gt;, если &amp;lt;math&amp;gt;u_{1}=0&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v_{i}=\frac{u_{1}}{|u_{1}|}(1+|u_{1}|)&amp;lt;/math&amp;gt; для остальных значений. При этом &amp;lt;math&amp;gt;\gamma = 1+|u_{1}|=|v_{i}|&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
После вычисления вектора &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; подстолбцы справа от ведущего модифицируются по формулам &amp;lt;math&amp;gt;x&#039;=x-\frac{(x,v)}{\gamma}v&amp;lt;/math&amp;gt;. Потом по аналогичным формулам модифицируются строки ниже ведущей. Благодаря ассоциативности этих операций после вычисления вектора &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; можно сразу выписать формулы модификации всех элементов справа и снизу от ведущих столбца и строки. Оказывается, что если для каждого столбца матрицы &amp;lt;math&amp;gt;x^{(j)}&amp;lt;/math&amp;gt; с номером &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; известно &amp;lt;math&amp;gt;\beta_{j}=\frac{(x^{(j)},v)}{\gamma}&amp;lt;/math&amp;gt;, то для модифицируемого элемента матрицы &amp;lt;math&amp;gt;y&amp;lt;/math&amp;gt; в позиции &amp;lt;math&amp;gt;(i,j)&amp;lt;/math&amp;gt; выполняется модификация &amp;lt;math&amp;gt;y&#039; = y - \beta_{i} v_{i} - \beta_{j} v_{j} - \Delta v_{i} v_{j}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\Delta = \frac{(\beta,v)}{\gamma}&amp;lt;/math&amp;gt;. При этом все эти модификации на каждом конкретном шаге алгоритма для разных пар &amp;lt;math&amp;gt;(i,j)&amp;lt;/math&amp;gt; можно выполнять независимо друг от друга, в том числе и параллельно.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
&lt;br /&gt;
[[File:HausLeftSym1.png|thumb|right|800px|Рисунок 1. Граф первой половины шага алгоритма приведения к трёхдиагональному виду с отображением входных данных. Изображён граф первой половины шага с номером &amp;lt;math&amp;gt;n-4&amp;lt;/math&amp;gt;. Квадраты - результаты выполнения предыдущего шага. Если шаг первый, то это входные данные. Зелёные кружки - операция вида &amp;lt;math&amp;gt;a+b^2&amp;lt;/math&amp;gt;, салатовые - операция вида &amp;lt;math&amp;gt;a+bc&amp;lt;/math&amp;gt;. Синий кружок - вычисление параметров матрицы отражения, светло-красные - вычисление коэффициентов &amp;lt;math&amp;gt;\beta_i&amp;lt;/math&amp;gt;для следующего полушага, жёлтые - вычисление вектора &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.]]&lt;br /&gt;
[[File:HousLeftSym2.png|thumb|right|600px|Рисунок 2. Граф второй половины шага алгоритма приведения к трёхдиагональному виду с отображением входных данных. Изображён граф первой половины шага с номером &amp;lt;math&amp;gt;n-3&amp;lt;/math&amp;gt;. Квадраты - результаты выполнения предыдущего шага. Если шаг первый, то это входные данные. Синий, жёлтые и светло-красные кружки выполняются на первом полушаге (см. Рисунок 1). Голубые кружки -  операции &amp;lt;math&amp;gt;a+bc&amp;lt;/math&amp;gt;, зелёное - умножение, чёрные - операции &amp;lt;math&amp;gt;a+bc+de+fce&amp;lt;/math&amp;gt;.]]&lt;br /&gt;
&lt;br /&gt;
Основную часть алгоритма составляют вычисления на каждом шагу скалярных произведений &amp;lt;math&amp;gt;(s,s)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(x,v)&amp;lt;/math&amp;gt; для всех подстолбцов &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt;справа от текущего, а также проводимые над нижним правым квадратом матрицы операции вида &amp;lt;math&amp;gt;y&#039;=y-ab-cd-fbd&amp;lt;/math&amp;gt;, с учётом симметрии матрицы.&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Как уже сказано в описании ядра, основная часть - вычисления на каждом шагу скалярных произведений &amp;lt;math&amp;gt;(s,s)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(x,v)&amp;lt;/math&amp;gt; для всех подстолбцов &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; справа от текущего, а также массовые покомпонентные операции &amp;lt;math&amp;gt;y&#039;=y-ab-cd-fbd&amp;lt;/math&amp;gt;. При этом, однако, строгая последовательность выполнения первых двух подшагов не обязательна, в силу связи получаемых векторов &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; можно одновременно с &amp;lt;math&amp;gt;(s,s)&amp;lt;/math&amp;gt; вычислять и произведения &amp;lt;math&amp;gt;(x,s)&amp;lt;/math&amp;gt; с последующим выражением через них коэффициентов модификации. Это позволяет почти вдвое уменьшать критический путь графа алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Последовательность выполнения алгоритма обычно записывается как последовательное &amp;quot;обнуление&amp;quot; поддиагональных элементов столбцов, начиная с 1-го столбца и заканчивая предпоследним &amp;lt;math&amp;gt;(n-1)&amp;lt;/math&amp;gt;-м.&lt;br /&gt;
&lt;br /&gt;
При этом в каждом &amp;quot;обнуляемом&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м столбце &amp;quot;обнуляются&amp;quot; сразу все его поддиагональные элементы одновременно, с &amp;lt;math&amp;gt;(i+1)&amp;lt;/math&amp;gt;-го до &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;-го. &lt;br /&gt;
&lt;br /&gt;
Каждое &amp;quot;обнуление&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца состоит из двух шагов: &lt;br /&gt;
а) вычисление параметров матрицы отражения &amp;lt;math&amp;gt;U_{i}&amp;lt;/math&amp;gt; такой, чтобы при умножении на неё слева &amp;quot;обнулились&amp;quot; все поддиагональные его элементы;&lt;br /&gt;
б) одновременное умножение слева матрицы отражения &amp;lt;math&amp;gt;U_{i}&amp;lt;/math&amp;gt; и справа  матрицы отражения &amp;lt;math&amp;gt;U_{i}^T&amp;lt;/math&amp;gt;на текущую версию матрицы.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В последовательной версии основная сложность алгоритма определяется прежде всего вычислениями скалярных произведений векторов, а также массовых модификаций элементов вида &amp;lt;math&amp;gt;y&#039;=y-\alpha v - \beta w - \Delta vw&amp;lt;/math&amp;gt;. Они, если не учитывать возможную разреженность, составляют (в главном члене) по &amp;lt;math&amp;gt;O(n^3)&amp;lt;/math&amp;gt; операций действительного умножения и сложения/вычитания.&lt;br /&gt;
&lt;br /&gt;
При классификации по последовательной сложности, таким образом, метод Хаусхолдера относится к алгоритмам &#039;&#039;с кубической сложностью&#039;&#039;.&lt;br /&gt;
&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;n&amp;lt;/math&amp;gt; к трёхдиагональной методом Хаусхолдера нужно рассмотреть критический путь графа. &lt;br /&gt;
&lt;br /&gt;
Как видно из описания разных вершин, вычисления при &amp;quot;обнулении&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца параметров отражения и скалярных произведений состоят из основной части - ветви длиной по &amp;lt;math&amp;gt;2(n-i)&amp;lt;/math&amp;gt; умножений и сложений - и коррекции вычислений, которые составляют &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; операций. &lt;br /&gt;
&lt;br /&gt;
Поэтому по грубой (без членов низших порядков) оценке критический путь метода Хаусхолдера будет идти через &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt; умножений и &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt; сложений/вычитаний. &lt;br /&gt;
 &lt;br /&gt;
Поэтому в параллельном варианте, как и в последовательном, основную долю требуемого для выполнения алгоритма времени будут определять операции вида &amp;lt;math&amp;gt;a+bc&amp;lt;/math&amp;gt;.  &lt;br /&gt;
&lt;br /&gt;
При классификации по высоте ЯПФ, таким образом, метод Хаусхолдера относится к алгоритмам &#039;&#039;с квадратичной сложностью&#039;&#039;. При классификации по ширине ЯПФ его сложность будет также &#039;&#039;квадратичной&#039;&#039; (без расширения ряда ярусов, связанных с векторными операциями сложения, пришлось бы увеличить вдвое длину критического пути).&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: плотная симметричная квадратная матрица &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; (элементы &amp;lt;math&amp;gt;a_{ij}&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;(n^2+n)/2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: трёхдиагональная матрица &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; (ненулевые элементы &amp;lt;math&amp;gt;r_{ij}&amp;lt;/math&amp;gt; в последовательном варианте хранятся в элементах исходной матрицы &amp;lt;math&amp;gt;a_{ij}&amp;lt;/math&amp;gt;), унитарная (ортогональная) матрица Q - как произведение матриц Хаусхолдера (отражения) (их вектора нормалей к плоскостям отражения в последовательном варианте хранятся в поддиагональных элементах исходной матрицы &amp;lt;math&amp;gt;a_{ij}&amp;lt;/math&amp;gt; и в одном дополнительном столбце размерности n).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;(n^2+3n)/2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Соотношение последовательной и параллельной сложности, как хорошо видно, является &#039;&#039;линейным&#039;&#039;, что даёт определённый стимул для распараллеливания. Однако у наискорейшей ЯПФ ширина &#039;&#039;квадратична&#039;&#039;, что указывает на дисбаланс между загруженностями устройств при попытке её реально запрограммировать. Поэтому более практично даже при хорошей (быстрой) вычислительной сети оставить количество устройств (например, узлов кластера) &#039;&#039;линейным&#039;&#039; по размеру матрицы, что удвоит критический путь реализуемой ЯПФ. &lt;br /&gt;
&lt;br /&gt;
При этом вычислительная мощность алгоритма, как отношение числа операций к суммарному объему входных и выходных данных, &#039;&#039;линейна&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Алгоритм в рамках выбранной версии полностью детерминирован.&lt;br /&gt;
&lt;br /&gt;
Вычислительная погрешность в методе отражений (Хаусхолдера) растет &#039;&#039;линейно&#039;&#039;, как и в методе Гивенса (вращений).&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
В варианте с кратчайшим критическим путём графа алгоритма (с использованием зависимости между обнуляемым вектором и направляющим вектором отражения) метод Хаусхолдера (отражений) приведения квадратной симметричной вещественной матрицы к трёхдиагональному виду на Фортране 77 можно записать так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;fortran&amp;quot;&amp;gt;&lt;br /&gt;
	DO  I = 1, N-2&lt;br /&gt;
&lt;br /&gt;
            DO K = I, N&lt;br /&gt;
              SX(K)=A(N,I)*A(N,K)&lt;br /&gt;
            END DO&lt;br /&gt;
            DO J = N-1, I+1, -1&lt;br /&gt;
                 SX(I)=SX(I)+A(J,I)*A(J,I)&lt;br /&gt;
            END DO &lt;br /&gt;
            DO K = I+1, N&lt;br /&gt;
              DO J = N-1, K, -1&lt;br /&gt;
                 SX(K)=SX(K)+A(J,I)*A(J,K)&lt;br /&gt;
              END DO&lt;br /&gt;
              DO J = K-1,I+1,-1&lt;br /&gt;
                 SX(K)=SX(K)+A(J,I)*A(K,J)&lt;br /&gt;
              END DO&lt;br /&gt;
            END DO&lt;br /&gt;
&lt;br /&gt;
               ALPHA = SQRT (SX(I))&lt;br /&gt;
               IF (A(I+1,I).NE.0.) THEN&lt;br /&gt;
                  BETA = 1./ALPHA&lt;br /&gt;
                  DO J = I+2, N&lt;br /&gt;
                    A(J,I)=A(J,I)*BETA&lt;br /&gt;
                  END DO&lt;br /&gt;
                  SX(I) = A(I+1,I)*BETA+SIGN(1.,A(I+1,I))                     &lt;br /&gt;
                  A(I+1,I)=ALPHA&lt;br /&gt;
                  G=1./ABS(SX(I)) ! 1/gamma&lt;br /&gt;
                  SX2=0.&lt;br /&gt;
                  DO K = I+2, N&lt;br /&gt;
                     SX(K)=SX(K)*BETA*G+SIGN(A(K,I+1),SX(I))&lt;br /&gt;
                     SX2=SX(K)*A(K,I)+SX2                     &lt;br /&gt;
                  END DO&lt;br /&gt;
                  SX2=G*SX2&lt;br /&gt;
                  DO K = I+2, N&lt;br /&gt;
                     A(K,K) = A(K,K)-2*A(K,I)*SX(K)+SX2*A(K,I)**2&lt;br /&gt;
                     DO J = K+1, N&lt;br /&gt;
                       A (J,K) = A(J,K)-A(J,I)*SX(K)-A(K,I)*SX(I)+SX2*A(J,I)*A(K,I)&lt;br /&gt;
                     END DO&lt;br /&gt;
                  END DO&lt;br /&gt;
               ELSE&lt;br /&gt;
                  IF (ALPHA.NE.0.) THEN&lt;br /&gt;
                     BETA = 1./ALPHA&lt;br /&gt;
                     DO J = I+2, N&lt;br /&gt;
                       A(J,I)=A(J,I)*BETA&lt;br /&gt;
                     END DO&lt;br /&gt;
                     SX(I) = -1. &lt;br /&gt;
                     A(I+1,I)=ALPHA&lt;br /&gt;
                     G=1.! 1/gamma&lt;br /&gt;
                     SX2=0.&lt;br /&gt;
                     DO K = I+2, N&lt;br /&gt;
                        SX(K)=SX(K)*BETA*G+SIGN(A(K,I+1),SX(I))&lt;br /&gt;
                        SX2=SX(K)*A(K,I)+SX2                 &lt;br /&gt;
                     END DO&lt;br /&gt;
                     SX2=G*SX2&lt;br /&gt;
                     DO K = I+2, N  &lt;br /&gt;
                        A(K,K) = A(K,K)-2*A(K,I)*SX(K)+SX2*A(K,I)**2                   &lt;br /&gt;
                        DO J = K+1, N&lt;br /&gt;
                          A (J,K) = A(J,K)-A(J,I)*SX(K)-A(K,I)*SX(I)+SX2*A(J,I)*A(K,I)&lt;br /&gt;
                        END DO&lt;br /&gt;
                     END DO&lt;br /&gt;
                  ELSE&lt;br /&gt;
                     SX(I)=1.&lt;br /&gt;
                  END IF&lt;br /&gt;
               END IF               &lt;br /&gt;
&lt;br /&gt;
		&lt;br /&gt;
	END DO&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Здесь симметричная трёхдиагональная матрица хранится в диагонали и нижней кодиагонали массива A, вектора v - в поддиагональной части массива, за исключением первых их элементов, для которых выделен массив SX.&lt;br /&gt;
&lt;br /&gt;
Обычно же в последовательных версиях коэффициенты модификаций столбцов вычисляются целиком через скалярные произведения после вычислений параметров матрицы отражения. При этом схема чуть проще. Удлиняется критический путь графа, но для последовательных реализаций это неважно.&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
К сожалению, в графе, как видно по рисунку, в наличии пучки рассылок, в них неизбежно часть дуг остаются длинными, что отрицательно влияет на локальность вычислений по пространству.  &lt;br /&gt;
&lt;br /&gt;
==== Локальность реализации алгоритма ====&lt;br /&gt;
===== Структура обращений в память и качественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qtq_1.png|thumb|center|700px|Рисунок 3. Метод Хаусхолдера (отражений) для приведения симметричных матриц к трёхдиагональному виду. Общий профиль обращений в память]]&lt;br /&gt;
&lt;br /&gt;
На рис. 3 представлен профиль обращений в память для реализации метода Хаусхолдера (отражений) для приведения симметричных матриц к трёхдиагональному виду. Данный профиль обладает явной итерационной структурой, при этом видно, что итерации очень похожи. Основное их отличие заключается в наборе используемых данных – на каждой следующей итерации несколько первых элементов отбрасываются их рассмотрения, то есть чем позже итерация, тем меньше данных в ней задействовано. Также можно отметить, что число обращений в каждой последующей итерации немного уменьшается. Для того чтобы проанализировать общий профиль, в таком случае чаще всего достаточно изучить одну итерацию. Рассмотрим самую первую из них более детально.&lt;br /&gt;
&lt;br /&gt;
На рис. 4 показан набор обращений в рамках первой итерации (выделен на рис. 3 зеленым). Его можно разбить на несколько фрагментов и рассмотреть их отдельно. Строение фрагментов 2-5 можно оценить по общему графику для итерации. Фрагмент 2 представляет собой набор последовательных переборов в обратном порядке с небольшим шагом, причем число элементов в переборе постепенно уменьшается. Такое строение характеризуется достаточно высокой пространственной локальностью, поскольку часто происходит обращений к близко расположенным данным, однако низкой временной, поскольку данные повторно не используются. То же самое верно и для фрагмента 4, основное отличие которого заключается в том, что переборы выполняются в прямом порядке. Отметим, что некоторое искривление данного фрагмента связано не со строением самого фрагмента, а с разным числом параллельно выполняющихся обращений (см., например, правую область фрагмента 6), чего не наблюдается при выполнении фрагмента 2.&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qtq_2.png|thumb|center|700px|Рисунок 4. Профиль обращений, одна итерация]]&lt;br /&gt;
&lt;br /&gt;
Далее, фрагменты  3 и 5 состоят из небольшого числа обращений к данным, расположенным далеко друг от друга. Такие фрагменты характеризуются низкой пространственной и временной локальностью.&lt;br /&gt;
&lt;br /&gt;
Оставшиеся фрагменты требуют более детального рассмотрения. Перейдем к изучению фрагмента 1, который целиком представлен на рис. 5. Здесь мы можем видеть несколько этапов, каждый из которых представляет собой последовательный перебор элементов с шагом по памяти 1, причем в некоторых случаях данные много раз используются повторно (особенно это заметно в правой нижней области рисунка, где выполняется множество обращений к одному и тому же элементу). При условии, что в данном фрагменте задействовано всего около 40 элементов, можно говорить, что данный набор обращений обладает высокой пространственной и временной локальностью. &lt;br /&gt;
&lt;br /&gt;
[[file:householder_qtq_3.png|thumb|center|500px|Рисунок 5. Профиль обращений, одна итерация, фрагмент 1]]&lt;br /&gt;
&lt;br /&gt;
Далее рассмотрим детально фрагмент 6 (рис. 6). Здесь можно увидеть, что повторные обращения к данным выполняются не подряд, а через некоторые промежутки, однако число задействованных элементов также очень невелико, так что и в этом случае локальность (и пространственная, и временная) будет высокой.&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qtq_4.png|thumb|center|500px|Рисунок 6. Профиль обращений, одна итерация, фрагмент 6]]&lt;br /&gt;
&lt;br /&gt;
В целом можно сказать, что рассматриваемая итерация обладает достаточно высокой пространственной и временной локальностью. Поскольку остальные итерации устроены подобным образом, данные вывод можно перенести и на общий профиль обращений.&lt;br /&gt;
&lt;br /&gt;
===== Количественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
Оценка выполняется на основе характеристики daps, которая оценивает число выполненных обращений (чтений и записей) в память в секунду. Данная характеристика является аналогом оценки flops применительно к работе с памятью и является в большей степени оценкой производительности взаимодействия с памятью, чем оценкой локальности. Однако она служит хорошим источником информации, в том числе для сравнения с результатами по следующей характеристике cvg.&lt;br /&gt;
&lt;br /&gt;
На рисунке 7 приведены значения daps для реализаций распространенных алгоритмов, отсортированные по возрастанию (чем больше daps, тем в общем случае выше производительность). Можно увидеть, что производительность работы с памятью в данном случае достаточно высока – значение daps лишь немногим уступает тесту Linpack, который обладает высокой эффективностью работы с памятью, и немного превосходит, например, реализацию метода Якоби или метода Гаусса.&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qtq_daps.png|thumb|center|700px|Рисунок 7. Сравнение значений оценки daps]]&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
==== Масштабируемость алгоритма ====&lt;br /&gt;
==== Масштабируемость реализации алгоритма ====&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
В сравнении с методом Гивенса, который имеет естественное двумерное блочное разбиение на основе точечного метода, метод Хаусхолдера из-за худших характеристик локальности (наличие пучков рассылок) и меньшего количества независимых обобщённых развёрток графа не так хорош для реализаций на системах с распределённой памятью, как для систем с общей памятью. Поэтому на массово параллельных системах с распределённой памятью следует применять метод Хаусхолдера (если уж именно его нужно реализовать) не в точечной версии, а в разрабатываемых исследователями блочных вариантах. Следует отметить, что эти варианты - не блочная нарезка описанного метода, а самостоятельные методы. Особенно их применение рекомендуется в случаях с большой разрежённостью матрицы.&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Большинство пакетов от LINPACKа и LAPACKa до SCALAPACKa используют для QR-разложения матриц именно метод Хаусхолдера, правда, в различных модификациях (обычно с использованием BLAS). Существует большая подборка исследовательских работ по блочным версиям.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Статьи в работе]]&lt;br /&gt;
[[Категория:Разложения матриц]]&lt;/div&gt;</summary>
		<author><name>Teplov</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D0%A5%D0%B0%D1%83%D1%81%D1%85%D0%BE%D0%BB%D0%B4%D0%B5%D1%80%D0%B0_(%D0%BE%D1%82%D1%80%D0%B0%D0%B6%D0%B5%D0%BD%D0%B8%D0%B9)_%D0%BF%D1%80%D0%B8%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D1%8F_%D0%BC%D0%B0%D1%82%D1%80%D0%B8%D1%86%D1%8B_%D0%BA_%D0%B4%D0%B2%D1%83%D1%85%D0%B4%D0%B8%D0%B0%D0%B3%D0%BE%D0%BD%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%B9_%D1%84%D0%BE%D1%80%D0%BC%D0%B5&amp;diff=21167</id>
		<title>Метод Хаусхолдера (отражений) приведения матрицы к двухдиагональной форме</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D0%A5%D0%B0%D1%83%D1%81%D1%85%D0%BE%D0%BB%D0%B4%D0%B5%D1%80%D0%B0_(%D0%BE%D1%82%D1%80%D0%B0%D0%B6%D0%B5%D0%BD%D0%B8%D0%B9)_%D0%BF%D1%80%D0%B8%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D1%8F_%D0%BC%D0%B0%D1%82%D1%80%D0%B8%D1%86%D1%8B_%D0%BA_%D0%B4%D0%B2%D1%83%D1%85%D0%B4%D0%B8%D0%B0%D0%B3%D0%BE%D0%BD%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%B9_%D1%84%D0%BE%D1%80%D0%BC%D0%B5&amp;diff=21167"/>
		<updated>2016-12-18T10:30:18Z</updated>

		<summary type="html">&lt;p&gt;Teplov: /* Масштабируемость реализации алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Приведение матрицы к двудиагональному виду методом Хаусхолдера (отражений)&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;\frac{8 n^3}{3}+O(n^2)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;2n^2+O(n)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;n(n + 2)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
Основные авторы описания: [[Участник:Frolov|А.В.Фролов]]&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Метод Хаусхолдера&#039;&#039;&#039; (в советской математической литературе чаще называется &#039;&#039;&#039;методом отражений&#039;&#039;&#039;) используется для приведения симметричных вещественных матриц к двухдиагональному виду, или, что то же самое, для разложения &amp;lt;math&amp;gt;A=QDU^T&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;Q, U&amp;lt;/math&amp;gt; - ортогональные, &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; — правая двухдиагональная матрица)&amp;lt;ref&amp;gt;В.В.Воеводин, Ю.А.Кузнецов. Матрицы и вычисления. М.: Наука, 1984.&amp;lt;/ref&amp;gt;. При этом матрицы &amp;lt;math&amp;gt;Q, U&amp;lt;/math&amp;gt; хранятся и используются не в своём явном виде, а в виде произведения матриц отражения&amp;lt;ref name=&amp;quot;VOLA&amp;quot;&amp;gt;Воеводин В.В. Вычислительные основы линейной алгебры. М.: Наука, 1977.&amp;lt;/ref&amp;gt;. Каждая из матриц отражения может быть определена одним вектором. Это позволяет в классическом исполнении метода отражений хранить результаты разложения на месте матрицы A с использованием двух одномерных дополнительных массивов. &lt;br /&gt;
&lt;br /&gt;
В данной статье рассматривается именно классическое исполнение, в котором не используются приёмы типа сдваивания при вычислениях скалярных произведений.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В методе Хаусхолдера для выполнения разложения матрицы в произведение двухдиагональной и двух ортогональных используются попеременные умножения слева и справа её текущих модификаций на матрицы Хаусхолдера (отражений). &lt;br /&gt;
&lt;br /&gt;
{{Шаблон:Матрица отражений}}&lt;br /&gt;
&lt;br /&gt;
На &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м шаге метода с помощью преобразования отражения &amp;quot;убираются&amp;quot; ненулевые поддиагональные элементы в &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м столбце, а потом (кроме шага с номером &amp;lt;math&amp;gt;i=n-1&amp;lt;/math&amp;gt;) с помощью преобразования отражения &amp;quot;убираются&amp;quot; ненулевые наддиагональные элементы в &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м столбце, кроме самого левого из них. Таким образом, после &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; шагов преобразований получается правая двудиагональная &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; из разложения матрицы в произведение двудиагональной и двух ортогональных.&lt;br /&gt;
&lt;br /&gt;
На каждом из шагов метода матрицы отражений обычно представляют не в стандартном виде, а в виде &amp;lt;math&amp;gt;U=E-\frac{1}{\gamma}vv^*&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; находится для левых матриц отражения через координаты текущего &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; - вектор размерности &amp;lt;math&amp;gt;n+1-i&amp;lt;/math&amp;gt;, составленный из элементов &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца, начиная с &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;(s,s)=0&amp;lt;/math&amp;gt;, то &amp;lt;math&amp;gt;v=e_{i}&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\gamma = \frac{1}{2}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В остальных случаях по алгоритму вычисляется &amp;lt;math&amp;gt;u = \frac{1}{\sqrt{(s,s)}}s&amp;lt;/math&amp;gt;, и далее &amp;lt;math&amp;gt;v_{j}=0&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;j&amp;lt;i&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;v_{j}=u_{j-i+1}&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;j&amp;gt;i&amp;lt;/math&amp;gt;, а &amp;lt;math&amp;gt;v_{i}=1&amp;lt;/math&amp;gt;, если &amp;lt;math&amp;gt;u_{1}=0&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v_{i}=\frac{u_{1}}{|u_{1}|}(1+|u_{1}|)&amp;lt;/math&amp;gt; для остальных значений. При этом &amp;lt;math&amp;gt;\gamma = 1+|u_{1}|=|v_{i}|&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
После вычисления вектора &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; подстолбцы справа от ведущего модифицируются по формулам &amp;lt;math&amp;gt;x&#039;=x-\frac{(x,v)}{\gamma}v&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Аналогично для правых матриц отражений &amp;lt;math&amp;gt;U=E-\frac{1}{\gamma}vv^*&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; находится через координаты текущей &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-й строки так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; - вектор размерности &amp;lt;math&amp;gt;n-i&amp;lt;/math&amp;gt;, составленный из элементов &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-й строки, начиная с &amp;lt;math&amp;gt;i+1&amp;lt;/math&amp;gt;-го элемента.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;(s,s)=0&amp;lt;/math&amp;gt;, то &amp;lt;math&amp;gt;v=e_{i+1}&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\gamma = \frac{1}{2}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В остальных случаях по алгоритму вычисляется &amp;lt;math&amp;gt;u = \frac{1}{\sqrt{(s,s)}}s&amp;lt;/math&amp;gt;, и далее &amp;lt;math&amp;gt;v_{j}=0&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;j&amp;lt;i+1&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;v_{j}=u_{j-i+2}&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;j&amp;gt;i+1&amp;lt;/math&amp;gt;, а &amp;lt;math&amp;gt;v_{i+1}=1&amp;lt;/math&amp;gt;, если &amp;lt;math&amp;gt;u_{1}=0&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v_{i+1}=\frac{u_{1}}{|u_{1}|}(1+|u_{1}|)&amp;lt;/math&amp;gt; для остальных значений. При этом &amp;lt;math&amp;gt;\gamma = 1+|u_{1}|=|v_{i+1}|&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Основную часть алгоритма составляют вычисления на каждом шагу скалярных произведений &amp;lt;math&amp;gt;(s,s)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(x,v)&amp;lt;/math&amp;gt; для всех подстолбцов &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; справа от текущего, а также векторные операции &amp;lt;math&amp;gt;x&#039;=x-\frac{(x,v)}{\gamma}v&amp;lt;/math&amp;gt;, и затем вычисления на каждом шагу (кроме шага с номером &amp;lt;math&amp;gt;i=n-1&amp;lt;/math&amp;gt;) скалярных произведений &amp;lt;math&amp;gt;(s,s)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(x,v)&amp;lt;/math&amp;gt; для всех подстрок &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; снизу от текущей, а также векторные операции &amp;lt;math&amp;gt;x&#039;=x-\frac{(x,v)}{\gamma}v&amp;lt;/math&amp;gt;. Это используется при программировании метода во многих библиотеках для его конструирования из стандартных подпрограмм (например, из BLAS).&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Всё приведение состоит из &amp;lt;math&amp;gt;2n-3&amp;lt;/math&amp;gt; полушагов. Каждый из них состоит из вычисления некоторой матрицы отражений (Хаусхолдера) и умножения на неё матрицы. Строгая последовательность выполнения этих частей полушагов не обязательна, в силу связи получаемых векторов &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; можно одновременно с &amp;lt;math&amp;gt;(s,s)&amp;lt;/math&amp;gt; вычислять и произведения &amp;lt;math&amp;gt;(x,s)&amp;lt;/math&amp;gt; с последующим выражением через них &amp;lt;math&amp;gt;(x,v)&amp;lt;/math&amp;gt;. Это позволяет почти вдвое уменьшать критический путь графа алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Последовательность выполнения алгоритма обычно записывается как последовательное &amp;quot;обнуление&amp;quot; поддиагональных элементов столбцов, начиная с 1-го столбца, заканчивая &amp;lt;math&amp;gt;(n-1)&amp;lt;/math&amp;gt;-м, попеременно с последовательным &amp;quot;обнулением&amp;quot; наддиагональных (кроме первой кодиагонали) элементов строк, начиная с 1й строки и заканчивая &amp;lt;math&amp;gt;(n-2)&amp;lt;/math&amp;gt;-й &lt;br /&gt;
&lt;br /&gt;
При этом в каждом &amp;quot;обнуляемом&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м столбце &amp;quot;обнуляются&amp;quot; сразу все его поддиагональные элементы одновременно, с &amp;lt;math&amp;gt;(i+1)&amp;lt;/math&amp;gt;-го до &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;-го. &lt;br /&gt;
&lt;br /&gt;
Каждое &amp;quot;обнуление&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца состоит из двух шагов: &lt;br /&gt;
а) вычисление параметров матрицы отражения &amp;lt;math&amp;gt;U_{2i-1}&amp;lt;/math&amp;gt; такой, чтобы при умножении на неё слева &amp;quot;обнулились&amp;quot; все поддиагональные его элементы;&lt;br /&gt;
б) умножение слева матрицы отражения &amp;lt;math&amp;gt;U_{2i-1}&amp;lt;/math&amp;gt; на текущую версию матрицы.&lt;br /&gt;
&lt;br /&gt;
В каждой &amp;quot;обнуляемой&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-й строке &amp;quot;обнуляются&amp;quot; сразу все его наддиагональные элементы одновременно, с &amp;lt;math&amp;gt;(i+2)&amp;lt;/math&amp;gt;-го до &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;-го. &lt;br /&gt;
&lt;br /&gt;
Каждое &amp;quot;обнуление&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-й строки состоит из двух шагов: &lt;br /&gt;
а) вычисление параметров матрицы отражения &amp;lt;math&amp;gt;U_{2i}&amp;lt;/math&amp;gt; такой, чтобы при умножении на неё справа &amp;quot;обнулились&amp;quot; все (кроме первого) наддиагональные её элементы;&lt;br /&gt;
б) умножение справа матрицы отражения &amp;lt;math&amp;gt;U_{2i}&amp;lt;/math&amp;gt; на текущую версию матрицы.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В последовательной версии основная сложность алгоритма определяется прежде всего вычислениями скалярных произведений векторов, а также модификаций векторов вида &amp;lt;math&amp;gt;x&#039;=x-\alpha v&amp;lt;/math&amp;gt;, причем над векторами убывающей по ходу алгоритма размерности. Они, если не учитывать возможную разреженность, составляют (в главном члене) по &amp;lt;math&amp;gt;4n^3/3&amp;lt;/math&amp;gt; операций действительного умножения и сложения/вычитания.&lt;br /&gt;
&lt;br /&gt;
При классификации по последовательной сложности, таким образом, метод Хаусхолдера относится к алгоритмам &#039;&#039;с кубической сложностью&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
[[File:HausLeft1.png|thumb|right|600px|Рисунок 1. Граф нечётного полушага (обнуление &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;го столбца) алгоритма. Квадратики - входные данные шага (берутся с предыдущего полушага), кружки - операции. Зелёным выделены операции типа a+bb, салатовым и голубым - типа a+bc, тёмно-синим - вычисление &amp;lt;math&amp;gt;\gamma , v_{1}&amp;lt;/math&amp;gt;, жёлтым - умножения (или деления). Обведённая группа операций повторяется независимо n-i раз. Результаты синего и жёлтых кружков - выходные для алгоритма.]]&lt;br /&gt;
&lt;br /&gt;
На рисунке 1 изображён граф нечётного (&amp;lt;math&amp;gt;(2i-1)&amp;lt;/math&amp;gt;го) полушага в привязке к элементам обрабатываемой матрицы. Граф чётного (&amp;lt;math&amp;gt;2i&amp;lt;/math&amp;gt;го) полушага почти аналогичен, только длина ветвей (нарисована слева) будет меньше на 1 (&amp;lt;math&amp;gt;n-i&amp;lt;/math&amp;gt;), и для привязки к элементам матрицы его надо отразить относительно диагонали матрицы. После этого отражения графы полушагов можно положить друг на друга слоями, при этом правые нижние углы должны быть друг над другом.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Для понимания ресурса параллелизма в разложении матрицы порядка &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; методом Хаусхолдера нужно рассмотреть критический путь графа. &lt;br /&gt;
&lt;br /&gt;
Как видно из описания разных вершин, вычисления при &amp;quot;обнулении&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца параметров отражения и скалярных произведений состоят из основной части - ветви длиной по &amp;lt;math&amp;gt;n-i&amp;lt;/math&amp;gt; умножений и сложений - и коррекции вычислений, которые составляют &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; операций. Вычисления при &amp;quot;обнулении&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-й строки параметров отражения и скалярных произведений состоят из основной части - ветви длиной по &amp;lt;math&amp;gt;n-i-1&amp;lt;/math&amp;gt; умножений и сложений - и коррекции вычислений, которые составляют &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; операций.&lt;br /&gt;
&lt;br /&gt;
Поэтому по грубой (без членов низших порядков) оценке критический путь метода Хаусхолдера будет идти через &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt; умножений и &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt; сложений/вычитаний. &lt;br /&gt;
 &lt;br /&gt;
Поэтому в параллельном варианте, как и в последовательном, основную долю требуемого для выполнения алгоритма времени будут определять операции вида &amp;lt;math&amp;gt;a+bc&amp;lt;/math&amp;gt;.  &lt;br /&gt;
&lt;br /&gt;
При классификации по высоте ЯПФ, таким образом, метод Хаусхолдера относится к алгоритмам &#039;&#039;с квадратичной сложностью&#039;&#039;. При классификации по ширине ЯПФ его сложность будет также &#039;&#039;квадратичной&#039;&#039; (без расширения ряда ярусов, связанных с векторными операциями сложения, пришлось бы увеличить вдвое длину критического пути; при таком расширении сложность по ширине ЯПФ станет &#039;&#039;линейной&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
Надо сказать, что здесь в оценках речь идёт именно о классическом способе реализации метода Хаусхолдера. Даже использование схем сдваивания или последовательно-параллельных для вычисления скалярных произведений уменьшает критический путь с квадратичного до &#039;&#039;степени 3/2&#039;&#039; или &#039;&#039;линейно-логарифмического&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: плотная квадратная матрица &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; (элементы &amp;lt;math&amp;gt;a_{ij}&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;n^2+2n&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Соотношение последовательной и параллельной сложности, как хорошо видно, является &#039;&#039;линейным&#039;&#039;, что даёт определённый стимул для распараллеливания. Однако у наискорейшей ЯПФ ширина &#039;&#039;квадратична&#039;&#039;, что указывает на дисбаланс между загруженностями устройств при попытке её реально запрограммировать. Поэтому более практично даже при хорошей (быстрой) вычислительной сети оставить количество устройств (например, узлов кластера) &#039;&#039;линейным&#039;&#039; по размеру матрицы, что удвоит критический путь реализуемой ЯПФ. &lt;br /&gt;
&lt;br /&gt;
При этом вычислительная мощность алгоритма, как отношение числа операций к суммарному объему входных и выходных данных, &#039;&#039;линейна&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Алгоритм в рамках выбранной версии полностью детерминирован.&lt;br /&gt;
&lt;br /&gt;
Вычислительная погрешность в методе отражений (Хаусхолдера) растет &#039;&#039;линейно&#039;&#039;, как и в методе Гивенса (вращений).&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В варианте с кратчайшим критическим путём графа алгоритма (с использованием зависимости между обнуляемым вектором и направляющим вектором отражения) метод Хаусхолдера (отражений) приведения квадратной вещественной матрицы к двудиагональной форме на Фортране 77 можно записать так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;fortran&amp;quot;&amp;gt;&lt;br /&gt;
	DO  I = 1, N-1&lt;br /&gt;
            DO K = I, N&lt;br /&gt;
              SX(K)=A(N,I)*A(N,K)&lt;br /&gt;
            END DO&lt;br /&gt;
            DO J = N-1, I, -1&lt;br /&gt;
               DO K = I, N&lt;br /&gt;
                  SX(K)=SX(K)+A(J,I)*A(J,K)&lt;br /&gt;
               END DO&lt;br /&gt;
            END DO&lt;br /&gt;
&lt;br /&gt;
               ALPHA = SQRT (SX(I))&lt;br /&gt;
               IF (A(I,I).NE.0.) THEN&lt;br /&gt;
                  BETA = 1./ALPHA&lt;br /&gt;
                  DO J = I+1, N&lt;br /&gt;
                    A(J,I)=A(J,I)*BETA&lt;br /&gt;
                  END DO&lt;br /&gt;
                  SX(I) = A(I,I)*BETA+SIGN(1.,A(I,I))                     &lt;br /&gt;
                  A(I,I)=ALPHA&lt;br /&gt;
                  G=1./ABS(SX(I)) ! 1/gamma&lt;br /&gt;
                  DO K = I+1, N&lt;br /&gt;
                     SX(K)=SX(K)*BETA*G+SIGN(A(I,K),SX(I))&lt;br /&gt;
                     A(I,K) = A(I,K)+SX(K)*SX(I)&lt;br /&gt;
                     DO J = I+1, N&lt;br /&gt;
                       A (J,K) = A(J,K)-A(J,I)*SX(K)&lt;br /&gt;
                     END DO&lt;br /&gt;
                  END DO&lt;br /&gt;
               ELSE&lt;br /&gt;
                  IF (ALPHA.NE.0.) THEN&lt;br /&gt;
                     BETA = 1./ALPHA&lt;br /&gt;
                     DO J = I+1, N&lt;br /&gt;
                       A(J,I)=A(J,I)*BETA&lt;br /&gt;
                     END DO&lt;br /&gt;
                     SX(I) = -1. &lt;br /&gt;
                     A(I,I)=ALPHA&lt;br /&gt;
                     G=1.! 1/gamma&lt;br /&gt;
                     DO K = I+1, N&lt;br /&gt;
                        SX(K)=SX(K)*BETA*G+SIGN(A(I,K),SX(I))&lt;br /&gt;
                        A(I,K) = A(I,K)+SX(K)*SX(I)&lt;br /&gt;
                        DO J = I+1, N&lt;br /&gt;
                          A (J,K) = A(J,K)-A(J,I)*SX(K)&lt;br /&gt;
                        END DO&lt;br /&gt;
                     END DO&lt;br /&gt;
                  ELSE&lt;br /&gt;
                     SX(I)=1&lt;br /&gt;
                     G=2&lt;br /&gt;
                     DO K = I+1, N&lt;br /&gt;
                        SX(K)=2&lt;br /&gt;
                        A(I,K) = A(I,K)-SX(K)&lt;br /&gt;
                     END DO&lt;br /&gt;
                  END IF&lt;br /&gt;
               END IF               &lt;br /&gt;
&lt;br /&gt;
              IF (I.LT.N-1) THEN&lt;br /&gt;
&lt;br /&gt;
            DO K = I, N&lt;br /&gt;
              SL(K)=A(I,N)*A(K,N)&lt;br /&gt;
            END DO&lt;br /&gt;
            DO J = N-1, I+1, -1&lt;br /&gt;
               DO K = I, N&lt;br /&gt;
                  SL(K)=SL(K)+A(I,J)*A(K,J)&lt;br /&gt;
               END DO&lt;br /&gt;
            END DO&lt;br /&gt;
&lt;br /&gt;
               ALPHA = SQRT (SL(I))&lt;br /&gt;
               IF (A(I,I+1).NE.0.) THEN&lt;br /&gt;
                  BETA = 1./ALPHA&lt;br /&gt;
                  DO J = I+2, N&lt;br /&gt;
                    A(I,J)=A(I,J)*BETA&lt;br /&gt;
                  END DO&lt;br /&gt;
                  SL(I) = A(I,I+1)*BETA+SIGN(1.,A(I,I+1))                     &lt;br /&gt;
                  A(I,I+1)=ALPHA&lt;br /&gt;
                  G=1./ABS(SL(I)) ! 1/gamma&lt;br /&gt;
                  DO K = I+1, N&lt;br /&gt;
                     SL(K)=SL(K)*BETA*G+SIGN(A(K,I+1),SL(I))&lt;br /&gt;
                     A(K,I+1) = A(K,I+1)+SL(K)*SX(I)&lt;br /&gt;
                     DO J = I+2, N&lt;br /&gt;
                       A (K,J) = A(K,J)-A(I,J)*SL(K)&lt;br /&gt;
                     END DO&lt;br /&gt;
                  END DO&lt;br /&gt;
               ELSE&lt;br /&gt;
                  IF (ALPHA.NE.0.) THEN&lt;br /&gt;
                     BETA = 1./ALPHA&lt;br /&gt;
                     DO J = I+2, N&lt;br /&gt;
                       A(I,J)=A(I,J)*BETA&lt;br /&gt;
                     END DO&lt;br /&gt;
                     SL(I) = -1. &lt;br /&gt;
                     A(I,I+1)=ALPHA&lt;br /&gt;
                     G=1. ! 1/gamma&lt;br /&gt;
                     DO K = I+1, N&lt;br /&gt;
                        SL(K)=SL(K)*BETA*G+SIGN(A(K,I+1),SL(I))&lt;br /&gt;
                        A(K,I+1) = A(K,I+1)+SL(K)*SL(I)&lt;br /&gt;
                        DO J = I+2, N&lt;br /&gt;
                          A (K,J) = A(K,J)-A(I,J)*SL(K)&lt;br /&gt;
                        END DO&lt;br /&gt;
                     END DO&lt;br /&gt;
                  ELSE&lt;br /&gt;
                     SL(I)=1&lt;br /&gt;
                     G=2.&lt;br /&gt;
                     DO K = I+1, N&lt;br /&gt;
                        SL(K)=2.&lt;br /&gt;
                        A(K,I+1) = A(K,I+1)-SL(K)&lt;br /&gt;
                     END DO&lt;br /&gt;
                  END IF&lt;br /&gt;
               END IF   &lt;br /&gt;
&lt;br /&gt;
              END IF&lt;br /&gt;
	END DO&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В этом варианте D расположена в диагонали и верхней кодиагонали массива A, направляющие вектора матриц отражений размещены в поддиагональных элементах соответствующих столбцов или надддиагональных - строк, а их первые элементы - в элементах массивов SX и SL.&lt;br /&gt;
&lt;br /&gt;
Обычно же в последовательных версиях коэффициенты модификаций столбцов вычисляются целиком через скалярные произведения после вычислений параметров матрицы отражения. При этом схема чуть проще. Удлиняется критический путь графа, но для последовательных реализаций это неважно.&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
К сожалению, в графе, как видно по рисунку, в наличии пучки рассылок, в них неизбежно часть дуг остаются длинными, что отрицательно влияет на локальность вычислений по пространству.  &lt;br /&gt;
&lt;br /&gt;
==== Локальность реализации алгоритма ====&lt;br /&gt;
===== Структура обращений в память и качественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qdu_1.png|thumb|center|700px|Рисунок 2. Метод Хаусхолдера (отражений) приведения матрицы к двухдиагональной форме. Общий профиль обращений в память]]&lt;br /&gt;
&lt;br /&gt;
На рис. 2 представлен профиль обращений в память для реализации метода Хаусхолдера (отражений) приведения матрицы к двухдиагональной форме. Данный профиль обладает явной итерационной структурой, причем видно, что на каждой следующей итерации из рассмотрения отбрасывается небольшая часть элементов из начала массива данных. Также можно заметить, что основная часть данных используется достаточно равномерно в рамках одной итерации, однако в нижней и верхней частях итерации видны небольшие блоки с очень высокой локальностью обращений. Итерации явно устроены по одному принципу, поэтому стоит рассмотреть одну из них более детально.&lt;br /&gt;
&lt;br /&gt;
На рис. 3 показаны обращения в рамках первой итерации общего профиля (выделена на рис. 2 зеленым). Эти обращения можно условно разбить на несколько фрагментов. Фрагменты 1, 2 и 6 практически идентичны (различие заключается только в перестановке местами двух частей этих фрагментов), рассмотрим один из них.&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qdu_2.png|thumb|center|700px|Рисунок 3. Профиль обращений, одна итерация]]&lt;br /&gt;
&lt;br /&gt;
На рис. 4 показан отдельно фрагмент 6, выделенный на рис. 3. Теперь можно определить структуру каждой из двух частей более точно. Первая часть представляет собой последовательный перебор элементов в обратном порядке, причем к каждому элементу выполняется несколько обращений подряд. Во второй части также наблюдается последовательный перебор, однако с другими свойствами: элементы перебираются в прямом порядке, к каждому элементу выполняется только 1 обращение, однако данный перебор повторяется несколько раз подряд, причем каждый раз задействованы одни и те же элементы. Все это позволяет говорить о высокой пространственной локальности фрагмента (поскольку шаг по памяти каждый раз небольшой) и достаточно высокой временной локальности (поскольку данные часто используются повторно).&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qdu_3.png|thumb|center|500px|Рисунок 4. Профиль обращений, одна итерация, фрагмент 6]]&lt;br /&gt;
&lt;br /&gt;
Свойства локальности во фрагментах 3 и 5 можно оценить и без более детального рассмотрения. Данные в обоих фрагментах перебираются с достаточно большим шагом по памяти, при этом повторно они почти не используются, что говорит о низкой локальности в обоих случаях (хотя надо отметить, что локальность фрагмента 5 немного выше из-за более высокой пространственной локальности).&lt;br /&gt;
&lt;br /&gt;
Теперь перейдем к рассмотрению фрагмента 4, расположенного в центре рис. 3. Поскольку он обладает регулярной структурой, достаточно более детально изучить небольшой фрагмент (представлен на рис. 5, выделен на рис. 3 зеленым). Здесь можно увидеть, что первая часть фрагмента представляет собой практически стандартный последовательный перебор (с небольшими нечастыми сдвигами, которые не влияют на локальность). Такой набор обращений обладает высокой пространственной и низкой временной локальностью. Вторая часть данного фрагмента устроена несколько сложнее – она состоит из L-образных наборов обращений. Одна его область (часть 1 на рис. 5) по строению очень похожа на первую часть; вторая (часть 2 на рис. 5) представляет собой набор обращений к одному и тому же элементу, что достаточно сильно повышает локальность данного фрагмента. Из этого можно сделать вывод, что фрагмент 4 обладает высокой пространственной и средней временной локальностью.&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qdu_4.png|thumb|center|500px|Рисунок 5. Профиль обращений, одна итерация, часть фрагмент 4]]&lt;br /&gt;
&lt;br /&gt;
Суммируя выше сказанное, можно сказать, что фрагменты 1, 2 и 6 обладают высокой локальностью; фрагменты 3 и 5 – низкой локальностью; фрагмент 4 – высокой пространственной и средней временной локальностью. Можно предположить, что в итоге локальность общего профиля будет не очень высокой, в основном из-за более низкой временной локальности. Однако такое достаточно сложное строение общего профиля затрудняет оценку локальности общего профиля на основе визуального анализа.&lt;br /&gt;
&lt;br /&gt;
===== Количественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
Оценка выполняется на основе характеристики daps, которая оценивает число выполненных обращений (чтений и записей) в память в секунду. Данная характеристика является аналогом оценки flops применительно к работе с памятью и является в большей степени оценкой производительности взаимодействия с памятью, чем оценкой локальности. Однако она служит хорошим источником информации, в том числе для сравнения с результатами по следующей характеристике cvg.&lt;br /&gt;
&lt;br /&gt;
На рисунке 6 приведены значения daps для реализаций распространенных алгоритмов, отсортированные по возрастанию (чем больше daps, тем в общем случае выше производительность). Можно увидеть, что оценка производительности в данном случае не очень высока, что подтверждает сделанные ранее выводы. В частности, значение daps для этого примера показывает более низкую производительность по сравнению с реализациями других методов Хаусхолдера (householder_qr householder_qtq) и находится примерно на уровне оценки для реализации метода Холецкого.&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qdu_daps.png|thumb|center|700px|Рисунок 6. Сравнение значений оценки daps]]&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
==== Масштабируемость алгоритма ====&lt;br /&gt;
==== Масштабируемость реализации алгоритма ====&lt;br /&gt;
Проведём исследование масштабируемости вширь реализации согласно [[Scalability methodology|методике]]. Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов-2&amp;quot; [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Набор и границы значений изменяемых [[Глоссарий#Параметры запуска|параметров запуска]] реализации алгоритма: &lt;br /&gt;
&lt;br /&gt;
*	число процессоров [16:256] с шагом n^2;&lt;br /&gt;
*	размер матрицы [1000 : 32000] с шагом 1000.&lt;br /&gt;
&lt;br /&gt;
В результате проведённых экспериментов был получен следующий диапазон [[Глоссарий#Эффективность реализации|эффективности реализации]] алгоритма:&lt;br /&gt;
&lt;br /&gt;
*	минимальная эффективность реализации 7.511e-06%;&lt;br /&gt;
*	максимальная эффективность реализации 0.0746%.&lt;br /&gt;
&lt;br /&gt;
На следующих рисунках приведены графики [[Глоссарий#Производительность|производительности]] и эффективности выбранной реализации алгоритма в процедуре PDGEHRD из Scalapack в зависимости от изменяемых параметров запуска.&lt;br /&gt;
&lt;br /&gt;
[[file:PDGEHRD PERF.png|thumb|center|700px|Рисунок 8. Параллельная реализация метода Хаусхолдера для приведения матриц к двухдиагональному виду. Изменение производительности в зависимости от числа процессоров и размера матрицы.]]&lt;br /&gt;
[[file:PDGEHRD EFF.png|thumb|center|700px|Рисунок 9. Параллельная реализация метода Хаусхолдера для приведения матриц к двухдиагональному виду. Изменение эффективности в зависимости от числа процессоров и размера матрицы.]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
В сравнении с методом Гивенса, который имеет естественное двумерное блочное разбиение на основе точечного метода, метод Хаусхолдера из-за худших характеристик локальности (наличие пучков рассылок) и меньшего количества независимых обобщённых развёрток графа не так хорош для реализаций на системах с распределённой памятью, как для систем с общей памятью. Поэтому на массово параллельных системах с распределённой памятью следует применять метод Хаусхолдера (если уж именно его нужно реализовать) не в точечной версии, а в разрабатываемых исследователями блочных вариантах. Следует отметить, что эти варианты - не блочная нарезка описанного метода, а самостоятельные методы. Особенно их применение рекомендуется в случаях с большой разрежённостью матрицы.&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Большинство пакетов от LINPACKа и LAPACKa до SCALAPACKa используют для приведения матриц к двудиагональному виду именно метод Хаусхолдера, правда, в различных модификациях (обычно с использованием BLAS). Существует большая подборка исследовательских работ по блочным версиям.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Статьи в работе]]&lt;br /&gt;
[[Категория:Разложения матриц]]&lt;/div&gt;</summary>
		<author><name>Teplov</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%98%D1%82%D0%B5%D1%80%D0%B0%D1%86%D0%B8%D1%8F_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0_dqds&amp;diff=21166</id>
		<title>Итерация алгоритма dqds</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%98%D1%82%D0%B5%D1%80%D0%B0%D1%86%D0%B8%D1%8F_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0_dqds&amp;diff=21166"/>
		<updated>2016-12-18T10:26:18Z</updated>

		<summary type="html">&lt;p&gt;Teplov: /* Масштабируемость алгоритма и его реализации */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Алгоритм dqds нахождения&amp;lt;br /&amp;gt; сингулярных чисел двухдиагональной матрицы&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;5n-4&amp;lt;/math&amp;gt;   &lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;4n-3&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;2&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;2n&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;2n&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Основные авторы описания: [[Участник:Chernyavskiy|А.Ю.Чернявский]]&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритмов ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Итерация алгоритма dqds&#039;&#039;&#039; является одним шагом алгоритма dqds нахождения сингулярных чисел двухдиагональной матрицы.&lt;br /&gt;
&lt;br /&gt;
Сам алгоритм &#039;&#039;&#039;dqds&#039;&#039;&#039; (&#039;&#039;differential quotient-difference algorithm with shifts&#039;&#039;)&amp;lt;ref name=&amp;quot;vla&amp;quot;&amp;gt;Деммель Д. Вычислительная линейная алгебра. – М : Мир, 2001.&amp;lt;/ref&amp;gt;&amp;lt;ref name=&amp;quot;hola&amp;quot;&amp;gt;Hogben L. (ed.). Handbook of linear algebra. – CRC Press, 2006.&amp;lt;/ref&amp;gt; строит последовательность двухдиагональных матриц, сходящуюся к диагональной матрице, содержащей квадраты искомых сингулярных чисел. Его особенностью является высокая точность решения задачи. &lt;br /&gt;
Вычислительным ядром алгоритма является именно внутренняя итерация, вне итераций происходит подбор сдвига &amp;lt;math&amp;gt;\delta&amp;lt;/math&amp;gt; (параметер итерации, см. [[#Математическое описание алгоритма|математическое описание алгоритма]]), отслеживание сходимости, а также применение различных оптимизационных &amp;quot;хитростей&amp;quot;. Отметим, что внеитерационная часть алгоритма не существенна с точки зрения структуры вычислений, т.к. основные затраты ложатся на вычисления внутри итерации. Подробности и варианты внеитерационной части, а также анализ сходимости можно найти в соответствующей литературе&lt;br /&gt;
&amp;lt;ref&amp;gt;Fernando K. V., Parlett B. N. Accurate singular values and differential qd algorithms //Numerische Mathematik. – 1994. – Т. 67. – №. 2. – С. 191-229.&amp;lt;/ref&amp;gt;&lt;br /&gt;
&amp;lt;ref&amp;gt;Parlett B. N., Marques O. A. An implementation of the dqds algorithm (positive case) //Linear Algebra and its Applications. – 2000. – Т. 309. – №. 1. – С. 217-259.&amp;lt;/ref&amp;gt; &lt;br /&gt;
&amp;lt;ref&amp;gt;Aishima K. et al. On convergence of the DQDS algorithm for singular value computation //SIAM Journal on Matrix Analysis and Applications. – 2008. – Т. 30. – №. 2. – С. 522-537.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
==== Вспомогательные сведения ====&lt;br /&gt;
Для понимания математических основ dqds-итерации полезно рассмотреть кратко её вывод, частично отражающий и историю возникновения алгоритма (подробности можно найти в &amp;lt;ref name=&amp;quot;vla&amp;quot;/&amp;gt;).&lt;br /&gt;
За основу dqds-алгоритма удобно взять так называемую LR-итерацию, предшествующую хорошо-известной QR-итерации. LR-алгоритм, начиная с входной матрицы &amp;lt;math&amp;gt;T_0&amp;gt;0,&amp;lt;/math&amp;gt; строит сходящуюся последовательность подобных &amp;lt;math&amp;gt;T_0&amp;lt;/math&amp;gt; матриц &amp;lt;math&amp;gt;T_i&amp;gt;0,&amp;lt;/math&amp;gt; итерационно используя следующие три шага:&lt;br /&gt;
#Выбрать сдвиг &amp;lt;math&amp;gt;\tau_i&amp;lt;/math&amp;gt; меньший младшего собственного значения &amp;lt;math&amp;gt;T_i.&amp;lt;/math&amp;gt;&lt;br /&gt;
#Вычислить разложение Холецкого &amp;lt;math&amp;gt;T_i-\tau^2_iI=B_i^TB_i,&amp;lt;/math&amp;gt; где &amp;lt;math&amp;gt;B_i&amp;lt;/math&amp;gt; - верхняя треугольная матрица с положительной диагональю.&lt;br /&gt;
#&amp;lt;math&amp;gt;T_{i+1}=B_iB_i^T+\tau_i^2I.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Отметим, что два шага LR-итерации с нулевым сдвигом эквивалентны одному шагу QR-итерации. Итерационная процедура приводит матрицу к диагональному виду, тем самым вычисляя собственные значения исходной матрицы. LR-алгоритм достаточно легко может быть переформулирован с заметными упрощениями для задачи поиска сингулярных значений двухдиагональных матриц. А именно, будем вычислять последовательность двухдиагональных матриц &amp;lt;math&amp;gt;B_i&amp;lt;/math&amp;gt; без непосредственного вычисления &amp;lt;math&amp;gt;T_i&amp;lt;/math&amp;gt;(которые в данном случае будут трехдиагональными). Пусть матрица &amp;lt;math&amp;gt;B_i&amp;lt;/math&amp;gt; имеет диагональные элементы &amp;lt;math&amp;gt;a_1 \ldots a_n&amp;lt;/math&amp;gt; и наддиагональные элементы &amp;lt;math&amp;gt;b_1 \ldots b_{n-1}&amp;lt;/math&amp;gt;, а матрица &amp;lt;math&amp;gt;B_{i+1}&amp;lt;/math&amp;gt; - диагональные элементы &amp;lt;math&amp;gt;\widehat{a}_1 \ldots \widehat{a}_n&amp;lt;/math&amp;gt; и наддиагональные элементы &amp;lt;math&amp;gt;\widehat{b}_1 \ldots \widehat{b}_{n-1}.&amp;lt;/math&amp;gt; Тогда шаг LR-итерации в терминах матриц &amp;lt;math&amp;gt;B_i&amp;lt;/math&amp;gt; можно привести к простому циклу, пробегающему значения &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; от &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; до &amp;lt;math&amp;gt;n-1:&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
\widehat{a}^2_j = a^2_j+b^2_j-\widehat{b}^2_{j-1}-\delta&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
\widehat{b}^2_j = b^2_j\dot (a^2_{j+1}/a^2_j)&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
и вычислению &amp;lt;math&amp;gt;\widehat{a}^2_n = a^2_n-\widehat{b}^2_{n-1}-\delta.&amp;lt;/math&amp;gt; Очевидно, что работу с извлечением квадратов выгодно вести лишь после окончания работы алгоритма, поэтому можно ввести замену  &amp;lt;math&amp;gt;q_j=a^2_j,\; e_j=b^2,&amp;lt;/math&amp;gt; что в итоге приводит к так называемому алгоритму qds.&lt;br /&gt;
Формулы алгоритма следующие:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
\widehat{q}_j = q_j + e_j - \widehat{e}_{j-1} - \delta, \quad j \in [1,n-1]&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
\widehat{e}_j = e_j \cdot q_{j+1} / \widehat{q}_j, \quad j \in [1,n-1]&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
\widehat{q}_n = q_n - \widehat{e}_{n-1} - \delta. &lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Здесь &amp;lt;math&amp;gt;q_j, \; j \in [1,n]&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;e_j, \; j \in [1,n-1]&amp;lt;/math&amp;gt; - квадраты элемнтов главной и верхней побочной диагонали соответственно. Крышка означает выходные переменные, а &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\delta&amp;lt;/math&amp;gt; - сдвиг (параметр алгоритма).&lt;br /&gt;
Такая математическая запись наиболее компактна и соответствует так называемой qds-итерации.&lt;br /&gt;
&lt;br /&gt;
==== Математическое описание итерации алгоритма dqds====&lt;br /&gt;
Представим теперь математическую запись, приближенную к dqds-итерации (с математической точки зрения qds и dqds-итерации эквивалентны) с введенными вспомогательными переменными &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;t_j&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;d_j.&amp;lt;/math&amp;gt; Итерация алгоритма dqds преобразует входную двухдиагональную матрицу &amp;lt;math&amp;gt;B&amp;lt;/math&amp;gt; в выходную &amp;lt;math&amp;gt;\widehat{B}.&amp;lt;/math&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
Входные и выходные данные: &amp;lt;math&amp;gt;q_{j}, \; j\in [1,n], \; e_{k}, \; k\in [1,n-1] &amp;lt;/math&amp;gt; - квадраты элементов главной и побочной диагонали входной матрицы &amp;lt;math&amp;gt;B&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;  \widehat{q}_j , \; \widehat{e}_k &amp;lt;/math&amp;gt; - то же для вычисляемой матрицы &amp;lt;math&amp;gt;\widehat{B}.&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Формулы метода выглядят следующим образом:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
d_1 = q_1 - \delta, \; q_n = d_n &lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
:для&amp;lt;math&amp;gt;&lt;br /&gt;
\quad j\in [1,n-1]: &lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
\widehat{q}_j = d_j + e_j&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
t_j = q_{j+1}/\widehat{q}_j&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
\widehat{e}_j=e_j \cdot t_j &lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
d_{j+1} = d \cdot t - \delta&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Вычислительным ядром алгоритма является последовательный расчёт квадратов диагональных (&amp;lt;math&amp;gt;\widehat{q}_j&amp;lt;/math&amp;gt;) и внедиагональных (&amp;lt;math&amp;gt;\widehat{e}_k&amp;lt;/math&amp;gt;) элементов выходной матрицы. Учитывая использование вспомогательных переменных расчёт каждой новой пары содержит по одной операции сложения, вычитания и деления, а также две операции умножения.&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм состоит из отдельного вычисления начального значения вспомогательной переменной &amp;lt;math&amp;gt;d,&amp;lt;/math&amp;gt; последующим (n-1)-кратным выполнением повторяющейся последовательности из 5 операций (+,/,*,*,-) для вычисления квадратов диагональных (&amp;lt;math&amp;gt;\widehat{q}_j&amp;lt;/math&amp;gt;) и внедиагональных (&amp;lt;math&amp;gt;\widehat{e}_k&amp;lt;/math&amp;gt;) элементов выходной матрицы и завершающего вычислением крайнего значения &amp;lt;math&amp;gt;\widehat{q}_n&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Отметим, что выходные данные сразу могут быть записаны на место входных (это учтено в схеме), также для хранения вспомогательных переменных &amp;lt;math&amp;gt;t_j&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;d_j&amp;lt;/math&amp;gt; достаточно двух перезаписываемых переменных. Таким образом элементы главной (&amp;lt;math&amp;gt;q_j&amp;lt;/math&amp;gt;) и побочной (&amp;lt;math&amp;gt;e_k&amp;lt;/math&amp;gt;) диагонали входной матрицы последовательно перезаписываются соответствующими элементами выходной матрицы.&lt;br /&gt;
&lt;br /&gt;
Последовательность исполнения метода следующая:&lt;br /&gt;
&lt;br /&gt;
1. Вычисляется начальное значение вспомогательной переменной &amp;lt;math&amp;gt;d = q_1-\delta.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2. Производится цикл по j от 1 до n-1, состоящий из:&lt;br /&gt;
&lt;br /&gt;
:2.1 Вычисляется значение &amp;lt;math&amp;gt;q_j = d + e_j;&amp;lt;/math&amp;gt;&lt;br /&gt;
:2.2 Вычисляется значение вспомогательной переменной &amp;lt;math&amp;gt;t = q_{j+1}/q_j;&amp;lt;/math&amp;gt;&lt;br /&gt;
:2.3 Вычисляется значение &amp;lt;math&amp;gt;e_j = e_j \cdot t;&amp;lt;/math&amp;gt;&lt;br /&gt;
:2.4 Вычисляется значение вспомогательной переменной &amp;lt;math&amp;gt;d = d \cdot t - \delta.&amp;lt;/math&amp;gt;&lt;br /&gt;
:&lt;br /&gt;
&lt;br /&gt;
3. Вычисляется &amp;lt;math&amp;gt;q_n = d.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Легко заметить, что можно представить вычисления в другой форме, например, в виде qds-итерации (см. [[Итерация алгоритма dqds#Математическое описание алгоритма|Математическое описание dqds-итерации]]), однако, именно dqds реализация вычисления позволяет достичь высокой точности&amp;lt;ref name=&amp;quot;vla&amp;quot;&amp;gt;&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Для выполнения одной итерации dqds необходимо выполнить:&lt;br /&gt;
	&lt;br /&gt;
* &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; делений,&lt;br /&gt;
* &amp;lt;math&amp;gt;2n-2&amp;lt;/math&amp;gt; умножений,&lt;br /&gt;
* &amp;lt;math&amp;gt;2n-1&amp;lt;/math&amp;gt; сложений/вычитаний.&lt;br /&gt;
&lt;br /&gt;
Таким образом одна dqds-итерация имеет &#039;&#039;линейную сложность&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
[[file:dqds.png|thumb|center|600px|Рисунок 1. Граф алгоритма для n=4 без отображения входных и выходных данных.]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Как видно из информационного графа алгоритма, на каждом шаге основного цикла возможно лишь параллельное выполнение операции умножения (2.2) и умножения+сложения (2.4). Это позволяет сократить число ярусов на одной итерации цикла c 5 до 4, а общее число ярусов алгоритма с 5n-4 до 4n-3. Ярусы с операциями умножения состоят из двух операций, остальные же из одной.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: Квадраты элементов основной и верхней побочной диагонали двухдиагональной матрицы (вектора &amp;lt;math&amp;gt;q&amp;lt;/math&amp;gt; длины n и &amp;lt;math&amp;gt;e&amp;lt;/math&amp;gt; длины n-1), а также параметр сдвига &amp;lt;math&amp;gt;\delta&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;2n&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: Квадраты элементов основной и верхней побочной диагонали выходной двухдиагональной матрицы.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;2n-1&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма===&lt;br /&gt;
&lt;br /&gt;
Соотношение последовательной и параллельной сложности при наличии возможности параллельного выполнения операций умножения составляет &amp;lt;math&amp;gt;\frac{5n-4}{4n-3}&amp;lt;/math&amp;gt;, т.е. алгоритм плохо распараллеливается. &lt;br /&gt;
&lt;br /&gt;
Вычислительная мощность алгоритма, как отношение числа операций к суммарному объему входных и выходных данных - константа.&lt;br /&gt;
&lt;br /&gt;
Описываемый алгоритм является полностью детерминированным. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритмов ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм на языке Matlab может быть записать так:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;matlab&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
d = q(1)-delta;&lt;br /&gt;
for j = 1:n-1&lt;br /&gt;
    q(j)=d+e(j);&lt;br /&gt;
    t=q(j+1)/q(j);&lt;br /&gt;
    e(j) = e(j)*t;&lt;br /&gt;
    d = d*t-delta;&lt;br /&gt;
end&lt;br /&gt;
q(n) = d;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Как говорилось в [[#Схема реализации последовательного алгоритма|cхеме реализации последовательного алгоритма]], вычисляемые данные записываются сразу на место входных.&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
Легко видеть, что локальность данных высока. Её легко повысить, размещая рядом соответствующие элементы массивов e и q, однако, это не оказывает существенного влияния на производительность в силу константного количества операций относительно объема обрабатываемых данных.&lt;br /&gt;
&lt;br /&gt;
==== Локальность реализации алгоритма ====&lt;br /&gt;
&lt;br /&gt;
===== Структура обращений в память и качественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
[[file:dqds_1.png|thumb|center|700px|Рисунок 2. Итерация алгоритма dqds. Общий профиль обращений в память]]&lt;br /&gt;
&lt;br /&gt;
На рис. 2 представлен профиль обращений в память для реализации итерации алгоритма dqds. Данный профиль внешне устроен очень просто и состоит из двух параллельно выполняемых последовательных переборов. Отметим, что общее число обращений в память всего в 3 раза больше числа задействованных данных, что говорит о том, что данные повторно используют редко. Зачастую это сигнализирует о достаточно невысокой локальности.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим более детально строение одного из переборов (второй устроен практически идентично). На рис. 3 показан выделенный на рис. 2 небольшой фрагмент 1. Видно, что на каждом шаге в данном переборе выполняется три обращения в память; подобное строение говорит о высокой пространственной локальности, однако низкой временной, поскольку данные повторно используются только по 3 раза. &lt;br /&gt;
&lt;br /&gt;
Поскольку данная структура обращений и составляет общий профиль, то же самое можно говорить и о локальности всего профиля.&lt;br /&gt;
&lt;br /&gt;
[[file:dqds_2.png|thumb|center|700px|Рисунок 3. Профиль обращений, фрагмент 1]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===== Количественная оценка локальности =====&lt;br /&gt;
Оценка выполняется на основе характеристики daps, которая оценивает число выполненных обращений (чтений и записей) в память в секунду. Данная характеристика является аналогом оценки flops применительно к работе с памятью и является в большей степени оценкой производительности взаимодействия с памятью, чем оценкой локальности. Однако она служит хорошим источником информации, в том числе для сравнения с результатами по следующей характеристике cvg.&lt;br /&gt;
&lt;br /&gt;
На рисунке 4 приведены значения daps для реализаций распространенных алгоритмов, отсортированные по возрастанию (чем больше daps, тем в общем случае выше производительность). Результат получен достаточно неожиданный – производительность работы с памятью очень невелика. Оценка daps для данного профиля сравнима с реализациями самых неэффективных алгоритмов в части работы с памятью – тестов RandomAccess и неэффективных вариантов обычного перемножения матриц. Отчасти это может объясняться низкой временной локальностью. Однако в данном случае причина может также заключаться в том, что в данной реализации объем вычислений на одно обращение в память достаточно велик, что может приводить к недостаточной загруженности подсистемы памяти. В таком случае, несмотря на в целом неплохую эффективность работы с памятью, производительность будет достаточно низка.&lt;br /&gt;
&lt;br /&gt;
[[file:dqds_daps.png|thumb|center|700px|Рисунок 4. Сравнение значений оценки daps]]&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Итерация dqds практически полностью последовательна. Единственная возможность - одновременное выполнение операции умножения (2.3) и операции (2.4) умножения и сложения, что дает небольшой выигрыш в производительности.&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм не является масштабируемым, максимального эффекта ускорения можно добиться на двух независимых процессорах.&lt;br /&gt;
&lt;br /&gt;
Проведём исследование масштабируемости вширь реализации согласно [[Scalability methodology|методике]]. Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов-2&amp;quot; [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Набор и границы значений изменяемых [[Глоссарий#Параметры запуска|параметров запуска]] реализации алгоритма: &lt;br /&gt;
&lt;br /&gt;
*	число процессоров 1;&lt;br /&gt;
*	размер матрицы [1000 : 260000] с шагом 500.&lt;br /&gt;
&lt;br /&gt;
В результате проведённых экспериментов был получен следующий диапазон [[Глоссарий#Эффективность реализации|эффективности реализации]] алгоритма:&lt;br /&gt;
&lt;br /&gt;
*	минимальная эффективность реализации 2.559e-06%;&lt;br /&gt;
*	максимальная эффективность реализации 2.492e-08%.&lt;br /&gt;
&lt;br /&gt;
На следующих рисунках приведены графики [[Глоссарий#Производительность|производительности]] и эффективности выбранной реализации DQDS в зависимости от изменяемых параметров запуска.&lt;br /&gt;
&lt;br /&gt;
[[file:DQDS Perf.png|thumb|center|700px|Рисунок 8. Параллельная реализация алгоритма dqds. Изменение производительности в зависимости от числа процессоров и размера матрицы.]]&lt;br /&gt;
[[file:DQDS eff.png|thumb|center|700px|Рисунок 9. Параллельная реализация алгоритма dqds. Изменение эффективности в зависимости от числа процессоров и размера матрицы.]]&lt;br /&gt;
&lt;br /&gt;
[http://git.algowiki-project.org/Teplov/Scalability/tree/master/DQDS/dqds.c Исследованная параллельная реализация на языке C]&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
Эффективное выполнение алгоритма возможно только на вычислительных устройствах с одним или двумя ядрами. &lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
Сам алгоритм dqds реализован в функции xBDSQR пакета LAPACK и используется при её вызове без расчёта сингулярных векторов.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Статьи в работе]]&lt;/div&gt;</summary>
		<author><name>Teplov</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D0%A5%D0%B0%D1%83%D1%81%D1%85%D0%BE%D0%BB%D0%B4%D0%B5%D1%80%D0%B0_(%D0%BE%D1%82%D1%80%D0%B0%D0%B6%D0%B5%D0%BD%D0%B8%D0%B9)_QR-%D1%80%D0%B0%D0%B7%D0%BB%D0%BE%D0%B6%D0%B5%D0%BD%D0%B8%D1%8F_%D0%BA%D0%B2%D0%B0%D0%B4%D1%80%D0%B0%D1%82%D0%BD%D0%BE%D0%B9_%D0%BC%D0%B0%D1%82%D1%80%D0%B8%D1%86%D1%8B,_%D0%B2%D0%B5%D1%89%D0%B5%D1%81%D1%82%D0%B2%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D1%82%D0%BE%D1%87%D0%B5%D1%87%D0%BD%D1%8B%D0%B9_%D0%B2%D0%B0%D1%80%D0%B8%D0%B0%D0%BD%D1%82&amp;diff=21165</id>
		<title>Метод Хаусхолдера (отражений) QR-разложения квадратной матрицы, вещественный точечный вариант</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D0%A5%D0%B0%D1%83%D1%81%D1%85%D0%BE%D0%BB%D0%B4%D0%B5%D1%80%D0%B0_(%D0%BE%D1%82%D1%80%D0%B0%D0%B6%D0%B5%D0%BD%D0%B8%D0%B9)_QR-%D1%80%D0%B0%D0%B7%D0%BB%D0%BE%D0%B6%D0%B5%D0%BD%D0%B8%D1%8F_%D0%BA%D0%B2%D0%B0%D0%B4%D1%80%D0%B0%D1%82%D0%BD%D0%BE%D0%B9_%D0%BC%D0%B0%D1%82%D1%80%D0%B8%D1%86%D1%8B,_%D0%B2%D0%B5%D1%89%D0%B5%D1%81%D1%82%D0%B2%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D1%82%D0%BE%D1%87%D0%B5%D1%87%D0%BD%D1%8B%D0%B9_%D0%B2%D0%B0%D1%80%D0%B8%D0%B0%D0%BD%D1%82&amp;diff=21165"/>
		<updated>2016-12-18T10:18:40Z</updated>

		<summary type="html">&lt;p&gt;Teplov: /* Масштабируемость реализации алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = QR-разложение методом Хаусхолдера (отражений)&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;\frac{4 n^3}{3}&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;n(n + 1)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
Основные авторы описания: [[Участник:Frolov|А.В.Фролов]]&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Метод Хаусхолдера&#039;&#039;&#039; (в советской математической литературе чаще называется &#039;&#039;&#039;методом отражений&#039;&#039;&#039;) используется для разложения  матриц в виде &amp;lt;math&amp;gt;A=QR&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; - унитарная, &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; — правая треугольная матрица)&amp;lt;ref&amp;gt;В.В.Воеводин, Ю.А.Кузнецов. Матрицы и вычисления. М.: Наука, 1984.&amp;lt;/ref&amp;gt;. При этом матрица &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; хранится и используется не в своём явном виде, а в виде произведения матриц отражения&amp;lt;ref name=&amp;quot;VOLA&amp;quot;&amp;gt;Воеводин В.В. Вычислительные основы линейной алгебры. М.: Наука, 1977.&amp;lt;/ref&amp;gt;. Каждая из матриц отражения может быть определена одним вектором. Это позволяет в классическом исполнении метода отражений хранить результаты разложения на месте матрицы A с использованием минимального одномерного дополнительного массива. &lt;br /&gt;
&lt;br /&gt;
В данной статье рассматривается именно классическое исполнение, в котором не используются приёмы типа сдваивания при вычислениях скалярных произведений.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В методе Хаусхолдера для выполнения &amp;lt;math&amp;gt;QR&amp;lt;/math&amp;gt;-разложения матрицы используются умножения слева её текущих модификаций на матрицы Хаусхолдера (отражений). &lt;br /&gt;
&lt;br /&gt;
{{Шаблон:Матрица отражений}}&lt;br /&gt;
&lt;br /&gt;
На &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м шаге метода с помощью преобразования отражения &amp;quot;убираются&amp;quot; ненулевые поддиагональные элементы в &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м столбце. Таким образом, после &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; шагов преобразований получается матрица &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; из &amp;lt;math&amp;gt;QR&amp;lt;/math&amp;gt;-разложения.&lt;br /&gt;
&lt;br /&gt;
На каждом из шагов метода матрицу отражений обычно представляют не в стандартном виде, а в виде &amp;lt;math&amp;gt;U=E-\frac{1}{\gamma}vv^*&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; находится через координаты текущего &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; - вектор размерности &amp;lt;math&amp;gt;n+1-i&amp;lt;/math&amp;gt;, составленный из элементов &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца, начиная с &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;(s,s)=0&amp;lt;/math&amp;gt;, то &amp;lt;math&amp;gt;v=e_{i}&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\gamma = \frac{1}{2}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В остальных случаях по алгоритму вычисляется &amp;lt;math&amp;gt;u = \frac{1}{\sqrt{(s,s)}}s&amp;lt;/math&amp;gt;, и далее &amp;lt;math&amp;gt;v_{j}=0&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;j&amp;lt;i&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;v_{j}=u_{j-i+1}&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;j&amp;gt;i&amp;lt;/math&amp;gt;, а &amp;lt;math&amp;gt;v_{i}=1&amp;lt;/math&amp;gt;, если &amp;lt;math&amp;gt;u_{1}=0&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v_{i}=\frac{u_{1}}{|u_{1}|}(1+|u_{1}|)&amp;lt;/math&amp;gt; для остальных значений. При этом &amp;lt;math&amp;gt;\gamma =1+|u_{1}|=|v_{i}|&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
После вычисления вектора &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; подстолбцы справа от ведущего модифицируются по формулам &amp;lt;math&amp;gt;x&#039;=x-\frac{(x,v)}{\gamma}v&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Основную часть алгоритма составляют вычисления на каждом шагу скалярных произведений &amp;lt;math&amp;gt;(s,s)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(x,v)&amp;lt;/math&amp;gt; для всех подстолбцов &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt;справа от текущего, а также векторные операции &amp;lt;math&amp;gt;x&#039;=x-\frac{(x,v)}{\gamma}v&amp;lt;/math&amp;gt;. Это используется при программировании метода во многих библиотеках для его конструирования из стандартных подпрограмм (например, из BLAS).&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
[[File:HausLeft1.png|thumb|right|600px|Рисунок 1. Граф шага (обнуление &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;го столбца) алгоритма. Квадратики - входные данные шага (берутся с предыдущего или из входных данных), кружки - операции. Зелёным выделены операции типа a+bb, салатовым и голубым - типа a+bc, тёмно-синим - вычисление &amp;lt;math&amp;gt;\gamma , v_{1}&amp;lt;/math&amp;gt;, жёлтым - умножения (или деления). Обведённая группа операций повторяется независимо n-i раз. Результаты синего, красных и жёлтых кружков, а на последнем шаге и голубого - выходные для алгоритма.]]&lt;br /&gt;
&lt;br /&gt;
Как уже сказано в описании ядра, основная часть - вычисления на каждом шагу скалярных произведений &amp;lt;math&amp;gt;(s,s)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(x,v)&amp;lt;/math&amp;gt; для всех подстолбцов &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; справа от текущего, а также векторные операции &amp;lt;math&amp;gt;x&#039;=x-\frac{(x,v)}{\gamma}v&amp;lt;/math&amp;gt;. При этом, однако, строгая последовательность выполнения этих трёх подшагов не обязательна, в силу связи получаемых векторов &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; можно одновременно с &amp;lt;math&amp;gt;(s,s)&amp;lt;/math&amp;gt; вычислять и произведения &amp;lt;math&amp;gt;(x,s)&amp;lt;/math&amp;gt; с последующим выражением через них &amp;lt;math&amp;gt;(x,v)&amp;lt;/math&amp;gt;. Это позволяет почти вдвое уменьшать критический путь графа алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Последовательность выполнения алгоритма обычно записывается как последовательное &amp;quot;обнуление&amp;quot; поддиагональных элементов столбцов, начиная с 1-го столбца и заканчивая предпоследним &amp;lt;math&amp;gt;(n-1)&amp;lt;/math&amp;gt;-м.&lt;br /&gt;
&lt;br /&gt;
При этом в каждом &amp;quot;обнуляемом&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м столбце &amp;quot;обнуляются&amp;quot; сразу все его поддиагональные элементы одновременно, с &amp;lt;math&amp;gt;(i+1)&amp;lt;/math&amp;gt;-го до &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;-го. &lt;br /&gt;
&lt;br /&gt;
Каждое &amp;quot;обнуление&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца состоит из двух шагов: &lt;br /&gt;
а) вычисление параметров матрицы отражения &amp;lt;math&amp;gt;U_{i}&amp;lt;/math&amp;gt; такой, чтобы при умножении на неё слева &amp;quot;обнулились&amp;quot; все поддиагональные его элементы;&lt;br /&gt;
б) умножение слева матрицы отражения &amp;lt;math&amp;gt;U_{i}&amp;lt;/math&amp;gt; на текущую версию матрицы.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В последовательной версии основная сложность алгоритма определяется прежде всего вычислениями скалярных произведений векторов, а также модификаций векторов вида &amp;lt;math&amp;gt;x&#039;=x-\alpha v&amp;lt;/math&amp;gt;, причем над векторами убывающей по ходу алгоритма размерности. Они, если не учитывать возможную разреженность, составляют (в главном члене) по &amp;lt;math&amp;gt;2n^3/3&amp;lt;/math&amp;gt; операций действительного умножения и сложения/вычитания.&lt;br /&gt;
&lt;br /&gt;
При классификации по последовательной сложности, таким образом, метод Хаусхолдера относится к алгоритмам &#039;&#039;с кубической сложностью&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
На Рисунке 1 приведён шаг графа алгоритма метода Хаусхолдера в наиболее его быстром (с параллельной точки зрения) варианте, использующем то, что с точностью до множителя ведущий вектор матрицы отражения отличается отличается от подстолбца, где выполняется очередное исключение, только одним элементом. Операции привязаны к обрабатываемым элементам матрицы. Для получения полного графа графы шагов нужно положить друг на друга последовательными слоями, при этом правые нижние углы должны быть друг над другом.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Для понимания ресурса параллелизма в разложении матрицы порядка &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; методом Хаусхолдера нужно рассмотреть критический путь графа. &lt;br /&gt;
&lt;br /&gt;
Как видно из описания разных вершин, вычисления при &amp;quot;обнулении&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца параметров отражения и скалярных произведений состоят из основной части - ветви длиной по &amp;lt;math&amp;gt;n-i&amp;lt;/math&amp;gt; умножений и сложений - и коррекции вычислений, которые составляют &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; операций.&lt;br /&gt;
&lt;br /&gt;
Поэтому по грубой (без членов низших порядков) оценке критический путь метода Хаусхолдера будет идти через &amp;lt;math&amp;gt;\frac{n^2}{2}&amp;lt;/math&amp;gt; умножений и &amp;lt;math&amp;gt;\frac{n^2}{2}&amp;lt;/math&amp;gt; сложений/вычитаний. &lt;br /&gt;
 &lt;br /&gt;
Поэтому в параллельном варианте, как и в последовательном, основную долю требуемого для выполнения алгоритма времени будут определять операции вида &amp;lt;math&amp;gt;a+bc&amp;lt;/math&amp;gt;.  &lt;br /&gt;
&lt;br /&gt;
При классификации по высоте ЯПФ, таким образом, метод Хаусхолдера относится к алгоритмам &#039;&#039;с квадратичной сложностью&#039;&#039;. При классификации по ширине ЯПФ его сложность будет также &#039;&#039;квадратичной&#039;&#039; (без расширения ряда ярусов, связанных с векторными операциями сложения, пришлось бы увеличить вдвое длину критического пути; при таком расширении сложность по ширине ЯПФ станет &#039;&#039;линейной&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
Надо сказать, что здесь в оценках речь идёт именно о классическом способе реализации метода Хаусхолдера. Даже использование схем сдваивания или последовательно-параллельных для вычисления скалярных произведений уменьшает критический путь с квадратичного до &#039;&#039;степени 3/2&#039;&#039; или &#039;&#039;линейно-логарифмического&#039;&#039;. Однако все эти широко распространённые методы пока не дали возможности снизить критический путь метода Хаусхолдера до &#039;&#039;линейного&#039;&#039; (как, скажем, у метода Гивенса).&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: плотная квадратная матрица &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; (элементы &amp;lt;math&amp;gt;a_{ij}&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: правая треугольная матрица &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; (ненулевые элементы &amp;lt;math&amp;gt;r_{ij}&amp;lt;/math&amp;gt; в последовательном варианте хранятся в элементах исходной матрицы &amp;lt;math&amp;gt;a_{ij}&amp;lt;/math&amp;gt;), унитарная (ортогональная) матрица Q - как произведение матриц Хаусхолдера (отражения) (их вектора нормалей к плоскостям отражения в последовательном варианте хранятся в поддиагональных элементах исходной матрицы &amp;lt;math&amp;gt;a_{ij}&amp;lt;/math&amp;gt; и в одном дополнительном столбце размерности n).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;n^2+n&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Соотношение последовательной и параллельной сложности, как хорошо видно, является &#039;&#039;линейным&#039;&#039;, что даёт определённый стимул для распараллеливания. Однако у наискорейшей ЯПФ ширина &#039;&#039;квадратична&#039;&#039;, что указывает на дисбаланс между загруженностями устройств при попытке её реально запрограммировать. Поэтому более практично даже при хорошей (быстрой) вычислительной сети оставить количество устройств (например, узлов кластера) &#039;&#039;линейным&#039;&#039; по размеру матрицы, что удвоит критический путь реализуемой ЯПФ. &lt;br /&gt;
&lt;br /&gt;
При этом вычислительная мощность алгоритма, как отношение числа операций к суммарному объему входных и выходных данных, &#039;&#039;линейна&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Алгоритм в рамках выбранной версии полностью детерминирован.&lt;br /&gt;
&lt;br /&gt;
Вычислительная погрешность в методе отражений (Хаусхолдера) растет &#039;&#039;линейно&#039;&#039;, как и в методе Гивенса (вращений).&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В варианте с кратчайшим критическим путём графа алгоритма (с использованием зависимости между обнуляемым вектором и направляющим вектором отражения) метод Хаусхолдера (отражений) QR-разложения квадратной вещественной матрицы на Фортране 77 можно записать так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;fortran&amp;quot;&amp;gt;&lt;br /&gt;
	DO  I = 1, N-1&lt;br /&gt;
            DO K = I, N&lt;br /&gt;
              SX(K)=A(N,I)*A(N,K)&lt;br /&gt;
            END DO&lt;br /&gt;
            DO J = N-1, I, -1&lt;br /&gt;
               DO K = I, N&lt;br /&gt;
                  SX(K)=SX(K)+A(J,I)*A(J,K)&lt;br /&gt;
               END DO&lt;br /&gt;
            END DO&lt;br /&gt;
&lt;br /&gt;
               ALPHA = SQRT (SX(I))&lt;br /&gt;
               IF (A(I,I).NE.0.) THEN&lt;br /&gt;
                  BETA = 1./ALPHA&lt;br /&gt;
                  DO J = I+1, N&lt;br /&gt;
                    A(J,I)=A(J,I)*BETA&lt;br /&gt;
                  END DO&lt;br /&gt;
                  SX(I) = A(I,I)*BETA+SIGN(1.,A(I,I))                     &lt;br /&gt;
                  A(I,I)=ALPHA&lt;br /&gt;
                  G=1./ABS(SX(I)) ! 1/gamma&lt;br /&gt;
                  DO K = I+1, N&lt;br /&gt;
                     SX(K)=SX(K)*BETA*G+SIGN(A(I,K),SX(I))&lt;br /&gt;
                     A(I,K) = A(I,K)+SX(K)*SX(I)&lt;br /&gt;
                     DO J = I+1, N&lt;br /&gt;
                       A (J,K) = A(J,K)-A(J,I)*SX(K)&lt;br /&gt;
                     END DO&lt;br /&gt;
                  END DO&lt;br /&gt;
               ELSE&lt;br /&gt;
                  IF (ALPHA.NE.0.) THEN&lt;br /&gt;
                     BETA = 1./ALPHA&lt;br /&gt;
                     DO J = I+1, N&lt;br /&gt;
                       A(J,I)=A(J,I)*BETA&lt;br /&gt;
                     END DO&lt;br /&gt;
                     SX(I) = -1. &lt;br /&gt;
                     A(I,I)=ALPHA&lt;br /&gt;
                     G=1.! 1/gamma&lt;br /&gt;
                     DO K = I+1, N&lt;br /&gt;
                        SX(K)=SX(K)*BETA*G+SIGN(A(I,K),SX(I))&lt;br /&gt;
                        A(I,K) = A(I,K)+SX(K)*SX(I)&lt;br /&gt;
                        DO J = I+1, N&lt;br /&gt;
                          A (J,K) = A(J,K)-A(J,I)*SX(K)&lt;br /&gt;
                        END DO&lt;br /&gt;
                     END DO&lt;br /&gt;
                  ELSE&lt;br /&gt;
                     SX(I)=1&lt;br /&gt;
                     G=2.&lt;br /&gt;
                     DO K = I+1, N&lt;br /&gt;
                        SX(K)=2.&lt;br /&gt;
                        A(I,K) = A(I,K)-SX(K)&lt;br /&gt;
                     END DO&lt;br /&gt;
                  END IF&lt;br /&gt;
               END IF               &lt;br /&gt;
&lt;br /&gt;
		&lt;br /&gt;
	END DO&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В этом варианте R расположена в верхнем правом треугольнике массива A, направляющие вектора матриц отражений размещены в поддиагональных элементах соответствующих столбцов, а их первые элементы - в элементах массива SX.&lt;br /&gt;
&lt;br /&gt;
Обычно же в последовательных версиях коэффициенты модификаций столбцов вычисляются целиком через скалярные произведения после вычислений параметров матрицы отражения. При этом схема чуть проще. Удлиняется критический путь графа, но для последовательных реализаций это неважно.&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
К сожалению, в графе, как видно по рисунку, в наличии пучки рассылок, в них неизбежно часть дуг остаются длинными, что отрицательно влияет на локальность вычислений по пространству.  &lt;br /&gt;
&lt;br /&gt;
==== Локальность реализации алгоритма ====&lt;br /&gt;
===== Структура обращений в память и качественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qr_1.png|thumb|center|700px|Рисунок 2. Метод Хаусхолдера (отражений) QR-разложения квадратной матрицы, вещественный вариант. Общий профиль обращений в память]]&lt;br /&gt;
&lt;br /&gt;
На рис. 2 представлен профиль обращений в память для реализации вещественного варианта метода Хаусхолдера (отражений) QR-разложения квадратной матрицы. Из данного рисунка можно сделать несколько выводов. Во-первых, видно, что в среднем к каждому элементу выполняется примерно 90 обращений (отношение общего числа обращений к числу задействованных данных), что является достаточно хорошим показателем в плане повторного использования данных. Далее, явно видна итерационная структура профиля, причем на каждой следующей итерации обращения к небольшой части первых элементов перестают выполняться. Это говорит о том, что ближе к концу профиля задействовано меньше данных, что хорошо сказывается на пространственной локальности. Однако в то же время видно, что на каждой следующей итерации выполняется меньше обращений к одним и тем же локальным областям, то есть временная локальность, по всей вероятности, становится ниже. Рассмотрим подробнее первую итерацию, что более детально оценить их строение.&lt;br /&gt;
&lt;br /&gt;
На рис. 3 представлена одна итерация общего профиля (выделенный зеленый фрагмент на рис. 2). Ее можно условно разбить на 5 фрагментов, каждую из которых рассмотреть отдельно. Фрагменты 2 и 5 устроены просто – элементы перебираются с большим шагом по памяти, при достижении конца данных перебор повторяется, только с небольшим сдвигом. Про такие фрагменты можно сказать, что они обладают низкой пространственной локальностью, поскольку шаг по памяти достаточно большой, и низкой временной локальностью, так как данные повторно не используются. Однако отметим, что данные фрагменты состоят из небольшого числа обращений, что означает, что они не так сильно будут снижать общую локальность профиля.&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qr_2.png|thumb|center|700px|Рисунок 3. Профиль обращений, одна итерация]]&lt;br /&gt;
&lt;br /&gt;
Далее, фрагмент 1 и 3 устроены практически идентично (основное отличие – два основных этапа переставлены местами), поэтому рассмотрим только один из них. На рис. 4 представлен фрагмент 3. Здесь хорошо видно, как устроены эти этапы. На первом этапе выполняется перебор элементов в обратном порядке с шагом 1, при этом к каждому элементу подряд обращаются несколько раз. Такой фрагмент обладает очень высокой пространственной локальностью и достаточно высокой временной локальностью. &lt;br /&gt;
&lt;br /&gt;
[[file:householder_qr_3.png|thumb|center|500px|Рисунок 4. Профиль обращений, одна итерация, фрагмент 3]]&lt;br /&gt;
&lt;br /&gt;
На втором этапе также выполняется последовательный перебор, но уже с небольшим шагом, и к каждому элементу обращаются только по 1 разу на каждой итерации. Однако, поскольку в каждой небольшой итерации на втором этапе всего около 30 обращений, повторное обращение к каждому элементу происходит недалеко от предыдущего, что говорит о достаточно высокой временной локальности. Пространственная локальность также высока, поскольку шаг по памяти невелик.&lt;br /&gt;
&lt;br /&gt;
Далее рассмотрим фрагмент 4. Чтобы лучше понять локальную структуру, детально изучим небольшую его часть (рис. 5). Из данного рисунка хорошо видно, что здесь профиль устроен очень просто и представляет собой обычный последовательный перебор с шагом 1 (с небольшими и нечастыми сдвигами, которые не влияют на локальность этого фрагмента). Такой фрагмент обладает высокой пространственной, но низкой временной локальностью.&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qr_4.png|thumb|center|500px|Рисунок 5. Профиль обращений, одна итерация, часть фрагмента 4]]&lt;br /&gt;
&lt;br /&gt;
В целом можно сказать, что одна итерация обладает высокой пространственной и средней временной локальностью, хотя она несколько снижается из-за наличия фрагментов 2, 4 и 5. При условии, что итерации подобны, выводы относительно локальности одной итерации можно перенести и на общий профиль.&lt;br /&gt;
&lt;br /&gt;
===== Количественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
Оценка выполняется на основе характеристики daps, которая оценивает число выполненных обращений (чтений и записей) в память в секунду. Данная характеристика является аналогом оценки flops применительно к работе с памятью и является в большей степени оценкой производительности взаимодействия с памятью, чем оценкой локальности. Однако она служит хорошим источником информации, в том числе для сравнения с результатами по следующей характеристике cvg.&lt;br /&gt;
&lt;br /&gt;
На рисунке 6 приведены значения daps для реализаций распространенных алгоритмов, отсортированные по возрастанию (чем больше daps, тем в общем случае выше производительность). Можно увидеть, что производительность работы с памятью в данном примере высока. Значение daps для него находится на одном уровне с тестом Linpack и лишь немногим ниже, чем, например, самые эффективные варианты перемножения плотных матриц. Это означает, что негативное влияние фрагментов 2 и 5 оказывается достаточно слабым, вероятно (как и предполагалось ранее) из-за небольшого их размера.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qr_daps.png|thumb|center|700px|Рисунок 6. Сравнение значений оценки daps]]&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
==== Масштабируемость алгоритма ====&lt;br /&gt;
==== Масштабируемость реализации алгоритма ====&lt;br /&gt;
Проведём исследование масштабируемости вширь реализации согласно [[Scalability methodology|методике]]. Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов-2&amp;quot; [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Набор и границы значений изменяемых [[Глоссарий#Параметры запуска|параметров запуска]] реализации алгоритма: &lt;br /&gt;
&lt;br /&gt;
*	число процессоров [16:256] с шагом n^2;&lt;br /&gt;
*	размер матрицы [1000 : 32000] с шагом 1000.&lt;br /&gt;
&lt;br /&gt;
В результате проведённых экспериментов был получен следующий диапазон [[Глоссарий#Эффективность реализации|эффективности реализации]] алгоритма:&lt;br /&gt;
&lt;br /&gt;
*	минимальная эффективность реализации 7.511e-06%;&lt;br /&gt;
*	максимальная эффективность реализации 0.0746%.&lt;br /&gt;
&lt;br /&gt;
На следующих рисунках приведены графики [[Глоссарий#Производительность|производительности]] и эффективности выбранной реализации алгоритма в процедуре PDGEQRF из Scalapack в зависимости от изменяемых параметров запуска.&lt;br /&gt;
&lt;br /&gt;
[[file:PDGEQRF PERF.png|thumb|center|700px|Рисунок 8. Параллельная реализация метода Хаусхолдера QR-разложения. Изменение производительности в зависимости от числа процессоров и размера матрицы.]]&lt;br /&gt;
[[file:PDGEQRF EFF.png|thumb|center|700px|Рисунок 9. Параллельная реализация метода Хаусхолдера QR-разложения. Изменение эффективности в зависимости от числа процессоров и размера матрицы.]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
В сравнении с методом Гивенса, который имеет естественное двумерное блочное разбиение на основе точечного метода, метод Хаусхолдера из-за худших характеристик локальности (наличие пучков рассылок) и меньшего количества независимых обобщённых развёрток графа не так хорош для реализаций на системах с распределённой памятью, как для систем с общей памятью. Поэтому на массово параллельных системах с распределённой памятью следует применять метод Хаусхолдера (если уж именно его нужно реализовать) не в точечной версии, а в разрабатываемых исследователями блочных вариантах. Следует отметить, что эти варианты - не блочная нарезка описанного метода, а самостоятельные методы. Особенно их применение рекомендуется в случаях с большой разрежённостью матрицы.&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Большинство пакетов от LINPACKа и LAPACKa до SCALAPACKa используют для QR-разложения матриц именно метод Хаусхолдера, правда, в различных модификациях (обычно с использованием BLAS). Существует большая подборка исследовательских работ по блочным версиям.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Статьи в работе]]&lt;br /&gt;
[[Категория:Разложения матриц]]&lt;/div&gt;</summary>
		<author><name>Teplov</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D0%A5%D0%B0%D1%83%D1%81%D1%85%D0%BE%D0%BB%D0%B4%D0%B5%D1%80%D0%B0_(%D0%BE%D1%82%D1%80%D0%B0%D0%B6%D0%B5%D0%BD%D0%B8%D0%B9)_%D0%B4%D0%BB%D1%8F_%D0%BF%D1%80%D0%B8%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D1%8F_%D1%81%D0%B8%D0%BC%D0%BC%D0%B5%D1%82%D1%80%D0%B8%D1%87%D0%BD%D1%8B%D1%85_%D0%BC%D0%B0%D1%82%D1%80%D0%B8%D1%86_%D0%BA_%D1%82%D1%80%D1%91%D1%85%D0%B4%D0%B8%D0%B0%D0%B3%D0%BE%D0%BD%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%BC%D1%83_%D0%B2%D0%B8%D0%B4%D1%83&amp;diff=21164</id>
		<title>Метод Хаусхолдера (отражений) для приведения симметричных матриц к трёхдиагональному виду</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D0%A5%D0%B0%D1%83%D1%81%D1%85%D0%BE%D0%BB%D0%B4%D0%B5%D1%80%D0%B0_(%D0%BE%D1%82%D1%80%D0%B0%D0%B6%D0%B5%D0%BD%D0%B8%D0%B9)_%D0%B4%D0%BB%D1%8F_%D0%BF%D1%80%D0%B8%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D1%8F_%D1%81%D0%B8%D0%BC%D0%BC%D0%B5%D1%82%D1%80%D0%B8%D1%87%D0%BD%D1%8B%D1%85_%D0%BC%D0%B0%D1%82%D1%80%D0%B8%D1%86_%D0%BA_%D1%82%D1%80%D1%91%D1%85%D0%B4%D0%B8%D0%B0%D0%B3%D0%BE%D0%BD%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%BC%D1%83_%D0%B2%D0%B8%D0%B4%D1%83&amp;diff=21164"/>
		<updated>2016-12-18T10:14:57Z</updated>

		<summary type="html">&lt;p&gt;Teplov: /* Масштабируемость реализации алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Приведение симметричной вещественной матрицы к трёхдиагональному виду методом Хаусхолдера (отражений)&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(n^3)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;2n^2+O(n)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;n^2/2&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;(n^2+n)/2&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;(n^2+3n)/2&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
Основные авторы описания: [[Участник:Frolov|А.В.Фролов]]&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Метод Хаусхолдера&#039;&#039;&#039; (в советской математической литературе чаще называется &#039;&#039;&#039;методом отражений&#039;&#039;&#039;) используется для приведения симметричных вещественных матриц к трёхдиагональному виду, или, что то же самое, для разложения &amp;lt;math&amp;gt;A=QTQ^T&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; - ортогональная, &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; — симметричная трёхдиагональная матрица)&amp;lt;ref&amp;gt;В.В.Воеводин, Ю.А.Кузнецов. Матрицы и вычисления. М.: Наука, 1984.&amp;lt;/ref&amp;gt;. При этом матрица &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; хранится и используется не в своём явном виде, а в виде произведения матриц отражения&amp;lt;ref name=&amp;quot;VOLA&amp;quot;&amp;gt;Воеводин В.В. Вычислительные основы линейной алгебры. М.: Наука, 1977.&amp;lt;/ref&amp;gt;. Каждая из матриц отражения может быть определена одним вектором. Это позволяет в классическом исполнении метода отражений хранить результаты разложения на месте матрицы A с использованием одномерного дополнительного массива. &lt;br /&gt;
&lt;br /&gt;
В данной статье рассматривается именно классическое исполнение, в котором не используются приёмы типа сдваивания при вычислениях скалярных произведений.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В методе Хаусхолдера для выполнения &amp;lt;math&amp;gt;QTQ^T&amp;lt;/math&amp;gt;-разложения матрицы используются умножения слева её текущих модификаций на матрицы Хаусхолдера (отражений) с последующим умножением на те же матрицы справа. &lt;br /&gt;
&lt;br /&gt;
{{Шаблон:Матрица отражений}}&lt;br /&gt;
&lt;br /&gt;
На &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м шаге метода с помощью преобразования отражения &amp;quot;убираются&amp;quot; ненулевые поддиагональные элементы, начиная с &amp;lt;math&amp;gt;i+2&amp;lt;/math&amp;gt;-го в &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м столбце. После умножения на эту же матрицу отражения справа автоматически убираются и ненулевые наддиагональные элементы, начиная с &amp;lt;math&amp;gt;i+2&amp;lt;/math&amp;gt;-го в &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-й строке, а полученная модификация снова приобретает симметричный вид. &lt;br /&gt;
&lt;br /&gt;
На каждом из шагов метода матрицу отражений обычно представляют не в стандартном виде, а в виде &amp;lt;math&amp;gt;U=E-\frac{1}{\gamma}vv^*&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; находится через координаты текущего &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; - вектор размерности &amp;lt;math&amp;gt;n+1-i&amp;lt;/math&amp;gt;, составленный из элементов &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца, начиная с &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;(s,s)=0&amp;lt;/math&amp;gt;, то &amp;lt;math&amp;gt;v=e_{i}&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\gamma = \frac{1}{2}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В остальных случаях по алгоритму вычисляется &amp;lt;math&amp;gt;u = \frac{1}{\sqrt{(s,s)}}s&amp;lt;/math&amp;gt;, и далее &amp;lt;math&amp;gt;v_{j}=0&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;j&amp;lt;i&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;v_{j}=u_{j-i+1}&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;j&amp;gt;i&amp;lt;/math&amp;gt;, а &amp;lt;math&amp;gt;v_{i}=1&amp;lt;/math&amp;gt;, если &amp;lt;math&amp;gt;u_{1}=0&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v_{i}=\frac{u_{1}}{|u_{1}|}(1+|u_{1}|)&amp;lt;/math&amp;gt; для остальных значений. При этом &amp;lt;math&amp;gt;\gamma = 1+|u_{1}|=|v_{i}|&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
После вычисления вектора &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; подстолбцы справа от ведущего модифицируются по формулам &amp;lt;math&amp;gt;x&#039;=x-\frac{(x,v)}{\gamma}v&amp;lt;/math&amp;gt;. Потом по аналогичным формулам модифицируются строки ниже ведущей. Благодаря ассоциативности этих операций после вычисления вектора &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; можно сразу выписать формулы модификации всех элементов справа и снизу от ведущих столбца и строки. Оказывается, что если для каждого столбца матрицы &amp;lt;math&amp;gt;x^{(j)}&amp;lt;/math&amp;gt; с номером &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; известно &amp;lt;math&amp;gt;\beta_{j}=\frac{(x^{(j)},v)}{\gamma}&amp;lt;/math&amp;gt;, то для модифицируемого элемента матрицы &amp;lt;math&amp;gt;y&amp;lt;/math&amp;gt; в позиции &amp;lt;math&amp;gt;(i,j)&amp;lt;/math&amp;gt; выполняется модификация &amp;lt;math&amp;gt;y&#039; = y - \beta_{i} v_{i} - \beta_{j} v_{j} - \Delta v_{i} v_{j}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\Delta = \frac{(\beta,v)}{\gamma}&amp;lt;/math&amp;gt;. При этом все эти модификации на каждом конкретном шаге алгоритма для разных пар &amp;lt;math&amp;gt;(i,j)&amp;lt;/math&amp;gt; можно выполнять независимо друг от друга, в том числе и параллельно.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
&lt;br /&gt;
[[File:HausLeftSym1.png|thumb|right|800px|Рисунок 1. Граф первой половины шага алгоритма приведения к трёхдиагональному виду с отображением входных данных. Изображён граф первой половины шага с номером &amp;lt;math&amp;gt;n-4&amp;lt;/math&amp;gt;. Квадраты - результаты выполнения предыдущего шага. Если шаг первый, то это входные данные. Зелёные кружки - операция вида &amp;lt;math&amp;gt;a+b^2&amp;lt;/math&amp;gt;, салатовые - операция вида &amp;lt;math&amp;gt;a+bc&amp;lt;/math&amp;gt;. Синий кружок - вычисление параметров матрицы отражения, светло-красные - вычисление коэффициентов &amp;lt;math&amp;gt;\beta_i&amp;lt;/math&amp;gt;для следующего полушага, жёлтые - вычисление вектора &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.]]&lt;br /&gt;
[[File:HousLeftSym2.png|thumb|right|600px|Рисунок 2. Граф второй половины шага алгоритма приведения к трёхдиагональному виду с отображением входных данных. Изображён граф первой половины шага с номером &amp;lt;math&amp;gt;n-3&amp;lt;/math&amp;gt;. Квадраты - результаты выполнения предыдущего шага. Если шаг первый, то это входные данные. Синий, жёлтые и светло-красные кружки выполняются на первом полушаге (см. Рисунок 1). Голубые кружки -  операции &amp;lt;math&amp;gt;a+bc&amp;lt;/math&amp;gt;, зелёное - умножение, чёрные - операции &amp;lt;math&amp;gt;a+bc+de+fce&amp;lt;/math&amp;gt;.]]&lt;br /&gt;
&lt;br /&gt;
Основную часть алгоритма составляют вычисления на каждом шагу скалярных произведений &amp;lt;math&amp;gt;(s,s)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(x,v)&amp;lt;/math&amp;gt; для всех подстолбцов &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt;справа от текущего, а также проводимые над нижним правым квадратом матрицы операции вида &amp;lt;math&amp;gt;y&#039;=y-ab-cd-fbd&amp;lt;/math&amp;gt;, с учётом симметрии матрицы.&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Как уже сказано в описании ядра, основная часть - вычисления на каждом шагу скалярных произведений &amp;lt;math&amp;gt;(s,s)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(x,v)&amp;lt;/math&amp;gt; для всех подстолбцов &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; справа от текущего, а также массовые покомпонентные операции &amp;lt;math&amp;gt;y&#039;=y-ab-cd-fbd&amp;lt;/math&amp;gt;. При этом, однако, строгая последовательность выполнения первых двух подшагов не обязательна, в силу связи получаемых векторов &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; можно одновременно с &amp;lt;math&amp;gt;(s,s)&amp;lt;/math&amp;gt; вычислять и произведения &amp;lt;math&amp;gt;(x,s)&amp;lt;/math&amp;gt; с последующим выражением через них коэффициентов модификации. Это позволяет почти вдвое уменьшать критический путь графа алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Последовательность выполнения алгоритма обычно записывается как последовательное &amp;quot;обнуление&amp;quot; поддиагональных элементов столбцов, начиная с 1-го столбца и заканчивая предпоследним &amp;lt;math&amp;gt;(n-1)&amp;lt;/math&amp;gt;-м.&lt;br /&gt;
&lt;br /&gt;
При этом в каждом &amp;quot;обнуляемом&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м столбце &amp;quot;обнуляются&amp;quot; сразу все его поддиагональные элементы одновременно, с &amp;lt;math&amp;gt;(i+1)&amp;lt;/math&amp;gt;-го до &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;-го. &lt;br /&gt;
&lt;br /&gt;
Каждое &amp;quot;обнуление&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца состоит из двух шагов: &lt;br /&gt;
а) вычисление параметров матрицы отражения &amp;lt;math&amp;gt;U_{i}&amp;lt;/math&amp;gt; такой, чтобы при умножении на неё слева &amp;quot;обнулились&amp;quot; все поддиагональные его элементы;&lt;br /&gt;
б) одновременное умножение слева матрицы отражения &amp;lt;math&amp;gt;U_{i}&amp;lt;/math&amp;gt; и справа  матрицы отражения &amp;lt;math&amp;gt;U_{i}^T&amp;lt;/math&amp;gt;на текущую версию матрицы.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В последовательной версии основная сложность алгоритма определяется прежде всего вычислениями скалярных произведений векторов, а также массовых модификаций элементов вида &amp;lt;math&amp;gt;y&#039;=y-\alpha v - \beta w - \Delta vw&amp;lt;/math&amp;gt;. Они, если не учитывать возможную разреженность, составляют (в главном члене) по &amp;lt;math&amp;gt;O(n^3)&amp;lt;/math&amp;gt; операций действительного умножения и сложения/вычитания.&lt;br /&gt;
&lt;br /&gt;
При классификации по последовательной сложности, таким образом, метод Хаусхолдера относится к алгоритмам &#039;&#039;с кубической сложностью&#039;&#039;.&lt;br /&gt;
&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;n&amp;lt;/math&amp;gt; к трёхдиагональной методом Хаусхолдера нужно рассмотреть критический путь графа. &lt;br /&gt;
&lt;br /&gt;
Как видно из описания разных вершин, вычисления при &amp;quot;обнулении&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца параметров отражения и скалярных произведений состоят из основной части - ветви длиной по &amp;lt;math&amp;gt;2(n-i)&amp;lt;/math&amp;gt; умножений и сложений - и коррекции вычислений, которые составляют &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; операций. &lt;br /&gt;
&lt;br /&gt;
Поэтому по грубой (без членов низших порядков) оценке критический путь метода Хаусхолдера будет идти через &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt; умножений и &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt; сложений/вычитаний. &lt;br /&gt;
 &lt;br /&gt;
Поэтому в параллельном варианте, как и в последовательном, основную долю требуемого для выполнения алгоритма времени будут определять операции вида &amp;lt;math&amp;gt;a+bc&amp;lt;/math&amp;gt;.  &lt;br /&gt;
&lt;br /&gt;
При классификации по высоте ЯПФ, таким образом, метод Хаусхолдера относится к алгоритмам &#039;&#039;с квадратичной сложностью&#039;&#039;. При классификации по ширине ЯПФ его сложность будет также &#039;&#039;квадратичной&#039;&#039; (без расширения ряда ярусов, связанных с векторными операциями сложения, пришлось бы увеличить вдвое длину критического пути).&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: плотная симметричная квадратная матрица &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; (элементы &amp;lt;math&amp;gt;a_{ij}&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;(n^2+n)/2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: трёхдиагональная матрица &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; (ненулевые элементы &amp;lt;math&amp;gt;r_{ij}&amp;lt;/math&amp;gt; в последовательном варианте хранятся в элементах исходной матрицы &amp;lt;math&amp;gt;a_{ij}&amp;lt;/math&amp;gt;), унитарная (ортогональная) матрица Q - как произведение матриц Хаусхолдера (отражения) (их вектора нормалей к плоскостям отражения в последовательном варианте хранятся в поддиагональных элементах исходной матрицы &amp;lt;math&amp;gt;a_{ij}&amp;lt;/math&amp;gt; и в одном дополнительном столбце размерности n).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;(n^2+3n)/2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Соотношение последовательной и параллельной сложности, как хорошо видно, является &#039;&#039;линейным&#039;&#039;, что даёт определённый стимул для распараллеливания. Однако у наискорейшей ЯПФ ширина &#039;&#039;квадратична&#039;&#039;, что указывает на дисбаланс между загруженностями устройств при попытке её реально запрограммировать. Поэтому более практично даже при хорошей (быстрой) вычислительной сети оставить количество устройств (например, узлов кластера) &#039;&#039;линейным&#039;&#039; по размеру матрицы, что удвоит критический путь реализуемой ЯПФ. &lt;br /&gt;
&lt;br /&gt;
При этом вычислительная мощность алгоритма, как отношение числа операций к суммарному объему входных и выходных данных, &#039;&#039;линейна&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Алгоритм в рамках выбранной версии полностью детерминирован.&lt;br /&gt;
&lt;br /&gt;
Вычислительная погрешность в методе отражений (Хаусхолдера) растет &#039;&#039;линейно&#039;&#039;, как и в методе Гивенса (вращений).&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
В варианте с кратчайшим критическим путём графа алгоритма (с использованием зависимости между обнуляемым вектором и направляющим вектором отражения) метод Хаусхолдера (отражений) приведения квадратной симметричной вещественной матрицы к трёхдиагональному виду на Фортране 77 можно записать так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;fortran&amp;quot;&amp;gt;&lt;br /&gt;
	DO  I = 1, N-2&lt;br /&gt;
&lt;br /&gt;
            DO K = I, N&lt;br /&gt;
              SX(K)=A(N,I)*A(N,K)&lt;br /&gt;
            END DO&lt;br /&gt;
            DO J = N-1, I+1, -1&lt;br /&gt;
                 SX(I)=SX(I)+A(J,I)*A(J,I)&lt;br /&gt;
            END DO &lt;br /&gt;
            DO K = I+1, N&lt;br /&gt;
              DO J = N-1, K, -1&lt;br /&gt;
                 SX(K)=SX(K)+A(J,I)*A(J,K)&lt;br /&gt;
              END DO&lt;br /&gt;
              DO J = K-1,I+1,-1&lt;br /&gt;
                 SX(K)=SX(K)+A(J,I)*A(K,J)&lt;br /&gt;
              END DO&lt;br /&gt;
            END DO&lt;br /&gt;
&lt;br /&gt;
               ALPHA = SQRT (SX(I))&lt;br /&gt;
               IF (A(I+1,I).NE.0.) THEN&lt;br /&gt;
                  BETA = 1./ALPHA&lt;br /&gt;
                  DO J = I+2, N&lt;br /&gt;
                    A(J,I)=A(J,I)*BETA&lt;br /&gt;
                  END DO&lt;br /&gt;
                  SX(I) = A(I+1,I)*BETA+SIGN(1.,A(I+1,I))                     &lt;br /&gt;
                  A(I+1,I)=ALPHA&lt;br /&gt;
                  G=1./ABS(SX(I)) ! 1/gamma&lt;br /&gt;
                  SX2=0.&lt;br /&gt;
                  DO K = I+2, N&lt;br /&gt;
                     SX(K)=SX(K)*BETA*G+SIGN(A(K,I+1),SX(I))&lt;br /&gt;
                     SX2=SX(K)*A(K,I)+SX2                     &lt;br /&gt;
                  END DO&lt;br /&gt;
                  SX2=G*SX2&lt;br /&gt;
                  DO K = I+2, N&lt;br /&gt;
                     A(K,K) = A(K,K)-2*A(K,I)*SX(K)+SX2*A(K,I)**2&lt;br /&gt;
                     DO J = K+1, N&lt;br /&gt;
                       A (J,K) = A(J,K)-A(J,I)*SX(K)-A(K,I)*SX(I)+SX2*A(J,I)*A(K,I)&lt;br /&gt;
                     END DO&lt;br /&gt;
                  END DO&lt;br /&gt;
               ELSE&lt;br /&gt;
                  IF (ALPHA.NE.0.) THEN&lt;br /&gt;
                     BETA = 1./ALPHA&lt;br /&gt;
                     DO J = I+2, N&lt;br /&gt;
                       A(J,I)=A(J,I)*BETA&lt;br /&gt;
                     END DO&lt;br /&gt;
                     SX(I) = -1. &lt;br /&gt;
                     A(I+1,I)=ALPHA&lt;br /&gt;
                     G=1.! 1/gamma&lt;br /&gt;
                     SX2=0.&lt;br /&gt;
                     DO K = I+2, N&lt;br /&gt;
                        SX(K)=SX(K)*BETA*G+SIGN(A(K,I+1),SX(I))&lt;br /&gt;
                        SX2=SX(K)*A(K,I)+SX2                 &lt;br /&gt;
                     END DO&lt;br /&gt;
                     SX2=G*SX2&lt;br /&gt;
                     DO K = I+2, N  &lt;br /&gt;
                        A(K,K) = A(K,K)-2*A(K,I)*SX(K)+SX2*A(K,I)**2                   &lt;br /&gt;
                        DO J = K+1, N&lt;br /&gt;
                          A (J,K) = A(J,K)-A(J,I)*SX(K)-A(K,I)*SX(I)+SX2*A(J,I)*A(K,I)&lt;br /&gt;
                        END DO&lt;br /&gt;
                     END DO&lt;br /&gt;
                  ELSE&lt;br /&gt;
                     SX(I)=1.&lt;br /&gt;
                  END IF&lt;br /&gt;
               END IF               &lt;br /&gt;
&lt;br /&gt;
		&lt;br /&gt;
	END DO&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Здесь симметричная трёхдиагональная матрица хранится в диагонали и нижней кодиагонали массива A, вектора v - в поддиагональной части массива, за исключением первых их элементов, для которых выделен массив SX.&lt;br /&gt;
&lt;br /&gt;
Обычно же в последовательных версиях коэффициенты модификаций столбцов вычисляются целиком через скалярные произведения после вычислений параметров матрицы отражения. При этом схема чуть проще. Удлиняется критический путь графа, но для последовательных реализаций это неважно.&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
К сожалению, в графе, как видно по рисунку, в наличии пучки рассылок, в них неизбежно часть дуг остаются длинными, что отрицательно влияет на локальность вычислений по пространству.  &lt;br /&gt;
&lt;br /&gt;
==== Локальность реализации алгоритма ====&lt;br /&gt;
===== Структура обращений в память и качественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qtq_1.png|thumb|center|700px|Рисунок 3. Метод Хаусхолдера (отражений) для приведения симметричных матриц к трёхдиагональному виду. Общий профиль обращений в память]]&lt;br /&gt;
&lt;br /&gt;
На рис. 3 представлен профиль обращений в память для реализации метода Хаусхолдера (отражений) для приведения симметричных матриц к трёхдиагональному виду. Данный профиль обладает явной итерационной структурой, при этом видно, что итерации очень похожи. Основное их отличие заключается в наборе используемых данных – на каждой следующей итерации несколько первых элементов отбрасываются их рассмотрения, то есть чем позже итерация, тем меньше данных в ней задействовано. Также можно отметить, что число обращений в каждой последующей итерации немного уменьшается. Для того чтобы проанализировать общий профиль, в таком случае чаще всего достаточно изучить одну итерацию. Рассмотрим самую первую из них более детально.&lt;br /&gt;
&lt;br /&gt;
На рис. 4 показан набор обращений в рамках первой итерации (выделен на рис. 3 зеленым). Его можно разбить на несколько фрагментов и рассмотреть их отдельно. Строение фрагментов 2-5 можно оценить по общему графику для итерации. Фрагмент 2 представляет собой набор последовательных переборов в обратном порядке с небольшим шагом, причем число элементов в переборе постепенно уменьшается. Такое строение характеризуется достаточно высокой пространственной локальностью, поскольку часто происходит обращений к близко расположенным данным, однако низкой временной, поскольку данные повторно не используются. То же самое верно и для фрагмента 4, основное отличие которого заключается в том, что переборы выполняются в прямом порядке. Отметим, что некоторое искривление данного фрагмента связано не со строением самого фрагмента, а с разным числом параллельно выполняющихся обращений (см., например, правую область фрагмента 6), чего не наблюдается при выполнении фрагмента 2.&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qtq_2.png|thumb|center|700px|Рисунок 4. Профиль обращений, одна итерация]]&lt;br /&gt;
&lt;br /&gt;
Далее, фрагменты  3 и 5 состоят из небольшого числа обращений к данным, расположенным далеко друг от друга. Такие фрагменты характеризуются низкой пространственной и временной локальностью.&lt;br /&gt;
&lt;br /&gt;
Оставшиеся фрагменты требуют более детального рассмотрения. Перейдем к изучению фрагмента 1, который целиком представлен на рис. 5. Здесь мы можем видеть несколько этапов, каждый из которых представляет собой последовательный перебор элементов с шагом по памяти 1, причем в некоторых случаях данные много раз используются повторно (особенно это заметно в правой нижней области рисунка, где выполняется множество обращений к одному и тому же элементу). При условии, что в данном фрагменте задействовано всего около 40 элементов, можно говорить, что данный набор обращений обладает высокой пространственной и временной локальностью. &lt;br /&gt;
&lt;br /&gt;
[[file:householder_qtq_3.png|thumb|center|500px|Рисунок 5. Профиль обращений, одна итерация, фрагмент 1]]&lt;br /&gt;
&lt;br /&gt;
Далее рассмотрим детально фрагмент 6 (рис. 6). Здесь можно увидеть, что повторные обращения к данным выполняются не подряд, а через некоторые промежутки, однако число задействованных элементов также очень невелико, так что и в этом случае локальность (и пространственная, и временная) будет высокой.&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qtq_4.png|thumb|center|500px|Рисунок 6. Профиль обращений, одна итерация, фрагмент 6]]&lt;br /&gt;
&lt;br /&gt;
В целом можно сказать, что рассматриваемая итерация обладает достаточно высокой пространственной и временной локальностью. Поскольку остальные итерации устроены подобным образом, данные вывод можно перенести и на общий профиль обращений.&lt;br /&gt;
&lt;br /&gt;
===== Количественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
Оценка выполняется на основе характеристики daps, которая оценивает число выполненных обращений (чтений и записей) в память в секунду. Данная характеристика является аналогом оценки flops применительно к работе с памятью и является в большей степени оценкой производительности взаимодействия с памятью, чем оценкой локальности. Однако она служит хорошим источником информации, в том числе для сравнения с результатами по следующей характеристике cvg.&lt;br /&gt;
&lt;br /&gt;
На рисунке 7 приведены значения daps для реализаций распространенных алгоритмов, отсортированные по возрастанию (чем больше daps, тем в общем случае выше производительность). Можно увидеть, что производительность работы с памятью в данном случае достаточно высока – значение daps лишь немногим уступает тесту Linpack, который обладает высокой эффективностью работы с памятью, и немного превосходит, например, реализацию метода Якоби или метода Гаусса.&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qtq_daps.png|thumb|center|700px|Рисунок 7. Сравнение значений оценки daps]]&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
==== Масштабируемость алгоритма ====&lt;br /&gt;
==== Масштабируемость реализации алгоритма ====&lt;br /&gt;
Проведём исследование масштабируемости вширь реализации согласно [[Scalability methodology|методике]]. Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов-2&amp;quot; [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Набор и границы значений изменяемых [[Глоссарий#Параметры запуска|параметров запуска]] реализации алгоритма: &lt;br /&gt;
&lt;br /&gt;
*	число процессоров [16:256] с шагом n^2;&lt;br /&gt;
*	размер матрицы [1000 : 32000] с шагом 1000.&lt;br /&gt;
&lt;br /&gt;
В результате проведённых экспериментов был получен следующий диапазон [[Глоссарий#Эффективность реализации|эффективности реализации]] алгоритма:&lt;br /&gt;
&lt;br /&gt;
*	минимальная эффективность реализации 7.511e-06%;&lt;br /&gt;
*	максимальная эффективность реализации 0.0746%.&lt;br /&gt;
&lt;br /&gt;
На следующих рисунках приведены графики [[Глоссарий#Производительность|производительности]] и эффективности выбранной реализации алгоритма в процедуре PDGEHRD из Scalapack в зависимости от изменяемых параметров запуска.&lt;br /&gt;
&lt;br /&gt;
[[file:PDGEHRD PERF.png|thumb|center|700px|Рисунок 8. Параллельная реализация метода Хаусхолдера для приведения симметричных матриц к трехдиагональному виду. Изменение производительности в зависимости от числа процессоров и размера матрицы.]]&lt;br /&gt;
[[file:PDGEHRD EFF.png|thumb|center|700px|Рисунок 9. Параллельная реализация метода Хаусхолдера для приведения симметричных матриц к трехдиагональному виду. Изменение эффективности в зависимости от числа процессоров и размера матрицы.]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
В сравнении с методом Гивенса, который имеет естественное двумерное блочное разбиение на основе точечного метода, метод Хаусхолдера из-за худших характеристик локальности (наличие пучков рассылок) и меньшего количества независимых обобщённых развёрток графа не так хорош для реализаций на системах с распределённой памятью, как для систем с общей памятью. Поэтому на массово параллельных системах с распределённой памятью следует применять метод Хаусхолдера (если уж именно его нужно реализовать) не в точечной версии, а в разрабатываемых исследователями блочных вариантах. Следует отметить, что эти варианты - не блочная нарезка описанного метода, а самостоятельные методы. Особенно их применение рекомендуется в случаях с большой разрежённостью матрицы.&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Большинство пакетов от LINPACKа и LAPACKa до SCALAPACKa используют для QR-разложения матриц именно метод Хаусхолдера, правда, в различных модификациях (обычно с использованием BLAS). Существует большая подборка исследовательских работ по блочным версиям.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Статьи в работе]]&lt;br /&gt;
[[Категория:Разложения матриц]]&lt;/div&gt;</summary>
		<author><name>Teplov</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D0%A5%D0%B0%D1%83%D1%81%D1%85%D0%BE%D0%BB%D0%B4%D0%B5%D1%80%D0%B0_(%D0%BE%D1%82%D1%80%D0%B0%D0%B6%D0%B5%D0%BD%D0%B8%D0%B9)_QR-%D1%80%D0%B0%D0%B7%D0%BB%D0%BE%D0%B6%D0%B5%D0%BD%D0%B8%D1%8F_%D0%BA%D0%B2%D0%B0%D0%B4%D1%80%D0%B0%D1%82%D0%BD%D0%BE%D0%B9_%D0%BC%D0%B0%D1%82%D1%80%D0%B8%D1%86%D1%8B,_%D0%B2%D0%B5%D1%89%D0%B5%D1%81%D1%82%D0%B2%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D1%82%D0%BE%D1%87%D0%B5%D1%87%D0%BD%D1%8B%D0%B9_%D0%B2%D0%B0%D1%80%D0%B8%D0%B0%D0%BD%D1%82&amp;diff=21163</id>
		<title>Метод Хаусхолдера (отражений) QR-разложения квадратной матрицы, вещественный точечный вариант</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D0%A5%D0%B0%D1%83%D1%81%D1%85%D0%BE%D0%BB%D0%B4%D0%B5%D1%80%D0%B0_(%D0%BE%D1%82%D1%80%D0%B0%D0%B6%D0%B5%D0%BD%D0%B8%D0%B9)_QR-%D1%80%D0%B0%D0%B7%D0%BB%D0%BE%D0%B6%D0%B5%D0%BD%D0%B8%D1%8F_%D0%BA%D0%B2%D0%B0%D0%B4%D1%80%D0%B0%D1%82%D0%BD%D0%BE%D0%B9_%D0%BC%D0%B0%D1%82%D1%80%D0%B8%D1%86%D1%8B,_%D0%B2%D0%B5%D1%89%D0%B5%D1%81%D1%82%D0%B2%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D1%82%D0%BE%D1%87%D0%B5%D1%87%D0%BD%D1%8B%D0%B9_%D0%B2%D0%B0%D1%80%D0%B8%D0%B0%D0%BD%D1%82&amp;diff=21163"/>
		<updated>2016-12-18T10:10:26Z</updated>

		<summary type="html">&lt;p&gt;Teplov: /* Масштабируемость реализации алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = QR-разложение методом Хаусхолдера (отражений)&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;\frac{4 n^3}{3}&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;n(n + 1)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
Основные авторы описания: [[Участник:Frolov|А.В.Фролов]]&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Метод Хаусхолдера&#039;&#039;&#039; (в советской математической литературе чаще называется &#039;&#039;&#039;методом отражений&#039;&#039;&#039;) используется для разложения  матриц в виде &amp;lt;math&amp;gt;A=QR&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; - унитарная, &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; — правая треугольная матрица)&amp;lt;ref&amp;gt;В.В.Воеводин, Ю.А.Кузнецов. Матрицы и вычисления. М.: Наука, 1984.&amp;lt;/ref&amp;gt;. При этом матрица &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; хранится и используется не в своём явном виде, а в виде произведения матриц отражения&amp;lt;ref name=&amp;quot;VOLA&amp;quot;&amp;gt;Воеводин В.В. Вычислительные основы линейной алгебры. М.: Наука, 1977.&amp;lt;/ref&amp;gt;. Каждая из матриц отражения может быть определена одним вектором. Это позволяет в классическом исполнении метода отражений хранить результаты разложения на месте матрицы A с использованием минимального одномерного дополнительного массива. &lt;br /&gt;
&lt;br /&gt;
В данной статье рассматривается именно классическое исполнение, в котором не используются приёмы типа сдваивания при вычислениях скалярных произведений.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В методе Хаусхолдера для выполнения &amp;lt;math&amp;gt;QR&amp;lt;/math&amp;gt;-разложения матрицы используются умножения слева её текущих модификаций на матрицы Хаусхолдера (отражений). &lt;br /&gt;
&lt;br /&gt;
{{Шаблон:Матрица отражений}}&lt;br /&gt;
&lt;br /&gt;
На &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м шаге метода с помощью преобразования отражения &amp;quot;убираются&amp;quot; ненулевые поддиагональные элементы в &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м столбце. Таким образом, после &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; шагов преобразований получается матрица &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; из &amp;lt;math&amp;gt;QR&amp;lt;/math&amp;gt;-разложения.&lt;br /&gt;
&lt;br /&gt;
На каждом из шагов метода матрицу отражений обычно представляют не в стандартном виде, а в виде &amp;lt;math&amp;gt;U=E-\frac{1}{\gamma}vv^*&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; находится через координаты текущего &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; - вектор размерности &amp;lt;math&amp;gt;n+1-i&amp;lt;/math&amp;gt;, составленный из элементов &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца, начиная с &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;(s,s)=0&amp;lt;/math&amp;gt;, то &amp;lt;math&amp;gt;v=e_{i}&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\gamma = \frac{1}{2}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В остальных случаях по алгоритму вычисляется &amp;lt;math&amp;gt;u = \frac{1}{\sqrt{(s,s)}}s&amp;lt;/math&amp;gt;, и далее &amp;lt;math&amp;gt;v_{j}=0&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;j&amp;lt;i&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;v_{j}=u_{j-i+1}&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;j&amp;gt;i&amp;lt;/math&amp;gt;, а &amp;lt;math&amp;gt;v_{i}=1&amp;lt;/math&amp;gt;, если &amp;lt;math&amp;gt;u_{1}=0&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v_{i}=\frac{u_{1}}{|u_{1}|}(1+|u_{1}|)&amp;lt;/math&amp;gt; для остальных значений. При этом &amp;lt;math&amp;gt;\gamma =1+|u_{1}|=|v_{i}|&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
После вычисления вектора &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; подстолбцы справа от ведущего модифицируются по формулам &amp;lt;math&amp;gt;x&#039;=x-\frac{(x,v)}{\gamma}v&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Основную часть алгоритма составляют вычисления на каждом шагу скалярных произведений &amp;lt;math&amp;gt;(s,s)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(x,v)&amp;lt;/math&amp;gt; для всех подстолбцов &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt;справа от текущего, а также векторные операции &amp;lt;math&amp;gt;x&#039;=x-\frac{(x,v)}{\gamma}v&amp;lt;/math&amp;gt;. Это используется при программировании метода во многих библиотеках для его конструирования из стандартных подпрограмм (например, из BLAS).&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
[[File:HausLeft1.png|thumb|right|600px|Рисунок 1. Граф шага (обнуление &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;го столбца) алгоритма. Квадратики - входные данные шага (берутся с предыдущего или из входных данных), кружки - операции. Зелёным выделены операции типа a+bb, салатовым и голубым - типа a+bc, тёмно-синим - вычисление &amp;lt;math&amp;gt;\gamma , v_{1}&amp;lt;/math&amp;gt;, жёлтым - умножения (или деления). Обведённая группа операций повторяется независимо n-i раз. Результаты синего, красных и жёлтых кружков, а на последнем шаге и голубого - выходные для алгоритма.]]&lt;br /&gt;
&lt;br /&gt;
Как уже сказано в описании ядра, основная часть - вычисления на каждом шагу скалярных произведений &amp;lt;math&amp;gt;(s,s)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(x,v)&amp;lt;/math&amp;gt; для всех подстолбцов &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; справа от текущего, а также векторные операции &amp;lt;math&amp;gt;x&#039;=x-\frac{(x,v)}{\gamma}v&amp;lt;/math&amp;gt;. При этом, однако, строгая последовательность выполнения этих трёх подшагов не обязательна, в силу связи получаемых векторов &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; можно одновременно с &amp;lt;math&amp;gt;(s,s)&amp;lt;/math&amp;gt; вычислять и произведения &amp;lt;math&amp;gt;(x,s)&amp;lt;/math&amp;gt; с последующим выражением через них &amp;lt;math&amp;gt;(x,v)&amp;lt;/math&amp;gt;. Это позволяет почти вдвое уменьшать критический путь графа алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Последовательность выполнения алгоритма обычно записывается как последовательное &amp;quot;обнуление&amp;quot; поддиагональных элементов столбцов, начиная с 1-го столбца и заканчивая предпоследним &amp;lt;math&amp;gt;(n-1)&amp;lt;/math&amp;gt;-м.&lt;br /&gt;
&lt;br /&gt;
При этом в каждом &amp;quot;обнуляемом&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м столбце &amp;quot;обнуляются&amp;quot; сразу все его поддиагональные элементы одновременно, с &amp;lt;math&amp;gt;(i+1)&amp;lt;/math&amp;gt;-го до &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;-го. &lt;br /&gt;
&lt;br /&gt;
Каждое &amp;quot;обнуление&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца состоит из двух шагов: &lt;br /&gt;
а) вычисление параметров матрицы отражения &amp;lt;math&amp;gt;U_{i}&amp;lt;/math&amp;gt; такой, чтобы при умножении на неё слева &amp;quot;обнулились&amp;quot; все поддиагональные его элементы;&lt;br /&gt;
б) умножение слева матрицы отражения &amp;lt;math&amp;gt;U_{i}&amp;lt;/math&amp;gt; на текущую версию матрицы.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В последовательной версии основная сложность алгоритма определяется прежде всего вычислениями скалярных произведений векторов, а также модификаций векторов вида &amp;lt;math&amp;gt;x&#039;=x-\alpha v&amp;lt;/math&amp;gt;, причем над векторами убывающей по ходу алгоритма размерности. Они, если не учитывать возможную разреженность, составляют (в главном члене) по &amp;lt;math&amp;gt;2n^3/3&amp;lt;/math&amp;gt; операций действительного умножения и сложения/вычитания.&lt;br /&gt;
&lt;br /&gt;
При классификации по последовательной сложности, таким образом, метод Хаусхолдера относится к алгоритмам &#039;&#039;с кубической сложностью&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
На Рисунке 1 приведён шаг графа алгоритма метода Хаусхолдера в наиболее его быстром (с параллельной точки зрения) варианте, использующем то, что с точностью до множителя ведущий вектор матрицы отражения отличается отличается от подстолбца, где выполняется очередное исключение, только одним элементом. Операции привязаны к обрабатываемым элементам матрицы. Для получения полного графа графы шагов нужно положить друг на друга последовательными слоями, при этом правые нижние углы должны быть друг над другом.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Для понимания ресурса параллелизма в разложении матрицы порядка &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; методом Хаусхолдера нужно рассмотреть критический путь графа. &lt;br /&gt;
&lt;br /&gt;
Как видно из описания разных вершин, вычисления при &amp;quot;обнулении&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца параметров отражения и скалярных произведений состоят из основной части - ветви длиной по &amp;lt;math&amp;gt;n-i&amp;lt;/math&amp;gt; умножений и сложений - и коррекции вычислений, которые составляют &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; операций.&lt;br /&gt;
&lt;br /&gt;
Поэтому по грубой (без членов низших порядков) оценке критический путь метода Хаусхолдера будет идти через &amp;lt;math&amp;gt;\frac{n^2}{2}&amp;lt;/math&amp;gt; умножений и &amp;lt;math&amp;gt;\frac{n^2}{2}&amp;lt;/math&amp;gt; сложений/вычитаний. &lt;br /&gt;
 &lt;br /&gt;
Поэтому в параллельном варианте, как и в последовательном, основную долю требуемого для выполнения алгоритма времени будут определять операции вида &amp;lt;math&amp;gt;a+bc&amp;lt;/math&amp;gt;.  &lt;br /&gt;
&lt;br /&gt;
При классификации по высоте ЯПФ, таким образом, метод Хаусхолдера относится к алгоритмам &#039;&#039;с квадратичной сложностью&#039;&#039;. При классификации по ширине ЯПФ его сложность будет также &#039;&#039;квадратичной&#039;&#039; (без расширения ряда ярусов, связанных с векторными операциями сложения, пришлось бы увеличить вдвое длину критического пути; при таком расширении сложность по ширине ЯПФ станет &#039;&#039;линейной&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
Надо сказать, что здесь в оценках речь идёт именно о классическом способе реализации метода Хаусхолдера. Даже использование схем сдваивания или последовательно-параллельных для вычисления скалярных произведений уменьшает критический путь с квадратичного до &#039;&#039;степени 3/2&#039;&#039; или &#039;&#039;линейно-логарифмического&#039;&#039;. Однако все эти широко распространённые методы пока не дали возможности снизить критический путь метода Хаусхолдера до &#039;&#039;линейного&#039;&#039; (как, скажем, у метода Гивенса).&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: плотная квадратная матрица &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; (элементы &amp;lt;math&amp;gt;a_{ij}&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: правая треугольная матрица &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; (ненулевые элементы &amp;lt;math&amp;gt;r_{ij}&amp;lt;/math&amp;gt; в последовательном варианте хранятся в элементах исходной матрицы &amp;lt;math&amp;gt;a_{ij}&amp;lt;/math&amp;gt;), унитарная (ортогональная) матрица Q - как произведение матриц Хаусхолдера (отражения) (их вектора нормалей к плоскостям отражения в последовательном варианте хранятся в поддиагональных элементах исходной матрицы &amp;lt;math&amp;gt;a_{ij}&amp;lt;/math&amp;gt; и в одном дополнительном столбце размерности n).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;n^2+n&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Соотношение последовательной и параллельной сложности, как хорошо видно, является &#039;&#039;линейным&#039;&#039;, что даёт определённый стимул для распараллеливания. Однако у наискорейшей ЯПФ ширина &#039;&#039;квадратична&#039;&#039;, что указывает на дисбаланс между загруженностями устройств при попытке её реально запрограммировать. Поэтому более практично даже при хорошей (быстрой) вычислительной сети оставить количество устройств (например, узлов кластера) &#039;&#039;линейным&#039;&#039; по размеру матрицы, что удвоит критический путь реализуемой ЯПФ. &lt;br /&gt;
&lt;br /&gt;
При этом вычислительная мощность алгоритма, как отношение числа операций к суммарному объему входных и выходных данных, &#039;&#039;линейна&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Алгоритм в рамках выбранной версии полностью детерминирован.&lt;br /&gt;
&lt;br /&gt;
Вычислительная погрешность в методе отражений (Хаусхолдера) растет &#039;&#039;линейно&#039;&#039;, как и в методе Гивенса (вращений).&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В варианте с кратчайшим критическим путём графа алгоритма (с использованием зависимости между обнуляемым вектором и направляющим вектором отражения) метод Хаусхолдера (отражений) QR-разложения квадратной вещественной матрицы на Фортране 77 можно записать так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;fortran&amp;quot;&amp;gt;&lt;br /&gt;
	DO  I = 1, N-1&lt;br /&gt;
            DO K = I, N&lt;br /&gt;
              SX(K)=A(N,I)*A(N,K)&lt;br /&gt;
            END DO&lt;br /&gt;
            DO J = N-1, I, -1&lt;br /&gt;
               DO K = I, N&lt;br /&gt;
                  SX(K)=SX(K)+A(J,I)*A(J,K)&lt;br /&gt;
               END DO&lt;br /&gt;
            END DO&lt;br /&gt;
&lt;br /&gt;
               ALPHA = SQRT (SX(I))&lt;br /&gt;
               IF (A(I,I).NE.0.) THEN&lt;br /&gt;
                  BETA = 1./ALPHA&lt;br /&gt;
                  DO J = I+1, N&lt;br /&gt;
                    A(J,I)=A(J,I)*BETA&lt;br /&gt;
                  END DO&lt;br /&gt;
                  SX(I) = A(I,I)*BETA+SIGN(1.,A(I,I))                     &lt;br /&gt;
                  A(I,I)=ALPHA&lt;br /&gt;
                  G=1./ABS(SX(I)) ! 1/gamma&lt;br /&gt;
                  DO K = I+1, N&lt;br /&gt;
                     SX(K)=SX(K)*BETA*G+SIGN(A(I,K),SX(I))&lt;br /&gt;
                     A(I,K) = A(I,K)+SX(K)*SX(I)&lt;br /&gt;
                     DO J = I+1, N&lt;br /&gt;
                       A (J,K) = A(J,K)-A(J,I)*SX(K)&lt;br /&gt;
                     END DO&lt;br /&gt;
                  END DO&lt;br /&gt;
               ELSE&lt;br /&gt;
                  IF (ALPHA.NE.0.) THEN&lt;br /&gt;
                     BETA = 1./ALPHA&lt;br /&gt;
                     DO J = I+1, N&lt;br /&gt;
                       A(J,I)=A(J,I)*BETA&lt;br /&gt;
                     END DO&lt;br /&gt;
                     SX(I) = -1. &lt;br /&gt;
                     A(I,I)=ALPHA&lt;br /&gt;
                     G=1.! 1/gamma&lt;br /&gt;
                     DO K = I+1, N&lt;br /&gt;
                        SX(K)=SX(K)*BETA*G+SIGN(A(I,K),SX(I))&lt;br /&gt;
                        A(I,K) = A(I,K)+SX(K)*SX(I)&lt;br /&gt;
                        DO J = I+1, N&lt;br /&gt;
                          A (J,K) = A(J,K)-A(J,I)*SX(K)&lt;br /&gt;
                        END DO&lt;br /&gt;
                     END DO&lt;br /&gt;
                  ELSE&lt;br /&gt;
                     SX(I)=1&lt;br /&gt;
                     G=2.&lt;br /&gt;
                     DO K = I+1, N&lt;br /&gt;
                        SX(K)=2.&lt;br /&gt;
                        A(I,K) = A(I,K)-SX(K)&lt;br /&gt;
                     END DO&lt;br /&gt;
                  END IF&lt;br /&gt;
               END IF               &lt;br /&gt;
&lt;br /&gt;
		&lt;br /&gt;
	END DO&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В этом варианте R расположена в верхнем правом треугольнике массива A, направляющие вектора матриц отражений размещены в поддиагональных элементах соответствующих столбцов, а их первые элементы - в элементах массива SX.&lt;br /&gt;
&lt;br /&gt;
Обычно же в последовательных версиях коэффициенты модификаций столбцов вычисляются целиком через скалярные произведения после вычислений параметров матрицы отражения. При этом схема чуть проще. Удлиняется критический путь графа, но для последовательных реализаций это неважно.&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
К сожалению, в графе, как видно по рисунку, в наличии пучки рассылок, в них неизбежно часть дуг остаются длинными, что отрицательно влияет на локальность вычислений по пространству.  &lt;br /&gt;
&lt;br /&gt;
==== Локальность реализации алгоритма ====&lt;br /&gt;
===== Структура обращений в память и качественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qr_1.png|thumb|center|700px|Рисунок 2. Метод Хаусхолдера (отражений) QR-разложения квадратной матрицы, вещественный вариант. Общий профиль обращений в память]]&lt;br /&gt;
&lt;br /&gt;
На рис. 2 представлен профиль обращений в память для реализации вещественного варианта метода Хаусхолдера (отражений) QR-разложения квадратной матрицы. Из данного рисунка можно сделать несколько выводов. Во-первых, видно, что в среднем к каждому элементу выполняется примерно 90 обращений (отношение общего числа обращений к числу задействованных данных), что является достаточно хорошим показателем в плане повторного использования данных. Далее, явно видна итерационная структура профиля, причем на каждой следующей итерации обращения к небольшой части первых элементов перестают выполняться. Это говорит о том, что ближе к концу профиля задействовано меньше данных, что хорошо сказывается на пространственной локальности. Однако в то же время видно, что на каждой следующей итерации выполняется меньше обращений к одним и тем же локальным областям, то есть временная локальность, по всей вероятности, становится ниже. Рассмотрим подробнее первую итерацию, что более детально оценить их строение.&lt;br /&gt;
&lt;br /&gt;
На рис. 3 представлена одна итерация общего профиля (выделенный зеленый фрагмент на рис. 2). Ее можно условно разбить на 5 фрагментов, каждую из которых рассмотреть отдельно. Фрагменты 2 и 5 устроены просто – элементы перебираются с большим шагом по памяти, при достижении конца данных перебор повторяется, только с небольшим сдвигом. Про такие фрагменты можно сказать, что они обладают низкой пространственной локальностью, поскольку шаг по памяти достаточно большой, и низкой временной локальностью, так как данные повторно не используются. Однако отметим, что данные фрагменты состоят из небольшого числа обращений, что означает, что они не так сильно будут снижать общую локальность профиля.&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qr_2.png|thumb|center|700px|Рисунок 3. Профиль обращений, одна итерация]]&lt;br /&gt;
&lt;br /&gt;
Далее, фрагмент 1 и 3 устроены практически идентично (основное отличие – два основных этапа переставлены местами), поэтому рассмотрим только один из них. На рис. 4 представлен фрагмент 3. Здесь хорошо видно, как устроены эти этапы. На первом этапе выполняется перебор элементов в обратном порядке с шагом 1, при этом к каждому элементу подряд обращаются несколько раз. Такой фрагмент обладает очень высокой пространственной локальностью и достаточно высокой временной локальностью. &lt;br /&gt;
&lt;br /&gt;
[[file:householder_qr_3.png|thumb|center|500px|Рисунок 4. Профиль обращений, одна итерация, фрагмент 3]]&lt;br /&gt;
&lt;br /&gt;
На втором этапе также выполняется последовательный перебор, но уже с небольшим шагом, и к каждому элементу обращаются только по 1 разу на каждой итерации. Однако, поскольку в каждой небольшой итерации на втором этапе всего около 30 обращений, повторное обращение к каждому элементу происходит недалеко от предыдущего, что говорит о достаточно высокой временной локальности. Пространственная локальность также высока, поскольку шаг по памяти невелик.&lt;br /&gt;
&lt;br /&gt;
Далее рассмотрим фрагмент 4. Чтобы лучше понять локальную структуру, детально изучим небольшую его часть (рис. 5). Из данного рисунка хорошо видно, что здесь профиль устроен очень просто и представляет собой обычный последовательный перебор с шагом 1 (с небольшими и нечастыми сдвигами, которые не влияют на локальность этого фрагмента). Такой фрагмент обладает высокой пространственной, но низкой временной локальностью.&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qr_4.png|thumb|center|500px|Рисунок 5. Профиль обращений, одна итерация, часть фрагмента 4]]&lt;br /&gt;
&lt;br /&gt;
В целом можно сказать, что одна итерация обладает высокой пространственной и средней временной локальностью, хотя она несколько снижается из-за наличия фрагментов 2, 4 и 5. При условии, что итерации подобны, выводы относительно локальности одной итерации можно перенести и на общий профиль.&lt;br /&gt;
&lt;br /&gt;
===== Количественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
Оценка выполняется на основе характеристики daps, которая оценивает число выполненных обращений (чтений и записей) в память в секунду. Данная характеристика является аналогом оценки flops применительно к работе с памятью и является в большей степени оценкой производительности взаимодействия с памятью, чем оценкой локальности. Однако она служит хорошим источником информации, в том числе для сравнения с результатами по следующей характеристике cvg.&lt;br /&gt;
&lt;br /&gt;
На рисунке 6 приведены значения daps для реализаций распространенных алгоритмов, отсортированные по возрастанию (чем больше daps, тем в общем случае выше производительность). Можно увидеть, что производительность работы с памятью в данном примере высока. Значение daps для него находится на одном уровне с тестом Linpack и лишь немногим ниже, чем, например, самые эффективные варианты перемножения плотных матриц. Это означает, что негативное влияние фрагментов 2 и 5 оказывается достаточно слабым, вероятно (как и предполагалось ранее) из-за небольшого их размера.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qr_daps.png|thumb|center|700px|Рисунок 6. Сравнение значений оценки daps]]&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
==== Масштабируемость алгоритма ====&lt;br /&gt;
==== Масштабируемость реализации алгоритма ====&lt;br /&gt;
Проведём исследование масштабируемости вширь реализации согласно [[Scalability methodology|методике]]. Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов-2&amp;quot; [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Набор и границы значений изменяемых [[Глоссарий#Параметры запуска|параметров запуска]] реализации алгоритма: &lt;br /&gt;
&lt;br /&gt;
*	число процессоров [16:256] с шагом n^2;&lt;br /&gt;
*	размер матрицы [1000 : 32000] с шагом 1000.&lt;br /&gt;
&lt;br /&gt;
В результате проведённых экспериментов был получен следующий диапазон [[Глоссарий#Эффективность реализации|эффективности реализации]] алгоритма:&lt;br /&gt;
&lt;br /&gt;
*	минимальная эффективность реализации 7.511e-06%;&lt;br /&gt;
*	максимальная эффективность реализации 0.0746%.&lt;br /&gt;
&lt;br /&gt;
На следующих рисунках приведены графики [[Глоссарий#Производительность|производительности]] и эффективности выбранной реализации алгоритма в процедуре PDGEQRF из Scalapack в зависимости от изменяемых параметров запуска.&lt;br /&gt;
&lt;br /&gt;
[[file:PDGEQRF PERF.png|thumb|center|700px|Рисунок 8. Параллельная реализация циклической редукции. Изменение производительности в зависимости от числа процессоров и размера матрицы.]]&lt;br /&gt;
[[file:PDGEQRF EFF.png|thumb|center|700px|Рисунок 9. Параллельная реализация циклической редукции. Изменение эффективности в зависимости от числа процессоров и размера матрицы.]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
В сравнении с методом Гивенса, который имеет естественное двумерное блочное разбиение на основе точечного метода, метод Хаусхолдера из-за худших характеристик локальности (наличие пучков рассылок) и меньшего количества независимых обобщённых развёрток графа не так хорош для реализаций на системах с распределённой памятью, как для систем с общей памятью. Поэтому на массово параллельных системах с распределённой памятью следует применять метод Хаусхолдера (если уж именно его нужно реализовать) не в точечной версии, а в разрабатываемых исследователями блочных вариантах. Следует отметить, что эти варианты - не блочная нарезка описанного метода, а самостоятельные методы. Особенно их применение рекомендуется в случаях с большой разрежённостью матрицы.&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Большинство пакетов от LINPACKа и LAPACKa до SCALAPACKa используют для QR-разложения матриц именно метод Хаусхолдера, правда, в различных модификациях (обычно с использованием BLAS). Существует большая подборка исследовательских работ по блочным версиям.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Статьи в работе]]&lt;br /&gt;
[[Категория:Разложения матриц]]&lt;/div&gt;</summary>
		<author><name>Teplov</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D0%A5%D0%B0%D1%83%D1%81%D1%85%D0%BE%D0%BB%D0%B4%D0%B5%D1%80%D0%B0_(%D0%BE%D1%82%D1%80%D0%B0%D0%B6%D0%B5%D0%BD%D0%B8%D0%B9)_%D0%B4%D0%BB%D1%8F_%D0%BF%D1%80%D0%B8%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D1%8F_%D1%81%D0%B8%D0%BC%D0%BC%D0%B5%D1%82%D1%80%D0%B8%D1%87%D0%BD%D1%8B%D1%85_%D0%BC%D0%B0%D1%82%D1%80%D0%B8%D1%86_%D0%BA_%D1%82%D1%80%D1%91%D1%85%D0%B4%D0%B8%D0%B0%D0%B3%D0%BE%D0%BD%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%BC%D1%83_%D0%B2%D0%B8%D0%B4%D1%83&amp;diff=21162</id>
		<title>Метод Хаусхолдера (отражений) для приведения симметричных матриц к трёхдиагональному виду</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D0%A5%D0%B0%D1%83%D1%81%D1%85%D0%BE%D0%BB%D0%B4%D0%B5%D1%80%D0%B0_(%D0%BE%D1%82%D1%80%D0%B0%D0%B6%D0%B5%D0%BD%D0%B8%D0%B9)_%D0%B4%D0%BB%D1%8F_%D0%BF%D1%80%D0%B8%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D1%8F_%D1%81%D0%B8%D0%BC%D0%BC%D0%B5%D1%82%D1%80%D0%B8%D1%87%D0%BD%D1%8B%D1%85_%D0%BC%D0%B0%D1%82%D1%80%D0%B8%D1%86_%D0%BA_%D1%82%D1%80%D1%91%D1%85%D0%B4%D0%B8%D0%B0%D0%B3%D0%BE%D0%BD%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%BC%D1%83_%D0%B2%D0%B8%D0%B4%D1%83&amp;diff=21162"/>
		<updated>2016-12-18T10:08:20Z</updated>

		<summary type="html">&lt;p&gt;Teplov: /* Масштабируемость реализации алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Приведение симметричной вещественной матрицы к трёхдиагональному виду методом Хаусхолдера (отражений)&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(n^3)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;2n^2+O(n)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;n^2/2&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;(n^2+n)/2&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;(n^2+3n)/2&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
Основные авторы описания: [[Участник:Frolov|А.В.Фролов]]&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Метод Хаусхолдера&#039;&#039;&#039; (в советской математической литературе чаще называется &#039;&#039;&#039;методом отражений&#039;&#039;&#039;) используется для приведения симметричных вещественных матриц к трёхдиагональному виду, или, что то же самое, для разложения &amp;lt;math&amp;gt;A=QTQ^T&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; - ортогональная, &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; — симметричная трёхдиагональная матрица)&amp;lt;ref&amp;gt;В.В.Воеводин, Ю.А.Кузнецов. Матрицы и вычисления. М.: Наука, 1984.&amp;lt;/ref&amp;gt;. При этом матрица &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; хранится и используется не в своём явном виде, а в виде произведения матриц отражения&amp;lt;ref name=&amp;quot;VOLA&amp;quot;&amp;gt;Воеводин В.В. Вычислительные основы линейной алгебры. М.: Наука, 1977.&amp;lt;/ref&amp;gt;. Каждая из матриц отражения может быть определена одним вектором. Это позволяет в классическом исполнении метода отражений хранить результаты разложения на месте матрицы A с использованием одномерного дополнительного массива. &lt;br /&gt;
&lt;br /&gt;
В данной статье рассматривается именно классическое исполнение, в котором не используются приёмы типа сдваивания при вычислениях скалярных произведений.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В методе Хаусхолдера для выполнения &amp;lt;math&amp;gt;QTQ^T&amp;lt;/math&amp;gt;-разложения матрицы используются умножения слева её текущих модификаций на матрицы Хаусхолдера (отражений) с последующим умножением на те же матрицы справа. &lt;br /&gt;
&lt;br /&gt;
{{Шаблон:Матрица отражений}}&lt;br /&gt;
&lt;br /&gt;
На &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м шаге метода с помощью преобразования отражения &amp;quot;убираются&amp;quot; ненулевые поддиагональные элементы, начиная с &amp;lt;math&amp;gt;i+2&amp;lt;/math&amp;gt;-го в &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м столбце. После умножения на эту же матрицу отражения справа автоматически убираются и ненулевые наддиагональные элементы, начиная с &amp;lt;math&amp;gt;i+2&amp;lt;/math&amp;gt;-го в &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-й строке, а полученная модификация снова приобретает симметричный вид. &lt;br /&gt;
&lt;br /&gt;
На каждом из шагов метода матрицу отражений обычно представляют не в стандартном виде, а в виде &amp;lt;math&amp;gt;U=E-\frac{1}{\gamma}vv^*&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; находится через координаты текущего &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; - вектор размерности &amp;lt;math&amp;gt;n+1-i&amp;lt;/math&amp;gt;, составленный из элементов &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца, начиная с &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;(s,s)=0&amp;lt;/math&amp;gt;, то &amp;lt;math&amp;gt;v=e_{i}&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\gamma = \frac{1}{2}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В остальных случаях по алгоритму вычисляется &amp;lt;math&amp;gt;u = \frac{1}{\sqrt{(s,s)}}s&amp;lt;/math&amp;gt;, и далее &amp;lt;math&amp;gt;v_{j}=0&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;j&amp;lt;i&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;v_{j}=u_{j-i+1}&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;j&amp;gt;i&amp;lt;/math&amp;gt;, а &amp;lt;math&amp;gt;v_{i}=1&amp;lt;/math&amp;gt;, если &amp;lt;math&amp;gt;u_{1}=0&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v_{i}=\frac{u_{1}}{|u_{1}|}(1+|u_{1}|)&amp;lt;/math&amp;gt; для остальных значений. При этом &amp;lt;math&amp;gt;\gamma = 1+|u_{1}|=|v_{i}|&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
После вычисления вектора &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; подстолбцы справа от ведущего модифицируются по формулам &amp;lt;math&amp;gt;x&#039;=x-\frac{(x,v)}{\gamma}v&amp;lt;/math&amp;gt;. Потом по аналогичным формулам модифицируются строки ниже ведущей. Благодаря ассоциативности этих операций после вычисления вектора &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; можно сразу выписать формулы модификации всех элементов справа и снизу от ведущих столбца и строки. Оказывается, что если для каждого столбца матрицы &amp;lt;math&amp;gt;x^{(j)}&amp;lt;/math&amp;gt; с номером &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; известно &amp;lt;math&amp;gt;\beta_{j}=\frac{(x^{(j)},v)}{\gamma}&amp;lt;/math&amp;gt;, то для модифицируемого элемента матрицы &amp;lt;math&amp;gt;y&amp;lt;/math&amp;gt; в позиции &amp;lt;math&amp;gt;(i,j)&amp;lt;/math&amp;gt; выполняется модификация &amp;lt;math&amp;gt;y&#039; = y - \beta_{i} v_{i} - \beta_{j} v_{j} - \Delta v_{i} v_{j}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\Delta = \frac{(\beta,v)}{\gamma}&amp;lt;/math&amp;gt;. При этом все эти модификации на каждом конкретном шаге алгоритма для разных пар &amp;lt;math&amp;gt;(i,j)&amp;lt;/math&amp;gt; можно выполнять независимо друг от друга, в том числе и параллельно.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
&lt;br /&gt;
[[File:HausLeftSym1.png|thumb|right|800px|Рисунок 1. Граф первой половины шага алгоритма приведения к трёхдиагональному виду с отображением входных данных. Изображён граф первой половины шага с номером &amp;lt;math&amp;gt;n-4&amp;lt;/math&amp;gt;. Квадраты - результаты выполнения предыдущего шага. Если шаг первый, то это входные данные. Зелёные кружки - операция вида &amp;lt;math&amp;gt;a+b^2&amp;lt;/math&amp;gt;, салатовые - операция вида &amp;lt;math&amp;gt;a+bc&amp;lt;/math&amp;gt;. Синий кружок - вычисление параметров матрицы отражения, светло-красные - вычисление коэффициентов &amp;lt;math&amp;gt;\beta_i&amp;lt;/math&amp;gt;для следующего полушага, жёлтые - вычисление вектора &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.]]&lt;br /&gt;
[[File:HousLeftSym2.png|thumb|right|600px|Рисунок 2. Граф второй половины шага алгоритма приведения к трёхдиагональному виду с отображением входных данных. Изображён граф первой половины шага с номером &amp;lt;math&amp;gt;n-3&amp;lt;/math&amp;gt;. Квадраты - результаты выполнения предыдущего шага. Если шаг первый, то это входные данные. Синий, жёлтые и светло-красные кружки выполняются на первом полушаге (см. Рисунок 1). Голубые кружки -  операции &amp;lt;math&amp;gt;a+bc&amp;lt;/math&amp;gt;, зелёное - умножение, чёрные - операции &amp;lt;math&amp;gt;a+bc+de+fce&amp;lt;/math&amp;gt;.]]&lt;br /&gt;
&lt;br /&gt;
Основную часть алгоритма составляют вычисления на каждом шагу скалярных произведений &amp;lt;math&amp;gt;(s,s)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(x,v)&amp;lt;/math&amp;gt; для всех подстолбцов &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt;справа от текущего, а также проводимые над нижним правым квадратом матрицы операции вида &amp;lt;math&amp;gt;y&#039;=y-ab-cd-fbd&amp;lt;/math&amp;gt;, с учётом симметрии матрицы.&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Как уже сказано в описании ядра, основная часть - вычисления на каждом шагу скалярных произведений &amp;lt;math&amp;gt;(s,s)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(x,v)&amp;lt;/math&amp;gt; для всех подстолбцов &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; справа от текущего, а также массовые покомпонентные операции &amp;lt;math&amp;gt;y&#039;=y-ab-cd-fbd&amp;lt;/math&amp;gt;. При этом, однако, строгая последовательность выполнения первых двух подшагов не обязательна, в силу связи получаемых векторов &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; можно одновременно с &amp;lt;math&amp;gt;(s,s)&amp;lt;/math&amp;gt; вычислять и произведения &amp;lt;math&amp;gt;(x,s)&amp;lt;/math&amp;gt; с последующим выражением через них коэффициентов модификации. Это позволяет почти вдвое уменьшать критический путь графа алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Последовательность выполнения алгоритма обычно записывается как последовательное &amp;quot;обнуление&amp;quot; поддиагональных элементов столбцов, начиная с 1-го столбца и заканчивая предпоследним &amp;lt;math&amp;gt;(n-1)&amp;lt;/math&amp;gt;-м.&lt;br /&gt;
&lt;br /&gt;
При этом в каждом &amp;quot;обнуляемом&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м столбце &amp;quot;обнуляются&amp;quot; сразу все его поддиагональные элементы одновременно, с &amp;lt;math&amp;gt;(i+1)&amp;lt;/math&amp;gt;-го до &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;-го. &lt;br /&gt;
&lt;br /&gt;
Каждое &amp;quot;обнуление&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца состоит из двух шагов: &lt;br /&gt;
а) вычисление параметров матрицы отражения &amp;lt;math&amp;gt;U_{i}&amp;lt;/math&amp;gt; такой, чтобы при умножении на неё слева &amp;quot;обнулились&amp;quot; все поддиагональные его элементы;&lt;br /&gt;
б) одновременное умножение слева матрицы отражения &amp;lt;math&amp;gt;U_{i}&amp;lt;/math&amp;gt; и справа  матрицы отражения &amp;lt;math&amp;gt;U_{i}^T&amp;lt;/math&amp;gt;на текущую версию матрицы.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В последовательной версии основная сложность алгоритма определяется прежде всего вычислениями скалярных произведений векторов, а также массовых модификаций элементов вида &amp;lt;math&amp;gt;y&#039;=y-\alpha v - \beta w - \Delta vw&amp;lt;/math&amp;gt;. Они, если не учитывать возможную разреженность, составляют (в главном члене) по &amp;lt;math&amp;gt;O(n^3)&amp;lt;/math&amp;gt; операций действительного умножения и сложения/вычитания.&lt;br /&gt;
&lt;br /&gt;
При классификации по последовательной сложности, таким образом, метод Хаусхолдера относится к алгоритмам &#039;&#039;с кубической сложностью&#039;&#039;.&lt;br /&gt;
&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;n&amp;lt;/math&amp;gt; к трёхдиагональной методом Хаусхолдера нужно рассмотреть критический путь графа. &lt;br /&gt;
&lt;br /&gt;
Как видно из описания разных вершин, вычисления при &amp;quot;обнулении&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца параметров отражения и скалярных произведений состоят из основной части - ветви длиной по &amp;lt;math&amp;gt;2(n-i)&amp;lt;/math&amp;gt; умножений и сложений - и коррекции вычислений, которые составляют &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; операций. &lt;br /&gt;
&lt;br /&gt;
Поэтому по грубой (без членов низших порядков) оценке критический путь метода Хаусхолдера будет идти через &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt; умножений и &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt; сложений/вычитаний. &lt;br /&gt;
 &lt;br /&gt;
Поэтому в параллельном варианте, как и в последовательном, основную долю требуемого для выполнения алгоритма времени будут определять операции вида &amp;lt;math&amp;gt;a+bc&amp;lt;/math&amp;gt;.  &lt;br /&gt;
&lt;br /&gt;
При классификации по высоте ЯПФ, таким образом, метод Хаусхолдера относится к алгоритмам &#039;&#039;с квадратичной сложностью&#039;&#039;. При классификации по ширине ЯПФ его сложность будет также &#039;&#039;квадратичной&#039;&#039; (без расширения ряда ярусов, связанных с векторными операциями сложения, пришлось бы увеличить вдвое длину критического пути).&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: плотная симметричная квадратная матрица &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; (элементы &amp;lt;math&amp;gt;a_{ij}&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;(n^2+n)/2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: трёхдиагональная матрица &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; (ненулевые элементы &amp;lt;math&amp;gt;r_{ij}&amp;lt;/math&amp;gt; в последовательном варианте хранятся в элементах исходной матрицы &amp;lt;math&amp;gt;a_{ij}&amp;lt;/math&amp;gt;), унитарная (ортогональная) матрица Q - как произведение матриц Хаусхолдера (отражения) (их вектора нормалей к плоскостям отражения в последовательном варианте хранятся в поддиагональных элементах исходной матрицы &amp;lt;math&amp;gt;a_{ij}&amp;lt;/math&amp;gt; и в одном дополнительном столбце размерности n).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;(n^2+3n)/2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Соотношение последовательной и параллельной сложности, как хорошо видно, является &#039;&#039;линейным&#039;&#039;, что даёт определённый стимул для распараллеливания. Однако у наискорейшей ЯПФ ширина &#039;&#039;квадратична&#039;&#039;, что указывает на дисбаланс между загруженностями устройств при попытке её реально запрограммировать. Поэтому более практично даже при хорошей (быстрой) вычислительной сети оставить количество устройств (например, узлов кластера) &#039;&#039;линейным&#039;&#039; по размеру матрицы, что удвоит критический путь реализуемой ЯПФ. &lt;br /&gt;
&lt;br /&gt;
При этом вычислительная мощность алгоритма, как отношение числа операций к суммарному объему входных и выходных данных, &#039;&#039;линейна&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Алгоритм в рамках выбранной версии полностью детерминирован.&lt;br /&gt;
&lt;br /&gt;
Вычислительная погрешность в методе отражений (Хаусхолдера) растет &#039;&#039;линейно&#039;&#039;, как и в методе Гивенса (вращений).&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
В варианте с кратчайшим критическим путём графа алгоритма (с использованием зависимости между обнуляемым вектором и направляющим вектором отражения) метод Хаусхолдера (отражений) приведения квадратной симметричной вещественной матрицы к трёхдиагональному виду на Фортране 77 можно записать так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;fortran&amp;quot;&amp;gt;&lt;br /&gt;
	DO  I = 1, N-2&lt;br /&gt;
&lt;br /&gt;
            DO K = I, N&lt;br /&gt;
              SX(K)=A(N,I)*A(N,K)&lt;br /&gt;
            END DO&lt;br /&gt;
            DO J = N-1, I+1, -1&lt;br /&gt;
                 SX(I)=SX(I)+A(J,I)*A(J,I)&lt;br /&gt;
            END DO &lt;br /&gt;
            DO K = I+1, N&lt;br /&gt;
              DO J = N-1, K, -1&lt;br /&gt;
                 SX(K)=SX(K)+A(J,I)*A(J,K)&lt;br /&gt;
              END DO&lt;br /&gt;
              DO J = K-1,I+1,-1&lt;br /&gt;
                 SX(K)=SX(K)+A(J,I)*A(K,J)&lt;br /&gt;
              END DO&lt;br /&gt;
            END DO&lt;br /&gt;
&lt;br /&gt;
               ALPHA = SQRT (SX(I))&lt;br /&gt;
               IF (A(I+1,I).NE.0.) THEN&lt;br /&gt;
                  BETA = 1./ALPHA&lt;br /&gt;
                  DO J = I+2, N&lt;br /&gt;
                    A(J,I)=A(J,I)*BETA&lt;br /&gt;
                  END DO&lt;br /&gt;
                  SX(I) = A(I+1,I)*BETA+SIGN(1.,A(I+1,I))                     &lt;br /&gt;
                  A(I+1,I)=ALPHA&lt;br /&gt;
                  G=1./ABS(SX(I)) ! 1/gamma&lt;br /&gt;
                  SX2=0.&lt;br /&gt;
                  DO K = I+2, N&lt;br /&gt;
                     SX(K)=SX(K)*BETA*G+SIGN(A(K,I+1),SX(I))&lt;br /&gt;
                     SX2=SX(K)*A(K,I)+SX2                     &lt;br /&gt;
                  END DO&lt;br /&gt;
                  SX2=G*SX2&lt;br /&gt;
                  DO K = I+2, N&lt;br /&gt;
                     A(K,K) = A(K,K)-2*A(K,I)*SX(K)+SX2*A(K,I)**2&lt;br /&gt;
                     DO J = K+1, N&lt;br /&gt;
                       A (J,K) = A(J,K)-A(J,I)*SX(K)-A(K,I)*SX(I)+SX2*A(J,I)*A(K,I)&lt;br /&gt;
                     END DO&lt;br /&gt;
                  END DO&lt;br /&gt;
               ELSE&lt;br /&gt;
                  IF (ALPHA.NE.0.) THEN&lt;br /&gt;
                     BETA = 1./ALPHA&lt;br /&gt;
                     DO J = I+2, N&lt;br /&gt;
                       A(J,I)=A(J,I)*BETA&lt;br /&gt;
                     END DO&lt;br /&gt;
                     SX(I) = -1. &lt;br /&gt;
                     A(I+1,I)=ALPHA&lt;br /&gt;
                     G=1.! 1/gamma&lt;br /&gt;
                     SX2=0.&lt;br /&gt;
                     DO K = I+2, N&lt;br /&gt;
                        SX(K)=SX(K)*BETA*G+SIGN(A(K,I+1),SX(I))&lt;br /&gt;
                        SX2=SX(K)*A(K,I)+SX2                 &lt;br /&gt;
                     END DO&lt;br /&gt;
                     SX2=G*SX2&lt;br /&gt;
                     DO K = I+2, N  &lt;br /&gt;
                        A(K,K) = A(K,K)-2*A(K,I)*SX(K)+SX2*A(K,I)**2                   &lt;br /&gt;
                        DO J = K+1, N&lt;br /&gt;
                          A (J,K) = A(J,K)-A(J,I)*SX(K)-A(K,I)*SX(I)+SX2*A(J,I)*A(K,I)&lt;br /&gt;
                        END DO&lt;br /&gt;
                     END DO&lt;br /&gt;
                  ELSE&lt;br /&gt;
                     SX(I)=1.&lt;br /&gt;
                  END IF&lt;br /&gt;
               END IF               &lt;br /&gt;
&lt;br /&gt;
		&lt;br /&gt;
	END DO&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Здесь симметричная трёхдиагональная матрица хранится в диагонали и нижней кодиагонали массива A, вектора v - в поддиагональной части массива, за исключением первых их элементов, для которых выделен массив SX.&lt;br /&gt;
&lt;br /&gt;
Обычно же в последовательных версиях коэффициенты модификаций столбцов вычисляются целиком через скалярные произведения после вычислений параметров матрицы отражения. При этом схема чуть проще. Удлиняется критический путь графа, но для последовательных реализаций это неважно.&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
К сожалению, в графе, как видно по рисунку, в наличии пучки рассылок, в них неизбежно часть дуг остаются длинными, что отрицательно влияет на локальность вычислений по пространству.  &lt;br /&gt;
&lt;br /&gt;
==== Локальность реализации алгоритма ====&lt;br /&gt;
===== Структура обращений в память и качественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qtq_1.png|thumb|center|700px|Рисунок 3. Метод Хаусхолдера (отражений) для приведения симметричных матриц к трёхдиагональному виду. Общий профиль обращений в память]]&lt;br /&gt;
&lt;br /&gt;
На рис. 3 представлен профиль обращений в память для реализации метода Хаусхолдера (отражений) для приведения симметричных матриц к трёхдиагональному виду. Данный профиль обладает явной итерационной структурой, при этом видно, что итерации очень похожи. Основное их отличие заключается в наборе используемых данных – на каждой следующей итерации несколько первых элементов отбрасываются их рассмотрения, то есть чем позже итерация, тем меньше данных в ней задействовано. Также можно отметить, что число обращений в каждой последующей итерации немного уменьшается. Для того чтобы проанализировать общий профиль, в таком случае чаще всего достаточно изучить одну итерацию. Рассмотрим самую первую из них более детально.&lt;br /&gt;
&lt;br /&gt;
На рис. 4 показан набор обращений в рамках первой итерации (выделен на рис. 3 зеленым). Его можно разбить на несколько фрагментов и рассмотреть их отдельно. Строение фрагментов 2-5 можно оценить по общему графику для итерации. Фрагмент 2 представляет собой набор последовательных переборов в обратном порядке с небольшим шагом, причем число элементов в переборе постепенно уменьшается. Такое строение характеризуется достаточно высокой пространственной локальностью, поскольку часто происходит обращений к близко расположенным данным, однако низкой временной, поскольку данные повторно не используются. То же самое верно и для фрагмента 4, основное отличие которого заключается в том, что переборы выполняются в прямом порядке. Отметим, что некоторое искривление данного фрагмента связано не со строением самого фрагмента, а с разным числом параллельно выполняющихся обращений (см., например, правую область фрагмента 6), чего не наблюдается при выполнении фрагмента 2.&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qtq_2.png|thumb|center|700px|Рисунок 4. Профиль обращений, одна итерация]]&lt;br /&gt;
&lt;br /&gt;
Далее, фрагменты  3 и 5 состоят из небольшого числа обращений к данным, расположенным далеко друг от друга. Такие фрагменты характеризуются низкой пространственной и временной локальностью.&lt;br /&gt;
&lt;br /&gt;
Оставшиеся фрагменты требуют более детального рассмотрения. Перейдем к изучению фрагмента 1, который целиком представлен на рис. 5. Здесь мы можем видеть несколько этапов, каждый из которых представляет собой последовательный перебор элементов с шагом по памяти 1, причем в некоторых случаях данные много раз используются повторно (особенно это заметно в правой нижней области рисунка, где выполняется множество обращений к одному и тому же элементу). При условии, что в данном фрагменте задействовано всего около 40 элементов, можно говорить, что данный набор обращений обладает высокой пространственной и временной локальностью. &lt;br /&gt;
&lt;br /&gt;
[[file:householder_qtq_3.png|thumb|center|500px|Рисунок 5. Профиль обращений, одна итерация, фрагмент 1]]&lt;br /&gt;
&lt;br /&gt;
Далее рассмотрим детально фрагмент 6 (рис. 6). Здесь можно увидеть, что повторные обращения к данным выполняются не подряд, а через некоторые промежутки, однако число задействованных элементов также очень невелико, так что и в этом случае локальность (и пространственная, и временная) будет высокой.&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qtq_4.png|thumb|center|500px|Рисунок 6. Профиль обращений, одна итерация, фрагмент 6]]&lt;br /&gt;
&lt;br /&gt;
В целом можно сказать, что рассматриваемая итерация обладает достаточно высокой пространственной и временной локальностью. Поскольку остальные итерации устроены подобным образом, данные вывод можно перенести и на общий профиль обращений.&lt;br /&gt;
&lt;br /&gt;
===== Количественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
Оценка выполняется на основе характеристики daps, которая оценивает число выполненных обращений (чтений и записей) в память в секунду. Данная характеристика является аналогом оценки flops применительно к работе с памятью и является в большей степени оценкой производительности взаимодействия с памятью, чем оценкой локальности. Однако она служит хорошим источником информации, в том числе для сравнения с результатами по следующей характеристике cvg.&lt;br /&gt;
&lt;br /&gt;
На рисунке 7 приведены значения daps для реализаций распространенных алгоритмов, отсортированные по возрастанию (чем больше daps, тем в общем случае выше производительность). Можно увидеть, что производительность работы с памятью в данном случае достаточно высока – значение daps лишь немногим уступает тесту Linpack, который обладает высокой эффективностью работы с памятью, и немного превосходит, например, реализацию метода Якоби или метода Гаусса.&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qtq_daps.png|thumb|center|700px|Рисунок 7. Сравнение значений оценки daps]]&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
==== Масштабируемость алгоритма ====&lt;br /&gt;
==== Масштабируемость реализации алгоритма ====&lt;br /&gt;
Проведём исследование масштабируемости вширь реализации согласно [[Scalability methodology|методике]]. Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов-2&amp;quot; [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Набор и границы значений изменяемых [[Глоссарий#Параметры запуска|параметров запуска]] реализации алгоритма: &lt;br /&gt;
&lt;br /&gt;
*	число процессоров [16:256] с шагом n^2;&lt;br /&gt;
*	размер матрицы [1000 : 32000] с шагом 1000.&lt;br /&gt;
&lt;br /&gt;
В результате проведённых экспериментов был получен следующий диапазон [[Глоссарий#Эффективность реализации|эффективности реализации]] алгоритма:&lt;br /&gt;
&lt;br /&gt;
*	минимальная эффективность реализации 7.511e-06%;&lt;br /&gt;
*	максимальная эффективность реализации 0.0746%.&lt;br /&gt;
&lt;br /&gt;
На следующих рисунках приведены графики [[Глоссарий#Производительность|производительности]] и эффективности выбранной реализации алгоритма в процедуре PDGEQRF из Scalapack в зависимости от изменяемых параметров запуска.&lt;br /&gt;
&lt;br /&gt;
[[file:PDGEQRF PERF.png|thumb|center|700px|Рисунок 8. Параллельная реализация циклической редукции. Изменение производительности в зависимости от числа процессоров и размера матрицы.]]&lt;br /&gt;
[[file:PDGEQRF EFF.png|thumb|center|700px|Рисунок 9. Параллельная реализация циклической редукции. Изменение эффективности в зависимости от числа процессоров и размера матрицы.]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
В сравнении с методом Гивенса, который имеет естественное двумерное блочное разбиение на основе точечного метода, метод Хаусхолдера из-за худших характеристик локальности (наличие пучков рассылок) и меньшего количества независимых обобщённых развёрток графа не так хорош для реализаций на системах с распределённой памятью, как для систем с общей памятью. Поэтому на массово параллельных системах с распределённой памятью следует применять метод Хаусхолдера (если уж именно его нужно реализовать) не в точечной версии, а в разрабатываемых исследователями блочных вариантах. Следует отметить, что эти варианты - не блочная нарезка описанного метода, а самостоятельные методы. Особенно их применение рекомендуется в случаях с большой разрежённостью матрицы.&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Большинство пакетов от LINPACKа и LAPACKa до SCALAPACKa используют для QR-разложения матриц именно метод Хаусхолдера, правда, в различных модификациях (обычно с использованием BLAS). Существует большая подборка исследовательских работ по блочным версиям.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Статьи в работе]]&lt;br /&gt;
[[Категория:Разложения матриц]]&lt;/div&gt;</summary>
		<author><name>Teplov</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D0%A5%D0%B0%D1%83%D1%81%D1%85%D0%BE%D0%BB%D0%B4%D0%B5%D1%80%D0%B0_(%D0%BE%D1%82%D1%80%D0%B0%D0%B6%D0%B5%D0%BD%D0%B8%D0%B9)_%D0%B4%D0%BB%D1%8F_%D0%BF%D1%80%D0%B8%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D1%8F_%D1%81%D0%B8%D0%BC%D0%BC%D0%B5%D1%82%D1%80%D0%B8%D1%87%D0%BD%D1%8B%D1%85_%D0%BC%D0%B0%D1%82%D1%80%D0%B8%D1%86_%D0%BA_%D1%82%D1%80%D1%91%D1%85%D0%B4%D0%B8%D0%B0%D0%B3%D0%BE%D0%BD%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%BC%D1%83_%D0%B2%D0%B8%D0%B4%D1%83&amp;diff=21161</id>
		<title>Метод Хаусхолдера (отражений) для приведения симметричных матриц к трёхдиагональному виду</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D0%A5%D0%B0%D1%83%D1%81%D1%85%D0%BE%D0%BB%D0%B4%D0%B5%D1%80%D0%B0_(%D0%BE%D1%82%D1%80%D0%B0%D0%B6%D0%B5%D0%BD%D0%B8%D0%B9)_%D0%B4%D0%BB%D1%8F_%D0%BF%D1%80%D0%B8%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D1%8F_%D1%81%D0%B8%D0%BC%D0%BC%D0%B5%D1%82%D1%80%D0%B8%D1%87%D0%BD%D1%8B%D1%85_%D0%BC%D0%B0%D1%82%D1%80%D0%B8%D1%86_%D0%BA_%D1%82%D1%80%D1%91%D1%85%D0%B4%D0%B8%D0%B0%D0%B3%D0%BE%D0%BD%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%BC%D1%83_%D0%B2%D0%B8%D0%B4%D1%83&amp;diff=21161"/>
		<updated>2016-12-18T10:07:28Z</updated>

		<summary type="html">&lt;p&gt;Teplov: /* Масштабируемость реализации алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{algorithm&lt;br /&gt;
| name              = Приведение симметричной вещественной матрицы к трёхдиагональному виду методом Хаусхолдера (отражений)&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(n^3)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;2n^2+O(n)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;n^2/2&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;(n^2+n)/2&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;(n^2+3n)/2&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
Основные авторы описания: [[Участник:Frolov|А.В.Фролов]]&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Метод Хаусхолдера&#039;&#039;&#039; (в советской математической литературе чаще называется &#039;&#039;&#039;методом отражений&#039;&#039;&#039;) используется для приведения симметричных вещественных матриц к трёхдиагональному виду, или, что то же самое, для разложения &amp;lt;math&amp;gt;A=QTQ^T&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; - ортогональная, &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; — симметричная трёхдиагональная матрица)&amp;lt;ref&amp;gt;В.В.Воеводин, Ю.А.Кузнецов. Матрицы и вычисления. М.: Наука, 1984.&amp;lt;/ref&amp;gt;. При этом матрица &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; хранится и используется не в своём явном виде, а в виде произведения матриц отражения&amp;lt;ref name=&amp;quot;VOLA&amp;quot;&amp;gt;Воеводин В.В. Вычислительные основы линейной алгебры. М.: Наука, 1977.&amp;lt;/ref&amp;gt;. Каждая из матриц отражения может быть определена одним вектором. Это позволяет в классическом исполнении метода отражений хранить результаты разложения на месте матрицы A с использованием одномерного дополнительного массива. &lt;br /&gt;
&lt;br /&gt;
В данной статье рассматривается именно классическое исполнение, в котором не используются приёмы типа сдваивания при вычислениях скалярных произведений.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В методе Хаусхолдера для выполнения &amp;lt;math&amp;gt;QTQ^T&amp;lt;/math&amp;gt;-разложения матрицы используются умножения слева её текущих модификаций на матрицы Хаусхолдера (отражений) с последующим умножением на те же матрицы справа. &lt;br /&gt;
&lt;br /&gt;
{{Шаблон:Матрица отражений}}&lt;br /&gt;
&lt;br /&gt;
На &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м шаге метода с помощью преобразования отражения &amp;quot;убираются&amp;quot; ненулевые поддиагональные элементы, начиная с &amp;lt;math&amp;gt;i+2&amp;lt;/math&amp;gt;-го в &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м столбце. После умножения на эту же матрицу отражения справа автоматически убираются и ненулевые наддиагональные элементы, начиная с &amp;lt;math&amp;gt;i+2&amp;lt;/math&amp;gt;-го в &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-й строке, а полученная модификация снова приобретает симметричный вид. &lt;br /&gt;
&lt;br /&gt;
На каждом из шагов метода матрицу отражений обычно представляют не в стандартном виде, а в виде &amp;lt;math&amp;gt;U=E-\frac{1}{\gamma}vv^*&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; находится через координаты текущего &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; - вектор размерности &amp;lt;math&amp;gt;n+1-i&amp;lt;/math&amp;gt;, составленный из элементов &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца, начиная с &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;(s,s)=0&amp;lt;/math&amp;gt;, то &amp;lt;math&amp;gt;v=e_{i}&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\gamma = \frac{1}{2}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В остальных случаях по алгоритму вычисляется &amp;lt;math&amp;gt;u = \frac{1}{\sqrt{(s,s)}}s&amp;lt;/math&amp;gt;, и далее &amp;lt;math&amp;gt;v_{j}=0&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;j&amp;lt;i&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;v_{j}=u_{j-i+1}&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;j&amp;gt;i&amp;lt;/math&amp;gt;, а &amp;lt;math&amp;gt;v_{i}=1&amp;lt;/math&amp;gt;, если &amp;lt;math&amp;gt;u_{1}=0&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v_{i}=\frac{u_{1}}{|u_{1}|}(1+|u_{1}|)&amp;lt;/math&amp;gt; для остальных значений. При этом &amp;lt;math&amp;gt;\gamma = 1+|u_{1}|=|v_{i}|&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
После вычисления вектора &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; подстолбцы справа от ведущего модифицируются по формулам &amp;lt;math&amp;gt;x&#039;=x-\frac{(x,v)}{\gamma}v&amp;lt;/math&amp;gt;. Потом по аналогичным формулам модифицируются строки ниже ведущей. Благодаря ассоциативности этих операций после вычисления вектора &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; можно сразу выписать формулы модификации всех элементов справа и снизу от ведущих столбца и строки. Оказывается, что если для каждого столбца матрицы &amp;lt;math&amp;gt;x^{(j)}&amp;lt;/math&amp;gt; с номером &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; известно &amp;lt;math&amp;gt;\beta_{j}=\frac{(x^{(j)},v)}{\gamma}&amp;lt;/math&amp;gt;, то для модифицируемого элемента матрицы &amp;lt;math&amp;gt;y&amp;lt;/math&amp;gt; в позиции &amp;lt;math&amp;gt;(i,j)&amp;lt;/math&amp;gt; выполняется модификация &amp;lt;math&amp;gt;y&#039; = y - \beta_{i} v_{i} - \beta_{j} v_{j} - \Delta v_{i} v_{j}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\Delta = \frac{(\beta,v)}{\gamma}&amp;lt;/math&amp;gt;. При этом все эти модификации на каждом конкретном шаге алгоритма для разных пар &amp;lt;math&amp;gt;(i,j)&amp;lt;/math&amp;gt; можно выполнять независимо друг от друга, в том числе и параллельно.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
&lt;br /&gt;
[[File:HausLeftSym1.png|thumb|right|800px|Рисунок 1. Граф первой половины шага алгоритма приведения к трёхдиагональному виду с отображением входных данных. Изображён граф первой половины шага с номером &amp;lt;math&amp;gt;n-4&amp;lt;/math&amp;gt;. Квадраты - результаты выполнения предыдущего шага. Если шаг первый, то это входные данные. Зелёные кружки - операция вида &amp;lt;math&amp;gt;a+b^2&amp;lt;/math&amp;gt;, салатовые - операция вида &amp;lt;math&amp;gt;a+bc&amp;lt;/math&amp;gt;. Синий кружок - вычисление параметров матрицы отражения, светло-красные - вычисление коэффициентов &amp;lt;math&amp;gt;\beta_i&amp;lt;/math&amp;gt;для следующего полушага, жёлтые - вычисление вектора &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt;.]]&lt;br /&gt;
[[File:HousLeftSym2.png|thumb|right|600px|Рисунок 2. Граф второй половины шага алгоритма приведения к трёхдиагональному виду с отображением входных данных. Изображён граф первой половины шага с номером &amp;lt;math&amp;gt;n-3&amp;lt;/math&amp;gt;. Квадраты - результаты выполнения предыдущего шага. Если шаг первый, то это входные данные. Синий, жёлтые и светло-красные кружки выполняются на первом полушаге (см. Рисунок 1). Голубые кружки -  операции &amp;lt;math&amp;gt;a+bc&amp;lt;/math&amp;gt;, зелёное - умножение, чёрные - операции &amp;lt;math&amp;gt;a+bc+de+fce&amp;lt;/math&amp;gt;.]]&lt;br /&gt;
&lt;br /&gt;
Основную часть алгоритма составляют вычисления на каждом шагу скалярных произведений &amp;lt;math&amp;gt;(s,s)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(x,v)&amp;lt;/math&amp;gt; для всех подстолбцов &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt;справа от текущего, а также проводимые над нижним правым квадратом матрицы операции вида &amp;lt;math&amp;gt;y&#039;=y-ab-cd-fbd&amp;lt;/math&amp;gt;, с учётом симметрии матрицы.&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Как уже сказано в описании ядра, основная часть - вычисления на каждом шагу скалярных произведений &amp;lt;math&amp;gt;(s,s)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(x,v)&amp;lt;/math&amp;gt; для всех подстолбцов &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; справа от текущего, а также массовые покомпонентные операции &amp;lt;math&amp;gt;y&#039;=y-ab-cd-fbd&amp;lt;/math&amp;gt;. При этом, однако, строгая последовательность выполнения первых двух подшагов не обязательна, в силу связи получаемых векторов &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; можно одновременно с &amp;lt;math&amp;gt;(s,s)&amp;lt;/math&amp;gt; вычислять и произведения &amp;lt;math&amp;gt;(x,s)&amp;lt;/math&amp;gt; с последующим выражением через них коэффициентов модификации. Это позволяет почти вдвое уменьшать критический путь графа алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Последовательность выполнения алгоритма обычно записывается как последовательное &amp;quot;обнуление&amp;quot; поддиагональных элементов столбцов, начиная с 1-го столбца и заканчивая предпоследним &amp;lt;math&amp;gt;(n-1)&amp;lt;/math&amp;gt;-м.&lt;br /&gt;
&lt;br /&gt;
При этом в каждом &amp;quot;обнуляемом&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-м столбце &amp;quot;обнуляются&amp;quot; сразу все его поддиагональные элементы одновременно, с &amp;lt;math&amp;gt;(i+1)&amp;lt;/math&amp;gt;-го до &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;-го. &lt;br /&gt;
&lt;br /&gt;
Каждое &amp;quot;обнуление&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца состоит из двух шагов: &lt;br /&gt;
а) вычисление параметров матрицы отражения &amp;lt;math&amp;gt;U_{i}&amp;lt;/math&amp;gt; такой, чтобы при умножении на неё слева &amp;quot;обнулились&amp;quot; все поддиагональные его элементы;&lt;br /&gt;
б) одновременное умножение слева матрицы отражения &amp;lt;math&amp;gt;U_{i}&amp;lt;/math&amp;gt; и справа  матрицы отражения &amp;lt;math&amp;gt;U_{i}^T&amp;lt;/math&amp;gt;на текущую версию матрицы.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В последовательной версии основная сложность алгоритма определяется прежде всего вычислениями скалярных произведений векторов, а также массовых модификаций элементов вида &amp;lt;math&amp;gt;y&#039;=y-\alpha v - \beta w - \Delta vw&amp;lt;/math&amp;gt;. Они, если не учитывать возможную разреженность, составляют (в главном члене) по &amp;lt;math&amp;gt;O(n^3)&amp;lt;/math&amp;gt; операций действительного умножения и сложения/вычитания.&lt;br /&gt;
&lt;br /&gt;
При классификации по последовательной сложности, таким образом, метод Хаусхолдера относится к алгоритмам &#039;&#039;с кубической сложностью&#039;&#039;.&lt;br /&gt;
&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;n&amp;lt;/math&amp;gt; к трёхдиагональной методом Хаусхолдера нужно рассмотреть критический путь графа. &lt;br /&gt;
&lt;br /&gt;
Как видно из описания разных вершин, вычисления при &amp;quot;обнулении&amp;quot; &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца параметров отражения и скалярных произведений состоят из основной части - ветви длиной по &amp;lt;math&amp;gt;2(n-i)&amp;lt;/math&amp;gt; умножений и сложений - и коррекции вычислений, которые составляют &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; операций. &lt;br /&gt;
&lt;br /&gt;
Поэтому по грубой (без членов низших порядков) оценке критический путь метода Хаусхолдера будет идти через &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt; умножений и &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt; сложений/вычитаний. &lt;br /&gt;
 &lt;br /&gt;
Поэтому в параллельном варианте, как и в последовательном, основную долю требуемого для выполнения алгоритма времени будут определять операции вида &amp;lt;math&amp;gt;a+bc&amp;lt;/math&amp;gt;.  &lt;br /&gt;
&lt;br /&gt;
При классификации по высоте ЯПФ, таким образом, метод Хаусхолдера относится к алгоритмам &#039;&#039;с квадратичной сложностью&#039;&#039;. При классификации по ширине ЯПФ его сложность будет также &#039;&#039;квадратичной&#039;&#039; (без расширения ряда ярусов, связанных с векторными операциями сложения, пришлось бы увеличить вдвое длину критического пути).&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: плотная симметричная квадратная матрица &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; (элементы &amp;lt;math&amp;gt;a_{ij}&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;(n^2+n)/2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: трёхдиагональная матрица &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; (ненулевые элементы &amp;lt;math&amp;gt;r_{ij}&amp;lt;/math&amp;gt; в последовательном варианте хранятся в элементах исходной матрицы &amp;lt;math&amp;gt;a_{ij}&amp;lt;/math&amp;gt;), унитарная (ортогональная) матрица Q - как произведение матриц Хаусхолдера (отражения) (их вектора нормалей к плоскостям отражения в последовательном варианте хранятся в поддиагональных элементах исходной матрицы &amp;lt;math&amp;gt;a_{ij}&amp;lt;/math&amp;gt; и в одном дополнительном столбце размерности n).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;(n^2+3n)/2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Соотношение последовательной и параллельной сложности, как хорошо видно, является &#039;&#039;линейным&#039;&#039;, что даёт определённый стимул для распараллеливания. Однако у наискорейшей ЯПФ ширина &#039;&#039;квадратична&#039;&#039;, что указывает на дисбаланс между загруженностями устройств при попытке её реально запрограммировать. Поэтому более практично даже при хорошей (быстрой) вычислительной сети оставить количество устройств (например, узлов кластера) &#039;&#039;линейным&#039;&#039; по размеру матрицы, что удвоит критический путь реализуемой ЯПФ. &lt;br /&gt;
&lt;br /&gt;
При этом вычислительная мощность алгоритма, как отношение числа операций к суммарному объему входных и выходных данных, &#039;&#039;линейна&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Алгоритм в рамках выбранной версии полностью детерминирован.&lt;br /&gt;
&lt;br /&gt;
Вычислительная погрешность в методе отражений (Хаусхолдера) растет &#039;&#039;линейно&#039;&#039;, как и в методе Гивенса (вращений).&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
В варианте с кратчайшим критическим путём графа алгоритма (с использованием зависимости между обнуляемым вектором и направляющим вектором отражения) метод Хаусхолдера (отражений) приведения квадратной симметричной вещественной матрицы к трёхдиагональному виду на Фортране 77 можно записать так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;fortran&amp;quot;&amp;gt;&lt;br /&gt;
	DO  I = 1, N-2&lt;br /&gt;
&lt;br /&gt;
            DO K = I, N&lt;br /&gt;
              SX(K)=A(N,I)*A(N,K)&lt;br /&gt;
            END DO&lt;br /&gt;
            DO J = N-1, I+1, -1&lt;br /&gt;
                 SX(I)=SX(I)+A(J,I)*A(J,I)&lt;br /&gt;
            END DO &lt;br /&gt;
            DO K = I+1, N&lt;br /&gt;
              DO J = N-1, K, -1&lt;br /&gt;
                 SX(K)=SX(K)+A(J,I)*A(J,K)&lt;br /&gt;
              END DO&lt;br /&gt;
              DO J = K-1,I+1,-1&lt;br /&gt;
                 SX(K)=SX(K)+A(J,I)*A(K,J)&lt;br /&gt;
              END DO&lt;br /&gt;
            END DO&lt;br /&gt;
&lt;br /&gt;
               ALPHA = SQRT (SX(I))&lt;br /&gt;
               IF (A(I+1,I).NE.0.) THEN&lt;br /&gt;
                  BETA = 1./ALPHA&lt;br /&gt;
                  DO J = I+2, N&lt;br /&gt;
                    A(J,I)=A(J,I)*BETA&lt;br /&gt;
                  END DO&lt;br /&gt;
                  SX(I) = A(I+1,I)*BETA+SIGN(1.,A(I+1,I))                     &lt;br /&gt;
                  A(I+1,I)=ALPHA&lt;br /&gt;
                  G=1./ABS(SX(I)) ! 1/gamma&lt;br /&gt;
                  SX2=0.&lt;br /&gt;
                  DO K = I+2, N&lt;br /&gt;
                     SX(K)=SX(K)*BETA*G+SIGN(A(K,I+1),SX(I))&lt;br /&gt;
                     SX2=SX(K)*A(K,I)+SX2                     &lt;br /&gt;
                  END DO&lt;br /&gt;
                  SX2=G*SX2&lt;br /&gt;
                  DO K = I+2, N&lt;br /&gt;
                     A(K,K) = A(K,K)-2*A(K,I)*SX(K)+SX2*A(K,I)**2&lt;br /&gt;
                     DO J = K+1, N&lt;br /&gt;
                       A (J,K) = A(J,K)-A(J,I)*SX(K)-A(K,I)*SX(I)+SX2*A(J,I)*A(K,I)&lt;br /&gt;
                     END DO&lt;br /&gt;
                  END DO&lt;br /&gt;
               ELSE&lt;br /&gt;
                  IF (ALPHA.NE.0.) THEN&lt;br /&gt;
                     BETA = 1./ALPHA&lt;br /&gt;
                     DO J = I+2, N&lt;br /&gt;
                       A(J,I)=A(J,I)*BETA&lt;br /&gt;
                     END DO&lt;br /&gt;
                     SX(I) = -1. &lt;br /&gt;
                     A(I+1,I)=ALPHA&lt;br /&gt;
                     G=1.! 1/gamma&lt;br /&gt;
                     SX2=0.&lt;br /&gt;
                     DO K = I+2, N&lt;br /&gt;
                        SX(K)=SX(K)*BETA*G+SIGN(A(K,I+1),SX(I))&lt;br /&gt;
                        SX2=SX(K)*A(K,I)+SX2                 &lt;br /&gt;
                     END DO&lt;br /&gt;
                     SX2=G*SX2&lt;br /&gt;
                     DO K = I+2, N  &lt;br /&gt;
                        A(K,K) = A(K,K)-2*A(K,I)*SX(K)+SX2*A(K,I)**2                   &lt;br /&gt;
                        DO J = K+1, N&lt;br /&gt;
                          A (J,K) = A(J,K)-A(J,I)*SX(K)-A(K,I)*SX(I)+SX2*A(J,I)*A(K,I)&lt;br /&gt;
                        END DO&lt;br /&gt;
                     END DO&lt;br /&gt;
                  ELSE&lt;br /&gt;
                     SX(I)=1.&lt;br /&gt;
                  END IF&lt;br /&gt;
               END IF               &lt;br /&gt;
&lt;br /&gt;
		&lt;br /&gt;
	END DO&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Здесь симметричная трёхдиагональная матрица хранится в диагонали и нижней кодиагонали массива A, вектора v - в поддиагональной части массива, за исключением первых их элементов, для которых выделен массив SX.&lt;br /&gt;
&lt;br /&gt;
Обычно же в последовательных версиях коэффициенты модификаций столбцов вычисляются целиком через скалярные произведения после вычислений параметров матрицы отражения. При этом схема чуть проще. Удлиняется критический путь графа, но для последовательных реализаций это неважно.&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
К сожалению, в графе, как видно по рисунку, в наличии пучки рассылок, в них неизбежно часть дуг остаются длинными, что отрицательно влияет на локальность вычислений по пространству.  &lt;br /&gt;
&lt;br /&gt;
==== Локальность реализации алгоритма ====&lt;br /&gt;
===== Структура обращений в память и качественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qtq_1.png|thumb|center|700px|Рисунок 3. Метод Хаусхолдера (отражений) для приведения симметричных матриц к трёхдиагональному виду. Общий профиль обращений в память]]&lt;br /&gt;
&lt;br /&gt;
На рис. 3 представлен профиль обращений в память для реализации метода Хаусхолдера (отражений) для приведения симметричных матриц к трёхдиагональному виду. Данный профиль обладает явной итерационной структурой, при этом видно, что итерации очень похожи. Основное их отличие заключается в наборе используемых данных – на каждой следующей итерации несколько первых элементов отбрасываются их рассмотрения, то есть чем позже итерация, тем меньше данных в ней задействовано. Также можно отметить, что число обращений в каждой последующей итерации немного уменьшается. Для того чтобы проанализировать общий профиль, в таком случае чаще всего достаточно изучить одну итерацию. Рассмотрим самую первую из них более детально.&lt;br /&gt;
&lt;br /&gt;
На рис. 4 показан набор обращений в рамках первой итерации (выделен на рис. 3 зеленым). Его можно разбить на несколько фрагментов и рассмотреть их отдельно. Строение фрагментов 2-5 можно оценить по общему графику для итерации. Фрагмент 2 представляет собой набор последовательных переборов в обратном порядке с небольшим шагом, причем число элементов в переборе постепенно уменьшается. Такое строение характеризуется достаточно высокой пространственной локальностью, поскольку часто происходит обращений к близко расположенным данным, однако низкой временной, поскольку данные повторно не используются. То же самое верно и для фрагмента 4, основное отличие которого заключается в том, что переборы выполняются в прямом порядке. Отметим, что некоторое искривление данного фрагмента связано не со строением самого фрагмента, а с разным числом параллельно выполняющихся обращений (см., например, правую область фрагмента 6), чего не наблюдается при выполнении фрагмента 2.&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qtq_2.png|thumb|center|700px|Рисунок 4. Профиль обращений, одна итерация]]&lt;br /&gt;
&lt;br /&gt;
Далее, фрагменты  3 и 5 состоят из небольшого числа обращений к данным, расположенным далеко друг от друга. Такие фрагменты характеризуются низкой пространственной и временной локальностью.&lt;br /&gt;
&lt;br /&gt;
Оставшиеся фрагменты требуют более детального рассмотрения. Перейдем к изучению фрагмента 1, который целиком представлен на рис. 5. Здесь мы можем видеть несколько этапов, каждый из которых представляет собой последовательный перебор элементов с шагом по памяти 1, причем в некоторых случаях данные много раз используются повторно (особенно это заметно в правой нижней области рисунка, где выполняется множество обращений к одному и тому же элементу). При условии, что в данном фрагменте задействовано всего около 40 элементов, можно говорить, что данный набор обращений обладает высокой пространственной и временной локальностью. &lt;br /&gt;
&lt;br /&gt;
[[file:householder_qtq_3.png|thumb|center|500px|Рисунок 5. Профиль обращений, одна итерация, фрагмент 1]]&lt;br /&gt;
&lt;br /&gt;
Далее рассмотрим детально фрагмент 6 (рис. 6). Здесь можно увидеть, что повторные обращения к данным выполняются не подряд, а через некоторые промежутки, однако число задействованных элементов также очень невелико, так что и в этом случае локальность (и пространственная, и временная) будет высокой.&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qtq_4.png|thumb|center|500px|Рисунок 6. Профиль обращений, одна итерация, фрагмент 6]]&lt;br /&gt;
&lt;br /&gt;
В целом можно сказать, что рассматриваемая итерация обладает достаточно высокой пространственной и временной локальностью. Поскольку остальные итерации устроены подобным образом, данные вывод можно перенести и на общий профиль обращений.&lt;br /&gt;
&lt;br /&gt;
===== Количественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
Оценка выполняется на основе характеристики daps, которая оценивает число выполненных обращений (чтений и записей) в память в секунду. Данная характеристика является аналогом оценки flops применительно к работе с памятью и является в большей степени оценкой производительности взаимодействия с памятью, чем оценкой локальности. Однако она служит хорошим источником информации, в том числе для сравнения с результатами по следующей характеристике cvg.&lt;br /&gt;
&lt;br /&gt;
На рисунке 7 приведены значения daps для реализаций распространенных алгоритмов, отсортированные по возрастанию (чем больше daps, тем в общем случае выше производительность). Можно увидеть, что производительность работы с памятью в данном случае достаточно высока – значение daps лишь немногим уступает тесту Linpack, который обладает высокой эффективностью работы с памятью, и немного превосходит, например, реализацию метода Якоби или метода Гаусса.&lt;br /&gt;
&lt;br /&gt;
[[file:householder_qtq_daps.png|thumb|center|700px|Рисунок 7. Сравнение значений оценки daps]]&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
==== Масштабируемость алгоритма ====&lt;br /&gt;
==== Масштабируемость реализации алгоритма ====&lt;br /&gt;
Проведём исследование масштабируемости вширь реализации согласно [[Scalability methodology|методике]]. Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов-2&amp;quot; [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
Набор и границы значений изменяемых [[Глоссарий#Параметры запуска|параметров запуска]] реализации алгоритма: &lt;br /&gt;
&lt;br /&gt;
*	число процессоров [16:256] с шагом n^2;&lt;br /&gt;
*	размер матрицы [1000 : 32000] с шагом 1000.&lt;br /&gt;
&lt;br /&gt;
В результате проведённых экспериментов был получен следующий диапазон [[Глоссарий#Эффективность реализации|эффективности реализации]] алгоритма:&lt;br /&gt;
&lt;br /&gt;
*	минимальная эффективность реализации 7.511e-06%;&lt;br /&gt;
*	максимальная эффективность реализации 0.0746%.&lt;br /&gt;
&lt;br /&gt;
На следующих рисунках приведены графики [[Глоссарий#Производительность|производительности]] и эффективности выбранной реализации DQDS в зависимости от изменяемых параметров запуска.&lt;br /&gt;
&lt;br /&gt;
[[file:PDGEQRF PERF.png|thumb|center|700px|Рисунок 8. Параллельная реализация циклической редукции. Изменение производительности в зависимости от числа процессоров и размера матрицы.]]&lt;br /&gt;
[[file:PDGEQRF EFF.png|thumb|center|700px|Рисунок 9. Параллельная реализация циклической редукции. Изменение эффективности в зависимости от числа процессоров и размера матрицы.]]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
В сравнении с методом Гивенса, который имеет естественное двумерное блочное разбиение на основе точечного метода, метод Хаусхолдера из-за худших характеристик локальности (наличие пучков рассылок) и меньшего количества независимых обобщённых развёрток графа не так хорош для реализаций на системах с распределённой памятью, как для систем с общей памятью. Поэтому на массово параллельных системах с распределённой памятью следует применять метод Хаусхолдера (если уж именно его нужно реализовать) не в точечной версии, а в разрабатываемых исследователями блочных вариантах. Следует отметить, что эти варианты - не блочная нарезка описанного метода, а самостоятельные методы. Особенно их применение рекомендуется в случаях с большой разрежённостью матрицы.&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Большинство пакетов от LINPACKа и LAPACKa до SCALAPACKa используют для QR-разложения матриц именно метод Хаусхолдера, правда, в различных модификациях (обычно с использованием BLAS). Существует большая подборка исследовательских работ по блочным версиям.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Статьи в работе]]&lt;br /&gt;
[[Категория:Разложения матриц]]&lt;/div&gt;</summary>
		<author><name>Teplov</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:Viktorrulev/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D1%83%D1%81%D1%82%D0%BE%D0%B9%D1%87%D0%B8%D0%B2%D0%BE%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8c%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%81%D0%B2%D1%8F%D0%B7%D0%B5%D0%B9&amp;diff=20243</id>
		<title>Участник:Viktorrulev/Алгоритм устойчивой кластеризации с иcпользованием связей</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:Viktorrulev/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D1%83%D1%81%D1%82%D0%BE%D0%B9%D1%87%D0%B8%D0%B2%D0%BE%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8c%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%81%D0%B2%D1%8F%D0%B7%D0%B5%D0%B9&amp;diff=20243"/>
		<updated>2016-12-07T12:49:21Z</updated>

		<summary type="html">&lt;p&gt;Teplov: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment|Teplov}}&lt;br /&gt;
&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Алгоритм устойчивой кластеризации с иcпользованием связей&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(M^2 + M^2m_{nbr} + M^{2}logM)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O(MlogM)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;O(M^2)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;M \times N&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Автор описания: [[Участник:viktorrulev|В.А. Рулев]].&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Кластеризация (кластерный анализ) — задача разбиения заданной выборки объектов на непересекающиеся подмножества, называемые кластерами, так, чтобы каждый кластер состоял из схожих объектов, а объекты разных кластеров существенно отличались. Каждый объект из выборки характеризуется рядом признаков, которые могут быть вещественными, целочисленными, категорийными (то есть принимающими значения из какого-либо множества) и другими. Множество значений, которые может принимать признак, называется доменом этого признака. Так, например, у объекта &#039;&#039;&#039;кошка&#039;&#039;&#039; может быть категорийный признак &#039;&#039;&#039;порода&#039;&#039;&#039;, доменом которого является множество &#039;&#039;&#039;[персидская, бенгальская, сфинкс, мейн-кун, ...]&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм устойчивой кластеризации с иcпользованием связей (robust clustering using links, ROCK)&#039;&#039;&#039; был предложен в 2000 году Sudipto Guha (Stanford University), Rajeev Rastogi (Bell Laboratories) и Kyuseok Shim (Bell Laboratories) &amp;lt;ref&amp;gt;Sudipto Guha, Rajeev Rastogi, Kyuseok Shim ROCK: A robust clustering algorithm for categorical attributes. 2000. Information Systems. Vol 25, Issue 5, Pages 345-366&amp;lt;/ref&amp;gt; для кластеризации объектов с категорийными признаками.&lt;br /&gt;
&lt;br /&gt;
Алгоритм устойчивой кластеризации с использованием связей предназначен для работы с объектами типа &amp;quot;транзакция&amp;quot; (&amp;quot;покупательская корзина&amp;quot;). Транзакция представляет собой множество товаров, приобретенных покупателем у поставщика. Каждому товару, который есть в наличии у поставщика, в транзакции соответствует отдельный признак, который принимает значение &#039;&#039;&#039;true&#039;&#039;&#039;, если товар присутствует в транзакции, и &#039;&#039;&#039;false&#039;&#039;&#039;, если товар в транзакции отсутствует.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В ходе кластеризации &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; имеющихся транзакций &amp;lt;math&amp;gt;P=\{p_1,...,p_M\}&amp;lt;/math&amp;gt; (каждая транзакция имеет &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; признаков) должны быть разделены на &amp;lt;math&amp;gt;K&amp;lt;/math&amp;gt; непересекающихся подмножеств (кластеров) &amp;lt;math&amp;gt;C_1, ..., C_K&amp;lt;/math&amp;gt; таким образом, чтобы полученные кластеры максимизировали некоторую критериальную функцию &amp;lt;math&amp;gt;E(C_1, ..., C_K)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Будет называть две транзакции &amp;lt;math&amp;gt;p_1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;p_2&amp;lt;/math&amp;gt; &#039;&#039;&#039;соседями&#039;&#039;&#039;, если мера сходства &amp;lt;math&amp;gt;neighbour(p_1,p_2)&amp;lt;/math&amp;gt; этих транзакций больше некоторого заранее заданного порогового значения &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt; В качестве меры сходства в алгоритме устойчивой кластеризации с использованием связей используется основанная на коэффициенте Жаккара мера сходства &amp;lt;math&amp;gt;sim(p_1,p_2)&amp;lt;/math&amp;gt;. В ней &amp;lt;math&amp;gt;N(C_i)^{1+2f(\theta)}&amp;lt;/math&amp;gt; имеет смысл ожидаемого (среднего) количества связей внутри одного кластера.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Количеством связей&#039;&#039;&#039; &amp;lt;math&amp;gt;link(p_1,p_2)&amp;lt;/math&amp;gt; между двумя транзакциями &amp;lt;math&amp;gt;p_1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;p_2&amp;lt;/math&amp;gt; будем называть количество общих соседей этих транзакций.&lt;br /&gt;
&lt;br /&gt;
Для того, чтобы сформировать кластеры, удовлетворяющие поставленному условию, необходимо последовательно объединять среди имеющихся кластеров такие пары кластеров, для которых метрика &amp;lt;math&amp;gt;goodness(C_i,C_j)&amp;lt;/math&amp;gt; достигает максимального значения среди всех пар кластеров. На начальном этапе каждая транзакция считается отдельным кластером.&lt;br /&gt;
&lt;br /&gt;
Формулы метода:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
neighbour(p_1,p_2)=(sim(p_1,p_2)&amp;gt;\theta)&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
sim(p_1,p_2)=\frac{N(p_1 \cap p_2)}{N(p_1 \cup p_2)}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
link(p_1,p_2)=N \Big( \{ p \in P | sim(p_1,p) &amp;lt; \theta \} \cap \{ p \in P | sim(p_2,p) &amp;lt; \theta \} \Big)&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
E(C_1,...,C_K)=\sum_{i=1}^{K}N(C_i) \ast  \sum_{p_q,p_r \in C_i}\frac{link(p_q,p_r)}{N(C_i)^{1+2f(\theta)}}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
f(\theta)=\frac {1-\theta}{1+\theta}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
goodness(C_i,C_j)=\frac {\sum_{p_r \in C_i, p_q \in C_j}{link(p_r,p_q)}} {(N(C_i) + N(C_j))^{1+2f(\theta)} - N(C_i)^{1+2f(\theta)} - N(C_j)^{1+2f(\theta)}}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Вычислительное ядро алгоритма можно разделить на два основных этапа:&lt;br /&gt;
&lt;br /&gt;
# Вычисление количества связей между каждой парой транзакций (&amp;lt;math&amp;gt;\frac {M \times M} {2}&amp;lt;/math&amp;gt; вычислений количества связей)&lt;br /&gt;
# Последовательные объединения пар наиболее подходящих кластеров (&amp;lt;math&amp;gt;M-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;
&#039;&#039;&#039;Шаг 1:&#039;&#039;&#039; вычисление соседних транзакций.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг 2:&#039;&#039;&#039; вычисление количества связей между всеми транзакциями.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг 3:&#039;&#039;&#039; построение для каждого кластера упорядоченного списка значений метрики goodness со всеми остальными кластерами&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг 4:&#039;&#039;&#039; построение общего упорядоченного списка значений метрики goodness&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг 5 (повторяется то тех пор, пока количество кластеров не достигнет желаемого)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг 5.1:&#039;&#039;&#039; объединение двух кластеров с наилучшим goodness&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг 5.2:&#039;&#039;&#039; обновление списков кластеров&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Ниже приведен псевдокод схемы реализации последовательного алгоритма:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
procedure compute_links(P)&lt;br /&gt;
begin&lt;br /&gt;
  for each p[i], p[j] in P do&lt;br /&gt;
    if sim(p[i], p[j]) &amp;gt; theta&lt;br /&gt;
      is_neighbour[i, j] = true&lt;br /&gt;
    else&lt;br /&gt;
      is_neighbour[i, j] = false&lt;br /&gt;
&lt;br /&gt;
  links[i, j] = 0&lt;br /&gt;
  for each p[i], p[j] in P do&lt;br /&gt;
    for p[k] in P do&lt;br /&gt;
      if is_neighbour[i, k] &amp;amp;&amp;amp; is_neighbour[j, k]&lt;br /&gt;
        links[i, j] = links[i, j] + 1&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
procedure cluster(P, k)&lt;br /&gt;
begin&lt;br /&gt;
  links := compute_links(P)&lt;br /&gt;
&lt;br /&gt;
  for each p in P do&lt;br /&gt;
    q[p] := build_local_list(links, p)&lt;br /&gt;
  Q := build global_list(P, q)&lt;br /&gt;
&lt;br /&gt;
  while size(Q) &amp;gt; k do&lt;br /&gt;
  begin&lt;br /&gt;
    u := extract_max(Q)&lt;br /&gt;
    v := extract_max(q[u])&lt;br /&gt;
    w := merge(u, v)&lt;br /&gt;
    delete(Q, v)&lt;br /&gt;
&lt;br /&gt;
    for each x in (q[u] or q[v]) do&lt;br /&gt;
    begin&lt;br /&gt;
      delete(q[x], u);&lt;br /&gt;
      delete(q[x], v)&lt;br /&gt;
      insert(q[x], w, goodness(x, w))&lt;br /&gt;
      insert(q[w], x, goodness(x, w))&lt;br /&gt;
      update(Q, x, q[x])&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    insert(Q, w, q[w])&lt;br /&gt;
    deallocate(q[u])&lt;br /&gt;
    deallocate(q[v])&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Рассмотрим сложности шагов алгоритма из раздела 1.4.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг 1:&#039;&#039;&#039; выполняется с помощью двух вложенных циклов и содержит &amp;lt;math&amp;gt;O(M^2)&amp;lt;/math&amp;gt; операций&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг 2:&#039;&#039;&#039; задача сводится к умножению полученной на предыдущем шаге матрицы соседства на саму себя. Это действие быть выполнено с помощью наивного метода за &amp;lt;math&amp;gt;O(M^3)&amp;lt;/math&amp;gt; операций. С помощью алгоритма Копперсмита — Винограда это можно сделать за &amp;lt;math&amp;gt;O(M^{2.37})&amp;lt;/math&amp;gt; операций.&amp;lt;ref&amp;gt;Donald Coppersmith and Shmuel Winograd. Matrix multiplication via arithmetic progressions. In Proc. of the 19th Annual ACM Symposium on Theory of Computing. 1987.&amp;lt;/ref&amp;gt; В случае, если среднее количество соседей каждой транзакции значительно меньше общего количества транзакций, связи могут быть вычислены за &amp;lt;math&amp;gt;O(M^2m_{nbr})&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;m_{nbr}&amp;lt;/math&amp;gt; - среднее количество соседей транзакции.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг 3:&#039;&#039;&#039; имеет сложность &amp;lt;math&amp;gt;O(M)&amp;lt;/math&amp;gt;.&amp;lt;ref&amp;gt;Thomas H. Cormen, Charles E. Leiserson, and Ronald L. Rivest. Introduction to&lt;br /&gt;
Algorithms. The MIT Press, Massachusetts, 1990.&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг 4:&#039;&#039;&#039; имеет такую же вычислительную сложность, как и предыдущий шаг.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг 5:&#039;&#039;&#039; вычислительная сложность каждой итерации цикла составляет &amp;lt;math&amp;gt;O(MlogM)&amp;lt;/math&amp;gt;, так как в худшем случае в ходе итерации выполняется &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; вставок элемента в упорядоченный список. Желаемое количество кластеров &amp;lt;math&amp;gt;K&amp;lt;/math&amp;gt; мало по сравнению с &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt;, следовательно можно считать, что нужно выполнить &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; итераций цикла. Таким образом, общая вычислительная сложность цикла равна &amp;lt;math&amp;gt;O(M^{2}logM)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Таким образом, общую последовательную сложность алгоритма в общем случае можно оценить, как&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;O(M^2 + M^{2.37} + M^{2}logM)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
или в случае небольшого количества соседних транзакций, как&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;O(M^2 + M^2m_{nbr} + M^{2}logM)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Информационный граф перемножения плотных матриц с подробным описанием представлен в статье [[Перемножение_плотных_неособенных_матриц_(последовательный_вещественный_вариант)|перемножение плотных матриц]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Ниже представлен информационный граф наиболее интересного для рассмотрения этапа алгоритма устойчивой кластеризации с использованием связей - тела цикла (полупрозрачным показана следующая итерация цикла) при &amp;lt;math&amp;gt;n=5&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[file:Rock-graph.png|thumb|center|1000px|Рисунок 1. Граф тела цикла; merge - объединение кластеров, insert - вставка goodness с новым кластером в упорядоченные списки соответствующих кластеров.]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг 1:&#039;&#039;&#039; каждая итерация цикла может быть выполнена независимо от всех остальных. Таким образом, в этом случае ширина ЯПФ равна &amp;lt;math&amp;gt;O(M^2)&amp;lt;/math&amp;gt;, а высота - &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг 2:&#039;&#039;&#039; высота ЯПФ равна &amp;lt;math&amp;gt;O(M)&amp;lt;/math&amp;gt;, а ширина - &amp;lt;math&amp;gt;O(M^2)&amp;lt;/math&amp;gt; (&#039;&#039;см. [[Перемножение_плотных_неособенных_матриц_(последовательный_вещественный_вариант)|перемножение плотных матриц]]&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг 3:&#039;&#039;&#039; имеет небольшую асимптотическую сложность, может быть выполнен последовательно.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг 4:&#039;&#039;&#039; аналогично шагу 3.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг 5:&#039;&#039;&#039; каждая итерация цикла может быть выполнена только после полного завершения предыдущей, а внутри каждой отдельной итерации все вставки могут быть выполнены независимо друг от друга. Следовательно ширина ЯПФ в этом случае равна &amp;lt;math&amp;gt;O(M)&amp;lt;/math&amp;gt;, а высота ЯПФ равна &amp;lt;math&amp;gt;O(MlogM)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Таким образом, высота ЯПФ зависит от размерности задачи как &amp;lt;math&amp;gt;O(MlogM)&amp;lt;/math&amp;gt;, а ширина ЯПФ - как &amp;lt;math&amp;gt;O(M^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: информация об &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; транзакциях, каждая их которых имеет &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; бинарных атрибутов.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;M \times N&amp;lt;/math&amp;gt; бинарных переменных. В силу того, что обычно в большинстве случаев лишь небольшое количество атрибутов транзакции имеет значение &amp;quot;1&amp;quot;, каждую транзакцию можно хранить в виде списка номеров атрибутов, значения которых равны &amp;quot;1&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: &amp;lt;math&amp;gt;K&amp;lt;/math&amp;gt; кластеров.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; индексов транзакций, распределенных в &amp;lt;math&amp;gt;K&amp;lt;/math&amp;gt; списков.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Вычислительная сложность последовательного алгоритма в общем случае равна &amp;lt;math&amp;gt;O(M^{2.37})&amp;lt;/math&amp;gt;, а в случае небольшого количества соседних транзакций у каждой транзакции сложность равна &amp;lt;math&amp;gt;O(M^{2})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Вычислительная мощность алгоритма (соотношение количества операций к объему входных и выходных данных) в общем случае равна O(M^{1.37}), в а случае небольшого количества соседних транзакций (то есть в том случае, для которого и разрабатывался алгоритм) вычислительная мощность линейна.&lt;br /&gt;
&lt;br /&gt;
Соотношение последовательной и параллельной сложности в случае неограниченных ресурсов, как показано выше, является квадратичным.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
Проведём исследование масштабируемости параллельной реализации алгоритма устройчивой кластеризации с использованием связей, выполненной с использованием OpenMP. Исследование проводилось на персональном компьютере HP Pavilion dv6 (процессор Intel Core i7 3610QM 2300 МГц, 8 логических ядер).&lt;br /&gt;
&lt;br /&gt;
Набор и границы значений изменяемых [[Глоссарий#Параметры запуска|параметров запуска]] реализации алгоритма: &lt;br /&gt;
&lt;br /&gt;
*	число нитей [1 : 8] с шагом 1;&lt;br /&gt;
*	количество точек [500 : 1500] с шагом 100.&lt;br /&gt;
&lt;br /&gt;
На следующих рисунках приведены графики времени выполнения выбранной реализации разложения Холецкого в зависимости от изменяемых параметров запуска.&lt;br /&gt;
&lt;br /&gt;
[[file:ROCK results.png|thumb|center|700px|Рисунок 2. Параллельная реализация алгоритма устойчивой кластеризации с использованием связей. Изменение времени выполнения кластеризации в зависимости от количества точек и от количества параллельных нитей.]] &lt;br /&gt;
&lt;br /&gt;
&#039;&#039; &#039;&#039;&#039;Для проверяющего&#039;&#039;&#039;: я сделал OpenMP и MPI реализации этого алгоритма, но при запуске на Ломоносове они не дают никакого ускорения в зависимости от количества нитей/процессов. Я могу предоставить графики, показывающие это. Также я готов предоставить исходники обеих программ, но не могу выложить их на git.algowiki-project.org, потому что мне не приходит письмо с подтверждением регистрации.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм устойчивой кластеризации с иcпользованием связей реализован в пакете CBA языка R.&amp;lt;ref&amp;gt;https://en.wikibooks.org/wiki/Data_Mining_Algorithms_In_R/Clustering/RockCluster&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references \&amp;gt;&lt;/div&gt;</summary>
		<author><name>Teplov</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A1%D0%B0%D0%BC%D0%BE%D0%BE%D1%80%D0%B3%D0%B0%D0%BD%D0%B8%D0%B7%D1%83%D1%8E%D1%89%D0%B0%D1%8F%D1%81%D1%8F_%D0%BA%D0%B0%D1%80%D1%82%D0%B0_%D0%9A%D0%BE%D1%85%D0%BE%D0%BD%D0%B5%D0%BD%D0%B0_(%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8)&amp;diff=20242</id>
		<title>Самоорганизующаяся карта Кохонена (алгоритм кластеризации)</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A1%D0%B0%D0%BC%D0%BE%D0%BE%D1%80%D0%B3%D0%B0%D0%BD%D0%B8%D0%B7%D1%83%D1%8E%D1%89%D0%B0%D1%8F%D1%81%D1%8F_%D0%BA%D0%B0%D1%80%D1%82%D0%B0_%D0%9A%D0%BE%D1%85%D0%BE%D0%BD%D0%B5%D0%BD%D0%B0_(%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8)&amp;diff=20242"/>
		<updated>2016-12-07T12:35:52Z</updated>

		<summary type="html">&lt;p&gt;Teplov: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{assignment|Teplov}}&lt;br /&gt;
Авторы проекта: &amp;lt;b&amp;gt;[[Участник:Eugene|Быковец Евгений Владимирович (620 группа)]]&amp;lt;/b&amp;gt; и &amp;lt;b&amp;gt;[[Участник:Crowslord|Ворона Игорь Игоревич (620 группа)]]&amp;lt;/b&amp;gt;&lt;br /&gt;
Быковец Е.В.: 1.1,1.2,1.3,1.4,1.5,2.7&lt;br /&gt;
Ворона И.И.: 1.6,1.7,1.8,1.9,1.10,3.0&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Алгоритм кластеризации, основанный на самоорганизующихся сетях Кохонена&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(TMn) + O(M^2)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;O(nN)&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;O(nM)&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;
Cамоорганизующаяся карта Кохонена (Self-Organizing Map, SOM) представляет из себя вычислительный метод, предназначенный для задач, в первую очередь, кластеризации и визуализации, а также анализа данных из пространств высокой размерности (иначе, многомерных данных), полученных экспериментально. Методы был предложен Туево Кохоненом (1982). Прародителями модели самоорганизующейся сети Кохонена были ранние нейросетевые модели (в частности, модель ассоциативной памяти и модель адаптивного обучения). &lt;br /&gt;
&lt;br /&gt;
Целью применения данного метода является поиск скрытых закономерностей в данных, основываясь на снижении размерности исходного пространства в пространство меньшей размерности (на практике чаще всего используется двумерное, по причине, в частности, удобной визуализации). При этом топология исходного пространства остается той же самой. В результате обучения данной модели получается решетка, состоящая из обученных нейронов, она же и называется &amp;quot;картой&amp;quot; исходного пространства.&lt;br /&gt;
&lt;br /&gt;
Архитектура самоорганизующейся карты Кохонена следующая: имеется два слоя - входной слой (распределительный) нейроноы и выходной слой (слой Кохонена) нейронов, при этом нейроны второго слоя расположены в виде двумерной решетки (обычно сетка либо квадратная, либо шестиугольная, об этом далее), так, что каждый нейрон из первого слоя соединен с каждым нейроном второго слоя (см. рис. 1) [[Файл:General Kohonen.png|thumb|200px|right|Рис. 1. Общая структура карты Кохонена]]Количество нейронов входного слоя равно размерности исходного пространства. Чаще нейроны слоя Кохонена принято называть кластерными элементами. Более точно, их количество определяет количество кластеров, на которые карта может разбить данные, которые поданы на распределительный слой. Чем больше кластерных элементов, тем более гранулярная кластеризация. Как уже было сказано, топология слоя Кохонена может быть представлена либо  виде четырехугольной сетки, либо в виде шестиугольной сетки. Второй вариант более привлекателен в силу того, что расстояния для каждого нерона до каждого соседнего с ним нейрона одинаковые. Более наглядно это представлено на рисунках (cм. рис. 2а, рис. 2б) [[Файл:quadr.jpg|thumb|200px|right|Рис. 2а.Четырехугольная решетка слоя Кохонена]] [[Файл:hex.png|thumb|200px|right|Рис. 2б.Шестиугольная решетка слоя Кохонена]]&lt;br /&gt;
&lt;br /&gt;
Каждый нейрон выходного слоя определяется вектором весов (размерность вектора - есть разность входного пространства) и упорядоченной парой : &amp;lt;math&amp;gt;(x,y)&amp;lt;/math&amp;gt;, определяющую позицию нейрона на карте Кохонена. &lt;br /&gt;
&lt;br /&gt;
Принцип обучения сети заключается в следующем:  для каждого входного вектора определяется нейрон-победитель из слоя Кохонена, после чего у нейрона-победителя и некоторого количества его соседей корректируются веса. Процедура повторяется для каждого входного вектора из обучающей выборки.&lt;br /&gt;
&lt;br /&gt;
Сеть, с предложенной архитектурой, обучается на обучающей выборке, обучение заключается в корректировке весов нейронов в слое Кохонена. После того, как сеть обучена она начинает свою работу, относя каждый нейрон из тестовой выборки к тому или иному кластеру.&lt;br /&gt;
&lt;br /&gt;
== Математическое описание алгоритма ==&lt;br /&gt;
=== Используемые обозначения ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; - номер итерации,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; - размерность исходного пространства,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \chi \subset R^n&amp;lt;/math&amp;gt; - обучающая выборка,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \Phi \subset R^n &amp;lt;/math&amp;gt; - тестовая выборка,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; N = |\chi|&amp;lt;/math&amp;gt;  - размер обучающей выборки,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; K = |\Phi|&amp;lt;/math&amp;gt;  - размер тестовой выборки,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; a &amp;lt;/math&amp;gt; -  высота слоя Кохонена,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; b &amp;lt;/math&amp;gt; - ширина слоя Кохонена,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; M = a \times b &amp;lt;/math&amp;gt; - общее количество нейронов в слое Кохонена,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; ne_j, j=1,...M &amp;lt;/math&amp;gt; - нейрон с индексом &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\vec{x}^{(i)}(t) = (x_1^{(i)}(t),...,x_n^{(i)}(t)), i=1,..,N&amp;lt;/math&amp;gt; - &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-й вектор, подлежайщий кластеризации,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\vec{w}^{(j)}(t) = (w_1^{(j)}(t),...,w_n^{(j)}(t)), j = 1,..,M&amp;lt;/math&amp;gt; - вектор весовых коэффициентов для &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;-го нейрона,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; r_k&amp;lt;/math&amp;gt; - положение &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;-го нейрона в топологии карты, то есть &amp;lt;math&amp;gt;(i_k,j_k)&amp;lt;/math&amp;gt;, &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; h_{ij}(t)&amp;lt;/math&amp;gt; - функция соседства между нейронами с индексами &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; на итерации t,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; h(d,t) &amp;lt;/math&amp;gt; - функция от расстояния между нейронами,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \alpha(t)&amp;lt;/math&amp;gt; - скорость обучения сети,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \sigma(t) &amp;lt;/math&amp;gt; - функция, уменьшающая количество соседей с при увеличении итерации (монотонно убывающий),&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; T &amp;lt;/math&amp;gt; - максимальное число итераций алгоритма обучения - может быть задано фиксировано, либо же равно мощности обучающей выборки, то есть  &amp;lt;math&amp;gt; N &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Алгоритм обучения ===&lt;br /&gt;
&lt;br /&gt;
Шаг №0. Определить значение параметров алгоритма: используемые метрики, функции &amp;lt;math&amp;gt;\alpha(t) &amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\sigma(t) &amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;t:= 0 &amp;lt;/math&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
Шаг №1. Инициализировать векторы начальных весов нейронов &amp;lt;math&amp;gt; \vec{w}^{(j)}(0) \forall j=1,..,M&amp;lt;/math&amp;gt;. Подробнее о стратегии начальной инициализации далее.&lt;br /&gt;
&lt;br /&gt;
шаг №2. Если есть еще элементы в обучающей выборке, вытащить случайным образом (например, индекс &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; - может быть реализацией случайной величины  из равномерного дискретного распределения на &amp;lt;math&amp;gt; [0,N] &amp;lt;/math&amp;gt;) входной вектор &amp;lt;math&amp;gt;\vec{x}^{(i)}(t), N:=N-1, \chi = \chi \setminus \vec{x}^{(i)}(t)&amp;lt;/math&amp;gt;, в противном случае алгоритм завершает свою работу&lt;br /&gt;
&lt;br /&gt;
шаг №3. Для &amp;lt;math&amp;gt;\vec{x}^{(i)}(t)&amp;lt;/math&amp;gt; и  &amp;lt;math&amp;gt; \vec{w}^{(j)}(t) \forall j=1,..,M&amp;lt;/math&amp;gt; найти &amp;lt;math&amp;gt; = \rho(\vec{x}^{(i)}(t),  \vec{w}^{(j)}(t)) =  || \vec{x}^{(i)}(t) -  \vec{w}^{(j)}(t)||^2 &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
шаг №4.  Находим нейрон-победитель &amp;lt;math&amp;gt;ne_c&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt; c = \arg \min_{\forall j \in \{1, \dots, M\}} \rho(\vec{x}^{(i)}(t), \vec{w}^{(j)}(t))&amp;lt;/math&amp;gt;, который лежит ближе к текущему объекту &amp;lt;math&amp;gt;\vec{x}^{(i)}(t)&amp;lt;/math&amp;gt; по рассматриваемой метрике &amp;lt;math&amp;gt; || \circ || &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
шаг №5. Для всех нейронов &amp;lt;math&amp;gt; ne_{j} &amp;lt;/math&amp;gt;  выполнить пересчет весовых векторов:&lt;br /&gt;
&amp;lt;math&amp;gt; \vec{w}^{(j)}(t+1) := \vec{w}^{(j)}(t) +\alpha(t)h_{ci}(t)[\vec{x}^{(i)}(t) -  \vec{w}^{(j)}(t)]&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
шаг №6. &amp;lt;math&amp;gt;t := t+1 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
шаг №7. Перейти к шагу 2.&lt;br /&gt;
&lt;br /&gt;
=== Настройка алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В этом разделе опишем более подробно настраиваемые аспекты алгоритма.&lt;br /&gt;
&lt;br /&gt;
==== Выбор начального приближения для &amp;lt;math&amp;gt;\vec{w}^{j}(0) \forall j=1,\dots,M&amp;lt;/math&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Во-первых, инициализацию можно проводить объектами из обучающей выборки, то есть выбрав для каждого весового вектора некоторый элемент из обучающий выборки и установить значения весовых компонент равными компонентам выбранного вектора. Плюсом такого подхода является то, что вектора весов изначально находятся в том регионе, где и объекты из выборки, то есть требуется меньше итераций на этапе настройки.&lt;br /&gt;
&lt;br /&gt;
Во-вторых, инициализацию весов можно проводить какими-то случайными значениями. Например можно взять нескорое распределение, пусть равномерное или нормальное, и для каждой сгенерировать псевдослучайное число из выбранного распределения, то есть &amp;lt;math&amp;gt; w_i^{(j)} \sim \mathbf P &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; \mathbf P &amp;lt;/math&amp;gt; - это заданное распределение. Плюсом данного подхода является простота однако, &amp;quot;настройка&amp;quot; в данном случае может быть долгой. &lt;br /&gt;
&lt;br /&gt;
В-третьих, можно использовать линейную инициализацию, то есть весовые вектора имеют значения векторов из подпространства, натянутого на  два главных собственными вектора пространства исходного набора данных, при этом упорядоченным образом. Это наиболее практичный способ.&lt;br /&gt;
&lt;br /&gt;
==== Выбор скорости обучения сети ====&lt;br /&gt;
&lt;br /&gt;
Функцию скорости обучения сети &amp;lt;math&amp;gt;\alpha(t)&amp;lt;/math&amp;gt; можно выбрать, например, следующим образом:&lt;br /&gt;
&lt;br /&gt;
Самый простой способ: &amp;lt;math&amp;gt;\alpha(t) := \frac{A}{B+t}&amp;lt;/math&amp;gt;, где  &amp;lt;math&amp;gt;A = const, B = const &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Экспоненциальная скорость обучения: &amp;lt;math&amp;gt;\alpha(t) := \alpha_0 e^{ -\frac{t}{\lambda}},  \lambda \in (0, 1), \lambda = const, \alpha_0 = const&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Показательная скорость обучения: &amp;lt;math&amp;gt;\alpha(t) := \lambda^{\frac{t}{T}}, \lambda \in (0, 1), \lambda = const&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Выбор функции соседства между нейронами ====&lt;br /&gt;
&lt;br /&gt;
Обычно полагают  &amp;lt;math&amp;gt; h_{cj}(t) :=  \alpha(t) h(d,t) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; d = || ne_c - ne_j ||, c &amp;lt;/math&amp;gt; -индекс нейрона победителя, при этом:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; h(d,t) = \begin{cases}const, d\leq \sigma(t) \\0, d &amp;gt; \sigma(t) \end{cases}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; h(d,t) = e ^{-\frac{d^2}{2(\sigma(t)^2)}}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\sigma(t)&amp;lt;/math&amp;gt;  - это некоторый сомножитель, уменьшающий количество соседей, которые будут подвержены корректировке весов с увеличением итераций, эта функция монотонно убывает. &lt;br /&gt;
&lt;br /&gt;
Функция &amp;lt;math&amp;gt; \sigma(t) &amp;lt;/math&amp;gt; также является параметром алгоритма. Возможная функциональная зависимость для нее:&lt;br /&gt;
&amp;lt;math&amp;gt;\sigma(t) = \frac{\sigma_0}{1 + \frac{t}{T}}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Стоит отметить, что  имеется много других эвристических мер сходств которые присваивают ненулевые веса внутри некоторого радиуса, это позволяет производить вычисления не для всех соседей.&lt;br /&gt;
&lt;br /&gt;
=== Алгоритм работы карты ===&lt;br /&gt;
&lt;br /&gt;
шаг 0. Пока есть вектора в тестовой выборке извлечь (последовательно), &amp;lt;math&amp;gt;\vec{x}^{(i)}, K:=K-1, \Phi = \Phi \setminus \vec{x}^{(i)}&amp;lt;/math&amp;gt;, в противном случае алгоритм завершает свою работу.&lt;br /&gt;
&lt;br /&gt;
шаг 1.  Для &amp;lt;math&amp;gt;\vec{x}^{(i)}&amp;lt;/math&amp;gt; и  &amp;lt;math&amp;gt; \vec{w}^{(j)} \forall j=1,..,M&amp;lt;/math&amp;gt; найти &amp;lt;math&amp;gt; \rho(\vec{x}^{(i)},  \vec{w}^{(j)}) =  || \vec{x}^{(i)} -  \vec{w}^{(j)}||^2 &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
шаг 2. Находим нейрон-победитель &amp;lt;math&amp;gt;ne_c&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt; c = \arg \min_{\forall j \in \{1, \dots, L\}} \rho(\vec{x}^{(i)}, \vec{w}^{(j)})&amp;lt;/math&amp;gt;, который лежит ближе к текущему объекту &amp;lt;math&amp;gt;\vec{x}^{(i)}&amp;lt;/math&amp;gt; по рассматриваемой метрике &amp;lt;math&amp;gt; || \circ || &amp;lt;/math&amp;gt; и относим &amp;lt;math&amp;gt;\vec{x}^{(i)} &amp;lt;/math&amp;gt; к кластеру, соответствующему нейрону &amp;lt;math&amp;gt;ne_c&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
шаг 3. Переход к шагу 0.&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Вычислительное ядро алгоритма обучения составляет (соответсвенно, пункты 3,4,5 алгоритма обучения):&lt;br /&gt;
&lt;br /&gt;
* подсчет расстояний между входным вектором и всеми нейронами:&lt;br /&gt;
&lt;br /&gt;
Для &amp;lt;math&amp;gt;\vec{x}^{(i)}(t)&amp;lt;/math&amp;gt; и  &amp;lt;math&amp;gt; \vec{w}^{(j)}(t) \forall j=1,..,M&amp;lt;/math&amp;gt; найти &amp;lt;math&amp;gt; = \rho(\vec{x}^{(i)}(t),  \vec{w}^{(j)}(t)) =  || \vec{x}^{(i)}(t) -  \vec{w}^{(j)}(t)||^2 &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* Поиск минимума между всеми найденными расстояниями для нахождения нейрона-победителя:&lt;br /&gt;
&lt;br /&gt;
Находим нейрон-победитель &amp;lt;math&amp;gt;ne_c&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt; c = \arg \min_{\forall j \in \{1, \dots, L\}} \rho(\vec{x}^{(i)}(t), \vec{w}^{(j)}(t))&amp;lt;/math&amp;gt;, который лежит ближе к текущему объекту &amp;lt;math&amp;gt;\vec{x}^{(i)}(t)&amp;lt;/math&amp;gt; по рассматриваемой метрике &amp;lt;math&amp;gt; || \circ || &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* Корректировка весовых векторов: сюда же входит поиск расстояний между всеми нейронами в рамках заданной топологии, а также вычисление функции соседства:&lt;br /&gt;
&lt;br /&gt;
Для всех нейронов &amp;lt;math&amp;gt; ne_{j} &amp;lt;/math&amp;gt;  выполнить пересчет весовых векторов:&lt;br /&gt;
&amp;lt;math&amp;gt; \vec{w}^{(j)}(t+1) := \vec{w}^{(j)}(t) +\alpha(t)h_{ci}(t)[\vec{x}^{(i)}(t) -  \vec{w}^{(j)}(t)]&amp;lt;/math&amp;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;\vec{x}^{(i)}&amp;lt;/math&amp;gt; и  &amp;lt;math&amp;gt; \vec{w}^{(j)} \forall j=1,..,M&amp;lt;/math&amp;gt; найти &amp;lt;math&amp;gt; \rho(\vec{x}^{(i)},  \vec{w}^{(j)}) =  || \vec{x}^{(i)} -  \vec{w}^{(j)}||^2 &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* Поиск минимума между всеми найденными расстояниями для нахождения нейрона-победителя:&lt;br /&gt;
&lt;br /&gt;
Находим нейрон-победитель &amp;lt;math&amp;gt;ne_c&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt; c = \arg \min_{\forall j \in \{1, \dots, L\}} \rho(\vec{x}^{(i)}, \vec{w}^{(j)})&amp;lt;/math&amp;gt;, который лежит ближе к текущему объекту &amp;lt;math&amp;gt;\vec{x}^{(i)}&amp;lt;/math&amp;gt; по рассматриваемой метрике &amp;lt;math&amp;gt; || \circ || &amp;lt;/math&amp;gt; и относим &amp;lt;math&amp;gt;\vec{x}^{(i)} &amp;lt;/math&amp;gt; к кластеру, соответствующему нейрону &amp;lt;math&amp;gt;ne_c&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
В алгоритме обучения можно выделить 4 макрофазы:&lt;br /&gt;
&lt;br /&gt;
* Начальная инициализация всех весов: построение матрицы &amp;lt;math&amp;gt; \mathbf{ W(0)} \in \mathbf {R^{N \times M}} &amp;lt;/math&amp;gt;, столбцами которой являются весовые вектора всех нейронов.&lt;br /&gt;
&lt;br /&gt;
* Выполнение следующих фаз &amp;lt;math&amp;gt; \mathbf T &amp;lt;/math&amp;gt; раз:&lt;br /&gt;
** Вычисление вектора расстояний &amp;lt;math&amp;gt; \mathbf D \in \mathbf {R^M} &amp;lt;/math&amp;gt;, в котором хранятся вектора расстояний от вектора &amp;lt;math&amp;gt; \mathbf X &amp;lt;/math&amp;gt; до векторов матрицы &amp;lt;math&amp;gt; \mathbf {W(t)} &amp;lt;/math&amp;gt;.&lt;br /&gt;
** Нахождение индекса минимума в векторе &amp;lt;math&amp;gt; \mathbf D &amp;lt;/math&amp;gt; - индекса нейрона победителя;&lt;br /&gt;
** Корректировка всех весов: получение матрицы &amp;lt;math&amp;gt; \mathbf {W(t+1)} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
В алгоритме работы алгоритма можно выделить две макрофазы:&lt;br /&gt;
&lt;br /&gt;
* Выполнение следующих фаз &amp;lt;math&amp;gt; \mathbf K &amp;lt;/math&amp;gt; раз:&lt;br /&gt;
** Вычисление вектора расстояний &amp;lt;math&amp;gt; \mathbf D \in \mathbf {R^M} &amp;lt;/math&amp;gt;, в котором хранятся вектора расстояний от вектора &amp;lt;math&amp;gt; \mathbf X &amp;lt;/math&amp;gt; до векторов матрицы &amp;lt;math&amp;gt; \mathbf {W} &amp;lt;/math&amp;gt;.&lt;br /&gt;
** Нахождение индекса минимума в векторе &amp;lt;math&amp;gt; \mathbf D &amp;lt;/math&amp;gt; - индекса нейрона победителя и определение входного вектора в кластер, соответсвующий нейрону-победителю;&lt;br /&gt;
&lt;br /&gt;
Также можно выделить математические операции, используемые в вышеприведенных:&lt;br /&gt;
* &amp;lt;math&amp;gt;\vec{x}^{T} \vec{y}&amp;lt;/math&amp;gt; - скалярное произведение вектров;&lt;br /&gt;
* &amp;lt;math&amp;gt;\vec{x} + \vec{y}&amp;lt;/math&amp;gt; - сложение двух векторов;&lt;br /&gt;
* &amp;lt;math&amp;gt;\lambda \vec{x}&amp;lt;/math&amp;gt; - умножение вектора на скаляр;&lt;br /&gt;
* &amp;lt;math&amp;gt;\min_i x_i&amp;lt;/math&amp;gt; - поиск минимального элемента в векторе &amp;lt;math&amp;gt;\vec{x}&amp;lt;/math&amp;gt;;&lt;br /&gt;
* Вычисление функции соседства &amp;lt;math&amp;gt; h_{ci}(t) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Схему реализации последовательного алгоритма обучения запишем в виде псевдокода:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=oberon2&amp;gt;&lt;br /&gt;
SetAlgorithmParameters()&lt;br /&gt;
CalculateDistancesBetweenNeuronsInEuclidianPlane()&lt;br /&gt;
W = SetNeuronWeightsVector()&lt;br /&gt;
FOR t = 1 to T do&lt;br /&gt;
  x = ChooseInputVector()&lt;br /&gt;
  D = InitialiseDistancesVector()&lt;br /&gt;
  FOR n = 1 to M do&lt;br /&gt;
    d = CalculateDistanseBetweenInputVectorAndNeuronWeightsVector(x,n)&lt;br /&gt;
    PutCalculatedDistanceToDistancesVector(D, d)&lt;br /&gt;
  END FOR&lt;br /&gt;
  ne_c = FindNeuronWinner(D)&lt;br /&gt;
  FOR n = 1 to M do&lt;br /&gt;
    CorrectNeuronsWeightsVector(W)&lt;br /&gt;
  END FOR&lt;br /&gt;
END FOR&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Схему реализации последовательного алгоритма работы запишем в виде псевдокода:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=oberon2&amp;gt;&lt;br /&gt;
FOR t = 1 to T do&lt;br /&gt;
  x = ChooseInputVector()&lt;br /&gt;
  D = InitialiseDistancesVector()&lt;br /&gt;
  FOR n = 1 to K do&lt;br /&gt;
    d = CalculateDistanseBetweenInputVectorAndNeuronWeightsVector(x,n)&lt;br /&gt;
    PutCalculatedDistanceToDistancesVector(D, d)&lt;br /&gt;
  END FOR&lt;br /&gt;
  ne_c = FindNeuronWinner(D)&lt;br /&gt;
  DefineInputVectorToCluster()&lt;br /&gt;
END FOR&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Дадим комментарии по некоторым функциям:&lt;br /&gt;
&amp;lt;math&amp;gt;SetAlgorithmParameters()&amp;lt;/math&amp;gt; - установка параметров алгоритма проводится исследователем для увеличения/уменьшения гранулярности кластеризации;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;CalculateDistancesBetweenNeuronsInEuclidianPlane()&amp;lt;/math&amp;gt; - вычисление матрицы расстояний между всеми нейронами в Евклидовой плоскости, в соответствии с их расположением в слое Кохонена - эта информация потребуется &amp;lt;math&amp;gt;CorrectNeuronsWeightsVector(W)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;W = SetNeuronWeightsVector()&amp;lt;/math&amp;gt; - установка векторов веса нейронов (стратегии инициализации были рассмотрены выше: случайным образом, значениями из обучающей выборки, с помощью значений из пространства векторов, натянутого на главные компоненты);&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;math&amp;gt;x = ChooseInputVector()&amp;lt;/math&amp;gt; - выбор входного вектора из выборки (стратегия выбора также было рассмотрена выше: индекс вектора в обучающей выборке - реализация случайной величины из заданного распределения вероятностей, например);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;D = InitialiseDistancesVector()&amp;lt;/math&amp;gt; - заполнение массива растояний нулями;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;d= CalculateDistanseBetweenInputVectorAndNeuronWeightsVector(x,n)&amp;lt;/math&amp;gt; - вычисление расстояния между входным вектором и вектора веса нейрона по выбранной метрике (например, евклидовом);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;PutCalculatedDistanceToDistancesVector(D, d)&amp;lt;/math&amp;gt;  - добавление вычисленного расстояния между входным вектором и вектором веса текущего нейрона в массив расстояний;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;ne_c = FindNeuronWinner(D)&amp;lt;/math&amp;gt; - нахождение нерона победителя, индекс которого является индексом минимума в массиве расстояний;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;CorrectNeuronsWeightsVector(W)&amp;lt;/math&amp;gt; - корректировка весов нейронов по формуле: &amp;lt;math&amp;gt; \vec{w}^{(j)}(t+1) := \vec{w}^{(j)}(t) +\alpha(t)h_{ci}(t)[\vec{x}^{(i)}(t) -  \vec{w}^{(j)}(t)]&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;DefineInputVectorToCluster()&amp;lt;/math&amp;gt; - определение входного вектора в соответсвующий кластер, концептуально ничем не отличается от нахождения нейрона-победителя, однако отвечает за формальный вывод результата.&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Начнём рассмотрение сложности последовательной реализации алгоритма Кохонена «сверху – вниз» начиная со сложности максимально абстрактного набора функций и потом конкретизируя сложность каждой функции в рамках фундаментальных вычислительных операций (присваивание, суммирование и т.п.). Это позволяет пересчитать сложность алгоритма Кохонена при условии замены функции составляющих его основу и при реализации его на целевую платформу.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;I АЛГОРИТМ ОБУЧЕНИЯ&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
1)	Уровень набора функций&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;Complexity(SOMLearning) = &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;Complexity(SetAlgorithmParameters) +&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;Complexity(CalculateDistancesBetweenNeuronsInEuclidianPlane) +&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;Complexity(SetNeuronWeightsVector) +&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;T \cdot Complexity(ChooseInputVector) +&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;T \cdot Complexity(InitialiseDistancesVector) +&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;T \cdot M \cdot Complexity(CalculateDistanseBetweenInputVectorAndNeuronWeightsVector) +&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;T \cdot M \cdot Complexity(PutCalculatedDistanceToDistancesVector) +&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;T \cdot Complexity(FindNeuronWinner) +&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;T \cdot M \cdot Complexity(CorrectNeuronsWeightsVector)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2)	Уровень реализации функций в рамках фундаментальных операций&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Будем считать, что следующие операции выполняются за константное время (&amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;):&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;mov&amp;lt;/math&amp;gt; - присваивание&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;sqrt&amp;lt;/math&amp;gt; - взятие квадратного корня из числа&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;rand&amp;lt;/math&amp;gt; – генерация случайного числа&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;add&amp;lt;/math&amp;gt; - сложение&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;sub&amp;lt;/math&amp;gt; - вычитание&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;mul&amp;lt;/math&amp;gt; - умножение&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;comp&amp;lt;/math&amp;gt; - сравнение&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;exp&amp;lt;/math&amp;gt; - экспоненцирование&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;div&amp;lt;/math&amp;gt; - деление&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Тогда:&lt;br /&gt;
:Так как параметров всегда конечное число и мы делаем допущение, что это число меньше M и T:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;Complexity(SetAlgorithmParameters) = O(Complexity(mov)) = O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;Complexity(CalculateDistancesBetweenNeuronsInEuclidianPlane) = &amp;lt;/math&amp;gt;&lt;br /&gt;
:&amp;lt;math&amp;gt;O(M \cdot (M-1) \cdot (2 \cdot Complexity(sub) + 2 \cdot Complexity(mul) +&amp;lt;/math&amp;gt;&lt;br /&gt;
:&amp;lt;math&amp;gt; Complexity(add) + Complexity(sqrt))) = O(M^2)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;Complexity(SetNeuronWeightsVector) = O(Mn \cdot Complexity(mov)) \vee O(Mn \cdot Complexity(rand))) = O(Mn)&amp;lt;/math&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;Complexity(ChooseInputVector) = O(n \cdot Complexity(mov))= O(n)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;Complexity(InitialiseDistancesVector) = O(M \cdot n \cdot Complexity(mov)) = O(M \cdot n)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;Complexity(CalculatedDistanseBetweenInputVectorAndNeuronWeightsVector) = O(n \cdot Complexity(sub)) +&amp;lt;/math&amp;gt;&lt;br /&gt;
:&amp;lt;math&amp;gt;O(n \cdot Complexity(mul)) + O((n - 1) \cdot Complexity(add)) = O(n)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;Complexity(PutCalculatedDistanceToDistancesVector) = O(n \cdot Complexity(mov)) = O(n)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;Complexity(FindNeuronWinner) = O(M \cdot n \cdot Complexity(comp)) = O(Mn)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;Complexity(CorrectNeuronsWeightsVector) = O(nComplexity(add)) + O(nComplexity(mul)) + &amp;lt;/math&amp;gt;&lt;br /&gt;
:&amp;lt;math&amp;gt;O(Complexity(NeighborhoodFunction)) = O(n) + O(Complexity(NeighborhoodFunction)) = O(n) +&amp;lt;/math&amp;gt;&lt;br /&gt;
:&amp;lt;math&amp;gt; O(Complexity(exp) + Complexity(div)) = O(n)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3)	Конечная сложность&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;Complexity(SOMLearning) = O(1) + O(M^2) + O(Mn) + O(Tn) + O(TMn) + O(TMn) + O(TMn) + O(TMn) + O(TMn) =&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt; O(TMn) + O(M^2)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;II АЛГОРИТМ РАБОТЫ&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
1)	Уровень набора функций&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;Complexity(SOMWorking) = &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;T \cdot Complexity(ChooseInputVector) +&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;T \cdot Complexity(InitialiseDistancesVector) +&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;T \cdot K \cdot Complexity(CalculateDistanseBetweenInputVectorAndNeuronWeightsVector) +&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;T \cdot K \cdot Complexity(PutCalculatedDistanceToDistancesVector) +&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;T \cdot Complexity(FindNeuronWinner) +&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;T \cdot Complexity(DefineInputVectorToCluster)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2)	Уровень реализации функций в рамках фундаментальных операций&lt;br /&gt;
&lt;br /&gt;
Эквивалентно п.2 в Алгоритме обучения (но в качестве M у нас K).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;Complexity(DefineInputVectorToCluster) = O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3)	Конечная сложность&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;Complexity(SOMWorking) = O(Tn) + O(TKn) + O(TKn) + O(TKn) + O(TKn) + O(T) = O(TKn)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
[[Файл:InfGraph-1.png|500px|thumb|right|Рис. 3а.Информационный граф алгоритма обучения]]&lt;br /&gt;
&lt;br /&gt;
В качестве операторных узлов информационного графа были выбраны:&lt;br /&gt;
&lt;br /&gt;
•	&#039;&#039;&#039;||a – b||&#039;&#039;&#039; - взятие евклидовой метрики от двух операндов&lt;br /&gt;
&lt;br /&gt;
•	&#039;&#039;&#039;FindMinimum&#039;&#039;&#039; – нахождение минимума из входящих значений&lt;br /&gt;
&lt;br /&gt;
•	&#039;&#039;&#039;UpdateWeights&#039;&#039;&#039; – обновление весов (для каждого входящего вектора отдельный узел)&lt;br /&gt;
&lt;br /&gt;
•	&#039;&#039;&#039;DefineCluster&#039;&#039;&#039; – определение кластера на основе полученного значения от нахождения минимума&lt;br /&gt;
&lt;br /&gt;
Это обусловлено тем, что с нашей точки зрения именно такой набор операций наиболее оптимально показывает структуру алгоритма, избавляя нас от излишней детализации.&lt;br /&gt;
&lt;br /&gt;
Входные параметры алгоритма, для которого был построен информационный граф:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; N = 2&amp;lt;/math&amp;gt;  - размер обучающей выборки,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; K = 2&amp;lt;/math&amp;gt;  - размер тестовой выборки,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; a = 1&amp;lt;/math&amp;gt; -  высота слоя Кохонена,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; b = 2&amp;lt;/math&amp;gt; - ширина слоя Кохонена,&lt;br /&gt;
&lt;br /&gt;
Остальные параметры могут быть любыми, так как не оказывают влияние на построенный информационный граф.&lt;br /&gt;
[[Файл:InfGraphSOMSecond.png|500px|thumb|center|Рис. 3б.Информационный граф алгоритма работы]]&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Для того чтобы оценить насколько эффективно Алгоритм Кохонена параллелизуется, рассмотрим его структуру в виде последовательности этапов исполнения. Вначале рассмотрим алгоритм обучения. У нас есть 4 фундаментальных этапа:&lt;br /&gt;
&lt;br /&gt;
[[Файл:SOMParallel.png|500px|thumb|right|Рис. 4.Пример параллельного алгоритма SOM при threadNumber = 2]]&lt;br /&gt;
1)	&#039;&#039;&#039;Инициализация&#039;&#039;&#039;. На этом этапе мы определяем параметры алгоритма, рассчитываем расстояния между нейронами и определяем начальные веса нейронов. Здесь имеет смысл распараллелить подсчет метрики между нейронами. Таким образом асимптотическая сложность упадет с &amp;lt;math&amp;gt;O(M^2)&amp;lt;/math&amp;gt; до &amp;lt;math&amp;gt;O(\frac{M^2}{threadNumber})&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2)	&#039;&#039;&#039;Генерация подвыборки&#039;&#039;&#039;. Здесь мы определяем в каком порядке будем обрабатывать вектора исходного входного пространства, какой вектор возьмём для следующей итерации. К сожалению, эта стадия не поддается распараллеливанию, так как каждая итерация алгоритма зависит от предыдущей;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3)	&#039;&#039;&#039;Поиск максимального подобия&#039;&#039;&#039;. Находим наиболее подходящий (победивший) нейрон, используя критерий минимума Евклидова расстояния. Этот этап распараллеливается следующим образом. Вначале мы рассчитываем метрику между вектором весов каждого нейрона и входящим вектором исходного пространства, для этого мы разбиваем количество нейронов &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;threadNumber&amp;lt;/math&amp;gt; потоков и подсчитываем метрику в каждой группе нейронов на поток параллельно. Затем мы пользуемся фактом, что минимум – дистрибутивная функция (см. Рис. 4)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;min([a, b, c, d]) = min(min([a, b]), min([b, c]))&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
и рассчитываем её за &amp;lt;math&amp;gt;\log_{2} (threadNumber)&amp;lt;/math&amp;gt; операций сравнения, что значительно меньше &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; в исходном варианте;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4)	Коррекция. Корректируем векторы синаптических всех нейронов. Также очевидным образом распараллеливается на &amp;lt;math&amp;gt;threadNumber&amp;lt;/math&amp;gt; потоков, в каждым из которых последовательно для каждого нейрона в группе внутри потока обновляются веса.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Итак, общая сложность параллельной версии алгоритма обучения &lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;Complexity(SOMParallelLearning) = O(\frac{M^2}{threadNumber}) + &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;O(\frac{TMn}{threadNumber} + \log_{2} (threadNumber)) + O(\frac{TMn}{threadNumber})&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
что меньше последовательной (при &amp;lt;math&amp;gt;threadNumber \approx M&amp;lt;/math&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;Complexity(SOMLearning) = O(M^2) + O(TMn) + O(TMn)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Алгоритм работы карты эквивалентен первым 3 уровням алгоритма обучения только с другими константами&lt;br /&gt;
(нужно заменить &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;K&amp;lt;/math&amp;gt;, предварительно убрав слагаемое &amp;lt;math&amp;gt;O(M^2)&amp;lt;/math&amp;gt;,&lt;br /&gt;
так как во время работы рассчитывать расстояние между нейронами не нужно).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вывод&#039;&#039;&#039;: Проведенный обзор показал, что только этап этап генерации подвыборки не поддается распараллеливанию. Это типичная проблема большинства алгоритмов машинного обучения, так как они итеративны (последовательно обучается по всей выборке). Однако в рамках остальных этапов алгоритм легко разбивается на потоки, что в теории улучшает производительность.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Входные данные алгоритма:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; - размерность исходного пространства,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \chi \subset R^n&amp;lt;/math&amp;gt; - обучающая выборка,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \Phi \subset R^n &amp;lt;/math&amp;gt; - тестовая выборка,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; N = |\chi|&amp;lt;/math&amp;gt;  - размер обучающей выборки,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; K = |\Phi|&amp;lt;/math&amp;gt;  - размер тестовой выборки,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; a &amp;lt;/math&amp;gt; -  высота слоя Кохонена,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; b &amp;lt;/math&amp;gt; - ширина слоя Кохонена,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; T &amp;lt;/math&amp;gt; - максимальное число итераций алгоритма обучения - может быть задано фиксировано, либо же равно мощности обучающей выборки, то есть  &amp;lt;math&amp;gt; N &amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; h_{ij}(t)&amp;lt;/math&amp;gt; - функция соседства между нейронами с индексами &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; на итерации t,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; h(d,t) &amp;lt;/math&amp;gt; - функция от расстояния между нейронами,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \alpha(t)&amp;lt;/math&amp;gt; - скорость обучения сети,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \sigma(t) &amp;lt;/math&amp;gt; - функция, уменьшающая количество соседей с при увеличении итерации (монотонно убывающий).&lt;br /&gt;
&lt;br /&gt;
Вычислительная сложность хранения входных данных для алгоритма обучения равна &amp;lt;math&amp;gt;O(n \times N)&amp;lt;/math&amp;gt;&lt;br /&gt;
(установка функций и прочих параметров выполняется за константное время).&lt;br /&gt;
&lt;br /&gt;
Вычислительная сложность хранения входных данных для алгоритма работы равна &amp;lt;math&amp;gt;O(n \times K)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные алгоритма обучения:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\vec{w}^{(j)}(t) = (w_1^{(j)}(t),...,w_n^{(j)}(t)), j = 1,..,M&amp;lt;/math&amp;gt; - вектор весовых коэффициентов для &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;-го нейрона,&lt;br /&gt;
&lt;br /&gt;
матрица &amp;lt;math&amp;gt;\mathbf{W}&amp;lt;/math&amp;gt;, представляющая веса нейронов &amp;lt;math&amp;gt;\vec{w}^{(j)}(t)&amp;lt;/math&amp;gt;&lt;br /&gt;
Вычислительная сложность хранения выходных данных равна &amp;lt;math&amp;gt;O(n \times M)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные алгоритма работы&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
Вычислительная сложность хранения выходных данных равна &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;, если алгоритм возвращает индекс кластера или координату нейрона,&lt;br /&gt;
иначе если возвращает вектор весов нейрона-победителя, то &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
Итак, вычислительная мощность алгоритма кластеризации самоорганизующимися сетями Кохонена равна &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;ComputationalPower(SOM) = \frac{TMn + M^2}{Mn + Tn} = \frac{M(Tn + M)}{n(M + T)}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Cоотношение последовательной и параллельной сложности алгоритма описано в п. 1.8&lt;br /&gt;
&lt;br /&gt;
Детерминированность алгоритма зависит от способа генерации выборки из исходного входного пространства, если она &lt;br /&gt;
детерминирована, то и алгоритм детерминирован, иначе нет.&lt;br /&gt;
&lt;br /&gt;
Общие свойства алгоритма:&lt;br /&gt;
*Аппроксимация входного пространства. Карта признаков, представленная множеством векторов синоптических весов, в выходном пространстве реализует хорошую аппроксимацию входного пространства.&lt;br /&gt;
*Топологический порядок. Карта признаков, полученная алгоритмом SOM, является топологически упорядоченной в том смысле, что пространственное положение нейронов в решетке соответствует конкретной области или признаку входного образа.&lt;br /&gt;
*Соответствие плотности. Карта признаков отражает вариации в статистиках распределения входного сигнала. Области во входном пространстве, из которого берутся векторы &amp;lt;math&amp;gt;\vec{x}&amp;lt;/math&amp;gt; с большей вероятностью, отображаются в гораздо большие области выходного пространства и, таким образом, с большим разрешением, чем области в исходном пространстве, из которых берутся векторы &amp;lt;math&amp;gt;\vec{x}&amp;lt;/math&amp;gt; с меньшей вероятностью.&lt;br /&gt;
*Выбор признаков. Для данных из входного пространства с нелинейным распределением самоорганизующаяся карта для аппроксимации исследуемого распределения способна извлечь набор наилучших признаков.&lt;br /&gt;
&lt;br /&gt;
Преимущества алгоритма:&lt;br /&gt;
&lt;br /&gt;
*Является устойчивым, в том числе и к зашумленным данным;&lt;br /&gt;
*Реализует быстрое и неуправляемое обучение (процесс самоорганизуюется);&lt;br /&gt;
*Может визуализировать многомерные входные данные (упрощения многомерных входных данных с помощью визуализации);&lt;br /&gt;
*Может аппроксимировать любую непрерывную функцию, что позволяет исследователю не принимать заранее какие-либо гипотезы относительно модели.&lt;br /&gt;
&lt;br /&gt;
Недостатки алгоритма:&lt;br /&gt;
&lt;br /&gt;
*Работает только с вещественными числовыми векторами;&lt;br /&gt;
*Может быть использован для кластерного анализа только в том случае, если заранее известно число кластеров;&lt;br /&gt;
*Наличие искажений. Близкие объекты исходного пространства могут переходить в далёкие точки на карте;&lt;br /&gt;
*Окончательный результат работы нейронных сетей сильно зависит от начальных установок сети (начальное распределение весов, свойства потенциальной функции).&lt;br /&gt;
&lt;br /&gt;
= Программная реализация алгоритма =&lt;br /&gt;
&lt;br /&gt;
== Особенности реализации последовательного алгоритма ==&lt;br /&gt;
&lt;br /&gt;
== Локальность данных и вычислений ==&lt;br /&gt;
&lt;br /&gt;
== Возможные способы и особенности параллельной реализации алгоритма ==&lt;br /&gt;
&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
Вышеописанный алгоритм кластеризации был реализован с помощью технологии OpenMP и протестирован на суперкомпьютере Ломоносов. &lt;br /&gt;
&lt;br /&gt;
Методология тестирования представляет из себя последовательные запуски:&lt;br /&gt;
* с  размером решетки Кохонена (решетки нейронов) 10x10, 30x30, 50x50, 70x70,90x90,110x110;&lt;br /&gt;
* числом openmp_threads: 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20; &lt;br /&gt;
* при размере входного пространства равном 4;&lt;br /&gt;
* для обучающей выборки размером 400000 (4-x мерных) векторов;&lt;br /&gt;
* для тестовой выборки размером 34874 (4-х мерных) вектора.&lt;br /&gt;
&lt;br /&gt;
В результате было построено два графика (ссылки на интерактивные графики): &lt;br /&gt;
* [https://plot.ly/~crowslord/5.embed Зависимость времени работы обученной сети Кохонена от размера решетки Кохонена и числа openmp_threads];&lt;br /&gt;
* [https://plot.ly/~crowslord/7.embed Зависимость времени обучения сети Кохонена от размера решетки Кохонена и числа openmp_threads].&lt;br /&gt;
&lt;br /&gt;
[[Файл:TrainSom.png]] [[Файл:WorkSom.png]]&lt;br /&gt;
&lt;br /&gt;
При этом по фронтальной оси X откладывается количество openmp_threads, по оси Y откладывается одно измерение размера матрицы (то есть если матрица была 50x50, то по оси будет отложено 50, по оси Z откладывается  время обучения сети Кохонена для первого графика и время работы сети Кохонена для второго графика.&lt;br /&gt;
&lt;br /&gt;
Анализ графиков позволяет сделать вывод о том, что алгоритм хорошо масштабируем - по графикам видно, что время обучения/работы сети Кохонена резко возрастает при одновременном уменьшении количества openmp_threads и увеличении размера решетки Кохонена (то есть, в конечном счете, числе кластеров).&lt;br /&gt;
&lt;br /&gt;
Также входе тестирования была получена следующая закономерность - если зафиксировать некоторый размер сетки и последовательно увеличивать количество openmp_threads, то время работы/время обучения сети Кохонена уменьшается лишь до некоторого количества openmp_threads, а потом начинает возрастать - это позволяет говорить о наличии потенциального порога масштабируемости для данного размера сетки при использовании данной технологии. &lt;br /&gt;
&lt;br /&gt;
Также  было установлено, что при использовании вложенного параллелизма время работы существенно увеличивается, поэтому значение OMP_NESTED = 0.&lt;br /&gt;
&lt;br /&gt;
Параллелизм реализован на уровне openmp_threads в следующих блоках кода:&lt;br /&gt;
* расчет расстояний между нейронами в карте Кохохнена;&lt;br /&gt;
* начальная инициализация нейронов;&lt;br /&gt;
* поиск нейрона-победителя.&lt;br /&gt;
&lt;br /&gt;
== Динамические характеристики и эффективность реализации алгоритма ==&lt;br /&gt;
&lt;br /&gt;
== Выводы для классов архитектур ==&lt;br /&gt;
&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Авторами проекта была также практически написана собственная реализация (возможно, она будет доведена до конца и протестирована).&lt;br /&gt;
&lt;br /&gt;
На данный момент удалось найти следующие последовательные реализации:&lt;br /&gt;
&lt;br /&gt;
* [http://knnl.sourceforge.net/ KNNL]:Реализация на C++&lt;br /&gt;
* [https://pypi.python.org/pypi/kohonen kohonen] Реалиазция на Python&lt;br /&gt;
* [https://cran.r-project.org/web/packages/kohonen/ kohonen] Реалиазция на R&lt;br /&gt;
* [http://jknnl.sourceforge.net/ JKNNL]: Реалиазция на Java.&lt;br /&gt;
* [https://www.mathworks.com/help/nnet/ug/cluster-with-self-organizing-map-neural-network.html?requestedDomain=www.mathworks.com Matlab realization]: Реалиазция на Matlab.&lt;br /&gt;
&lt;br /&gt;
Также есть параллельные реализации:&lt;br /&gt;
&lt;br /&gt;
* [https://github.com/peterwittek/somoclu Somoclu]: Реализация на Python, R, Julia, и Matlab, поддерживаются OpenMP, MPI и CUDA.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
* T. Kohonen, Self-Organizing Maps (Third Extended Edition), New York, 2001, 501 pages. ISBN 3-540-67921-9&lt;br /&gt;
* Нейский И.М. Классификация и сравнение методов кластеризации [[http://it-claim.ru/Persons/Neyskiy/Article2_Neiskiy.pdf]]&lt;br /&gt;
* Хайкин С. Нейронные сети: полный курс, 2-е изд.: Пер. с англ. - М.:ООО &amp;quot;И. Д. Вильямс&amp;quot;, 2016. - 1104с. ISBN 978-5-8459-2069-0 (рус.)&lt;br /&gt;
* К. В. Воронцов Лекции по искусственным нейронным сетям 21 декабря 2007 г.&lt;/div&gt;</summary>
		<author><name>Teplov</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:Emaksimov/%D0%9A%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F_%D1%81%D0%B5%D1%82%D1%8F%D0%BC%D0%B8_%D0%9A%D0%BE%D1%85%D0%BE%D0%BD%D0%B5%D0%BD%D0%B0&amp;diff=20241</id>
		<title>Участник:Emaksimov/Кластеризация сетями Кохонена</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:Emaksimov/%D0%9A%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F_%D1%81%D0%B5%D1%82%D1%8F%D0%BC%D0%B8_%D0%9A%D0%BE%D1%85%D0%BE%D0%BD%D0%B5%D0%BD%D0%B0&amp;diff=20241"/>
		<updated>2016-12-07T12:33:05Z</updated>

		<summary type="html">&lt;p&gt;Teplov: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment | Teplov}}&lt;br /&gt;
&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Кластеризация сетями Кохонена&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(mnT)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;mk&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;mn&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O(T*log(max(m,n)))&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Авторы страницы &amp;lt;b&amp;gt;[[Участник:Emaksimov|Егор Максимов]] (1.1, 1.2, 1.4, 1.5, 1.6, 1.7, 1.9, 2.4, 1.10)&amp;lt;/b&amp;gt; и &amp;lt;b&amp;gt;[[Участник:Kravtsovkirill|Кирилл Кравцов]] (1.2, 1.3, 1.6, 1.7, 1.8, 1.9, 1.10, 2.4, 2.7)&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&#039;&#039;&#039;Искусственная нейронная сеть Кохонена&#039;&#039;&#039; или самоорганизующаяся карта признаков (SOM) была предложена финским исследователем Тойво Кохоненом в начале 1980-х годов.&lt;br /&gt;
[[Файл:Teuvokohonen.jpg|thumb|150px|right|Рис. 1. Кохонен, Теуво.]]&lt;br /&gt;
Она представляет собой двухслойную сеть. Каждый нейрон первого (распределительного) слоя соединен со всеми нейронами второго (выходного) слоя, которые расположены в виде двумерной решетки. Для визуализации топологии слоя Кохонена используют либо шестиугольные, либо прямоугольные ячейки, в которых распологаются нейроны. Шестиугольные ячейки часто являются более предпочтительными, так как расстояние от центра выбранной ячейки до ее соседей одинаково.Нейроны выходного слоя называются кластерными элементами, их количество определят максимальное количество групп, на которые система может разделить входные данные. Увеличивая количество нейронов второго слоя можно увеличивать детализацию результатов процесса кластеризации.&lt;br /&gt;
&lt;br /&gt;
Система работает по принципу соревнования – нейроны второго слоя соревнуются друг с другом за право наилучшим образом сочетаться с входным вектором сигналов, побеждает тот элемент-нейрон, чей вектор весов ближе всего к входному вектору сигналов. За меру близости двух векторов можно взять квадрат евклидова расстояния. Таким образом, каждый входной вектор относится к некоторому кластерному элементу.&lt;br /&gt;
&lt;br /&gt;
Для обучения сети Кохонена используется соревновательный метод. На каждом шаге обучения из исходного набора данных случайно выбирается один вектор. Затем производится поиск нейрона выходного слоя, для которого расстояние между его вектором весов и входным вектором - минимально.&lt;br /&gt;
По определённому правилу производится корректировка весов для нейрона-победителя и нейронов из его окрестности, которая задаётся соответствующей функцией окрестности. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
[[Файл:FNaEm.png|thumb|550px|right|Риc. 2. Кластеризация сетями Кохонена]]&lt;br /&gt;
Пусть имеется некоторое множество векторов &amp;lt;math&amp;gt; x^{(i)}=(x_1^{(i)},x_2^{(i)},...,x_m^{(i)}) \in \mathbb{R}^m,  i=1, \dots, k&amp;lt;/math&amp;gt;. Заранее зададим количество кластеров равным &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;. Соответственно, Карта Кохонена представляет собой двухслойную fully-connected нейронную сеть, где первый слой состоит из &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; нейронов, а второй - из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;.&lt;br /&gt;
Каждый j-ый нейрон описывается вектором весов &amp;lt;math&amp;gt; w_j=(w_{1j},w_{2j},...,w_{mj}),  j=1, \dots, n&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Алгоритм обучения сети без учителя==== &lt;br /&gt;
&lt;br /&gt;
Обучение состоит из последовательности коррекций векторов, представляющих собой нейроны. Сначала следует инициализировать векторы весов. Далее определенное количество раз (эпох) производятся следующие действия:&lt;br /&gt;
#Случайным образов выбирается вектор &amp;lt;math&amp;gt;x(t)&amp;lt;/math&amp;gt; из входных данных.&lt;br /&gt;
#Выбирается нейрон-победитель &amp;lt;math&amp;gt;w_c&amp;lt;/math&amp;gt; &amp;quot;наиболее похожий&amp;quot; (по евклидову расстоянию) на вектор &amp;lt;math&amp;gt;x(t)&amp;lt;/math&amp;gt;: &amp;lt;center&amp;gt; &amp;lt;math&amp;gt; ||x(t)-w_c||=\min\limits_{i}(||x(t)-w_i(t)||)&amp;lt;/math&amp;gt;. &amp;lt;/center&amp;gt;&lt;br /&gt;
# Веса всех нейронов обновляются по формуле:&lt;br /&gt;
 &amp;lt;center&amp;gt; &amp;lt;math&amp;gt; w_i(t+1)=w_i(t)+\alpha(t)h_{ic}(t)||x(t)-w_i(t)||&amp;lt;/math&amp;gt;, &amp;lt;/center&amp;gt; &lt;br /&gt;
где &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; - номер эпохи, &amp;lt;math&amp;gt; h_{ic}(t)&amp;lt;/math&amp;gt;- функция соседства нейронов,&lt;br /&gt;
&amp;lt;math&amp;gt;\alpha (t)&amp;lt;/math&amp;gt; - функция скорости обучения сети.&lt;br /&gt;
&lt;br /&gt;
==== Инициализация начальных весов==== &lt;br /&gt;
&lt;br /&gt;
Инициализация случайными значениями, когда всем весам даются малые случайные величины.&lt;br /&gt;
Инициализация примерами, когда в качестве начальных значений задаются значения случайно выбранных примеров из обучающей выборки&lt;br /&gt;
Линейная инициализация. В этом случае веса инициируются значениями векторов, линейно упорядоченных вдоль линейного подпространства, проходящего между двумя главных собственными векторами исходного набора данных. Собственные вектора могут быть найдены например при помощи процедуры Грама-Шмидта.&lt;br /&gt;
&lt;br /&gt;
==== Функция соседства нейронов==== &lt;br /&gt;
&lt;br /&gt;
Функции от расстояния применяются двух видов: &lt;br /&gt;
&lt;br /&gt;
Константа: &lt;br /&gt;
 &amp;lt;math&amp;gt;&lt;br /&gt;
 h(d,t) = \begin{cases}&lt;br /&gt;
   c,  &amp;amp; \mbox{if } n \ d&amp;lt;=\delta \\&lt;br /&gt;
   0, &amp;amp; \mbox{if } n \ d&amp;gt;\delta&lt;br /&gt;
 \end{cases}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Функция Гаусса: &lt;br /&gt;
 &amp;lt;math&amp;gt;h_{ic}(t)=\exp(-\frac{\|r_c-r_i\|^2}{2\sigma^2(t)})&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;r_i&amp;lt;/math&amp;gt; определяет положение &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го нейрона в сетке&lt;br /&gt;
&lt;br /&gt;
==== Функция скорости обучения сети==== &lt;br /&gt;
&lt;br /&gt;
Главное требование к ней - монотонное убывание при возрастании &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; для замедления темпов вариации весов нейронов слоя Кохонена.&lt;br /&gt;
Функция может быть задана, например, как &amp;lt;math&amp;gt;\alpha(t)=\frac{A}{B+t}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;B&amp;lt;/math&amp;gt; - некоторые константы.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро алгоритма состоит из двух шагов, повторяющихся каждую итерацию (эпоху):&lt;br /&gt;
*Вычисление расстояния от случайно выбранного вектора &amp;lt;math&amp;gt;x(t)&amp;lt;/math&amp;gt; до каждого нейрона и выбор нейрона с наименьшим расстоянием. Т.е. вычисление &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; евклидовых расстояний и сортировка полученного массива расстояний. Сложность евклидового расстояния растет линейно с увеличением длины вектора. Следовательно получаем сложность данного шага &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt;.&lt;br /&gt;
*Обновление весов всех нейронов. Пусть используется гауссовская функция для вычисления расстояний между нейронами. Сложность обновления веса одного нейрона линейно зависит от &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt;. Т.е. сложность данного шага также &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В итоге получаем, что для всего алгоритма с количеством эпох равным &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; сложность составляет &amp;lt;math&amp;gt;O(Tmn)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
Алгоритм кластеризации можно разбить на следующие этапы:&lt;br /&gt;
*Инициализация &amp;lt;math&amp;gt;\mathbf{W} \leftarrow ComputeInitialWeights(\mathbf{W})&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Цикл, повторяющийся &amp;lt;math&amp;gt;T_{max}&amp;lt;/math&amp;gt; (максимальное число &amp;quot;эпох&amp;quot;) раз:&lt;br /&gt;
# &amp;lt;math&amp;gt;\mathbf{X} \leftarrow ChooseRandomVector(\mathbf{V})&amp;lt;/math&amp;gt;&lt;br /&gt;
#Вложенный цикл, в котором осуществляется вычисление расстояний &amp;lt;math&amp;gt;\mathbf{D_i} \leftarrow CalculateEucledeNorm(X, \mathbf{W_i})&amp;lt;/math&amp;gt;&lt;br /&gt;
#Выбор победителя &amp;lt;math&amp;gt;\mathbf{W_c} \leftarrow ChooseWinner(\mathbf{D_i})&amp;lt;/math&amp;gt;&lt;br /&gt;
#Вложенный цикл, в котором осуществляется корректировка весов &amp;lt;math&amp;gt;\mathbf{W_i(t+1)}\leftarrow CorrectWeights(\mathbf{W_i(t)})&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;\mathbf{W}&amp;lt;/math&amp;gt; - матрица весов, &amp;lt;math&amp;gt;T_{max}&amp;lt;/math&amp;gt; - максимальное число &amp;quot;эпох&amp;quot;, &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; - мощность входного множества, &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; - число нейронов выходного слоя. Тогда схема реализации последовательного алгоритма будет выглядеть следующим образом:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;math&amp;gt;\mathbf{W} := InitializeWeights()&amp;lt;/math&amp;gt;&lt;br /&gt;
 &amp;lt;math&amp;gt;\mathbf{FOR} \ t:=1 \ \mathbf{TO} \ T_{max} \ \mathbf{DO}&amp;lt;/math&amp;gt;&lt;br /&gt;
     &amp;lt;math&amp;gt; \quad \mathbf{X} := ChooseRandomVector\left( \{1, \dots, k\} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
     &amp;lt;math&amp;gt;\mathbf{FOR} \ i:=1 \ \mathbf{TO} \ N \ \mathbf{DO}&amp;lt;/math&amp;gt;&lt;br /&gt;
         &amp;lt;math&amp;gt;\mathbf{D} := CalculateEucledeNorm(\mathbf{X}, \mathbf{W_i})&amp;lt;/math&amp;gt;&lt;br /&gt;
     &amp;lt;math&amp;gt;\mathbf{W_c} := ChooseWinner(\mathbf{D})&amp;lt;/math&amp;gt;&lt;br /&gt;
     &amp;lt;math&amp;gt;\mathbf{FOR} \ i:=1 \ \mathbf{TO} \ N \ \mathbf{DO} \ \mathbf{AND} \ \mathbf{i} \neq \mathbf{c}&amp;lt;/math&amp;gt;&lt;br /&gt;
         &amp;lt;math&amp;gt;\mathbf{W_i(t+1)} := CorrectWeights(\mathbf{W_i(t)}, \mathbf{W_c})&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Рассмотрим вычислительную сложность описанного алгоритма. Основная часть операций алгоритма приходится на вложенные циклы, где &lt;br /&gt;
*выполняется вычисление &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; евклидовых норм, (функция &amp;lt;math&amp;gt;CalculateEucledeNorm&amp;lt;/math&amp;gt;), на вычисление каждой из которых требуется по &amp;lt;math&amp;gt;2m-1&amp;lt;/math&amp;gt; операций сложения, &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; операций умножения и один квадратный корень. (&amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - размерность входных векторов) для вычисления всех норм&lt;br /&gt;
*количество операций на корректировку весов одного нейрона можно оценить как &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Итак, как уже было сказано, общая последовательная сложность алгоритма составляет &amp;lt;math&amp;gt;O(Tmn)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; - количество эпох обучения (итераций), &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - размерность входных векторов (иначе говоря, количество нейронов во входном слое сети Кохонена), &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; - количество кластеров (иначе говоря, количество нейронов во втором слое сети - слое Кохонена).&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
Общая схема работы алгоритма выглядит следующим образом:&lt;br /&gt;
[[Файл:Graph2.png|thumb|400px|center| Рис. 3. Общая схема работы алгоритма кластеризации]]&lt;br /&gt;
Здесь In - входные данные, Init - шаг, на котором происходит инициализация весов, Norm - вычисление расстояний, Win - выбор победителя, Correct - корректировка весов, Out - выходные данные.&lt;br /&gt;
&lt;br /&gt;
Опишем информационный граф алгоритма.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первый этап&#039;&#039;&#039; состоит из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; параллельных операций вычисления евклидового расстояния до случайно выбранного входного вектора. &lt;br /&gt;
&lt;br /&gt;
На выходе из каждой ветки получаем значение расстояние соответствующего нейрона. &lt;br /&gt;
Далее находим минимальное расстояние, тем самым определим нейрона победителя.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Graph3.png|thumb|600px|center| Рис. 4. Первый этап алгоритма]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Второй этап&#039;&#039;&#039; состоит их &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; параллельных операций коррекций весов.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Graph4.png|thumb|600px|center| Рис. 5. Второй этап алгоритма]]&lt;br /&gt;
&lt;br /&gt;
На выходе, собрав из каждой ветки откорректированный вектор весов, получаем обновленную матрицу весов, которая используется на следующей эпохе.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Для алгоритма кластеризации сетями Кохонена средних имеет место массовый параллелизм: в основе каждого из основных этапов алгоритма (поиска евклидовых расстояний для определения нейрона-победителя и обновления весов) лежат информационно независимые действия.&lt;br /&gt;
Учитывая данный факт можно оценить параллельную сложность алгоритма в предположении доступности неограниченного числа необходимых процессоров.&lt;br /&gt;
&lt;br /&gt;
Итоговая сложность параллельного алгоритма будет определяться, исходя из формулы &amp;lt;math&amp;gt;Complexity = T * (PComplexity_{Norm} + PComplexity_{Winner} + PComplexity_{Correct})&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;PComplexity_{Norm}&amp;lt;/math&amp;gt; - параллельная сложность поиска евклидовых расстояний для определения нейрона-победителя, &amp;lt;math&amp;gt;PComplexity_{Correct}&amp;lt;/math&amp;gt; - параллельная сложность обновления весов нейронов, &amp;lt;math&amp;gt;PComplexity_{Winner}&amp;lt;/math&amp;gt; - сложность глобального поиска минимального значения расстояния, &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; - количество эпох (итераций) алгоритма.&lt;br /&gt;
&lt;br /&gt;
Этап Norm заключается в параллельном вычислении евклидовых норм. Сложность этого этапа можно оценить с учетом доступности неограниченного числа процессоров как &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - длина векторов. &lt;br /&gt;
Этап Winner заключается в поиске минимума по подсчитанным расстояниям для определения нейрона-победителя. Параллельная сложность реализации операции нахождения минимума из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; элементов определяется как &amp;lt;math&amp;gt;O(\log n)&amp;lt;/math&amp;gt;. &lt;br /&gt;
Этап Correct состоит из нескольких операций, но асимптотически сложность определяет именно подсчет евклидова расстояния. Аналогично получаем, что сложность данного этапа составляет &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Итоговую параллельную сложность кластеризации сетями Кохонена можно оценить как &amp;lt;math&amp;gt;O(T * \log max(m,n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&#039;&#039;&#039;Входные данные:&#039;&#039;&#039; множество входных векторов (нейронов первого уровня), множество кластеров (выходов, нейронов второго уровня), максимальное число &amp;quot;эпох&amp;quot; &amp;lt;math&amp;gt;T_{max}&amp;lt;/math&amp;gt;, константа &amp;lt;math&amp;gt; \delta &amp;lt;/math&amp;gt;, константы &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;B&amp;lt;/math&amp;gt;, константа &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; (в случае, если выбрана простая константа).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объем входные данных:&#039;&#039;&#039; так как обучение происходит не на всем входном векторе, а только на количестве векторов равном &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то алгоритм может быть организован так, чтобы принять на выход только &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; случайных векторов из исходной выборки. И, соответственно, объем входных данных можно считать равным &amp;lt;math&amp;gt;m*T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные:&#039;&#039;&#039; матрица весов.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объем выходных данных:&#039;&#039;&#039; равен &amp;lt;math&amp;gt;m*k&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&amp;lt;p&amp;gt;&amp;lt;b&amp;gt;[[глоссарий#Вычислительная мощность|&#039;&#039;Вычислительная мощность&#039;&#039;]]&amp;lt;/b&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
Вычислительная мощность алгоритма кластеризации сетями Кохонена равна &amp;lt;math&amp;gt;\frac{m*n*T}{m*k+m*n} = \frac{n*T}{k+n}&amp;lt;/math&amp;gt;, где, напомним, &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; - число итераций, &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; - число нейронов выходного слоя, &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; - мощность входного множества.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p&amp;gt;&amp;lt;b&amp;gt;[[глоссарий#Детерминированность |&#039;&#039;Детерминированность&#039;&#039;]] и [[глоссарий#Устойчивость |&#039;&#039;Устойчивость&#039;&#039;]] &amp;lt;/b&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
Описанный алгоритм кластеризации случайно выбирает элемент входного множества, следовательно, не является детерминированным. Как и все простые сети, построенный алгоритм является устойчивым.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p&amp;gt;&amp;lt;b&amp;gt;&#039;&#039;Сильные и слабые стороны алгоритма&#039;&#039;&amp;lt;/b&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Устойчивость к зашумленным данным, быстрое и неуправляемое обучение, возможность упрощения многомерных входных данных с помощью визуализации.&lt;br /&gt;
Главное преимущество самоорганизующихся карт Кохонена является их способность &amp;quot;проецирования&amp;quot; многомерных пространств на двумерное. Важной особенностью карт Кохонена является то, что объекты с похожими характеристиками (по всем компонентам) располагаются рядом, образуя кластеры.&lt;br /&gt;
&lt;br /&gt;
Визуализация данных в виде двумерной карты позволяет проводить кластеризацию данных и значительно упрощает корреляционный анализ.&lt;br /&gt;
Важным недостатком является то, что окончательный результат работы нейронных сетей зависит от начальных установок сети. С другой стороны, нейронные сети теоретически могут аппроксимировать любую непрерывную функцию, что позволяет исследователю не принимать заранее какие-либо гипотезы относительно модели.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма==&lt;br /&gt;
===Особенности реализации последовательного алгоритма===&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
==== Локальность реализации алгоритма ====&lt;br /&gt;
===== Структура обращений в память и качественная оценка локальности =====&lt;br /&gt;
===== Количественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
==== Масштабируемость алгоритма ====&lt;br /&gt;
&lt;br /&gt;
Как было описано в [[1.8 | п. 1.8]], для алгоритма кластеризации имеет место массовый параллелизм, поскольку основные этапы алгоритма (поиск евклидовых расстояний для определения нейрона-победителя и обновление весов) информационно независимы, что означает хорошую масштабируемость алгоритма.&lt;br /&gt;
&lt;br /&gt;
==== Масштабируемость реализации алгоритма ====&lt;br /&gt;
&lt;br /&gt;
Исследование проводилось на суперкомпьютере &amp;quot;Ломоносов&amp;quot; Суперкомпьютерного комплекса Московского университета. Алгоритм был реализован с помощью языка Fortran 90. &lt;br /&gt;
&lt;br /&gt;
Реализация тестировалась со следующими параметрами: &lt;br /&gt;
&lt;br /&gt;
*	число процессоров [1 : 128];&lt;br /&gt;
*	размер входного слоя сети, а также слоя Кохонена (были установлены равными друг другу) [1000 : 4000].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:Plot_s2.svg|thumb|600px|center| На рисунке представлен график зависимости времени выполнения от числа процессоров и размерности входных данных]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:Plot_s3.svg|thumb|600px|center| Производительность в зависимости от числа процессоров и размерности входных данных]]&lt;br /&gt;
&lt;br /&gt;
Можно сделать вывод, что с протестированными параметрами запуска, алгоритм хорошо параллелится только до момента, когда начинают расти накладные расходы, и для максимальных протестированных размеров слоев сети, оптимальным количество процессоров является 32. Из графиков видно, что при увеличении объема задачи эффективность от большего, чем 32, количества процессоров будет расти.&lt;br /&gt;
&lt;br /&gt;
Была использована следующая авторская реализация алгоритма.&lt;br /&gt;
&lt;br /&gt;
    program Kohonen&lt;br /&gt;
        implicit none&lt;br /&gt;
        include &#039;mpif.h&#039;&lt;br /&gt;
        integer rank,ierr,size,sizeBlock&lt;br /&gt;
        integer n,perProc,i,j,k,jmin,jAbsWin&lt;br /&gt;
        integer iterNum,IOS&lt;br /&gt;
        real learning_rate&lt;br /&gt;
        real dist,prom,minDist&lt;br /&gt;
        real, allocatable :: W(:,:),sample(:,:),maswin(:,:)&lt;br /&gt;
        real, allocatable :: buf(:,:),win(:)&lt;br /&gt;
        double precision start,finish&lt;br /&gt;
        character str&lt;br /&gt;
        	&lt;br /&gt;
        iterNum = 1000&lt;br /&gt;
        n=1000&lt;br /&gt;
        &lt;br /&gt;
            learning_rate = 1.0&lt;br /&gt;
            &lt;br /&gt;
            allocate(W(n,n))&lt;br /&gt;
            allocate(sample(iterNum,n))&lt;br /&gt;
            allocate(win(2))&lt;br /&gt;
            &lt;br /&gt;
            call randMatr(sample,iterNum,n,.FALSE.)&lt;br /&gt;
            call randMatr(W,n,n,.True.)&lt;br /&gt;
            &lt;br /&gt;
            call MPI_INIT(IERR)&lt;br /&gt;
            call MPI_COMM_SIZE(MPI_COMM_WORLD,SIZE,IERR)&lt;br /&gt;
            call MPI_COMM_RANK(MPI_COMM_WORLD,RANK,IERR)&lt;br /&gt;
            &lt;br /&gt;
            perProc = n/size&lt;br /&gt;
            allocate(buf(n,perProc))&lt;br /&gt;
            allocate(maswin(2,size))&lt;br /&gt;
            sizeBlock = perProc * n&lt;br /&gt;
            &lt;br /&gt;
            if (rank .eq. 0) then&lt;br /&gt;
                start = MPI_WTIME(IERR)&lt;br /&gt;
            end if&lt;br /&gt;
            &lt;br /&gt;
            do i=1,IterNum,1&lt;br /&gt;
                call obnul(buf,n,perProc)&lt;br /&gt;
                call MPI_SCATTER(W,sizeBlock,MPI_REAL,buf,sizeBlock,MPI_REAL,0,MPI_COMM_WORLD,IERR)&lt;br /&gt;
                &lt;br /&gt;
                dist = euclDist(sample(i,1:n),buf(1:n,1),n)&lt;br /&gt;
                do j=1,perProc,1&lt;br /&gt;
                    prom = euclDist(sample(i,1:n),buf(1:n,j),n)&lt;br /&gt;
                    if (prom .le. dist) then&lt;br /&gt;
                        dist = prom&lt;br /&gt;
                        jmin = j + rank*perProc&lt;br /&gt;
                    end if&lt;br /&gt;
                enddo		&lt;br /&gt;
                win(1) = jmin&lt;br /&gt;
                win(2) = dist&lt;br /&gt;
                &lt;br /&gt;
                call MPI_BARRIER(MPI_COMM_WORLD,IERR)	&lt;br /&gt;
                			&lt;br /&gt;
                call MPI_GATHER(win,2,MPI_REAL,maswin,2,MPI_REAL,0,MPI_COMM_WORLD,IERR)&lt;br /&gt;
                &lt;br /&gt;
                if (rank .eq. 0) then&lt;br /&gt;
                    minDist = maswin(2,1)&lt;br /&gt;
                    jAbsWin = maswin(1,1)&lt;br /&gt;
                    do j=2,size,1&lt;br /&gt;
                        if (mindist .ge. maswin(2,j)) then&lt;br /&gt;
                            minDist = maswin(2,j)&lt;br /&gt;
                            jAbsWin = maswin(1,j)&lt;br /&gt;
                        endif&lt;br /&gt;
                    enddo&lt;br /&gt;
                    call modW(W,n,learning_rate,i,jAbsWin)&lt;br /&gt;
                    end if&lt;br /&gt;
            enddo&lt;br /&gt;
            &lt;br /&gt;
            if (rank .eq. 0) then&lt;br /&gt;
                finish = MPI_WTIME(IERR)&lt;br /&gt;
                open(1,file=&amp;quot;Rezult.txt&amp;quot;,iostat=IOS)&lt;br /&gt;
                do while (.NOT. eof(1))&lt;br /&gt;
                    read(1,*) str&lt;br /&gt;
                enddo&lt;br /&gt;
                write (1,*) &#039;Neurons: &#039;,n,&#039;Processes: &#039;,size,&#039;Time: &#039;,finish-start&lt;br /&gt;
                close(1)&lt;br /&gt;
            end if       &lt;br /&gt;
            &lt;br /&gt;
            call MPI_FINALIZE(IERR)&lt;br /&gt;
            &lt;br /&gt;
            !call printm(W,n,n)&lt;br /&gt;
        &lt;br /&gt;
            deallocate(W,sample,win,maswin,buf)&lt;br /&gt;
            	&lt;br /&gt;
            &lt;br /&gt;
        contains&lt;br /&gt;
        &lt;br /&gt;
        subroutine printm(W,a,b)&lt;br /&gt;
            integer a,b,i,j&lt;br /&gt;
            real W(n,n)&lt;br /&gt;
            &lt;br /&gt;
            do j=1,b,1&lt;br /&gt;
                write(*,*)(W(i,j), i=1,a)&lt;br /&gt;
            enddo&lt;br /&gt;
        end subroutine printm&lt;br /&gt;
        &lt;br /&gt;
        subroutine obnul(W,a,b)&lt;br /&gt;
            integer a,b&lt;br /&gt;
            integer i,j&lt;br /&gt;
            real W(a,b)&lt;br /&gt;
            &lt;br /&gt;
            do j=1,b,1&lt;br /&gt;
                do i=1,a,1&lt;br /&gt;
                    W(i,j) = 0.0&lt;br /&gt;
                enddo&lt;br /&gt;
            enddo&lt;br /&gt;
        end subroutine obnul&lt;br /&gt;
        &lt;br /&gt;
        real function euclDist(a,b,n)&lt;br /&gt;
            integer n,i&lt;br /&gt;
                real a(n),b(n),res&lt;br /&gt;
                &lt;br /&gt;
                res = 0&lt;br /&gt;
                do i = 1,n,1&lt;br /&gt;
                    res = res + (a(i) - b(i)) * (a(i) - b(i))&lt;br /&gt;
                enddo&lt;br /&gt;
                &lt;br /&gt;
                euclDist = sqrt(res)&lt;br /&gt;
        end function euclDist&lt;br /&gt;
        &lt;br /&gt;
        subroutine modW(W,n,learning_rate,iter,jAbsWin)&lt;br /&gt;
            integer n,iter,JAbsWin&lt;br /&gt;
            real learning_rate,W(n,n)&lt;br /&gt;
            integer i,j&lt;br /&gt;
            &lt;br /&gt;
            do j=1,n,1&lt;br /&gt;
                if (j .ne. jAbsWin) then &lt;br /&gt;
                    do i=1,n,1&lt;br /&gt;
                        W(i,j) = W(i,j) + learning_rate/sqrt(iter+1.0)*(W(i,jAbsWin)-W(i,j))&lt;br /&gt;
                    enddo&lt;br /&gt;
                end if&lt;br /&gt;
            enddo&lt;br /&gt;
        end subroutine modW&lt;br /&gt;
        &lt;br /&gt;
        subroutine randMatr(matr,a,b,small)&lt;br /&gt;
            integer a,b&lt;br /&gt;
            logical small&lt;br /&gt;
            integer i,j&lt;br /&gt;
            real matr(a,b),x&lt;br /&gt;
            &lt;br /&gt;
            do j=1,b,1&lt;br /&gt;
                do i=1,a,1&lt;br /&gt;
                    call random_seed&lt;br /&gt;
                    call random_number(x)&lt;br /&gt;
                    x = x * 10 - x * 8&lt;br /&gt;
                    if (small) then&lt;br /&gt;
                        x = x / 40&lt;br /&gt;
                    end if&lt;br /&gt;
                    matr(i,j) = x&lt;br /&gt;
                enddo&lt;br /&gt;
            enddo&lt;br /&gt;
        end subroutine randMatr&lt;br /&gt;
    end program Kohonen&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Последовательные реализации:&lt;br /&gt;
&lt;br /&gt;
* [https://cran.r-project.org/web/packages/kohonen/ kohonen] Бибилиотека для R&lt;br /&gt;
* [https://pypi.python.org/pypi/kohonen kohonen] Бибилиотека для Python&lt;br /&gt;
* [http://knnl.sourceforge.net/ KNNL]: C++ бибилиотека.&lt;br /&gt;
* [https://www.mathworks.com/help/nnet/ug/cluster-with-self-organizing-map-neural-network.html?requestedDomain=www.mathworks.com Matlab realization]: Реализация в Matlab.&lt;br /&gt;
* [http://jknnl.sourceforge.net/ JKNNL]: Библиотека для Java.&lt;br /&gt;
* [https://support.sas.com/documentation/onlinedoc/miner/casestudy_59123.pdf SAS EM]: Реализация в SAS Enterprise Miner&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Параллельные реализации:&lt;br /&gt;
&lt;br /&gt;
* [https://github.com/peterwittek/somoclu Somoclu]: Библиотека для Python, R, Julia, и Matlab с поддержкой OpenMP, MPI и CUDA.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
 &lt;br /&gt;
# Teuvo Kohonen, Self-Organizing Maps/Helsinki University of Technology,1997/Second Edition.&lt;br /&gt;
# Нейский И.М. Классификация и сравнение методов кластеризации http://it-claim.ru/Persons/Neyskiy/Article2_Neiskiy.pdf&lt;/div&gt;</summary>
		<author><name>Teplov</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9E%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D1%83%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA%D0%B0:Skripnikarthur&amp;diff=18947</id>
		<title>Обсуждение участника:Skripnikarthur</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9E%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D1%83%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA%D0%B0:Skripnikarthur&amp;diff=18947"/>
		<updated>2016-11-22T12:10:11Z</updated>

		<summary type="html">&lt;p&gt;Teplov: /* Замечания */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Участники:Потапов Юрий Скрипник Артур/Алгоритм концептуальной кластеризации COBWEB ==&lt;br /&gt;
&lt;br /&gt;
=== Замечания ===&lt;br /&gt;
* не указан вклад авторов [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 02:09, 23 октября 2016 (MSK)&lt;br /&gt;
* лучше выделить формулы в отдельные блоки для лучшей читаемости [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 02:09, 23 октября 2016 (MSK)&lt;br /&gt;
* использованы не очень наглядные графы часто мелкие и плохо читаемые [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 02:09, 23 октября 2016 (MSK)&lt;br /&gt;
* не указано для каких параметров построены графы [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 02:09, 23 октября 2016 (MSK)&lt;br /&gt;
* нет оценок объема входных и выходных данных [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 02:09, 23 октября 2016 (MSK)&lt;br /&gt;
* в п. 1.10 нет оценки вычислительной мощности алгоритма [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 02:09, 23 октября 2016 (MSK)&lt;br /&gt;
* отсутствуют данные по численным экспериментам и масштабируемости [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 15:10, 22 ноября 2016 (MSK)&lt;/div&gt;</summary>
		<author><name>Teplov</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9E%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D1%83%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA%D0%B0:%D0%9C%D1%8F%D0%B7%D0%B8%D0%BD%D0%B0_%D0%95%D0%BA%D0%B0%D1%82%D0%B5%D1%80%D0%B8%D0%BD%D0%B0&amp;diff=18946</id>
		<title>Обсуждение участника:Мязина Екатерина</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9E%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D1%83%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA%D0%B0:%D0%9C%D1%8F%D0%B7%D0%B8%D0%BD%D0%B0_%D0%95%D0%BA%D0%B0%D1%82%D0%B5%D1%80%D0%B8%D0%BD%D0%B0&amp;diff=18946"/>
		<updated>2016-11-22T12:08:55Z</updated>

		<summary type="html">&lt;p&gt;Teplov: /* Замечания */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Статья [[Участник:Мязина Екатерина/Алгоритм концептуальной кластеризации COBWEB]] ==&lt;br /&gt;
&lt;br /&gt;
=== Замечания ===&lt;br /&gt;
* отсутствует список литературы [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 01:55, 23 октября 2016 (MSK)&lt;br /&gt;
* отсутствует указание автора в статье [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 01:55, 23 октября 2016 (MSK)&lt;br /&gt;
* текст нужно больше структурировать, формулы выделить. Читаемость текста очень низкая [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 01:55, 23 октября 2016 (MSK)&lt;br /&gt;
* граф очень мелкий, непонятный [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 01:55, 23 октября 2016 (MSK)&lt;br /&gt;
* рисунки не подписаны и не пронумерованы [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 01:55, 23 октября 2016 (MSK)&lt;br /&gt;
* в кратких свойствах необходимо указать не только последовательную сложность [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 01:55, 23 октября 2016 (MSK)&lt;br /&gt;
* необходимо указать для каких параметров построен граф [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 01:55, 23 октября 2016 (MSK)&lt;br /&gt;
* отсутствует п. 1.10 [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 01:55, 23 октября 2016 (MSK)&lt;br /&gt;
* отсутствуют данные оп численному эксперименту и масштабируемости [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 15:08, 22 ноября 2016 (MSK)&lt;/div&gt;</summary>
		<author><name>Teplov</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9E%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D1%83%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA%D0%B0:Lkruglov&amp;diff=18945</id>
		<title>Обсуждение участника:Lkruglov</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9E%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D1%83%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA%D0%B0:Lkruglov&amp;diff=18945"/>
		<updated>2016-11-22T12:07:39Z</updated>

		<summary type="html">&lt;p&gt;Teplov: /* Замечания */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Статья [[Участники: Круглов Леонид, Баранов Максим/Алгоритм устойчивой кластеризации с использованием связей]] ==&lt;br /&gt;
&lt;br /&gt;
=== Замечания ===&lt;br /&gt;
* формулы нужно оформить в отдельные блоки для лучшей читаемости [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 01:39, 23 октября 2016 (MSK)&lt;br /&gt;
* необходимо указать на каком языке приведен код [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 01:39, 23 октября 2016 (MSK)&lt;br /&gt;
* необходимо указать для каких параметров построен граф [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 01:39, 23 октября 2016 (MSK)&lt;br /&gt;
* в п. 1.10 необходимо явно указать вычислительную мощность алгоритма [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 01:39, 23 октября 2016 (MSK)&lt;br /&gt;
* отсутствует краткая сводка свойств алгоритма [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 02:14, 23 октября 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
* необходимо в п.2.4 провести анализ масштабируемости алгоритма. Описать конфигурации запуска и т.п. Эксперименты провести с изменением числа процессоров/потоков/процессов и размера задачи. [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 15:07, 22 ноября 2016 (MSK)&lt;br /&gt;
* результаты анализа необходимо представить в виде наглядного графика. [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 15:07, 22 ноября 2016 (MSK)&lt;/div&gt;</summary>
		<author><name>Teplov</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:LKruglov/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D1%83%D1%81%D1%82%D0%BE%D0%B9%D1%87%D0%B8%D0%B2%D0%BE%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%81%D0%B2%D1%8F%D0%B7%D0%B5%D0%B9&amp;diff=18944</id>
		<title>Участник:LKruglov/Алгоритм устойчивой кластеризации с использованием связей</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:LKruglov/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D1%83%D1%81%D1%82%D0%BE%D0%B9%D1%87%D0%B8%D0%B2%D0%BE%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%81%D0%B2%D1%8F%D0%B7%D0%B5%D0%B9&amp;diff=18944"/>
		<updated>2016-11-22T12:05:46Z</updated>

		<summary type="html">&lt;p&gt;Teplov: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Алгоритм устойчивой кластеризации с использованием связей&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(n^{2,37} + n^2\log n)&amp;lt;/math&amp;gt;, если для вычисления количества связей используется алгоритм Копперсмита—Винограда&lt;br /&gt;
| input_data        = Вектор из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек в виде векторов характеристик произвольного вида, дополнительно одно вещественное и одно челое число &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = Сгруппированные в &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; классифицируемых точек представленных индексами во входном векторе&lt;br /&gt;
}}&lt;br /&gt;
Авторы: [[Участник:LKruglov|Круглов Л. В.]](1.1,1.2,1.3,1.6,1.8,1.10), [[Участник:Innot|Баранов М. С.]](1.1,1.2,1.3,1.4,1.5,1.7,1.9,2.7)&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
=== Свойства и структура алгоритма ===&lt;br /&gt;
В связи с наличием огромного количества накопленной информации задача добычи данных становится все более важной. Кластеризация позволяет распределять данные по группам (кластерам) так, что в каждой группе данные обладают схожими характеристиками, а в разных группах характеристики различаются.&lt;br /&gt;
&lt;br /&gt;
Алгоритм устойчивой кластеризации с использованием связей (robust clustering using links, ROCK [1]) построен на основе понятий связей между кластеризуемыми точками и их соседства. Две точки считаются соседями, если являются достаточно близким (схожими): &amp;lt;math&amp;gt;sim({p_i}, {p_j}) &amp;gt;= \theta&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt; - заданное пороговое значение в интервале &amp;lt;math&amp;gt;(0, 1)&amp;lt;/math&amp;gt;. Связь &amp;lt;math&amp;gt;link({p_i}, {p_j})&amp;lt;/math&amp;gt; между двумя точками определяется как число общих соседей между точками &amp;lt;math&amp;gt;{p_i}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;{p_j}&amp;lt;/math&amp;gt;. Таким образом, чем большее значение функции связи для двух точек, тем более вероятно они принадлежат одному кластеру.&lt;br /&gt;
&lt;br /&gt;
В отличие от алгоритмов кластеризации, учитывающих только локальные характеристики точек, данный алгоритм учитывает глобальную информацию о соседстве при принятии решений о выделении кластеров, что делает его очень устойчивым [1].&lt;br /&gt;
&lt;br /&gt;
ROCK принадлежит к классу иерархических алгоритмов кластеризации. На первом этапе работы алгоритма происходит определение соседей для каждой точки и подсчет количества связей для каждой пары точек. Изначально каждая точка рассматривается в качестве отдельного кластера и для каждого такого кластера рассчитываются значения функции качества,которые используются при последующем слиянии кластеров. Далее в ходе итерационного процесса на каждом шаге выбираются и объединяются два кластера и пересчитываются количество связей и функции качества для нового кластера. По достижении требуемого числа кластеров работа алгоритма завершается.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
Задача кластеризации сводится к выделению такого разбиения точек на кластеры, которое максимизирует сумму связей точек принадлежащих одному кластеру, и минимизирует эту функцию для точек из разных кластеров. Таким образом, для разбияния на &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров необходима максимизация следующей функции.&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;E_l = \sum_{i=1}^k n_i * \sum_{p_q, p_r \in C_i} \frac{link(p_q, p_r)}{n_i^{1+2f(\theta)}} \qquad(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Данная функция отличается от простой суммы связей для каждого кластера, так как не обеспечивает распределение точек, имеющих мало связей между собой, по разным кластерам. Поэтому действительную сумму связей в кластере следует разделить на ожидаемую сумму связей. Функция &amp;lt;math&amp;gt;f(\theta)&amp;lt;/math&amp;gt; задается пользователем исходя из свойств обрабатываемых данных и ожидаемого характера кластеризации. Она должна обладать следующим свойством: каждая точка, принадлежащая кластеру &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;, имеет приблизительно &amp;lt;math&amp;gt;n_i^{f(\theta)}&amp;lt;/math&amp;gt; соседей в этом кластере.&lt;br /&gt;
&lt;br /&gt;
Связь между двумя кластерами определяется как&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;link({C_i}, {C_j}) = \sum^{}_{{p_q}\in{C_i}, {p_r}\in{C_j}}{link[{C_i}, {C_j}]}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Тогда для выбора кластеров для объединения определяется целевая функция качества:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;g(C_i, C_j) = \frac{link[C_i, C_j]}{(n_i+n_j)^{1+2f(\theta)} - f_i^{1+2f(\theta)} - f_j^{1+2f(\theta)}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Аналогично целевой функции (1) значение связи между двумя кластерами делится на ожидаемое число связей между кластерами.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро алгоритма состоит из двух частей:&lt;br /&gt;
# Подсчет количества связей между точками;&lt;br /&gt;
# Итеративное объединение кластеров.&lt;br /&gt;
&lt;br /&gt;
Один из возможных подходов к подсчету количества связей основывается на матрице смежности точек: достаточно умножить матрицу смежности на себя, и тогда каждая клетка полученной матрицы будет содержать число связей соответствующей точки.&lt;br /&gt;
&lt;br /&gt;
Итеративная часть алгоритма может быть реализована с помощью структуры данных куча. На каждой итерации с каждым кластером &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; связана своя куча &amp;lt;math&amp;gt;q[i]&amp;lt;/math&amp;gt;, содержащая все связанный с ним кластеры, упорядоченная по убыванию функции качества. Также поддерживается глобальная куча &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt;, содержащая все кластеры &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, упорядоченные по мере убывания функции качества &amp;lt;math&amp;gt;g(j, max(q[j]))&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;max(q[j])&amp;lt;/math&amp;gt; - это вершина кучи &amp;lt;math&amp;gt;q[j]&amp;lt;/math&amp;gt;. Тогда на каждой итерации объединяются кластер &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; с вершины глобальной кучи и кластер &amp;lt;math&amp;gt;q[j]&amp;lt;/math&amp;gt;. После объединения необходимо обновить все кучи, содержащие объединенные кластеры, обновить глобальную кучу &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; и создать кучу для нового кластера. Итеративный процесс продолжается до тех пор, пока в глобальной куче &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; не останется требуемое число кластеров.&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
Как было описано выше, основную часть каждой итерации алгоритма составляет обновление куч, содержащих сливаемые кластеры.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
Следующая схема последовательного алгоритма представлена в работе [1] (псевдокод):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot; line&amp;gt;&lt;br /&gt;
procedure cluster(S, k)&lt;br /&gt;
begin&lt;br /&gt;
    link := compute_links(S)&lt;br /&gt;
    for s in S do&lt;br /&gt;
    begin&lt;br /&gt;
        q[s] := build_local_heap(link, s)&lt;br /&gt;
    end&lt;br /&gt;
    Q := build_global_heap(S, q)&lt;br /&gt;
    while size(Q) &amp;gt; k do&lt;br /&gt;
    begin&lt;br /&gt;
        u := extract_max(Q)&lt;br /&gt;
        v := max(q[u])&lt;br /&gt;
        delete(Q, v)&lt;br /&gt;
        w := merge(u, v)&lt;br /&gt;
        for each x in q[u] or q[v] do&lt;br /&gt;
        begin&lt;br /&gt;
            link[x, w] := link[x, u] + link[x, v]&lt;br /&gt;
            delete(q[x], u)&lt;br /&gt;
            delete(q[x], v)&lt;br /&gt;
            insert(q[x], w, g(x, w))&lt;br /&gt;
            insert(q[w], x, g(x, w))&lt;br /&gt;
            update(Q, x, q[x])&lt;br /&gt;
        end&lt;br /&gt;
        insert(Q, w, q[w])&lt;br /&gt;
        deallocate(q[u])&lt;br /&gt;
        deallocate(q[v])&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Для вычисления количества связей с помощью возведения матрицы в квадрат можно воспользоваться алгоритмом Копперсмита—Винограда [2], вычислительная сложность которого равна &amp;lt;math&amp;gt;O(n^{2,37})&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; - количество кластеризуемых точек). Для случаев разреженной матрицы смежности в работе [1] описывается алгоритм, имеющий сложность &amp;lt;math&amp;gt;O(n^2 + n{m_m}{m_a})&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;{m_a}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;{m_m}&amp;lt;/math&amp;gt; - это среднее и максимальное количество соседей точки соответственно.&lt;br /&gt;
&lt;br /&gt;
Каждая куча может быть построена за время &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; [3]. Внешний цикл итерационной части алгоритма исполняется &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; раз. Так как на очередной итерации размер каждой локальной кучи в худшем случае может равняться &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;, то новый кластер должен быть помещен в &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; локальных куч, что дает временную сложность вложенного цикла &amp;lt;math&amp;gt;O(n\log{n})&amp;lt;/math&amp;gt;. Таким образом, сложность внешнего цикла в худшем случае будет &amp;lt;math&amp;gt;O(n^2\log{n})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Итак, оценка времени выполнения алгоритма &amp;lt;math&amp;gt;C_t&amp;lt;/math&amp;gt; задается следующей формулой:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;C_t = O(n^{2,37} + n^2\log{n})&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Пространственную сложность подсчета количества связей можно оценить как &amp;lt;math&amp;gt;O(min(n^2, n{m_m}{m_a}))&amp;lt;/math&amp;gt;. Так как при слиянии двух кластеров размер новой кучи не превосходит суммы размеров куч объединяемых кластеров, то пространственная сложность итерационной части алгоритма зависит только от размеров исходных куч и задается формулой:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;C_s = O(min(n^2, n{m_m}{m_a}))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
[[Файл:ROCK_graph.png|400px|thumb|right|Визуализация информационного графа для n = 100 исходных точек и k = 8 требуемых кластеров]]&lt;br /&gt;
На рисунке представлен информационный граф алгоритма. Используются следующие обозначения:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;I&#039;&#039;&#039; - входные данные&lt;br /&gt;
* &#039;&#039;&#039;M&#039;&#039;&#039; - вычисление матрицы связей&lt;br /&gt;
* &#039;&#039;&#039;QS&#039;&#039;&#039; - созданиение локальной кучи&lt;br /&gt;
* &#039;&#039;&#039;QG&#039;&#039;&#039; - создание глобальной кучи&lt;br /&gt;
* &#039;&#039;&#039;HG&#039;&#039;&#039; - взятие элемента/элементов из кучи&lt;br /&gt;
* &#039;&#039;&#039;HD&#039;&#039;&#039; - удаление элемента из кучи&lt;br /&gt;
* &#039;&#039;&#039;HI&#039;&#039;&#039; - добавление элемента в кучу&lt;br /&gt;
* &#039;&#039;&#039;HU&#039;&#039;&#039; - обновление кучи&lt;br /&gt;
* &#039;&#039;&#039;HX&#039;&#039;&#039; - удаление куч&lt;br /&gt;
* &#039;&#039;&#039;CM&#039;&#039;&#039; - слияние кластеров&lt;br /&gt;
* &#039;&#039;&#039;A&#039;&#039;&#039; - арифметические операции&lt;br /&gt;
* &#039;&#039;&#039;K&#039;&#039;&#039; - полученный кластер&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
В описанном последовательном алгоритме основной ресурс параллелизма заключается в возможности параллельного вычисления количества связей (например, используя алгоритм параллельного вычисления произведения матриц), создания очередей, и параллельной обработки локальных очередей во внутреннем цикле итерационного этапа алгоритма. В таком случае при распараллеливании алгоритма на &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; вычислительных узлов верна следующая оценка времени работы алгоритма:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;C_{t_p} = {C_{L_p}} + O(\frac{n^2\log n}{p})&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;{C_{L_p}}&amp;lt;/math&amp;gt; - оценка времени работы выбранного параллельного алгоритма подсчета числа связей.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
Входные данные:&lt;br /&gt;
&lt;br /&gt;
 1. Множество &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек, которые необходимо кластеризовать&lt;br /&gt;
 2. Пороговое значение &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt; из интервала &amp;lt;math&amp;gt;(0, 1)&amp;lt;/math&amp;gt;&lt;br /&gt;
 3. Требуемое число кластеров &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Также к входным данным относится информация о характеристиках точек (например в виде таблицы булевых значений принадлежности характеристик точкам).&lt;br /&gt;
&lt;br /&gt;
Выходные данные:&lt;br /&gt;
&lt;br /&gt;
 1. &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек, распределенных по &amp;lt;math&amp;gt;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;
:&amp;lt;math&amp;gt;\frac{O(n^{2,37} + n^2\log n)}{2n+2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Отношение последовательной и параллельной сложности:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;\frac{O(n^{2,37} + n^2\log n)}{{C_{L_p}} + O(\frac{n^2\log n}{p})}&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;C_{L_p}&amp;lt;/math&amp;gt; - сложность параллельного алгоритма подсчета числа связей (оценки приведены для использования алгоритма Копперсмита—Винограда для вычисления количества связей).&lt;br /&gt;
&lt;br /&gt;
Одним из важных вопросов, влияющих на эффективность алгоритма, является выбор исходных точек. Подход к выбору размера исходного набора точек, достаточного для качественной кластеризации, описывается в работе [4]. Использование случайной стратегии выбора исходных точек помогает алгоритму кластеризации отбрасыванием выпадающих точек (то есть точек, слабо связанных с другими точками). Различные алгоритмы случайного выбора исходных точек рассматриваются в работе [5].&lt;br /&gt;
&lt;br /&gt;
Обработка выпадающих точек в рассматриваемом алгоритме обеспечивается выбором порогового значения &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt;. Также такие точки могут быть принудительно отброшены на последних итерациях алгоритма.&lt;br /&gt;
&lt;br /&gt;
После завершения кластеризации выбранных исходных точек, оставшиеся точки распределяются по кластерам следующим образом. Из каждого кластера выбирается набор точек &amp;lt;math&amp;gt;{L_i}&amp;lt;/math&amp;gt;, и очередная точка &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; помещается в кластер &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;, если &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; имеет наибольшее число соседей множеством &amp;lt;math&amp;gt;{L_i}&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;
Для оценки производительности была выбрана реализация из [https://github.com/annoviko/pyclustering pyclustering], выполненная на C++. Исходная версия алгоритма показала следующие результаты. Здесь и далее для тестирования использовался суперкомпьютер «Ломоносов» [6] [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Название теста&lt;br /&gt;
! Время выполнения (сек)&lt;br /&gt;
! Количество точек&lt;br /&gt;
! Количество кластеров&lt;br /&gt;
|-&lt;br /&gt;
| Chainlink&lt;br /&gt;
| 312.7&lt;br /&gt;
| 1500&lt;br /&gt;
| 2&lt;br /&gt;
|-&lt;br /&gt;
| TwoDiamonds&lt;br /&gt;
| 160.4&lt;br /&gt;
| 1270&lt;br /&gt;
| 2&lt;br /&gt;
|-&lt;br /&gt;
| Target&lt;br /&gt;
| 142.9&lt;br /&gt;
| 1150&lt;br /&gt;
| 6&lt;br /&gt;
|-&lt;br /&gt;
| WingNut&lt;br /&gt;
| 1324.6&lt;br /&gt;
| 2400&lt;br /&gt;
| 2&lt;br /&gt;
|-&lt;br /&gt;
| EngyTime&lt;br /&gt;
| 2728.09&lt;br /&gt;
| 4096&lt;br /&gt;
| 13&lt;br /&gt;
|-&lt;br /&gt;
| GolfBall&lt;br /&gt;
| 2429.69&lt;br /&gt;
| 4002&lt;br /&gt;
| 13&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Из всех методов, задействованных в реализации, возможности для параллельного выполнения включали два, &#039;&#039;calculate_links&#039;&#039; и &#039;&#039;create_adjacency_matrix&#039;&#039;. В обоих случаях были предприняты попытки использования этих возможностей.&lt;br /&gt;
&lt;br /&gt;
Метод &#039;&#039;calculate_links&#039;&#039; в оригинальной версии отвечал за 20-25% общего времени выполнения. После распараллеливания на OpenMP производительность только ухудшилась, тот же результат принесло использование MPI. Для наглядности исходный код метода приведен ниже:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c++&amp;quot; line&amp;gt;&lt;br /&gt;
size_t rock::calculate_links(const cluster &amp;amp; cluster1, const cluster &amp;amp; cluster2) const {&lt;br /&gt;
    size_t number_links = 0;&lt;br /&gt;
    for (auto i : cluster1)&lt;br /&gt;
        for (auto j : cluster2)&lt;br /&gt;
            number_links += (size_t) m_adjacency_matrix.has_connection(i, j);&lt;br /&gt;
    return number_links;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Как видно, проблема заключается в том, что метод практически не содержит вычислений, и его производительность определяется в первую очередь производительностью памяти. Таким образом, OpenMP просто распределил время ожидания доступа к памяти между различными процессами, а MPI добавил времени за счет выполнения передач между узлами.&lt;br /&gt;
&lt;br /&gt;
Метод &#039;&#039;create_adjacency_matrix&#039;&#039; также был распараллелен с помощью MPI. К качественному ускорению это не привело в первую очередь из-за того, что на использованных тестах среднее время выполнения этого метода занимало около 0.004% от общего времени выполнения. Распараллеленный код приведен ниже для справки.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c++&amp;quot; line&amp;gt;&lt;br /&gt;
void rock::create_adjacency_matrix(const dataset &amp;amp; p_data) {&lt;br /&gt;
    auto m_adjacency_matrix = adjacency_matrix(p_data.size());&lt;br /&gt;
    int numprocs = 0, rank = 0, len = 0, rc = 0;&lt;br /&gt;
    char hostname[MPI_MAX_PROCESSOR_NAME];&lt;br /&gt;
&lt;br /&gt;
    MPI_Init(&amp;amp;argc, &amp;amp;argv);&lt;br /&gt;
    MPI_Comm_size(MPI_COMM_WORLD, &amp;amp;numprocs);&lt;br /&gt;
    MPI_Comm_rank(MPI_COMM_WORLD, &amp;amp;rank);&lt;br /&gt;
    MPI_Get_processor_name(hostname, &amp;amp;len);&lt;br /&gt;
    printf (&amp;quot;Number of tasks= %d My rank= %d Running on %s\n&amp;quot;, numprocs, rank, hostname);&lt;br /&gt;
&lt;br /&gt;
    size_t msize = m_adjacency_matrix.size();&lt;br /&gt;
    unsigned num_tasks = msize / numprocs;&lt;br /&gt;
    unsigned delta = msize % numprocs;&lt;br /&gt;
    MPI_Request reqs[msize];&lt;br /&gt;
    MPI_Status stats[msize];&lt;br /&gt;
&lt;br /&gt;
    if (rank == 0) {&lt;br /&gt;
        // Initialize async receives for rows computed at other nodes&lt;br /&gt;
        for (size_t r = 1; r != numprocs; r++) {&lt;br /&gt;
            for (size_t i = num_tasks*r; i &amp;lt; num_tasks*(r + 1); i++) {&lt;br /&gt;
                MPI_Irecv(&amp;amp;(m_adjacency_matrix.raw_data()[i][0]), msize, MPI_DOUBLE, r, MPI_ANY_TAG, MPI_COMM_WORLD, reqs + i);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // compute what is needed&lt;br /&gt;
    for (size_t i = num_tasks*rank; i &amp;lt; num_tasks*(rank + 1); i++) {&lt;br /&gt;
        for (size_t j = 0; j &amp;lt; msize; j++) {&lt;br /&gt;
            double distance = euclidean_distance_sqrt(&amp;amp;p_data[i], &amp;amp;p_data[j]);&lt;br /&gt;
&lt;br /&gt;
            if (distance &amp;lt; m_radius) {&lt;br /&gt;
                m_adjacency_matrix.set_connection(i, j);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (rank == 0) {&lt;br /&gt;
        // finish is there&#039;s anything left&lt;br /&gt;
        for (size_t i = num_tasks * numprocs; i != msize; i++) {&lt;br /&gt;
            for (size_t j = 0; j &amp;lt; msize; j++) {&lt;br /&gt;
                double distance = euclidean_distance_sqrt(&amp;amp;p_data[i], &amp;amp;p_data[j]);&lt;br /&gt;
&lt;br /&gt;
                if (distance &amp;lt; m_radius) {&lt;br /&gt;
                    m_adjacency_matrix.set_connection(i, j);&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        MPI_Waitall(msize, reqs, stats);&lt;br /&gt;
    } else {&lt;br /&gt;
        // send the data to node 0&lt;br /&gt;
        for (size_t i = num_tasks*rank; i &amp;lt; num_tasks*(rank + 1); i++) {&lt;br /&gt;
            MPI_Isend(&amp;amp;(m_adjacency_matrix.raw_data()[i][0]), msize, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD, reqs + i);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    MPI_Finalize();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&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;
# Библиотека [https://github.com/annoviko/pyclustering annoviko/pyclustering] включает реализации для Python и C++ под GPL 3&lt;br /&gt;
# Пакет [https://cran.r-project.org/web/packages/cba/index.html CBA]  из репозитория CRAN содержит реализацию на R под GPL 2&lt;br /&gt;
# Библиотеки [http://www.cs.waikato.ac.nz/ml/weka/ Weka] и [https://sourceforge.net/projects/feed4weka/files/?source=navbar feed4weka] содержат реализацию для Java под GPL 2&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
[1] Guha S., Rastogi R., Shim K. ROCK: A robust clustering algorithm for categorical attributes //Data Engineering, 1999. Proceedings., 15th International Conference on. – IEEE, 1999. – С. 512-521.&lt;br /&gt;
&lt;br /&gt;
[2] Coppersmith D., Winograd S. Matrix multiplication via arithmetic progressions //Proceedings of the nineteenth annual ACM symposium on Theory of computing. – ACM, 1987. – С. 1-6.&lt;br /&gt;
&lt;br /&gt;
[3] Thomas H. Cormen et al. Introduction to algorithms. – Cambridge : MIT press, 2001. – Т. 6.&lt;br /&gt;
&lt;br /&gt;
[4] Guha S., Rastogi R., Shim K. Cure: an efficient clustering algorithm for large databases //Information Systems. – 2001. – Т. 26. – №. 1. – С. 35-58.&lt;br /&gt;
&lt;br /&gt;
[5] Vitter J. S. Random sampling with a reservoir //ACM Transactions on Mathematical Software (TOMS). – 1985. – Т. 11. – №. 1. – С. 37-57.&lt;br /&gt;
&lt;br /&gt;
[6] Воеводин Вл., Жуматий С., Соболев С., Антонов А., Брызгалов П., Никитенко Д., Стефанов К., Воеводин Вад. Практика суперкомпьютера «Ломоносов» // Открытые системы, 2012, N 7, С. 36-39.&lt;/div&gt;</summary>
		<author><name>Teplov</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:LKruglov/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D1%83%D1%81%D1%82%D0%BE%D0%B9%D1%87%D0%B8%D0%B2%D0%BE%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%81%D0%B2%D1%8F%D0%B7%D0%B5%D0%B9&amp;diff=18943</id>
		<title>Участник:LKruglov/Алгоритм устойчивой кластеризации с использованием связей</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:LKruglov/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D1%83%D1%81%D1%82%D0%BE%D0%B9%D1%87%D0%B8%D0%B2%D0%BE%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%81%D0%B2%D1%8F%D0%B7%D0%B5%D0%B9&amp;diff=18943"/>
		<updated>2016-11-22T12:05:30Z</updated>

		<summary type="html">&lt;p&gt;Teplov: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{{Assignment|Teplov}}&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Алгоритм устойчивой кластеризации с использованием связей&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(n^{2,37} + n^2\log n)&amp;lt;/math&amp;gt;, если для вычисления количества связей используется алгоритм Копперсмита—Винограда&lt;br /&gt;
| input_data        = Вектор из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек в виде векторов характеристик произвольного вида, дополнительно одно вещественное и одно челое число &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = Сгруппированные в &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; классифицируемых точек представленных индексами во входном векторе&lt;br /&gt;
}}&lt;br /&gt;
Авторы: [[Участник:LKruglov|Круглов Л. В.]](1.1,1.2,1.3,1.6,1.8,1.10), [[Участник:Innot|Баранов М. С.]](1.1,1.2,1.3,1.4,1.5,1.7,1.9,2.7)&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
=== Свойства и структура алгоритма ===&lt;br /&gt;
В связи с наличием огромного количества накопленной информации задача добычи данных становится все более важной. Кластеризация позволяет распределять данные по группам (кластерам) так, что в каждой группе данные обладают схожими характеристиками, а в разных группах характеристики различаются.&lt;br /&gt;
&lt;br /&gt;
Алгоритм устойчивой кластеризации с использованием связей (robust clustering using links, ROCK [1]) построен на основе понятий связей между кластеризуемыми точками и их соседства. Две точки считаются соседями, если являются достаточно близким (схожими): &amp;lt;math&amp;gt;sim({p_i}, {p_j}) &amp;gt;= \theta&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt; - заданное пороговое значение в интервале &amp;lt;math&amp;gt;(0, 1)&amp;lt;/math&amp;gt;. Связь &amp;lt;math&amp;gt;link({p_i}, {p_j})&amp;lt;/math&amp;gt; между двумя точками определяется как число общих соседей между точками &amp;lt;math&amp;gt;{p_i}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;{p_j}&amp;lt;/math&amp;gt;. Таким образом, чем большее значение функции связи для двух точек, тем более вероятно они принадлежат одному кластеру.&lt;br /&gt;
&lt;br /&gt;
В отличие от алгоритмов кластеризации, учитывающих только локальные характеристики точек, данный алгоритм учитывает глобальную информацию о соседстве при принятии решений о выделении кластеров, что делает его очень устойчивым [1].&lt;br /&gt;
&lt;br /&gt;
ROCK принадлежит к классу иерархических алгоритмов кластеризации. На первом этапе работы алгоритма происходит определение соседей для каждой точки и подсчет количества связей для каждой пары точек. Изначально каждая точка рассматривается в качестве отдельного кластера и для каждого такого кластера рассчитываются значения функции качества,которые используются при последующем слиянии кластеров. Далее в ходе итерационного процесса на каждом шаге выбираются и объединяются два кластера и пересчитываются количество связей и функции качества для нового кластера. По достижении требуемого числа кластеров работа алгоритма завершается.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
Задача кластеризации сводится к выделению такого разбиения точек на кластеры, которое максимизирует сумму связей точек принадлежащих одному кластеру, и минимизирует эту функцию для точек из разных кластеров. Таким образом, для разбияния на &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров необходима максимизация следующей функции.&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;E_l = \sum_{i=1}^k n_i * \sum_{p_q, p_r \in C_i} \frac{link(p_q, p_r)}{n_i^{1+2f(\theta)}} \qquad(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Данная функция отличается от простой суммы связей для каждого кластера, так как не обеспечивает распределение точек, имеющих мало связей между собой, по разным кластерам. Поэтому действительную сумму связей в кластере следует разделить на ожидаемую сумму связей. Функция &amp;lt;math&amp;gt;f(\theta)&amp;lt;/math&amp;gt; задается пользователем исходя из свойств обрабатываемых данных и ожидаемого характера кластеризации. Она должна обладать следующим свойством: каждая точка, принадлежащая кластеру &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;, имеет приблизительно &amp;lt;math&amp;gt;n_i^{f(\theta)}&amp;lt;/math&amp;gt; соседей в этом кластере.&lt;br /&gt;
&lt;br /&gt;
Связь между двумя кластерами определяется как&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;link({C_i}, {C_j}) = \sum^{}_{{p_q}\in{C_i}, {p_r}\in{C_j}}{link[{C_i}, {C_j}]}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Тогда для выбора кластеров для объединения определяется целевая функция качества:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;g(C_i, C_j) = \frac{link[C_i, C_j]}{(n_i+n_j)^{1+2f(\theta)} - f_i^{1+2f(\theta)} - f_j^{1+2f(\theta)}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Аналогично целевой функции (1) значение связи между двумя кластерами делится на ожидаемое число связей между кластерами.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро алгоритма состоит из двух частей:&lt;br /&gt;
# Подсчет количества связей между точками;&lt;br /&gt;
# Итеративное объединение кластеров.&lt;br /&gt;
&lt;br /&gt;
Один из возможных подходов к подсчету количества связей основывается на матрице смежности точек: достаточно умножить матрицу смежности на себя, и тогда каждая клетка полученной матрицы будет содержать число связей соответствующей точки.&lt;br /&gt;
&lt;br /&gt;
Итеративная часть алгоритма может быть реализована с помощью структуры данных куча. На каждой итерации с каждым кластером &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; связана своя куча &amp;lt;math&amp;gt;q[i]&amp;lt;/math&amp;gt;, содержащая все связанный с ним кластеры, упорядоченная по убыванию функции качества. Также поддерживается глобальная куча &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt;, содержащая все кластеры &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, упорядоченные по мере убывания функции качества &amp;lt;math&amp;gt;g(j, max(q[j]))&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;max(q[j])&amp;lt;/math&amp;gt; - это вершина кучи &amp;lt;math&amp;gt;q[j]&amp;lt;/math&amp;gt;. Тогда на каждой итерации объединяются кластер &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; с вершины глобальной кучи и кластер &amp;lt;math&amp;gt;q[j]&amp;lt;/math&amp;gt;. После объединения необходимо обновить все кучи, содержащие объединенные кластеры, обновить глобальную кучу &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; и создать кучу для нового кластера. Итеративный процесс продолжается до тех пор, пока в глобальной куче &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; не останется требуемое число кластеров.&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
Как было описано выше, основную часть каждой итерации алгоритма составляет обновление куч, содержащих сливаемые кластеры.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
Следующая схема последовательного алгоритма представлена в работе [1] (псевдокод):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot; line&amp;gt;&lt;br /&gt;
procedure cluster(S, k)&lt;br /&gt;
begin&lt;br /&gt;
    link := compute_links(S)&lt;br /&gt;
    for s in S do&lt;br /&gt;
    begin&lt;br /&gt;
        q[s] := build_local_heap(link, s)&lt;br /&gt;
    end&lt;br /&gt;
    Q := build_global_heap(S, q)&lt;br /&gt;
    while size(Q) &amp;gt; k do&lt;br /&gt;
    begin&lt;br /&gt;
        u := extract_max(Q)&lt;br /&gt;
        v := max(q[u])&lt;br /&gt;
        delete(Q, v)&lt;br /&gt;
        w := merge(u, v)&lt;br /&gt;
        for each x in q[u] or q[v] do&lt;br /&gt;
        begin&lt;br /&gt;
            link[x, w] := link[x, u] + link[x, v]&lt;br /&gt;
            delete(q[x], u)&lt;br /&gt;
            delete(q[x], v)&lt;br /&gt;
            insert(q[x], w, g(x, w))&lt;br /&gt;
            insert(q[w], x, g(x, w))&lt;br /&gt;
            update(Q, x, q[x])&lt;br /&gt;
        end&lt;br /&gt;
        insert(Q, w, q[w])&lt;br /&gt;
        deallocate(q[u])&lt;br /&gt;
        deallocate(q[v])&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Для вычисления количества связей с помощью возведения матрицы в квадрат можно воспользоваться алгоритмом Копперсмита—Винограда [2], вычислительная сложность которого равна &amp;lt;math&amp;gt;O(n^{2,37})&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; - количество кластеризуемых точек). Для случаев разреженной матрицы смежности в работе [1] описывается алгоритм, имеющий сложность &amp;lt;math&amp;gt;O(n^2 + n{m_m}{m_a})&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;{m_a}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;{m_m}&amp;lt;/math&amp;gt; - это среднее и максимальное количество соседей точки соответственно.&lt;br /&gt;
&lt;br /&gt;
Каждая куча может быть построена за время &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; [3]. Внешний цикл итерационной части алгоритма исполняется &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; раз. Так как на очередной итерации размер каждой локальной кучи в худшем случае может равняться &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;, то новый кластер должен быть помещен в &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; локальных куч, что дает временную сложность вложенного цикла &amp;lt;math&amp;gt;O(n\log{n})&amp;lt;/math&amp;gt;. Таким образом, сложность внешнего цикла в худшем случае будет &amp;lt;math&amp;gt;O(n^2\log{n})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Итак, оценка времени выполнения алгоритма &amp;lt;math&amp;gt;C_t&amp;lt;/math&amp;gt; задается следующей формулой:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;C_t = O(n^{2,37} + n^2\log{n})&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Пространственную сложность подсчета количества связей можно оценить как &amp;lt;math&amp;gt;O(min(n^2, n{m_m}{m_a}))&amp;lt;/math&amp;gt;. Так как при слиянии двух кластеров размер новой кучи не превосходит суммы размеров куч объединяемых кластеров, то пространственная сложность итерационной части алгоритма зависит только от размеров исходных куч и задается формулой:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;C_s = O(min(n^2, n{m_m}{m_a}))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
[[Файл:ROCK_graph.png|400px|thumb|right|Визуализация информационного графа для n = 100 исходных точек и k = 8 требуемых кластеров]]&lt;br /&gt;
На рисунке представлен информационный граф алгоритма. Используются следующие обозначения:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;I&#039;&#039;&#039; - входные данные&lt;br /&gt;
* &#039;&#039;&#039;M&#039;&#039;&#039; - вычисление матрицы связей&lt;br /&gt;
* &#039;&#039;&#039;QS&#039;&#039;&#039; - созданиение локальной кучи&lt;br /&gt;
* &#039;&#039;&#039;QG&#039;&#039;&#039; - создание глобальной кучи&lt;br /&gt;
* &#039;&#039;&#039;HG&#039;&#039;&#039; - взятие элемента/элементов из кучи&lt;br /&gt;
* &#039;&#039;&#039;HD&#039;&#039;&#039; - удаление элемента из кучи&lt;br /&gt;
* &#039;&#039;&#039;HI&#039;&#039;&#039; - добавление элемента в кучу&lt;br /&gt;
* &#039;&#039;&#039;HU&#039;&#039;&#039; - обновление кучи&lt;br /&gt;
* &#039;&#039;&#039;HX&#039;&#039;&#039; - удаление куч&lt;br /&gt;
* &#039;&#039;&#039;CM&#039;&#039;&#039; - слияние кластеров&lt;br /&gt;
* &#039;&#039;&#039;A&#039;&#039;&#039; - арифметические операции&lt;br /&gt;
* &#039;&#039;&#039;K&#039;&#039;&#039; - полученный кластер&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
В описанном последовательном алгоритме основной ресурс параллелизма заключается в возможности параллельного вычисления количества связей (например, используя алгоритм параллельного вычисления произведения матриц), создания очередей, и параллельной обработки локальных очередей во внутреннем цикле итерационного этапа алгоритма. В таком случае при распараллеливании алгоритма на &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; вычислительных узлов верна следующая оценка времени работы алгоритма:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;C_{t_p} = {C_{L_p}} + O(\frac{n^2\log n}{p})&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;{C_{L_p}}&amp;lt;/math&amp;gt; - оценка времени работы выбранного параллельного алгоритма подсчета числа связей.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
Входные данные:&lt;br /&gt;
&lt;br /&gt;
 1. Множество &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек, которые необходимо кластеризовать&lt;br /&gt;
 2. Пороговое значение &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt; из интервала &amp;lt;math&amp;gt;(0, 1)&amp;lt;/math&amp;gt;&lt;br /&gt;
 3. Требуемое число кластеров &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Также к входным данным относится информация о характеристиках точек (например в виде таблицы булевых значений принадлежности характеристик точкам).&lt;br /&gt;
&lt;br /&gt;
Выходные данные:&lt;br /&gt;
&lt;br /&gt;
 1. &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек, распределенных по &amp;lt;math&amp;gt;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;
:&amp;lt;math&amp;gt;\frac{O(n^{2,37} + n^2\log n)}{2n+2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Отношение последовательной и параллельной сложности:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;\frac{O(n^{2,37} + n^2\log n)}{{C_{L_p}} + O(\frac{n^2\log n}{p})}&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;C_{L_p}&amp;lt;/math&amp;gt; - сложность параллельного алгоритма подсчета числа связей (оценки приведены для использования алгоритма Копперсмита—Винограда для вычисления количества связей).&lt;br /&gt;
&lt;br /&gt;
Одним из важных вопросов, влияющих на эффективность алгоритма, является выбор исходных точек. Подход к выбору размера исходного набора точек, достаточного для качественной кластеризации, описывается в работе [4]. Использование случайной стратегии выбора исходных точек помогает алгоритму кластеризации отбрасыванием выпадающих точек (то есть точек, слабо связанных с другими точками). Различные алгоритмы случайного выбора исходных точек рассматриваются в работе [5].&lt;br /&gt;
&lt;br /&gt;
Обработка выпадающих точек в рассматриваемом алгоритме обеспечивается выбором порогового значения &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt;. Также такие точки могут быть принудительно отброшены на последних итерациях алгоритма.&lt;br /&gt;
&lt;br /&gt;
После завершения кластеризации выбранных исходных точек, оставшиеся точки распределяются по кластерам следующим образом. Из каждого кластера выбирается набор точек &amp;lt;math&amp;gt;{L_i}&amp;lt;/math&amp;gt;, и очередная точка &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; помещается в кластер &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;, если &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; имеет наибольшее число соседей множеством &amp;lt;math&amp;gt;{L_i}&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;
Для оценки производительности была выбрана реализация из [https://github.com/annoviko/pyclustering pyclustering], выполненная на C++. Исходная версия алгоритма показала следующие результаты. Здесь и далее для тестирования использовался суперкомпьютер «Ломоносов» [6] [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского университета].&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Название теста&lt;br /&gt;
! Время выполнения (сек)&lt;br /&gt;
! Количество точек&lt;br /&gt;
! Количество кластеров&lt;br /&gt;
|-&lt;br /&gt;
| Chainlink&lt;br /&gt;
| 312.7&lt;br /&gt;
| 1500&lt;br /&gt;
| 2&lt;br /&gt;
|-&lt;br /&gt;
| TwoDiamonds&lt;br /&gt;
| 160.4&lt;br /&gt;
| 1270&lt;br /&gt;
| 2&lt;br /&gt;
|-&lt;br /&gt;
| Target&lt;br /&gt;
| 142.9&lt;br /&gt;
| 1150&lt;br /&gt;
| 6&lt;br /&gt;
|-&lt;br /&gt;
| WingNut&lt;br /&gt;
| 1324.6&lt;br /&gt;
| 2400&lt;br /&gt;
| 2&lt;br /&gt;
|-&lt;br /&gt;
| EngyTime&lt;br /&gt;
| 2728.09&lt;br /&gt;
| 4096&lt;br /&gt;
| 13&lt;br /&gt;
|-&lt;br /&gt;
| GolfBall&lt;br /&gt;
| 2429.69&lt;br /&gt;
| 4002&lt;br /&gt;
| 13&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Из всех методов, задействованных в реализации, возможности для параллельного выполнения включали два, &#039;&#039;calculate_links&#039;&#039; и &#039;&#039;create_adjacency_matrix&#039;&#039;. В обоих случаях были предприняты попытки использования этих возможностей.&lt;br /&gt;
&lt;br /&gt;
Метод &#039;&#039;calculate_links&#039;&#039; в оригинальной версии отвечал за 20-25% общего времени выполнения. После распараллеливания на OpenMP производительность только ухудшилась, тот же результат принесло использование MPI. Для наглядности исходный код метода приведен ниже:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c++&amp;quot; line&amp;gt;&lt;br /&gt;
size_t rock::calculate_links(const cluster &amp;amp; cluster1, const cluster &amp;amp; cluster2) const {&lt;br /&gt;
    size_t number_links = 0;&lt;br /&gt;
    for (auto i : cluster1)&lt;br /&gt;
        for (auto j : cluster2)&lt;br /&gt;
            number_links += (size_t) m_adjacency_matrix.has_connection(i, j);&lt;br /&gt;
    return number_links;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Как видно, проблема заключается в том, что метод практически не содержит вычислений, и его производительность определяется в первую очередь производительностью памяти. Таким образом, OpenMP просто распределил время ожидания доступа к памяти между различными процессами, а MPI добавил времени за счет выполнения передач между узлами.&lt;br /&gt;
&lt;br /&gt;
Метод &#039;&#039;create_adjacency_matrix&#039;&#039; также был распараллелен с помощью MPI. К качественному ускорению это не привело в первую очередь из-за того, что на использованных тестах среднее время выполнения этого метода занимало около 0.004% от общего времени выполнения. Распараллеленный код приведен ниже для справки.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c++&amp;quot; line&amp;gt;&lt;br /&gt;
void rock::create_adjacency_matrix(const dataset &amp;amp; p_data) {&lt;br /&gt;
    auto m_adjacency_matrix = adjacency_matrix(p_data.size());&lt;br /&gt;
    int numprocs = 0, rank = 0, len = 0, rc = 0;&lt;br /&gt;
    char hostname[MPI_MAX_PROCESSOR_NAME];&lt;br /&gt;
&lt;br /&gt;
    MPI_Init(&amp;amp;argc, &amp;amp;argv);&lt;br /&gt;
    MPI_Comm_size(MPI_COMM_WORLD, &amp;amp;numprocs);&lt;br /&gt;
    MPI_Comm_rank(MPI_COMM_WORLD, &amp;amp;rank);&lt;br /&gt;
    MPI_Get_processor_name(hostname, &amp;amp;len);&lt;br /&gt;
    printf (&amp;quot;Number of tasks= %d My rank= %d Running on %s\n&amp;quot;, numprocs, rank, hostname);&lt;br /&gt;
&lt;br /&gt;
    size_t msize = m_adjacency_matrix.size();&lt;br /&gt;
    unsigned num_tasks = msize / numprocs;&lt;br /&gt;
    unsigned delta = msize % numprocs;&lt;br /&gt;
    MPI_Request reqs[msize];&lt;br /&gt;
    MPI_Status stats[msize];&lt;br /&gt;
&lt;br /&gt;
    if (rank == 0) {&lt;br /&gt;
        // Initialize async receives for rows computed at other nodes&lt;br /&gt;
        for (size_t r = 1; r != numprocs; r++) {&lt;br /&gt;
            for (size_t i = num_tasks*r; i &amp;lt; num_tasks*(r + 1); i++) {&lt;br /&gt;
                MPI_Irecv(&amp;amp;(m_adjacency_matrix.raw_data()[i][0]), msize, MPI_DOUBLE, r, MPI_ANY_TAG, MPI_COMM_WORLD, reqs + i);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // compute what is needed&lt;br /&gt;
    for (size_t i = num_tasks*rank; i &amp;lt; num_tasks*(rank + 1); i++) {&lt;br /&gt;
        for (size_t j = 0; j &amp;lt; msize; j++) {&lt;br /&gt;
            double distance = euclidean_distance_sqrt(&amp;amp;p_data[i], &amp;amp;p_data[j]);&lt;br /&gt;
&lt;br /&gt;
            if (distance &amp;lt; m_radius) {&lt;br /&gt;
                m_adjacency_matrix.set_connection(i, j);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (rank == 0) {&lt;br /&gt;
        // finish is there&#039;s anything left&lt;br /&gt;
        for (size_t i = num_tasks * numprocs; i != msize; i++) {&lt;br /&gt;
            for (size_t j = 0; j &amp;lt; msize; j++) {&lt;br /&gt;
                double distance = euclidean_distance_sqrt(&amp;amp;p_data[i], &amp;amp;p_data[j]);&lt;br /&gt;
&lt;br /&gt;
                if (distance &amp;lt; m_radius) {&lt;br /&gt;
                    m_adjacency_matrix.set_connection(i, j);&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        MPI_Waitall(msize, reqs, stats);&lt;br /&gt;
    } else {&lt;br /&gt;
        // send the data to node 0&lt;br /&gt;
        for (size_t i = num_tasks*rank; i &amp;lt; num_tasks*(rank + 1); i++) {&lt;br /&gt;
            MPI_Isend(&amp;amp;(m_adjacency_matrix.raw_data()[i][0]), msize, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD, reqs + i);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    MPI_Finalize();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&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;
# Библиотека [https://github.com/annoviko/pyclustering annoviko/pyclustering] включает реализации для Python и C++ под GPL 3&lt;br /&gt;
# Пакет [https://cran.r-project.org/web/packages/cba/index.html CBA]  из репозитория CRAN содержит реализацию на R под GPL 2&lt;br /&gt;
# Библиотеки [http://www.cs.waikato.ac.nz/ml/weka/ Weka] и [https://sourceforge.net/projects/feed4weka/files/?source=navbar feed4weka] содержат реализацию для Java под GPL 2&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
[1] Guha S., Rastogi R., Shim K. ROCK: A robust clustering algorithm for categorical attributes //Data Engineering, 1999. Proceedings., 15th International Conference on. – IEEE, 1999. – С. 512-521.&lt;br /&gt;
&lt;br /&gt;
[2] Coppersmith D., Winograd S. Matrix multiplication via arithmetic progressions //Proceedings of the nineteenth annual ACM symposium on Theory of computing. – ACM, 1987. – С. 1-6.&lt;br /&gt;
&lt;br /&gt;
[3] Thomas H. Cormen et al. Introduction to algorithms. – Cambridge : MIT press, 2001. – Т. 6.&lt;br /&gt;
&lt;br /&gt;
[4] Guha S., Rastogi R., Shim K. Cure: an efficient clustering algorithm for large databases //Information Systems. – 2001. – Т. 26. – №. 1. – С. 35-58.&lt;br /&gt;
&lt;br /&gt;
[5] Vitter J. S. Random sampling with a reservoir //ACM Transactions on Mathematical Software (TOMS). – 1985. – Т. 11. – №. 1. – С. 37-57.&lt;br /&gt;
&lt;br /&gt;
[6] Воеводин Вл., Жуматий С., Соболев С., Антонов А., Брызгалов П., Никитенко Д., Стефанов К., Воеводин Вад. Практика суперкомпьютера «Ломоносов» // Открытые системы, 2012, N 7, С. 36-39.&lt;/div&gt;</summary>
		<author><name>Teplov</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9E%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D1%83%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA%D0%B0:Divanov&amp;diff=18942</id>
		<title>Обсуждение участника:Divanov</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9E%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D1%83%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA%D0%B0:Divanov&amp;diff=18942"/>
		<updated>2016-11-22T12:02:43Z</updated>

		<summary type="html">&lt;p&gt;Teplov: /* Замечания */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Статья [[Участник:Иванов Даниил/Алгоритм устойчивой кластеризации с иcпользованием связей]] ==&lt;br /&gt;
&lt;br /&gt;
=== Замечания ===&lt;br /&gt;
* Отсутствуют разделы с реализациями алгоритма и списком литературы [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 01:26, 23 октября 2016 (MSK)&lt;br /&gt;
* Формулы необходимо выделить в отдельные блоки, так же везде проверить корретность тегов. Встречается кривое описание математических выражений типа math&amp;gt;i&amp;lt;/math&amp;gt; [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 01:26, 23 октября 2016 (MSK)&lt;br /&gt;
* Граф совем не читаем, необходимо переработать и масштаб и структуру. [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 01:26, 23 октября 2016 (MSK)&lt;br /&gt;
* Необходимо указать автора статьи в тексте. [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 01:26, 23 октября 2016 (MSK)&lt;br /&gt;
* п. 1.5 указать язык кода [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 01:26, 23 октября 2016 (MSK)&lt;br /&gt;
* п. 1.7 необходимо указать для каких параметров создан граф [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 01:26, 23 октября 2016 (MSK)&lt;br /&gt;
* п. 1.8 необходимо расписать значительно более подробно и обосновать почему получены такие значения [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 01:26, 23 октября 2016 (MSK)&lt;br /&gt;
* п. 1.9 необходимо оценить объемы значений [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 01:26, 23 октября 2016 (MSK)&lt;br /&gt;
* п. 1.10 нужна вычислительная мощность алгоритма. Сложность - другие пункты. [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 01:26, 23 октября 2016 (MSK)&lt;br /&gt;
* отсутствует краткая сводка свойств алгоритма [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 02:14, 23 октября 2016 (MSK)&lt;br /&gt;
* отсутствуют данные по численным экспериментам и масштабируемости. [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 15:02, 22 ноября 2016 (MSK)&lt;/div&gt;</summary>
		<author><name>Teplov</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9E%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D1%83%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA%D0%B0:Viktorrulev&amp;diff=18941</id>
		<title>Обсуждение участника:Viktorrulev</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9E%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D1%83%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA%D0%B0:Viktorrulev&amp;diff=18941"/>
		<updated>2016-11-22T12:01:30Z</updated>

		<summary type="html">&lt;p&gt;Teplov: /* Замечания */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Статья [[Участник:Viktorrulev/Алгоритм устойчивой кластеризации с иcпользованием связей]] ==&lt;br /&gt;
&lt;br /&gt;
=== Замечания ===&lt;br /&gt;
* Формулы нужно оформить в отдельные блоки для лучшей читаемости [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 01:13, 23 октября 2016 (MSK)&lt;br /&gt;
:: Исправлено (сделать все формулы в одном блоке и выровнять по левой стороне вики не позволяет) [[Участник:Viktorrulev|Viktorrulev]] ([[Обсуждение участника:Viktorrulev|обсуждение]]) 12:18, 26 октября 2016 (MSK)&lt;br /&gt;
* В п. 2.7 необходимо указать ссылку на источник [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 01:13, 23 октября 2016 (MSK)&lt;br /&gt;
:: Исправлено [[Участник:Viktorrulev|Viktorrulev]] ([[Обсуждение участника:Viktorrulev|обсуждение]]) 12:18, 26 октября 2016 (MSK)&lt;br /&gt;
* В п. 1.5 указать на каком языке представлена реализация [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 01:13, 23 октября 2016 (MSK)&lt;br /&gt;
:: Исправлено [[Участник:Viktorrulev|Viktorrulev]] ([[Обсуждение участника:Viktorrulev|обсуждение]]) 12:18, 26 октября 2016 (MSK)&lt;br /&gt;
* В п. в п. 1.7  не указано для каких параметров построен граф. [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 01:13, 23 октября 2016 (MSK)&lt;br /&gt;
:: Исправлено [[Участник:Viktorrulev|Viktorrulev]] ([[Обсуждение участника:Viktorrulev|обсуждение]]) 12:18, 26 октября 2016 (MSK)&lt;br /&gt;
* Нужно более полное описание вычислительной мощности алгоритма [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 01:13, 23 октября 2016 (MSK)&lt;br /&gt;
:: Дополнено [[Участник:Viktorrulev|Viktorrulev]] ([[Обсуждение участника:Viktorrulev|обсуждение]]) 12:18, 26 октября 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
* Отсутствуют данные по численному эксперименту и масштабируемости. [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 15:01, 22 ноября 2016 (MSK)&lt;/div&gt;</summary>
		<author><name>Teplov</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:Viktorrulev/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D1%83%D1%81%D1%82%D0%BE%D0%B9%D1%87%D0%B8%D0%B2%D0%BE%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8c%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%81%D0%B2%D1%8F%D0%B7%D0%B5%D0%B9&amp;diff=18940</id>
		<title>Участник:Viktorrulev/Алгоритм устойчивой кластеризации с иcпользованием связей</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:Viktorrulev/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D1%83%D1%81%D1%82%D0%BE%D0%B9%D1%87%D0%B8%D0%B2%D0%BE%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8c%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%81%D0%B2%D1%8F%D0%B7%D0%B5%D0%B9&amp;diff=18940"/>
		<updated>2016-11-22T12:01:00Z</updated>

		<summary type="html">&lt;p&gt;Teplov: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Алгоритм устойчивой кластеризации с иcпользованием связей&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(M^2 + M^2m_{nbr} + M^{2}logM)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O(MlogM)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;O(M^2)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;M \times N&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Автор описания: [[Участник:viktorrulev|В.А. Рулев]].&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Кластеризация (кластерный анализ) — задача разбиения заданной выборки объектов на непересекающиеся подмножества, называемые кластерами, так, чтобы каждый кластер состоял из схожих объектов, а объекты разных кластеров существенно отличались. Каждый объект из выборки характеризуется рядом признаков, которые могут быть вещественными, целочисленными, категорийными (то есть принимающими значения из какого-либо множества) и другими. Множество значений, которые может принимать признак, называется доменом этого признака. Так, например, у объекта &#039;&#039;&#039;кошка&#039;&#039;&#039; может быть категорийный признак &#039;&#039;&#039;порода&#039;&#039;&#039;, доменом которого является множество &#039;&#039;&#039;[персидская, бенгальская, сфинкс, мейн-кун, ...]&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм устойчивой кластеризации с иcпользованием связей (robust clustering using links, ROCK)&#039;&#039;&#039; был предложен в 2000 году Sudipto Guha (Stanford University), Rajeev Rastogi (Bell Laboratories) и Kyuseok Shim (Bell Laboratories) &amp;lt;ref&amp;gt;Sudipto Guha, Rajeev Rastogi, Kyuseok Shim ROCK: A robust clustering algorithm for categorical attributes. 2000. Information Systems. Vol 25, Issue 5, Pages 345-366&amp;lt;/ref&amp;gt; для кластеризации объектов с категорийными признаками.&lt;br /&gt;
&lt;br /&gt;
Алгоритм устойчивой кластеризации с использованием связей предназначен для работы с объектами типа &amp;quot;транзакция&amp;quot; (&amp;quot;покупательская корзина&amp;quot;). Транзакция представляет собой множество товаров, приобретенных покупателем у поставщика. Каждому товару, который есть в наличии у поставщика, в транзакции соответствует отдельный признак, который принимает значение &#039;&#039;&#039;true&#039;&#039;&#039;, если товар присутствует в транзакции, и &#039;&#039;&#039;false&#039;&#039;&#039;, если товар в транзакции отсутствует.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В ходе кластеризации &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; имеющихся транзакций &amp;lt;math&amp;gt;P=\{p_1,...,p_M\}&amp;lt;/math&amp;gt; (каждая транзакция имеет &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; признаков) должны быть разделены на &amp;lt;math&amp;gt;K&amp;lt;/math&amp;gt; непересекающихся подмножеств (кластеров) &amp;lt;math&amp;gt;C_1, ..., C_K&amp;lt;/math&amp;gt; таким образом, чтобы полученные кластеры максимизировали некоторую критериальную функцию &amp;lt;math&amp;gt;E(C_1, ..., C_K)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Будет называть две транзакции &amp;lt;math&amp;gt;p_1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;p_2&amp;lt;/math&amp;gt; &#039;&#039;&#039;соседями&#039;&#039;&#039;, если мера сходства &amp;lt;math&amp;gt;neighbour(p_1,p_2)&amp;lt;/math&amp;gt; этих транзакций больше некоторого заранее заданного порогового значения &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt; В качестве меры сходства в алгоритме устойчивой кластеризации с использованием связей используется основанная на коэффициенте Жаккара мера сходства &amp;lt;math&amp;gt;sim(p_1,p_2)&amp;lt;/math&amp;gt;. В ней &amp;lt;math&amp;gt;N(C_i)^{1+2f(\theta)}&amp;lt;/math&amp;gt; имеет смысл ожидаемого (среднего) количества связей внутри одного кластера.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Количеством связей&#039;&#039;&#039; &amp;lt;math&amp;gt;link(p_1,p_2)&amp;lt;/math&amp;gt; между двумя транзакциями &amp;lt;math&amp;gt;p_1&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;p_2&amp;lt;/math&amp;gt; будем называть количество общих соседей этих транзакций.&lt;br /&gt;
&lt;br /&gt;
Для того, чтобы сформировать кластеры, удовлетворяющие поставленному условию, необходимо последовательно объединять среди имеющихся кластеров такие пары кластеров, для которых метрика &amp;lt;math&amp;gt;goodness(C_i,C_j)&amp;lt;/math&amp;gt; достигает максимального значения среди всех пар кластеров. На начальном этапе каждая транзакция считается отдельным кластером.&lt;br /&gt;
&lt;br /&gt;
Формулы метода:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
neighbour(p_1,p_2)=(sim(p_1,p_2)&amp;gt;\theta)&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
sim(p_1,p_2)=\frac{N(p_1 \cap p_2)}{N(p_1 \cup p_2)}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
link(p_1,p_2)=N \Big( \{ p \in P | sim(p_1,p) &amp;lt; \theta \} \cap \{ p \in P | sim(p_2,p) &amp;lt; \theta \} \Big)&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
E(C_1,...,C_K)=\sum_{i=1}^{K}N(C_i) \ast  \sum_{p_q,p_r \in C_i}\frac{link(p_q,p_r)}{N(C_i)^{1+2f(\theta)}}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
f(\theta)=\frac {1-\theta}{1+\theta}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
goodness(C_i,C_j)=\frac {\sum_{p_r \in C_i, p_q \in C_j}{link(p_r,p_q)}} {(N(C_i) + N(C_j))^{1+2f(\theta)} - N(C_i)^{1+2f(\theta)} - N(C_j)^{1+2f(\theta)}}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Вычислительное ядро алгоритма можно разделить на два основных этапа:&lt;br /&gt;
&lt;br /&gt;
# Вычисление количества связей между каждой парой транзакций (&amp;lt;math&amp;gt;\frac {M \times M} {2}&amp;lt;/math&amp;gt; вычислений количества связей)&lt;br /&gt;
# Последовательные объединения пар наиболее подходящих кластеров (&amp;lt;math&amp;gt;M-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;
&#039;&#039;&#039;Шаг 1:&#039;&#039;&#039; вычисление соседних транзакций.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг 2:&#039;&#039;&#039; вычисление количества связей между всеми транзакциями.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг 3:&#039;&#039;&#039; построение для каждого кластера упорядоченного списка значений метрики goodness со всеми остальными кластерами&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг 4:&#039;&#039;&#039; построение общего упорядоченного списка значений метрики goodness&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг 5 (повторяется то тех пор, пока количество кластеров не достигнет желаемого)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг 5.1:&#039;&#039;&#039; объединение двух кластеров с наилучшим goodness&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг 5.2:&#039;&#039;&#039; обновление списков кластеров&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Ниже приведен псевдокод схемы реализации последовательного алгоритма:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pascal&amp;quot;&amp;gt;&lt;br /&gt;
procedure compute_links(P)&lt;br /&gt;
begin&lt;br /&gt;
  for each p[i], p[j] in P do&lt;br /&gt;
    if sim(p[i], p[j]) &amp;gt; theta&lt;br /&gt;
      is_neighbour[i, j] = true&lt;br /&gt;
    else&lt;br /&gt;
      is_neighbour[i, j] = false&lt;br /&gt;
&lt;br /&gt;
  links[i, j] = 0&lt;br /&gt;
  for each p[i], p[j] in P do&lt;br /&gt;
    for p[k] in P do&lt;br /&gt;
      if is_neighbour[i, k] &amp;amp;&amp;amp; is_neighbour[j, k]&lt;br /&gt;
        links[i, j] = links[i, j] + 1&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
procedure cluster(P, k)&lt;br /&gt;
begin&lt;br /&gt;
  links := compute_links(P)&lt;br /&gt;
&lt;br /&gt;
  for each p in P do&lt;br /&gt;
    q[p] := build_local_list(links, p)&lt;br /&gt;
  Q := build global_list(P, q)&lt;br /&gt;
&lt;br /&gt;
  while size(Q) &amp;gt; k do&lt;br /&gt;
  begin&lt;br /&gt;
    u := extract_max(Q)&lt;br /&gt;
    v := extract_max(q[u])&lt;br /&gt;
    w := merge(u, v)&lt;br /&gt;
    delete(Q, v)&lt;br /&gt;
&lt;br /&gt;
    for each x in (q[u] or q[v]) do&lt;br /&gt;
    begin&lt;br /&gt;
      delete(q[x], u);&lt;br /&gt;
      delete(q[x], v)&lt;br /&gt;
      insert(q[x], w, goodness(x, w))&lt;br /&gt;
      insert(q[w], x, goodness(x, w))&lt;br /&gt;
      update(Q, x, q[x])&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    insert(Q, w, q[w])&lt;br /&gt;
    deallocate(q[u])&lt;br /&gt;
    deallocate(q[v])&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Рассмотрим сложности шагов алгоритма из раздела 1.4.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг 1:&#039;&#039;&#039; выполняется с помощью двух вложенных циклов и содержит &amp;lt;math&amp;gt;O(M^2)&amp;lt;/math&amp;gt; операций&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг 2:&#039;&#039;&#039; задача сводится к умножению полученной на предыдущем шаге матрицы соседства на саму себя. Это действие быть выполнено с помощью наивного метода за &amp;lt;math&amp;gt;O(M^3)&amp;lt;/math&amp;gt; операций. С помощью алгоритма Копперсмита — Винограда это можно сделать за &amp;lt;math&amp;gt;O(M^{2.37})&amp;lt;/math&amp;gt; операций.&amp;lt;ref&amp;gt;Donald Coppersmith and Shmuel Winograd. Matrix multiplication via arithmetic progressions. In Proc. of the 19th Annual ACM Symposium on Theory of Computing. 1987.&amp;lt;/ref&amp;gt; В случае, если среднее количество соседей каждой транзакции значительно меньше общего количества транзакций, связи могут быть вычислены за &amp;lt;math&amp;gt;O(M^2m_{nbr})&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;m_{nbr}&amp;lt;/math&amp;gt; - среднее количество соседей транзакции.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг 3:&#039;&#039;&#039; имеет сложность &amp;lt;math&amp;gt;O(M)&amp;lt;/math&amp;gt;.&amp;lt;ref&amp;gt;Thomas H. Cormen, Charles E. Leiserson, and Ronald L. Rivest. Introduction to&lt;br /&gt;
Algorithms. The MIT Press, Massachusetts, 1990.&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг 4:&#039;&#039;&#039; имеет такую же вычислительную сложность, как и предыдущий шаг.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг 5:&#039;&#039;&#039; вычислительная сложность каждой итерации цикла составляет &amp;lt;math&amp;gt;O(MlogM)&amp;lt;/math&amp;gt;, так как в худшем случае в ходе итерации выполняется &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; вставок элемента в упорядоченный список. Желаемое количество кластеров &amp;lt;math&amp;gt;K&amp;lt;/math&amp;gt; мало по сравнению с &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt;, следовательно можно считать, что нужно выполнить &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; итераций цикла. Таким образом, общая вычислительная сложность цикла равна &amp;lt;math&amp;gt;O(M^{2}logM)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Таким образом, общую последовательную сложность алгоритма в общем случае можно оценить, как&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;O(M^2 + M^{2.37} + M^{2}logM)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
или в случае небольшого количества соседних транзакций, как&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;O(M^2 + M^2m_{nbr} + M^{2}logM)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Информационный граф перемножения плотных матриц с подробным описанием представлен в статье [[Перемножение_плотных_неособенных_матриц_(последовательный_вещественный_вариант)|перемножение плотных матриц]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Ниже представлен информационный граф наиболее интересного для рассмотрения этапа алгоритма устойчивой кластеризации с использованием связей - тела цикла (полупрозрачным показана следующая итерация цикла) при &amp;lt;math&amp;gt;n=5&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[file:Rock-graph.png|thumb|center|1000px|Рисунок 1. Граф тела цикла; merge - объединение кластеров, insert - вставка goodness с новым кластером в упорядоченные списки соответствующих кластеров.]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг 1:&#039;&#039;&#039; каждая итерация цикла может быть выполнена независимо от всех остальных. Таким образом, в этом случае ширина ЯПФ равна &amp;lt;math&amp;gt;O(M^2)&amp;lt;/math&amp;gt;, а высота - &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг 2:&#039;&#039;&#039; высота ЯПФ равна &amp;lt;math&amp;gt;O(M)&amp;lt;/math&amp;gt;, а ширина - &amp;lt;math&amp;gt;O(M^2)&amp;lt;/math&amp;gt; (&#039;&#039;см. [[Перемножение_плотных_неособенных_матриц_(последовательный_вещественный_вариант)|перемножение плотных матриц]]&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг 3:&#039;&#039;&#039; имеет небольшую асимптотическую сложность, может быть выполнен последовательно.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг 4:&#039;&#039;&#039; аналогично шагу 3.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Шаг 5:&#039;&#039;&#039; каждая итерация цикла может быть выполнена только после полного завершения предыдущей, а внутри каждой отдельной итерации все вставки могут быть выполнены независимо друг от друга. Следовательно ширина ЯПФ в этом случае равна &amp;lt;math&amp;gt;O(M)&amp;lt;/math&amp;gt;, а высота ЯПФ равна &amp;lt;math&amp;gt;O(MlogM)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Таким образом, высота ЯПФ зависит от размерности задачи как &amp;lt;math&amp;gt;O(MlogM)&amp;lt;/math&amp;gt;, а ширина ЯПФ - как &amp;lt;math&amp;gt;O(M^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Входные данные&#039;&#039;&#039;: информация об &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; транзакциях, каждая их которых имеет &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; бинарных атрибутов.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;M \times N&amp;lt;/math&amp;gt; бинарных переменных. В силу того, что обычно в большинстве случаев лишь небольшое количество атрибутов транзакции имеет значение &amp;quot;1&amp;quot;, каждую транзакцию можно хранить в виде списка номеров атрибутов, значения которых равны &amp;quot;1&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: &amp;lt;math&amp;gt;K&amp;lt;/math&amp;gt; кластеров.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; индексов транзакций, распределенных в &amp;lt;math&amp;gt;K&amp;lt;/math&amp;gt; списков.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Вычислительная сложность последовательного алгоритма в общем случае равна &amp;lt;math&amp;gt;O(M^{2.37})&amp;lt;/math&amp;gt;, а в случае небольшого количества соседних транзакций у каждой транзакции сложность равна &amp;lt;math&amp;gt;O(M^{2})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Вычислительная мощность алгоритма (соотношение количества операций к объему входных и выходных данных) в общем случае равна O(M^{1.37}), в а случае небольшого количества соседних транзакций (то есть в том случае, для которого и разрабатывался алгоритм) вычислительная мощность линейна.&lt;br /&gt;
&lt;br /&gt;
Соотношение последовательной и параллельной сложности в случае неограниченных ресурсов, как показано выше, является квадратичным.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм устойчивой кластеризации с иcпользованием связей реализован в пакете CBA языка R.&amp;lt;ref&amp;gt;https://en.wikibooks.org/wiki/Data_Mining_Algorithms_In_R/Clustering/RockCluster&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references \&amp;gt;&lt;/div&gt;</summary>
		<author><name>Teplov</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:Tikhomirov.mm/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D1%83%D1%81%D1%82%D0%BE%D0%B9%D1%87%D0%B8%D0%B2%D0%BE%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8c%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%81%D0%B2%D1%8F%D0%B7%D0%B5%D0%B9_(robust_clustering_using_links,_ROCK)&amp;diff=18939</id>
		<title>Участник:Tikhomirov.mm/Алгоритм устойчивой кластеризации с иcпользованием связей (robust clustering using links, ROCK)</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:Tikhomirov.mm/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D1%83%D1%81%D1%82%D0%BE%D0%B9%D1%87%D0%B8%D0%B2%D0%BE%D0%B9_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8_%D1%81_%D0%B8c%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D1%81%D0%B2%D1%8F%D0%B7%D0%B5%D0%B9_(robust_clustering_using_links,_ROCK)&amp;diff=18939"/>
		<updated>2016-11-22T11:59:04Z</updated>

		<summary type="html">&lt;p&gt;Teplov: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment|Teplov}}&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Алгоритм устойчивой кластеризации с использованием связей&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt; O(n^2 + n m_m m_a + n^2 \log{} n)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = Множество &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; элементов, число кластеров &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек с метками &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров&lt;br /&gt;
}}&lt;br /&gt;
Основные авторы описания: [[Участник:Простов В. А.|В.А.Простов]](1.1 - 1.6, 1.9, 2.4), [[Участник:Tikhomirov.mm|М.М.Тихомиров]](1.7, 1.8, 1.10, 2.4, 2.7)&lt;br /&gt;
&lt;br /&gt;
= ЧАСТЬ. Свойства и структура алгоритмов =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Алгоритм устойчивой кластеризации с иcпользованием связей ROCK (RObust Clustering using linKs)&#039;&#039;&#039; был совместно разработан Sudipto Guha, Rajeev Rastogi и Kyuseok Shim при работе в Bell Laboratories. Впервые алгоритм был опубликован в 1999 году в статье под названием &amp;quot;ROCK: A Robust Clustering Algorithm for Categorical Attributes&amp;quot;, входящей в &amp;quot;ICDE &#039;99 Proceedings of the 15th International Conference on Data Engineering&amp;quot;. &lt;br /&gt;
&lt;br /&gt;
Данный алгоритм принадлежит классу алгоритмов кластеризации, целью которых является разбиение данных на некоторое заранее заданное число групп. Подобные методы часто используются для анализа данных в различных областях, в том числе в маркетинге. Основная особенность алгоритма ROCK заключается в использовании связей между точками (количество общих соседей), в отличие от методов, базирующихся на различных метриках, таких как расстояние между точками (Евклидово и прочие). Такой подход улучшает определение глобальных зависимостей, а также наиболее эффективен при рассмотрении данных, свойства которых принимают достаточно малое конечное количество значений.&lt;br /&gt;
&lt;br /&gt;
Одним из основных понятий для алгоритма ROCK является соседство двух точек. Пусть нам дана функция схожести &amp;lt;math&amp;gt; sim(p_i,p_j) &amp;lt;/math&amp;gt;, принимающая значения от 0 до 1, которая выражает схожесть или близость объектов(точек) &amp;lt;math&amp;gt;p_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;p_j&amp;lt;/math&amp;gt;. Предполагается, что 1 соответствует абсолютной близости, и 0 - наоборот. Тогда при некоторой границе &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt; между 0 и 1, если &amp;lt;math&amp;gt; sim(p_i,p_j) \geq \theta &amp;lt;/math&amp;gt;, то &amp;lt;math&amp;gt;p_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;p_j&amp;lt;/math&amp;gt; будут соседними точками. Выбор Функции &amp;lt;math&amp;gt; sim(p_i,p_j) &amp;lt;/math&amp;gt; и граничного значения &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt; зависит входных данных и особенности реализации. &lt;br /&gt;
&lt;br /&gt;
Вторым ключевым понятием являются связи. Функция связи &amp;lt;math&amp;gt; link(p_i,p_j) &amp;lt;/math&amp;gt; определяется как количество общих соседей у &amp;lt;math&amp;gt;p_i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;p_j&amp;lt;/math&amp;gt;. Из такого определения сразу видно, что чем больше значение связи, тем больше вероятность, что эти точки принадлежат одному и тому же кластеру. Такой подход является более глобальным, по сравнению с использованием только близости двух точек, что позволяет снизить число ошибок, особенно в тех случаях, когда кластеры имеют несколько близких точек.&lt;br /&gt;
&lt;br /&gt;
Алгоритм состоит из двух основных этапов. Изначально имеется &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; точек и &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; - желаемое число кластеров. На первом этапе вычисляются значения связей &amp;lt;math&amp;gt; link(p_i,p_j) &amp;lt;/math&amp;gt; между всеми парами точек, каждая точка объявляется отдельным кластером. Для каждого кластера &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; создается локальная куча &amp;lt;math&amp;gt;q[i]&amp;lt;/math&amp;gt;, которая содержит все такие кластеры &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, что связь между ними не нулевая. Кроме этого, создается глобальная куча &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt;, содержащая все кластеры. После этого алгоритм переходит ко второму этапу. Вторая часть представляет из себя цикл, на каждом шаге которого объединяются два кластера с максимальным значением функции полезности &amp;lt;math&amp;gt;g(i,j)&amp;lt;/math&amp;gt;, после чего вносятся соответствующие изменения в кучи. Алгоритм завершает работу в двух случаях: когда осталось &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров, или когда все связи между оставшимися кластерами равны нулю.&lt;br /&gt;
&lt;br /&gt;
== Математическое описание алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Дано множество &amp;lt;math&amp;gt; S &amp;lt;/math&amp;gt; из &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt; элементов и число &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt;. Для каждых &amp;lt;math&amp;gt; p_i,p_j \in S &amp;lt;/math&amp;gt; задана функция схожести &amp;lt;math&amp;gt; 0 \leq sim(p_i,p_j) \leq 1&amp;lt;/math&amp;gt;. Также дано число &amp;lt;math&amp;gt; 0 \leq \theta \leq 1&amp;lt;/math&amp;gt;. Дана функция &amp;lt;math&amp;gt; f( \theta ) &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Требуется разбить &amp;lt;math&amp;gt; S &amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt; не пересекающихся подмножеств(кластеров) &amp;lt;math&amp;gt;C_1, \dots, C_k&amp;lt;/math&amp;gt; так, чтобы значение целевой функции &amp;lt;math&amp;gt;E_l&amp;lt;/math&amp;gt;было как можно большим.&lt;br /&gt;
&lt;br /&gt;
Определим следующие функции, полагая, что &amp;lt;math&amp;gt; p_i,p_j \in S. &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Функция, определяющая соседство двух точек:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
\begin{align}&lt;br /&gt;
neib(p_i, p_j) = \begin{cases}  &lt;br /&gt;
                 1, sim(p_i, p_j) \geq \theta,\\&lt;br /&gt;
                 0, sim(p_i, p_j) &amp;lt; \theta. &lt;br /&gt;
                 \end{cases} \\&lt;br /&gt;
&lt;br /&gt;
\end{align}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Функция, определяющая количество связей между точками:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
link(p_i, p_j) = \sum_{s\in S} neib(p_i,s) neib(p_j,s).&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Определим теперь целевую функцию, считая &amp;lt;math&amp;gt;n_i&amp;lt;/math&amp;gt; количеством элементов в &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
E_l = \sum_{i=1}^{k} n_i * \sum_{p_q, p_r \in C_i} \frac{link(p_q,p_r)}{n_{i}^{1+2f( \theta )}}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Определим функцию связи для двух подмножеств &amp;lt;math&amp;gt; C_i, C_j &amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
link[C_i, C_j] = \sum_{p_q \in C_i, p_r \in C_j} link(p_i, p_j)&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Введем функцию полезности, выражающую то, насколько выгодно объединить подмножества &amp;lt;math&amp;gt; C_i, C_j &amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
g(C_i, C_j) = \frac{link[C_i,C_j]}{(n_i + n_j)^{1+2f( \theta )} - n_i^{1+2f( \theta )} - n_j^{1+2f( \theta )}}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В данном алгоритме сначала все элементы разбиваются на &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt; подмножеств, затем делается &amp;lt;math&amp;gt; n - k &amp;lt;/math&amp;gt; шагов, на каждом из которых объединяются два подмножества, для которых значение функции полезности &amp;lt;math&amp;gt; g(i,j) &amp;lt;/math&amp;gt; наибольшее.&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Вычислительное ядро алгоритма состоит из двух основных частей. &lt;br /&gt;
&lt;br /&gt;
1. Вычисление всех связей между точками. Для каждой точки создается список соседей. Проходим по каждому такому списку и увеличиваем количество связей между всеми точками из списка, поскольку они имеют общую соседнюю точку (ту, которой принадлежит список).&lt;br /&gt;
&lt;br /&gt;
2. Цикл по объединению кластеров, содержит &amp;lt;math&amp;gt; n-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;
 procedure cluster(S, k)&lt;br /&gt;
 begin&lt;br /&gt;
   link := compute_links(S)&lt;br /&gt;
   for each s &amp;lt;math&amp;gt;\in&amp;lt;/math&amp;gt; S do&lt;br /&gt;
     q[s] := build_local_heap(link, s)&lt;br /&gt;
   Q := build_global_heap(S, q)&lt;br /&gt;
   while size(Q) &amp;gt; k do {&lt;br /&gt;
     u := extract_max(Q)&lt;br /&gt;
     v := max(q[u])&lt;br /&gt;
     delete(Q, v)&lt;br /&gt;
     w := merge(u, v)&lt;br /&gt;
     for each x &amp;lt;math&amp;gt;\in&amp;lt;/math&amp;gt; q[u] &amp;lt;math&amp;gt;\cup&amp;lt;/math&amp;gt; q[v] do {&lt;br /&gt;
       link[x,w] := link[x, u] + link[x, v]&lt;br /&gt;
       delete(q[x], u); delete(q[x], v)&lt;br /&gt;
       insert(q[x],w, g(x,w)); insert(q[w], x, g(x,w))&lt;br /&gt;
       update(Q, x, q[x])&lt;br /&gt;
     }&lt;br /&gt;
     insert(Q,w, q[w])&lt;br /&gt;
     deallocate(q[u]), deallocate(q[v])&lt;br /&gt;
   }&lt;br /&gt;
 end&lt;br /&gt;
&lt;br /&gt;
1. Вычисление связей между каждой парой точек:&lt;br /&gt;
&lt;br /&gt;
 procedure compute_links(S)&lt;br /&gt;
 begin&lt;br /&gt;
   Compute nbrlist[i] for every point i in S&lt;br /&gt;
   Set link[i, j] to be zero for all i, j&lt;br /&gt;
   for i := 1 to n do {&lt;br /&gt;
     N := nbrlist[i]&lt;br /&gt;
     for j := 1 to |N| - 1 do&lt;br /&gt;
       for l := j + 1 to |N| do&lt;br /&gt;
         link[N[j],N[l]] := link[N[j],N[l]] + 1&lt;br /&gt;
   }&lt;br /&gt;
 end&lt;br /&gt;
&lt;br /&gt;
Дла каждой точки &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; строится список соседей &amp;lt;math&amp;gt;nbrlist[i]&amp;lt;/math&amp;gt;. Поскольку любые две точки в таком списке имеют nbrlist[i] в качестве общего соседа, то для всех пар точек из списка увеличиваем значение функции связей.&lt;br /&gt;
&lt;br /&gt;
2. Каждая точка объявляется отдельным кластером. Для каждого кластера &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; строится локальная куча &amp;lt;math&amp;gt;q[i]&amp;lt;/math&amp;gt;, которая поддерживается до конца работы алгоритма. &amp;lt;math&amp;gt;q[i]&amp;lt;/math&amp;gt; содержит все такие кластеры &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, что &amp;lt;math&amp;gt;link[i,j] \neq 0&amp;lt;/math&amp;gt;. Кластеры &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;q[i]&amp;lt;/math&amp;gt; отсортированы в порядке убывания функции полезности &amp;lt;math&amp;gt;g(i,j)&amp;lt;/math&amp;gt;. Аналогичным образом создается глобальная куча &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt;, содержащая все кластеры. Элементы внутри аналогично отсортированы в соответствии с их максимальным значением функции полезности. &lt;br /&gt;
&lt;br /&gt;
3. Запускается while-цикл до тех пор, пока количество кластеров не станет равно &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;. На каждом шаге цикла берутся два кластера с максимальным значением &amp;lt;math&amp;gt;g(i,j)&amp;lt;/math&amp;gt; и объединяются. Из глобальной кучи извлекается максимум, затем для этого кластера из его локальной кучи также извлекается максимум. По построению куч легко видеть, что это будет искомой парой. Далее происходит объединение кластеров, после чего для каждого кластера, который содержал в своей локальной куче один из данных, необходимо провести соответствующую коррекцию. Из глобальной кучи также удаляются два элемента и добавляется один новый с сохранением порядка.&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Вычисление связей может рассматриваться как перемножение двух матриц размера &amp;lt;math? n &amp;lt;/math&amp;gt;, что можно реализовать за &amp;lt;math&amp;gt; O(n^{2.37})&amp;lt;/math&amp;gt;. Затраты памяти на хранение связей не превышают &amp;lt;math&amp;gt; n(n+1)/2 &amp;lt;/math&amp;gt;, когда любая пара точек - соседние. Однако в большинстве случаев среднее число соседей &amp;lt;math&amp;gt; m_a &amp;lt;/math&amp;gt; и максимальное число соседей &amp;lt;math&amp;gt; m_m &amp;lt;/math&amp;gt; значительно меньше &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt;, в связи с этим оценки сложности зависят от данных параметров. Сложность построения списка соседей оценивается &amp;lt;math&amp;gt; O(n^2)&amp;lt;/math&amp;gt;, Вычисление связей состоит из трех вложенных циклов и может быть оценено &amp;lt;math&amp;gt; O(n m_m m_a)&amp;lt;/math&amp;gt;. Поскольку каждая точка &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; может иметь не более &amp;lt;math&amp;gt;\min \{  m_m m_i, n \} )&amp;lt;/math&amp;gt; связей, то общие затраты памяти не превышают &amp;lt;math&amp;gt; O(\min \{  n m_m m_a, n^2 \} )&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Построение каждой кучи потребует не более &amp;lt;math&amp;gt; O(n)&amp;lt;/math&amp;gt; времени (поскольку размер каждой кучи не может превышать &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt;) Сортировка каждой кучи потребует &amp;lt;math&amp;gt; O( n \log{} n)&amp;lt;/math&amp;gt;. Рассмотрим теперь основной while-цикл. Он содержит &amp;lt;math&amp;gt; O(n)&amp;lt;/math&amp;gt; итераций, где основная сложность каждого шага приходится на for-цикл. В худшем случае потребуется вставить новый кластер в &amp;lt;math&amp;gt; O(n)&amp;lt;/math&amp;gt; локальных куч размера &amp;lt;math&amp;gt; n&amp;lt;/math&amp;gt;, что потребует &amp;lt;math&amp;gt; O( n \log{} n)&amp;lt;/math&amp;gt; времени. Таким образом, сложность всего внешнего цикла составляет &amp;lt;math&amp;gt; O(n^2 \log{} n)&amp;lt;/math&amp;gt; в худшем случае. Затраты памяти зависят от начального размера локальных куч, поскольку при слиянии кластеров их старые кучи удаляются, а размер новой не превышает суммы старых. Поскольку каждая куча сожержит лишь кластеры с ненулевыми связями, то сложность совпадает с первой частью и равна &amp;lt;math&amp;gt; O(\min \{  n m_m m_a, n^2 \} )&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Итоговые оценки для всего алгоритма:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Сложность по времени&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt; O(n^2 + n m_m m_a + n^2 \log{} n)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Сложность по памяти&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt; O(\min \{ n^2, n m_m m_a \} )&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
[[Файл:Graph_tikh.png|400px|thumb|рис 1. Граф в общем виде для n точек и k кластеров.]]&lt;br /&gt;
Обозначения :&lt;br /&gt;
* &#039;&#039;&#039;CN&#039;&#039;&#039; - вершина построения соседей: заполнение nbrlist[i].&lt;br /&gt;
* &#039;&#039;&#039;CLM&#039;&#039;&#039; - вершина построения матрицы связей: заполнение link[i][j].&lt;br /&gt;
* &#039;&#039;&#039;CLH&#039;&#039;&#039; - вершина построения локальных куч: заполнение q[i].&lt;br /&gt;
* &#039;&#039;&#039;CGH&#039;&#039;&#039; - вершина построения глобальной кучи: заполнение Q.&lt;br /&gt;
* &#039;&#039;&#039;GC&#039;&#039;&#039; - вершина поиска и слияние топ 2 кластеров: построение w.&lt;br /&gt;
* &#039;&#039;&#039;RL&#039;&#039;&#039; - пересчет матрицы связей: заполнение link[x,w].&lt;br /&gt;
* &#039;&#039;&#039;UPDATE&#039;&#039;&#039; - Обновление глобальной кучи: обновление Q.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Как видно из графа в алгоритме есть ряд узких мест.&lt;br /&gt;
Рассмотрим их подробнее:&lt;br /&gt;
&lt;br /&gt;
1) Вершина построения матрицы links. Данные вычисления не могут быть прямо распараллелены, нельзя для каждого списка соседей запустить данную функцию. Это связано с тем, что во время выполнения данных вычислений необходимо производить запись в матрицу и любой список может писать в любое место матрицы. В худшем случае (когда все соседи всем) при параллельном запуске возможны постоянные конфликты. Но в данной вершине все же есть потенциал для параллельной работы. Он заключается в том, что можно в параллели обрабатывать список соседей. При подходе в лоб нагрузка на ядра будет не равномерна. &lt;br /&gt;
&lt;br /&gt;
Так же данный пункт можно реализовать иначе. Но затраты по памяти возрастут в N раз. Можно параллельно обрабатывать каждый вектор соседей. При среднем количестве соседей &amp;lt;math&amp;gt;m_a&amp;lt;/math&amp;gt; это займет &amp;lt;math&amp;gt;O(m_a ^ 2)&amp;lt;/math&amp;gt; операций (или если мы имеем &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt; ядер, то можно и за &amp;lt;math&amp;gt;O(m_a)&amp;lt;/math&amp;gt; + &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt; для объединения N матриц.&lt;br /&gt;
&lt;br /&gt;
2) Вершина построения глобальной кучи. В данной вершине необходимо добавить все вершины и после отсортировать в зависимости от наилучшего значения полезности каждой локальной кучи. Сортировку можно распараллелить.&lt;br /&gt;
&lt;br /&gt;
3) Вершина взятия топ 2 кластеров. Узкая, но малозатратная вершина.&lt;br /&gt;
&lt;br /&gt;
4) Вершина обновления куч. Так же узкая вершина в которой нужно в каждой локальной куче вставить в нужное место кластер и так же поступить с глобальной кучей.&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Кроме нескольких узких мест алгоритма, которые не поддаются распараллеливанию, в целом алгоритм неплохо может быть переложен на параллельную архитектуру.&lt;br /&gt;
Рассчитаем сложность алгоритма.&lt;br /&gt;
&lt;br /&gt;
1) На первом этапе происходит вычисление соседей. Затраты данных операций при условии достаточного количества ядер &amp;lt;math&amp;gt;T_1 = O(n)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2) На этапе построения связей &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; этапов в каждом из которых можно сделать &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; расчетов(с учетом распараллеливания), где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - количество соседей. В худшем случае &amp;lt;math&amp;gt;T_2 = O(n^2)&amp;lt;/math&amp;gt; в среднем случае &amp;lt;math&amp;gt;T_2 = O(n * m_a)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;m_a&amp;lt;/math&amp;gt; среднее ожидаемое число соседей.&lt;br /&gt;
&lt;br /&gt;
3) Построение одной кучи это сложность &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;. Плюс для каждой кучи сортировка &amp;lt;math&amp;gt;O(n * log(n))&amp;lt;/math&amp;gt;. Соответственно итоговая сложность создания локальных куч &amp;lt;math&amp;gt;T_3 = O(n * log(n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
4) Построение и сортировка глобальной кучи так же &amp;lt;math&amp;gt;T_4 = O(n * log(n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
5) Цикл в котором происходит выбор кластеров, сливание, пересчет и обновление куч. Игнорируем выбор кластеров (достать из кучи &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; ). Для всех элементов кучи слитых кластеров пересчитываем значение связи, вставляем в кучу выбранного кластера информацию о новом кластере, удаляем старое. Делается это за &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt;. Соответственно в худшем случае, одна итерация цикла это &amp;lt;math&amp;gt;T_i = O(log(n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
Количество итераций n - k. Значит &lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;T_5 = O((n - k) * log(n))&amp;lt;/math&amp;gt;. При k = 1, &amp;lt;math&amp;gt;T_5 = O(n * log(n))&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Итоговая сложность в худшем случае равна:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;T_1 + T_2 + T_3 + T_4 + T_5 = O(n) + O(n^2) + O(n * log(n)) + O(n * log(n)) + O(n * log(n)) = O(n^2)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Итоговая сложность в среднем случае равна:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;O(n * m_a) + O(n * log(n))&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Тут важно понимать, что в зависимости от данных как &amp;lt;math&amp;gt;log(n)&amp;lt;/math&amp;gt; может быть больше &amp;lt;math&amp;gt;m_a&amp;lt;/math&amp;gt;, так и наоборот.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Сложность по времени:&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;O(n * (m_a + log(n)))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Входные данные:&#039;&#039;&#039; Множество &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; элементов, число кластеров &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;. В зависимости от реализации, некоторые параметры алгоритма могут также подаваться на вход или быть определены заранее. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объем входных данных:&#039;&#039;&#039; Зависит от типа элементов из множества &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt;, поскольку алгоритм может быть реализован для широкого класса данных. В общем случае требуется хранить массив из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; элементов.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные:&#039;&#039;&#039; Метки класса (номер кластера) для каждого элемента.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объем выходных данных:&#039;&#039;&#039; Для каждого входного элемента достаточно вернуть соответствующую ему метку (число), поэтому минимальный размер выхода: массив из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; чисел.&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
Алгоритм является детерминированным.&lt;br /&gt;
&lt;br /&gt;
Алгоритм является устойчивым.&lt;br /&gt;
&lt;br /&gt;
Вычислительная мощность алгоритма равна:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;\frac{O(n^2 + n m_m m_a + n^2 \log{} n)}{n}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Отношение последовательной и параллельной сложности:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;\frac{O(n^2 + n m_m m_a + n^2 \log{} n)}{O(n * (m_a + log(n)))}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
К свойствам алгоритма можно отнести то, что сложность в среднем зависит от данных. Если растет среднее ожидаемое количество соседей, то растет и средняя сложность алгоритма. В худшем случае она может достигать &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt; для данных с большой кучностью (одна большая плотная куча, где по факту 1 кластер). Но, если же среднее ожидаемое число соседей меньше &amp;lt;math&amp;gt;log(n)&amp;lt;/math&amp;gt;, то сложность алгоритма будет &amp;lt;math&amp;gt;O(n *log(n))&amp;lt;/math&amp;gt;. В целом можно взять оценку &amp;lt;math&amp;gt;O(n * m_m)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; m_m &amp;lt;/math&amp;gt; максимальное количество соседей.&lt;br /&gt;
&lt;br /&gt;
= ЧАСТЬ. Программная реализация алгоритма =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Особенности реализации последовательного алгоритма ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== [[Локальность данных и вычислений]] ==&lt;br /&gt;
&lt;br /&gt;
== Возможные способы и особенности параллельной реализации алгоритма ==&lt;br /&gt;
&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
=== Масштабируемость алгоритма ===&lt;br /&gt;
Как можно видеть из графа, представленного выше, в алгоритме есть 3 основных места для распараллеливания:&lt;br /&gt;
1) Составление матрицы связей&lt;br /&gt;
2) Построение локальных куч&lt;br /&gt;
3) Основной цикл алгоритма&lt;br /&gt;
&lt;br /&gt;
Теоретическая оценка сложности алгоритма распараллеленного на p потоков:&lt;br /&gt;
:&amp;lt;math&amp;gt;O(\frac{n}{p}(n + m_m m_a + n\log{} n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Соответственно:&lt;br /&gt;
1) При одинаковых входных данных, время работы обратно пропорционально количеству потоков.&lt;br /&gt;
2) При одинаковом количестве потоков, время зависит от входных данных кубически в худшем случае.&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость реализации ===&lt;br /&gt;
Реализация алгоритма тестировалась на IBM pSeries 690 HPC Regatta.&lt;br /&gt;
* Максимальное количество потоков, способных работать в параллельном режиме: 16.&lt;br /&gt;
* Процессоры POWER4 1,3 ГГц; 16-процессорная архитектура SMP&lt;br /&gt;
&lt;br /&gt;
Параметры запусков:&lt;br /&gt;
1) Запуски осуществлялись на задачах объемом 400, 800, 1600, 2400 точек.&lt;br /&gt;
2) Запуски осуществлялись на 1, 2, 4, 8, 16 потоках.&lt;br /&gt;
3) Количество кластеров = 2, &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt; = 0.3, соседями считались все точки в радиусе 0.3 .&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Количество потоков / Количество точек&lt;br /&gt;
! 400&lt;br /&gt;
! 800&lt;br /&gt;
! 1200&lt;br /&gt;
! 1600&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| 23 с.&lt;br /&gt;
| 188 с. &lt;br /&gt;
| 631 с.&lt;br /&gt;
| 1514 с.&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| 17 с&lt;br /&gt;
| 134 с&lt;br /&gt;
| 452 с&lt;br /&gt;
| 1079 с&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| 10 с&lt;br /&gt;
| 79 с&lt;br /&gt;
| 268 с&lt;br /&gt;
| 631 с&lt;br /&gt;
|-&lt;br /&gt;
| 8&lt;br /&gt;
| 8 с&lt;br /&gt;
| 45 с&lt;br /&gt;
| 144 с&lt;br /&gt;
| 342 с&lt;br /&gt;
|-&lt;br /&gt;
| 16&lt;br /&gt;
| 4 с&lt;br /&gt;
| 24 с&lt;br /&gt;
| 79 с&lt;br /&gt;
| 185 с&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Как видно из таблицы, масштабируемость реализации коррелирует с теоретической оценкой.&lt;br /&gt;
&lt;br /&gt;
Наглядно эти данные можно увидеть на следующем изображении.&lt;br /&gt;
[[Файл:Rock regatta.png|1200px|center|thumb|рис 2. Масштабируемость реализации]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Реализация доступна по ссылке на github [https://github.com/refalc/rock_cmc_msu/]&lt;br /&gt;
&lt;br /&gt;
== Динамические характеристики и эффективность реализации алгоритма ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Выводы для классов архитектур ==&lt;br /&gt;
&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
feed4weka [https://sourceforge.net/projects/feed4weka/files/] - открытая библиотека, которая содержит различные алгоритмы для работы с данными.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
1) ROCK: A Robust Clustering Algorithm for&lt;br /&gt;
Categorical Attributes[https://www.cis.upenn.edu/~sudipto/mypapers/categorical.pdf]&lt;br /&gt;
&lt;br /&gt;
2) Data Mining Algorithms In R/Clustering/RockCluste [https://en.wikibooks.org/wiki/Data_Mining_Algorithms_In_R/Clustering/RockCluster]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[en:Description of algorithm properties and structure]]&lt;/div&gt;</summary>
		<author><name>Teplov</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9E%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D1%83%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA%D0%B0:Anton_goy&amp;diff=18937</id>
		<title>Обсуждение участника:Anton goy</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9E%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D1%83%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA%D0%B0:Anton_goy&amp;diff=18937"/>
		<updated>2016-11-22T11:48:10Z</updated>

		<summary type="html">&lt;p&gt;Teplov: /* Замечания */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Статья [[Самоорганизующиеся карты Кохонена]] ==&lt;br /&gt;
&lt;br /&gt;
=== Замечания ===&lt;br /&gt;
* Формулы нужно оформить в отдельные блоки для лучшей читаемости [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 00:37, 23 октября 2016 (MSK)&lt;br /&gt;
* Графы рис 4,5 имеют уж очень округлые дуги. Сильно это восприятию не мешает, для построенного размера, но лучше дуги сделать значительно более прямыми [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 00:39, 23 октября 2016 (MSK)&lt;br /&gt;
* В п. 1.7 не указаны параметры для которых построен граф [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 00:39, 23 октября 2016 (MSK)&lt;br /&gt;
* В п 1.10 нет оценки вычислительной мощности [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 00:39, 23 октября 2016 (MSK)&lt;br /&gt;
* Отсутствует краткая сводка свойств алгоритма [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 02:11, 23 октября 2016 (MSK)&lt;br /&gt;
* Отсутствуют данные по вычислительным экспериментам. [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 14:48, 22 ноября 2016 (MSK)&lt;/div&gt;</summary>
		<author><name>Teplov</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9E%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D1%83%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA%D0%B0:Eugene&amp;diff=18936</id>
		<title>Обсуждение участника:Eugene</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9E%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D1%83%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA%D0%B0:Eugene&amp;diff=18936"/>
		<updated>2016-11-22T11:46:55Z</updated>

		<summary type="html">&lt;p&gt;Teplov: /* Замечания */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Статья [[Самоорганизующаяся карта Кохонена (алгоритм кластеризации)]] ==&lt;br /&gt;
&lt;br /&gt;
=== Структура статьи ===&lt;br /&gt;
* Пункт Литература должен быть 3 а не 2.8 [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 00:24, 23 октября 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
=== Замечания ===&lt;br /&gt;
* Формулы нужно выделить в отдельные блоки для лучшей читаемости [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 00:24, 23 октября 2016 (MSK)&lt;br /&gt;
* Код нужно выделить в отдельные блоки для лучшей читаемости [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 00:24, 23 октября 2016 (MSK)&lt;br /&gt;
* Графы рис 3а и особенно 3б имеют запутанную структуру. Можно ее представить значительно более просто и понятно [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 00:24, 23 октября 2016 (MSK)&lt;br /&gt;
** Необходимо улучшить читаемость всего графа (особенно 3a). Примеров построения информационного графа с меньшим числом пересечений дуг, способов организации и визуализации блоков можно найти на АлгоВики достаточно много. [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 14:46, 22 ноября 2016 (MSK)&lt;br /&gt;
* В п. 1.7 не указаны параметры алгоритма, для которых построен граф [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 00:24, 23 октября 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
* остутствуют данные по вычислительному эксперименту. [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 14:46, 22 ноября 2016 (MSK)&lt;/div&gt;</summary>
		<author><name>Teplov</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A1%D0%B0%D0%BC%D0%BE%D0%BE%D1%80%D0%B3%D0%B0%D0%BD%D0%B8%D0%B7%D1%83%D1%8E%D1%89%D0%B0%D1%8F%D1%81%D1%8F_%D0%BA%D0%B0%D1%80%D1%82%D0%B0_%D0%9A%D0%BE%D1%85%D0%BE%D0%BD%D0%B5%D0%BD%D0%B0_(%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8)&amp;diff=18935</id>
		<title>Самоорганизующаяся карта Кохонена (алгоритм кластеризации)</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A1%D0%B0%D0%BC%D0%BE%D0%BE%D1%80%D0%B3%D0%B0%D0%BD%D0%B8%D0%B7%D1%83%D1%8E%D1%89%D0%B0%D1%8F%D1%81%D1%8F_%D0%BA%D0%B0%D1%80%D1%82%D0%B0_%D0%9A%D0%BE%D1%85%D0%BE%D0%BD%D0%B5%D0%BD%D0%B0_(%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8)&amp;diff=18935"/>
		<updated>2016-11-22T11:43:59Z</updated>

		<summary type="html">&lt;p&gt;Teplov: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
Авторы проекта: &amp;lt;b&amp;gt;[[Участник:Eugene|Быковец Евгений Владимирович (620 группа)]]&amp;lt;/b&amp;gt; и &amp;lt;b&amp;gt;[[Участник:Crowslord|Ворона Игорь Игоревич (620 группа)]]&amp;lt;/b&amp;gt;&lt;br /&gt;
Быковец Е.В.: 1.1,1.2,1.3,1.4,1.5,2.7&lt;br /&gt;
Ворона И.И.: 1.6,1.7,1.8,1.9,1.10,2.8&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Алгоритм кластеризации, основанный на самоорганизующихся сетях Кохонена&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(TMn) + O(M^2)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;O(nN)&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;O(nM)&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;
Cамоорганизующаяся карта Кохонена (Self-Organizing Map, SOM) представляет из себя вычислительный метод, предназначенный для задач, в первую очередь, кластеризации и визуализации, а также анализа данных из пространств высокой размерности (иначе, многомерных данных), полученных экспериментально. Методы был предложен Туево Кохоненом (1982). Прародителями модели самоорганизующейся сети Кохонена были ранние нейросетевые модели (в частности, модель ассоциативной памяти и модель адаптивного обучения). &lt;br /&gt;
&lt;br /&gt;
Целью применения данного метода является поиск скрытых закономерностей в данных, основываясь на снижении размерности исходного пространства в пространство меньшей размерности (на практике чаще всего используется двумерное, по причине, в частности, удобной визуализации). При этом топология исходного пространства остается той же самой. В результате обучения данной модели получается решетка, состоящая из обученных нейронов, она же и называется &amp;quot;картой&amp;quot; исходного пространства.&lt;br /&gt;
&lt;br /&gt;
Архитектура самоорганизующейся карты Кохонена следующая: имеется два слоя - входной слой (распределительный) нейроноы и выходной слой (слой Кохонена) нейронов, при этом нейроны второго слоя расположены в виде двумерной решетки (обычно сетка либо квадратная, либо шестиугольная, об этом далее), так, что каждый нейрон из первого слоя соединен с каждым нейроном второго слоя (см. рис. 1) [[Файл:General Kohonen.png|thumb|200px|right|Рис. 1. Общая структура карты Кохонена]]Количество нейронов входного слоя равно размерности исходного пространства. Чаще нейроны слоя Кохонена принято называть кластерными элементами. Более точно, их количество определяет количество кластеров, на которые карта может разбить данные, которые поданы на распределительный слой. Чем больше кластерных элементов, тем более гранулярная кластеризация. Как уже было сказано, топология слоя Кохонена может быть представлена либо  виде четырехугольной сетки, либо в виде шестиугольной сетки. Второй вариант более привлекателен в силу того, что расстояния для каждого нерона до каждого соседнего с ним нейрона одинаковые. Более наглядно это представлено на рисунках (cм. рис. 2а, рис. 2б) [[Файл:quadr.jpg|thumb|200px|right|Рис. 2а.Четырехугольная решетка слоя Кохонена]] [[Файл:hex.png|thumb|200px|right|Рис. 2б.Шестиугольная решетка слоя Кохонена]]&lt;br /&gt;
&lt;br /&gt;
Каждый нейрон выходного слоя определяется вектором весов (размерность вектора - есть разность входного пространства) и упорядоченной парой : &amp;lt;math&amp;gt;(x,y)&amp;lt;/math&amp;gt;, определяющую позицию нейрона на карте Кохонена. &lt;br /&gt;
&lt;br /&gt;
Принцип обучения сети заключается в следующем:  для каждого входного вектора определяется нейрон-победитель из слоя Кохонена, после чего у нейрона-победителя и некоторого количества его соседей корректируются веса. Процедура повторяется для каждого входного вектора из обучающей выборки.&lt;br /&gt;
&lt;br /&gt;
Сеть, с предложенной архитектурой, обучается на обучающей выборке, обучение заключается в корректировке весов нейронов в слое Кохонена. После того, как сеть обучена она начинает свою работу, относя каждый нейрон из тестовой выборки к тому или иному кластеру.&lt;br /&gt;
&lt;br /&gt;
== Математическое описание алгоритма ==&lt;br /&gt;
=== Используемые обозначения ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; - номер итерации,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; - размерность исходного пространства,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \chi \subset R^n&amp;lt;/math&amp;gt; - обучающая выборка,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \Phi \subset R^n &amp;lt;/math&amp;gt; - тестовая выборка,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; N = |\chi|&amp;lt;/math&amp;gt;  - размер обучающей выборки,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; K = |\Phi|&amp;lt;/math&amp;gt;  - размер тестовой выборки,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; a &amp;lt;/math&amp;gt; -  высота слоя Кохонена,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; b &amp;lt;/math&amp;gt; - ширина слоя Кохонена,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; M = a \times b &amp;lt;/math&amp;gt; - общее количество нейронов в слое Кохонена,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; ne_j, j=1,...M &amp;lt;/math&amp;gt; - нейрон с индексом &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\vec{x}^{(i)}(t) = (x_1^{(i)}(t),...,x_n^{(i)}(t)), i=1,..,N&amp;lt;/math&amp;gt; - &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-й вектор, подлежайщий кластеризации,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\vec{w}^{(j)}(t) = (w_1^{(j)}(t),...,w_n^{(j)}(t)), j = 1,..,M&amp;lt;/math&amp;gt; - вектор весовых коэффициентов для &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;-го нейрона,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; r_k&amp;lt;/math&amp;gt; - положение &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;-го нейрона в топологии карты, то есть &amp;lt;math&amp;gt;(i_k,j_k)&amp;lt;/math&amp;gt;, &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; h_{ij}(t)&amp;lt;/math&amp;gt; - функция соседства между нейронами с индексами &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; на итерации t,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; h(d,t) &amp;lt;/math&amp;gt; - функция от расстояния между нейронами,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \alpha(t)&amp;lt;/math&amp;gt; - скорость обучения сети,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \sigma(t) &amp;lt;/math&amp;gt; - функция, уменьшающая количество соседей с при увеличении итерации (монотонно убывающий),&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; T &amp;lt;/math&amp;gt; - максимальное число итераций алгоритма обучения - может быть задано фиксировано, либо же равно мощности обучающей выборки, то есть  &amp;lt;math&amp;gt; N &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Алгоритм обучения ===&lt;br /&gt;
&lt;br /&gt;
Шаг №0. Определить значение параметров алгоритма: используемые метрики, функции &amp;lt;math&amp;gt;\alpha(t) &amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\sigma(t) &amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;t:= 0 &amp;lt;/math&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
Шаг №1. Инициализировать векторы начальных весов нейронов &amp;lt;math&amp;gt; \vec{w}^{(j)}(0) \forall j=1,..,M&amp;lt;/math&amp;gt;. Подробнее о стратегии начальной инициализации далее.&lt;br /&gt;
&lt;br /&gt;
шаг №2. Если есть еще элементы в обучающей выборке, вытащить случайным образом (например, индекс &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; - может быть реализацией случайной величины  из равномерного дискретного распределения на &amp;lt;math&amp;gt; [0,N] &amp;lt;/math&amp;gt;) входной вектор &amp;lt;math&amp;gt;\vec{x}^{(i)}(t), N:=N-1, \chi = \chi \setminus \vec{x}^{(i)}(t)&amp;lt;/math&amp;gt;, в противном случае алгоритм завершает свою работу&lt;br /&gt;
&lt;br /&gt;
шаг №3. Для &amp;lt;math&amp;gt;\vec{x}^{(i)}(t)&amp;lt;/math&amp;gt; и  &amp;lt;math&amp;gt; \vec{w}^{(j)}(t) \forall j=1,..,M&amp;lt;/math&amp;gt; найти &amp;lt;math&amp;gt; = \rho(\vec{x}^{(i)}(t),  \vec{w}^{(j)}(t)) =  || \vec{x}^{(i)}(t) -  \vec{w}^{(j)}(t)||^2 &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
шаг №4.  Находим нейрон-победитель &amp;lt;math&amp;gt;ne_c&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt; c = \arg \min_{\forall j \in \{1, \dots, M\}} \rho(\vec{x}^{(i)}(t), \vec{w}^{(j)}(t))&amp;lt;/math&amp;gt;, который лежит ближе к текущему объекту &amp;lt;math&amp;gt;\vec{x}^{(i)}(t)&amp;lt;/math&amp;gt; по рассматриваемой метрике &amp;lt;math&amp;gt; || \circ || &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
шаг №5. Для всех нейронов &amp;lt;math&amp;gt; ne_{j} &amp;lt;/math&amp;gt;  выполнить пересчет весовых векторов:&lt;br /&gt;
&amp;lt;math&amp;gt; \vec{w}^{(j)}(t+1) := \vec{w}^{(j)}(t) +\alpha(t)h_{ci}(t)[\vec{x}^{(i)}(t) -  \vec{w}^{(j)}(t)]&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
шаг №6. &amp;lt;math&amp;gt;t := t+1 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
шаг №7. Перейти к шагу 2.&lt;br /&gt;
&lt;br /&gt;
=== Настройка алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В этом разделе опишем более подробно настраиваемые аспекты алгоритма.&lt;br /&gt;
&lt;br /&gt;
==== Выбор начального приближения для &amp;lt;math&amp;gt;\vec{w}^{j}(0) \forall j=1,\dots,M&amp;lt;/math&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Во-первых, инициализацию можно проводить объектами из обучающей выборки, то есть выбрав для каждого весового вектора некоторый элемент из обучающий выборки и установить значения весовых компонент равными компонентам выбранного вектора. Плюсом такого подхода является то, что вектора весов изначально находятся в том регионе, где и объекты из выборки, то есть требуется меньше итераций на этапе настройки.&lt;br /&gt;
&lt;br /&gt;
Во-вторых, инициализацию весов можно проводить какими-то случайными значениями. Например можно взять нескорое распределение, пусть равномерное или нормальное, и для каждой сгенерировать псевдослучайное число из выбранного распределения, то есть &amp;lt;math&amp;gt; w_i^{(j)} \sim \mathbf P &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; \mathbf P &amp;lt;/math&amp;gt; - это заданное распределение. Плюсом данного подхода является простота однако, &amp;quot;настройка&amp;quot; в данном случае может быть долгой. &lt;br /&gt;
&lt;br /&gt;
В-третьих, можно использовать линейную инициализацию, то есть весовые вектора имеют значения векторов из подпространства, натянутого на  два главных собственными вектора пространства исходного набора данных, при этом упорядоченным образом. Это наиболее практичный способ.&lt;br /&gt;
&lt;br /&gt;
==== Выбор скорости обучения сети ====&lt;br /&gt;
&lt;br /&gt;
Функцию скорости обучения сети &amp;lt;math&amp;gt;\alpha(t)&amp;lt;/math&amp;gt; можно выбрать, например, следующим образом:&lt;br /&gt;
&lt;br /&gt;
Самый простой способ: &amp;lt;math&amp;gt;\alpha(t) := \frac{A}{B+t}&amp;lt;/math&amp;gt;, где  &amp;lt;math&amp;gt;A = const, B = const &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Экспоненциальная скорость обучения: &amp;lt;math&amp;gt;\alpha(t) := \alpha_0 e^{ -\frac{t}{\lambda}},  \lambda \in (0, 1), \lambda = const, \alpha_0 = const&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Показательная скорость обучения: &amp;lt;math&amp;gt;\alpha(t) := \lambda^{\frac{t}{T}}, \lambda \in (0, 1), \lambda = const&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Выбор функции соседства между нейронами ====&lt;br /&gt;
&lt;br /&gt;
Обычно полагают  &amp;lt;math&amp;gt; h_{cj}(t) :=  \alpha(t) h(d,t) &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt; d = || ne_c - ne_j ||, c &amp;lt;/math&amp;gt; -индекс нейрона победителя, при этом:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; h(d,t) = \begin{cases}const, d\leq \sigma(t) \\0, d &amp;gt; \sigma(t) \end{cases}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; h(d,t) = e ^{-\frac{d^2}{2(\sigma(t)^2)}}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\sigma(t)&amp;lt;/math&amp;gt;  - это некоторый сомножитель, уменьшающий количество соседей, которые будут подвержены корректировке весов с увеличением итераций, эта функция монотонно убывает. &lt;br /&gt;
&lt;br /&gt;
Функция &amp;lt;math&amp;gt; \sigma(t) &amp;lt;/math&amp;gt; также является параметром алгоритма. Возможная функциональная зависимость для нее:&lt;br /&gt;
&amp;lt;math&amp;gt;\sigma(t) = \frac{\sigma_0}{1 + \frac{t}{T}}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Стоит отметить, что  имеется много других эвристических мер сходств которые присваивают ненулевые веса внутри некоторого радиуса, это позволяет производить вычисления не для всех соседей.&lt;br /&gt;
&lt;br /&gt;
=== Алгоритм работы карты ===&lt;br /&gt;
&lt;br /&gt;
шаг 0. Пока есть вектора в тестовой выборке извлечь (последовательно), &amp;lt;math&amp;gt;\vec{x}^{(i)}, K:=K-1, \Phi = \Phi \setminus \vec{x}^{(i)}&amp;lt;/math&amp;gt;, в противном случае алгоритм завершает свою работу.&lt;br /&gt;
&lt;br /&gt;
шаг 1.  Для &amp;lt;math&amp;gt;\vec{x}^{(i)}&amp;lt;/math&amp;gt; и  &amp;lt;math&amp;gt; \vec{w}^{(j)} \forall j=1,..,M&amp;lt;/math&amp;gt; найти &amp;lt;math&amp;gt; \rho(\vec{x}^{(i)},  \vec{w}^{(j)}) =  || \vec{x}^{(i)} -  \vec{w}^{(j)}||^2 &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
шаг 2. Находим нейрон-победитель &amp;lt;math&amp;gt;ne_c&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt; c = \arg \min_{\forall j \in \{1, \dots, L\}} \rho(\vec{x}^{(i)}, \vec{w}^{(j)})&amp;lt;/math&amp;gt;, который лежит ближе к текущему объекту &amp;lt;math&amp;gt;\vec{x}^{(i)}&amp;lt;/math&amp;gt; по рассматриваемой метрике &amp;lt;math&amp;gt; || \circ || &amp;lt;/math&amp;gt; и относим &amp;lt;math&amp;gt;\vec{x}^{(i)} &amp;lt;/math&amp;gt; к кластеру, соответствующему нейрону &amp;lt;math&amp;gt;ne_c&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
шаг 3. Переход к шагу 0.&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Вычислительное ядро алгоритма обучения составляет (соответсвенно, пункты 3,4,5 алгоритма обучения):&lt;br /&gt;
&lt;br /&gt;
* подсчет расстояний между входным вектором и всеми нейронами:&lt;br /&gt;
&lt;br /&gt;
Для &amp;lt;math&amp;gt;\vec{x}^{(i)}(t)&amp;lt;/math&amp;gt; и  &amp;lt;math&amp;gt; \vec{w}^{(j)}(t) \forall j=1,..,M&amp;lt;/math&amp;gt; найти &amp;lt;math&amp;gt; = \rho(\vec{x}^{(i)}(t),  \vec{w}^{(j)}(t)) =  || \vec{x}^{(i)}(t) -  \vec{w}^{(j)}(t)||^2 &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* Поиск минимума между всеми найденными расстояниями для нахождения нейрона-победителя:&lt;br /&gt;
&lt;br /&gt;
Находим нейрон-победитель &amp;lt;math&amp;gt;ne_c&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt; c = \arg \min_{\forall j \in \{1, \dots, L\}} \rho(\vec{x}^{(i)}(t), \vec{w}^{(j)}(t))&amp;lt;/math&amp;gt;, который лежит ближе к текущему объекту &amp;lt;math&amp;gt;\vec{x}^{(i)}(t)&amp;lt;/math&amp;gt; по рассматриваемой метрике &amp;lt;math&amp;gt; || \circ || &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* Корректировка весовых векторов: сюда же входит поиск расстояний между всеми нейронами в рамках заданной топологии, а также вычисление функции соседства:&lt;br /&gt;
&lt;br /&gt;
Для всех нейронов &amp;lt;math&amp;gt; ne_{j} &amp;lt;/math&amp;gt;  выполнить пересчет весовых векторов:&lt;br /&gt;
&amp;lt;math&amp;gt; \vec{w}^{(j)}(t+1) := \vec{w}^{(j)}(t) +\alpha(t)h_{ci}(t)[\vec{x}^{(i)}(t) -  \vec{w}^{(j)}(t)]&amp;lt;/math&amp;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;\vec{x}^{(i)}&amp;lt;/math&amp;gt; и  &amp;lt;math&amp;gt; \vec{w}^{(j)} \forall j=1,..,M&amp;lt;/math&amp;gt; найти &amp;lt;math&amp;gt; \rho(\vec{x}^{(i)},  \vec{w}^{(j)}) =  || \vec{x}^{(i)} -  \vec{w}^{(j)}||^2 &amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* Поиск минимума между всеми найденными расстояниями для нахождения нейрона-победителя:&lt;br /&gt;
&lt;br /&gt;
Находим нейрон-победитель &amp;lt;math&amp;gt;ne_c&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt; c = \arg \min_{\forall j \in \{1, \dots, L\}} \rho(\vec{x}^{(i)}, \vec{w}^{(j)})&amp;lt;/math&amp;gt;, который лежит ближе к текущему объекту &amp;lt;math&amp;gt;\vec{x}^{(i)}&amp;lt;/math&amp;gt; по рассматриваемой метрике &amp;lt;math&amp;gt; || \circ || &amp;lt;/math&amp;gt; и относим &amp;lt;math&amp;gt;\vec{x}^{(i)} &amp;lt;/math&amp;gt; к кластеру, соответствующему нейрону &amp;lt;math&amp;gt;ne_c&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
В алгоритме обучения можно выделить 4 макрофазы:&lt;br /&gt;
&lt;br /&gt;
* Начальная инициализация всех весов: построение матрицы &amp;lt;math&amp;gt; \mathbf{ W(0)} \in \mathbf {R^{N \times M}} &amp;lt;/math&amp;gt;, столбцами которой являются весовые вектора всех нейронов.&lt;br /&gt;
&lt;br /&gt;
* Выполнение следующих фаз &amp;lt;math&amp;gt; \mathbf T &amp;lt;/math&amp;gt; раз:&lt;br /&gt;
** Вычисление вектора расстояний &amp;lt;math&amp;gt; \mathbf D \in \mathbf {R^M} &amp;lt;/math&amp;gt;, в котором хранятся вектора расстояний от вектора &amp;lt;math&amp;gt; \mathbf X &amp;lt;/math&amp;gt; до векторов матрицы &amp;lt;math&amp;gt; \mathbf {W(t)} &amp;lt;/math&amp;gt;.&lt;br /&gt;
** Нахождение индекса минимума в векторе &amp;lt;math&amp;gt; \mathbf D &amp;lt;/math&amp;gt; - индекса нейрона победителя;&lt;br /&gt;
** Корректировка всех весов: получение матрицы &amp;lt;math&amp;gt; \mathbf {W(t+1)} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
В алгоритме работы алгоритма можно выделить две макрофазы:&lt;br /&gt;
&lt;br /&gt;
* Выполнение следующих фаз &amp;lt;math&amp;gt; \mathbf K &amp;lt;/math&amp;gt; раз:&lt;br /&gt;
** Вычисление вектора расстояний &amp;lt;math&amp;gt; \mathbf D \in \mathbf {R^M} &amp;lt;/math&amp;gt;, в котором хранятся вектора расстояний от вектора &amp;lt;math&amp;gt; \mathbf X &amp;lt;/math&amp;gt; до векторов матрицы &amp;lt;math&amp;gt; \mathbf {W} &amp;lt;/math&amp;gt;.&lt;br /&gt;
** Нахождение индекса минимума в векторе &amp;lt;math&amp;gt; \mathbf D &amp;lt;/math&amp;gt; - индекса нейрона победителя и определение входного вектора в кластер, соответсвующий нейрону-победителю;&lt;br /&gt;
&lt;br /&gt;
Также можно выделить математические операции, используемые в вышеприведенных:&lt;br /&gt;
* &amp;lt;math&amp;gt;\vec{x}^{T} \vec{y}&amp;lt;/math&amp;gt; - скалярное произведение вектров;&lt;br /&gt;
* &amp;lt;math&amp;gt;\vec{x} + \vec{y}&amp;lt;/math&amp;gt; - сложение двух векторов;&lt;br /&gt;
* &amp;lt;math&amp;gt;\lambda \vec{x}&amp;lt;/math&amp;gt; - умножение вектора на скаляр;&lt;br /&gt;
* &amp;lt;math&amp;gt;\min_i x_i&amp;lt;/math&amp;gt; - поиск минимального элемента в векторе &amp;lt;math&amp;gt;\vec{x}&amp;lt;/math&amp;gt;;&lt;br /&gt;
* Вычисление функции соседства &amp;lt;math&amp;gt; h_{ci}(t) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Схему реализации последовательного алгоритма обучения запишем в виде псевдокода:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=oberon2&amp;gt;&lt;br /&gt;
SetAlgorithmParameters()&lt;br /&gt;
CalculateDistancesBetweenNeuronsInEuclidianPlane()&lt;br /&gt;
W = SetNeuronWeightsVector()&lt;br /&gt;
FOR t = 1 to T do&lt;br /&gt;
  x = ChooseInputVector()&lt;br /&gt;
  D = InitialiseDistancesVector()&lt;br /&gt;
  FOR n = 1 to M do&lt;br /&gt;
    d = CalculateDistanseBetweenInputVectorAndNeuronWeightsVector(x,n)&lt;br /&gt;
    PutCalculatedDistanceToDistancesVector(D, d)&lt;br /&gt;
  END FOR&lt;br /&gt;
  ne_c = FindNeuronWinner(D)&lt;br /&gt;
  FOR n = 1 to M do&lt;br /&gt;
    CorrectNeuronsWeightsVector(W)&lt;br /&gt;
  END FOR&lt;br /&gt;
END FOR&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Схему реализации последовательного алгоритма работы запишем в виде псевдокода:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=oberon2&amp;gt;&lt;br /&gt;
FOR t = 1 to T do&lt;br /&gt;
  x = ChooseInputVector()&lt;br /&gt;
  D = InitialiseDistancesVector()&lt;br /&gt;
  FOR n = 1 to K do&lt;br /&gt;
    d = CalculateDistanseBetweenInputVectorAndNeuronWeightsVector(x,n)&lt;br /&gt;
    PutCalculatedDistanceToDistancesVector(D, d)&lt;br /&gt;
  END FOR&lt;br /&gt;
  ne_c = FindNeuronWinner(D)&lt;br /&gt;
  DefineInputVectorToCluster()&lt;br /&gt;
END FOR&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Дадим комментарии по некоторым функциям:&lt;br /&gt;
&amp;lt;math&amp;gt;SetAlgorithmParameters()&amp;lt;/math&amp;gt; - установка параметров алгоритма проводится исследователем для увеличения/уменьшения гранулярности кластеризации;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;CalculateDistancesBetweenNeuronsInEuclidianPlane()&amp;lt;/math&amp;gt; - вычисление матрицы расстояний между всеми нейронами в Евклидовой плоскости, в соответствии с их расположением в слое Кохонена - эта информация потребуется &amp;lt;math&amp;gt;CorrectNeuronsWeightsVector(W)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;W = SetNeuronWeightsVector()&amp;lt;/math&amp;gt; - установка векторов веса нейронов (стратегии инициализации были рассмотрены выше: случайным образом, значениями из обучающей выборки, с помощью значений из пространства векторов, натянутого на главные компоненты);&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;math&amp;gt;x = ChooseInputVector()&amp;lt;/math&amp;gt; - выбор входного вектора из выборки (стратегия выбора также было рассмотрена выше: индекс вектора в обучающей выборке - реализация случайной величины из заданного распределения вероятностей, например);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;D = InitialiseDistancesVector()&amp;lt;/math&amp;gt; - заполнение массива растояний нулями;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;d= CalculateDistanseBetweenInputVectorAndNeuronWeightsVector(x,n)&amp;lt;/math&amp;gt; - вычисление расстояния между входным вектором и вектора веса нейрона по выбранной метрике (например, евклидовом);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;PutCalculatedDistanceToDistancesVector(D, d)&amp;lt;/math&amp;gt;  - добавление вычисленного расстояния между входным вектором и вектором веса текущего нейрона в массив расстояний;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;ne_c = FindNeuronWinner(D)&amp;lt;/math&amp;gt; - нахождение нерона победителя, индекс которого является индексом минимума в массиве расстояний;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;CorrectNeuronsWeightsVector(W)&amp;lt;/math&amp;gt; - корректировка весов нейронов по формуле: &amp;lt;math&amp;gt; \vec{w}^{(j)}(t+1) := \vec{w}^{(j)}(t) +\alpha(t)h_{ci}(t)[\vec{x}^{(i)}(t) -  \vec{w}^{(j)}(t)]&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;DefineInputVectorToCluster()&amp;lt;/math&amp;gt; - определение входного вектора в соответсвующий кластер, концептуально ничем не отличается от нахождения нейрона-победителя, однако отвечает за формальный вывод результата.&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Начнём рассмотрение сложности последовательной реализации алгоритма Кохонена «сверху – вниз» начиная со сложности максимально абстрактного набора функций и потом конкретизируя сложность каждой функции в рамках фундаментальных вычислительных операций (присваивание, суммирование и т.п.). Это позволяет пересчитать сложность алгоритма Кохонена при условии замены функции составляющих его основу и при реализации его на целевую платформу.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;I АЛГОРИТМ ОБУЧЕНИЯ&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
1)	Уровень набора функций&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;Complexity(SOMLearning) = &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;Complexity(SetAlgorithmParameters) +&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;Complexity(CalculateDistancesBetweenNeuronsInEuclidianPlane) +&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;Complexity(SetNeuronWeightsVector) +&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;T \cdot Complexity(ChooseInputVector) +&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;T \cdot Complexity(InitialiseDistancesVector) +&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;T \cdot M \cdot Complexity(CalculateDistanseBetweenInputVectorAndNeuronWeightsVector) +&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;T \cdot M \cdot Complexity(PutCalculatedDistanceToDistancesVector) +&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;T \cdot Complexity(FindNeuronWinner) +&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;T \cdot M \cdot Complexity(CorrectNeuronsWeightsVector)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2)	Уровень реализации функций в рамках фундаментальных операций&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Будем считать, что следующие операции выполняются за константное время (&amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;):&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;mov&amp;lt;/math&amp;gt; - присваивание&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;sqrt&amp;lt;/math&amp;gt; - взятие квадратного корня из числа&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;rand&amp;lt;/math&amp;gt; – генерация случайного числа&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;add&amp;lt;/math&amp;gt; - сложение&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;sub&amp;lt;/math&amp;gt; - вычитание&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;mul&amp;lt;/math&amp;gt; - умножение&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;comp&amp;lt;/math&amp;gt; - сравнение&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;exp&amp;lt;/math&amp;gt; - экспоненцирование&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;div&amp;lt;/math&amp;gt; - деление&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Тогда:&lt;br /&gt;
:Так как параметров всегда конечное число и мы делаем допущение, что это число меньше M и T:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;Complexity(SetAlgorithmParameters) = O(Complexity(mov)) = O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;Complexity(CalculateDistancesBetweenNeuronsInEuclidianPlane) = &amp;lt;/math&amp;gt;&lt;br /&gt;
:&amp;lt;math&amp;gt;O(M \cdot (M-1) \cdot (2 \cdot Complexity(sub) + 2 \cdot Complexity(mul) +&amp;lt;/math&amp;gt;&lt;br /&gt;
:&amp;lt;math&amp;gt; Complexity(add) + Complexity(sqrt))) = O(M^2)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;Complexity(SetNeuronWeightsVector) = O(Mn \cdot Complexity(mov)) \vee O(Mn \cdot Complexity(rand))) = O(Mn)&amp;lt;/math&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;Complexity(ChooseInputVector) = O(n \cdot Complexity(mov))= O(n)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;Complexity(InitialiseDistancesVector) = O(M \cdot n \cdot Complexity(mov)) = O(M \cdot n)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;Complexity(CalculatedDistanseBetweenInputVectorAndNeuronWeightsVector) = O(n \cdot Complexity(sub)) +&amp;lt;/math&amp;gt;&lt;br /&gt;
:&amp;lt;math&amp;gt;O(n \cdot Complexity(mul)) + O((n - 1) \cdot Complexity(add)) = O(n)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;Complexity(PutCalculatedDistanceToDistancesVector) = O(n \cdot Complexity(mov)) = O(n)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;Complexity(FindNeuronWinner) = O(M \cdot n \cdot Complexity(comp)) = O(Mn)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;Complexity(CorrectNeuronsWeightsVector) = O(nComplexity(add)) + O(nComplexity(mul)) + &amp;lt;/math&amp;gt;&lt;br /&gt;
:&amp;lt;math&amp;gt;O(Complexity(NeighborhoodFunction)) = O(n) + O(Complexity(NeighborhoodFunction)) = O(n) +&amp;lt;/math&amp;gt;&lt;br /&gt;
:&amp;lt;math&amp;gt; O(Complexity(exp) + Complexity(div)) = O(n)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3)	Конечная сложность&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;Complexity(SOMLearning) = O(1) + O(M^2) + O(Mn) + O(Tn) + O(TMn) + O(TMn) + O(TMn) + O(TMn) + O(TMn) =&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt; O(TMn) + O(M^2)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;II АЛГОРИТМ РАБОТЫ&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
1)	Уровень набора функций&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;Complexity(SOMWorking) = &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;T \cdot Complexity(ChooseInputVector) +&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;T \cdot Complexity(InitialiseDistancesVector) +&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;T \cdot K \cdot Complexity(CalculateDistanseBetweenInputVectorAndNeuronWeightsVector) +&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;T \cdot K \cdot Complexity(PutCalculatedDistanceToDistancesVector) +&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;T \cdot Complexity(FindNeuronWinner) +&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;T \cdot Complexity(DefineInputVectorToCluster)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2)	Уровень реализации функций в рамках фундаментальных операций&lt;br /&gt;
&lt;br /&gt;
Эквивалентно п.2 в Алгоритме обучения (но в качестве M у нас K).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;Complexity(DefineInputVectorToCluster) = O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3)	Конечная сложность&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;Complexity(SOMWorking) = O(Tn) + O(TKn) + O(TKn) + O(TKn) + O(TKn) + O(T) = O(TKn)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
[[Файл:InfGraphSOM.png|500px|thumb|right|Рис. 3а.Информационный граф алгоритма обучения]]&lt;br /&gt;
&lt;br /&gt;
В качестве операторных узлов информационного графа были выбраны:&lt;br /&gt;
&lt;br /&gt;
•	&#039;&#039;&#039;||a – b||&#039;&#039;&#039; - взятие евклидовой метрики от двух операндов&lt;br /&gt;
&lt;br /&gt;
•	&#039;&#039;&#039;FindMinimum&#039;&#039;&#039; – нахождение минимума из входящих значений&lt;br /&gt;
&lt;br /&gt;
•	&#039;&#039;&#039;UpdateWeights&#039;&#039;&#039; – обновление весов (для каждого входящего вектора отдельный узел)&lt;br /&gt;
&lt;br /&gt;
•	&#039;&#039;&#039;DefineCluster&#039;&#039;&#039; – определение кластера на основе полученного значения от нахождения минимума&lt;br /&gt;
&lt;br /&gt;
Это обусловлено тем, что с нашей точки зрения именно такой набор операций наиболее оптимально показывает структуру алгоритма, избавляя нас от излишней детализации.&lt;br /&gt;
&lt;br /&gt;
Входные параметры алгоритма, для которого был построен информационный граф:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; N = 2&amp;lt;/math&amp;gt;  - размер обучающей выборки,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; K = 2&amp;lt;/math&amp;gt;  - размер тестовой выборки,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; a = 1&amp;lt;/math&amp;gt; -  высота слоя Кохонена,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; b = 2&amp;lt;/math&amp;gt; - ширина слоя Кохонена,&lt;br /&gt;
&lt;br /&gt;
Остальные параметры могут быть любыми, так как не оказывают влияние на построенный информационный граф.&lt;br /&gt;
[[Файл:InfGraphSOM2.png|500px|thumb|center|Рис. 3б.Информационный граф алгоритма работы]]&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Для того чтобы оценить насколько эффективно Алгоритм Кохонена параллелизуется, рассмотрим его структуру в виде последовательности этапов исполнения. Вначале рассмотрим алгоритм обучения. У нас есть 4 фундаментальных этапа:&lt;br /&gt;
&lt;br /&gt;
[[Файл:SOMParallel.png|500px|thumb|right|Рис. 4.Пример параллельного алгоритма SOM при threadNumber = 2]]&lt;br /&gt;
1)	&#039;&#039;&#039;Инициализация&#039;&#039;&#039;. На этом этапе мы определяем параметры алгоритма, рассчитываем расстояния между нейронами и определяем начальные веса нейронов. Здесь имеет смысл распараллелить подсчет метрики между нейронами. Таким образом асимптотическая сложность упадет с &amp;lt;math&amp;gt;O(M^2)&amp;lt;/math&amp;gt; до &amp;lt;math&amp;gt;O(\frac{M^2}{threadNumber})&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2)	&#039;&#039;&#039;Генерация подвыборки&#039;&#039;&#039;. Здесь мы определяем в каком порядке будем обрабатывать вектора исходного входного пространства, какой вектор возьмём для следующей итерации. К сожалению, эта стадия не поддается распараллеливанию, так как каждая итерация алгоритма зависит от предыдущей;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3)	&#039;&#039;&#039;Поиск максимального подобия&#039;&#039;&#039;. Находим наиболее подходящий (победивший) нейрон, используя критерий минимума Евклидова расстояния. Этот этап распараллеливается следующим образом. Вначале мы рассчитываем метрику между вектором весов каждого нейрона и входящим вектором исходного пространства, для этого мы разбиваем количество нейронов &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;threadNumber&amp;lt;/math&amp;gt; потоков и подсчитываем метрику в каждой группе нейронов на поток параллельно. Затем мы пользуемся фактом, что минимум – дистрибутивная функция (см. Рис. 4)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;min([a, b, c, d]) = min(min([a, b]), min([b, c]))&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
и рассчитываем её за &amp;lt;math&amp;gt;\log_{2} (threadNumber)&amp;lt;/math&amp;gt; операций сравнения, что значительно меньше &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; в исходном варианте;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4)	Коррекция. Корректируем векторы синаптических всех нейронов. Также очевидным образом распараллеливается на &amp;lt;math&amp;gt;threadNumber&amp;lt;/math&amp;gt; потоков, в каждым из которых последовательно для каждого нейрона в группе внутри потока обновляются веса.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Итак, общая сложность параллельной версии алгоритма обучения &lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;Complexity(SOMParallelLearning) = O(\frac{M^2}{threadNumber}) + &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;O(\frac{TMn}{threadNumber} + \log_{2} (threadNumber)) + O(\frac{TMn}{threadNumber})&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
что меньше последовательной (при &amp;lt;math&amp;gt;threadNumber \approx M&amp;lt;/math&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;Complexity(SOMLearning) = O(M^2) + O(TMn) + O(TMn)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Алгоритм работы карты эквивалентен первым 3 уровням алгоритма обучения только с другими константами&lt;br /&gt;
(нужно заменить &amp;lt;math&amp;gt;M&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;K&amp;lt;/math&amp;gt;, предварительно убрав слагаемое &amp;lt;math&amp;gt;O(M^2)&amp;lt;/math&amp;gt;,&lt;br /&gt;
так как во время работы рассчитывать расстояние между нейронами не нужно).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вывод&#039;&#039;&#039;: Проведенный обзор показал, что только этап этап генерации подвыборки не поддается распараллеливанию. Это типичная проблема большинства алгоритмов машинного обучения, так как они итеративны (последовательно обучается по всей выборке). Однако в рамках остальных этапов алгоритм легко разбивается на потоки, что в теории улучшает производительность.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Входные данные алгоритма:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; - размерность исходного пространства,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \chi \subset R^n&amp;lt;/math&amp;gt; - обучающая выборка,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \Phi \subset R^n &amp;lt;/math&amp;gt; - тестовая выборка,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; N = |\chi|&amp;lt;/math&amp;gt;  - размер обучающей выборки,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; K = |\Phi|&amp;lt;/math&amp;gt;  - размер тестовой выборки,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; a &amp;lt;/math&amp;gt; -  высота слоя Кохонена,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; b &amp;lt;/math&amp;gt; - ширина слоя Кохонена,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; T &amp;lt;/math&amp;gt; - максимальное число итераций алгоритма обучения - может быть задано фиксировано, либо же равно мощности обучающей выборки, то есть  &amp;lt;math&amp;gt; N &amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; h_{ij}(t)&amp;lt;/math&amp;gt; - функция соседства между нейронами с индексами &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; на итерации t,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; h(d,t) &amp;lt;/math&amp;gt; - функция от расстояния между нейронами,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \alpha(t)&amp;lt;/math&amp;gt; - скорость обучения сети,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; \sigma(t) &amp;lt;/math&amp;gt; - функция, уменьшающая количество соседей с при увеличении итерации (монотонно убывающий).&lt;br /&gt;
&lt;br /&gt;
Вычислительная сложность хранения входных данных для алгоритма обучения равна &amp;lt;math&amp;gt;O(n \times N)&amp;lt;/math&amp;gt;&lt;br /&gt;
(установка функций и прочих параметров выполняется за константное время).&lt;br /&gt;
&lt;br /&gt;
Вычислительная сложность хранения входных данных для алгоритма работы равна &amp;lt;math&amp;gt;O(n \times K)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные алгоритма обучения:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\vec{w}^{(j)}(t) = (w_1^{(j)}(t),...,w_n^{(j)}(t)), j = 1,..,M&amp;lt;/math&amp;gt; - вектор весовых коэффициентов для &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;-го нейрона,&lt;br /&gt;
&lt;br /&gt;
матрица &amp;lt;math&amp;gt;\mathbf{W}&amp;lt;/math&amp;gt;, представляющая веса нейронов &amp;lt;math&amp;gt;\vec{w}^{(j)}(t)&amp;lt;/math&amp;gt;&lt;br /&gt;
Вычислительная сложность хранения выходных данных равна &amp;lt;math&amp;gt;O(n \times M)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные алгоритма работы&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
Вычислительная сложность хранения выходных данных равна &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;, если алгоритм возвращает индекс кластера или координату нейрона,&lt;br /&gt;
иначе если возвращает вектор весов нейрона-победителя, то &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
Итак, вычислительная мощность алгоритма кластеризации самоорганизующимися сетями Кохонена равна &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;ComputationalPower(SOM) = \frac{TMn + M^2}{Mn + Tn} = \frac{M(Tn + M)}{n(M + T)}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Cоотношение последовательной и параллельной сложности алгоритма описано в п. 1.8&lt;br /&gt;
&lt;br /&gt;
Детерминированность алгоритма зависит от способа генерации выборки из исходного входного пространства, если она &lt;br /&gt;
детерминирована, то и алгоритм детерминирован, иначе нет.&lt;br /&gt;
&lt;br /&gt;
Общие свойства алгоритма:&lt;br /&gt;
*Аппроксимация входного пространства. Карта признаков, представленная множеством векторов синоптических весов, в выходном пространстве реализует хорошую аппроксимацию входного пространства.&lt;br /&gt;
*Топологический порядок. Карта признаков, полученная алгоритмом SOM, является топологически упорядоченной в том смысле, что пространственное положение нейронов в решетке соответствует конкретной области или признаку входного образа.&lt;br /&gt;
*Соответствие плотности. Карта признаков отражает вариации в статистиках распределения входного сигнала. Области во входном пространстве, из которого берутся векторы &amp;lt;math&amp;gt;\vec{x}&amp;lt;/math&amp;gt; с большей вероятностью, отображаются в гораздо большие области выходного пространства и, таким образом, с большим разрешением, чем области в исходном пространстве, из которых берутся векторы &amp;lt;math&amp;gt;\vec{x}&amp;lt;/math&amp;gt; с меньшей вероятностью.&lt;br /&gt;
*Выбор признаков. Для данных из входного пространства с нелинейным распределением самоорганизующаяся карта для аппроксимации исследуемого распределения способна извлечь набор наилучших признаков.&lt;br /&gt;
&lt;br /&gt;
Преимущества алгоритма:&lt;br /&gt;
&lt;br /&gt;
*Является устойчивым, в том числе и к зашумленным данным;&lt;br /&gt;
*Реализует быстрое и неуправляемое обучение (процесс самоорганизуюется);&lt;br /&gt;
*Может визуализировать многомерные входные данные (упрощения многомерных входных данных с помощью визуализации);&lt;br /&gt;
*Может аппроксимировать любую непрерывную функцию, что позволяет исследователю не принимать заранее какие-либо гипотезы относительно модели.&lt;br /&gt;
&lt;br /&gt;
Недостатки алгоритма:&lt;br /&gt;
&lt;br /&gt;
*Работает только с вещественными числовыми векторами;&lt;br /&gt;
*Может быть использован для кластерного анализа только в том случае, если заранее известно число кластеров;&lt;br /&gt;
*Наличие искажений. Близкие объекты исходного пространства могут переходить в далёкие точки на карте;&lt;br /&gt;
*Окончательный результат работы нейронных сетей сильно зависит от начальных установок сети (начальное распределение весов, свойства потенциальной функции).&lt;br /&gt;
&lt;br /&gt;
= Программная реализация алгоритма =&lt;br /&gt;
&lt;br /&gt;
== Особенности реализации последовательного алгоритма ==&lt;br /&gt;
&lt;br /&gt;
== Локальность данных и вычислений ==&lt;br /&gt;
&lt;br /&gt;
== Возможные способы и особенности параллельной реализации алгоритма ==&lt;br /&gt;
&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
&lt;br /&gt;
== Динамические характеристики и эффективность реализации алгоритма ==&lt;br /&gt;
&lt;br /&gt;
== Выводы для классов архитектур ==&lt;br /&gt;
&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Авторами проекта была также практически написана собственная реализация (возможно, она будет доведена до конца и протестирована).&lt;br /&gt;
&lt;br /&gt;
На данный момент удалось найти следующие последовательные реализации:&lt;br /&gt;
&lt;br /&gt;
* [http://knnl.sourceforge.net/ KNNL]:Реализация на C++&lt;br /&gt;
* [https://pypi.python.org/pypi/kohonen kohonen] Реалиазция на Python&lt;br /&gt;
* [https://cran.r-project.org/web/packages/kohonen/ kohonen] Реалиазция на R&lt;br /&gt;
* [http://jknnl.sourceforge.net/ JKNNL]: Реалиазция на Java.&lt;br /&gt;
* [https://www.mathworks.com/help/nnet/ug/cluster-with-self-organizing-map-neural-network.html?requestedDomain=www.mathworks.com Matlab realization]: Реалиазция на Matlab.&lt;br /&gt;
&lt;br /&gt;
Также есть параллельные реализации:&lt;br /&gt;
&lt;br /&gt;
* [https://github.com/peterwittek/somoclu Somoclu]: Реализация на Python, R, Julia, и Matlab, поддерживаются OpenMP, MPI и CUDA.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
* T. Kohonen, Self-Organizing Maps (Third Extended Edition), New York, 2001, 501 pages. ISBN 3-540-67921-9&lt;br /&gt;
* Нейский И.М. Классификация и сравнение методов кластеризации [[http://it-claim.ru/Persons/Neyskiy/Article2_Neiskiy.pdf]]&lt;br /&gt;
* Хайкин С. Нейронные сети: полный курс, 2-е изд.: Пер. с англ. - М.:ООО &amp;quot;И. Д. Вильямс&amp;quot;, 2016. - 1104с. ISBN 978-5-8459-2069-0 (рус.)&lt;br /&gt;
* К. В. Воронцов Лекции по искусственным нейронным сетям 21 декабря 2007 г.&lt;/div&gt;</summary>
		<author><name>Teplov</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9E%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D1%83%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA%D0%B0:Kravtsovkirill&amp;diff=18934</id>
		<title>Обсуждение участника:Kravtsovkirill</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9E%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D1%83%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA%D0%B0:Kravtsovkirill&amp;diff=18934"/>
		<updated>2016-11-22T11:42:20Z</updated>

		<summary type="html">&lt;p&gt;Teplov: /* Замечания */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Статья [[Кластеризация сетями Кохонена]] ==&lt;br /&gt;
&lt;br /&gt;
=== Замечания ===&lt;br /&gt;
* Формулы в статье лучше выделить в отдельные блоки, примерно так, как это сделано с кодом [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 00:01, 23 октября 2016 (MSK)&lt;br /&gt;
* В п. 1.2 подпункты лучше внести в общую нумерованную иерархию.  [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 00:01, 23 октября 2016 (MSK)&lt;br /&gt;
* В п. 1.9 должны быть оценки объемов входных и выходных данных [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 00:07, 23 октября 2016 (MSK)&lt;br /&gt;
* Необходимо указать вклад каждого автора [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 00:38, 23 октября 2016 (MSK)&lt;br /&gt;
* Код необходимо вносить в соответствующие теги source ... [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 14:37, 22 ноября 2016 (MSK)&lt;br /&gt;
* Необходимо описание использованной для проведения экспериментов аппаратной платформы. [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 14:42, 22 ноября 2016 (MSK)&lt;/div&gt;</summary>
		<author><name>Teplov</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:Emaksimov/%D0%9A%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F_%D1%81%D0%B5%D1%82%D1%8F%D0%BC%D0%B8_%D0%9A%D0%BE%D1%85%D0%BE%D0%BD%D0%B5%D0%BD%D0%B0&amp;diff=18933</id>
		<title>Участник:Emaksimov/Кластеризация сетями Кохонена</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:Emaksimov/%D0%9A%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F_%D1%81%D0%B5%D1%82%D1%8F%D0%BC%D0%B8_%D0%9A%D0%BE%D1%85%D0%BE%D0%BD%D0%B5%D0%BD%D0%B0&amp;diff=18933"/>
		<updated>2016-11-22T11:38:27Z</updated>

		<summary type="html">&lt;p&gt;Teplov: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Кластеризация сетями Кохонена&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(mnT)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;mk&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;mn&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O(T*log(max(m,n)))&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Авторы страницы &amp;lt;b&amp;gt;[[Участник:Emaksimov|Егор Максимов]] (1.1, 1.2, 1.4, 1.5, 1.6, 1.7, 1.9, 2.4, 1.10)&amp;lt;/b&amp;gt; и &amp;lt;b&amp;gt;[[Участник:Kravtsovkirill|Кирилл Кравцов]] (1.2, 1.3, 1.6, 1.7, 1.8, 1.9, 1.10, 2.4, 2.7)&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&#039;&#039;&#039;Искусственная нейронная сеть Кохонена&#039;&#039;&#039; или самоорганизующаяся карта признаков (SOM) была предложена финским исследователем Тойво Кохоненом в начале 1980-х годов.&lt;br /&gt;
[[Файл:Teuvokohonen.jpg|thumb|150px|right|Рис. 1. Кохонен, Теуво.]]&lt;br /&gt;
Она представляет собой двухслойную сеть. Каждый нейрон первого (распределительного) слоя соединен со всеми нейронами второго (выходного) слоя, которые расположены в виде двумерной решетки. Для визуализации топологии слоя Кохонена используют либо шестиугольные, либо прямоугольные ячейки, в которых распологаются нейроны. Шестиугольные ячейки часто являются более предпочтительными, так как расстояние от центра выбранной ячейки до ее соседей одинаково.Нейроны выходного слоя называются кластерными элементами, их количество определят максимальное количество групп, на которые система может разделить входные данные. Увеличивая количество нейронов второго слоя можно увеличивать детализацию результатов процесса кластеризации.&lt;br /&gt;
&lt;br /&gt;
Система работает по принципу соревнования – нейроны второго слоя соревнуются друг с другом за право наилучшим образом сочетаться с входным вектором сигналов, побеждает тот элемент-нейрон, чей вектор весов ближе всего к входному вектору сигналов. За меру близости двух векторов можно взять квадрат евклидова расстояния. Таким образом, каждый входной вектор относится к некоторому кластерному элементу.&lt;br /&gt;
&lt;br /&gt;
Для обучения сети Кохонена используется соревновательный метод. На каждом шаге обучения из исходного набора данных случайно выбирается один вектор. Затем производится поиск нейрона выходного слоя, для которого расстояние между его вектором весов и входным вектором - минимально.&lt;br /&gt;
По определённому правилу производится корректировка весов для нейрона-победителя и нейронов из его окрестности, которая задаётся соответствующей функцией окрестности. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
[[Файл:FNaEm.png|thumb|550px|right|Риc. 2. Кластеризация сетями Кохонена]]&lt;br /&gt;
Пусть имеется некоторое множество векторов &amp;lt;math&amp;gt; x^{(i)}=(x_1^{(i)},x_2^{(i)},...,x_m^{(i)}) \in \mathbb{R}^m,  i=1, \dots, k&amp;lt;/math&amp;gt;. Заранее зададим количество кластеров равным &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;. Соответственно, Карта Кохонена представляет собой двухслойную fully-connected нейронную сеть, где первый слой состоит из &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; нейронов, а второй - из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;.&lt;br /&gt;
Каждый j-ый нейрон описывается вектором весов &amp;lt;math&amp;gt; w_j=(w_{1j},w_{2j},...,w_{mj}),  j=1, \dots, n&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Алгоритм обучения сети без учителя==== &lt;br /&gt;
&lt;br /&gt;
Обучение состоит из последовательности коррекций векторов, представляющих собой нейроны. Сначала следует инициализировать векторы весов. Далее определенное количество раз (эпох) производятся следующие действия:&lt;br /&gt;
#Случайным образов выбирается вектор &amp;lt;math&amp;gt;x(t)&amp;lt;/math&amp;gt; из входных данных.&lt;br /&gt;
#Выбирается нейрон-победитель &amp;lt;math&amp;gt;w_c&amp;lt;/math&amp;gt; &amp;quot;наиболее похожий&amp;quot; (по евклидову расстоянию) на вектор &amp;lt;math&amp;gt;x(t)&amp;lt;/math&amp;gt;: &amp;lt;center&amp;gt; &amp;lt;math&amp;gt; ||x(t)-w_c||=\min\limits_{i}(||x(t)-w_i(t)||)&amp;lt;/math&amp;gt;. &amp;lt;/center&amp;gt;&lt;br /&gt;
# Веса всех нейронов обновляются по формуле:&lt;br /&gt;
 &amp;lt;center&amp;gt; &amp;lt;math&amp;gt; w_i(t+1)=w_i(t)+\alpha(t)h_{ic}(t)||x(t)-w_i(t)||&amp;lt;/math&amp;gt;, &amp;lt;/center&amp;gt; &lt;br /&gt;
где &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; - номер эпохи, &amp;lt;math&amp;gt; h_{ic}(t)&amp;lt;/math&amp;gt;- функция соседства нейронов,&lt;br /&gt;
&amp;lt;math&amp;gt;\alpha (t)&amp;lt;/math&amp;gt; - функция скорости обучения сети.&lt;br /&gt;
&lt;br /&gt;
==== Инициализация начальных весов==== &lt;br /&gt;
&lt;br /&gt;
Инициализация случайными значениями, когда всем весам даются малые случайные величины.&lt;br /&gt;
Инициализация примерами, когда в качестве начальных значений задаются значения случайно выбранных примеров из обучающей выборки&lt;br /&gt;
Линейная инициализация. В этом случае веса инициируются значениями векторов, линейно упорядоченных вдоль линейного подпространства, проходящего между двумя главных собственными векторами исходного набора данных. Собственные вектора могут быть найдены например при помощи процедуры Грама-Шмидта.&lt;br /&gt;
&lt;br /&gt;
==== Функция соседства нейронов==== &lt;br /&gt;
&lt;br /&gt;
Функции от расстояния применяются двух видов: &lt;br /&gt;
&lt;br /&gt;
Константа: &lt;br /&gt;
 &amp;lt;math&amp;gt;&lt;br /&gt;
 h(d,t) = \begin{cases}&lt;br /&gt;
   c,  &amp;amp; \mbox{if } n \ d&amp;lt;=\delta \\&lt;br /&gt;
   0, &amp;amp; \mbox{if } n \ d&amp;gt;\delta&lt;br /&gt;
 \end{cases}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Функция Гаусса: &lt;br /&gt;
 &amp;lt;math&amp;gt;h_{ic}(t)=\exp(-\frac{\|r_c-r_i\|^2}{2\sigma^2(t)})&amp;lt;/math&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;r_i&amp;lt;/math&amp;gt; определяет положение &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го нейрона в сетке&lt;br /&gt;
&lt;br /&gt;
==== Функция скорости обучения сети==== &lt;br /&gt;
&lt;br /&gt;
Главное требование к ней - монотонное убывание при возрастании &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; для замедления темпов вариации весов нейронов слоя Кохонена.&lt;br /&gt;
Функция может быть задана, например, как &amp;lt;math&amp;gt;\alpha(t)=\frac{A}{B+t}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;B&amp;lt;/math&amp;gt; - некоторые константы.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро алгоритма состоит из двух шагов, повторяющихся каждую итерацию (эпоху):&lt;br /&gt;
*Вычисление расстояния от случайно выбранного вектора &amp;lt;math&amp;gt;x(t)&amp;lt;/math&amp;gt; до каждого нейрона и выбор нейрона с наименьшим расстоянием. Т.е. вычисление &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; евклидовых расстояний и сортировка полученного массива расстояний. Сложность евклидового расстояния растет линейно с увеличением длины вектора. Следовательно получаем сложность данного шага &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt;.&lt;br /&gt;
*Обновление весов всех нейронов. Пусть используется гауссовская функция для вычисления расстояний между нейронами. Сложность обновления веса одного нейрона линейно зависит от &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt;. Т.е. сложность данного шага также &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В итоге получаем, что для всего алгоритма с количеством эпох равным &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; сложность составляет &amp;lt;math&amp;gt;O(Tmn)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
Алгоритм кластеризации можно разбить на следующие этапы:&lt;br /&gt;
*Инициализация &amp;lt;math&amp;gt;\mathbf{W} \leftarrow ComputeInitialWeights(\mathbf{W})&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Цикл, повторяющийся &amp;lt;math&amp;gt;T_{max}&amp;lt;/math&amp;gt; (максимальное число &amp;quot;эпох&amp;quot;) раз:&lt;br /&gt;
# &amp;lt;math&amp;gt;\mathbf{X} \leftarrow ChooseRandomVector(\mathbf{V})&amp;lt;/math&amp;gt;&lt;br /&gt;
#Вложенный цикл, в котором осуществляется вычисление расстояний &amp;lt;math&amp;gt;\mathbf{D_i} \leftarrow CalculateEucledeNorm(X, \mathbf{W_i})&amp;lt;/math&amp;gt;&lt;br /&gt;
#Выбор победителя &amp;lt;math&amp;gt;\mathbf{W_c} \leftarrow ChooseWinner(\mathbf{D_i})&amp;lt;/math&amp;gt;&lt;br /&gt;
#Вложенный цикл, в котором осуществляется корректировка весов &amp;lt;math&amp;gt;\mathbf{W_i(t+1)}\leftarrow CorrectWeights(\mathbf{W_i(t)})&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;\mathbf{W}&amp;lt;/math&amp;gt; - матрица весов, &amp;lt;math&amp;gt;T_{max}&amp;lt;/math&amp;gt; - максимальное число &amp;quot;эпох&amp;quot;, &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; - мощность входного множества, &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; - число нейронов выходного слоя. Тогда схема реализации последовательного алгоритма будет выглядеть следующим образом:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;math&amp;gt;\mathbf{W} := InitializeWeights()&amp;lt;/math&amp;gt;&lt;br /&gt;
 &amp;lt;math&amp;gt;\mathbf{FOR} \ t:=1 \ \mathbf{TO} \ T_{max} \ \mathbf{DO}&amp;lt;/math&amp;gt;&lt;br /&gt;
     &amp;lt;math&amp;gt; \quad \mathbf{X} := ChooseRandomVector\left( \{1, \dots, k\} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
     &amp;lt;math&amp;gt;\mathbf{FOR} \ i:=1 \ \mathbf{TO} \ N \ \mathbf{DO}&amp;lt;/math&amp;gt;&lt;br /&gt;
         &amp;lt;math&amp;gt;\mathbf{D} := CalculateEucledeNorm(\mathbf{X}, \mathbf{W_i})&amp;lt;/math&amp;gt;&lt;br /&gt;
     &amp;lt;math&amp;gt;\mathbf{W_c} := ChooseWinner(\mathbf{D})&amp;lt;/math&amp;gt;&lt;br /&gt;
     &amp;lt;math&amp;gt;\mathbf{FOR} \ i:=1 \ \mathbf{TO} \ N \ \mathbf{DO} \ \mathbf{AND} \ \mathbf{i} \neq \mathbf{c}&amp;lt;/math&amp;gt;&lt;br /&gt;
         &amp;lt;math&amp;gt;\mathbf{W_i(t+1)} := CorrectWeights(\mathbf{W_i(t)}, \mathbf{W_c})&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Рассмотрим вычислительную сложность описанного алгоритма. Основная часть операций алгоритма приходится на вложенные циклы, где &lt;br /&gt;
*выполняется вычисление &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; евклидовых норм, (функция &amp;lt;math&amp;gt;CalculateEucledeNorm&amp;lt;/math&amp;gt;), на вычисление каждой из которых требуется по &amp;lt;math&amp;gt;2m-1&amp;lt;/math&amp;gt; операций сложения, &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; операций умножения и один квадратный корень. (&amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - размерность входных векторов) для вычисления всех норм&lt;br /&gt;
*количество операций на корректировку весов одного нейрона можно оценить как &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Итак, как уже было сказано, общая последовательная сложность алгоритма составляет &amp;lt;math&amp;gt;O(Tmn)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; - количество эпох обучения (итераций), &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - размерность входных векторов (иначе говоря, количество нейронов во входном слое сети Кохонена), &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; - количество кластеров (иначе говоря, количество нейронов во втором слое сети - слое Кохонена).&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
Общая схема работы алгоритма выглядит следующим образом:&lt;br /&gt;
[[Файл:Graph2.png|thumb|400px|center| Рис. 3. Общая схема работы алгоритма кластеризации]]&lt;br /&gt;
Здесь In - входные данные, Init - шаг, на котором происходит инициализация весов, Norm - вычисление расстояний, Win - выбор победителя, Correct - корректировка весов, Out - выходные данные.&lt;br /&gt;
&lt;br /&gt;
Опишем информационный граф алгоритма.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Первый этап&#039;&#039;&#039; состоит из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; параллельных операций вычисления евклидового расстояния до случайно выбранного входного вектора. &lt;br /&gt;
&lt;br /&gt;
На выходе из каждой ветки получаем значение расстояние соответствующего нейрона. &lt;br /&gt;
Далее находим минимальное расстояние, тем самым определим нейрона победителя.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Graph3.png|thumb|600px|center| Рис. 4. Первый этап алгоритма]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Второй этап&#039;&#039;&#039; состоит их &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; параллельных операций коррекций весов.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Graph4.png|thumb|600px|center| Рис. 5. Второй этап алгоритма]]&lt;br /&gt;
&lt;br /&gt;
На выходе, собрав из каждой ветки откорректированный вектор весов, получаем обновленную матрицу весов, которая используется на следующей эпохе.&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Для алгоритма кластеризации сетями Кохонена средних имеет место массовый параллелизм: в основе каждого из основных этапов алгоритма (поиска евклидовых расстояний для определения нейрона-победителя и обновления весов) лежат информационно независимые действия.&lt;br /&gt;
Учитывая данный факт можно оценить параллельную сложность алгоритма в предположении доступности неограниченного числа необходимых процессоров.&lt;br /&gt;
&lt;br /&gt;
Итоговая сложность параллельного алгоритма будет определяться, исходя из формулы &amp;lt;math&amp;gt;Complexity = T * (PComplexity_{Norm} + PComplexity_{Winner} + PComplexity_{Correct})&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;PComplexity_{Norm}&amp;lt;/math&amp;gt; - параллельная сложность поиска евклидовых расстояний для определения нейрона-победителя, &amp;lt;math&amp;gt;PComplexity_{Correct}&amp;lt;/math&amp;gt; - параллельная сложность обновления весов нейронов, &amp;lt;math&amp;gt;PComplexity_{Winner}&amp;lt;/math&amp;gt; - сложность глобального поиска минимального значения расстояния, &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; - количество эпох (итераций) алгоритма.&lt;br /&gt;
&lt;br /&gt;
Этап Norm заключается в параллельном вычислении евклидовых норм. Сложность этого этапа можно оценить с учетом доступности неограниченного числа процессоров как &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - длина векторов. &lt;br /&gt;
Этап Winner заключается в поиске минимума по подсчитанным расстояниям для определения нейрона-победителя. Параллельная сложность реализации операции нахождения минимума из &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; элементов определяется как &amp;lt;math&amp;gt;O(\log n)&amp;lt;/math&amp;gt;. &lt;br /&gt;
Этап Correct состоит из нескольких операций, но асимптотически сложность определяет именно подсчет евклидова расстояния. Аналогично получаем, что сложность данного этапа составляет &amp;lt;math&amp;gt;O(\log m)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Итоговую параллельную сложность кластеризации сетями Кохонена можно оценить как &amp;lt;math&amp;gt;O(T * \log max(m,n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&#039;&#039;&#039;Входные данные:&#039;&#039;&#039; множество входных векторов (нейронов первого уровня), множество кластеров (выходов, нейронов второго уровня), максимальное число &amp;quot;эпох&amp;quot; &amp;lt;math&amp;gt;T_{max}&amp;lt;/math&amp;gt;, константа &amp;lt;math&amp;gt; \delta &amp;lt;/math&amp;gt;, константы &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;B&amp;lt;/math&amp;gt;, константа &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; (в случае, если выбрана простая константа).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объем входные данных:&#039;&#039;&#039; так как обучение происходит не на всем входном векторе, а только на количестве векторов равном &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;, то алгоритм может быть организован так, чтобы принять на выход только &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; случайных векторов из исходной выборки. И, соответственно, объем входных данных можно считать равным &amp;lt;math&amp;gt;m*T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные:&#039;&#039;&#039; матрица весов.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объем выходных данных:&#039;&#039;&#039; равен &amp;lt;math&amp;gt;m*k&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&amp;lt;p&amp;gt;&amp;lt;b&amp;gt;[[глоссарий#Вычислительная мощность|&#039;&#039;Вычислительная мощность&#039;&#039;]]&amp;lt;/b&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
Вычислительная мощность алгоритма кластеризации сетями Кохонена равна &amp;lt;math&amp;gt;\frac{m*n*T}{m*k+m*n} = \frac{n*T}{k+n}&amp;lt;/math&amp;gt;, где, напомним, &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; - число итераций, &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; - число нейронов выходного слоя, &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; - мощность входного множества.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p&amp;gt;&amp;lt;b&amp;gt;[[глоссарий#Детерминированность |&#039;&#039;Детерминированность&#039;&#039;]] и [[глоссарий#Устойчивость |&#039;&#039;Устойчивость&#039;&#039;]] &amp;lt;/b&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
Описанный алгоритм кластеризации случайно выбирает элемент входного множества, следовательно, не является детерминированным. Как и все простые сети, построенный алгоритм является устойчивым.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p&amp;gt;&amp;lt;b&amp;gt;&#039;&#039;Сильные и слабые стороны алгоритма&#039;&#039;&amp;lt;/b&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Устойчивость к зашумленным данным, быстрое и неуправляемое обучение, возможность упрощения многомерных входных данных с помощью визуализации.&lt;br /&gt;
Главное преимущество самоорганизующихся карт Кохонена является их способность &amp;quot;проецирования&amp;quot; многомерных пространств на двумерное. Важной особенностью карт Кохонена является то, что объекты с похожими характеристиками (по всем компонентам) располагаются рядом, образуя кластеры.&lt;br /&gt;
&lt;br /&gt;
Визуализация данных в виде двумерной карты позволяет проводить кластеризацию данных и значительно упрощает корреляционный анализ.&lt;br /&gt;
Важным недостатком является то, что окончательный результат работы нейронных сетей зависит от начальных установок сети. С другой стороны, нейронные сети теоретически могут аппроксимировать любую непрерывную функцию, что позволяет исследователю не принимать заранее какие-либо гипотезы относительно модели.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма==&lt;br /&gt;
===Особенности реализации последовательного алгоритма===&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
==== Локальность реализации алгоритма ====&lt;br /&gt;
===== Структура обращений в память и качественная оценка локальности =====&lt;br /&gt;
===== Количественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
==== Масштабируемость алгоритма ====&lt;br /&gt;
&lt;br /&gt;
Как было описано в [[1.8 | п. 1.8]], для алгоритма кластеризации имеет место массовый параллелизм, поскольку основные этапы алгоритма (поиск евклидовых расстояний для определения нейрона-победителя и обновление весов) информационно независимы, что означает хорошую масштабируемость алгоритма.&lt;br /&gt;
&lt;br /&gt;
==== Масштабируемость реализации алгоритма ====&lt;br /&gt;
&lt;br /&gt;
Ниже представлен график зависимости времени выполнения от числа потоков и размера сети при OpenMP реализации. &lt;br /&gt;
[[Файл:Openmp1.PNG|thumb|600px|center| График зависимости времени выполнения от числа потоков и размера сети]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Гистограмма ускорения выполнения алгоритма с различными размерами карт. Хорошо видно, что наибольший эффект достигается на 6-8 потоках.&lt;br /&gt;
[[Файл:Openmp2.PNG|thumb|600px|center| Гистограмма ускорения]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Как можно увидеть, на участках от 1 ядра (потока) до 8 программа хорошо масштабируема, но при увеличении количества ядер (потоков) до 9 и более происходит увеличение времени выполнения и [[Ускорение| ускорение]] программы уменьшается вследствие запроса на слишком большое количество ресурсов при относительно небольшом объёме входных данных.&lt;br /&gt;
&lt;br /&gt;
Была использована следующая реализация алгоритма.&lt;br /&gt;
&lt;br /&gt;
    #include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
    #include &amp;lt;iostream&amp;gt;&lt;br /&gt;
    #include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
    #include &amp;lt;math.h&amp;gt;&lt;br /&gt;
    #include &amp;lt;string.h&amp;gt;&lt;br /&gt;
    #include &amp;lt;time.h&amp;gt;&lt;br /&gt;
    using namespace std;&lt;br /&gt;
    double **sample;&lt;br /&gt;
    double **W;&lt;br /&gt;
    int inputSize=3;&lt;br /&gt;
    int numNeurons = 10;&lt;br /&gt;
    int iterNum = 2000;&lt;br /&gt;
    double learning_rate = 1;&lt;br /&gt;
    double radius = 0.2;&lt;br /&gt;
    double eucledeDist(double vect1[],double vect2[]) {&lt;br /&gt;
        double sum = 0;&lt;br /&gt;
        for (int i = 0; i &amp;lt;inputSize; ++i){&lt;br /&gt;
            sum += (vect1[i]-vect2[i])*(vect1[i]-vect2[i]);&lt;br /&gt;
        }&lt;br /&gt;
        return sqrt(sum);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    double ** getRandomArray(int rows, int cols, int randomize, int init=0){&lt;br /&gt;
        double ** matr = new double* [rows];&lt;br /&gt;
        for (int i = 0; i &amp;lt; rows; ++i){&lt;br /&gt;
            matr[i] = new double[cols]();&lt;br /&gt;
        }&lt;br /&gt;
        if (randomize==1){&lt;br /&gt;
            for (int i = 0; i &amp;lt; rows; ++i){&lt;br /&gt;
                for (int j = 0; j &amp;lt; cols; ++j){&lt;br /&gt;
                    matr[i][j] = (10*rand()-8*rand())/32767.0;&lt;br /&gt;
                    if (init){matr[i][j] /= 40;}&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return matr;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    int main()&lt;br /&gt;
    {&lt;br /&gt;
        double **sample = getRandomArray(iterNum,inputSize,1,0);&lt;br /&gt;
        double **W = getRandomArray(numNeurons,inputSize,1,1);&lt;br /&gt;
&lt;br /&gt;
        //for each epoch&lt;br /&gt;
        for (int iter = 0; iter &amp;lt; iterNum; ++iter){&lt;br /&gt;
&lt;br /&gt;
            //choose winner&lt;br /&gt;
            int winner_num = 0;&lt;br /&gt;
            double winnerDist = eucledeDist(sample[iter],W[0]);&lt;br /&gt;
            double dist = 0;&lt;br /&gt;
            for (int i = 1; i &amp;lt; numNeurons; ++i){&lt;br /&gt;
                dist = eucledeDist(sample[iter],W[i]);&lt;br /&gt;
                if ( dist &amp;lt; winnerDist) {&lt;br /&gt;
                    winner_num=i;&lt;br /&gt;
                    winnerDist=dist;&lt;br /&gt;
                };&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            // correct weights&lt;br /&gt;
            double *winner = W[winner_num];&lt;br /&gt;
            for (int i = 0; i &amp;lt; numNeurons; ++i){&lt;br /&gt;
                dist = eucledeDist(winner,W[i]);&lt;br /&gt;
                if (dist&amp;lt;radius &amp;amp;&amp;amp; i!=winner_num) {&lt;br /&gt;
                    for (int j = 0; j &amp;lt; inputSize; ++j){&lt;br /&gt;
                        W[i][j] += learning_rate/sqrt(iter+1) * (winner[j]-W[i][j]);&lt;br /&gt;
                    }&lt;br /&gt;
&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return 0;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Последовательные реализации:&lt;br /&gt;
&lt;br /&gt;
* [https://cran.r-project.org/web/packages/kohonen/ kohonen] Бибилиотека для R&lt;br /&gt;
* [https://pypi.python.org/pypi/kohonen kohonen] Бибилиотека для Python&lt;br /&gt;
* [http://knnl.sourceforge.net/ KNNL]: C++ бибилиотека.&lt;br /&gt;
* [https://www.mathworks.com/help/nnet/ug/cluster-with-self-organizing-map-neural-network.html?requestedDomain=www.mathworks.com Matlab realization]: Реализация в Matlab.&lt;br /&gt;
* [http://jknnl.sourceforge.net/ JKNNL]: Библиотека для Java.&lt;br /&gt;
* [https://support.sas.com/documentation/onlinedoc/miner/casestudy_59123.pdf SAS EM]: Реализация в SAS Enterprise Miner&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Параллельные реализации:&lt;br /&gt;
&lt;br /&gt;
* [https://github.com/peterwittek/somoclu Somoclu]: Библиотека для Python, R, Julia, и Matlab с поддержкой OpenMP, MPI и CUDA.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
 &lt;br /&gt;
# Teuvo Kohonen, Self-Organizing Maps/Helsinki University of Technology,1997/Second Edition.&lt;br /&gt;
# Нейский И.М. Классификация и сравнение методов кластеризации http://it-claim.ru/Persons/Neyskiy/Article2_Neiskiy.pdf&lt;/div&gt;</summary>
		<author><name>Teplov</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9E%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D1%83%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA%D0%B0:Kravtsovkirill&amp;diff=18932</id>
		<title>Обсуждение участника:Kravtsovkirill</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9E%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D1%83%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA%D0%B0:Kravtsovkirill&amp;diff=18932"/>
		<updated>2016-11-22T11:37:57Z</updated>

		<summary type="html">&lt;p&gt;Teplov: /* Статья Кластеризация сетями Кохонена */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Статья [[Кластеризация сетями Кохонена]] ==&lt;br /&gt;
&lt;br /&gt;
=== Замечания ===&lt;br /&gt;
* Формулы в статье лучше выделить в отдельные блоки, примерно так, как это сделано с кодом [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 00:01, 23 октября 2016 (MSK)&lt;br /&gt;
* В п. 1.2 подпункты лучше внести в общую нумерованную иерархию.  [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 00:01, 23 октября 2016 (MSK)&lt;br /&gt;
* В п. 1.9 должны быть оценки объемов входных и выходных данных [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 00:07, 23 октября 2016 (MSK)&lt;br /&gt;
* Необходимо указать вклад каждого автора [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 00:38, 23 октября 2016 (MSK)&lt;br /&gt;
* Код необходимо вносить в соответствующие теги source ... [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 14:37, 22 ноября 2016 (MSK)&lt;/div&gt;</summary>
		<author><name>Teplov</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9E%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D1%83%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA%D0%B0:Kravtsovkirill&amp;diff=18931</id>
		<title>Обсуждение участника:Kravtsovkirill</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9E%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D1%83%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA%D0%B0:Kravtsovkirill&amp;diff=18931"/>
		<updated>2016-11-22T11:37:37Z</updated>

		<summary type="html">&lt;p&gt;Teplov: /* Замечания */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Статья [[Кластеризация сетями Кохонена]] ==&lt;br /&gt;
&lt;br /&gt;
=== Замечания ===&lt;br /&gt;
* Формулы в статье лучше выделить в отдельные блоки, примерно так, как это сделано с кодом [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 00:01, 23 октября 2016 (MSK)&lt;br /&gt;
* В п. 1.2 подпункты лучше внести в общую нумерованную иерархию.  [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 00:01, 23 октября 2016 (MSK)&lt;br /&gt;
* В п. 1.9 должны быть оценки объемов входных и выходных данных [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 00:07, 23 октября 2016 (MSK)&lt;br /&gt;
* Необходимо указать вклад каждого автора [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 00:38, 23 октября 2016 (MSK)&lt;br /&gt;
* Код необходимо вносить в соответствующие теги &amp;lt;source ....&amp;gt; ~~~~&lt;/div&gt;</summary>
		<author><name>Teplov</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9E%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D1%83%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA%D0%B0:%D0%9B%D0%BE%D0%B3%D0%B2%D0%B8%D0%BD%D0%B5%D0%BD%D0%BA%D0%BE_%D0%90%D0%BB%D0%B5%D0%BA%D1%81%D0%B0%D0%BD%D0%B4%D1%80%D0%B0&amp;diff=18930</id>
		<title>Обсуждение участника:Логвиненко Александра</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9E%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D1%83%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA%D0%B0:%D0%9B%D0%BE%D0%B3%D0%B2%D0%B8%D0%BD%D0%B5%D0%BD%D0%BA%D0%BE_%D0%90%D0%BB%D0%B5%D0%BA%D1%81%D0%B0%D0%BD%D0%B4%D1%80%D0%B0&amp;diff=18930"/>
		<updated>2016-11-22T11:34:49Z</updated>

		<summary type="html">&lt;p&gt;Teplov: /* Не полная структура статьи */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Статья [[Алгоритм кластеризации, основанный на сетях Кохоннена]] ==&lt;br /&gt;
&lt;br /&gt;
=== Не полная структура статьи ===&lt;br /&gt;
* В статье отсутствуют пункты с 2.2 по 2.7 [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 17:37, 22 октября 2016 (MSK)&lt;br /&gt;
** Исправлено [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 20:57, 28 октября 2016 (MSK)&lt;br /&gt;
* Пункт 2.1 имеет неправильное название и должен быть пунктом 2.7 [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 17:37, 22 октября 2016 (MSK)&lt;br /&gt;
** Исправлено [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 20:57, 28 октября 2016 (MSK)&lt;br /&gt;
* Отсутсвует кратакая сводка свойств алгоритма [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 02:10, 23 октября 2016 (MSK)&lt;br /&gt;
* Отсутствуют данные по вычислительным экспериментам [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 14:34, 22 ноября 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
=== Замечания по оформлению ===&lt;br /&gt;
* В п. 1.3 формулы оформлены по разному. [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 17:37, 22 октября 2016 (MSK)&lt;br /&gt;
** Исправлено [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 21:02, 28 октября 2016 (MSK)&lt;br /&gt;
* В п. 1.7 отсутствуют данные о том, для каких параметров построен граф [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 17:37, 22 октября 2016 (MSK)&lt;br /&gt;
* В п. 1.8 должна быть оценка параллельной сложности алгоритма [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 17:37, 22 октября 2016 (MSK)&lt;br /&gt;
* В п. 1.10 должна присутствовать вычислительная мощность алгоритма [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 17:37, 22 октября 2016 (MSK)&lt;br /&gt;
* Часть рисунков не подписана и отсутствует их нумерация. [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 17:42, 22 октября 2016 (MSK)&lt;br /&gt;
** исправлено [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 21:02, 28 октября 2016 (MSK)&lt;br /&gt;
* Необходимо указать вклад каждого автора [[Участник:Teplov|Teplov]] ([[Обсуждение участника:Teplov|обсуждение]]) 00:41, 23 октября 2016 (MSK)&lt;/div&gt;</summary>
		<author><name>Teplov</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A4%D0%BE%D1%80%D0%B4%D0%B0-%D0%A4%D0%B0%D0%BB%D0%BA%D0%B5%D1%80%D1%81%D0%BE%D0%BD%D0%B0&amp;diff=18415</id>
		<title>Алгоритм Форда-Фалкерсона</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A4%D0%BE%D1%80%D0%B4%D0%B0-%D0%A4%D0%B0%D0%BB%D0%BA%D0%B5%D1%80%D1%81%D0%BE%D0%BD%D0%B0&amp;diff=18415"/>
		<updated>2016-11-18T21:52:15Z</updated>

		<summary type="html">&lt;p&gt;Teplov: /* Информационный граф */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм Форда-Фалкерсона&#039;&#039;&#039;&amp;lt;ref&amp;gt;Ford, L R, Jr., and D R Fulkerson. “Maximal Flow Through a Network.” Canadian Journal of Mathematics 8 (1956): 399–404. doi:10.4153/CJM-1956-045-5.&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;Ford, L R, Jr., and D R Fulkerson. “A Simple Algorithm for Finding Maximal Network Flows and an Application to the Hitchcock Problem.” Canadian Journal of Mathematics 9 (1957): 210–18.&amp;lt;/ref&amp;gt; (с последующими усовершенствованиями &#039;&#039;&#039;Эдмондса-Карпа&#039;&#039;&#039;&amp;lt;ref name=EdmondsKarp&amp;gt;Edmonds, Jack, and Richard M Karp. “Theoretical Improvements in Algorithmic Efficiency for Network Flow Problems.” Journal of the ACM 19, no. 2 (April 1972): 248–64. doi:10.1145/321694.321699.&amp;lt;/ref&amp;gt; и &#039;&#039;&#039;Е.&amp;amp;nbsp;А.&amp;amp;nbsp;Диница&#039;&#039;&#039;&amp;lt;ref&amp;gt;Диниц, Е. А. “Алгоритм решения задачи о максимальном потоке в сети со степенной оценкой.” Доклады АН СССР 194, no. 4 (1970): 754–57.&amp;lt;/ref&amp;gt;) предназначен для решения задачи о [[Поиск максимального потока в транспортной сети|максимальном потоке в транспортной сети]]. Время работы алгоритма &amp;lt;math&amp;gt;O(n^2m)&amp;lt;/math&amp;gt; (для алгоритма Диница). В случае целых пропускных способностей, не превосходящих &amp;lt;math&amp;gt;K&amp;lt;/math&amp;gt;, сложность &amp;lt;math&amp;gt;O(Km)&amp;lt;/math&amp;gt; (для алгоритма Эдмондса–Карпа).&lt;br /&gt;
&lt;br /&gt;
Алгоритм последовательно улучшает допустимый поток, находя так называемый &#039;&#039;дополняющий путь&#039;&#039; и увеличивая поток вдоль этого пути. Варианты алгоритма отличаются способом нахождения дополняющего пути.&lt;br /&gt;
&lt;br /&gt;
* В исходном алгоритме Форда–Фалкерсона способ выбора дополняющего пути не уточнялся.&lt;br /&gt;
* В алгоритме Эдмондса-Карпа выбирается кратчайший дополняющий путь, для чего используется [[Поиск в ширину (BFS)|поиск в ширину]] на каждой итерации;&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;f&amp;lt;/math&amp;gt;. &#039;&#039;Дополняющим путём&#039;&#039; называется последовательность рёбер &amp;lt;math&amp;gt;e_0 = (s, v_1), e_1 = (v_1, v_2), \ldots, e_k = (v_k, t)&amp;lt;/math&amp;gt;, каждое из которых обладает положительной &#039;&#039;остаточной пропускной способностью&#039;&#039; &amp;lt;math&amp;gt;c_f(e_i) = c(e_i) - f(e_i) &amp;gt; 0&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Поток останется допустимым, если увеличить его вдоль дополняющего пути на число &amp;lt;math&amp;gt;\delta = \min \{ c(e_i) - f(e_i) \} &amp;gt; 0&amp;lt;/math&amp;gt;, при этом величина потока возрастёт на то же число &amp;lt;math&amp;gt;\delta&amp;lt;/math&amp;gt;. Для сохранения антисимметричности увеличение потока производится присваиваниями&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
        f(e_i) \leftarrow f(e_i) + \delta, \quad f(e_i^R) \leftarrow f(e_i^R) - \delta, \quad i = \overline{0, k}.&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
Если дополняющего пути не существует, то поток &amp;lt;math&amp;gt;f&amp;lt;/math&amp;gt; является максимальным.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Вычислительными ядрами, на которые приходится наибольший объём вычислений, являются:&lt;br /&gt;
* [[Поиск в ширину (BFS)|поиск в ширину]];&lt;br /&gt;
* увеличение потока вдоль дополняющего пути.&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В статье Эдмондса–Карпа&amp;lt;ref name=EdmondsKarp /&amp;gt; (теорема 1) доказано, что количество последовательно построенных кратчайших дополнительных путей не превосходит &amp;lt;math&amp;gt;m(n + 1)/2 = O(mn)&amp;lt;/math&amp;gt;. Основными операциями алгоритма являются поиск в ширину сложностью &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt; и обновление потока вдоль дополняющего пути сложностью &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В &#039;&#039;&#039;алгоритме Эдмондса–Карпа&#039;&#039;&#039; выполняются следующие операции:&lt;br /&gt;
* поиск в ширину и определение кратчайшего пути на каждой итерации, сложность &amp;lt;math&amp;gt;O(m + n)&amp;lt;/math&amp;gt;, общая сложность &amp;lt;math&amp;gt;O(m^2n)&amp;lt;/math&amp;gt;;&lt;br /&gt;
* обновление потока вдоль дополняющего пути, сложность &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, общая сложность &amp;lt;math&amp;gt;O(n^2 m)&amp;lt;/math&amp;gt;.&lt;br /&gt;
Таким образом, общая сложность составляет &amp;lt;math&amp;gt;O(m^2n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В &#039;&#039;&#039;алгоритме Диница&#039;&#039;&#039; выполняются следующие операции:&lt;br /&gt;
* поиск кратчайшего пути и обновление потока вдоль дополняющего пути на каждой итерации, сложность &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, общая сложность &amp;lt;math&amp;gt;O(mn^2)&amp;lt;/math&amp;gt;;&lt;br /&gt;
* обновление расслоения на каждой итерации, сложность &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;, общая сложность &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt;;&lt;br /&gt;
* поиск в ширину для построения нового расслоения, сложность &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt;, число построений не более &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;, общая сложность &amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt;.&lt;br /&gt;
Таким образом, общая сложность составляет &amp;lt;math&amp;gt;O(mn^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
Далее представлен информационный граф алгоритма, демонстрирующий описанные уровни параллелизма. Информационный граф алгоритма — ориентированный граф, состоящий из вершин, соответствующих операциям алгоритма, и направленных дуг, соответствующих передаче данных (результаты одних операций передаются в качестве аргументов другим операциям) между ними. &lt;br /&gt;
&lt;br /&gt;
[[file:Max flow.png|thumb|center|1000px|Рисунок 1 – Информационный граф алгоритма Форда-Фалкерсона]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
Основной объём вычислений в алгоритме Форда-Фалкерсона приходится на поиск путей от источника к стоку. С этой целью может применяться [[Поиск в ширину (BFS)|поиск в ширину]], который хорошо распараллеливается. Наилучших результатов можно достичь, если распределить вершины между узлами по слоям примерно одинаковой толщины, так что в каждом слое вершины были бы примерно на одинаковом удалении от источника (такое расслоение также можно найти поиском в ширину).&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
==== Локальность реализации алгоритма ====&lt;br /&gt;
===== Структура обращений в память и качественная оценка локальности =====&lt;br /&gt;
===== Количественная оценка локальности =====&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
==== Масштабируемость алгоритма ====&lt;br /&gt;
==== Масштабируемость реализации алгоритма ====&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
* C++: [http://www.boost.org/libs/graph/doc/ Boost Graph Library] (функция &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph/doc/edmonds_karp_max_flow.html edmonds_karp_max_flow]&amp;lt;/code&amp;gt;): алгоритм Эдмондса–Карпа, сложность &amp;lt;math&amp;gt;O(nm^2)&amp;lt;/math&amp;gt; для действительных весов и &amp;lt;math&amp;gt;O(Kmn)&amp;lt;/math&amp;gt; для целых, не превосходящих &amp;lt;math&amp;gt;K&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Python: [https://networkx.github.io NetworkX] (функция &amp;lt;code&amp;gt;[http://networkx.github.io/documentation/networkx-1.9.1/reference/generated/networkx.algorithms.flow.edmonds_karp.html edmonds_karp]&amp;lt;/code&amp;gt;): алгоритм Эдмондса–Карпа, сложность &amp;lt;math&amp;gt;O(nm^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Java: [http://jgrapht.org JGraphT] (класс &amp;lt;code&amp;gt;[http://jgrapht.org/javadoc/org/jgrapht/alg/EdmondsKarpMaximumFlow.html EdmondsKarpMaximumFlow]&amp;lt;/code&amp;gt;), алгоритм Эдмондса–Карпа, сложность &amp;lt;math&amp;gt;O(nm^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Начатые статьи]]&lt;/div&gt;</summary>
		<author><name>Teplov</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9F%D1%83%D1%80%D0%B4%D0%BE%D0%BC%D0%B0&amp;diff=18371</id>
		<title>Алгоритм Пурдома</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9F%D1%83%D1%80%D0%B4%D0%BE%D0%BC%D0%B0&amp;diff=18371"/>
		<updated>2016-11-18T21:02:51Z</updated>

		<summary type="html">&lt;p&gt;Teplov: /* Входные и выходные данные алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм Пурдома&#039;&#039;&#039;&amp;lt;ref name=Purdom&amp;gt;Purdom, Paul, Jr. “A Transitive Closure Algorithm.” Bit 10, no. 1 (March 1970): 76–94. doi:10.1007/BF01940892.&amp;lt;/ref&amp;gt; находит [[Поиск транзитивного замыкания орграфа|транзитивное замыкание ориентированного графа]] за время &amp;lt;math&amp;gt;O(m + \mu n)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\mu \le m&amp;lt;/math&amp;gt; – число [[Связность в графах|компонент сильной связности]] этого графа.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм основан на следующих свойствах:&lt;br /&gt;
# Если вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;w&amp;lt;/math&amp;gt; принадлежат одной [[Связность в графах|компоненте сильной связности]] графа &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;, то его транзитивное замыкание &amp;lt;math&amp;gt;G^+&amp;lt;/math&amp;gt; содержит рёбра &amp;lt;math&amp;gt;(v, w)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(w, v)&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Если вершины &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;y&amp;lt;/math&amp;gt; принадлежат одной компоненте сильной связности графа &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;, а вершины &amp;lt;math&amp;gt;z&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; – другой, то рёбра &amp;lt;math&amp;gt;(x, z)&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;(x, t)&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;(y, z)&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;(y, t)&amp;lt;/math&amp;gt; принадлежат или не принадлежат транзитивному замыканию &amp;lt;math&amp;gt;G^+&amp;lt;/math&amp;gt; одновременно.&lt;br /&gt;
&lt;br /&gt;
Таким образом, поиск транзитивного замыкания графа &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt; сводится к поиску транзитивного замыкания ациклического графа &amp;lt;math&amp;gt;\tilde G&amp;lt;/math&amp;gt;, полученного из &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt; схлопыванием каждой компоненты сильной связности в одну вершину. Транзитивное замыкание ациклического графа вычисляется с использованием топологической сортировки вершин графа.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Вычисления производятся в четыре этапа:&lt;br /&gt;
&lt;br /&gt;
# Найти [[Связность в графах|компоненты сильной связности]] исходного графа, заменить каждую компоненту на одну вершину и удалить образовавшиеся рёбра-петли.&lt;br /&gt;
# Выполнить топологическую сортировку полученного ациклического графа &amp;lt;math&amp;gt;\tilde G&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Вычислить транзитивное замыкание графа &amp;lt;math&amp;gt;\tilde G&amp;lt;/math&amp;gt;, двигаясь от вершин с бо́льшими номерами к меньшим.&lt;br /&gt;
# По найденному транзитивному замыканию &amp;lt;math&amp;gt;\tilde G&amp;lt;/math&amp;gt; восстановить транзитивное замыкание исходного графа.&lt;br /&gt;
&lt;br /&gt;
Последний этап не является обязательным, если рассматривать транзитивное замыкание &amp;lt;math&amp;gt;\tilde G&amp;lt;/math&amp;gt; как «упакованное» транзитивное замыкание &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;1 этап&#039;&#039;&#039; (&#039;&#039;вычисление компонент сильной связности&#039;&#039;) может быть реализован [[Алгоритм Тарьяна поиска компонент сильной связности|алгоритмом Тарьяна]]&amp;lt;ref name=DFS&amp;gt;Tarjan, Robert. “Depth-First Search and Linear Graph Algorithms.” SIAM Journal on Computing 1, no. 2 (1972): 146–60.&amp;lt;/ref&amp;gt;, находящим компоненты сильной связности в ходе [[Поиск в глубину (DFS)|поиска в глубину]].&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;2 этап&#039;&#039;&#039; (&#039;&#039;топологическая сортировка&#039;&#039;) может быть реализован алгоритмом Кана&amp;lt;ref&amp;gt;Kahn, A B. “Topological Sorting of Large Networks.” Communications of the ACM 5, no. 11 (November 1962): 558–62. doi:10.1145/368996.369025.&amp;lt;/ref&amp;gt; либо последовательным применением [[Поиск в глубину (DFS)|поиска в глубину]]&amp;lt;ref name=DFS /&amp;gt; для нумерации вершин в предпорядке.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;3 этап&#039;&#039;&#039; (&#039;&#039;транзитивное замыкание&#039;&#039; &amp;lt;math&amp;gt;\tilde G&amp;lt;/math&amp;gt;) выполняется следующим алгоритмом:&lt;br /&gt;
&lt;br /&gt;
 &#039;&#039;&#039;Входные данные&#039;&#039;&#039;:&lt;br /&gt;
     ациклический граф &#039;G&#039;&#039; с вершинами &#039;V&#039;&#039;, пронумерованными в топологическом порядке, и рёбрами &#039;&#039;E&#039;&#039;.&lt;br /&gt;
 &#039;&#039;&#039;Выходные данные&#039;&#039;&#039;:&lt;br /&gt;
     для каждой вершины &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039; – набор вершин &#039;&#039;T&#039;&#039;(&#039;&#039;v&#039;&#039;),&lt;br /&gt;
         так что транзитивное замыкание &#039;&#039;G&#039;&#039; состоит из рёбер (&#039;&#039;v&#039;&#039;, &#039;&#039;w&#039;&#039;), &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039;, &#039;&#039;w&#039;&#039; ∈ &#039;&#039;T&#039;&#039;(&#039;&#039;v&#039;&#039;).&lt;br /&gt;
     &lt;br /&gt;
 &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039; &#039;&#039;&#039;do&#039;&#039;&#039; &#039;&#039;T&#039;&#039;(&#039;&#039;v&#039;&#039;) := { &#039;&#039;v&#039;&#039; }&lt;br /&gt;
 &#039;&#039;&#039;for&#039;&#039;&#039; &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039; &#039;&#039;&#039;in reverse order&#039;&#039;&#039;:&lt;br /&gt;
     &#039;&#039;&#039;for each&#039;&#039;&#039; (&#039;&#039;v&#039;&#039;, &#039;&#039;w&#039;&#039;) ∈ &#039;&#039;E&#039;&#039; &#039;&#039;&#039;do&#039;&#039;&#039; &#039;&#039;T&#039;&#039;(&#039;&#039;v&#039;&#039;) := &#039;&#039;T&#039;&#039;(&#039;&#039;v&#039;&#039;) ∪ &#039;&#039;T&#039;&#039;(&#039;&#039;w&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;4 этап&#039;&#039;&#039; (&#039;&#039;транзитивное замыкание&#039;&#039; &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;) либо выполняется следующим алгоритмом:&lt;br /&gt;
&lt;br /&gt;
 &#039;&#039;&#039;Входные данные&#039;&#039;&#039;&lt;br /&gt;
     граф &#039;&#039;G&#039;&#039; с вершинами &#039;&#039;V&#039;&#039;;&lt;br /&gt;
     компоненты сильной связности &#039;&#039;SCC&#039;&#039;(&#039;&#039;v&#039;&#039;) графа &#039;&#039;G&#039;&#039;;&lt;br /&gt;
     граф &amp;lt;math&amp;gt;\tilde G&amp;lt;/math&amp;gt; с вершинами &amp;lt;math&amp;gt;\tilde V&amp;lt;/math&amp;gt;;&lt;br /&gt;
     транзитивное замыкание &amp;lt;math&amp;gt;\tilde T&amp;lt;/math&amp;gt; графа &amp;lt;math&amp;gt;\tilde G&amp;lt;/math&amp;gt;.&lt;br /&gt;
 &#039;&#039;&#039;Выходные данные&#039;&#039;&#039;:&lt;br /&gt;
     для каждой вершины &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039; – набор вершин &#039;&#039;T&#039;&#039;(&#039;&#039;v&#039;&#039;),&lt;br /&gt;
         так что транзитивное замыкание &#039;&#039;G&#039;&#039; состоит из рёбер (&#039;&#039;v&#039;&#039;, &#039;&#039;w&#039;&#039;), &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039;, &#039;&#039;w&#039;&#039; ∈ &#039;&#039;T&#039;&#039;(&#039;&#039;v&#039;&#039;).&lt;br /&gt;
         &lt;br /&gt;
 &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039; &#039;&#039;&#039;do&#039;&#039;&#039; &#039;&#039;T&#039;&#039;(&#039;&#039;v&#039;&#039;) := { &#039;&#039;v&#039;&#039; }&lt;br /&gt;
 &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;v&#039;&#039; ∈ &amp;lt;math&amp;gt;\tilde V&amp;lt;/math&amp;gt;:&lt;br /&gt;
     &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;w&#039;&#039; ∈ &amp;lt;math&amp;gt;\tilde T(v)&amp;lt;/math&amp;gt;:&lt;br /&gt;
         &#039;&#039;T&#039;&#039;(&#039;&#039;v&#039;&#039;) := &#039;&#039;T&#039;&#039;(&#039;&#039;v&#039;&#039;) ∪ &#039;&#039;SCC&#039;&#039;(&#039;&#039;w&#039;&#039;)&lt;br /&gt;
     &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;x&#039;&#039; ∈ &#039;&#039;SCC&#039;&#039;(&#039;&#039;v&#039;&#039;):&lt;br /&gt;
         &#039;&#039;T&#039;&#039;(&#039;&#039;x&#039;&#039;) := &#039;&#039;T&#039;&#039;(&#039;&#039;v&#039;&#039;)    &#039;&#039;/* присваивание по ссылке */&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
либо транзитивное замыкание может строиться по упакованным данным &amp;lt;math&amp;gt;\tilde T(v)&amp;lt;/math&amp;gt; одной из следующих функций:&lt;br /&gt;
&lt;br /&gt;
 &#039;&#039;&#039;Входные данные&#039;&#039;&#039;&lt;br /&gt;
     граф &#039;&#039;G&#039;&#039; с вершинами &#039;&#039;V&#039;&#039;;&lt;br /&gt;
     компоненты сильной связности &#039;&#039;SCC&#039;&#039;(&#039;&#039;v&#039;&#039;) графа &#039;&#039;G&#039;&#039;;&lt;br /&gt;
     отображение &#039;&#039;R&#039;&#039;(&#039;&#039;v&#039;&#039;) вершин графа &#039;&#039;G&#039;&#039; на вершины графа &amp;lt;math&amp;gt;\tilde G&amp;lt;/math&amp;gt;;&lt;br /&gt;
     граф &amp;lt;math&amp;gt;\tilde G&amp;lt;/math&amp;gt; с вершинами &amp;lt;math&amp;gt;\tilde V&amp;lt;/math&amp;gt;;&lt;br /&gt;
     транзитивное замыкание &amp;lt;math&amp;gt;\tilde T&amp;lt;/math&amp;gt; графа &amp;lt;math&amp;gt;\tilde G&amp;lt;/math&amp;gt;.&lt;br /&gt;
     &lt;br /&gt;
 &#039;&#039;&#039;function&#039;&#039;&#039; transitive_closure(&#039;&#039;v&#039;&#039;):&lt;br /&gt;
     &#039;&#039;T&#039;&#039; := { &#039;&#039;v&#039;&#039; }&lt;br /&gt;
     &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;w&#039;&#039; ∈ &amp;lt;math&amp;gt;\tilde T(R(v))&amp;lt;/math&amp;gt;:&lt;br /&gt;
         &#039;&#039;T&#039;&#039; := &#039;&#039;T&#039;&#039; ∪ &#039;&#039;SCC&#039;&#039;(&#039;&#039;w&#039;&#039;)&lt;br /&gt;
     &#039;&#039;&#039;return&#039;&#039;&#039; &#039;&#039;T&#039;&#039;&lt;br /&gt;
     &lt;br /&gt;
 &#039;&#039;&#039;function&#039;&#039;&#039; is_in_transitive_closure(&#039;&#039;v&#039;&#039;, &#039;&#039;w&#039;&#039;):&lt;br /&gt;
     &#039;&#039;&#039;return&#039;&#039;&#039; &#039;&#039;R&#039;&#039;(&#039;&#039;w&#039;&#039;) ∈ &amp;lt;math&amp;gt;\tilde T(R(v))&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Первый и второй этап выполняются с помощью поиска в глубину со сложностью &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
На третьем этапе основой операцией является объединение списков вершин. В случае хранения списков в отсортированном виде объединение выполняется за линейное время. Количество списков не превосходит &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;, а в каждом списке не более &amp;lt;math&amp;gt;\mu&amp;lt;/math&amp;gt; вершин, где &amp;lt;math&amp;gt;\mu&amp;lt;/math&amp;gt; – количество компонент сильной связности. Общая сложность тогда составляет &amp;lt;math&amp;gt;O(\mu^2)&amp;lt;/math&amp;gt;. Альтернативным форматов хранения списка вершин является битовая маска с такой же общей сложностью.&lt;br /&gt;
&lt;br /&gt;
Сложность четвёртого этапа составляет &amp;lt;math&amp;gt;O(\mu n)&amp;lt;/math&amp;gt;, так как для каждой из &amp;lt;math&amp;gt;\mu&amp;lt;/math&amp;gt; компонент сильной связности за линейное время вычисляется список вершин из не более чем &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин. Поскольку компоненты сильной связности не пересекаются, на данном этапе уже не требуется хранить списки в отсортированном виде.&lt;br /&gt;
&lt;br /&gt;
Таким образом, общая сложность составляет &amp;lt;math&amp;gt;O(m + \mu^2)&amp;lt;/math&amp;gt;, если не требуется явное построение транзитивного замыкания, или &amp;lt;math&amp;gt;O(m + \mu n)&amp;lt;/math&amp;gt; в противном случае.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
==== Локальность реализации алгоритма ====&lt;br /&gt;
===== Структура обращений в память и качественная оценка локальности =====&lt;br /&gt;
===== Количественная оценка локальности =====&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
Программа, реализующая поиск транзитного замыкания, состоит CPU части, отвечающей за общую координацию вычислений, использующей вспомогательные классы.&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
==== Масштабируемость алгоритма ====&lt;br /&gt;
==== Масштабируемость реализации алгоритма ====&lt;br /&gt;
&lt;br /&gt;
Для демонстрации эффективности представлены графики времени выполнения различных режимов вычислений в зависимости от размера входного графа. &lt;br /&gt;
&lt;br /&gt;
Основной характеристикой для сравнения было выбрано время выполнения, так как производительность (определения как TEPS то есть число ребер графа, которое алгоритм обрабатывает в секунду) для данной операции не отражает реальной ситуации. &lt;br /&gt;
&lt;br /&gt;
Дело в том, что сложность выбранного алгоритма Пурдома зависит от количества ребер в графе не линейно, и, кроме того, зависит еще и от структуры графа количества сильно связанных компонент). &lt;br /&gt;
&lt;br /&gt;
[[file:Замыкание scaling wide.png|thumb|center|700px|Рисунок 2. Параллельная реализация алгоритма Пурдома масштабируемость различных версий: производительность в зависимости от размера графа.]]&lt;br /&gt;
&lt;br /&gt;
Анализируя время выполнения можно оценить, насколько понижается эффективность обработки графа при увеличении его размера (данные перестают помещаться в кэш, в память GPU, узла и т.д.). На представленных графиках по оси X граф с каждым делением увеличивается в два раза, и по представленному графику можно оценивать, на сколько увеличилось время выполнения.&lt;br /&gt;
Так же время выполнения можно сравнивать для различных версий, например, последовательной CPU, параллельной CPU или же GPU.&lt;br /&gt;
На приведенной диаграмме (рисунок 1) для различных размеров графа (от 2^20 до 2^26) представлено время выполнения трех различных версий: последовательной, параллельной CPU и GPU. Шкала на данной диаграмме логарифмическая.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
* [http://www.boost.org/libs/graph/doc/ Boost Graph Library] (функция &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph/doc/transitive_closure.html transitive_closure]&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Начатые статьи]]&lt;/div&gt;</summary>
		<author><name>Teplov</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9F%D1%83%D1%80%D0%B4%D0%BE%D0%BC%D0%B0&amp;diff=18336</id>
		<title>Алгоритм Пурдома</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9F%D1%83%D1%80%D0%B4%D0%BE%D0%BC%D0%B0&amp;diff=18336"/>
		<updated>2016-11-18T20:52:43Z</updated>

		<summary type="html">&lt;p&gt;Teplov: /* Масштабируемость реализации алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм Пурдома&#039;&#039;&#039;&amp;lt;ref name=Purdom&amp;gt;Purdom, Paul, Jr. “A Transitive Closure Algorithm.” Bit 10, no. 1 (March 1970): 76–94. doi:10.1007/BF01940892.&amp;lt;/ref&amp;gt; находит [[Поиск транзитивного замыкания орграфа|транзитивное замыкание ориентированного графа]] за время &amp;lt;math&amp;gt;O(m + \mu n)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\mu \le m&amp;lt;/math&amp;gt; – число [[Связность в графах|компонент сильной связности]] этого графа.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм основан на следующих свойствах:&lt;br /&gt;
# Если вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;w&amp;lt;/math&amp;gt; принадлежат одной [[Связность в графах|компоненте сильной связности]] графа &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;, то его транзитивное замыкание &amp;lt;math&amp;gt;G^+&amp;lt;/math&amp;gt; содержит рёбра &amp;lt;math&amp;gt;(v, w)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(w, v)&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Если вершины &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;y&amp;lt;/math&amp;gt; принадлежат одной компоненте сильной связности графа &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;, а вершины &amp;lt;math&amp;gt;z&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; – другой, то рёбра &amp;lt;math&amp;gt;(x, z)&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;(x, t)&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;(y, z)&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;(y, t)&amp;lt;/math&amp;gt; принадлежат или не принадлежат транзитивному замыканию &amp;lt;math&amp;gt;G^+&amp;lt;/math&amp;gt; одновременно.&lt;br /&gt;
&lt;br /&gt;
Таким образом, поиск транзитивного замыкания графа &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt; сводится к поиску транзитивного замыкания ациклического графа &amp;lt;math&amp;gt;\tilde G&amp;lt;/math&amp;gt;, полученного из &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt; схлопыванием каждой компоненты сильной связности в одну вершину. Транзитивное замыкание ациклического графа вычисляется с использованием топологической сортировки вершин графа.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Вычисления производятся в четыре этапа:&lt;br /&gt;
&lt;br /&gt;
# Найти [[Связность в графах|компоненты сильной связности]] исходного графа, заменить каждую компоненту на одну вершину и удалить образовавшиеся рёбра-петли.&lt;br /&gt;
# Выполнить топологическую сортировку полученного ациклического графа &amp;lt;math&amp;gt;\tilde G&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Вычислить транзитивное замыкание графа &amp;lt;math&amp;gt;\tilde G&amp;lt;/math&amp;gt;, двигаясь от вершин с бо́льшими номерами к меньшим.&lt;br /&gt;
# По найденному транзитивному замыканию &amp;lt;math&amp;gt;\tilde G&amp;lt;/math&amp;gt; восстановить транзитивное замыкание исходного графа.&lt;br /&gt;
&lt;br /&gt;
Последний этап не является обязательным, если рассматривать транзитивное замыкание &amp;lt;math&amp;gt;\tilde G&amp;lt;/math&amp;gt; как «упакованное» транзитивное замыкание &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;1 этап&#039;&#039;&#039; (&#039;&#039;вычисление компонент сильной связности&#039;&#039;) может быть реализован [[Алгоритм Тарьяна поиска компонент сильной связности|алгоритмом Тарьяна]]&amp;lt;ref name=DFS&amp;gt;Tarjan, Robert. “Depth-First Search and Linear Graph Algorithms.” SIAM Journal on Computing 1, no. 2 (1972): 146–60.&amp;lt;/ref&amp;gt;, находящим компоненты сильной связности в ходе [[Поиск в глубину (DFS)|поиска в глубину]].&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;2 этап&#039;&#039;&#039; (&#039;&#039;топологическая сортировка&#039;&#039;) может быть реализован алгоритмом Кана&amp;lt;ref&amp;gt;Kahn, A B. “Topological Sorting of Large Networks.” Communications of the ACM 5, no. 11 (November 1962): 558–62. doi:10.1145/368996.369025.&amp;lt;/ref&amp;gt; либо последовательным применением [[Поиск в глубину (DFS)|поиска в глубину]]&amp;lt;ref name=DFS /&amp;gt; для нумерации вершин в предпорядке.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;3 этап&#039;&#039;&#039; (&#039;&#039;транзитивное замыкание&#039;&#039; &amp;lt;math&amp;gt;\tilde G&amp;lt;/math&amp;gt;) выполняется следующим алгоритмом:&lt;br /&gt;
&lt;br /&gt;
 &#039;&#039;&#039;Входные данные&#039;&#039;&#039;:&lt;br /&gt;
     ациклический граф &#039;G&#039;&#039; с вершинами &#039;V&#039;&#039;, пронумерованными в топологическом порядке, и рёбрами &#039;&#039;E&#039;&#039;.&lt;br /&gt;
 &#039;&#039;&#039;Выходные данные&#039;&#039;&#039;:&lt;br /&gt;
     для каждой вершины &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039; – набор вершин &#039;&#039;T&#039;&#039;(&#039;&#039;v&#039;&#039;),&lt;br /&gt;
         так что транзитивное замыкание &#039;&#039;G&#039;&#039; состоит из рёбер (&#039;&#039;v&#039;&#039;, &#039;&#039;w&#039;&#039;), &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039;, &#039;&#039;w&#039;&#039; ∈ &#039;&#039;T&#039;&#039;(&#039;&#039;v&#039;&#039;).&lt;br /&gt;
     &lt;br /&gt;
 &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039; &#039;&#039;&#039;do&#039;&#039;&#039; &#039;&#039;T&#039;&#039;(&#039;&#039;v&#039;&#039;) := { &#039;&#039;v&#039;&#039; }&lt;br /&gt;
 &#039;&#039;&#039;for&#039;&#039;&#039; &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039; &#039;&#039;&#039;in reverse order&#039;&#039;&#039;:&lt;br /&gt;
     &#039;&#039;&#039;for each&#039;&#039;&#039; (&#039;&#039;v&#039;&#039;, &#039;&#039;w&#039;&#039;) ∈ &#039;&#039;E&#039;&#039; &#039;&#039;&#039;do&#039;&#039;&#039; &#039;&#039;T&#039;&#039;(&#039;&#039;v&#039;&#039;) := &#039;&#039;T&#039;&#039;(&#039;&#039;v&#039;&#039;) ∪ &#039;&#039;T&#039;&#039;(&#039;&#039;w&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;4 этап&#039;&#039;&#039; (&#039;&#039;транзитивное замыкание&#039;&#039; &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;) либо выполняется следующим алгоритмом:&lt;br /&gt;
&lt;br /&gt;
 &#039;&#039;&#039;Входные данные&#039;&#039;&#039;&lt;br /&gt;
     граф &#039;&#039;G&#039;&#039; с вершинами &#039;&#039;V&#039;&#039;;&lt;br /&gt;
     компоненты сильной связности &#039;&#039;SCC&#039;&#039;(&#039;&#039;v&#039;&#039;) графа &#039;&#039;G&#039;&#039;;&lt;br /&gt;
     граф &amp;lt;math&amp;gt;\tilde G&amp;lt;/math&amp;gt; с вершинами &amp;lt;math&amp;gt;\tilde V&amp;lt;/math&amp;gt;;&lt;br /&gt;
     транзитивное замыкание &amp;lt;math&amp;gt;\tilde T&amp;lt;/math&amp;gt; графа &amp;lt;math&amp;gt;\tilde G&amp;lt;/math&amp;gt;.&lt;br /&gt;
 &#039;&#039;&#039;Выходные данные&#039;&#039;&#039;:&lt;br /&gt;
     для каждой вершины &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039; – набор вершин &#039;&#039;T&#039;&#039;(&#039;&#039;v&#039;&#039;),&lt;br /&gt;
         так что транзитивное замыкание &#039;&#039;G&#039;&#039; состоит из рёбер (&#039;&#039;v&#039;&#039;, &#039;&#039;w&#039;&#039;), &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039;, &#039;&#039;w&#039;&#039; ∈ &#039;&#039;T&#039;&#039;(&#039;&#039;v&#039;&#039;).&lt;br /&gt;
         &lt;br /&gt;
 &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039; &#039;&#039;&#039;do&#039;&#039;&#039; &#039;&#039;T&#039;&#039;(&#039;&#039;v&#039;&#039;) := { &#039;&#039;v&#039;&#039; }&lt;br /&gt;
 &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;v&#039;&#039; ∈ &amp;lt;math&amp;gt;\tilde V&amp;lt;/math&amp;gt;:&lt;br /&gt;
     &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;w&#039;&#039; ∈ &amp;lt;math&amp;gt;\tilde T(v)&amp;lt;/math&amp;gt;:&lt;br /&gt;
         &#039;&#039;T&#039;&#039;(&#039;&#039;v&#039;&#039;) := &#039;&#039;T&#039;&#039;(&#039;&#039;v&#039;&#039;) ∪ &#039;&#039;SCC&#039;&#039;(&#039;&#039;w&#039;&#039;)&lt;br /&gt;
     &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;x&#039;&#039; ∈ &#039;&#039;SCC&#039;&#039;(&#039;&#039;v&#039;&#039;):&lt;br /&gt;
         &#039;&#039;T&#039;&#039;(&#039;&#039;x&#039;&#039;) := &#039;&#039;T&#039;&#039;(&#039;&#039;v&#039;&#039;)    &#039;&#039;/* присваивание по ссылке */&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
либо транзитивное замыкание может строиться по упакованным данным &amp;lt;math&amp;gt;\tilde T(v)&amp;lt;/math&amp;gt; одной из следующих функций:&lt;br /&gt;
&lt;br /&gt;
 &#039;&#039;&#039;Входные данные&#039;&#039;&#039;&lt;br /&gt;
     граф &#039;&#039;G&#039;&#039; с вершинами &#039;&#039;V&#039;&#039;;&lt;br /&gt;
     компоненты сильной связности &#039;&#039;SCC&#039;&#039;(&#039;&#039;v&#039;&#039;) графа &#039;&#039;G&#039;&#039;;&lt;br /&gt;
     отображение &#039;&#039;R&#039;&#039;(&#039;&#039;v&#039;&#039;) вершин графа &#039;&#039;G&#039;&#039; на вершины графа &amp;lt;math&amp;gt;\tilde G&amp;lt;/math&amp;gt;;&lt;br /&gt;
     граф &amp;lt;math&amp;gt;\tilde G&amp;lt;/math&amp;gt; с вершинами &amp;lt;math&amp;gt;\tilde V&amp;lt;/math&amp;gt;;&lt;br /&gt;
     транзитивное замыкание &amp;lt;math&amp;gt;\tilde T&amp;lt;/math&amp;gt; графа &amp;lt;math&amp;gt;\tilde G&amp;lt;/math&amp;gt;.&lt;br /&gt;
     &lt;br /&gt;
 &#039;&#039;&#039;function&#039;&#039;&#039; transitive_closure(&#039;&#039;v&#039;&#039;):&lt;br /&gt;
     &#039;&#039;T&#039;&#039; := { &#039;&#039;v&#039;&#039; }&lt;br /&gt;
     &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;w&#039;&#039; ∈ &amp;lt;math&amp;gt;\tilde T(R(v))&amp;lt;/math&amp;gt;:&lt;br /&gt;
         &#039;&#039;T&#039;&#039; := &#039;&#039;T&#039;&#039; ∪ &#039;&#039;SCC&#039;&#039;(&#039;&#039;w&#039;&#039;)&lt;br /&gt;
     &#039;&#039;&#039;return&#039;&#039;&#039; &#039;&#039;T&#039;&#039;&lt;br /&gt;
     &lt;br /&gt;
 &#039;&#039;&#039;function&#039;&#039;&#039; is_in_transitive_closure(&#039;&#039;v&#039;&#039;, &#039;&#039;w&#039;&#039;):&lt;br /&gt;
     &#039;&#039;&#039;return&#039;&#039;&#039; &#039;&#039;R&#039;&#039;(&#039;&#039;w&#039;&#039;) ∈ &amp;lt;math&amp;gt;\tilde T(R(v))&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Первый и второй этап выполняются с помощью поиска в глубину со сложностью &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
На третьем этапе основой операцией является объединение списков вершин. В случае хранения списков в отсортированном виде объединение выполняется за линейное время. Количество списков не превосходит &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;, а в каждом списке не более &amp;lt;math&amp;gt;\mu&amp;lt;/math&amp;gt; вершин, где &amp;lt;math&amp;gt;\mu&amp;lt;/math&amp;gt; – количество компонент сильной связности. Общая сложность тогда составляет &amp;lt;math&amp;gt;O(\mu^2)&amp;lt;/math&amp;gt;. Альтернативным форматов хранения списка вершин является битовая маска с такой же общей сложностью.&lt;br /&gt;
&lt;br /&gt;
Сложность четвёртого этапа составляет &amp;lt;math&amp;gt;O(\mu n)&amp;lt;/math&amp;gt;, так как для каждой из &amp;lt;math&amp;gt;\mu&amp;lt;/math&amp;gt; компонент сильной связности за линейное время вычисляется список вершин из не более чем &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин. Поскольку компоненты сильной связности не пересекаются, на данном этапе уже не требуется хранить списки в отсортированном виде.&lt;br /&gt;
&lt;br /&gt;
Таким образом, общая сложность составляет &amp;lt;math&amp;gt;O(m + \mu^2)&amp;lt;/math&amp;gt;, если не требуется явное построение транзитивного замыкания, или &amp;lt;math&amp;gt;O(m + \mu n)&amp;lt;/math&amp;gt; в противном случае.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
==== Локальность реализации алгоритма ====&lt;br /&gt;
===== Структура обращений в память и качественная оценка локальности =====&lt;br /&gt;
===== Количественная оценка локальности =====&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
Программа, реализующая поиск транзитного замыкания, состоит CPU части, отвечающей за общую координацию вычислений, использующей вспомогательные классы.&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
==== Масштабируемость алгоритма ====&lt;br /&gt;
==== Масштабируемость реализации алгоритма ====&lt;br /&gt;
&lt;br /&gt;
Для демонстрации эффективности представлены графики времени выполнения различных режимов вычислений в зависимости от размера входного графа. &lt;br /&gt;
&lt;br /&gt;
Основной характеристикой для сравнения было выбрано время выполнения, так как производительность (определения как TEPS то есть число ребер графа, которое алгоритм обрабатывает в секунду) для данной операции не отражает реальной ситуации. &lt;br /&gt;
&lt;br /&gt;
Дело в том, что сложность выбранного алгоритма Пурдома зависит от количества ребер в графе не линейно, и, кроме того, зависит еще и от структуры графа количества сильно связанных компонент). &lt;br /&gt;
&lt;br /&gt;
[[file:Замыкание scaling wide.png|thumb|center|700px|Рисунок 2. Параллельная реализация алгоритма Пурдома масштабируемость различных версий: производительность в зависимости от размера графа.]]&lt;br /&gt;
&lt;br /&gt;
Анализируя время выполнения можно оценить, насколько понижается эффективность обработки графа при увеличении его размера (данные перестают помещаться в кэш, в память GPU, узла и т.д.). На представленных графиках по оси X граф с каждым делением увеличивается в два раза, и по представленному графику можно оценивать, на сколько увеличилось время выполнения.&lt;br /&gt;
Так же время выполнения можно сравнивать для различных версий, например, последовательной CPU, параллельной CPU или же GPU.&lt;br /&gt;
На приведенной диаграмме (рисунок 1) для различных размеров графа (от 2^20 до 2^26) представлено время выполнения трех различных версий: последовательной, параллельной CPU и GPU. Шкала на данной диаграмме логарифмическая.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
* [http://www.boost.org/libs/graph/doc/ Boost Graph Library] (функция &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph/doc/transitive_closure.html transitive_closure]&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Начатые статьи]]&lt;/div&gt;</summary>
		<author><name>Teplov</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9F%D1%83%D1%80%D0%B4%D0%BE%D0%BC%D0%B0&amp;diff=18331</id>
		<title>Алгоритм Пурдома</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9F%D1%83%D1%80%D0%B4%D0%BE%D0%BC%D0%B0&amp;diff=18331"/>
		<updated>2016-11-18T20:51:59Z</updated>

		<summary type="html">&lt;p&gt;Teplov: /* Масштабируемость реализации алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм Пурдома&#039;&#039;&#039;&amp;lt;ref name=Purdom&amp;gt;Purdom, Paul, Jr. “A Transitive Closure Algorithm.” Bit 10, no. 1 (March 1970): 76–94. doi:10.1007/BF01940892.&amp;lt;/ref&amp;gt; находит [[Поиск транзитивного замыкания орграфа|транзитивное замыкание ориентированного графа]] за время &amp;lt;math&amp;gt;O(m + \mu n)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\mu \le m&amp;lt;/math&amp;gt; – число [[Связность в графах|компонент сильной связности]] этого графа.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм основан на следующих свойствах:&lt;br /&gt;
# Если вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;w&amp;lt;/math&amp;gt; принадлежат одной [[Связность в графах|компоненте сильной связности]] графа &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;, то его транзитивное замыкание &amp;lt;math&amp;gt;G^+&amp;lt;/math&amp;gt; содержит рёбра &amp;lt;math&amp;gt;(v, w)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(w, v)&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Если вершины &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;y&amp;lt;/math&amp;gt; принадлежат одной компоненте сильной связности графа &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;, а вершины &amp;lt;math&amp;gt;z&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; – другой, то рёбра &amp;lt;math&amp;gt;(x, z)&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;(x, t)&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;(y, z)&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;(y, t)&amp;lt;/math&amp;gt; принадлежат или не принадлежат транзитивному замыканию &amp;lt;math&amp;gt;G^+&amp;lt;/math&amp;gt; одновременно.&lt;br /&gt;
&lt;br /&gt;
Таким образом, поиск транзитивного замыкания графа &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt; сводится к поиску транзитивного замыкания ациклического графа &amp;lt;math&amp;gt;\tilde G&amp;lt;/math&amp;gt;, полученного из &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt; схлопыванием каждой компоненты сильной связности в одну вершину. Транзитивное замыкание ациклического графа вычисляется с использованием топологической сортировки вершин графа.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Вычисления производятся в четыре этапа:&lt;br /&gt;
&lt;br /&gt;
# Найти [[Связность в графах|компоненты сильной связности]] исходного графа, заменить каждую компоненту на одну вершину и удалить образовавшиеся рёбра-петли.&lt;br /&gt;
# Выполнить топологическую сортировку полученного ациклического графа &amp;lt;math&amp;gt;\tilde G&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Вычислить транзитивное замыкание графа &amp;lt;math&amp;gt;\tilde G&amp;lt;/math&amp;gt;, двигаясь от вершин с бо́льшими номерами к меньшим.&lt;br /&gt;
# По найденному транзитивному замыканию &amp;lt;math&amp;gt;\tilde G&amp;lt;/math&amp;gt; восстановить транзитивное замыкание исходного графа.&lt;br /&gt;
&lt;br /&gt;
Последний этап не является обязательным, если рассматривать транзитивное замыкание &amp;lt;math&amp;gt;\tilde G&amp;lt;/math&amp;gt; как «упакованное» транзитивное замыкание &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;1 этап&#039;&#039;&#039; (&#039;&#039;вычисление компонент сильной связности&#039;&#039;) может быть реализован [[Алгоритм Тарьяна поиска компонент сильной связности|алгоритмом Тарьяна]]&amp;lt;ref name=DFS&amp;gt;Tarjan, Robert. “Depth-First Search and Linear Graph Algorithms.” SIAM Journal on Computing 1, no. 2 (1972): 146–60.&amp;lt;/ref&amp;gt;, находящим компоненты сильной связности в ходе [[Поиск в глубину (DFS)|поиска в глубину]].&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;2 этап&#039;&#039;&#039; (&#039;&#039;топологическая сортировка&#039;&#039;) может быть реализован алгоритмом Кана&amp;lt;ref&amp;gt;Kahn, A B. “Topological Sorting of Large Networks.” Communications of the ACM 5, no. 11 (November 1962): 558–62. doi:10.1145/368996.369025.&amp;lt;/ref&amp;gt; либо последовательным применением [[Поиск в глубину (DFS)|поиска в глубину]]&amp;lt;ref name=DFS /&amp;gt; для нумерации вершин в предпорядке.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;3 этап&#039;&#039;&#039; (&#039;&#039;транзитивное замыкание&#039;&#039; &amp;lt;math&amp;gt;\tilde G&amp;lt;/math&amp;gt;) выполняется следующим алгоритмом:&lt;br /&gt;
&lt;br /&gt;
 &#039;&#039;&#039;Входные данные&#039;&#039;&#039;:&lt;br /&gt;
     ациклический граф &#039;G&#039;&#039; с вершинами &#039;V&#039;&#039;, пронумерованными в топологическом порядке, и рёбрами &#039;&#039;E&#039;&#039;.&lt;br /&gt;
 &#039;&#039;&#039;Выходные данные&#039;&#039;&#039;:&lt;br /&gt;
     для каждой вершины &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039; – набор вершин &#039;&#039;T&#039;&#039;(&#039;&#039;v&#039;&#039;),&lt;br /&gt;
         так что транзитивное замыкание &#039;&#039;G&#039;&#039; состоит из рёбер (&#039;&#039;v&#039;&#039;, &#039;&#039;w&#039;&#039;), &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039;, &#039;&#039;w&#039;&#039; ∈ &#039;&#039;T&#039;&#039;(&#039;&#039;v&#039;&#039;).&lt;br /&gt;
     &lt;br /&gt;
 &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039; &#039;&#039;&#039;do&#039;&#039;&#039; &#039;&#039;T&#039;&#039;(&#039;&#039;v&#039;&#039;) := { &#039;&#039;v&#039;&#039; }&lt;br /&gt;
 &#039;&#039;&#039;for&#039;&#039;&#039; &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039; &#039;&#039;&#039;in reverse order&#039;&#039;&#039;:&lt;br /&gt;
     &#039;&#039;&#039;for each&#039;&#039;&#039; (&#039;&#039;v&#039;&#039;, &#039;&#039;w&#039;&#039;) ∈ &#039;&#039;E&#039;&#039; &#039;&#039;&#039;do&#039;&#039;&#039; &#039;&#039;T&#039;&#039;(&#039;&#039;v&#039;&#039;) := &#039;&#039;T&#039;&#039;(&#039;&#039;v&#039;&#039;) ∪ &#039;&#039;T&#039;&#039;(&#039;&#039;w&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;4 этап&#039;&#039;&#039; (&#039;&#039;транзитивное замыкание&#039;&#039; &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;) либо выполняется следующим алгоритмом:&lt;br /&gt;
&lt;br /&gt;
 &#039;&#039;&#039;Входные данные&#039;&#039;&#039;&lt;br /&gt;
     граф &#039;&#039;G&#039;&#039; с вершинами &#039;&#039;V&#039;&#039;;&lt;br /&gt;
     компоненты сильной связности &#039;&#039;SCC&#039;&#039;(&#039;&#039;v&#039;&#039;) графа &#039;&#039;G&#039;&#039;;&lt;br /&gt;
     граф &amp;lt;math&amp;gt;\tilde G&amp;lt;/math&amp;gt; с вершинами &amp;lt;math&amp;gt;\tilde V&amp;lt;/math&amp;gt;;&lt;br /&gt;
     транзитивное замыкание &amp;lt;math&amp;gt;\tilde T&amp;lt;/math&amp;gt; графа &amp;lt;math&amp;gt;\tilde G&amp;lt;/math&amp;gt;.&lt;br /&gt;
 &#039;&#039;&#039;Выходные данные&#039;&#039;&#039;:&lt;br /&gt;
     для каждой вершины &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039; – набор вершин &#039;&#039;T&#039;&#039;(&#039;&#039;v&#039;&#039;),&lt;br /&gt;
         так что транзитивное замыкание &#039;&#039;G&#039;&#039; состоит из рёбер (&#039;&#039;v&#039;&#039;, &#039;&#039;w&#039;&#039;), &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039;, &#039;&#039;w&#039;&#039; ∈ &#039;&#039;T&#039;&#039;(&#039;&#039;v&#039;&#039;).&lt;br /&gt;
         &lt;br /&gt;
 &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039; &#039;&#039;&#039;do&#039;&#039;&#039; &#039;&#039;T&#039;&#039;(&#039;&#039;v&#039;&#039;) := { &#039;&#039;v&#039;&#039; }&lt;br /&gt;
 &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;v&#039;&#039; ∈ &amp;lt;math&amp;gt;\tilde V&amp;lt;/math&amp;gt;:&lt;br /&gt;
     &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;w&#039;&#039; ∈ &amp;lt;math&amp;gt;\tilde T(v)&amp;lt;/math&amp;gt;:&lt;br /&gt;
         &#039;&#039;T&#039;&#039;(&#039;&#039;v&#039;&#039;) := &#039;&#039;T&#039;&#039;(&#039;&#039;v&#039;&#039;) ∪ &#039;&#039;SCC&#039;&#039;(&#039;&#039;w&#039;&#039;)&lt;br /&gt;
     &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;x&#039;&#039; ∈ &#039;&#039;SCC&#039;&#039;(&#039;&#039;v&#039;&#039;):&lt;br /&gt;
         &#039;&#039;T&#039;&#039;(&#039;&#039;x&#039;&#039;) := &#039;&#039;T&#039;&#039;(&#039;&#039;v&#039;&#039;)    &#039;&#039;/* присваивание по ссылке */&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
либо транзитивное замыкание может строиться по упакованным данным &amp;lt;math&amp;gt;\tilde T(v)&amp;lt;/math&amp;gt; одной из следующих функций:&lt;br /&gt;
&lt;br /&gt;
 &#039;&#039;&#039;Входные данные&#039;&#039;&#039;&lt;br /&gt;
     граф &#039;&#039;G&#039;&#039; с вершинами &#039;&#039;V&#039;&#039;;&lt;br /&gt;
     компоненты сильной связности &#039;&#039;SCC&#039;&#039;(&#039;&#039;v&#039;&#039;) графа &#039;&#039;G&#039;&#039;;&lt;br /&gt;
     отображение &#039;&#039;R&#039;&#039;(&#039;&#039;v&#039;&#039;) вершин графа &#039;&#039;G&#039;&#039; на вершины графа &amp;lt;math&amp;gt;\tilde G&amp;lt;/math&amp;gt;;&lt;br /&gt;
     граф &amp;lt;math&amp;gt;\tilde G&amp;lt;/math&amp;gt; с вершинами &amp;lt;math&amp;gt;\tilde V&amp;lt;/math&amp;gt;;&lt;br /&gt;
     транзитивное замыкание &amp;lt;math&amp;gt;\tilde T&amp;lt;/math&amp;gt; графа &amp;lt;math&amp;gt;\tilde G&amp;lt;/math&amp;gt;.&lt;br /&gt;
     &lt;br /&gt;
 &#039;&#039;&#039;function&#039;&#039;&#039; transitive_closure(&#039;&#039;v&#039;&#039;):&lt;br /&gt;
     &#039;&#039;T&#039;&#039; := { &#039;&#039;v&#039;&#039; }&lt;br /&gt;
     &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;w&#039;&#039; ∈ &amp;lt;math&amp;gt;\tilde T(R(v))&amp;lt;/math&amp;gt;:&lt;br /&gt;
         &#039;&#039;T&#039;&#039; := &#039;&#039;T&#039;&#039; ∪ &#039;&#039;SCC&#039;&#039;(&#039;&#039;w&#039;&#039;)&lt;br /&gt;
     &#039;&#039;&#039;return&#039;&#039;&#039; &#039;&#039;T&#039;&#039;&lt;br /&gt;
     &lt;br /&gt;
 &#039;&#039;&#039;function&#039;&#039;&#039; is_in_transitive_closure(&#039;&#039;v&#039;&#039;, &#039;&#039;w&#039;&#039;):&lt;br /&gt;
     &#039;&#039;&#039;return&#039;&#039;&#039; &#039;&#039;R&#039;&#039;(&#039;&#039;w&#039;&#039;) ∈ &amp;lt;math&amp;gt;\tilde T(R(v))&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Первый и второй этап выполняются с помощью поиска в глубину со сложностью &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
На третьем этапе основой операцией является объединение списков вершин. В случае хранения списков в отсортированном виде объединение выполняется за линейное время. Количество списков не превосходит &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;, а в каждом списке не более &amp;lt;math&amp;gt;\mu&amp;lt;/math&amp;gt; вершин, где &amp;lt;math&amp;gt;\mu&amp;lt;/math&amp;gt; – количество компонент сильной связности. Общая сложность тогда составляет &amp;lt;math&amp;gt;O(\mu^2)&amp;lt;/math&amp;gt;. Альтернативным форматов хранения списка вершин является битовая маска с такой же общей сложностью.&lt;br /&gt;
&lt;br /&gt;
Сложность четвёртого этапа составляет &amp;lt;math&amp;gt;O(\mu n)&amp;lt;/math&amp;gt;, так как для каждой из &amp;lt;math&amp;gt;\mu&amp;lt;/math&amp;gt; компонент сильной связности за линейное время вычисляется список вершин из не более чем &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин. Поскольку компоненты сильной связности не пересекаются, на данном этапе уже не требуется хранить списки в отсортированном виде.&lt;br /&gt;
&lt;br /&gt;
Таким образом, общая сложность составляет &amp;lt;math&amp;gt;O(m + \mu^2)&amp;lt;/math&amp;gt;, если не требуется явное построение транзитивного замыкания, или &amp;lt;math&amp;gt;O(m + \mu n)&amp;lt;/math&amp;gt; в противном случае.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
==== Локальность реализации алгоритма ====&lt;br /&gt;
===== Структура обращений в память и качественная оценка локальности =====&lt;br /&gt;
===== Количественная оценка локальности =====&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
Программа, реализующая поиск транзитного замыкания, состоит CPU части, отвечающей за общую координацию вычислений, использующей вспомогательные классы.&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
==== Масштабируемость алгоритма ====&lt;br /&gt;
==== Масштабируемость реализации алгоритма ====&lt;br /&gt;
&lt;br /&gt;
Для демонстрации эффективности представлены графики времени выполнения различных режимов вычислений в зависимости от размера входного графа. &lt;br /&gt;
&lt;br /&gt;
Основной характеристикой для сравнения было выбрано время выполнения, так как производительность (определения как TEPS то есть число ребер графа, которое алгоритм обрабатывает в секунду) для данной операции не отражает реальной ситуации. &lt;br /&gt;
&lt;br /&gt;
Дело в том, что сложность выбранного алгоритма Пурдома зависит от количества ребер в графе не линейно, и, кроме того, зависит еще и от структуры графа количества сильно связанных компонент). &lt;br /&gt;
&lt;br /&gt;
[[file:Замыкание scaling wide.png|thumb|center|700px|Рисунок 2. Параллельная реализация алгоритма Пурдома масштабируемость CPU версии: производительность в зависимости от размера графа.]]&lt;br /&gt;
&lt;br /&gt;
Анализируя время выполнения можно оценить, насколько понижается эффективность обработки графа при увеличении его размера (данные перестают помещаться в кэш, в память GPU, узла и т.д.). На представленных графиках по оси X граф с каждым делением увеличивается в два раза, и по представленному графику можно оценивать, на сколько увеличилось время выполнения.&lt;br /&gt;
Так же время выполнения можно сравнивать для различных версий, например, последовательной CPU, параллельной CPU или же GPU.&lt;br /&gt;
На приведенной диаграмме (рисунок 1) для различных размеров графа (от 2^20 до 2^26) представлено время выполнения трех различных версий: последовательной, параллельной CPU и GPU. Шкала на данной диаграмме логарифмическая.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
* [http://www.boost.org/libs/graph/doc/ Boost Graph Library] (функция &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph/doc/transitive_closure.html transitive_closure]&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Начатые статьи]]&lt;/div&gt;</summary>
		<author><name>Teplov</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9F%D1%83%D1%80%D0%B4%D0%BE%D0%BC%D0%B0&amp;diff=18318</id>
		<title>Алгоритм Пурдома</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9F%D1%83%D1%80%D0%B4%D0%BE%D0%BC%D0%B0&amp;diff=18318"/>
		<updated>2016-11-18T20:45:32Z</updated>

		<summary type="html">&lt;p&gt;Teplov: /* Возможные способы и особенности параллельной реализации алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Свойства и структура алгоритма ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Алгоритм Пурдома&#039;&#039;&#039;&amp;lt;ref name=Purdom&amp;gt;Purdom, Paul, Jr. “A Transitive Closure Algorithm.” Bit 10, no. 1 (March 1970): 76–94. doi:10.1007/BF01940892.&amp;lt;/ref&amp;gt; находит [[Поиск транзитивного замыкания орграфа|транзитивное замыкание ориентированного графа]] за время &amp;lt;math&amp;gt;O(m + \mu n)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\mu \le m&amp;lt;/math&amp;gt; – число [[Связность в графах|компонент сильной связности]] этого графа.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм основан на следующих свойствах:&lt;br /&gt;
# Если вершины &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;w&amp;lt;/math&amp;gt; принадлежат одной [[Связность в графах|компоненте сильной связности]] графа &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;, то его транзитивное замыкание &amp;lt;math&amp;gt;G^+&amp;lt;/math&amp;gt; содержит рёбра &amp;lt;math&amp;gt;(v, w)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;(w, v)&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Если вершины &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;y&amp;lt;/math&amp;gt; принадлежат одной компоненте сильной связности графа &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;, а вершины &amp;lt;math&amp;gt;z&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; – другой, то рёбра &amp;lt;math&amp;gt;(x, z)&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;(x, t)&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;(y, z)&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;(y, t)&amp;lt;/math&amp;gt; принадлежат или не принадлежат транзитивному замыканию &amp;lt;math&amp;gt;G^+&amp;lt;/math&amp;gt; одновременно.&lt;br /&gt;
&lt;br /&gt;
Таким образом, поиск транзитивного замыкания графа &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt; сводится к поиску транзитивного замыкания ациклического графа &amp;lt;math&amp;gt;\tilde G&amp;lt;/math&amp;gt;, полученного из &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt; схлопыванием каждой компоненты сильной связности в одну вершину. Транзитивное замыкание ациклического графа вычисляется с использованием топологической сортировки вершин графа.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Вычисления производятся в четыре этапа:&lt;br /&gt;
&lt;br /&gt;
# Найти [[Связность в графах|компоненты сильной связности]] исходного графа, заменить каждую компоненту на одну вершину и удалить образовавшиеся рёбра-петли.&lt;br /&gt;
# Выполнить топологическую сортировку полученного ациклического графа &amp;lt;math&amp;gt;\tilde G&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Вычислить транзитивное замыкание графа &amp;lt;math&amp;gt;\tilde G&amp;lt;/math&amp;gt;, двигаясь от вершин с бо́льшими номерами к меньшим.&lt;br /&gt;
# По найденному транзитивному замыканию &amp;lt;math&amp;gt;\tilde G&amp;lt;/math&amp;gt; восстановить транзитивное замыкание исходного графа.&lt;br /&gt;
&lt;br /&gt;
Последний этап не является обязательным, если рассматривать транзитивное замыкание &amp;lt;math&amp;gt;\tilde G&amp;lt;/math&amp;gt; как «упакованное» транзитивное замыкание &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;1 этап&#039;&#039;&#039; (&#039;&#039;вычисление компонент сильной связности&#039;&#039;) может быть реализован [[Алгоритм Тарьяна поиска компонент сильной связности|алгоритмом Тарьяна]]&amp;lt;ref name=DFS&amp;gt;Tarjan, Robert. “Depth-First Search and Linear Graph Algorithms.” SIAM Journal on Computing 1, no. 2 (1972): 146–60.&amp;lt;/ref&amp;gt;, находящим компоненты сильной связности в ходе [[Поиск в глубину (DFS)|поиска в глубину]].&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;2 этап&#039;&#039;&#039; (&#039;&#039;топологическая сортировка&#039;&#039;) может быть реализован алгоритмом Кана&amp;lt;ref&amp;gt;Kahn, A B. “Topological Sorting of Large Networks.” Communications of the ACM 5, no. 11 (November 1962): 558–62. doi:10.1145/368996.369025.&amp;lt;/ref&amp;gt; либо последовательным применением [[Поиск в глубину (DFS)|поиска в глубину]]&amp;lt;ref name=DFS /&amp;gt; для нумерации вершин в предпорядке.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;3 этап&#039;&#039;&#039; (&#039;&#039;транзитивное замыкание&#039;&#039; &amp;lt;math&amp;gt;\tilde G&amp;lt;/math&amp;gt;) выполняется следующим алгоритмом:&lt;br /&gt;
&lt;br /&gt;
 &#039;&#039;&#039;Входные данные&#039;&#039;&#039;:&lt;br /&gt;
     ациклический граф &#039;G&#039;&#039; с вершинами &#039;V&#039;&#039;, пронумерованными в топологическом порядке, и рёбрами &#039;&#039;E&#039;&#039;.&lt;br /&gt;
 &#039;&#039;&#039;Выходные данные&#039;&#039;&#039;:&lt;br /&gt;
     для каждой вершины &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039; – набор вершин &#039;&#039;T&#039;&#039;(&#039;&#039;v&#039;&#039;),&lt;br /&gt;
         так что транзитивное замыкание &#039;&#039;G&#039;&#039; состоит из рёбер (&#039;&#039;v&#039;&#039;, &#039;&#039;w&#039;&#039;), &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039;, &#039;&#039;w&#039;&#039; ∈ &#039;&#039;T&#039;&#039;(&#039;&#039;v&#039;&#039;).&lt;br /&gt;
     &lt;br /&gt;
 &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039; &#039;&#039;&#039;do&#039;&#039;&#039; &#039;&#039;T&#039;&#039;(&#039;&#039;v&#039;&#039;) := { &#039;&#039;v&#039;&#039; }&lt;br /&gt;
 &#039;&#039;&#039;for&#039;&#039;&#039; &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039; &#039;&#039;&#039;in reverse order&#039;&#039;&#039;:&lt;br /&gt;
     &#039;&#039;&#039;for each&#039;&#039;&#039; (&#039;&#039;v&#039;&#039;, &#039;&#039;w&#039;&#039;) ∈ &#039;&#039;E&#039;&#039; &#039;&#039;&#039;do&#039;&#039;&#039; &#039;&#039;T&#039;&#039;(&#039;&#039;v&#039;&#039;) := &#039;&#039;T&#039;&#039;(&#039;&#039;v&#039;&#039;) ∪ &#039;&#039;T&#039;&#039;(&#039;&#039;w&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;4 этап&#039;&#039;&#039; (&#039;&#039;транзитивное замыкание&#039;&#039; &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;) либо выполняется следующим алгоритмом:&lt;br /&gt;
&lt;br /&gt;
 &#039;&#039;&#039;Входные данные&#039;&#039;&#039;&lt;br /&gt;
     граф &#039;&#039;G&#039;&#039; с вершинами &#039;&#039;V&#039;&#039;;&lt;br /&gt;
     компоненты сильной связности &#039;&#039;SCC&#039;&#039;(&#039;&#039;v&#039;&#039;) графа &#039;&#039;G&#039;&#039;;&lt;br /&gt;
     граф &amp;lt;math&amp;gt;\tilde G&amp;lt;/math&amp;gt; с вершинами &amp;lt;math&amp;gt;\tilde V&amp;lt;/math&amp;gt;;&lt;br /&gt;
     транзитивное замыкание &amp;lt;math&amp;gt;\tilde T&amp;lt;/math&amp;gt; графа &amp;lt;math&amp;gt;\tilde G&amp;lt;/math&amp;gt;.&lt;br /&gt;
 &#039;&#039;&#039;Выходные данные&#039;&#039;&#039;:&lt;br /&gt;
     для каждой вершины &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039; – набор вершин &#039;&#039;T&#039;&#039;(&#039;&#039;v&#039;&#039;),&lt;br /&gt;
         так что транзитивное замыкание &#039;&#039;G&#039;&#039; состоит из рёбер (&#039;&#039;v&#039;&#039;, &#039;&#039;w&#039;&#039;), &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039;, &#039;&#039;w&#039;&#039; ∈ &#039;&#039;T&#039;&#039;(&#039;&#039;v&#039;&#039;).&lt;br /&gt;
         &lt;br /&gt;
 &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;v&#039;&#039; ∈ &#039;&#039;V&#039;&#039; &#039;&#039;&#039;do&#039;&#039;&#039; &#039;&#039;T&#039;&#039;(&#039;&#039;v&#039;&#039;) := { &#039;&#039;v&#039;&#039; }&lt;br /&gt;
 &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;v&#039;&#039; ∈ &amp;lt;math&amp;gt;\tilde V&amp;lt;/math&amp;gt;:&lt;br /&gt;
     &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;w&#039;&#039; ∈ &amp;lt;math&amp;gt;\tilde T(v)&amp;lt;/math&amp;gt;:&lt;br /&gt;
         &#039;&#039;T&#039;&#039;(&#039;&#039;v&#039;&#039;) := &#039;&#039;T&#039;&#039;(&#039;&#039;v&#039;&#039;) ∪ &#039;&#039;SCC&#039;&#039;(&#039;&#039;w&#039;&#039;)&lt;br /&gt;
     &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;x&#039;&#039; ∈ &#039;&#039;SCC&#039;&#039;(&#039;&#039;v&#039;&#039;):&lt;br /&gt;
         &#039;&#039;T&#039;&#039;(&#039;&#039;x&#039;&#039;) := &#039;&#039;T&#039;&#039;(&#039;&#039;v&#039;&#039;)    &#039;&#039;/* присваивание по ссылке */&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
либо транзитивное замыкание может строиться по упакованным данным &amp;lt;math&amp;gt;\tilde T(v)&amp;lt;/math&amp;gt; одной из следующих функций:&lt;br /&gt;
&lt;br /&gt;
 &#039;&#039;&#039;Входные данные&#039;&#039;&#039;&lt;br /&gt;
     граф &#039;&#039;G&#039;&#039; с вершинами &#039;&#039;V&#039;&#039;;&lt;br /&gt;
     компоненты сильной связности &#039;&#039;SCC&#039;&#039;(&#039;&#039;v&#039;&#039;) графа &#039;&#039;G&#039;&#039;;&lt;br /&gt;
     отображение &#039;&#039;R&#039;&#039;(&#039;&#039;v&#039;&#039;) вершин графа &#039;&#039;G&#039;&#039; на вершины графа &amp;lt;math&amp;gt;\tilde G&amp;lt;/math&amp;gt;;&lt;br /&gt;
     граф &amp;lt;math&amp;gt;\tilde G&amp;lt;/math&amp;gt; с вершинами &amp;lt;math&amp;gt;\tilde V&amp;lt;/math&amp;gt;;&lt;br /&gt;
     транзитивное замыкание &amp;lt;math&amp;gt;\tilde T&amp;lt;/math&amp;gt; графа &amp;lt;math&amp;gt;\tilde G&amp;lt;/math&amp;gt;.&lt;br /&gt;
     &lt;br /&gt;
 &#039;&#039;&#039;function&#039;&#039;&#039; transitive_closure(&#039;&#039;v&#039;&#039;):&lt;br /&gt;
     &#039;&#039;T&#039;&#039; := { &#039;&#039;v&#039;&#039; }&lt;br /&gt;
     &#039;&#039;&#039;for each&#039;&#039;&#039; &#039;&#039;w&#039;&#039; ∈ &amp;lt;math&amp;gt;\tilde T(R(v))&amp;lt;/math&amp;gt;:&lt;br /&gt;
         &#039;&#039;T&#039;&#039; := &#039;&#039;T&#039;&#039; ∪ &#039;&#039;SCC&#039;&#039;(&#039;&#039;w&#039;&#039;)&lt;br /&gt;
     &#039;&#039;&#039;return&#039;&#039;&#039; &#039;&#039;T&#039;&#039;&lt;br /&gt;
     &lt;br /&gt;
 &#039;&#039;&#039;function&#039;&#039;&#039; is_in_transitive_closure(&#039;&#039;v&#039;&#039;, &#039;&#039;w&#039;&#039;):&lt;br /&gt;
     &#039;&#039;&#039;return&#039;&#039;&#039; &#039;&#039;R&#039;&#039;(&#039;&#039;w&#039;&#039;) ∈ &amp;lt;math&amp;gt;\tilde T(R(v))&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Первый и второй этап выполняются с помощью поиска в глубину со сложностью &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
На третьем этапе основой операцией является объединение списков вершин. В случае хранения списков в отсортированном виде объединение выполняется за линейное время. Количество списков не превосходит &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;, а в каждом списке не более &amp;lt;math&amp;gt;\mu&amp;lt;/math&amp;gt; вершин, где &amp;lt;math&amp;gt;\mu&amp;lt;/math&amp;gt; – количество компонент сильной связности. Общая сложность тогда составляет &amp;lt;math&amp;gt;O(\mu^2)&amp;lt;/math&amp;gt;. Альтернативным форматов хранения списка вершин является битовая маска с такой же общей сложностью.&lt;br /&gt;
&lt;br /&gt;
Сложность четвёртого этапа составляет &amp;lt;math&amp;gt;O(\mu n)&amp;lt;/math&amp;gt;, так как для каждой из &amp;lt;math&amp;gt;\mu&amp;lt;/math&amp;gt; компонент сильной связности за линейное время вычисляется список вершин из не более чем &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; вершин. Поскольку компоненты сильной связности не пересекаются, на данном этапе уже не требуется хранить списки в отсортированном виде.&lt;br /&gt;
&lt;br /&gt;
Таким образом, общая сложность составляет &amp;lt;math&amp;gt;O(m + \mu^2)&amp;lt;/math&amp;gt;, если не требуется явное построение транзитивного замыкания, или &amp;lt;math&amp;gt;O(m + \mu n)&amp;lt;/math&amp;gt; в противном случае.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
==== Локальность реализации алгоритма ====&lt;br /&gt;
===== Структура обращений в память и качественная оценка локальности =====&lt;br /&gt;
===== Количественная оценка локальности =====&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
Программа, реализующая поиск транзитного замыкания, состоит CPU части, отвечающей за общую координацию вычислений, использующей вспомогательные классы.&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
==== Масштабируемость алгоритма ====&lt;br /&gt;
==== Масштабируемость реализации алгоритма ====&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
* [http://www.boost.org/libs/graph/doc/ Boost Graph Library] (функция &amp;lt;code&amp;gt;[http://www.boost.org/libs/graph/doc/transitive_closure.html transitive_closure]&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Начатые статьи]]&lt;/div&gt;</summary>
		<author><name>Teplov</name></author>
	</entry>
</feed>