<?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=Lineprinter</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=Lineprinter"/>
	<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/Lineprinter"/>
	<updated>2026-10-22T14:24:10Z</updated>
	<subtitle>Вклад</subtitle>
	<generator>MediaWiki 1.43.5</generator>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=Widget:Algoviewer&amp;diff=22658</id>
		<title>Widget:Algoviewer</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=Widget:Algoviewer&amp;diff=22658"/>
		<updated>2017-05-28T17:15:47Z</updated>

		<summary type="html">&lt;p&gt;Lineprinter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;noinclude&amp;gt;&lt;br /&gt;
Шаблон для вставки интерактивных графов алгоритмов.&lt;br /&gt;
&lt;br /&gt;
Параметры:&lt;br /&gt;
* url (e.g. &amp;quot;Givens/Algo_viewer_example_1.html&amp;quot;)&lt;br /&gt;
* width (default 400)&lt;br /&gt;
* height (default 300)&lt;br /&gt;
* border (default 0)&lt;br /&gt;
&lt;br /&gt;
Пример:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
{{#widget:Algoviewer&lt;br /&gt;
|url=Givens/giv3/Algo_view_givens3.html&lt;br /&gt;
|width=600&lt;br /&gt;
|height=600&lt;br /&gt;
|border=1&lt;br /&gt;
}}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
{{#widget:Algoviewer&lt;br /&gt;
|url=Givens/giv3/Algo_view_givens3.html&lt;br /&gt;
|width=600&lt;br /&gt;
|height=600&lt;br /&gt;
|border=1&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
&amp;lt;includeonly&amp;gt;&lt;br /&gt;
&amp;lt;iframe src=&amp;quot;https://algowiki-project.org/html/Algo_viewer_json/&amp;lt;!--{$url|escape:&#039;htmlall&#039;}--&amp;gt;&amp;quot; frameborder=&amp;quot;&amp;lt;!--{$border|validate:int|default:0}--&amp;gt;&amp;quot; width=&amp;quot;&amp;lt;!--{$width|escape:html|default:400}--&amp;gt;&amp;quot; height=&amp;quot;&amp;lt;!--{$height|escape:html|default:300}--&amp;gt;&amp;quot;&amp;gt;&amp;lt;/iframe&amp;gt;&lt;br /&gt;
&amp;lt;/includeonly&amp;gt;&lt;/div&gt;</summary>
		<author><name>Lineprinter</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%D0%B2%D0%B8%D0%BA%D0%B8:%D0%A2%D0%B5%D1%85%D0%BD%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B9_%D1%84%D0%BE%D1%80%D1%83%D0%BC&amp;diff=22554</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%D0%B2%D0%B8%D0%BA%D0%B8:%D0%A2%D0%B5%D1%85%D0%BD%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B9_%D1%84%D0%BE%D1%80%D1%83%D0%BC&amp;diff=22554"/>
		<updated>2017-04-20T14:35:18Z</updated>

		<summary type="html">&lt;p&gt;Lineprinter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{project internals nav}}&lt;br /&gt;
&lt;br /&gt;
== Свежие на момент 2017-04-20 изменения ==&lt;br /&gt;
* [[Widget:Iframe]] заменен на [[Widget:Algoviewer]]&lt;br /&gt;
* Установлено расширение [https://www.mediawiki.org/wiki/Extension:Tabs Tabs]&lt;br /&gt;
&lt;br /&gt;
— [[S:Contributions/Lineprinter|lineprinter]] 17:35, 20 апреля 2017 (MSK)&lt;br /&gt;
&lt;br /&gt;
== Свежие на момент 2016-11-01 изменения ==&lt;br /&gt;
* Починен redirect loop на страницах с символами +, &#039;, &amp;quot;, =, \.&lt;br /&gt;
* Mediawiki обновлена до 1.26.4.&lt;br /&gt;
* Появился [[Ш:Doc]] для документации шаблонов.&lt;br /&gt;
* Заполнен [[MediaWiki:Edittools]] на основе [[ru-wikipedia:MediaWiki:Edittools]].&lt;br /&gt;
&lt;br /&gt;
— [[S:Contributions/Lineprinter|lineprinter]] 11:11, 1 ноября 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
== Свежие на момент 2016-10-07 изменения ==&lt;br /&gt;
* Введен обязательный https.&lt;br /&gt;
* Сертификат заменен на Let&#039;s Encrypt&#039;овский.&lt;br /&gt;
* Введена капча на регистрацию и на повторные попытки входа в систему.&lt;br /&gt;
* Открыта регистрация.&lt;br /&gt;
* Введены дополнительные, короткие, редиректы на неймспейсы:&lt;br /&gt;
** &#039;&#039;&#039;Ш&#039;&#039;&#039; и &#039;&#039;&#039;T&#039;&#039;&#039; для &#039;&#039;&#039;Ш&#039;&#039;&#039;аблонов (&#039;&#039;&#039;T&#039;&#039;&#039;emplate)&lt;br /&gt;
** &#039;&#039;&#039;К&#039;&#039;&#039; и &#039;&#039;&#039;C&#039;&#039;&#039; для &#039;&#039;&#039;К&#039;&#039;&#039;атегорий (&#039;&#039;&#039;C&#039;&#039;&#039;ategory)&lt;br /&gt;
** &#039;&#039;&#039;С&#039;&#039;&#039; и &#039;&#039;&#039;S&#039;&#039;&#039; для &#039;&#039;&#039;С&#039;&#039;&#039;лужебных (&#039;&#039;&#039;S&#039;&#039;&#039;pecial)&lt;br /&gt;
** &#039;&#039;&#039;U&#039;&#039;&#039; для пользователей (&#039;&#039;&#039;U&#039;&#039;&#039;ser)&lt;br /&gt;
** &#039;&#039;&#039;Ф&#039;&#039;&#039; и &#039;&#039;&#039;F&#039;&#039;&#039; для &#039;&#039;&#039;Ф&#039;&#039;&#039;айлов (&#039;&#039;&#039;F&#039;&#039;&#039;ile)&lt;br /&gt;
** &#039;&#039;&#039;А&#039;&#039;&#039; и &#039;&#039;&#039;A&#039;&#039;&#039; для &#039;&#039;&#039;А&#039;&#039;&#039;лговики (&#039;&#039;&#039;A&#039;&#039;&#039;lgowiki)&lt;br /&gt;
: Таким образом, на страницу [[Алговики:Общий форум]] можно попасть по [[А:Общий форум]], например.&lt;br /&gt;
* Появился новый [[Ш:Плашка]], который предполагается использовать для создания производных от него шаблонов (например [[Ш:Assignment]])&lt;br /&gt;
&lt;br /&gt;
— [[S:Contributions/Lineprinter|lineprinter]] 14:27, 7 октября 2016 (MSK)&lt;/div&gt;</summary>
		<author><name>Lineprinter</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%D0%B2%D0%B8%D0%BA%D0%B8:%D0%A2%D0%B5%D1%85%D0%BD%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B9_%D1%84%D0%BE%D1%80%D1%83%D0%BC&amp;diff=22553</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%D0%B2%D0%B8%D0%BA%D0%B8:%D0%A2%D0%B5%D1%85%D0%BD%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B9_%D1%84%D0%BE%D1%80%D1%83%D0%BC&amp;diff=22553"/>
		<updated>2017-04-20T14:34:52Z</updated>

		<summary type="html">&lt;p&gt;Lineprinter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{project internals nav}}&lt;br /&gt;
&lt;br /&gt;
== Свежие на момент 2017-04-20 изменения ==&lt;br /&gt;
* [[Widget:Iframe]] заменен на [[Widget:Algoviewer]]&lt;br /&gt;
* Установлено расширение [https://www.mediawiki.org/wiki/Extension:Tabs Tabs]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Свежие на момент 2016-11-01 изменения ==&lt;br /&gt;
* Починен redirect loop на страницах с символами +, &#039;, &amp;quot;, =, \.&lt;br /&gt;
* Mediawiki обновлена до 1.26.4.&lt;br /&gt;
* Появился [[Ш:Doc]] для документации шаблонов.&lt;br /&gt;
* Заполнен [[MediaWiki:Edittools]] на основе [[ru-wikipedia:MediaWiki:Edittools]].&lt;br /&gt;
&lt;br /&gt;
— [[S:Contributions/Lineprinter|lineprinter]] 11:11, 1 ноября 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
== Свежие на момент 2016-10-07 изменения ==&lt;br /&gt;
* Введен обязательный https.&lt;br /&gt;
* Сертификат заменен на Let&#039;s Encrypt&#039;овский.&lt;br /&gt;
* Введена капча на регистрацию и на повторные попытки входа в систему.&lt;br /&gt;
* Открыта регистрация.&lt;br /&gt;
* Введены дополнительные, короткие, редиректы на неймспейсы:&lt;br /&gt;
** &#039;&#039;&#039;Ш&#039;&#039;&#039; и &#039;&#039;&#039;T&#039;&#039;&#039; для &#039;&#039;&#039;Ш&#039;&#039;&#039;аблонов (&#039;&#039;&#039;T&#039;&#039;&#039;emplate)&lt;br /&gt;
** &#039;&#039;&#039;К&#039;&#039;&#039; и &#039;&#039;&#039;C&#039;&#039;&#039; для &#039;&#039;&#039;К&#039;&#039;&#039;атегорий (&#039;&#039;&#039;C&#039;&#039;&#039;ategory)&lt;br /&gt;
** &#039;&#039;&#039;С&#039;&#039;&#039; и &#039;&#039;&#039;S&#039;&#039;&#039; для &#039;&#039;&#039;С&#039;&#039;&#039;лужебных (&#039;&#039;&#039;S&#039;&#039;&#039;pecial)&lt;br /&gt;
** &#039;&#039;&#039;U&#039;&#039;&#039; для пользователей (&#039;&#039;&#039;U&#039;&#039;&#039;ser)&lt;br /&gt;
** &#039;&#039;&#039;Ф&#039;&#039;&#039; и &#039;&#039;&#039;F&#039;&#039;&#039; для &#039;&#039;&#039;Ф&#039;&#039;&#039;айлов (&#039;&#039;&#039;F&#039;&#039;&#039;ile)&lt;br /&gt;
** &#039;&#039;&#039;А&#039;&#039;&#039; и &#039;&#039;&#039;A&#039;&#039;&#039; для &#039;&#039;&#039;А&#039;&#039;&#039;лговики (&#039;&#039;&#039;A&#039;&#039;&#039;lgowiki)&lt;br /&gt;
: Таким образом, на страницу [[Алговики:Общий форум]] можно попасть по [[А:Общий форум]], например.&lt;br /&gt;
* Появился новый [[Ш:Плашка]], который предполагается использовать для создания производных от него шаблонов (например [[Ш:Assignment]])&lt;br /&gt;
&lt;br /&gt;
— [[S:Contributions/Lineprinter|lineprinter]] 14:27, 7 октября 2016 (MSK)&lt;/div&gt;</summary>
		<author><name>Lineprinter</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=Widget:Algoviewer&amp;diff=22552</id>
		<title>Widget:Algoviewer</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=Widget:Algoviewer&amp;diff=22552"/>
		<updated>2017-04-20T14:33:45Z</updated>

		<summary type="html">&lt;p&gt;Lineprinter: Защищена страница «Widget:Algoviewer» ([Редактирование=Разрешено только администраторам] (бессрочно) [Переименование=Разрешено только админис…&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;noinclude&amp;gt;&lt;br /&gt;
* url (e.g. &amp;quot;Givens/Algo_viewer_example_1.html&amp;quot;)&lt;br /&gt;
* border (default 0)&lt;br /&gt;
* width (default 400)&lt;br /&gt;
* height (default 300)&lt;br /&gt;
{{#widget:Algoviewer&lt;br /&gt;
|url=seq_par/Algo_view_seq_par4.html&lt;br /&gt;
|width=1300&lt;br /&gt;
|height=800&lt;br /&gt;
|border=1&lt;br /&gt;
}}&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
&amp;lt;includeonly&amp;gt;&lt;br /&gt;
&amp;lt;iframe src=&amp;quot;https://algowiki-project.org/html/Algo_viewer_json/&amp;lt;!--{$url|escape:&#039;htmlall&#039;}--&amp;gt;&amp;quot; frameborder=&amp;quot;&amp;lt;!--{$border|validate:int|default:0}--&amp;gt;&amp;quot; width=&amp;quot;&amp;lt;!--{$width|escape:html|default:400}--&amp;gt;&amp;quot; height=&amp;quot;&amp;lt;!--{$height|escape:html|default:300}--&amp;gt;&amp;quot;&amp;gt;&amp;lt;/iframe&amp;gt;&lt;br /&gt;
&amp;lt;/includeonly&amp;gt;&lt;/div&gt;</summary>
		<author><name>Lineprinter</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%93%D0%B8%D0%B2%D0%B5%D0%BD%D1%81%D0%B0_(%D0%B2%D1%80%D0%B0%D1%89%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=22551</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%93%D0%B8%D0%B2%D0%B5%D0%BD%D1%81%D0%B0_(%D0%B2%D1%80%D0%B0%D1%89%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=22551"/>
		<updated>2017-04-20T14:30:21Z</updated>

		<summary type="html">&lt;p&gt;Lineprinter: /* Информационный граф */&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;2n^3&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;11n-16&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;n^2&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
Основные авторы описания: [[Участник:Frolov|А.В.Фролов]], [[Участник:VadimVV|Вад.В.Воеводин]] ([[#Описание локальности данных и вычислений|раздел 2.2]])&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; хранится и используется не в явном виде, а в виде произведения матриц вращения. Каждая из матриц вращения (Гивенса)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&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;QR&amp;lt;/math&amp;gt;-разложение матрицы &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; может быть использовано в разных целях: как для решения СЛАУ вида &amp;lt;math&amp;gt;Ax = b&amp;lt;/math&amp;gt;, так и в качестве одного из шагов так называемого &amp;lt;math&amp;gt;QR&amp;lt;/math&amp;gt;-алгоритма нахождения собственных чисел матрицы. &lt;br /&gt;
&lt;br /&gt;
На каждом шаге алгоритма две строки преобразуемой матрицы подвергаются преобразованию вращения. При этом параметр такого преобразования подбирается так, чтобы один из поддиагональных элементов преобразуемой матрицы стал нулевым. Сначала в нуль последовательно обращаются элементы 1-го столбца, затем 2-го, и т.д. до n-1-го, после чего получившаяся матрица - это и есть матрица &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt;. &lt;br /&gt;
Сам шаг алгоритма распадается на две части: подбор параметра вращения и выполнение вращения над двумя строками матрицы. Поскольку слева от &amp;quot;рабочего&amp;quot; столбца элементы вращаемых строк равны 0, то там выполнять вращение не нужно. Вращение элементов строк в текущем столбце выполняется одновременно с подбором параметра вращения. Таким образом, вторая часть шага заключается в выполнении вращения двумерных векторов, составленных из элементов вращаемых строк в столбцах справа от &amp;quot;рабочего&amp;quot;. Каждое такое вращение эквивалентно перемножению двух комплексных чисел (или в сумме 4 умножениям, 1 сложению и 1 вычитанию действительных), одно из которых имеет модуль 1.  &lt;br /&gt;
Подбор параметра вращения по двум элементам &amp;quot;рабочего&amp;quot; столбца является более сложной операцией, что связано в том числе и с необходимостью минимизации влияния ошибок округления. Обычно для хранения матрицы вращения используется тангенс половинного угла &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, с которым простыми формулами (т.н. &amp;quot;боевыми формулами тригонометрии&amp;quot;) связаны косинус &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; и синус &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; самого угла:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;c = (1 - t^2)/(1 + t^2), s = 2t/(1 + t^2)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Именно &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; обычно и хранится в соответствующем элементе массива.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Для получения &amp;lt;math&amp;gt;QR&amp;lt;/math&amp;gt;-разложения квадратной матрицы &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; последнюю приводят к правой треугольной (&amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; - от слова right) последовательностью умножений её слева на матрицы вращения &amp;lt;math&amp;gt;T_{1 2}, T_{1 3}, ..., T_{1 n}, T_{2 3}, T_{2 4}, ..., T_{2 n}, ... , T_{n-2 n}, T_{n-1 n}&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Каждая из матриц &amp;lt;math&amp;gt;T_{i j}&amp;lt;/math&amp;gt; задаёт вращение в 2-мерном подпространстве, связанном с &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-й и &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;-й компонентами столбцов, остальные компоненты не меняются. При этом вращение подбирается так, чтобы элемент новой матрицы в &amp;lt;math&amp;gt;j&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;R=T_{n-1 n}T_{n-2 n}T_{n-2 n-1}...T_{1 3}T_{1 2}A&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Так как матрицы вращения унитарны, то естественно получается &amp;lt;math&amp;gt;Q=(T_{n-1 n}T_{n-2 n}T_{n-2 n-1}...T_{1 3}T_{1 2})^* =T_{1 2}^* T_{1 3}^* ...T_{1 n}^* T_{2 3}^* T_{2 4}^* ...T_{2 n}^* ...T_{n-2 n}^* T_{n-1 n}^*&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;A=QR&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
В вещественном случае матрицы вращения ортогональны и &amp;lt;math&amp;gt;Q=(T_{n-1 n}T_{n-2 n}T_{n-2 n-1}...T_{1 3}T_{1 2})^T =T_{1 2}^T T_{1 3}^T ...T_{1 n}^T T_{2 3}^T T_{2 4}^T ...T_{2 n}^T ...T_{n-2 n}^T T_{n-1 n}^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для завершения математического описания остаётся записать вычисление&amp;lt;ref name=&amp;quot;VOLA&amp;quot;&amp;gt;Воеводин В.В. Вычислительные основы линейной алгебры. М.: Наука, 1977.&amp;lt;/ref&amp;gt; матрицы вращения &amp;lt;math&amp;gt;T_{i j}&amp;lt;/math&amp;gt; и формулы её умножения на текущую промежуточную матрицу.&lt;br /&gt;
&lt;br /&gt;
Пусть в позиции &amp;lt;math&amp;gt;(i,i)&amp;lt;/math&amp;gt; преобразуемой матрицы стоит число &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt;, а в позиции &amp;lt;math&amp;gt;(i,j)&amp;lt;/math&amp;gt; - число &amp;lt;math&amp;gt;y&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Тогда для минимизации влияния ошибок округления сначала вычисляется равномерная норма вектора &amp;lt;math&amp;gt;z = max (|x|,|y|)&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Если она равна 0, то поворот не требуется: &amp;lt;math&amp;gt;t=s=0, c=1&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;z=|x|&amp;lt;/math&amp;gt;, то вычисляем &amp;lt;math&amp;gt;y_1=y/x&amp;lt;/math&amp;gt; и далее &amp;lt;math&amp;gt;c = \frac {1}{\sqrt{1+y_1^2}}&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;s=-c y_1&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;t=\frac {1-\sqrt{1+y_1^2}}{y_1}&amp;lt;/math&amp;gt;, а в позиции &amp;lt;math&amp;gt;(i,i)&amp;lt;/math&amp;gt; после поворота будет число &amp;lt;math&amp;gt;x \sqrt{1+y_1^2}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Если же &amp;lt;math&amp;gt;z=|y|&amp;lt;/math&amp;gt;, то вычисляем &amp;lt;math&amp;gt;x_1=x/y&amp;lt;/math&amp;gt; и далее &amp;lt;math&amp;gt;t=x_1 - x_1^2 sign(x_1)&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;s=\frac{sign(x_1)}{\sqrt{1+x_1^2}}&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;c = s x_1&amp;lt;/math&amp;gt;, а в позиции &amp;lt;math&amp;gt;(i,i)&amp;lt;/math&amp;gt; после поворота будет число &amp;lt;math&amp;gt;y \sqrt{1+x_1^2} sign(x)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
После получения элементов &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; матрицы вращения &amp;lt;math&amp;gt;T_{i j}&amp;lt;/math&amp;gt;  запись преобразования для каждого столбца правее &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го выглядит просто. Если в &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;-м столбце в позиции c номером &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; стоит x, а в позиции с номером &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; стоит y, то их новые значения будут &amp;lt;math&amp;gt;cx - sy&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;sx + cy&amp;lt;/math&amp;gt;, соответственно, как если бы комплексное число с действительной частью &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; и мнимой &amp;lt;math&amp;gt;y&amp;lt;/math&amp;gt; умножили на комплексное число &amp;lt;math&amp;gt;(c,s)&amp;lt;/math&amp;gt;.&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;
Операции вычисления параметров вращений образуют из себя треугольник на двумерной сетке, операции двумерных вращений - пирамиду на трёхмерной сетке.&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;(j, i)&amp;lt;/math&amp;gt; состоит из двух шагов: &lt;br /&gt;
а) вычисление параметров матрицы вращения &amp;lt;math&amp;gt;T_{ij}&amp;lt;/math&amp;gt; таких, чтобы обнулился элемент в позиции &amp;lt;math&amp;gt;(j, i)&amp;lt;/math&amp;gt;;&lt;br /&gt;
б) умножение слева матрицы вращения &amp;lt;math&amp;gt;T_{ij}&amp;lt;/math&amp;gt; на текущую версию матрицы.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В последовательной версии основная сложность алгоритма определяется прежде всего массовыми операциями вращения. Они, если не учитывать возможную разреженность, составляют (в главном члене) &amp;lt;math&amp;gt;n^3/3&amp;lt;/math&amp;gt; операций комплексного умножения или, если не прибегать к ухищрениям, &amp;lt;math&amp;gt;4n^3/3&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;
Макрограф алгоритма изображён на рисунке 1, графы макровершин - на последующих.&lt;br /&gt;
&lt;br /&gt;
[[file:QRGivens2.png|thumb|center|600px|Рисунок 1. Граф алгоритма без отображения входных и выходных данных. n=4. F1 - операция вычисления параметров поворота, F2 - выполнение поворота 2-мерного вектора (эквивалентно операции умножения двух комплексных чисел).]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;thumb&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;thumbinner&amp;quot; style=&amp;quot;width:{{#expr: 3 * (400 + 35) + 4 * (3 - 1) + 8}}px&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;gallery widths=400px heights=400px&amp;gt;&lt;br /&gt;
File:F1-full.png|Рисунок 2. Выбор метода вычисления параметров поворота в вершинах F1.&lt;br /&gt;
File:Givens F1!=.png|Рисунок 3. Вычисление параметров поворота для различных x и y в вершине V2.&lt;br /&gt;
File:Givens F1==.png|Рисунок 4. Вычисление параметров поворота для одинаковых x и у в вершине V1.&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;thumb&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;thumbinner&amp;quot; style=&amp;quot;width:{{#expr: 3 * (400 + 35) + 4 * (3 - 1) + 8}}px&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;gallery widths=400px heights=300px&amp;gt;&lt;br /&gt;
File:Givens F1=0.png|Рисунок 5. Вычисление параметров поворота для нулевых x и у в вершине V0.&lt;br /&gt;
File:Povorot-complexmult.png|Рисунок 6. Внутренний граф вершин F2 с входными и выходными параметрами: (u,v) = (c,s)(x,y)&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;tabs&amp;gt;&lt;br /&gt;
&amp;lt;tab name=&amp;quot;n=3&amp;quot;&amp;gt;&lt;br /&gt;
{{#widget:Algoviewer&lt;br /&gt;
|url=Givens/giv3/Algo_view_givens3.html&lt;br /&gt;
|width=1300&lt;br /&gt;
|height=800&lt;br /&gt;
|border=1&lt;br /&gt;
}}&lt;br /&gt;
&amp;lt;/tab&amp;gt;&lt;br /&gt;
&amp;lt;tab name=&amp;quot;n=4&amp;quot;&amp;gt;&lt;br /&gt;
{{#widget:Algoviewer&lt;br /&gt;
|url=Givens/giv4/Algo_view_givens4.html&lt;br /&gt;
|width=1300&lt;br /&gt;
|height=800&lt;br /&gt;
|border=1&lt;br /&gt;
}}&lt;br /&gt;
&amp;lt;/tab&amp;gt;&lt;br /&gt;
&amp;lt;/tabs&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;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;
Как видно из описания подграфов, макровершина вычисления параметров поворота F1 в графе намного &amp;quot;весомее&amp;quot; вершины поворота F2.&lt;br /&gt;
А именно, в макровершине поворота критический путь - всего-навсего одно умножение (их 4, но все можно выполнить параллельно) и одно сложение/вычитание (их 2, но они тоже параллельны). Макровершина же вычисления параметров даёт в худшем случае критический путь, в котором одно вычисление квадратного корня, и по две операции деления, умножения, сложения/вычитания. &lt;br /&gt;
&lt;br /&gt;
Поэтому по грубой оценке критический путь в основном будет идти через &amp;lt;math&amp;gt;2n-3&amp;lt;/math&amp;gt; макровершины вычисления параметров F1, а также через &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; макровершин поворотов. В сумме это даёт критический путь, проходящий по &amp;lt;math&amp;gt;2n-3&amp;lt;/math&amp;gt; операциям вычисления квадратного корня, &amp;lt;math&amp;gt;4n-6&amp;lt;/math&amp;gt; делениям, по &amp;lt;math&amp;gt;5n-7&amp;lt;/math&amp;gt; умножениям и сложениям/вычитаниям. В макрографе, представленном на рисунке, один из критических путей может быть представлен, например, как прохождение по &amp;quot;верхней линии&amp;quot; вершин F1 (это &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; штук) и затем попеременное выполнение F2 и F1 (&amp;lt;math&amp;gt;n-2&amp;lt;/math&amp;gt; раза), после чего ещё одно выполнение F2.&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;
&#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;t_{ij}&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;
=== Свойства алгоритма ===&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;
Вычислительная погрешность в методе Гивенса (вращений) растет &#039;&#039;линейно&#039;&#039;, как и в методе отражений (Хаусхолдера).&lt;br /&gt;
&lt;br /&gt;
== Программная реализация ==&lt;br /&gt;
&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В простейшем варианте метод Гивенса (вращений) QR-разложения квадратной вещественной матрицы на Фортране можно записать так:&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 J = I+1, N&lt;br /&gt;
                    CALL PARAMS (A(I,I), A(J,I), C, S)&lt;br /&gt;
                    DO K = I+1, N&lt;br /&gt;
                       CALL ROT2D (C, S, A(I,K), A(J,K))&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;
&lt;br /&gt;
Подпрограмма вращения ROT2D может быть записана, если операции с комплексными числами реализованы в трансляторе грамотно, так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;fortran&amp;quot;&amp;gt;&lt;br /&gt;
	SUBROUTINE ROT2D (C, S, X, Y)&lt;br /&gt;
        COMPLEX Z&lt;br /&gt;
        REAL ZZ(2)&lt;br /&gt;
        EQUIVALENCE Z, ZZ&lt;br /&gt;
        Z = CMPLX(C, S)*CMPLX(X,Y)&lt;br /&gt;
        X = ZZ(1)&lt;br /&gt;
        Y = ZZ(2)&lt;br /&gt;
        RETURN&lt;br /&gt;
        END&lt;br /&gt;
&amp;lt;/source&amp;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;
	SUBROUTINE ROT2D (C, S, X, Y)&lt;br /&gt;
        ZZ = C*X - S*Y&lt;br /&gt;
        Y = S*X + C*Y&lt;br /&gt;
        X = ZZ&lt;br /&gt;
        RETURN&lt;br /&gt;
        END&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;
&amp;lt;source lang=&amp;quot;fortran&amp;quot;&amp;gt;&lt;br /&gt;
	SUBROUTINE PARAMS (X, Y, C, S)&lt;br /&gt;
        Z = MAX (ABS(X), ABS(Y))&lt;br /&gt;
        IF (Z.EQ.0.) THEN&lt;br /&gt;
C OR (Z.LE.OMEGA) WHERE OMEGA - COMPUTER ZERO&lt;br /&gt;
            C = 1.&lt;br /&gt;
            S = 0.&lt;br /&gt;
        ELSE IF (Z.EQ.ABS(X))&lt;br /&gt;
            R = Y/X&lt;br /&gt;
            RR = R*R&lt;br /&gt;
            RR2 = SQRT(1+RR)&lt;br /&gt;
            X = X*RR2&lt;br /&gt;
            Y = (1-RR2)/R&lt;br /&gt;
            C = 1./RR2&lt;br /&gt;
            S = -C*R&lt;br /&gt;
        ELSE&lt;br /&gt;
            R = X/Y&lt;br /&gt;
            RR = R*R&lt;br /&gt;
            RR2 = SQRT (1+RR)&lt;br /&gt;
            X = SIGN(Y, X)*RR&lt;br /&gt;
            Y = R - SIGN(RR,R)&lt;br /&gt;
            S = SIGN(1./RR2, R) &lt;br /&gt;
            C = S*R  &lt;br /&gt;
        END IF&lt;br /&gt;
        RETURN&lt;br /&gt;
        END&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В приведенной реализации параметр матрицы вращения t записывается туда, где освобождается место (элемент преобразованной матрицы c этими индексами заведомо равен нулю), что позволяет легко восстанавливать матрицы вращения при надобности.&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
==== Локальность реализации алгоритма ====&lt;br /&gt;
===== Структура обращений в память и качественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
[[file:givens_1.png|thumb|center|700px|Рисунок 7. Метод Гивенса (вращений) QR-разложения квадратной матрицы (вещественный вариант). Общий профиль обращений в память]]&lt;br /&gt;
&lt;br /&gt;
На рис. 7 представлен профиль обращений в память для реализации вещественного варианта метода Гивенса (вращений) QR-разложения квадратной матрицы. Данный профиль состоит из обращений к одному двумерному массиву, в котором хранится  матрица значений. Из данного графика можно увидеть наличие однотипных итераций, из которых профиль и состоит. &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-я итерация затрагивает элементы массива, начиная с &amp;lt;math&amp;gt;(i-1)*k&amp;lt;/math&amp;gt;, то есть после каждой итерации первые &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; элементов (значение &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; нам пока неизвестно) больше не рассматриваются. Каждая итерация состоит из двух параллельно выполняемых частей – последовательного перебора всех элементов, начиная с &amp;lt;math&amp;gt;(i-1)*k&amp;lt;/math&amp;gt;, и активного использования первых &amp;lt;math&amp;gt;i*k&amp;lt;/math&amp;gt; элементов. &lt;br /&gt;
&lt;br /&gt;
Исходя из общей картины, можно сказать, что локальность данного профиля достаточно высока – обращения к близко расположенным в памяти элементам расположены в программе рядом, а также есть хорошо локализованные части с частым повторным использованием данных. Однако, чтобы убедиться в этом, необходимо более детальное рассмотрение.&lt;br /&gt;
&lt;br /&gt;
На рис. 8 представлен фрагмент общего профиля, выделенный зеленым цветом. Можно увидеть, что обе параллельно выполняемые части на каждой итерации состоят из небольших участков, похожих на обычный последовательный перебор. Явно видна регулярная структура – все участки одинакового размера и расположены на одинаковом расстоянии. &lt;br /&gt;
&lt;br /&gt;
[[file:givens_2.png|thumb|center|700px|Рисунок 8. Выделенный фрагмент общего профиля]]&lt;br /&gt;
&lt;br /&gt;
Рассмотрим фрагмент на рис. 8 более подробно (рис. 9). Здесь уже видны отдельные обращения, и теперь можно с уверенностью сказать, что каждый участок представляет собой последовательный перебор небольшого числа элементов. При этом в верхней части на каждом новом участке перебираются новые элементы, а в нижней – одни и те же данные. Поэтому в целом подобный фрагмент обладает высокой пространственной локальностью (обе части состоят из последовательных переборов), но средней временной локальностью (верхняя часть – очень низкой, нижняя – очень высокой локальностью).&lt;br /&gt;
&lt;br /&gt;
[[file:givens_3.png|thumb|center|700px|Рисунок 9. Фрагмент общего профиля, дальнейшее приближение]]&lt;br /&gt;
&lt;br /&gt;
Общий профиль состоит из набора таких фрагментов. При этом, согласно рис. 6 и 7, данные в этих фрагментах используются повторно на разных итерациях, поэтому общий профиль, скорее всего, обладает более высокой временной локальностью. Поэтому в целом можно сказать, что обращения в память в данной программе обладают высокой пространственной локальностью и достаточно неплохой временной локальностью.&lt;br /&gt;
&lt;br /&gt;
===== Количественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
Основной фрагмент реализации, с помощью которого были получены количественные оценки, приведен [http://git.parallel.ru/shvets.pavel.srcc/locality/blob/master/benchmarks/givens/givens.h здесь] (функция Kernel). Условия запуска описаны [http://git.parallel.ru/shvets.pavel.srcc/locality/blob/master/README.md здесь].&lt;br /&gt;
&lt;br /&gt;
Первая оценка выполняется на основе характеристики daps, которая оценивает число выполненных обращений (чтений и записей) в память в секунду. Данная характеристика является аналогом оценки flops применительно к работе с памятью и является в большей степени оценкой производительности взаимодействия с памятью, чем оценкой локальности. Однако она служит хорошим источником информации, в том числе для сравнения с результатами по следующей характеристике cvg.&lt;br /&gt;
&lt;br /&gt;
На рисунке 10 приведены значения daps для реализаций распространенных алгоритмов, отсортированные по возрастанию (чем больше daps, тем в общем случае выше производительность). Можно увидеть, что производительность работы с памятью достаточно высока для этой программы, что соответствует нашим представлениям согласно изученной локальности. &lt;br /&gt;
&lt;br /&gt;
[[file:givens_daps.png|thumb|center|700px|Рисунок 10. Сравнение значений оценки daps]]&lt;br /&gt;
&lt;br /&gt;
Вторая характеристика – cvg – предназначена для получения более машинно-независимой оценки локальности. Она определяет, насколько часто в программе необходимо подтягивать данные в кэш-память. Соответственно, чем меньше значение cvg, тем реже это нужно делать и тем лучше локальность. &lt;br /&gt;
&lt;br /&gt;
На рисунке 11 приведены значения cvg для того же набора реализаций, отсортированные по убыванию (чем меньше cvg, тем в общем случае выше локальность). Можно увидеть, что, как и daps, cvg показывает достаточно хороший результат, тем самым подтверждая качественную оценку, описанную выше.&lt;br /&gt;
&lt;br /&gt;
[[file:givens_cvg.png|thumb|center|700px|Рисунок 11. Сравнение значений оценки cvg]]&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
На суперкомпьютерах кластерной (или подобной) архитектуры вполне возможна блочная нарезка алгоритма (с использованием координатных обобщённых развёрток графа алгоритма) и его распределение по разным узлам (MPI). В то же время внутренний параллелизм в блоках будет реализовывать частично многоядерные возможности узлов (OpenMP), а частично даже суперскалярность ядер. На такую реализацию следует нацелиться, исходя из структуры графа алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
==== Масштабируемость алгоритма ====&lt;br /&gt;
&lt;br /&gt;
Несмотря на то, что на данный момент это пока нельзя проверить на реализациях, структура графа такова, что позволяет надеяться на получение хорошей масштабируемости. За это говорит не только хороший (линейный) показатель критического пути графа, но и хорошая координатная структура обобщённых развёрток графа, что даёт возможность хорошей блочной нарезки графа алгоритма. &lt;br /&gt;
&lt;br /&gt;
==== Масштабируемость реализации алгоритма ====&lt;br /&gt;
&lt;br /&gt;
На данный момент проверка хорошей масштабируемости реализаций затруднена по естественным [[#Существующие реализации алгоритма|причинам, описанным ниже]]. После появления надлежащей реализации авторы пополнят данный раздел.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
На данный момент получение динамических характеристик и показателей эффективности реализаций затруднено по естественным [[#Существующие реализации алгоритма|причинам, описанным ниже]]. После появления надлежащей реализации авторы пополнят данный раздел.&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;ref&amp;gt;Фролов А.В., Антонов А.С., Воеводин Вл.В., Теплов А.М. Сопоставление разных методов решения одной задачи по методике проекта Algowiki // Подана на конференцию &amp;quot;Параллельные вычислительные технологии (ПаВТ&#039;2016)&amp;quot;.&amp;lt;/ref&amp;gt;. Несмотря на то, что в большинстве старых &amp;quot;последовательных&amp;quot; пакетов как правило наличествует и программа QR-разложения методом Гивенса, в новых, рассчитанных на параллельное исполнение, пакетах QR-разложение осуществляют только с помощью [[Метод Хаусхолдера (отражений) QR-разложения квадратной матрицы, вещественный вариант|метода Хаусхолдера (отражений)]], который именно по параллельным свойствам проигрывает методу Гивенса. Мы связываем это с тем, что [[Метод Хаусхолдера (отражений) QR-разложения квадратной матрицы, вещественный вариант|метод Хаусхолдера (отражений)]] легко записывается через BLAS-процедуры и не требует переупорядочивания обычного хода циклов (разве что при введении блочности, но это не &amp;quot;косая&amp;quot;, а координатная нарезка циклов), а вот для параллельной реализации метода Гивенса (вращений) не избежать перезаписи циклов, поскольку максимально параллельный вариант в нём требует применения [[Глоссарий#Скошенный параллелизм|cкошенного (в узком понимании этого термина) параллелизма]]. Поэтому временно описание масштабируемости реализаций данного алгоритма невозможно - пока нет материала для исследования этой масштабируемости.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Законченные статьи]]&lt;br /&gt;
[[Категория:Разложения матриц]]&lt;br /&gt;
&lt;br /&gt;
[[En: Givens method]]&lt;/div&gt;</summary>
		<author><name>Lineprinter</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9F%D0%B5%D1%80%D0%B5%D0%BC%D0%BD%D0%BE%D0%B6%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BF%D0%BB%D0%BE%D1%82%D0%BD%D1%8B%D1%85_%D0%BD%D0%B5%D0%BE%D1%81%D0%BE%D0%B1%D0%B5%D0%BD%D0%BD%D1%8B%D1%85_%D0%BC%D0%B0%D1%82%D1%80%D0%B8%D1%86_(%D0%BF%D0%BE%D1%81%D0%BB%D0%B5%D0%B4%D0%BE%D0%B2%D0%B0%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D1%8B%D0%B9_%D0%B2%D0%B5%D1%89%D0%B5%D1%81%D1%82%D0%B2%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%B2%D0%B0%D1%80%D0%B8%D0%B0%D0%BD%D1%82)&amp;diff=22540</id>
		<title>Перемножение плотных неособенных матриц (последовательный вещественный вариант)</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9F%D0%B5%D1%80%D0%B5%D0%BC%D0%BD%D0%BE%D0%B6%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BF%D0%BB%D0%BE%D1%82%D0%BD%D1%8B%D1%85_%D0%BD%D0%B5%D0%BE%D1%81%D0%BE%D0%B1%D0%B5%D0%BD%D0%BD%D1%8B%D1%85_%D0%BC%D0%B0%D1%82%D1%80%D0%B8%D1%86_(%D0%BF%D0%BE%D1%81%D0%BB%D0%B5%D0%B4%D0%BE%D0%B2%D0%B0%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D1%8B%D0%B9_%D0%B2%D0%B5%D1%89%D0%B5%D1%81%D1%82%D0%B2%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%B2%D0%B0%D1%80%D0%B8%D0%B0%D0%BD%D1%82)&amp;diff=22540"/>
		<updated>2017-04-20T06:54:35Z</updated>

		<summary type="html">&lt;p&gt;Lineprinter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Основные авторы описания: [[Участник:Frolov|А.В.Фролов]], [[Участник:VadimVV|Вад.В.Воеводин]] ([[#Описание локальности данных и вычислений|раздел 2.2]]), [[Участник:Teplov|А.М.Теплов]] (раздел [[#Масштабируемость алгоритма и его реализации|2.4]])&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Перемножение матриц&#039;&#039;&#039; - одна из базовых задач в алгоритмах линейной алгебры, широко применяется в большом количестве разных методов.&lt;br /&gt;
Здесь мы рассмотрим умножение &amp;lt;math&amp;gt;C = AB&amp;lt;/math&amp;gt;&amp;amp;nbsp; плотных неособенных матриц (последовательный вещественный вариант), то есть тот вариант, где никак не используются ни специальный вид матрицы, ни ассоциативные свойства операции сложения&amp;lt;ref&amp;gt;В.В.Воеводин, Ю.А.Кузнецов. Матрицы и вычисления. М.: Наука, 1984.&amp;lt;/ref&amp;gt;.&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;), плотная матрица &amp;lt;math&amp;gt;B&amp;lt;/math&amp;gt; (элементы &amp;lt;math&amp;gt;b_{ij}&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Вычисляемые данные: плотная матрица &amp;lt;math&amp;gt;C&amp;lt;/math&amp;gt; (элементы &amp;lt;math&amp;gt;c_{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;
c_{ij} = \sum_{k = 1}^{n} a_{ik} b_{kj}, \quad i \in [1, m], \quad i \in [1, l].&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;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Вычислительное ядро перемножения плотных неособенных матриц можно составить из множественных (всего их &amp;lt;math&amp;gt;l&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;ml&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;
:&amp;lt;math&amp;gt;\sum_{k = 1}^{n} a_{ik} b_{kj}&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;ml&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;
:&amp;lt;math&amp;gt;\sum_{k = 1}^{n} a_{ik} b_{kj}&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;i&amp;lt;/math&amp;gt; от &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; до &amp;lt;math&amp;gt;m&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;l&amp;lt;/math&amp;gt; выполняются&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;c_{ij} = \sum_{k = 1}^{n} a_{ik} b_{kj}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Особо отметим, что вычисления сумм вида &amp;lt;math&amp;gt;\sum_{k = 1}^{n} a_{ik} b_{kj}&amp;lt;/math&amp;gt; производят в режиме накопления прибавлением к текущему (временному) значению вычисляемого элемента матрицы &amp;lt;math&amp;gt;c_{ij}&amp;lt;/math&amp;gt; произведений &amp;lt;math&amp;gt;a_{ik} b_{kj}&amp;lt;/math&amp;gt; для &amp;lt;math&amp;gt;k&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;, &#039;&#039;&#039;c возрастанием&#039;&#039;&#039; &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;, вначале все элементы инициализируются нулями. При суммировании &amp;quot;по убыванию&amp;quot; общая схема принципиально не отличается и потому нами не рассматривается. Другие порядки выполнения суммирования приводят к изменению параллельных свойств алгоритма и будут рассматриваться нами в отдельных описаниях.&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=n=l&amp;lt;/math&amp;gt;) в последовательном (наиболее быстром) варианте требуется:&lt;br /&gt;
	&lt;br /&gt;
* по &amp;lt;math&amp;gt;n^3&amp;lt;/math&amp;gt; умножений и сложений.&lt;br /&gt;
&lt;br /&gt;
Для умножения матрицы размером &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; строк на &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; столбцов на матрицу размером &amp;lt;math&amp;gt;m&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;mnl&amp;lt;/math&amp;gt; умножений и сложений.&lt;br /&gt;
 &lt;br /&gt;
При этом использование режима накопления требует совершения умножений и сложений в режиме двойной точности (или использования функции вроде DPROD в Фортране), что ещё больше увеличивает затраты во времени, требуемом для выполнения умножения матриц.&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;
Опишем [[глоссарий#Граф алгоритма|граф алгоритма]] как аналитически, так и в виде рисунка.&lt;br /&gt;
&lt;br /&gt;
Граф алгоритма умножения плотных матриц состоит из одной группы вершин, расположенной в целочисленных узлах трёхмерной области, соответствующая ей операция  &amp;lt;math&amp;gt;a+bc&amp;lt;/math&amp;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;1&amp;lt;/math&amp;gt; до &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt;, принимая все целочисленные значения;&lt;br /&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;l&amp;lt;/math&amp;gt;, принимая все целочисленные значения;&lt;br /&gt;
* &amp;lt;math&amp;gt;k&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;
*&amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt;:&lt;br /&gt;
** при &amp;lt;math&amp;gt;k = 1&amp;lt;/math&amp;gt; константа &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;; &lt;br /&gt;
** при &amp;lt;math&amp;gt;k &amp;gt; 1&amp;lt;/math&amp;gt; — результат срабатывания операции, соответствующей вершине с координатами &amp;lt;math&amp;gt;i, j, k-1&amp;lt;/math&amp;gt;;&lt;br /&gt;
*&amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; — элемент &#039;&#039;входных данных&#039;&#039;, а именно  &amp;lt;math&amp;gt;a_{ik}&amp;lt;/math&amp;gt;;&lt;br /&gt;
*&amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; - элемент &#039;&#039;входных данных&#039;&#039; &amp;lt;math&amp;gt;b_{kj}&amp;lt;/math&amp;gt;; &lt;br /&gt;
&lt;br /&gt;
Результат срабатывания операции является:&lt;br /&gt;
* при &amp;lt;math&amp;gt;k &amp;lt; n&amp;lt;/math&amp;gt; - &#039;&#039;промежуточным данным&#039;&#039; алгоритма;&lt;br /&gt;
* при &amp;lt;math&amp;gt;k = n&amp;lt;/math&amp;gt; - выходным данным &amp;lt;math&amp;gt;c_{ij}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[file:Dense mtrx product.png|thumb|center|800px|Рисунок 1. Умножение плотных матриц с отображением выходных данных]]&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
Интерактивное изображение графа алгоритма без входных и выходных данных для случая перемножения двух квадратных матриц порядка 3 и 4&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
{{#widget:Algoviewer&lt;br /&gt;
|url=mat_mul/mat_mul_3/Algo_view_matrix3.html&lt;br /&gt;
|width=1300&lt;br /&gt;
|height=800&lt;br /&gt;
|border=1&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{#widget:Algoviewer&lt;br /&gt;
|url=mat_mul/mat_mul_4/Algo_view_matrix4.html&lt;br /&gt;
|width=1300&lt;br /&gt;
|height=800&lt;br /&gt;
|border=1&lt;br /&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; ярусов умножений и сложений (в каждом из ярусов — &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt; операций).&lt;br /&gt;
&lt;br /&gt;
Для умножения матрицы размером &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; строк на &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; столбцов на матрицу размером &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; строк на &amp;lt;math&amp;gt;l&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;ml&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;
=== Входные и выходные данные алгоритма ===&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;), матрица &amp;lt;math&amp;gt;B&amp;lt;/math&amp;gt; (элементы &amp;lt;math&amp;gt;b_{ij}&amp;lt;/math&amp;gt;)).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;mn+nl&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: матрица &amp;lt;math&amp;gt;C&amp;lt;/math&amp;gt; (элементы &amp;lt;math&amp;gt;c_{ij}&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных&#039;&#039;&#039;: &amp;lt;math&amp;gt;ml&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; (отношение кубической или трилинейной к линейной). &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;
В простейшем варианте алгоритм умножения матриц на Фортране можно записать так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;fortran&amp;quot;&amp;gt;&lt;br /&gt;
         &lt;br /&gt;
	DO  I = 1, M&lt;br /&gt;
        DO  J = 1, L&lt;br /&gt;
		S = 0.&lt;br /&gt;
		DO  K = 1, N&lt;br /&gt;
			S = S + DPROD(A(I,K), B(K,J))&lt;br /&gt;
		END DO	&lt;br /&gt;
	        C(I, J) = S&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;
=== Локальность данных и вычислений ===&lt;br /&gt;
==== Локальность реализации алгоритма ====&lt;br /&gt;
===== Структура обращений в память и качественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
[[file:matrmult 1.PNG|thumb|center|700px|Рисунок 2. Профили обращений для 6 вариантов перемножения матриц]]&lt;br /&gt;
&lt;br /&gt;
На рис.2 представлены 6 профилей обращений для различных вариантов классического перемножения матриц (в зависимости от выбранного порядка циклов). На каждом профиле хорошо выделяются фрагменты профилей для 3-х массивов, используемых в программе. При этом порядок обращений к разным массивам во всех 6 вариантах один и тот же; т.е. взаимодействие между  массивами везде устроен одинаково. В этом случае отличия в локальности задаются внутренним строением фрагментов профиля для каждого массива в отдельности.&lt;br /&gt;
&lt;br /&gt;
Исходя из исходного кода, можно увидеть, что всего встречается 6 разных видов фрагментов для отдельных массивов (эти виды выделены на рис.2 зеленым). Стоит сделать два уточнения: 1) профиль для результирующего массива С всегда в 2 раза больше, поскольку к элементам этого массива всегда происходят по два обращения подряд; 2) если во внутреннем цикле используется элементы массива, то обращение к этому элементу выносится за пределы внутреннего цикла (в цикле используется скалярная переменная вместо него).&lt;br /&gt;
&lt;br /&gt;
Также заранее отметим, что рисунки по профилям получаются следующим образом: строится рисунок по общему профилю, после чего из него оставляется только часть для рассматриваемого массива. Доля обращений к одному массиву может меняться, поэтому и частота обращений на каждом рисунке может в значительной степени отличаться.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим подробнее каждый из данных 6 фрагментов на примере массива С.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Фрагмент 1.&#039;&#039;&#039; На рис.3 показано начало фрагмента 1 (здесь и далее начало фрагмента соответствует выделенной оранжевой области на рис.2). Можно увидеть, что данный фрагмент устроен достаточно просто: выполняется перебор некоторого блока элементов массива, затем данный перебор циклически повторяется. После этого происходит переход к следующему блоку элементов массива, и выполняется тот же перебор в цикле.&lt;br /&gt;
&lt;br /&gt;
Если рассмотреть фрагмент более подробно (рис.4), можно увидеть, что каждый перебор является на самом деле последовательным перебором, при этом к каждому элементу происходит обращение дважды.&lt;br /&gt;
&lt;br /&gt;
В результате можно сделать вывод, что фрагмент 1 обладает высокой пространственной локальностью (из-за последовательного перебора и последовательной смены блоков перебора), а также высокой временно́й локальностью (из-за двойного обращения к элементам, а также сгруппированности всех обращений к одному элементу в рамках одного блока перебора). &lt;br /&gt;
&lt;br /&gt;
[[file:matrmult 2.PNG|thumb|center|700px|Рисунок 3. Начало фрагмента 1]]&lt;br /&gt;
[[file:matrmult 3.PNG|thumb|center|700px|Рисунок 4. Начало фрагмента 1, первые 3 итерации]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Фрагмент 2.&#039;&#039;&#039; На рис.5 представлено начало фрагмента 2. Видно, что данный фрагмент также представляет собой перебор элементов массива в цикле, однако в данном случае на каждой итерации цикла перебираются сразу все элементы массива, а не только элементы одного блока. При более подробном рассмотрении итерации (рис.6) видно, что здесь также происходит именно последовательный перебор, и также к каждому элементу обращение происходит дважды (поскольку это массив С).&lt;br /&gt;
&lt;br /&gt;
[[file:matrmult 4.PNG|thumb|center|700px|Рисунок 5. Начало фрагмента 2]]&lt;br /&gt;
[[file:matrmult 5.PNG|thumb|center|700px|Рисунок 6. Начало фрагмента 2, часть первой итерации]]&lt;br /&gt;
&lt;br /&gt;
Такой фрагмент также обладает высокой пространственной локальностью, однако временна́я локальность несколько ниже, чем во фрагменте 1. Это связано с тем, что на каждой итерации цикла перебираются все элементы массива, а не только отдельный блок, т.е. повторные обращения происходят реже.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Фрагмент 3.&#039;&#039;&#039; Начало данного фрагмента представлено на рисунке 7. Данный профиль является последовательным перебором всех элементов массива, однако более подробное рассмотрение показывает, что к каждому элементу происходит обращение дважды, при этом между обращениями происходит достаточно много других обращений. Это связано с указанным ранее замечанием касательно вынесения повторяющегося обращения за предел внутреннего цикла.&lt;br /&gt;
&lt;br /&gt;
[[file:matrmult 6.PNG|thumb|center|700px|Рисунок 7. Начало фрагмента 3]]&lt;br /&gt;
[[file:matrmult 7.PNG|thumb|center|700px|Рисунок 8. Начало фрагмента 3, первые несколько обращений]]&lt;br /&gt;
&lt;br /&gt;
Такой фрагмент также обладает высокой пространственной локальностью, однако очень низкой временно́й, поскольку к каждому элементу обращение происходит только два раза.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Фрагмент 4.&#039;&#039;&#039; По началу фрагмента 4 видно, что профиль снова состоит из перебора всех элементов массива, однако на этот раз перебор не последовательный, а с некоторым шагом. Также можно заметить, что на каждой новой итерации перебор с шагом начинается с элемента, индекс которого немного больше, чем на предыдущей итерации. В данном случае нет нужды рассматривать профиль более подробно, поскольку из данного рисунка можно получить всю необходимую информацию.&lt;br /&gt;
&lt;br /&gt;
По сравнению с предыдущим фрагментом, данный профиль обладает более низкой пространственной локальностью, поскольку перебор элементов идет с шагом, и также очень низкой временно́й локальностью, поскольку на каждой итерации происходят обращения к новым элементам.&lt;br /&gt;
&lt;br /&gt;
[[file:matrmult 8.PNG|thumb|center|700px|Рисунок 9. Начало фрагмента 4]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Фрагмент 5.&#039;&#039;&#039; В отличие от предыдущих фрагментов, исходя из визуализации начала данного фрагмента (рис.9), сложно сделать выводы относительно структуры обращения в память. Однако более подробное рассмотрение (рис.10) показывает, что данный профиль практически идентичен профилю предыдущего фрагмента. Более того, фрагмент 5 на самом деле состоит из итерационно повторяющихся фрагментов 4.&lt;br /&gt;
&lt;br /&gt;
[[file:matrmult 9.PNG|thumb|center|700px|Рисунок 10. Начало фрагмента 5]]&lt;br /&gt;
[[file:matrmult 10.PNG|thumb|center|700px|Рисунок 11. Начало фрагмента 5, первые несколько итераций]]&lt;br /&gt;
&lt;br /&gt;
Данный фрагмент обладает по сравнению с фрагментом 4 практически такой же пространственной локальностью, однако более высокой временной локальностью. Причина в обоих случаях одна – в данном случае тот же набор обращений повторяется несколько раз.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Фрагмент 6.&#039;&#039;&#039; Данный фрагмент, представленный на рис.12 и рис.13, так же сильно напоминает фрагмент 5, но с одним отличием. Во фрагменте 5 несколько раз повторяется фрагмент 4, в рамках которого выполняется несколько итераций, и на каждой итерации внутри фрагмента 4 используются разные элементы (поскольку первый элемент сдвигается на 1). В случае данного фрагмента выполняются все те же итерации, но в перемешанном виде. Сначала выполняются все итерации, начинающиеся с одного и того же элемента; затем все итерации, который начинаются с элемента, чей индекс идет следующим; и т.д.&lt;br /&gt;
&lt;br /&gt;
Такой фрагмент обладает более высокой и пространственной, и временно́й локальностью по сравнению с фрагментом 5, поскольку обращения в одним и тем же элементам расположены ближе друг к другу в профиле.&lt;br /&gt;
&lt;br /&gt;
[[file:matrmult 11.PNG|thumb|center|700px|Рисунок 12. Начало фрагмента 6]]&lt;br /&gt;
[[file:matrmult 12.PNG|thumb|center|700px|Рисунок 13. Начало фрагмента 6, первые несколько итераций]]&lt;br /&gt;
&lt;br /&gt;
В итоге можно сказать, что наиболее низкой локальностью в целом обладают фрагменты 5 и 6. Фрагмент 4 также обладает низкой локальностью, однако содержит значительно меньшее число обращений, а значит, привносит меньший вклад в общий профиль обращений. Это позволяет определить, что наименее эффективными с точки зрения работы с памятью являются варианты kji и jki, поскольку каждый из них содержит фрагменты 5 и 6. Далее идут варианты ijk и jik – в них по одному такому фрагменту. Самые лучшие варианты – ikj и kij.&lt;br /&gt;
&lt;br /&gt;
===== Количественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
Основные фрагменты реализации, на основе которых были получены количественные оценки, приведены [http://git.algowiki-project.org/Voevodin/locality/blob/master/benchmarks/matrix_mult/matrix_mult.h здесь] (функция Kernel***, где *** - порядок циклов (напр., KernelIJK) ). Условия запуска описаны [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;
На рис.14 приведены значения daps для реализаций распространенных алгоритмов, отсортированные по возрастанию (чем больше daps, тем в общем случае выше производительность). Можно увидеть, что, как и предполагалось  наиболее эффективными являются варианты kij,ikj. Заметно хуже результат у вариантов jik,ijk, при этом эти варианты практически равны между собой. Самый плохой результат ожидаемо у вариантов jki,kji.&lt;br /&gt;
&lt;br /&gt;
[[file:matrmult daps ru.PNG|thumb|center|700px|Рисунок 14. Сравнение значений оценки daps]]&lt;br /&gt;
&lt;br /&gt;
Вторая характеристика – cvg – предназначена для получения более машинно-независимой оценки локальности. Она определяет, насколько часто в программе необходимо подтягивать данные в кэш-память. Соответственно, чем меньше значение cvg, тем реже это нужно делать, тем лучше локальность. &lt;br /&gt;
&lt;br /&gt;
На рис.15 приведены значения cvg для того же набора реализаций, отсортированные по убыванию (чем меньше cvg, тем в общем случае выше локальность). Относительное сравнение результатов по cvg в целом повторяет результаты daps.&lt;br /&gt;
&lt;br /&gt;
[[file:matrmult cvg.PNG|thumb|center|700px|Рисунок 15. Сравнение значений оценки cvg]]&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
==== Масштабируемость алгоритма ====&lt;br /&gt;
==== Масштабируемость реализации алгоритма ====&lt;br /&gt;
&lt;br /&gt;
[[file:Масштабируемость перемножения матриц производительность.png|thumb|center|700px|Рисунок 16. Параллельная реализация произведения матриц Максимальная производительность. ]]&lt;br /&gt;
[[file:Масштабируемость параллельной реализации перемножения матриц Эффективность.png|thumb|center|700px|Рисунок 17. Параллельная реализация произведения матриц Максимальная эффективность. ]]&lt;br /&gt;
Набор изменяемых параметров запуска реализации алгоритма и границы значений параметров алгоритма: &lt;br /&gt;
*	число процессоров [4 : 1024]&lt;br /&gt;
*	размерность матрицы [1024 : 20480]&lt;br /&gt;
Эффективность выполнения реализации алгоритма&lt;br /&gt;
*	Минимальная эффективность 4,71%&lt;br /&gt;
*	Максимальная эффективность 31,72%&lt;br /&gt;
Оценка масштабируемости&lt;br /&gt;
*	По числу процессов: -0.0436 – при увеличении числа процессов эффективность убывает достаточно интенсивно на всей рассмотренной области изменений параметров запуска. Уменьшение эффективности на рассмотренной области работы параллельной программы звязано с увеличением числа пересылок с ростом числа процессов и как следствие ростом накладных расходов на организацию вычислений. Присутствует область, на которой при увеличении числа процессов эффективность возрастает, но при дальнейшем росте продолжает снижаться. Это объясняется декомпозицей данных, при которой наступает момент, когда размер матрицы позволяет блокам укладываться в КЭШ-память. Так же это подтверждает проявление этого явления, но со смещением по числу процессов, и при увеличении вычислительной сложности задачи.&lt;br /&gt;
*	По размеру задачи: -0.0255 – при увеличении размера задачи эффективность в целом уменьшается по рассматриваемой области, хотя и менее интенсивно, чем при увеличении числа процессов. Снижение эффективности объясняется тем, что при росте вычислительной сложности существенно возрастают объемы передаваемых данных. Присутствует область возрастания эффективности, на всех рассмотренных размерах матрицы. Это объясняется тем, что при малом размере задачи данные хорошо укладываются в КЭШ память, что приводит к высокой эффективности работы приложения при малом размере задачи. При дальнейшем увеличении размера эффективность уменьшается при выходе за границы КЭШ-памяти. &lt;br /&gt;
*	По двум направлениям: -0.000968 – при рассмотрении увеличения, как вычислительной сложности, так и числа процессов по всей рассмотренной области значений уменьшается, интенсивность уменьшения эффективности не очень высока. В совокупности с тем фактом, что разница между максимальной и минимальной эффективностью на рассмотренной области значений параметров составляет почти 25% говорит о том, что уменьшение эффективности по всей области довольно равномерное, но интенсивно лишь в не очень больших участках по площади. На остальной области значений параметров изменения эффективности не столь значительны и находятся на приблизительно одном и том же уровне.&lt;br /&gt;
&lt;br /&gt;
[http://git.algowiki-project.org/Teplov/Scalability/blob/master/matrixmult/matrixmult.c Реализация алгоритма на языке C].&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Законченные статьи]]&lt;br /&gt;
[[Категория:Матричные операции]]&lt;br /&gt;
&lt;br /&gt;
[[En:Dense matrix multiplication]]&lt;/div&gt;</summary>
		<author><name>Lineprinter</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%93%D0%B8%D0%B2%D0%B5%D0%BD%D1%81%D0%B0_(%D0%B2%D1%80%D0%B0%D1%89%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=22539</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%93%D0%B8%D0%B2%D0%B5%D0%BD%D1%81%D0%B0_(%D0%B2%D1%80%D0%B0%D1%89%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=22539"/>
		<updated>2017-04-20T06:54:17Z</updated>

		<summary type="html">&lt;p&gt;Lineprinter: &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;2n^3&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;11n-16&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;n^2&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
Основные авторы описания: [[Участник:Frolov|А.В.Фролов]], [[Участник:VadimVV|Вад.В.Воеводин]] ([[#Описание локальности данных и вычислений|раздел 2.2]])&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; хранится и используется не в явном виде, а в виде произведения матриц вращения. Каждая из матриц вращения (Гивенса)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&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;QR&amp;lt;/math&amp;gt;-разложение матрицы &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; может быть использовано в разных целях: как для решения СЛАУ вида &amp;lt;math&amp;gt;Ax = b&amp;lt;/math&amp;gt;, так и в качестве одного из шагов так называемого &amp;lt;math&amp;gt;QR&amp;lt;/math&amp;gt;-алгоритма нахождения собственных чисел матрицы. &lt;br /&gt;
&lt;br /&gt;
На каждом шаге алгоритма две строки преобразуемой матрицы подвергаются преобразованию вращения. При этом параметр такого преобразования подбирается так, чтобы один из поддиагональных элементов преобразуемой матрицы стал нулевым. Сначала в нуль последовательно обращаются элементы 1-го столбца, затем 2-го, и т.д. до n-1-го, после чего получившаяся матрица - это и есть матрица &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt;. &lt;br /&gt;
Сам шаг алгоритма распадается на две части: подбор параметра вращения и выполнение вращения над двумя строками матрицы. Поскольку слева от &amp;quot;рабочего&amp;quot; столбца элементы вращаемых строк равны 0, то там выполнять вращение не нужно. Вращение элементов строк в текущем столбце выполняется одновременно с подбором параметра вращения. Таким образом, вторая часть шага заключается в выполнении вращения двумерных векторов, составленных из элементов вращаемых строк в столбцах справа от &amp;quot;рабочего&amp;quot;. Каждое такое вращение эквивалентно перемножению двух комплексных чисел (или в сумме 4 умножениям, 1 сложению и 1 вычитанию действительных), одно из которых имеет модуль 1.  &lt;br /&gt;
Подбор параметра вращения по двум элементам &amp;quot;рабочего&amp;quot; столбца является более сложной операцией, что связано в том числе и с необходимостью минимизации влияния ошибок округления. Обычно для хранения матрицы вращения используется тангенс половинного угла &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;, с которым простыми формулами (т.н. &amp;quot;боевыми формулами тригонометрии&amp;quot;) связаны косинус &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; и синус &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; самого угла:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;c = (1 - t^2)/(1 + t^2), s = 2t/(1 + t^2)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Именно &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; обычно и хранится в соответствующем элементе массива.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Для получения &amp;lt;math&amp;gt;QR&amp;lt;/math&amp;gt;-разложения квадратной матрицы &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; последнюю приводят к правой треугольной (&amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; - от слова right) последовательностью умножений её слева на матрицы вращения &amp;lt;math&amp;gt;T_{1 2}, T_{1 3}, ..., T_{1 n}, T_{2 3}, T_{2 4}, ..., T_{2 n}, ... , T_{n-2 n}, T_{n-1 n}&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Каждая из матриц &amp;lt;math&amp;gt;T_{i j}&amp;lt;/math&amp;gt; задаёт вращение в 2-мерном подпространстве, связанном с &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-й и &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;-й компонентами столбцов, остальные компоненты не меняются. При этом вращение подбирается так, чтобы элемент новой матрицы в &amp;lt;math&amp;gt;j&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;R=T_{n-1 n}T_{n-2 n}T_{n-2 n-1}...T_{1 3}T_{1 2}A&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Так как матрицы вращения унитарны, то естественно получается &amp;lt;math&amp;gt;Q=(T_{n-1 n}T_{n-2 n}T_{n-2 n-1}...T_{1 3}T_{1 2})^* =T_{1 2}^* T_{1 3}^* ...T_{1 n}^* T_{2 3}^* T_{2 4}^* ...T_{2 n}^* ...T_{n-2 n}^* T_{n-1 n}^*&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;A=QR&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
В вещественном случае матрицы вращения ортогональны и &amp;lt;math&amp;gt;Q=(T_{n-1 n}T_{n-2 n}T_{n-2 n-1}...T_{1 3}T_{1 2})^T =T_{1 2}^T T_{1 3}^T ...T_{1 n}^T T_{2 3}^T T_{2 4}^T ...T_{2 n}^T ...T_{n-2 n}^T T_{n-1 n}^T&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для завершения математического описания остаётся записать вычисление&amp;lt;ref name=&amp;quot;VOLA&amp;quot;&amp;gt;Воеводин В.В. Вычислительные основы линейной алгебры. М.: Наука, 1977.&amp;lt;/ref&amp;gt; матрицы вращения &amp;lt;math&amp;gt;T_{i j}&amp;lt;/math&amp;gt; и формулы её умножения на текущую промежуточную матрицу.&lt;br /&gt;
&lt;br /&gt;
Пусть в позиции &amp;lt;math&amp;gt;(i,i)&amp;lt;/math&amp;gt; преобразуемой матрицы стоит число &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt;, а в позиции &amp;lt;math&amp;gt;(i,j)&amp;lt;/math&amp;gt; - число &amp;lt;math&amp;gt;y&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Тогда для минимизации влияния ошибок округления сначала вычисляется равномерная норма вектора &amp;lt;math&amp;gt;z = max (|x|,|y|)&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Если она равна 0, то поворот не требуется: &amp;lt;math&amp;gt;t=s=0, c=1&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;z=|x|&amp;lt;/math&amp;gt;, то вычисляем &amp;lt;math&amp;gt;y_1=y/x&amp;lt;/math&amp;gt; и далее &amp;lt;math&amp;gt;c = \frac {1}{\sqrt{1+y_1^2}}&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;s=-c y_1&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;t=\frac {1-\sqrt{1+y_1^2}}{y_1}&amp;lt;/math&amp;gt;, а в позиции &amp;lt;math&amp;gt;(i,i)&amp;lt;/math&amp;gt; после поворота будет число &amp;lt;math&amp;gt;x \sqrt{1+y_1^2}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Если же &amp;lt;math&amp;gt;z=|y|&amp;lt;/math&amp;gt;, то вычисляем &amp;lt;math&amp;gt;x_1=x/y&amp;lt;/math&amp;gt; и далее &amp;lt;math&amp;gt;t=x_1 - x_1^2 sign(x_1)&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;s=\frac{sign(x_1)}{\sqrt{1+x_1^2}}&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;c = s x_1&amp;lt;/math&amp;gt;, а в позиции &amp;lt;math&amp;gt;(i,i)&amp;lt;/math&amp;gt; после поворота будет число &amp;lt;math&amp;gt;y \sqrt{1+x_1^2} sign(x)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
После получения элементов &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; матрицы вращения &amp;lt;math&amp;gt;T_{i j}&amp;lt;/math&amp;gt;  запись преобразования для каждого столбца правее &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го выглядит просто. Если в &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;-м столбце в позиции c номером &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; стоит x, а в позиции с номером &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; стоит y, то их новые значения будут &amp;lt;math&amp;gt;cx - sy&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;sx + cy&amp;lt;/math&amp;gt;, соответственно, как если бы комплексное число с действительной частью &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; и мнимой &amp;lt;math&amp;gt;y&amp;lt;/math&amp;gt; умножили на комплексное число &amp;lt;math&amp;gt;(c,s)&amp;lt;/math&amp;gt;.&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;
Операции вычисления параметров вращений образуют из себя треугольник на двумерной сетке, операции двумерных вращений - пирамиду на трёхмерной сетке.&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;(j, i)&amp;lt;/math&amp;gt; состоит из двух шагов: &lt;br /&gt;
а) вычисление параметров матрицы вращения &amp;lt;math&amp;gt;T_{ij}&amp;lt;/math&amp;gt; таких, чтобы обнулился элемент в позиции &amp;lt;math&amp;gt;(j, i)&amp;lt;/math&amp;gt;;&lt;br /&gt;
б) умножение слева матрицы вращения &amp;lt;math&amp;gt;T_{ij}&amp;lt;/math&amp;gt; на текущую версию матрицы.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В последовательной версии основная сложность алгоритма определяется прежде всего массовыми операциями вращения. Они, если не учитывать возможную разреженность, составляют (в главном члене) &amp;lt;math&amp;gt;n^3/3&amp;lt;/math&amp;gt; операций комплексного умножения или, если не прибегать к ухищрениям, &amp;lt;math&amp;gt;4n^3/3&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;
Макрограф алгоритма изображён на рисунке 1, графы макровершин - на последующих.&lt;br /&gt;
&lt;br /&gt;
[[file:QRGivens2.png|thumb|center|600px|Рисунок 1. Граф алгоритма без отображения входных и выходных данных. n=4. F1 - операция вычисления параметров поворота, F2 - выполнение поворота 2-мерного вектора (эквивалентно операции умножения двух комплексных чисел).]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;thumb&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;thumbinner&amp;quot; style=&amp;quot;width:{{#expr: 3 * (400 + 35) + 4 * (3 - 1) + 8}}px&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;gallery widths=400px heights=400px&amp;gt;&lt;br /&gt;
File:F1-full.png|Рисунок 2. Выбор метода вычисления параметров поворота в вершинах F1.&lt;br /&gt;
File:Givens F1!=.png|Рисунок 3. Вычисление параметров поворота для различных x и y в вершине V2.&lt;br /&gt;
File:Givens F1==.png|Рисунок 4. Вычисление параметров поворота для одинаковых x и у в вершине V1.&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;thumb&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;thumbinner&amp;quot; style=&amp;quot;width:{{#expr: 3 * (400 + 35) + 4 * (3 - 1) + 8}}px&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;gallery widths=400px heights=300px&amp;gt;&lt;br /&gt;
File:Givens F1=0.png|Рисунок 5. Вычисление параметров поворота для нулевых x и у в вершине V0.&lt;br /&gt;
File:Povorot-complexmult.png|Рисунок 6. Внутренний граф вершин F2 с входными и выходными параметрами: (u,v) = (c,s)(x,y)&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
{{#widget:Algoviewer&lt;br /&gt;
|url=Givens/giv3/Algo_view_givens3.html&lt;br /&gt;
|width=1300&lt;br /&gt;
|height=800&lt;br /&gt;
|border=1&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{#widget:Algoviewer&lt;br /&gt;
|url=Givens/giv4/Algo_view_givens4.html&lt;br /&gt;
|width=1300&lt;br /&gt;
|height=800&lt;br /&gt;
|border=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;
Как видно из описания подграфов, макровершина вычисления параметров поворота F1 в графе намного &amp;quot;весомее&amp;quot; вершины поворота F2.&lt;br /&gt;
А именно, в макровершине поворота критический путь - всего-навсего одно умножение (их 4, но все можно выполнить параллельно) и одно сложение/вычитание (их 2, но они тоже параллельны). Макровершина же вычисления параметров даёт в худшем случае критический путь, в котором одно вычисление квадратного корня, и по две операции деления, умножения, сложения/вычитания. &lt;br /&gt;
&lt;br /&gt;
Поэтому по грубой оценке критический путь в основном будет идти через &amp;lt;math&amp;gt;2n-3&amp;lt;/math&amp;gt; макровершины вычисления параметров F1, а также через &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; макровершин поворотов. В сумме это даёт критический путь, проходящий по &amp;lt;math&amp;gt;2n-3&amp;lt;/math&amp;gt; операциям вычисления квадратного корня, &amp;lt;math&amp;gt;4n-6&amp;lt;/math&amp;gt; делениям, по &amp;lt;math&amp;gt;5n-7&amp;lt;/math&amp;gt; умножениям и сложениям/вычитаниям. В макрографе, представленном на рисунке, один из критических путей может быть представлен, например, как прохождение по &amp;quot;верхней линии&amp;quot; вершин F1 (это &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt; штук) и затем попеременное выполнение F2 и F1 (&amp;lt;math&amp;gt;n-2&amp;lt;/math&amp;gt; раза), после чего ещё одно выполнение F2.&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;
&#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;t_{ij}&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;
=== Свойства алгоритма ===&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;
Вычислительная погрешность в методе Гивенса (вращений) растет &#039;&#039;линейно&#039;&#039;, как и в методе отражений (Хаусхолдера).&lt;br /&gt;
&lt;br /&gt;
== Программная реализация ==&lt;br /&gt;
&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В простейшем варианте метод Гивенса (вращений) QR-разложения квадратной вещественной матрицы на Фортране можно записать так:&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 J = I+1, N&lt;br /&gt;
                    CALL PARAMS (A(I,I), A(J,I), C, S)&lt;br /&gt;
                    DO K = I+1, N&lt;br /&gt;
                       CALL ROT2D (C, S, A(I,K), A(J,K))&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;
&lt;br /&gt;
Подпрограмма вращения ROT2D может быть записана, если операции с комплексными числами реализованы в трансляторе грамотно, так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;fortran&amp;quot;&amp;gt;&lt;br /&gt;
	SUBROUTINE ROT2D (C, S, X, Y)&lt;br /&gt;
        COMPLEX Z&lt;br /&gt;
        REAL ZZ(2)&lt;br /&gt;
        EQUIVALENCE Z, ZZ&lt;br /&gt;
        Z = CMPLX(C, S)*CMPLX(X,Y)&lt;br /&gt;
        X = ZZ(1)&lt;br /&gt;
        Y = ZZ(2)&lt;br /&gt;
        RETURN&lt;br /&gt;
        END&lt;br /&gt;
&amp;lt;/source&amp;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;
	SUBROUTINE ROT2D (C, S, X, Y)&lt;br /&gt;
        ZZ = C*X - S*Y&lt;br /&gt;
        Y = S*X + C*Y&lt;br /&gt;
        X = ZZ&lt;br /&gt;
        RETURN&lt;br /&gt;
        END&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;
&amp;lt;source lang=&amp;quot;fortran&amp;quot;&amp;gt;&lt;br /&gt;
	SUBROUTINE PARAMS (X, Y, C, S)&lt;br /&gt;
        Z = MAX (ABS(X), ABS(Y))&lt;br /&gt;
        IF (Z.EQ.0.) THEN&lt;br /&gt;
C OR (Z.LE.OMEGA) WHERE OMEGA - COMPUTER ZERO&lt;br /&gt;
            C = 1.&lt;br /&gt;
            S = 0.&lt;br /&gt;
        ELSE IF (Z.EQ.ABS(X))&lt;br /&gt;
            R = Y/X&lt;br /&gt;
            RR = R*R&lt;br /&gt;
            RR2 = SQRT(1+RR)&lt;br /&gt;
            X = X*RR2&lt;br /&gt;
            Y = (1-RR2)/R&lt;br /&gt;
            C = 1./RR2&lt;br /&gt;
            S = -C*R&lt;br /&gt;
        ELSE&lt;br /&gt;
            R = X/Y&lt;br /&gt;
            RR = R*R&lt;br /&gt;
            RR2 = SQRT (1+RR)&lt;br /&gt;
            X = SIGN(Y, X)*RR&lt;br /&gt;
            Y = R - SIGN(RR,R)&lt;br /&gt;
            S = SIGN(1./RR2, R) &lt;br /&gt;
            C = S*R  &lt;br /&gt;
        END IF&lt;br /&gt;
        RETURN&lt;br /&gt;
        END&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В приведенной реализации параметр матрицы вращения t записывается туда, где освобождается место (элемент преобразованной матрицы c этими индексами заведомо равен нулю), что позволяет легко восстанавливать матрицы вращения при надобности.&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
==== Локальность реализации алгоритма ====&lt;br /&gt;
===== Структура обращений в память и качественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
[[file:givens_1.png|thumb|center|700px|Рисунок 7. Метод Гивенса (вращений) QR-разложения квадратной матрицы (вещественный вариант). Общий профиль обращений в память]]&lt;br /&gt;
&lt;br /&gt;
На рис. 7 представлен профиль обращений в память для реализации вещественного варианта метода Гивенса (вращений) QR-разложения квадратной матрицы. Данный профиль состоит из обращений к одному двумерному массиву, в котором хранится  матрица значений. Из данного графика можно увидеть наличие однотипных итераций, из которых профиль и состоит. &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-я итерация затрагивает элементы массива, начиная с &amp;lt;math&amp;gt;(i-1)*k&amp;lt;/math&amp;gt;, то есть после каждой итерации первые &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; элементов (значение &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; нам пока неизвестно) больше не рассматриваются. Каждая итерация состоит из двух параллельно выполняемых частей – последовательного перебора всех элементов, начиная с &amp;lt;math&amp;gt;(i-1)*k&amp;lt;/math&amp;gt;, и активного использования первых &amp;lt;math&amp;gt;i*k&amp;lt;/math&amp;gt; элементов. &lt;br /&gt;
&lt;br /&gt;
Исходя из общей картины, можно сказать, что локальность данного профиля достаточно высока – обращения к близко расположенным в памяти элементам расположены в программе рядом, а также есть хорошо локализованные части с частым повторным использованием данных. Однако, чтобы убедиться в этом, необходимо более детальное рассмотрение.&lt;br /&gt;
&lt;br /&gt;
На рис. 8 представлен фрагмент общего профиля, выделенный зеленым цветом. Можно увидеть, что обе параллельно выполняемые части на каждой итерации состоят из небольших участков, похожих на обычный последовательный перебор. Явно видна регулярная структура – все участки одинакового размера и расположены на одинаковом расстоянии. &lt;br /&gt;
&lt;br /&gt;
[[file:givens_2.png|thumb|center|700px|Рисунок 8. Выделенный фрагмент общего профиля]]&lt;br /&gt;
&lt;br /&gt;
Рассмотрим фрагмент на рис. 8 более подробно (рис. 9). Здесь уже видны отдельные обращения, и теперь можно с уверенностью сказать, что каждый участок представляет собой последовательный перебор небольшого числа элементов. При этом в верхней части на каждом новом участке перебираются новые элементы, а в нижней – одни и те же данные. Поэтому в целом подобный фрагмент обладает высокой пространственной локальностью (обе части состоят из последовательных переборов), но средней временной локальностью (верхняя часть – очень низкой, нижняя – очень высокой локальностью).&lt;br /&gt;
&lt;br /&gt;
[[file:givens_3.png|thumb|center|700px|Рисунок 9. Фрагмент общего профиля, дальнейшее приближение]]&lt;br /&gt;
&lt;br /&gt;
Общий профиль состоит из набора таких фрагментов. При этом, согласно рис. 6 и 7, данные в этих фрагментах используются повторно на разных итерациях, поэтому общий профиль, скорее всего, обладает более высокой временной локальностью. Поэтому в целом можно сказать, что обращения в память в данной программе обладают высокой пространственной локальностью и достаточно неплохой временной локальностью.&lt;br /&gt;
&lt;br /&gt;
===== Количественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
Основной фрагмент реализации, с помощью которого были получены количественные оценки, приведен [http://git.parallel.ru/shvets.pavel.srcc/locality/blob/master/benchmarks/givens/givens.h здесь] (функция Kernel). Условия запуска описаны [http://git.parallel.ru/shvets.pavel.srcc/locality/blob/master/README.md здесь].&lt;br /&gt;
&lt;br /&gt;
Первая оценка выполняется на основе характеристики daps, которая оценивает число выполненных обращений (чтений и записей) в память в секунду. Данная характеристика является аналогом оценки flops применительно к работе с памятью и является в большей степени оценкой производительности взаимодействия с памятью, чем оценкой локальности. Однако она служит хорошим источником информации, в том числе для сравнения с результатами по следующей характеристике cvg.&lt;br /&gt;
&lt;br /&gt;
На рисунке 10 приведены значения daps для реализаций распространенных алгоритмов, отсортированные по возрастанию (чем больше daps, тем в общем случае выше производительность). Можно увидеть, что производительность работы с памятью достаточно высока для этой программы, что соответствует нашим представлениям согласно изученной локальности. &lt;br /&gt;
&lt;br /&gt;
[[file:givens_daps.png|thumb|center|700px|Рисунок 10. Сравнение значений оценки daps]]&lt;br /&gt;
&lt;br /&gt;
Вторая характеристика – cvg – предназначена для получения более машинно-независимой оценки локальности. Она определяет, насколько часто в программе необходимо подтягивать данные в кэш-память. Соответственно, чем меньше значение cvg, тем реже это нужно делать и тем лучше локальность. &lt;br /&gt;
&lt;br /&gt;
На рисунке 11 приведены значения cvg для того же набора реализаций, отсортированные по убыванию (чем меньше cvg, тем в общем случае выше локальность). Можно увидеть, что, как и daps, cvg показывает достаточно хороший результат, тем самым подтверждая качественную оценку, описанную выше.&lt;br /&gt;
&lt;br /&gt;
[[file:givens_cvg.png|thumb|center|700px|Рисунок 11. Сравнение значений оценки cvg]]&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
На суперкомпьютерах кластерной (или подобной) архитектуры вполне возможна блочная нарезка алгоритма (с использованием координатных обобщённых развёрток графа алгоритма) и его распределение по разным узлам (MPI). В то же время внутренний параллелизм в блоках будет реализовывать частично многоядерные возможности узлов (OpenMP), а частично даже суперскалярность ядер. На такую реализацию следует нацелиться, исходя из структуры графа алгоритма.&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
==== Масштабируемость алгоритма ====&lt;br /&gt;
&lt;br /&gt;
Несмотря на то, что на данный момент это пока нельзя проверить на реализациях, структура графа такова, что позволяет надеяться на получение хорошей масштабируемости. За это говорит не только хороший (линейный) показатель критического пути графа, но и хорошая координатная структура обобщённых развёрток графа, что даёт возможность хорошей блочной нарезки графа алгоритма. &lt;br /&gt;
&lt;br /&gt;
==== Масштабируемость реализации алгоритма ====&lt;br /&gt;
&lt;br /&gt;
На данный момент проверка хорошей масштабируемости реализаций затруднена по естественным [[#Существующие реализации алгоритма|причинам, описанным ниже]]. После появления надлежащей реализации авторы пополнят данный раздел.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
На данный момент получение динамических характеристик и показателей эффективности реализаций затруднено по естественным [[#Существующие реализации алгоритма|причинам, описанным ниже]]. После появления надлежащей реализации авторы пополнят данный раздел.&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;ref&amp;gt;Фролов А.В., Антонов А.С., Воеводин Вл.В., Теплов А.М. Сопоставление разных методов решения одной задачи по методике проекта Algowiki // Подана на конференцию &amp;quot;Параллельные вычислительные технологии (ПаВТ&#039;2016)&amp;quot;.&amp;lt;/ref&amp;gt;. Несмотря на то, что в большинстве старых &amp;quot;последовательных&amp;quot; пакетов как правило наличествует и программа QR-разложения методом Гивенса, в новых, рассчитанных на параллельное исполнение, пакетах QR-разложение осуществляют только с помощью [[Метод Хаусхолдера (отражений) QR-разложения квадратной матрицы, вещественный вариант|метода Хаусхолдера (отражений)]], который именно по параллельным свойствам проигрывает методу Гивенса. Мы связываем это с тем, что [[Метод Хаусхолдера (отражений) QR-разложения квадратной матрицы, вещественный вариант|метод Хаусхолдера (отражений)]] легко записывается через BLAS-процедуры и не требует переупорядочивания обычного хода циклов (разве что при введении блочности, но это не &amp;quot;косая&amp;quot;, а координатная нарезка циклов), а вот для параллельной реализации метода Гивенса (вращений) не избежать перезаписи циклов, поскольку максимально параллельный вариант в нём требует применения [[Глоссарий#Скошенный параллелизм|cкошенного (в узком понимании этого термина) параллелизма]]. Поэтому временно описание масштабируемости реализаций данного алгоритма невозможно - пока нет материала для исследования этой масштабируемости.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Законченные статьи]]&lt;br /&gt;
[[Категория:Разложения матриц]]&lt;br /&gt;
&lt;br /&gt;
[[En: Givens method]]&lt;/div&gt;</summary>
		<author><name>Lineprinter</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=22538</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=22538"/>
		<updated>2017-04-20T06:53:43Z</updated>

		<summary type="html">&lt;p&gt;Lineprinter: &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:Algoviewer&lt;br /&gt;
|url=cholesky/Algo_view_cholesky4.html&lt;br /&gt;
|width=1300&lt;br /&gt;
|height=800&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>Lineprinter</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=Widget:Algoviewer&amp;diff=22537</id>
		<title>Widget:Algoviewer</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=Widget:Algoviewer&amp;diff=22537"/>
		<updated>2017-04-20T06:52:43Z</updated>

		<summary type="html">&lt;p&gt;Lineprinter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;noinclude&amp;gt;&lt;br /&gt;
* url (e.g. &amp;quot;Givens/Algo_viewer_example_1.html&amp;quot;)&lt;br /&gt;
* border (default 0)&lt;br /&gt;
* width (default 400)&lt;br /&gt;
* height (default 300)&lt;br /&gt;
{{#widget:Algoviewer&lt;br /&gt;
|url=seq_par/Algo_view_seq_par4.html&lt;br /&gt;
|width=1300&lt;br /&gt;
|height=800&lt;br /&gt;
|border=1&lt;br /&gt;
}}&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
&amp;lt;includeonly&amp;gt;&lt;br /&gt;
&amp;lt;iframe src=&amp;quot;https://algowiki-project.org/html/Algo_viewer_json/&amp;lt;!--{$url|escape:&#039;htmlall&#039;}--&amp;gt;&amp;quot; frameborder=&amp;quot;&amp;lt;!--{$border|validate:int|default:0}--&amp;gt;&amp;quot; width=&amp;quot;&amp;lt;!--{$width|escape:html|default:400}--&amp;gt;&amp;quot; height=&amp;quot;&amp;lt;!--{$height|escape:html|default:300}--&amp;gt;&amp;quot;&amp;gt;&amp;lt;/iframe&amp;gt;&lt;br /&gt;
&amp;lt;/includeonly&amp;gt;&lt;/div&gt;</summary>
		<author><name>Lineprinter</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=Widget:Algoviewer&amp;diff=22536</id>
		<title>Widget:Algoviewer</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=Widget:Algoviewer&amp;diff=22536"/>
		<updated>2017-04-20T06:52:25Z</updated>

		<summary type="html">&lt;p&gt;Lineprinter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;noinclude&amp;gt;&lt;br /&gt;
* url (e.g. &amp;quot;Givens/Algo_viewer_example_1.html&amp;quot;)&lt;br /&gt;
* border (default 0)&lt;br /&gt;
* width (default 400)&lt;br /&gt;
* height (default 300)&lt;br /&gt;
{{#widget:Algoviewer&lt;br /&gt;
|url=seq_par/Algo_view_seq_par4.html&lt;br /&gt;
|width=1300&lt;br /&gt;
|height=800&lt;br /&gt;
|border=1&lt;br /&gt;
}}&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
&amp;lt;includeonly&amp;gt;&lt;br /&gt;
&amp;lt;iframe src=&amp;quot;&amp;lt;!--{--&amp;gt;https://algowiki-project.org/html/Algo_viewer_json/&amp;lt;!--{$url|escape:&#039;htmlall&#039;}--&amp;gt;&amp;lt;!--|validate:url}--&amp;gt;&amp;quot; frameborder=&amp;quot;&amp;lt;!--{$border|validate:int|default:0}--&amp;gt;&amp;quot; width=&amp;quot;&amp;lt;!--{$width|escape:html|default:400}--&amp;gt;&amp;quot; height=&amp;quot;&amp;lt;!--{$height|escape:html|default:300}--&amp;gt;&amp;quot;&amp;gt;&amp;lt;/iframe&amp;gt;&lt;br /&gt;
&amp;lt;/includeonly&amp;gt;&lt;/div&gt;</summary>
		<author><name>Lineprinter</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9F%D0%BE%D1%81%D0%BB%D0%B5%D0%B4%D0%BE%D0%B2%D0%B0%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE-%D0%BF%D0%B0%D1%80%D0%B0%D0%BB%D0%BB%D0%B5%D0%BB%D1%8C%D0%BD%D1%8B%D0%B9_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4_%D1%81%D1%83%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F&amp;diff=22535</id>
		<title>Последовательно-параллельный метод суммирования</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9F%D0%BE%D1%81%D0%BB%D0%B5%D0%B4%D0%BE%D0%B2%D0%B0%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE-%D0%BF%D0%B0%D1%80%D0%B0%D0%BB%D0%BB%D0%B5%D0%BB%D1%8C%D0%BD%D1%8B%D0%B9_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4_%D1%81%D1%83%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F&amp;diff=22535"/>
		<updated>2017-04-20T06:51:24Z</updated>

		<summary type="html">&lt;p&gt;Lineprinter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Основные авторы описания: [[Участник:Frolov|А.В.Фролов]], [[Участник:VadimVV|Вад.В.Воеводин]] ([[#Описание локальности данных и вычислений|раздел 2.2]]), [[Участник:Teplov|А.М.Теплов]] (раздел [[#Масштабируемость алгоритма и его реализации|2.4]])&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Последовательно-параллельный метод&#039;&#039;&#039; используется в качестве эрзаца блочной реализации вычисления длинных последовательностей ассоциативных операций (например, массового суммирования). Получил распространение благодаря следующим особенностям: а) реализует приём получения двойных циклов из одинарных; б) в последовательной архитектуре компьютеров позволял для ряда операций уменьшать влияние округления на результат. Здесь будем описывать его версию для суммирования чисел.&lt;br /&gt;
&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;
Вычисляемые данные: сумма элементов массива.&lt;br /&gt;
&lt;br /&gt;
Формулы метода: число &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; разлагается в выражение типа &amp;lt;math&amp;gt;N = (p - 1) k + q&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; — количество процессоров, &amp;lt;math&amp;gt;k = \lceil \frac{N}{p} \rceil&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;q = N - k (p - 1)&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;i &amp;lt; p&amp;lt;/math&amp;gt;) последовательно вычисляется сумма элементов массива, начиная с &amp;lt;math&amp;gt;(i - 1) k + 1&amp;lt;/math&amp;gt;-го, до &amp;lt;math&amp;gt;i k&amp;lt;/math&amp;gt;-го.&lt;br /&gt;
:&amp;lt;math&amp;gt;S_i = \sum_{j = 1}^k x_{k (i - 1) + j}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
На &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt;-м процессоре последовательно вычисляется сумма элементов массива, начиная с &amp;lt;math&amp;gt;(p - 1) k + 1&amp;lt;/math&amp;gt;-го до &amp;lt;math&amp;gt;(p - 1) k + q&amp;lt;/math&amp;gt;-го.&lt;br /&gt;
:&amp;lt;math&amp;gt;S_p = \sum_{j = 1}^q x_{k (p - 1) + j}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
По окончании этого процесса процессоры обмениваются данными и на одном из них (либо на всех одновременно, если результат нужен далее на всех процессорах) получившиеся суммы суммируются последовательно друг с другом.&lt;br /&gt;
:&amp;lt;math&amp;gt;\sum_{i = 1}^p S_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Вычислительное ядро последовательно-параллельного метода суммирования можно составить из множественных (всего &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt;) вычислений сумм элементов массива:&lt;br /&gt;
:&amp;lt;math&amp;gt;S_i = \sum_{j = 1}^k x_{k (i - 1) + j}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
и ещё одного вычисления суммы элементов частичных сумм&lt;br /&gt;
:&amp;lt;math&amp;gt;\sum_{i = 1}^p S_i&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Как уже записано в описании ядра алгоритма, основную часть метода составляют множественные (всего &amp;lt;math&amp;gt;p + 1&amp;lt;/math&amp;gt;) вычисления сумм&lt;br /&gt;
:&amp;lt;math&amp;gt;S_i = \sum_{j = 1}^k x_{k (i - 1) + j}&amp;lt;/math&amp;gt;&lt;br /&gt;
:&amp;lt;math&amp;gt;\sum_{i = 1}^p S_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;
Для вычисления суммы массива, состоящего из &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;N - 1&amp;lt;/math&amp;gt;. Поэтому алгоритм должен быть отнесён к алгоритмам &#039;&#039;линейной&#039;&#039; сложности по количеству последовательных операций.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
На рис.1 изображён граф алгоритма. В данном случае выполнено суммирование 24 элементов массива.&lt;br /&gt;
&lt;br /&gt;
[[file:series-parallel summation graph.png|center|thumb|600px|Рисунок 1. Последовательно-параллельный метод суммирования массива]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
{{#widget:Algoviewer&lt;br /&gt;
|url=seq_par/Algo_view_seq_par4.html&lt;br /&gt;
|width=1300&lt;br /&gt;
|height=800&lt;br /&gt;
|border=1&lt;br /&gt;
}}&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
Интерактивное изображение графа алгоритма без входных и выходных данных для случая суммирования 20 элементов массива&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;k - 1&amp;lt;/math&amp;gt; ярусов суммирования по частям массива (&amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; ветвей),&lt;br /&gt;
* &amp;lt;math&amp;gt;p - 1&amp;lt;/math&amp;gt; ярусов суммирования (одна последовательная ветвь).&lt;br /&gt;
 &lt;br /&gt;
Таким образом, в параллельном варианте критический путь алгоритма (и соответствующая ему высота ЯПФ) будет зависеть от произведённого разбиения массива на части. В оптимальном случае (&amp;lt;math&amp;gt;p = \sqrt{n}&amp;lt;/math&amp;gt;)  высота ЯПФ будет равна &amp;lt;math&amp;gt;2 \sqrt{n} - 2&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;
Входные данные: массив &amp;lt;math&amp;gt;\vec{x}&amp;lt;/math&amp;gt; (элементы &amp;lt;math&amp;gt;x_i&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Дополнительные ограничения: отсутствуют.&lt;br /&gt;
&lt;br /&gt;
Объём входных данных: &amp;lt;nowiki/&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;
Объём выходных данных: один скаляр.&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Соотношение последовательной и параллельной сложности в случае неограниченных ресурсов, как хорошо видно, является &#039;&#039;корнем квадратным&#039;&#039; (отношение линейной к корню квадратному). При этом вычислительная мощность алгоритма, как отношение числа операций к суммарному объему входных и выходных данных — всего-навсего &#039;&#039;1 (входных и выходных данных столько же, сколько операций)&#039;&#039;. При этом алгоритм не вполне полностью детерминирован, суммирование может быть проведено в разном порядке. Использование другого порядка выполнения ассоциативных операций может дать, с учётом особенностей входных данных, уменьшение влияния ошибок округления на результат. Дуги информационного графа локальны.&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, P&lt;br /&gt;
		S (I) = X(K*(I-1)+1)&lt;br /&gt;
		IF (I.LQ.P) THEN&lt;br /&gt;
			DO J = 2,K&lt;br /&gt;
				S(I)=S(I)+X(K*(I-1)+J)&lt;br /&gt;
		             END DO&lt;br /&gt;
		ELSE&lt;br /&gt;
			DO J = 2,Q&lt;br /&gt;
				S(I)=S(I)+X(K*(I-1)+J)&lt;br /&gt;
		             END DO&lt;br /&gt;
		END IF&lt;br /&gt;
	END DO&lt;br /&gt;
	SUM = S(1)&lt;br /&gt;
	DO I = 2, P&lt;br /&gt;
		SUM = SUM + S(I)&lt;br /&gt;
	END DO&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;
[[file:Seqpar sum 1.PNG|thumb|center|700px|Рисунок 2. Суммирование элементов массива. Общий профиль обращений в память]]&lt;br /&gt;
&lt;br /&gt;
На рис.2 представлен профиль обращений в память для суммирования элементов массива. Данный профиль состоит из обращений к двум массивам, фрагменты для отдельных массивов выделены на рис.2 зеленым цветом. Поскольку мы рассматриваем последовательную реализацию последовательно-параллельного метода суммирования, строение профиля практически никак не зависит от выбранного количества ветвей – будет меняться только число задействованных элементов во фрагменте 1.&lt;br /&gt;
&lt;br /&gt;
Фрагмент 2 устроен очень просто и представляет собой последовательный перебор всех элементов массива. Фрагмент 1 состоит из двух одинаковых переборов элементов массива, что хорошо видно из рис.3, где данный фрагмент рассмотрен отдельно.&lt;br /&gt;
&lt;br /&gt;
[[file:Seqpar sum 2.jpg|thumb|center|400px|Рисунок 3. Фрагмент 1 (профиль обращений к первому массиву)]]&lt;br /&gt;
&lt;br /&gt;
Данные фрагмент характеризуются высокой пространственной локальностью и низкой временной, поскольку практически отсутствуют повторные обращения.&lt;br /&gt;
&lt;br /&gt;
===== Количественная оценка локальности =====&lt;br /&gt;
&lt;br /&gt;
Основной фрагмент реализации, на основе которого были получены количественные оценки, приведен [http://git.algowiki-project.org/Voevodin/locality/blob/master/benchmarks/vectors_seqpar/vectors_seqpar.h здесь] (функция KernelOpSeqpar). Условия запуска описаны [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;
На рис.4 приведены значения daps для реализаций распространенных алгоритмов, отсортированные по возрастанию (чем больше daps, тем в общем случае выше производительность). Можно увидеть, что значение данной оценки достаточно высоко и близко к последовательно-параллельному скалярному произведению двух векторов, что является закономерным вследствие однотипности структуры выполняемых операций. &lt;br /&gt;
&lt;br /&gt;
[[file:Seqpar sum daps ru.PNG|thumb|center|700px|Рисунок 4. Сравнение значений оценки daps]]&lt;br /&gt;
&lt;br /&gt;
Вторая характеристика – cvg – предназначена для получения более машинно-независимой оценки локальности. Она определяет, насколько часто в программе необходимо подтягивать данные в кэш-память. Соответственно, чем меньше значение cvg, тем реже это нужно делать, тем лучше локальность. &lt;br /&gt;
&lt;br /&gt;
На рис.5 приведены значения cvg для того же набора реализаций, отсортированные по убыванию (чем меньше cvg, тем в общем случае выше локальность). Можно увидеть, что, согласно данной оценке, локальность профиля достаточно высока, что коррелирует со значением оценки daps.&lt;br /&gt;
&lt;br /&gt;
[[file:Seqpar sum cvg.PNG|thumb|center|700px|Рисунок 5. Сравнение значений оценки cvg]]&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
==== Масштабируемость алгоритма ====&lt;br /&gt;
==== Масштабируемость реализации алгоритма ====&lt;br /&gt;
[[file:Масштабируемость производительность ссумирования элементов массива.png|thumb|center|700px|Рисунок 6. Параллельная реализация Сумирования элементов массива Максимальная производительность. ]]&lt;br /&gt;
[[file:Масштабируемость суммирования элементов массива Эффективность.png|thumb|center|700px|Рисунок 7. Параллельная реализация сумирования элементов массива Максимальная эффективность. ]]&lt;br /&gt;
Набор изменяемых параметров запуска реализации алгоритма и границы значений параметров алгоритма: &lt;br /&gt;
*	число процессоров [2 : 256]&lt;br /&gt;
*	размер вектора [80000000:320000000]&lt;br /&gt;
Эффективность выполнения реализации алгоритма&lt;br /&gt;
*	Минимальная эффективность 0,00004%&lt;br /&gt;
*	Максимальная эффективность 0,0037%&lt;br /&gt;
Оценка масштабируемости&lt;br /&gt;
*	По числу процессов: -9.432e-07 – при увеличении числа процессов эффективность уменьшается на рассмотренной области изменений параметров запуска, однако, в целом уменьшение не интенсивное. Это объясняется тем, что при увеличении числа процессов  сильно возрастает доля накладных расходов на организацию схемы сдваивания сумирования, однако, так как общая эффективность составляет доли процента интенсивность сильная только при переходе от работы процессов в рамках одного физического узла к использованию коммуникационной сети. В остальной области рассмотренных значений параметров запуска эффективность близка к 0 в силу того, что на каждый процесс приходится черезвычайно малая доля вычислений. Больше полезного времени уходит на организацию работы процессов.&lt;br /&gt;
*	По размеру задачи: 1.881e-06 – при увеличении размера задачи эффективность в целом очень незначительно увеличивается по рассматриваемой области. Это объясняется общим увеличением вычислительной сложности задачи в связи с увеличением размерности. Однако вычислительная сложность алгоритма &amp;lt;math&amp;gt;(N-1)&amp;lt;/math&amp;gt;не позволяет существенно увеличить долю времени затрачиваемую на вычисления.&lt;br /&gt;
*	По двум направлениям: -1.423e-07 – при рассмотрении увеличения, как вычислительной сложности, так и числа процессов по всей рассмотренной области значений уменьшается, однако интенсивность уменьшения эффективности небольшая. В совокупности с тем фактом, что разница между максимальной и минимальной эффективностью на рассмотренной области значений параметров несущественная говорит о том, что на поверхности присутствуют области с очень интенсивным изменением эффективности на участке 2-16 процессов, но очень малые по площади. На остальной поверхности изменения эффективности незначительны и находятся на приблизительно одном и том же уровне.&lt;br /&gt;
&lt;br /&gt;
[http://git.algowiki-project.org/Teplov/Scalability/blob/master/arraysum/arraysum.c Исследованная реализация алгоритма на языке C]&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В чистом виде алгоритм последовательно-параллельного метода для суммирования массива встречается редко, в основном встречаются его модификации, например для случаев вычисления скалярного произведения (вместо элементов массива будут фигурировать произведения элементов двух массивов), равномерной нормы (вместо элементов массива — их модули) и т. п. В случае вычисления скалярного произведения в одном из частных случаев подобный приём применён в библиотеке BLAS (там одна из размерностей равна 5), но, видимо, не для распараллеливания, а для оптимизации работы с регистрами процессора. Между тем, разбиения массивов на группы для вычислений частных сумм могут быть полезны и для лучшего использования кэша на отдельных узлах.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Статьи в работе]]&lt;br /&gt;
[[Категория:Последовательно-параллельная группировка операций]]&lt;br /&gt;
[[Категория:Векторные операции]]&lt;/div&gt;</summary>
		<author><name>Lineprinter</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=Widget:Algoviewer&amp;diff=22534</id>
		<title>Widget:Algoviewer</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=Widget:Algoviewer&amp;diff=22534"/>
		<updated>2017-04-20T06:50:22Z</updated>

		<summary type="html">&lt;p&gt;Lineprinter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;noinclude&amp;gt;&lt;br /&gt;
* url (e.g. &amp;quot;Givens/Algo_viewer_example_1.html&amp;quot;)&lt;br /&gt;
* border (default 0)&lt;br /&gt;
* width (default 400)&lt;br /&gt;
* height (default 300)&lt;br /&gt;
{{#widget:Algoviewer&lt;br /&gt;
|url=seq_par/Algo_view_seq_par4.html&lt;br /&gt;
|width=1300&lt;br /&gt;
|height=800&lt;br /&gt;
|border=1&lt;br /&gt;
}}&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
&amp;lt;includeonly&amp;gt;&lt;br /&gt;
&amp;lt;iframe src=&amp;quot;https://algowiki-project.org/html/Algo_viewer_json/&amp;lt;!--{$url|escape:&#039;htmlall&#039;}--&amp;gt;&amp;quot; frameborder=&amp;quot;&amp;lt;!--{$border|validate:int|default:0}--&amp;gt;&amp;quot; width=&amp;quot;&amp;lt;!--{$width|escape:html|default:400}--&amp;gt;&amp;quot; height=&amp;quot;&amp;lt;!--{$height|escape:html|default:300}--&amp;gt;&amp;quot;&amp;gt;&amp;lt;/iframe&amp;gt;&lt;br /&gt;
&amp;lt;/includeonly&amp;gt;&lt;/div&gt;</summary>
		<author><name>Lineprinter</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=Widget:Algoviewer&amp;diff=22533</id>
		<title>Widget:Algoviewer</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=Widget:Algoviewer&amp;diff=22533"/>
		<updated>2017-04-20T06:48:43Z</updated>

		<summary type="html">&lt;p&gt;Lineprinter: Новая страница: «&amp;lt;noinclude&amp;gt; * url (e.g. &amp;quot;Givens/Algo_viewer_example_1.html&amp;quot;) * border (default 0) * width (default 400) * height (default 300) {{#widget:Algoviewer |url=seq_par/A…»&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;noinclude&amp;gt;&lt;br /&gt;
* url (e.g. &amp;quot;Givens/Algo_viewer_example_1.html&amp;quot;)&lt;br /&gt;
* border (default 0)&lt;br /&gt;
* width (default 400)&lt;br /&gt;
* height (default 300)&lt;br /&gt;
{{#widget:Algoviewer&lt;br /&gt;
|url=seq_par/Algo_view_seq_par4.html&lt;br /&gt;
|width=1300&lt;br /&gt;
|height=800&lt;br /&gt;
|border=1&lt;br /&gt;
}}&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
&amp;lt;includeonly&amp;gt;&lt;br /&gt;
&amp;lt;iframe src=&amp;quot;&amp;lt;!--{--&amp;gt;https://algowiki-project.org/html/Algo_viewer_json/&amp;lt;!--{$url|escape:&#039;htmlall&#039;}|validate:url}--&amp;gt;&amp;quot; frameborder=&amp;quot;&amp;lt;!--{$border|validate:int|default:0}--&amp;gt;&amp;quot; width=&amp;quot;&amp;lt;!--{$width|escape:html|default:400}--&amp;gt;&amp;quot; height=&amp;quot;&amp;lt;!--{$height|escape:html|default:300}--&amp;gt;&amp;quot;&amp;gt;&amp;lt;/iframe&amp;gt;&lt;br /&gt;
&amp;lt;/includeonly&amp;gt;&lt;/div&gt;</summary>
		<author><name>Lineprinter</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%D1%86%D0%B0:%D0%AD%D0%BB%D1%8C%D0%B2%D0%B8%D1%80%D0%B0_%D0%90%D1%85%D0%B8%D1%8F%D1%80%D0%BE%D0%B2%D0%B0/%D0%98%D1%82%D0%B5%D1%80%D0%B0%D1%86%D0%B8%D0%BE%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4_%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D1%8F_%D1%81%D0%B8%D1%81%D1%82%D0%B5%D0%BC%D1%8B_%D0%BB%D0%B8%D0%BD%D0%B5%D0%B9%D0%BD%D1%8B%D1%85_%D0%B0%D0%BB%D0%B3%D0%B5%D0%B1%D1%80%D0%B0%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D1%85_%D1%83%D1%80%D0%B0%D0%B2%D0%BD%D0%B5%D0%BD%D0%B8%D0%B9_GMRES_(%D0%BE%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4_%D0%BC%D0%B8%D0%BD%D0%B8%D0%BC%D0%B0%D0%BB%D1%8C%D0%BD%D1%8B%D1%85_%D0%BD%D0%B5%D0%B2%D1%8F%D0%B7%D0%BE%D0%BA)&amp;diff=17767</id>
		<title>Участница:Эльвира Ахиярова/Итерационный метод решения системы линейных алгебраических уравнений GMRES (обобщенный метод минимальных невязок)</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%D1%86%D0%B0:%D0%AD%D0%BB%D1%8C%D0%B2%D0%B8%D1%80%D0%B0_%D0%90%D1%85%D0%B8%D1%8F%D1%80%D0%BE%D0%B2%D0%B0/%D0%98%D1%82%D0%B5%D1%80%D0%B0%D1%86%D0%B8%D0%BE%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4_%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D1%8F_%D1%81%D0%B8%D1%81%D1%82%D0%B5%D0%BC%D1%8B_%D0%BB%D0%B8%D0%BD%D0%B5%D0%B9%D0%BD%D1%8B%D1%85_%D0%B0%D0%BB%D0%B3%D0%B5%D0%B1%D1%80%D0%B0%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D1%85_%D1%83%D1%80%D0%B0%D0%B2%D0%BD%D0%B5%D0%BD%D0%B8%D0%B9_GMRES_(%D0%BE%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4_%D0%BC%D0%B8%D0%BD%D0%B8%D0%BC%D0%B0%D0%BB%D1%8C%D0%BD%D1%8B%D1%85_%D0%BD%D0%B5%D0%B2%D1%8F%D0%B7%D0%BE%D0%BA)&amp;diff=17767"/>
		<updated>2016-11-16T18:17:02Z</updated>

		<summary type="html">&lt;p&gt;Lineprinter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment}}&lt;br /&gt;
&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Обобщеннный метод минимальных невязок&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(mn+m^2+n^2)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;n^2+n+1&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &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;
Обобщенный метод минимальных невязок (GMRES) - это итерационный метод для нахождения приближенного решения системы линейных алгебраических уравнений &amp;lt;math&amp;gt;Ax = b &amp;lt;/math&amp;gt; с произвольной матрицей &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt;. Алгоритм был преложен в 1986 году  Йозефом Саадом и Мартином Г. Шульцем&lt;br /&gt;
&amp;lt;ref&amp;gt;Y. Saad and M.H. Schultz, &amp;quot;GMRES: A generalized minimal residual algorithm for solving nonsymmetric linear systems&amp;quot;, SIAM J. Sci. Stat. Comput., 7:856-869, 1986. doi:10.1137/0907058.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В то время как применение классических итерационных алгоритмов для решения СЛАУ ограничивается либо диагональным преобладанием, либо положительной определенностью матрицы, GMRES может быть использован для систем с произвольной невырожденной матрицей &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt;. Данный метод опирается на итерации Арнольди для нахождения минимизирующего невязку вектора из подпространства Крылова &amp;lt;ref&amp;gt;https://en.wikipedia.org/wiki/Krylov_subspace&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Основным недостатком метода GMRES является поитерационное увеличение объема требуемой памяти. В связи с этим, для некоторых вычислительных систем выполенние программы метода может быть невозможным. Для некоторых систем используют модифицированный  обобщенный метод минимальных невязок - GMRES(m), который подразумевает использование рестартов, существенно сокращающих объемы необходимой памяти.&lt;br /&gt;
&lt;br /&gt;
Ключевая идея обобщенного метода минимальных невязок основана на решении задачи наименьших квадратов на каждом итерационном шаге. На каждом &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;-м шаге точное решение &amp;lt;math&amp;gt;x^* = A^{-1}b &amp;lt;/math&amp;gt; аппроксимируется вектором &amp;lt;math&amp;gt;x_k \in K_k&amp;lt;/math&amp;gt;, таким образом, что:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
\|r_k{\|}_2 = \|Ax_k-b{\|}_2 \to \min&lt;br /&gt;
&amp;lt;/math&amp;gt;,&lt;br /&gt;
где &amp;lt;math&amp;gt;K_k&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;
&#039;&#039;&#039;Исходные данные:&#039;&#039;&#039; Матрица &amp;lt;math&amp;gt;A \in R^{n\times n}&amp;lt;/math&amp;gt;, вектор правой части &amp;lt;math&amp;gt;b \in R^n&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вычисляемые данные:&#039;&#039;&#039; Приближенное решение &amp;lt;math&amp;gt;\tilde{x} \in R^n&amp;lt;/math&amp;gt; системы &amp;lt;math&amp;gt;Ax = b&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
На каждом &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;-м шаге метода решение &amp;lt;math&amp;gt;x_k&amp;lt;/math&amp;gt; рассматривается в виде &amp;lt;math&amp;gt;x_0+Vy_k&amp;lt;/math&amp;gt;, где столбцы &amp;lt;math&amp;gt;v_k&amp;lt;/math&amp;gt; матрицы &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; находятся с помощью процесса Арнольди для построения ортонормированного базиса &amp;lt;math&amp;gt;\{v_1, v_2, \dots, v_k\}&amp;lt;/math&amp;gt; подпространства Крылова &amp;lt;math&amp;gt;K_k(A, v_1)&amp;lt;/math&amp;gt;,&lt;br /&gt;
вектор &amp;lt;math&amp;gt;y_k&amp;lt;/math&amp;gt; представляет собой решение системы &amp;lt;math&amp;gt;H_ky_k = \beta_k e_1&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;H_k&amp;lt;/math&amp;gt; - верхняя  матрица Хессенберга&amp;lt;ref&amp;gt;https://en.wikipedia.org/wiki/Hessenberg_matrix&amp;lt;/ref&amp;gt; размера &amp;lt;math&amp;gt;k\times k&amp;lt;/math&amp;gt;, элементы которой формируются в процессе ортогонализации Арнольди, &amp;lt;math&amp;gt;\beta_k = \|r_0{\|}_2, \ r_0 = b - Ax_0 &amp;lt;/math&amp;gt;, и, наконец, &amp;lt;math&amp;gt;e_1&amp;lt;/math&amp;gt; - канонический вектор &amp;lt;math&amp;gt;{\begin{pmatrix}1&amp;amp;0&amp;amp;\dots 0\end{pmatrix}}^T&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;
&#039;&#039;&#039;Исходные данные:&#039;&#039;&#039; начальный ортонормированный вектор &amp;lt;math&amp;gt;v_1&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;\{v_1, v_2, \dots, v_k\}&amp;lt;/math&amp;gt; подпространства &amp;lt;math&amp;gt;K_k(A, v_1)&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;
&amp;amp;\omega = Av_j \\&lt;br /&gt;
&amp;amp;h_{ij} = (\omega, v_i)   \\&lt;br /&gt;
&amp;amp;\omega = \omega - h_{ij}v_i \\&lt;br /&gt;
&amp;amp;h_{(i+1)j} = \|\omega\| \\&lt;br /&gt;
&amp;amp;v_{j+1} = \frac{\omega}{h_{(i+1)j}} \\&lt;br /&gt;
&amp;amp;j  = \overline{1,k}, \  i = \overline{1,j} &lt;br /&gt;
\end{align}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
В результате выполнения процесса Арнольди получаем:&lt;br /&gt;
:&amp;lt;math&amp;gt;V_k =\{v_1, v_2, \dots, v_k\}&amp;lt;/math&amp;gt; - ортонормированный базис подпространства Крылова&lt;br /&gt;
:&amp;lt;math&amp;gt;\overline{V_k} = \{v_1, v_2, \dots, v_k, v_{k+1}\}&amp;lt;/math&amp;gt; - матрица, расширенная последним вычисленным вектором &amp;lt;math&amp;gt;v_{k+1}&amp;lt;/math&amp;gt;&lt;br /&gt;
:&amp;lt;math&amp;gt;H_k&amp;lt;/math&amp;gt; - верхняя Хессенбергова матрица, элементы которой равны коэффициентам ортогонализации:&lt;br /&gt;
:&amp;lt;math&amp;gt;H_k = &lt;br /&gt;
\begin{pmatrix}&lt;br /&gt;
h_{11} &amp;amp; h_{12} &amp;amp; h_{13} &amp;amp; h_{14} &amp;amp; \dots &amp;amp; h_{1k} \\&lt;br /&gt;
h_{21} &amp;amp; h_{22} &amp;amp; h_{23} &amp;amp; h_{24} &amp;amp; \dots &amp;amp; h_{2k} \\&lt;br /&gt;
0 &amp;amp; h_{32} &amp;amp; h_{33} &amp;amp; h_{34} &amp;amp; \dots &amp;amp; h_{3k} \\&lt;br /&gt;
\vdots &amp;amp; &amp;amp;\ddots &amp;amp; \ddots &amp;amp; &amp;amp; \vdots \\&lt;br /&gt;
0 &amp;amp; \dots &amp;amp; 0 &amp;amp; h_{k-1,k-2}&amp;amp; h_{k-1, k-1} &amp;amp; h_{k-1, k} \\&lt;br /&gt;
0 &amp;amp; \dots &amp;amp; 0 &amp;amp; 0 &amp;amp; h_{k, k-1} &amp;amp; h_{k, k} \\&lt;br /&gt;
0 &amp;amp; \dots &amp;amp; &amp;amp; &amp;amp; 0&amp;amp; h_{k+1, k}&lt;br /&gt;
\end{pmatrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Минимизация нормы невязки ====&lt;br /&gt;
Выполнив несложные преобразования можно получить выражение для невязки на &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;- й итерации:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
r_k = \beta e_1 - H_ky_k&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
Очевидно, что для задачи&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
\|r_k{\|}_2 \to \min&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
минимум достигается при &lt;br /&gt;
:&amp;lt;math&amp;gt; H_ky_k = \beta e_1  &amp;lt;/math&amp;gt;&lt;br /&gt;
Для решения последней системы матрица &amp;lt;math&amp;gt;H_k&amp;lt;/math&amp;gt; приводится к верхнему треугольному виду с помощью &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt; вращений Гивенса:&lt;br /&gt;
:&amp;lt;math&amp;gt; R_k = \Omega_k\Omega_{k-1}\dots \Omega_1 &amp;lt;/math&amp;gt;,&lt;br /&gt;
:&amp;lt;math&amp;gt; \Omega_i = \begin{pmatrix} 1 &amp;amp; &amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp; \\&lt;br /&gt;
&amp;amp;\ddots &amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;\\&lt;br /&gt;
&amp;amp;&amp;amp;1&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp; \\        &lt;br /&gt;
&amp;amp;&amp;amp;&amp;amp;c_i&amp;amp;s_i &amp;amp;&amp;amp;&amp;amp; \\&lt;br /&gt;
&amp;amp;&amp;amp;&amp;amp;-s_i&amp;amp; c_i &amp;amp;&amp;amp;&amp;amp;\\&lt;br /&gt;
&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;1&amp;amp;&amp;amp;&amp;amp; \\&lt;br /&gt;
&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;\ddots&amp;amp;\\&lt;br /&gt;
&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;1 \\  \end{pmatrix}, \quad  &lt;br /&gt;
s_i = \frac{h_{i+1,i}}{\sqrt{h_{ii}^2+h_{i+1,i}^2}}, \quad c_i = \frac{h_{ii}}{\sqrt{h_{ii}^2+h_{i+1,i}^2}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро последовательной версии метода GMRES состоит из вычислений произведений матрицы на вектор и скалярного произведения в процессе Арнольди и произведений матрицы на вектор, обращений верхней треугольной матрицы для нахождения решения задачи минимизации невязки.&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
В методе используются следующие макрооперции:&lt;br /&gt;
* Умножение матрицы на вектор&lt;br /&gt;
* Вычисление скалярного произведение двух векторов&lt;br /&gt;
* Нахождени &amp;lt;math&amp;gt;l_2&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;k&amp;lt;/math&amp;gt;, в то время как число умножений имеет квадратичную зависимость от числа итераций. Этот факт является мотивацией для того, чтобы использовать метод минимальных невязок с рестартами. Вместо того, чтобы формировать базис размерности &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;, выбирается параметр &amp;lt;math&amp;gt;m &amp;lt;&amp;lt; n &amp;lt;/math&amp;gt; и с помощью ортонормированного базиса размерности &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; строится приближенное решение. Посредством такой схемы необходимое количество памяти может быть оценено заранее и оставаться внутри определенных пределов. Приведем подробное описание алгоритма GMRES c рестартами:&lt;br /&gt;
&lt;br /&gt;
1. &amp;lt;math&amp;gt;k = 0 &amp;lt;/math&amp;gt;; инициализировать &amp;lt;math&amp;gt;x_0&amp;lt;/math&amp;gt;; &amp;lt;math&amp;gt;r_0=b-Ax_0&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v_1=\frac{r_0}{\|r_0 \|}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2. Выполнить &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; шагов процессса Арнольди:&lt;br /&gt;
   &lt;br /&gt;
   Для &amp;lt;math&amp;gt; j  = \overline{1,m} &amp;lt;/math&amp;gt; выполнять:&lt;br /&gt;
      &amp;lt;math&amp;gt;&lt;br /&gt;
      \omega = Av_j &lt;br /&gt;
      &amp;lt;/math&amp;gt;&lt;br /&gt;
      Для &amp;lt;math&amp;gt; i = \overline{1,j} &amp;lt;/math&amp;gt; выполнять: &lt;br /&gt;
           &amp;lt;math&amp;gt;&lt;br /&gt;
           \begin{align}&lt;br /&gt;
            &amp;amp;h_{ij} = (\omega, v_i)   \\&lt;br /&gt;
            &amp;amp;\omega = \omega - h_{ij}v_i \\&lt;br /&gt;
            \end{align}&lt;br /&gt;
           &amp;lt;/math&amp;gt;&lt;br /&gt;
       &amp;lt;math&amp;gt;&lt;br /&gt;
          \begin{align}&lt;br /&gt;
           h_{(i+1)j} = \|\omega\| \\&lt;br /&gt;
           v_{j+1} = \frac{\omega}{h_{(i+1)j}} \\&lt;br /&gt;
         \end{align}&lt;br /&gt;
      &amp;lt;/math&amp;gt;   &lt;br /&gt;
&lt;br /&gt;
3. Вычислить &amp;lt;math&amp;gt;y_m=\arg\min_{y} \| \beta e_1 - H_m y \|,\ y \in \R^m&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;x_m=x_0+V_my_m&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
4. Вычислить &amp;lt;math&amp;gt;r_m=b-Ax_m&amp;lt;/math&amp;gt;. Если требуемая точность достигнута, остановиться&lt;br /&gt;
&lt;br /&gt;
5. Рестарт:&lt;br /&gt;
   &amp;lt;math&amp;gt;x_0 = x_m&amp;lt;/math&amp;gt;&lt;br /&gt;
   &amp;lt;math&amp;gt;r_m = \frac{r_m}{\|r_m\|}&amp;lt;/math&amp;gt;&lt;br /&gt;
   Перейти к шагу 2.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Вычислительная сложность метода GMRES складывается из сложности решения задачи минимизации невязки и сложности процесса Арнольди. Так как в  &lt;br /&gt;
задаче минимизации невязки матрица системы имеет размер &amp;lt;math&amp;gt;m+1 \times m&amp;lt;/math&amp;gt;, для нахождения решения требуется  &amp;lt;math&amp;gt;O(m^2)&amp;lt;/math&amp;gt; операций. Процесс &lt;br /&gt;
Арнольди предполагает выполнение &amp;lt;math&amp;gt;O(mn)&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;
Основным ресурсом параллелизма будут являться умножение матрицы на вектор&amp;lt;ref&amp;gt;https://algowiki-project.org/ru/Умножение_плотной_неособенной_матрицы_на_вектор_(последовательный_вещественный_вариант)&amp;lt;/ref&amp;gt; и вычисление скалярных произведений&amp;lt;ref&amp;gt;https://algowiki-project.org/ru/Скалярное_произведение_векторов,_вещественная_версия,_последовательно-параллельный_вариант&amp;lt;/ref&amp;gt;. Представим информационные графы для данных алгоритмов:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;thumb&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;thumbinner&amp;quot; style=&amp;quot;width:{{#expr: (700 + 15) + 3 * (3 - 1) + 8}}px&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;gallery widths=600px heights=600px&amp;gt;&lt;br /&gt;
File:Matvec.png|Рисунок 1. Граф последовательного умножения плотной матрицы на вектор с отображением входных и выходных данных&lt;br /&gt;
File:Dot.png|Рисунок 2. Последовательно-параллельное вычисление скалярного произведения &lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;thumbcaption&amp;quot; style=&amp;quot;text-align:center&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/center&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;n&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&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&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;n&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;
При классификации по высоте ЯПФ, таким образом, алгоритм умножения матрицы на вектор относится к алгоритмам &#039;&#039;с линейной сложностью&#039;&#039;. При классификации по ширине ЯПФ его сложность также будет &#039;&#039;линейной&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Для &#039;&#039;&#039;вычисления скалярного произведения векторов размерности &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;&#039;&#039;&#039; параллельном варианте требуется последовательно выполнить следующие ярусы:&lt;br /&gt;
* 1 ярус вычисления произведений,&lt;br /&gt;
* &amp;lt;math&amp;gt;k - 1&amp;lt;/math&amp;gt; ярусов суммирования по частям массивов (&amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; ветвей),&lt;br /&gt;
* &amp;lt;math&amp;gt;p - 1&amp;lt;/math&amp;gt; ярусов суммирования (одна последовательная ветвь),&lt;br /&gt;
где &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; - количество процессоров, &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;- номер процессора.&lt;br /&gt;
&lt;br /&gt;
Таким образом, в параллельном варианте критический путь алгоритма  (и соответствующая ему высота ЯПФ) будет зависеть от произведённого разбиения массива на части. В оптимальном случае (&amp;lt;math&amp;gt;p = \sqrt{n}&amp;lt;/math&amp;gt;) высота ЯПФ будет равна &amp;lt;math&amp;gt; 2 \sqrt{n} - 1&amp;lt;/math&amp;gt;. При классификации по высоте ЯПФ, таким образом, последовательно-параллельный метод относится к алгоритмам &#039;&#039;со сложностью «корень квадратный»&#039;&#039;. При классификации по ширине ЯПФ его сложность также будет &#039;&#039;«корень квадратный»&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&#039;&#039;&#039;Входные данные:&#039;&#039;&#039; Матрица &amp;lt;math&amp;gt;A \in R^{n\times n}&amp;lt;/math&amp;gt;, вектор &amp;lt;math&amp;gt;b \in R^n&amp;lt;/math&amp;gt;, точность &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;. Объём: &amp;lt;math&amp;gt;n^2 + n + 1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные:&#039;&#039;&#039; Вектор &amp;lt;math&amp;gt;x_m \in R^n&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;
В точной арифметике GMRES всегда сходится не более чем за &amp;lt;math&amp;gt;n &amp;lt;/math&amp;gt; итераций (т.к. &amp;lt;math&amp;gt;K_n = R^n = Im(A)&amp;lt;/math&amp;gt;). При этом  последовательность решений сходится монотонно, в связи с тем, что &amp;lt;math&amp;gt;\|r_{m+1}\| \le \|r_m\|&amp;lt;/math&amp;gt;. Этот факт справедлив из-за того, что подпространства, по которым минимизируется &amp;lt;math&amp;gt;\|r_m\|&amp;lt;/math&amp;gt;, вложены друг в друга: &amp;lt;math&amp;gt;K_m \subset K_{m+1}&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&amp;lt;/math&amp;gt;, являющимся главным критерием быстрой сходимости для метода сопряженных градиентов.&lt;br /&gt;
&lt;br /&gt;
Если обобщенный метод минимальных невязок применяется к СЛАУ с матрицами, обладающими &amp;quot;хорошим&amp;quot; распределением собственных чисел, то он может работать быстрее чем стандартное &amp;lt;math&amp;gt;LU&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;
Реализации обобщенного метода минимальных невязок (GMRES) присутствуют во многих пакетах. Приведем некоторые из них:&lt;br /&gt;
* [http://www.netlib.org/linpack/ LINPACK]&lt;br /&gt;
* [http://math.nist.gov/tnt/download.html/ JAMA/C++] &lt;br /&gt;
* [http://math-atlas.sourceforge.net/ ATLAS] &lt;br /&gt;
* [http://software.intel.com/mkl‎/ Intel MLK]&lt;br /&gt;
* [http://math.nist.gov/iml++/ IML++]&lt;br /&gt;
* [http://netlib.org/lapack/ LAPACK]&lt;br /&gt;
* [http://osl.iu.edu/research/mtl/ MTL]&lt;br /&gt;
* [http://www-users.cs.umn.edu/~saad/software/SPARSKIT/ SPARSKIT]&lt;br /&gt;
* [http://researcher.watson.ibm.com/researcher/view_group.php?id=1426 WSMP]&lt;br /&gt;
* [https://www.mcs.anl.gov/petsc/ PETSC]&lt;br /&gt;
* [https://www.scipy.org// SciPy]&lt;br /&gt;
* [https://www.mathworks.com/ Matlab]&lt;br /&gt;
* [https://fenicsproject.org/ FEniCS Project]&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
[1] A parallel implementation of the restarted GMRES iterative algorithm for nonsymmetric systems of linear equations, Rudnei Dias da Cunha, Centro de Processamento de Dados, Universidade Federal do Rio Grande do Sul, Brazil and Computing Laboratory, University of Kent, Canterbury, UK and Tim Hopkins, Computing Laboratory, University of Kent, Canterbury, UK, 1993&lt;br /&gt;
[2] Gene H. Golub, Charles F. Van Loan, Matrix and Computations, 4th edition&lt;/div&gt;</summary>
		<author><name>Lineprinter</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%D1%86%D0%B0:%D0%AD%D0%BB%D1%8C%D0%B2%D0%B8%D1%80%D0%B0_%D0%90%D1%85%D0%B8%D1%8F%D1%80%D0%BE%D0%B2%D0%B0/%D0%98%D1%82%D0%B5%D1%80%D0%B0%D1%86%D0%B8%D0%BE%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4_%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D1%8F_%D1%81%D0%B8%D1%81%D1%82%D0%B5%D0%BC%D1%8B_%D0%BB%D0%B8%D0%BD%D0%B5%D0%B9%D0%BD%D1%8B%D1%85_%D0%B0%D0%BB%D0%B3%D0%B5%D0%B1%D1%80%D0%B0%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D1%85_%D1%83%D1%80%D0%B0%D0%B2%D0%BD%D0%B5%D0%BD%D0%B8%D0%B9_GMRES_(%D0%BE%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4_%D0%BC%D0%B8%D0%BD%D0%B8%D0%BC%D0%B0%D0%BB%D1%8C%D0%BD%D1%8B%D1%85_%D0%BD%D0%B5%D0%B2%D1%8F%D0%B7%D0%BE%D0%BA)&amp;diff=17766</id>
		<title>Участница:Эльвира Ахиярова/Итерационный метод решения системы линейных алгебраических уравнений GMRES (обобщенный метод минимальных невязок)</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%D1%86%D0%B0:%D0%AD%D0%BB%D1%8C%D0%B2%D0%B8%D1%80%D0%B0_%D0%90%D1%85%D0%B8%D1%8F%D1%80%D0%BE%D0%B2%D0%B0/%D0%98%D1%82%D0%B5%D1%80%D0%B0%D1%86%D0%B8%D0%BE%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4_%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D1%8F_%D1%81%D0%B8%D1%81%D1%82%D0%B5%D0%BC%D1%8B_%D0%BB%D0%B8%D0%BD%D0%B5%D0%B9%D0%BD%D1%8B%D1%85_%D0%B0%D0%BB%D0%B3%D0%B5%D0%B1%D1%80%D0%B0%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D1%85_%D1%83%D1%80%D0%B0%D0%B2%D0%BD%D0%B5%D0%BD%D0%B8%D0%B9_GMRES_(%D0%BE%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4_%D0%BC%D0%B8%D0%BD%D0%B8%D0%BC%D0%B0%D0%BB%D1%8C%D0%BD%D1%8B%D1%85_%D0%BD%D0%B5%D0%B2%D1%8F%D0%B7%D0%BE%D0%BA)&amp;diff=17766"/>
		<updated>2016-11-16T18:16:34Z</updated>

		<summary type="html">&lt;p&gt;Lineprinter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment}}&lt;br /&gt;
Итерационный метод решения системы линейных алгебраических уравнений GMRES (обобщенный метод минимальных невязок).&lt;br /&gt;
&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Обобщеннный метод минимальных невязок&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(mn+m^2+n^2)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;n^2+n+1&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &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;
Обобщенный метод минимальных невязок (GMRES) - это итерационный метод для нахождения приближенного решения системы линейных алгебраических уравнений &amp;lt;math&amp;gt;Ax = b &amp;lt;/math&amp;gt; с произвольной матрицей &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt;. Алгоритм был преложен в 1986 году  Йозефом Саадом и Мартином Г. Шульцем&lt;br /&gt;
&amp;lt;ref&amp;gt;Y. Saad and M.H. Schultz, &amp;quot;GMRES: A generalized minimal residual algorithm for solving nonsymmetric linear systems&amp;quot;, SIAM J. Sci. Stat. Comput., 7:856-869, 1986. doi:10.1137/0907058.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В то время как применение классических итерационных алгоритмов для решения СЛАУ ограничивается либо диагональным преобладанием, либо положительной определенностью матрицы, GMRES может быть использован для систем с произвольной невырожденной матрицей &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt;. Данный метод опирается на итерации Арнольди для нахождения минимизирующего невязку вектора из подпространства Крылова &amp;lt;ref&amp;gt;https://en.wikipedia.org/wiki/Krylov_subspace&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Основным недостатком метода GMRES является поитерационное увеличение объема требуемой памяти. В связи с этим, для некоторых вычислительных систем выполенние программы метода может быть невозможным. Для некоторых систем используют модифицированный  обобщенный метод минимальных невязок - GMRES(m), который подразумевает использование рестартов, существенно сокращающих объемы необходимой памяти.&lt;br /&gt;
&lt;br /&gt;
Ключевая идея обобщенного метода минимальных невязок основана на решении задачи наименьших квадратов на каждом итерационном шаге. На каждом &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;-м шаге точное решение &amp;lt;math&amp;gt;x^* = A^{-1}b &amp;lt;/math&amp;gt; аппроксимируется вектором &amp;lt;math&amp;gt;x_k \in K_k&amp;lt;/math&amp;gt;, таким образом, что:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
\|r_k{\|}_2 = \|Ax_k-b{\|}_2 \to \min&lt;br /&gt;
&amp;lt;/math&amp;gt;,&lt;br /&gt;
где &amp;lt;math&amp;gt;K_k&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;
&#039;&#039;&#039;Исходные данные:&#039;&#039;&#039; Матрица &amp;lt;math&amp;gt;A \in R^{n\times n}&amp;lt;/math&amp;gt;, вектор правой части &amp;lt;math&amp;gt;b \in R^n&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вычисляемые данные:&#039;&#039;&#039; Приближенное решение &amp;lt;math&amp;gt;\tilde{x} \in R^n&amp;lt;/math&amp;gt; системы &amp;lt;math&amp;gt;Ax = b&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
На каждом &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;-м шаге метода решение &amp;lt;math&amp;gt;x_k&amp;lt;/math&amp;gt; рассматривается в виде &amp;lt;math&amp;gt;x_0+Vy_k&amp;lt;/math&amp;gt;, где столбцы &amp;lt;math&amp;gt;v_k&amp;lt;/math&amp;gt; матрицы &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; находятся с помощью процесса Арнольди для построения ортонормированного базиса &amp;lt;math&amp;gt;\{v_1, v_2, \dots, v_k\}&amp;lt;/math&amp;gt; подпространства Крылова &amp;lt;math&amp;gt;K_k(A, v_1)&amp;lt;/math&amp;gt;,&lt;br /&gt;
вектор &amp;lt;math&amp;gt;y_k&amp;lt;/math&amp;gt; представляет собой решение системы &amp;lt;math&amp;gt;H_ky_k = \beta_k e_1&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;H_k&amp;lt;/math&amp;gt; - верхняя  матрица Хессенберга&amp;lt;ref&amp;gt;https://en.wikipedia.org/wiki/Hessenberg_matrix&amp;lt;/ref&amp;gt; размера &amp;lt;math&amp;gt;k\times k&amp;lt;/math&amp;gt;, элементы которой формируются в процессе ортогонализации Арнольди, &amp;lt;math&amp;gt;\beta_k = \|r_0{\|}_2, \ r_0 = b - Ax_0 &amp;lt;/math&amp;gt;, и, наконец, &amp;lt;math&amp;gt;e_1&amp;lt;/math&amp;gt; - канонический вектор &amp;lt;math&amp;gt;{\begin{pmatrix}1&amp;amp;0&amp;amp;\dots 0\end{pmatrix}}^T&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;
&#039;&#039;&#039;Исходные данные:&#039;&#039;&#039; начальный ортонормированный вектор &amp;lt;math&amp;gt;v_1&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;\{v_1, v_2, \dots, v_k\}&amp;lt;/math&amp;gt; подпространства &amp;lt;math&amp;gt;K_k(A, v_1)&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;
&amp;amp;\omega = Av_j \\&lt;br /&gt;
&amp;amp;h_{ij} = (\omega, v_i)   \\&lt;br /&gt;
&amp;amp;\omega = \omega - h_{ij}v_i \\&lt;br /&gt;
&amp;amp;h_{(i+1)j} = \|\omega\| \\&lt;br /&gt;
&amp;amp;v_{j+1} = \frac{\omega}{h_{(i+1)j}} \\&lt;br /&gt;
&amp;amp;j  = \overline{1,k}, \  i = \overline{1,j} &lt;br /&gt;
\end{align}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
В результате выполнения процесса Арнольди получаем:&lt;br /&gt;
:&amp;lt;math&amp;gt;V_k =\{v_1, v_2, \dots, v_k\}&amp;lt;/math&amp;gt; - ортонормированный базис подпространства Крылова&lt;br /&gt;
:&amp;lt;math&amp;gt;\overline{V_k} = \{v_1, v_2, \dots, v_k, v_{k+1}\}&amp;lt;/math&amp;gt; - матрица, расширенная последним вычисленным вектором &amp;lt;math&amp;gt;v_{k+1}&amp;lt;/math&amp;gt;&lt;br /&gt;
:&amp;lt;math&amp;gt;H_k&amp;lt;/math&amp;gt; - верхняя Хессенбергова матрица, элементы которой равны коэффициентам ортогонализации:&lt;br /&gt;
:&amp;lt;math&amp;gt;H_k = &lt;br /&gt;
\begin{pmatrix}&lt;br /&gt;
h_{11} &amp;amp; h_{12} &amp;amp; h_{13} &amp;amp; h_{14} &amp;amp; \dots &amp;amp; h_{1k} \\&lt;br /&gt;
h_{21} &amp;amp; h_{22} &amp;amp; h_{23} &amp;amp; h_{24} &amp;amp; \dots &amp;amp; h_{2k} \\&lt;br /&gt;
0 &amp;amp; h_{32} &amp;amp; h_{33} &amp;amp; h_{34} &amp;amp; \dots &amp;amp; h_{3k} \\&lt;br /&gt;
\vdots &amp;amp; &amp;amp;\ddots &amp;amp; \ddots &amp;amp; &amp;amp; \vdots \\&lt;br /&gt;
0 &amp;amp; \dots &amp;amp; 0 &amp;amp; h_{k-1,k-2}&amp;amp; h_{k-1, k-1} &amp;amp; h_{k-1, k} \\&lt;br /&gt;
0 &amp;amp; \dots &amp;amp; 0 &amp;amp; 0 &amp;amp; h_{k, k-1} &amp;amp; h_{k, k} \\&lt;br /&gt;
0 &amp;amp; \dots &amp;amp; &amp;amp; &amp;amp; 0&amp;amp; h_{k+1, k}&lt;br /&gt;
\end{pmatrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Минимизация нормы невязки ====&lt;br /&gt;
Выполнив несложные преобразования можно получить выражение для невязки на &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;- й итерации:&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
r_k = \beta e_1 - H_ky_k&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
Очевидно, что для задачи&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
\|r_k{\|}_2 \to \min&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
минимум достигается при &lt;br /&gt;
:&amp;lt;math&amp;gt; H_ky_k = \beta e_1  &amp;lt;/math&amp;gt;&lt;br /&gt;
Для решения последней системы матрица &amp;lt;math&amp;gt;H_k&amp;lt;/math&amp;gt; приводится к верхнему треугольному виду с помощью &amp;lt;math&amp;gt; k &amp;lt;/math&amp;gt; вращений Гивенса:&lt;br /&gt;
:&amp;lt;math&amp;gt; R_k = \Omega_k\Omega_{k-1}\dots \Omega_1 &amp;lt;/math&amp;gt;,&lt;br /&gt;
:&amp;lt;math&amp;gt; \Omega_i = \begin{pmatrix} 1 &amp;amp; &amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp; \\&lt;br /&gt;
&amp;amp;\ddots &amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;\\&lt;br /&gt;
&amp;amp;&amp;amp;1&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp; \\        &lt;br /&gt;
&amp;amp;&amp;amp;&amp;amp;c_i&amp;amp;s_i &amp;amp;&amp;amp;&amp;amp; \\&lt;br /&gt;
&amp;amp;&amp;amp;&amp;amp;-s_i&amp;amp; c_i &amp;amp;&amp;amp;&amp;amp;\\&lt;br /&gt;
&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;1&amp;amp;&amp;amp;&amp;amp; \\&lt;br /&gt;
&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;\ddots&amp;amp;\\&lt;br /&gt;
&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;1 \\  \end{pmatrix}, \quad  &lt;br /&gt;
s_i = \frac{h_{i+1,i}}{\sqrt{h_{ii}^2+h_{i+1,i}^2}}, \quad c_i = \frac{h_{ii}}{\sqrt{h_{ii}^2+h_{i+1,i}^2}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро последовательной версии метода GMRES состоит из вычислений произведений матрицы на вектор и скалярного произведения в процессе Арнольди и произведений матрицы на вектор, обращений верхней треугольной матрицы для нахождения решения задачи минимизации невязки.&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
В методе используются следующие макрооперции:&lt;br /&gt;
* Умножение матрицы на вектор&lt;br /&gt;
* Вычисление скалярного произведение двух векторов&lt;br /&gt;
* Нахождени &amp;lt;math&amp;gt;l_2&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;k&amp;lt;/math&amp;gt;, в то время как число умножений имеет квадратичную зависимость от числа итераций. Этот факт является мотивацией для того, чтобы использовать метод минимальных невязок с рестартами. Вместо того, чтобы формировать базис размерности &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;, выбирается параметр &amp;lt;math&amp;gt;m &amp;lt;&amp;lt; n &amp;lt;/math&amp;gt; и с помощью ортонормированного базиса размерности &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; строится приближенное решение. Посредством такой схемы необходимое количество памяти может быть оценено заранее и оставаться внутри определенных пределов. Приведем подробное описание алгоритма GMRES c рестартами:&lt;br /&gt;
&lt;br /&gt;
1. &amp;lt;math&amp;gt;k = 0 &amp;lt;/math&amp;gt;; инициализировать &amp;lt;math&amp;gt;x_0&amp;lt;/math&amp;gt;; &amp;lt;math&amp;gt;r_0=b-Ax_0&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;v_1=\frac{r_0}{\|r_0 \|}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2. Выполнить &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; шагов процессса Арнольди:&lt;br /&gt;
   &lt;br /&gt;
   Для &amp;lt;math&amp;gt; j  = \overline{1,m} &amp;lt;/math&amp;gt; выполнять:&lt;br /&gt;
      &amp;lt;math&amp;gt;&lt;br /&gt;
      \omega = Av_j &lt;br /&gt;
      &amp;lt;/math&amp;gt;&lt;br /&gt;
      Для &amp;lt;math&amp;gt; i = \overline{1,j} &amp;lt;/math&amp;gt; выполнять: &lt;br /&gt;
           &amp;lt;math&amp;gt;&lt;br /&gt;
           \begin{align}&lt;br /&gt;
            &amp;amp;h_{ij} = (\omega, v_i)   \\&lt;br /&gt;
            &amp;amp;\omega = \omega - h_{ij}v_i \\&lt;br /&gt;
            \end{align}&lt;br /&gt;
           &amp;lt;/math&amp;gt;&lt;br /&gt;
       &amp;lt;math&amp;gt;&lt;br /&gt;
          \begin{align}&lt;br /&gt;
           h_{(i+1)j} = \|\omega\| \\&lt;br /&gt;
           v_{j+1} = \frac{\omega}{h_{(i+1)j}} \\&lt;br /&gt;
         \end{align}&lt;br /&gt;
      &amp;lt;/math&amp;gt;   &lt;br /&gt;
&lt;br /&gt;
3. Вычислить &amp;lt;math&amp;gt;y_m=\arg\min_{y} \| \beta e_1 - H_m y \|,\ y \in \R^m&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;x_m=x_0+V_my_m&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
4. Вычислить &amp;lt;math&amp;gt;r_m=b-Ax_m&amp;lt;/math&amp;gt;. Если требуемая точность достигнута, остановиться&lt;br /&gt;
&lt;br /&gt;
5. Рестарт:&lt;br /&gt;
   &amp;lt;math&amp;gt;x_0 = x_m&amp;lt;/math&amp;gt;&lt;br /&gt;
   &amp;lt;math&amp;gt;r_m = \frac{r_m}{\|r_m\|}&amp;lt;/math&amp;gt;&lt;br /&gt;
   Перейти к шагу 2.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Вычислительная сложность метода GMRES складывается из сложности решения задачи минимизации невязки и сложности процесса Арнольди. Так как в  &lt;br /&gt;
задаче минимизации невязки матрица системы имеет размер &amp;lt;math&amp;gt;m+1 \times m&amp;lt;/math&amp;gt;, для нахождения решения требуется  &amp;lt;math&amp;gt;O(m^2)&amp;lt;/math&amp;gt; операций. Процесс &lt;br /&gt;
Арнольди предполагает выполнение &amp;lt;math&amp;gt;O(mn)&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;
Основным ресурсом параллелизма будут являться умножение матрицы на вектор&amp;lt;ref&amp;gt;https://algowiki-project.org/ru/Умножение_плотной_неособенной_матрицы_на_вектор_(последовательный_вещественный_вариант)&amp;lt;/ref&amp;gt; и вычисление скалярных произведений&amp;lt;ref&amp;gt;https://algowiki-project.org/ru/Скалярное_произведение_векторов,_вещественная_версия,_последовательно-параллельный_вариант&amp;lt;/ref&amp;gt;. Представим информационные графы для данных алгоритмов:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;thumb&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;thumbinner&amp;quot; style=&amp;quot;width:{{#expr: (700 + 15) + 3 * (3 - 1) + 8}}px&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;gallery widths=600px heights=600px&amp;gt;&lt;br /&gt;
File:Matvec.png|Рисунок 1. Граф последовательного умножения плотной матрицы на вектор с отображением входных и выходных данных&lt;br /&gt;
File:Dot.png|Рисунок 2. Последовательно-параллельное вычисление скалярного произведения &lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;thumbcaption&amp;quot; style=&amp;quot;text-align:center&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/center&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;n&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&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&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;n&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;
При классификации по высоте ЯПФ, таким образом, алгоритм умножения матрицы на вектор относится к алгоритмам &#039;&#039;с линейной сложностью&#039;&#039;. При классификации по ширине ЯПФ его сложность также будет &#039;&#039;линейной&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Для &#039;&#039;&#039;вычисления скалярного произведения векторов размерности &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;&#039;&#039;&#039; параллельном варианте требуется последовательно выполнить следующие ярусы:&lt;br /&gt;
* 1 ярус вычисления произведений,&lt;br /&gt;
* &amp;lt;math&amp;gt;k - 1&amp;lt;/math&amp;gt; ярусов суммирования по частям массивов (&amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; ветвей),&lt;br /&gt;
* &amp;lt;math&amp;gt;p - 1&amp;lt;/math&amp;gt; ярусов суммирования (одна последовательная ветвь),&lt;br /&gt;
где &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; - количество процессоров, &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;- номер процессора.&lt;br /&gt;
&lt;br /&gt;
Таким образом, в параллельном варианте критический путь алгоритма  (и соответствующая ему высота ЯПФ) будет зависеть от произведённого разбиения массива на части. В оптимальном случае (&amp;lt;math&amp;gt;p = \sqrt{n}&amp;lt;/math&amp;gt;) высота ЯПФ будет равна &amp;lt;math&amp;gt; 2 \sqrt{n} - 1&amp;lt;/math&amp;gt;. При классификации по высоте ЯПФ, таким образом, последовательно-параллельный метод относится к алгоритмам &#039;&#039;со сложностью «корень квадратный»&#039;&#039;. При классификации по ширине ЯПФ его сложность также будет &#039;&#039;«корень квадратный»&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&#039;&#039;&#039;Входные данные:&#039;&#039;&#039; Матрица &amp;lt;math&amp;gt;A \in R^{n\times n}&amp;lt;/math&amp;gt;, вектор &amp;lt;math&amp;gt;b \in R^n&amp;lt;/math&amp;gt;, точность &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;. Объём: &amp;lt;math&amp;gt;n^2 + n + 1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные:&#039;&#039;&#039; Вектор &amp;lt;math&amp;gt;x_m \in R^n&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;
В точной арифметике GMRES всегда сходится не более чем за &amp;lt;math&amp;gt;n &amp;lt;/math&amp;gt; итераций (т.к. &amp;lt;math&amp;gt;K_n = R^n = Im(A)&amp;lt;/math&amp;gt;). При этом  последовательность решений сходится монотонно, в связи с тем, что &amp;lt;math&amp;gt;\|r_{m+1}\| \le \|r_m\|&amp;lt;/math&amp;gt;. Этот факт справедлив из-за того, что подпространства, по которым минимизируется &amp;lt;math&amp;gt;\|r_m\|&amp;lt;/math&amp;gt;, вложены друг в друга: &amp;lt;math&amp;gt;K_m \subset K_{m+1}&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&amp;lt;/math&amp;gt;, являющимся главным критерием быстрой сходимости для метода сопряженных градиентов.&lt;br /&gt;
&lt;br /&gt;
Если обобщенный метод минимальных невязок применяется к СЛАУ с матрицами, обладающими &amp;quot;хорошим&amp;quot; распределением собственных чисел, то он может работать быстрее чем стандартное &amp;lt;math&amp;gt;LU&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;
Реализации обобщенного метода минимальных невязок (GMRES) присутствуют во многих пакетах. Приведем некоторые из них:&lt;br /&gt;
* [http://www.netlib.org/linpack/ LINPACK]&lt;br /&gt;
* [http://math.nist.gov/tnt/download.html/ JAMA/C++] &lt;br /&gt;
* [http://math-atlas.sourceforge.net/ ATLAS] &lt;br /&gt;
* [http://software.intel.com/mkl‎/ Intel MLK]&lt;br /&gt;
* [http://math.nist.gov/iml++/ IML++]&lt;br /&gt;
* [http://netlib.org/lapack/ LAPACK]&lt;br /&gt;
* [http://osl.iu.edu/research/mtl/ MTL]&lt;br /&gt;
* [http://www-users.cs.umn.edu/~saad/software/SPARSKIT/ SPARSKIT]&lt;br /&gt;
* [http://researcher.watson.ibm.com/researcher/view_group.php?id=1426 WSMP]&lt;br /&gt;
* [https://www.mcs.anl.gov/petsc/ PETSC]&lt;br /&gt;
* [https://www.scipy.org// SciPy]&lt;br /&gt;
* [https://www.mathworks.com/ Matlab]&lt;br /&gt;
* [https://fenicsproject.org/ FEniCS Project]&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
[1] A parallel implementation of the restarted GMRES iterative algorithm for nonsymmetric systems of linear equations, Rudnei Dias da Cunha, Centro de Processamento de Dados, Universidade Federal do Rio Grande do Sul, Brazil and Computing Laboratory, University of Kent, Canterbury, UK and Tim Hopkins, Computing Laboratory, University of Kent, Canterbury, UK, 1993&lt;br /&gt;
[2] Gene H. Golub, Charles F. Van Loan, Matrix and Computations, 4th edition&lt;/div&gt;</summary>
		<author><name>Lineprinter</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:AleksLevin/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9B%D0%B0%D0%BD%D1%86%D0%BE%D1%88%D0%B0_%D0%B2%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D1%8F_%D1%81%D0%BE%D0%B1%D1%81%D1%82%D0%B2%D0%B5%D0%BD%D0%BD%D1%8B%D1%85_%D0%B7%D0%BD%D0%B0%D1%87%D0%B5%D0%BD%D0%B8%D0%B9_%D1%81%D0%B8%D0%BC%D0%BC%D0%B5%D1%82%D1%80%D0%B8%D1%87%D0%BD%D0%BE%D0%B9_%D0%BC%D0%B0%D1%82%D1%80%D0%B8%D1%86%D1%8B_%D0%B4%D0%BB%D1%8F_%D1%82%D0%BE%D1%87%D0%BD%D0%BE%D0%B9_%D0%B0%D1%80%D0%B8%D1%84%D0%BC%D0%B5%D1%82%D0%B8%D0%BA%D0%B8_(%D0%B1%D0%B5%D0%B7_%D0%BF%D0%B5%D1%80%D0%B5%D0%BE%D1%80%D1%82%D0%BE%D0%B3%D0%BE%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8)&amp;diff=17765</id>
		<title>Участник:AleksLevin/Алгоритм Ланцоша вычисления собственных значений симметричной матрицы для точной арифметики (без переортогонализации)</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:AleksLevin/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9B%D0%B0%D0%BD%D1%86%D0%BE%D1%88%D0%B0_%D0%B2%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D1%8F_%D1%81%D0%BE%D0%B1%D1%81%D1%82%D0%B2%D0%B5%D0%BD%D0%BD%D1%8B%D1%85_%D0%B7%D0%BD%D0%B0%D1%87%D0%B5%D0%BD%D0%B8%D0%B9_%D1%81%D0%B8%D0%BC%D0%BC%D0%B5%D1%82%D1%80%D0%B8%D1%87%D0%BD%D0%BE%D0%B9_%D0%BC%D0%B0%D1%82%D1%80%D0%B8%D1%86%D1%8B_%D0%B4%D0%BB%D1%8F_%D1%82%D0%BE%D1%87%D0%BD%D0%BE%D0%B9_%D0%B0%D1%80%D0%B8%D1%84%D0%BC%D0%B5%D1%82%D0%B8%D0%BA%D0%B8_(%D0%B1%D0%B5%D0%B7_%D0%BF%D0%B5%D1%80%D0%B5%D0%BE%D1%80%D1%82%D0%BE%D0%B3%D0%BE%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8)&amp;diff=17765"/>
		<updated>2016-11-16T18:16:06Z</updated>

		<summary type="html">&lt;p&gt;Lineprinter: Lineprinter переименовал страницу [[Алгоритм Ланцоша вычисления собственных значений симметричной матрицы для точной арифметики (без переорт…&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Основные авторы описания: Левин А.Д. (студент, кафедра вычислительных методов, 604 группа) - все описанные разделы самостоятельно&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(k\,n^2)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O(\log_2 n)&amp;lt;/math&amp;gt; для каждой итерации алгоритма. Максимальное число таких итераций = &amp;lt;math&amp;gt;k&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^2+3n}{2} + 1&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;k\;(n + 1)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
= Свойства и структура алгоритмов =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
Данный алгоритм появился в 1950 г. и носит имя венгерского физика и математика Корнелия Ланцоша (венг. Lánczos Kornél). Алгоритм Ланцоша относится к итерационным методам вычисления собственных значений для матриц столь больших порядков &amp;lt;math style=&amp;quot;vertical-align:0%;&amp;gt; n&amp;lt;/math&amp;gt;, что к ним нельзя применить прямые методы из-за ограничений по времени и памяти.&lt;br /&gt;
&lt;br /&gt;
Сам Ланцош указывал, что его метод предназначен для отыскания нескольких собственных векторов симметричных матриц, хотя к методу сразу было обращено внимание, как к способу приведения всей матрицы к трёхдиагональному виду. Двадцатью годами позже канадский математик Крис Пэж показал, что, несмотря на чувствительность к округлениям, алгоритм Ланцоша - эффективное средство вычисления некоторых &amp;lt;math style=&amp;quot;vertical-align:0%;&amp;gt;k&amp;lt;/math&amp;gt; собственных чисел и соответствующих им собственных векторов &amp;lt;span style=&amp;quot;color:#4169E1&amp;quot;&amp;gt;[1, c.276]&amp;lt;/span&amp;gt;.&lt;br /&gt;
Обычно число &amp;lt;math style=&amp;quot;vertical-align:0%;&amp;gt;k&amp;lt;/math&amp;gt; берут в два раза больше, чем количество собственных значений матрицы, которые поставлена цель найти, и оно имеет порядок не более &amp;lt;math style=&amp;quot;vertical-align:-10%;&amp;gt;10^2&amp;lt;/math&amp;gt;&amp;lt;ref&amp;gt;Susan W. Bostic - Lanczos Eigensolution Method for High-Perfomance Computers, page 7&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Алгоритм Ланцоша для вычисления собственных значений симметричной матрицы &amp;lt;math style=&amp;quot;vertical-align:0%;&amp;gt;A&amp;lt;/math&amp;gt; соединяет в себе метод Ланцоша для построения последовательности подпространств Крылова и ортонормированных векторов Ланцоша и процедуру Рэлея-Ритца получения оптимальных приближений собственных значений и соответствующих собственных векторов исходной матрицы &amp;lt;math style=&amp;quot;vertical-align:0%;&amp;gt;A&amp;lt;/math&amp;gt; &amp;lt;span style=&amp;quot;color:#4169E1&amp;quot;&amp;gt;[2, с.381]&amp;lt;/span&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В данной статье рассматривается вариант алгоритма Ланцоша, в котором опущено влияние ошибок округления на вычислительный процесс, хотя на практике этому посвящается отдельное внимание и существуют различные методы решения данной проблемы, такие как частичная или полная переортогонализация. Этим модифицированным методам посвящены отдельные статьи на данном ресурсе.&lt;br /&gt;
&lt;br /&gt;
== Математическое описание алгоритма ==&lt;br /&gt;
Для лучшего понимания описания, данного в этом пункте статьи, рекомендуется ознакомиться с параграфом &#039;&#039;6.6 Методы Крыловского подпространства&#039;&#039; &amp;lt;span style=&amp;quot;color:#4169E1&amp;quot;&amp;gt;[2, с.313]&amp;lt;/span&amp;gt;. &lt;br /&gt;
Здесь же дано краткое описание всех переменных, математических операций и необходимый теоретический минимум.&lt;br /&gt;
&lt;br /&gt;
Алгоритм Ланцоша для вычисления &amp;lt;math style=&amp;quot;vertical-align:0%;&amp;gt;k&amp;lt;/math&amp;gt; собственных значений и собственных векторов вещественной симметричной матрицы &amp;lt;math style=&amp;quot;vertical-align:0%;&amp;gt;A=A^T&amp;lt;/math&amp;gt; в точной арифметике &amp;lt;span style=&amp;quot;color:#4169E1&amp;quot;&amp;gt;[2, с.381]&amp;lt;/span&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\begin{align}&lt;br /&gt;
q_1 = &amp;amp; \,\frac{b}{\|b\|_2},\; \beta_0 = 0,\; q_0 = 0\\&lt;br /&gt;
for \; &amp;amp; j = 1 \; to \; k \\&lt;br /&gt;
&amp;amp; z = A\,q_j\\&lt;br /&gt;
&amp;amp; \alpha_j = q^T_j z\\&lt;br /&gt;
&amp;amp; z = z - \alpha_j q_j - \beta_{j-1}q_{j-1}\\&lt;br /&gt;
&amp;amp; \beta_j = \|z\|_2\\&lt;br /&gt;
&amp;amp; If \; (\beta_j == 0) \; then\; stop\; the\; algorithm \\ &lt;br /&gt;
&amp;amp; \;  q_{j+1} = \frac{z}{\beta_j} \\&lt;br /&gt;
&amp;amp; compute\; eigenvalues\; and \;eigenvectors\;of \;matrix \;\;T_j= Q^T_j A Q\;\;and\;estimate \;the\; errors\\&lt;br /&gt;
end \; &amp;amp; for&lt;br /&gt;
\end{align}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
В продемонстрированном выше алгоритме &amp;lt;math style=&amp;quot;vertical-align:0%;&amp;gt;b&amp;lt;/math&amp;gt; - заданный вещественный вектор. Также полагается известным алгоритм вычисления произведения матрицы &amp;lt;math style=&amp;quot;vertical-align:0%;&amp;gt;A&amp;lt;/math&amp;gt; на вектор &amp;lt;math style=&amp;quot;vertical-align:0%;&amp;gt;x&amp;lt;/math&amp;gt;. &lt;br /&gt;
Введём матрицу Крылова, определяемую следующим соотношением: &amp;lt;math&amp;gt;K_j = [b,Ab,A^2b,...,A^{j-1}b]&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Далее, на практике, матрица &amp;lt;math style=&amp;quot;vertical-align:0%;&amp;gt;K&amp;lt;/math&amp;gt; заменяется матрицей &amp;lt;math style=&amp;quot;vertical-align:-20%;&amp;gt;Q&amp;lt;/math&amp;gt;, такой, что при любом числе &amp;lt;math style=&amp;quot;vertical-align:0%;&amp;gt;k&amp;lt;/math&amp;gt; линейные оболочки первых &amp;lt;math style=&amp;quot;vertical-align:0%;&amp;gt;k&amp;lt;/math&amp;gt; столбцов в &amp;lt;math style=&amp;quot;vertical-align:0%;&amp;gt;K&amp;lt;/math&amp;gt; и &amp;lt;math style=&amp;quot;vertical-align:-20%;&amp;gt;Q&amp;lt;/math&amp;gt; являются одним и тем же подпространством &amp;lt;span style=&amp;quot;color:#4169E1&amp;quot;&amp;gt;[2, c.315]&amp;lt;/span&amp;gt;.&lt;br /&gt;
Тогда матрица &amp;lt;math style=&amp;quot;vertical-align:-20%;&amp;gt;Q&amp;lt;/math&amp;gt;, в отличие от матрицы &amp;lt;math style=&amp;quot;vertical-align:0%;&amp;gt;K&amp;lt;/math&amp;gt;, хорошо обусловлена и легко обратима. В результате получаем матрицу &amp;lt;math&amp;gt;Q_j = [q_1, q_2, \dots, q_j]&amp;lt;/math&amp;gt; размерности &amp;lt;math&amp;gt;n\times j&amp;lt;/math&amp;gt;, столбцы которой ортогональны, являются базисом подпространства Крылова и называются векторами Ланцоша.&lt;br /&gt;
&lt;br /&gt;
В алгоритме Ланцоша вычислению подлежит столько первых столбцов в матрице &amp;lt;math style=&amp;quot;vertical-align:-20%;&amp;gt;Q_j&amp;lt;/math&amp;gt;, сколько необходимо для получения требуемого приближения к решению &amp;lt;math&amp;gt;A\,x\,=b\,\,(A\,x=\lambda \, x)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Получение нулевого &amp;lt;math&amp;gt;\beta_j&amp;lt;/math&amp;gt; в итерационном процессе - событие, желательное в том смысле, что оно свидетельствует о том, что найдено некоторое подпространство, являющееся в точности инвариантным, а все собственные значения матрицы &amp;lt;math style=&amp;quot;vertical-align:-20%;&amp;gt;T_j&amp;lt;/math&amp;gt; являются собственными значениями матрицы &amp;lt;math style=&amp;quot;vertical-align:0%;&amp;gt;A&amp;lt;/math&amp;gt;. На практике же нулевое и близкие к нулю значения получаются редко &amp;lt;span style=&amp;quot;color:#4169E1&amp;quot;&amp;gt;[3, стр. 429]&amp;lt;/span&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Затем на каждом шаге цикла формируется симметричная трёхдиагональная матрица &amp;lt;math&amp;gt;T_j = Q^T_j A Q&amp;lt;/math&amp;gt;, к которой применяется процесс Рэлея-Ритца для поиска её собственных значений (на практике для поиска этих собственных значений можно использовать любой из специальных методов, изложенных в &amp;lt;span style=&amp;quot;color:#4169E1&amp;quot;&amp;gt;[3, &amp;lt;math&amp;gt;\S&amp;lt;/math&amp;gt; 8.4]&amp;lt;/span&amp;gt;). &lt;br /&gt;
Эти собственные значения, они же числа Ритца, и полагаются приближёнными собственными значениями исходной матрицы &amp;lt;math style=&amp;quot;vertical-align:0%;&amp;gt;A&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Если требуется вычислить собственные векторы, то необходимо хранить вычисляемые векторы Ланцоша. Если работа при этом производится только с оперативной памятью, то при большом порядке исходной матрицы (а именно для таких матриц и предназначен метод Ланцоша) число шагов  &amp;lt;math style=&amp;quot;vertical-align:-10%;&amp;gt;j&amp;lt;/math&amp;gt;, которые могут быть выполнены до того момента, как оперативная память будет исчерпана, будет очень незначительно, а следовательно, искомые собственные пары могут не успеть сойтись с требуемой точностью. В этом случае метод Ланцоша можно использовать итерационным образом: после выполнения заданного числа шагов процесс прерывается и, если еще не все требуемые пары Ритца сошлись с требуемой точностью, то формируется новый начальный вектор &amp;lt;math style=&amp;quot;vertical-align:-20%;&amp;gt;q_1&amp;lt;/math&amp;gt;, являющийся линейной комбинацией векторов Ритца, еще не сошедшихся с заданной точностью, и процесс начинается заново.&amp;lt;ref&amp;gt;http://num-anal.srcc.msu.ru/lib_na/int_ae/int_ae6.htm (пункт 6.5)&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
Вычислительное ядро рассматриваемого алгоритма (наиболее ресурсно-затратная часть алгоритма) -  формирование на каждом шаге цикла промежуточного вектора &amp;lt;math&amp;gt;z&amp;lt;/math&amp;gt;, вычисляемого по формуле: &amp;lt;math&amp;gt;z = A\,q_j&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Как уже было упомянуто в данной статье в описании алгоритма, рассматриваемый алгоритм состоит из двух последовательно выполненных алгоритмов: метод Ланцоша для построения последовательности подпространств Крылова и ортонормированных векторов Ланцоша и процедура Рэлея-Ритца получения оптимальных приближений собственных значений и соответствующих собственных векторов исходной матрицы.&lt;br /&gt;
&lt;br /&gt;
В первой части алгоритма, где итерационно строятся Крыловское подпространство и трёхдиагональная матрица &amp;lt;math style=&amp;quot;vertical-align:-30%;&amp;gt;T_j&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 style=&amp;quot;vertical-align:0%;&amp;gt;A&amp;lt;/math&amp;gt; разреженная. Именно она подлежит оптимизации с использованием техник распараллеливания, поскольку она является вычислительным ядром алгоритма, а весь упомянутый процесс построения матрицы &amp;lt;math style=&amp;quot;vertical-align:-30%;&amp;gt;T_j&amp;lt;/math&amp;gt; выполняется последовательно.&lt;br /&gt;
&lt;br /&gt;
Вторая часть алгоритма - поиск собственных значений матрицы &amp;lt;math style=&amp;quot;vertical-align:-30%;&amp;gt;T_j&amp;lt;/math&amp;gt;, в нашем случае с помощью процедуры Рэлея-Ритца, может рассматриваться как отдельная нетривиальная макрооперация.&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
С целью полностью не дублировать пункт &amp;lt;math style=&amp;quot;vertical-align:0%;&amp;gt;\mathbf{1.2}&amp;lt;/math&amp;gt; данной статьи, где представлен очень наглядный и доступный для понимания псевдокод алгоритма, постараемся лишь коротко просуммировать и обобщить весь перечень последовательных действий в рассматриваемом алгоритме.&lt;br /&gt;
&lt;br /&gt;
В качестве входных данных имеем: вещественная симметричная матрица &amp;lt;math style=&amp;quot;vertical-align:0%;&amp;gt;A&amp;lt;/math&amp;gt; размера &amp;lt;math style=&amp;quot;vertical-align:-10%&amp;gt;n\times n&amp;lt;/math&amp;gt;, известный вещественный вектор &amp;lt;math style=&amp;quot;vertical-align:0%;&amp;gt;b&amp;lt;/math&amp;gt; длины &amp;lt;math style=&amp;quot;vertical-align:0%&amp;gt;n&amp;lt;/math&amp;gt; и число &amp;lt;math style=&amp;quot;vertical-align:0%&amp;gt;k&amp;lt;n&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Далее:&lt;br /&gt;
* Инициализируем вещественную константу &amp;lt;math style=&amp;quot;vertical-align:-20%;&amp;gt;\beta_0&amp;lt;/math&amp;gt; и векторы &amp;lt;math style=&amp;quot;vertical-align:-10%;&amp;gt;q_0&amp;lt;/math&amp;gt; и &amp;lt;math style=&amp;quot;vertical-align:-10%;&amp;gt;q_1&amp;lt;/math&amp;gt;&lt;br /&gt;
* Итерационно для всех &amp;lt;math style=&amp;quot;vertical-align:-20%&amp;gt;j=\overline {1,k}&amp;lt;/math&amp;gt;:&lt;br /&gt;
** Вычисляем вспомогательный вектор &amp;lt;math style=&amp;quot;vertical-align:0%&amp;gt;z&amp;lt;/math&amp;gt;, используя умножение матрицы на вектор&lt;br /&gt;
** Вычисляем, используя скалярное произведение векторов, значение &amp;lt;math style=&amp;quot;vertical-align:-30%&amp;gt;\alpha_j&amp;lt;/math&amp;gt; - элемент на главной диагонали трёхдиагональной матрицы &amp;lt;math style=&amp;quot;vertical-align:-30%;&amp;gt;T_j&amp;lt;/math&amp;gt;&lt;br /&gt;
** Вычисляем по формуле новое значение и перезаписываем вектор &amp;lt;math style=&amp;quot;vertical-align:0%&amp;gt;z&amp;lt;/math&amp;gt;&lt;br /&gt;
** Вычисляем значение &amp;lt;math style=&amp;quot;vertical-align:-20%&amp;gt;\beta_j&amp;lt;/math&amp;gt; - элементы непосредственно над и под главной диагональю той же матрицы &amp;lt;math style=&amp;quot;vertical-align:-30%;&amp;gt;T_j&amp;lt;/math&amp;gt;&lt;br /&gt;
** Вычисляем с вектора &amp;lt;math style=&amp;quot;vertical-align:0%&amp;gt;z&amp;lt;/math&amp;gt;  очередной столбец матрицы &amp;lt;math style=&amp;quot;vertical-align:-20%&amp;gt;Q&amp;lt;/math&amp;gt;, он же вектор Ланцоша : &amp;lt;math style=&amp;quot;vertical-align:-20%&amp;gt;\,\,q_{j+1}&amp;lt;/math&amp;gt;&lt;br /&gt;
** Теперь, когда матрица &amp;lt;math style=&amp;quot;vertical-align:-30%;&amp;gt;T_j&amp;lt;/math&amp;gt; сформирована на очередном шаге, находим её собственные значения и вектора, например с помощью процедуры Рэлея-Ритца (о используемых на практике методах нахождения подробнее было сказано ранее в математическом описании алгоритма)&lt;br /&gt;
** Переходим на следующую итерацию: &amp;lt;math style=&amp;quot;vertical-align:-20%&amp;gt;j&amp;lt;/math&amp;gt;++&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Оценим количество операций и сложность последовательного алгоритма, изложенного в предыдущем пункте.&lt;br /&gt;
*Для вычисления вектора &amp;lt;math style=&amp;quot;vertical-align:0%&amp;gt;z&amp;lt;/math&amp;gt; умножается матрица размера &amp;lt;math style=&amp;quot;vertical-align:-10%&amp;gt;n\times n&amp;lt;/math&amp;gt; на вектор длины &amp;lt;math style=&amp;quot;vertical-align:0%&amp;gt;n&amp;lt;/math&amp;gt;, и потребуется &amp;lt;math style=&amp;quot;vertical-align:0%&amp;gt;n^2&amp;lt;/math&amp;gt; операций умножения и &amp;lt;math style=&amp;quot;vertical-align:0%&amp;gt;n^2&amp;lt;/math&amp;gt; операций сложения.&lt;br /&gt;
*Умножение векторов &amp;lt;math style=&amp;quot;vertical-align:-20%&amp;gt;q^T_j&amp;lt;/math&amp;gt; и &amp;lt;math style=&amp;quot;vertical-align:0%&amp;gt;z&amp;lt;/math&amp;gt; длины &amp;lt;math style=&amp;quot;vertical-align:0%&amp;gt;n&amp;lt;/math&amp;gt; потребует &amp;lt;math style=&amp;quot;vertical-align:0%&amp;gt;n&amp;lt;/math&amp;gt; операций умножения и &amp;lt;math style=&amp;quot;vertical-align:0%&amp;gt;n&amp;lt;/math&amp;gt; операций сложения.&lt;br /&gt;
*При вычислении нового значения вектора &amp;lt;math style=&amp;quot;vertical-align:0%&amp;gt;z&amp;lt;/math&amp;gt; поэлементное сложение двух векторов длины &amp;lt;math style=&amp;quot;vertical-align:0%&amp;gt;n&amp;lt;/math&amp;gt; требует &amp;lt;math style=&amp;quot;vertical-align:0%&amp;gt;n&amp;lt;/math&amp;gt; операций сложения.&lt;br /&gt;
*Умножение вектора длины &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; на вещественное число требует &amp;lt;math style=&amp;quot;vertical-align:0%&amp;gt;n&amp;lt;/math&amp;gt; операций умножения.&lt;br /&gt;
*Нахождение квадратичной нормы вектора &amp;lt;math style=&amp;quot;vertical-align:0%&amp;gt;z&amp;lt;/math&amp;gt;  длины &amp;lt;math style=&amp;quot;vertical-align:0%&amp;gt;n&amp;lt;/math&amp;gt; требует &amp;lt;math style=&amp;quot;vertical-align:0%&amp;gt;n&amp;lt;/math&amp;gt; умножений и &amp;lt;math style=&amp;quot;vertical-align:0%&amp;gt;n&amp;lt;/math&amp;gt; сложений и ещё 1 операцию извлечения квадратного корня.&lt;br /&gt;
*Заключительное нахождение собственных значений и собственных векторов трёхдиагональной матрицы &amp;lt;math style=&amp;quot;vertical-align:-30%&amp;gt;T_j&amp;lt;/math&amp;gt; размера &amp;lt;math style=&amp;quot;vertical-align:-20%&amp;gt;j\times j&amp;lt;/math&amp;gt; с помощью QR-алгоритма потребует в худшем случае &amp;lt;math style=&amp;quot;vertical-align:-30%&amp;gt;O(j^3)&amp;lt;/math&amp;gt; операций.&amp;lt;ref&amp;gt;http://num-anal.srcc.msu.ru/lib_na/int_ae/int_ae6.htm (пункт 6.2)&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Посчитаем теперь суммарное количество простейших операций на одной итерации алгоритма (без учёта записей в память):&lt;br /&gt;
*&amp;lt;math style=&amp;quot;vertical-align:0%&amp;gt;n^2+5\,n&amp;lt;/math&amp;gt; операций умножения/деления&lt;br /&gt;
*&amp;lt;math style=&amp;quot;vertical-align:0%&amp;gt;n^2+4\,n&amp;lt;/math&amp;gt; операций сложения/вычитания&lt;br /&gt;
*1 операция извлечения квадратного корня&amp;lt;br&amp;gt;&lt;br /&gt;
Отдельно после всех итераций при &amp;lt;math style=&amp;quot;vertical-align:-20%&amp;gt;j=k&amp;lt;/math&amp;gt; :&lt;br /&gt;
*&amp;lt;math style=&amp;quot;vertical-align:-30%&amp;gt;O(k^3)&amp;lt;/math&amp;gt; операций для поиска собственных значений и собственных векторов трёхдиагональной матрицы &amp;lt;math style=&amp;quot;vertical-align:-30%&amp;gt;T_k&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Итого можно утверждать следующее:&lt;br /&gt;
&lt;br /&gt;
Последовательная сложность одной итерации рассматриваемого алгоритма:  &amp;lt;math style=&amp;quot;vertical-align:-30%&amp;gt;O(n ^ 2) &amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
Суммарная сложность алгоритма (&amp;lt;math style=&amp;quot;vertical-align:0%&amp;gt;k&amp;lt;/math&amp;gt; итераций): &amp;lt;math style=&amp;quot;vertical-align:-30%&amp;gt;O(k\,n ^ 2) + O(k ^ 3)&amp;lt;/math&amp;gt; &amp;lt;ref&amp;gt;Здесь мы полагаем, что мы не вычисляем собственные значения и векторы матрицы &amp;lt;math style=&amp;quot;vertical-align:-20%&amp;gt;T_j&amp;lt;/math&amp;gt; на каждом промежуточном шаге, а лишь единожды затрачиваем &amp;lt;math style=&amp;quot;vertical-align:-20%&amp;gt;O(k ^ 3)&amp;lt;/math&amp;gt; действий в конце для нахождения &amp;lt;math style=&amp;quot;vertical-align:0%&amp;gt;k&amp;lt;/math&amp;gt; собственных значений и векторов матрицы &amp;lt;math style=&amp;quot;vertical-align:-20%&amp;gt;T_k&amp;lt;/math&amp;gt;&amp;lt;/ref&amp;gt; &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
На практике же обычно рассматривается число &amp;lt;math style=&amp;quot;vertical-align:0%&amp;gt;k&amp;lt;&amp;lt;n&amp;lt;/math&amp;gt;, поэтому второе слагаемое можно опустить.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
На рисунке продемонстрирован граф одной итерации (при каком-либо произвольном значении итератора &amp;lt;math style=&amp;quot;vertical-align:-20%&amp;gt;j&amp;lt;/math&amp;gt;) описанного алгоритма без отображения входных и выходных данных. Наглядно продемонстрированы связи между всеми действиями, и с помощью деления на &amp;lt;math style=&amp;quot;vertical-align:0%&amp;gt;n&amp;lt;/math&amp;gt; потоков показано, какие операции возможно выполнять параллельно на каждом шаге с целью оптимизации и экономии машинного времени. В результате, после параллельно выполненных операций &#039;&#039;&#039;Division&#039;&#039;&#039; на заключительном ярусе, на выходе имеем значение следующего вектора Ланцоша &amp;lt;math style=&amp;quot;vertical-align:-20%&amp;gt;q_{j+1}&amp;lt;/math&amp;gt;, после чего следует переход на следующую итерацию, и процесс повторяется.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Levin_graph.png|600px|thumb|center|&#039;&#039;&#039;Simp&#039;&#039;&#039; (от англ. simple) - операции сложения и умножения вещественных чисел при вычислении произведения матрицы на вектор;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Vec mult&#039;&#039;&#039; (от англ. vectors multiplication) - операция скалярного умножения двух векторов;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Vec subtr&#039;&#039;&#039; (от англ. vectors subtraction, из-за использованных в операции вычитаний векторов) - операция умножения вектора на число (выполняется дважды) и формирование нового значения &amp;lt;math style=&amp;quot;vertical-align:0%&amp;gt;z_{new}&amp;lt;/math&amp;gt; по формуле: &amp;lt;math style=&amp;quot;vertical-align:-30%&amp;gt;z = z - \alpha_j q_j - \beta_{j-1}q_{j-1}&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;||\cdot||&amp;lt;/math&amp;gt; - операция вычисления нормы вектора;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Division&#039;&#039;&#039; - операция деления вектора на вещественное число  &amp;lt;math style=&amp;quot;vertical-align:-30%&amp;gt;\beta_j&amp;lt;/math&amp;gt;]] &amp;lt;br&amp;gt;&lt;br /&gt;
Важно отметить, что упомянутые выше операции также легко поддаются оптимизации. Например, попарные умножения вещественных координат векторов при вычислении скалярного произведения также легко выполняются параллельно на &amp;lt;math style=&amp;quot;vertical-align:0%&amp;gt;n&amp;lt;/math&amp;gt; потоках, а затем можно применить суммирование методом сдваивания, о котором сказано в следующем пункте. Умножение или деление вектора на вещественное число являющееся, по определению, умножением или делением всех его координат на это число тоже может быть выполнено эффективнее, если мы используем &amp;lt;math style=&amp;quot;vertical-align:0%&amp;gt;n&amp;lt;/math&amp;gt; потоков, а не выполняем эти действия последовательно.&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Параллельные операции возможны только внутри итераций, так как рассматриваемый алгоритм является итерационным, и сами итерации строго последовательны.&amp;lt;br&amp;gt;&lt;br /&gt;
Рассмотрим в этом пункте подробнее, какой выигрыш мы можем получить при распараллеливании операций внутри одной итерации согласно графу из предыдущего пункта.&lt;br /&gt;
&lt;br /&gt;
Умножение вещественной симметричной матрицы &amp;lt;math style=&amp;quot;vertical-align:0%;&amp;gt;A&amp;lt;/math&amp;gt; размера &amp;lt;math style=&amp;quot;vertical-align:-10%&amp;gt;n\times n&amp;lt;/math&amp;gt; на вектор &amp;lt;math style=&amp;quot;vertical-align:0%;&amp;gt;b&amp;lt;/math&amp;gt; длины &amp;lt;math style=&amp;quot;vertical-align:0%&amp;gt;n&amp;lt;/math&amp;gt; требует последовательного выполнения &amp;lt;math style=&amp;quot;vertical-align:0%&amp;gt;n&amp;lt;/math&amp;gt; ярусов умножений и сложений. Сложение элементов вектора длины &amp;lt;math style=&amp;quot;vertical-align:0%&amp;gt;n&amp;lt;/math&amp;gt; на каждом шаге перемножения можно оптимизировать и выполнить за &amp;lt;math style=&amp;quot;vertical-align:-20%&amp;gt;\log_2 n&amp;lt;/math&amp;gt; действий, если применять метод сдваивания.&lt;br /&gt;
&lt;br /&gt;
Дальнейшие операции выполняются последовательно, а вычисление значений векторов происходит за один ярус операций сложения или умножения.&lt;br /&gt;
&lt;br /&gt;
Ресурс параллелизма алгоритма вычисления собственных значений построенной трёхдиагональной матрицы &amp;lt;math style=&amp;quot;vertical-align:-20%&amp;gt;T_k&amp;lt;/math&amp;gt; размера &amp;lt;math style=&amp;quot;vertical-align:0%&amp;gt;k\times k&amp;lt;/math&amp;gt; зависит от выбранного алгоритма (об этих алгоритмах подробнее было сказано в пункте &#039;&#039;&#039;1.2&#039;&#039;&#039;). Будет очень затратно с точки зрения объёма материала пытаться рассмотреть пусть даже часть этих методов в этой статье, а так же тонкости их распараллеливания. Предлагаем читателям при желании самим ознакомиться с этими алгоритмами на данном ресурсе или прибегнув к иным источникам.&lt;br /&gt;
&lt;br /&gt;
При классификации по высоте ЯПФ, таким образом, алгоритм Ланцоша без переортогонализации относится к алгоритмам с &#039;&#039;логарифмической сложностью&#039;&#039;, и его сложность равна &amp;lt;math style=&amp;quot;vertical-align:-30%&amp;gt;O(\log_2 n)&amp;lt;/math&amp;gt; с важным замечанием, что это сложность только одной итерации, а максимум в алгоритме может быть выполнено последовательно &amp;lt;math style=&amp;quot;vertical-align:0%&amp;gt;k&amp;lt;/math&amp;gt; таких однотипных итераций. &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Несмотря на то, что на графе в предыдущем пункте для лучшей визуализации и понимания было изображено &amp;lt;math style=&amp;quot;vertical-align:0%&amp;gt;n&amp;lt;/math&amp;gt; потоков (никаких допущений мы не делали и нарушений с точки зрения логики и математики нет, можно изобразить большее число потоков, просто часть времени некоторые из них могут бездействовать и не использоваться), на самом деле правильным и разумным будет реализовать начальное параллельное выполнение &amp;lt;math style=&amp;quot;vertical-align:0%&amp;gt;n^2&amp;lt;/math&amp;gt; операций умножения всех вещественных элементов матрицы &amp;lt;math style=&amp;quot;vertical-align:0%&amp;gt;A&amp;lt;/math&amp;gt; на соответствующие элементы вектора &amp;lt;math style=&amp;quot;vertical-align:-30%&amp;gt;q_j&amp;lt;/math&amp;gt;, после чего применять тот же самый упомянутый метод сдваивания. &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
Поэтому при классификации по ширине ЯПФ его сложность будет &#039;&#039;квадратичной&#039;&#039; и равна &amp;lt;math style=&amp;quot;vertical-align:-30%&amp;gt;O(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Входные данные алгоритма:&#039;&#039;&#039; вещественная симметричная &amp;lt;math style=&amp;quot;vertical-align:0%;&amp;gt;A&amp;lt;/math&amp;gt; размера &amp;lt;math style=&amp;quot;vertical-align:-10%&amp;gt;n\times n&amp;lt;/math&amp;gt;, вещественный вектор &amp;lt;math style=&amp;quot;vertical-align:0%;&amp;gt;b&amp;lt;/math&amp;gt; длины &amp;lt;math style=&amp;quot;vertical-align:0%&amp;gt;n&amp;lt;/math&amp;gt;, и одно целое число &amp;lt;math style=&amp;quot;vertical-align:-10%&amp;gt;k&amp;lt;/math&amp;gt;, означающее количество итераций.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём входных данных:&#039;&#039;&#039; &amp;lt;math style=&amp;quot;vertical-align:-10%;&amp;gt;n^2 +n + 1 &amp;lt;/math&amp;gt;. Если учесть, что матрица симметричная и достаточно для её однозначного определения задать элементы на главной диагонали и элементы над или под ней, то можно сократить объём входных данных с целью экономии памяти до: &amp;lt;math&amp;gt;\ \frac{n^2-n}{2}+n+n+1 =\frac{n^2+3n}{2} + 1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные алгоритма:&#039;&#039;&#039; вектор собственных значений &amp;lt;math style=&amp;quot;vertical-align:0%;&amp;gt;\Theta^k&amp;lt;/math&amp;gt; длины &amp;lt;math style=&amp;quot;vertical-align:0%&amp;gt;k&amp;lt;/math&amp;gt;, матрица &amp;lt;math style=&amp;quot;vertical-align:0%&amp;gt;S^k&amp;lt;/math&amp;gt; из соответствующих собственных векторов размера &amp;lt;math style=&amp;quot;vertical-align:0%&amp;gt;n\times k&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объём выходных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;k\;(n + 1)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
В данном пункте перечислены без какой-либо хронологии или связи друг с другом наиболее важные и разнообразные свойства алгоритма, на которые стоит и полезно обратить внимание, а также которые выгодно отличают его от схожих алгоритмов, решающих ту же задачу.&amp;lt;br&amp;gt;&lt;br /&gt;
1) Отношение последовательной сложности алгоритма к параллельной,с учётом &amp;lt;math style=&amp;quot;vertical-align:0%&amp;gt;k&amp;lt;/math&amp;gt; итераций, равно &amp;lt;math&amp;gt;\frac{kn^2}{k \log_2{n}}=\frac{n^2}{\log_2{n}}&amp;lt;/math&amp;gt;;&amp;lt;br&amp;gt;&lt;br /&gt;
2) Вычислительная мощность алгоритма, как отношение числа операций к суммарному объему входных и выходных данных, равна &amp;lt;math style=&amp;quot;vertical-align:-70%&amp;gt;\frac{k\,n^2}{0.5\,n^2+1.5\,n+k\,n+k+1}&amp;lt;/math&amp;gt; и &amp;lt;math style=&amp;quot;vertical-align:-10%&amp;gt;\approx 2\,k&amp;lt;/math&amp;gt;, так как на практике верно отношение &amp;lt;math style=&amp;quot;vertical-align:0%&amp;gt;k&amp;lt;&amp;lt;n&amp;lt;/math&amp;gt; ;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
3) Для собственных значений формируемых матриц &amp;lt;math style=&amp;quot;vertical-align:-20%&amp;gt;T_j&amp;lt;/math&amp;gt; верно отношение (следствие теоремы Коши о перемежаемости): &amp;lt;math style=&amp;quot;vertical-align:-30%&amp;gt;\lambda_i(T_{k+1})\geq \lambda_i(T_{k})\geq \lambda_{i+1}(T_{k+1})\geq \lambda_{i+1}(T_{k})&amp;lt;/math&amp;gt; ;  &amp;lt;span style=&amp;quot;color:#4169E1&amp;quot;&amp;gt;[2, c.383]&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
4) Крайние (наибольшие и наименьшие) собственные значения матриц &amp;lt;math style=&amp;quot;vertical-align:-20%&amp;gt;T_j&amp;lt;/math&amp;gt; сходятся к крайним собственным значениям матрицы &amp;lt;math style=&amp;quot;vertical-align:0%&amp;gt;A&amp;lt;/math&amp;gt; первыми (это часто происходит уже на ранних шагах при &amp;lt;math style=&amp;quot;vertical-align:-20%&amp;gt;j\approx 2\,\sqrt{n}&amp;lt;/math&amp;gt;&amp;lt;ref&amp;gt;http://num-anal.srcc.msu.ru/lib_na/int_ae/int_ae6.htm (пункт 6.5)&amp;lt;/ref&amp;gt;), а собственные значения в середине спектра - последними;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
5) Из-за ограниченной точности постепенно теряется ортогональность вектора Ланцоша &amp;lt;math style=&amp;quot;vertical-align:-20%&amp;gt;q_j&amp;lt;/math&amp;gt; к предыдущим векторам Ланцоша, поэтому необходимо примерно каждые 10-15 итераций проводить дорогостоящую операцию реортогонализации, но это уже будет иной алгоритм с очень малым различием в одной операции и в коде, реализующем его, соответственно; &amp;lt;ref&amp;gt;Sam Handler - The Lanczos Algorithm For Large-Scale Eigenvalue Problems (08.01.07), slide №8&amp;lt;/ref&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
6) Эффективность и быстрота выполнения алгоритма очень сильно зависит от скорости памяти (диска). Согласно тесту только &#039;&#039;6%&#039;&#039; времени работы программы приходится на вычисления, остальное время затрачивается на ожидание отклика диска;&amp;lt;ref&amp;gt;Sam Handler - The Lanczos Algorithm For Large-Scale Eigenvalue Problems (08.01.07), slide №10&amp;lt;/ref&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
7) Метод Ланцоша удобен тем, что не требует хранения исходной матрицы полным массивом: метод использует исходную матрицу только в операциях умножения матрицы на вектор, что позволяет эффективно использовать разреженность матрицы или регулярность структуры матрицы. Поэтому метод Ланцоша может быть использован для матриц таких больших порядков, когда методы вращений и отражений не могут быть применены.&lt;br /&gt;
&lt;br /&gt;
= Программная реализация алгоритма =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Особенности реализации последовательного алгоритма ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Локальность данных и вычислений ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Возможные способы и особенности параллельной реализации алгоритма ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&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;br&amp;gt;&lt;br /&gt;
Перечислим далее некоторые из них, указывая язык программирования, лежащий в их основе, и некоторые важные характеристики, которые обсуждались в статье ранее:&lt;br /&gt;
*&#039;&#039;Code on netlib.org by Jane Cullum and Ralph Willoughby&#039;&#039; (&#039;&#039;&#039;Fortran 77&#039;&#039;&#039;; без реортогонализации векторов; без реализации распараллеливания)&amp;lt;ref&amp;gt;http://www.netlib.org/lanczos/leval&amp;lt;/ref&amp;gt;&lt;br /&gt;
*&#039;&#039;Lanczos algorithm on freesourcecode.net&#039;&#039; (&#039;&#039;&#039;Matlab&#039;&#039;&#039;; присутствует реортогонализация векторов; без реализации распараллеливания)&amp;lt;ref&amp;gt;http://freesourcecode.net/matlabprojects/61741/lanczos-algorithm--in-matlab&amp;lt;/ref&amp;gt;&lt;br /&gt;
*&#039;&#039;Lanczos for eigenvalues in Numerical Linear Algebra, Spring 2007&#039;&#039; (&#039;&#039;&#039;Matlab&#039;&#039;&#039;; частичная или полная реортогонализация - доступны оба варианта; без реализации распараллеливания)&amp;lt;ref&amp;gt;Ссылки на файлы lanso.m и lanfu.m можно найти на https://www.nada.kth.se/~ruhe/2D5219/labs2007.html&amp;lt;/ref&amp;gt;&lt;br /&gt;
*&#039;&#039;Planso&#039;&#039; (название происходит от parallel Lanso.f) &#039;&#039;code by Kesheng Wu and Horst D. Simon&#039;&#039; (&#039;&#039;&#039;Fortran 77&#039;&#039;&#039;; частичная реортогонализация векторов; использование MPI для коммуникаций)&amp;lt;ref&amp;gt;https://github.com/jiahao/planso.jl/tree/master/external/PLAN/plan К сожалению, ссылка в описании пакета на github, ведущая на сайт National Energy Research Scientific Computing Center (NERSC), с более подробной информацией о данной программе больше не активна и выдаёт результат &#039;&#039;Page not found&#039;&#039;&amp;lt;/ref&amp;gt; &lt;br /&gt;
* &#039;&#039;Lanczos.cpp in the Vienna Computing Library&#039;&#039;  (&#039;&#039;&#039;C++&#039;&#039;&#039;, частичная реортогонализация, без реализации распараллеливания) &amp;lt;ref&amp;gt;http://viennacl.sourceforge.net/doc/lanczos_8cpp_source.html&amp;lt;/ref&amp;gt;&lt;br /&gt;
* &#039;&#039;Parallel Eigen Values code using C++ AMP on microsoft msdn blog&#039;&#039; (&#039;&#039;&#039;C++&#039;&#039;&#039;; ?(нужно проанализировать код на наличие ортогонализации)? ; распараллеливание есть и реализовано через C++ AMP) &amp;lt;ref&amp;gt;https://blogs.msdn.microsoft.com/nativeconcurrency/2013/12/20/eigen-values/ Подробнее о C++ Accelerated Massive Parallelism можно почитать здесь: https://msdn.microsoft.com/ru-ru/library/hh265137(v=vs.140).aspx или здесь: http://professorweb.ru/my/csharp/optimization/level4/4_6.php &amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
[1] Парлетт Б. - Симметричная проблема собственных значений. Численные методы //М.: Мир. - 1983. - С. 276-294&lt;br /&gt;
&lt;br /&gt;
[2] James W. Demmel - Вычислительная линейная алгебра. Теория и приложения //Мир. - 2001. С. 381-391&lt;br /&gt;
&lt;br /&gt;
[3] Голуб Дж., Ван Лоун Ч. - Матричные вычисления//М.: Мир. - 1999. - С. 426-436&lt;br /&gt;
&lt;br /&gt;
[4] https://ru.wikipedia.org/wiki/Алгоритм_вычисления_собственных_значений#.D0.9C.D0.B0.D1.82.D1.80.D0.B8.D1.86.D1.8B_.D0.A5.D0.B5.D1.81.D1.81.D0.B5.D0.BD.D0.B1.D0.B5.D1.80.D0.B3.D0.B0_.D0.B8_.D1.82.D1.80.D1.91.D1.85.D0.B4.D0.B8.D0.B3.D0.BE.D0.BD.D0.B0.D0.BB.D1.8C.D0.BD.D1.8B.D0.B5_.D0.BC.D0.B0.D1.82.D1.80.D0.B8.D1.86.D1.8B&lt;br /&gt;
&lt;br /&gt;
[5] http://num-anal.srcc.msu.ru/lib_na/int_ae/int_ae6.htm&lt;br /&gt;
&lt;br /&gt;
[[en:Description of algorithm properties and structure]]&lt;/div&gt;</summary>
		<author><name>Lineprinter</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:Davletshuna_Alexandra/%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D0%AF%D0%BA%D0%BE%D0%B1%D0%B8_%D0%B2%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D1%8F_%D1%81%D0%B8%D0%BD%D0%B3%D1%83%D0%BB%D1%8F%D1%80%D0%BD%D1%8B%D1%85_%D1%87%D0%B8%D1%81%D0%B5%D0%BB_%D0%B8_%D0%B2%D0%B5%D0%BA%D1%82%D0%BE%D1%80%D0%BE%D0%B2&amp;diff=16217</id>
		<title>Участник:Davletshuna Alexandra/Метод Якоби вычисления сингулярных чисел и векторов</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:Davletshuna_Alexandra/%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D0%AF%D0%BA%D0%BE%D0%B1%D0%B8_%D0%B2%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D1%8F_%D1%81%D0%B8%D0%BD%D0%B3%D1%83%D0%BB%D1%8F%D1%80%D0%BD%D1%8B%D1%85_%D1%87%D0%B8%D1%81%D0%B5%D0%BB_%D0%B8_%D0%B2%D0%B5%D0%BA%D1%82%D0%BE%D1%80%D0%BE%D0%B2&amp;diff=16217"/>
		<updated>2016-11-09T00:09:46Z</updated>

		<summary type="html">&lt;p&gt;Lineprinter: Lineprinter переименовал страницу Участник:Давлетшина Александра, Зайцева Александра/Метод Якоби вычисления сингулярных чисел и векторов…&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment|ASA}}&lt;br /&gt;
&lt;br /&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^2)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;O(n)&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;2n^2 + n&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Авторы статьи: [[Участница:Davletshuna Alexandra|Давлетшина Александра]] (группа 619) пункты 1.1-1.4, 1.6, 2.7; [[Участница:Alek sandra|Зайцева Александра]] (группа 601) пункты 1.5, 1.7-1.10, 2.7.&lt;br /&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;AA^*&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
•	правые сингулярные векторы матрицы A — это собственные векторы матрицы &amp;lt;math&amp;gt;A^*A&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Геометрическая интерпретация сингулярного разложения заключается в следующем факте из геометрии: &#039;&#039;Образом любого линейного преобразования, заданного с помощью матрицы &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt;x&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;, примененного к единичной сфере является гиперэллипсоид&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Из многочисленных разложений матриц, наиболее удобным является сингулярное разложение матрицы в виде: &amp;lt;math&amp;gt;A=U\Sigma V^*&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;U, V&amp;lt;/math&amp;gt; – унитарные матрицы, а &amp;lt;math&amp;gt;\Sigma&amp;lt;/math&amp;gt; – диагональная матрица с вещественными положительными числами на диагонали &amp;lt;ref&amp;gt;http://algorithms.parallel.ru/&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Диагональные элементы матрицы &amp;lt;math&amp;gt;\Sigma&amp;lt;/math&amp;gt; называются сингулярными числами матрицы &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt;, а столбцы матриц &amp;lt;math&amp;gt;U,V&amp;lt;/math&amp;gt; левыми и правыми сингулярными векторами соответственно.&lt;br /&gt;
&lt;br /&gt;
Алгоритм Якоби сингулярного разложения матрицы был предложен одним из первых в 1846 году. Он приводит прямоугольную матрицу к диагональной матрице с помощью последовательности элементарных вращений. Метод может найти все сингулярные значения с очень высокой точностью. Однако его производительность является довольно низкой, в сравнении с конкурирующими методами.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Неявный&#039;&#039;&amp;lt;ref&amp;gt;Дж. Деммель «Вычислительная линейная алгебра» (стр. 261-264)&amp;lt;/ref&amp;gt; метод Якоби математически эквивалентен применению метода Якоби вычисления собственных значений к матрице &amp;lt;math&amp;gt;A = G^TG&amp;lt;/math&amp;gt;.&lt;br /&gt;
Это значит, что на каждом шаге вычисляется вращение Якоби &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt;, с помощью которого матрица &amp;lt;math&amp;gt;G^TG&amp;lt;/math&amp;gt; неявно пересчитывется в &amp;lt;math&amp;gt;J^TG^TGJ&amp;lt;/math&amp;gt;; вращение выбрано так, чтобы пара внедиагональных элементов из &amp;lt;math&amp;gt;G^TG&amp;lt;/math&amp;gt; обратилась в нули в матрице &amp;lt;math&amp;gt;J^TG^TGJ&amp;lt;/math&amp;gt;. При этом ни &amp;lt;math&amp;gt;G^TG&amp;lt;/math&amp;gt;, ни &amp;lt;math&amp;gt;J^TG^TGJ&amp;lt;/math&amp;gt; не вычисляются в явном виде; вместо них вычисляется матрица &amp;lt;math&amp;gt;GJ&amp;lt;/math&amp;gt;. Поэтому алгоритм называется &#039;&#039;методом односторонних вращений&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
Исходные данные: матрица &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt; (элементы &amp;lt;math&amp;gt;g_{ij}, i, j = 1, \ldots, n&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Вычисляемые данные: матрица &amp;lt;math&amp;gt;\Sigma = diag(\sigma_{i})&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\sigma_{i}&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;A = (G^TG)&amp;lt;/math&amp;gt; можно привести к диагональному виду:&lt;br /&gt;
&amp;lt;math&amp;gt;RG^TGR^T&amp;lt;/math&amp;gt;, где&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
R(j,k,\theta) = &lt;br /&gt;
\begin{matrix}&lt;br /&gt;
  \\&lt;br /&gt;
  \\&lt;br /&gt;
  \\&lt;br /&gt;
j \\&lt;br /&gt;
  \\&lt;br /&gt;
k \\&lt;br /&gt;
  \\&lt;br /&gt;
  \\&lt;br /&gt;
  \\&lt;br /&gt;
\end{matrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;  &lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\begin{bmatrix}&lt;br /&gt;
  &amp;amp; 1 &amp;amp;   &amp;amp;        &amp;amp;              &amp;amp;        &amp;amp;               &amp;amp;         &amp;amp;    &amp;amp;   \\&lt;br /&gt;
  &amp;amp;   &amp;amp; 1 &amp;amp;        &amp;amp;              &amp;amp;        &amp;amp;               &amp;amp;         &amp;amp;    &amp;amp;   \\&lt;br /&gt;
  &amp;amp;   &amp;amp;   &amp;amp; \ddots &amp;amp;              &amp;amp;        &amp;amp;               &amp;amp;         &amp;amp;    &amp;amp;   \\&lt;br /&gt;
  &amp;amp;   &amp;amp;   &amp;amp;        &amp;amp; \cos(\theta) &amp;amp;        &amp;amp; -\sin(\theta) &amp;amp;         &amp;amp;    &amp;amp;   \\&lt;br /&gt;
  &amp;amp;   &amp;amp;   &amp;amp;        &amp;amp;              &amp;amp; \ddots &amp;amp;               &amp;amp;         &amp;amp;    &amp;amp;   \\&lt;br /&gt;
  &amp;amp;   &amp;amp;   &amp;amp;        &amp;amp; \sin(\theta) &amp;amp;        &amp;amp; \cos(\theta)  &amp;amp;         &amp;amp;    &amp;amp;   \\&lt;br /&gt;
  &amp;amp;   &amp;amp;   &amp;amp;        &amp;amp;              &amp;amp;        &amp;amp;               &amp;amp; \ddots  &amp;amp;    &amp;amp;   \\&lt;br /&gt;
  &amp;amp;   &amp;amp;   &amp;amp;        &amp;amp;              &amp;amp;        &amp;amp;               &amp;amp;         &amp;amp; 1  &amp;amp;   \\&lt;br /&gt;
  &amp;amp;   &amp;amp;   &amp;amp;        &amp;amp;              &amp;amp;        &amp;amp;               &amp;amp;         &amp;amp;    &amp;amp; 1 \\&lt;br /&gt;
\end{bmatrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Обозначим &amp;lt;math&amp;gt;s = \sin \theta&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;c = \cos \theta&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Перемножив матрицы &amp;lt;math&amp;gt;RG^TGR^T&amp;lt;/math&amp;gt;, получим следующие выражения&amp;lt;ref&amp;gt;http://www.astro.tsu.ru/OsChMet/5_2.html&amp;lt;/ref&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
 a_{jj}^{(i+1)} &amp;amp;= c^2\, a_{jj}^{(i)}  -  2\, s c \,a_{jk}^{(i)}  +  s^2\, a_{kk}^{(i)} \\&lt;br /&gt;
 a_{kk}^{(i+1)} &amp;amp;= s^2 \,a_{jj}^{(i)}  +  2 s c\, a_{jk}^{(i)}  +  c^2 \, a_{kk}^{(i)} \\&lt;br /&gt;
 a_{jk}^{(i+1)} &amp;amp;= a_{kj}^{(i+1)} = (c^2 - s^2 ) \, a_{jk}^{(i)}  +  s c \, (a_{kk}^{(i)} - a_{jj}^{(i)} ) \\&lt;br /&gt;
 a_{jm}^{(i+1)} &amp;amp;= a_{mj}^{(i+1)} = c \, a_{jm}^{(i)}  -  s \, a_{km}^{(i)} &amp;amp; m \ne j,k \\&lt;br /&gt;
 a_{km}^{(i+1)} &amp;amp;= a_{mk}^{(i+1)} = s \, a_{jm}^{(i)}  + c \, a_{km}^{(i)} &amp;amp; m \ne j,k \\&lt;br /&gt;
 a_{ml}^{(i+1)} &amp;amp;= a_{ml}^{(i)} &amp;amp;m,l \ne j,k&lt;br /&gt;
\end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Т.к внедиагональные элементы должны быть равны 0, то выбираем &amp;lt;math&amp;gt;\theta&amp;lt;/math&amp;gt; так, чтобы &amp;lt;math&amp;gt;a_{jk}^{(i+1)} = 0&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;a_{kj}^{(i+1)} = 0&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;
         \tau &amp;amp;= {a_{jj} - a_{kk}}/{2a_{jk}} \\&lt;br /&gt;
         t &amp;amp;= {sign(\tau)}/(|\tau| + \sqrt{1 + \tau^2}) \\&lt;br /&gt;
         c &amp;amp;= 1/(\sqrt{1 + t^2}) \\&lt;br /&gt;
         s &amp;amp;= ct \\&lt;br /&gt;
         \end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Остановка вычислений алгоритма наступает, когда внедиагональные элементы становятся меньше некоторого наперед заданного числа &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро алгоритма составляет непосредственно вычисление одностороннего вращения Якоби, т.е. вычисление элементов матрицы &amp;lt;math&amp;gt;g_{jm}^{(i+1)} = g_{mj}^{(i+1)}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;g_{km}^{(i+1)} = g_{mk}^{(i+1)}&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;m \ne j,k&amp;lt;/math&amp;gt;, а также вычисления элементов &amp;lt;math&amp;gt;g_{jj}^{(i+1)} &amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;g_{kk}^{(i+1)} &amp;lt;/math&amp;gt;, получившихся после умножения промежуточной матрицы на матрицу вращений Якоби:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\begin{align}&lt;br /&gt;
 g_{jj}^{(i+1)} &amp;amp;= c^2\, g_{jj}^{(i)}  -  2\, s c \,g_{jk}^{(i)}  +  s^2\, g_{kk}^{(i)} \\&lt;br /&gt;
 g_{kk}^{(i+1)} &amp;amp;= s^2 \,g_{jj}^{(i)}  +  2 s c\, g_{jk}^{(i)}  +  c^2 \, g_{kk}^{(i)} \\&lt;br /&gt;
 g_{jk}^{(i+1)} &amp;amp;= g_{kj}^{(i+1)} = (c^2 - s^2 ) \, g_{jk}^{(i)}  +  s c \, (g_{kk}^{(i)} - g_{jj}^{(i)} ) \\&lt;br /&gt;
 g_{jm}^{(i+1)} &amp;amp;= g_{mj}^{(i+1)} = c \, g_{jm}^{(i)}  -  s \, g_{km}^{(i)} &amp;amp; m \ne j,k \\&lt;br /&gt;
 g_{km}^{(i+1)} &amp;amp;= g_{mk}^{(i+1)} = s \, g_{jm}^{(i)}  + c \, g_{km}^{(i)} &amp;amp; m \ne j,k \\&lt;br /&gt;
 g_{ml}^{(i+1)} &amp;amp;= g_{ml}^{(i)} &amp;amp;m,l \ne j,k&lt;br /&gt;
\end{align}&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;\tau, c, s, t&amp;lt;/math&amp;gt;.&lt;br /&gt;
#Одностороннее вращение матрицы. &lt;br /&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;
#Вычислить сингулярные числа &amp;lt;math&amp;gt;\sigma_i&amp;lt;/math&amp;gt;.  Что требует &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; операций.&lt;br /&gt;
#Вычислить &amp;lt;math&amp;gt;U&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt;. Что требует &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt; операций.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
Применение метода Якоби к матрице &amp;lt;math&amp;gt;A = (G^TG)&amp;lt;/math&amp;gt; можно описать следующим образом&amp;lt;ref&amp;gt;Дж. Деммель «Вычислительная линейная алгебра» (стр. 261-263)&amp;lt;/ref&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;G = U\Sigma V^T&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;\Sigma = diag(\sigma_{i})&amp;lt;/math&amp;gt;, то алгоритм вычисляет сингулярные числа &amp;lt;math&amp;gt;\sigma_{i}&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;
 repeat&lt;br /&gt;
     for &amp;lt;math&amp;gt;j=1&amp;lt;/math&amp;gt; to &amp;lt;math&amp;gt;n-1&amp;lt;/math&amp;gt;&lt;br /&gt;
         for &amp;lt;math&amp;gt;k=j+1&amp;lt;/math&amp;gt; to &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;&lt;br /&gt;
             обратиться к процедуре &#039;&#039;&#039;&#039;&#039;One-Sided-Jacobi-Rotation&amp;lt;math&amp;gt;(G,j,k)&amp;lt;/math&amp;gt;&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
         end for&lt;br /&gt;
     end for&lt;br /&gt;
 пока &amp;lt;math&amp;gt;G^TG&amp;lt;/math&amp;gt; не станет достаточно близка к диагональной матрице&lt;br /&gt;
 Положить &amp;lt;math&amp;gt;\sigma_{i} = ||G(:,i)||_2 &amp;lt;/math&amp;gt; (норма &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-го столбца в &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;)&lt;br /&gt;
 Положить &amp;lt;math&amp;gt;U = [u_{1},\dots, u_{n}]&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;u_{i} = G(:,i)/\sigma_{i}&amp;lt;/math&amp;gt;&lt;br /&gt;
 Положить &amp;lt;math&amp;gt;V = J&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;J&amp;lt;/math&amp;gt; - накопленное произведение вращений Якоби.&lt;br /&gt;
&lt;br /&gt;
Где процедура &#039;&#039;&#039;&#039;&#039;One-Sided-Jacobi-Rotation&#039;&#039;&#039;&#039;&#039; является метод односторонних вращений Якоби:&lt;br /&gt;
&lt;br /&gt;
 proc &#039;&#039;&#039;&#039;&#039;One-Sided-Jacobi-Rotation&amp;lt;math&amp;gt;(G,j,k)&amp;lt;/math&amp;gt;&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
     вычислить &amp;lt;math&amp;gt;a_{jj} = (G^TG)_{jj}, a_{jk} = (G^TG)_{jk}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;a_{kk} = (G^TG)_{kk}&amp;lt;/math&amp;gt;&lt;br /&gt;
     if &amp;lt;math&amp;gt;|a_{jk}|&amp;lt;/math&amp;gt; не слишком мал&lt;br /&gt;
         &amp;lt;math&amp;gt;&lt;br /&gt;
         \begin{align}&lt;br /&gt;
         \tau &amp;amp;= {a_{jj} - a_{kk}}/{2a_{jk}} \\&lt;br /&gt;
         t &amp;amp;= {sign(\tau)}/(|\tau| + \sqrt{1 + \tau^2}) \\&lt;br /&gt;
         c &amp;amp;= 1/(\sqrt{1 + t^2}) \\&lt;br /&gt;
         s &amp;amp;= ct \\&lt;br /&gt;
         G &amp;amp;= GR(j,k,\theta) \  \dots c=cos\theta, s=sin\theta \end{align}&amp;lt;/math&amp;gt;&lt;br /&gt;
         if нужны правые сингулярные векторы &lt;br /&gt;
             &amp;lt;math&amp;gt;J = JR(j,k,\theta)&amp;lt;/math&amp;gt;&lt;br /&gt;
         end if&lt;br /&gt;
     end if&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Для одного вращения Якоби необходимо выполнить &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; операций умножения и сложения. &lt;br /&gt;
&lt;br /&gt;
Всего вариантов выбора &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;k&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;
=== Информационный граф ===&lt;br /&gt;
Граф алгоритма состоит из четырех групп вершин&lt;br /&gt;
&lt;br /&gt;
(А)Первая группа вершин расположена в двумерной области, соответствующая ей операция - вычисление элементов &amp;lt;math&amp;gt;a_{jj},a_{jk},a_{kk}&amp;lt;/math&amp;gt; матрицы произведения &amp;lt;math&amp;gt;A = (G^TG)&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;, принимая все целочисленные значения, &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;  изменяется от &amp;lt;math&amp;gt;j+1&amp;lt;/math&amp;gt; до &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;, принимая все целочисленные значения.&lt;br /&gt;
&lt;br /&gt;
Граф вычисления каждого из элементов приведен на рисунке 1, где значениям &amp;lt;math&amp;gt;g_{ij}&amp;lt;/math&amp;gt; соответствуют обозначения &amp;lt;math&amp;gt;x_{ij}&amp;lt;/math&amp;gt;, а элементам &amp;lt;math&amp;gt;g^T_{ij}&amp;lt;/math&amp;gt; обозначения &amp;lt;math&amp;gt;z_{ij}&amp;lt;/math&amp;gt;. Значение &amp;lt;math&amp;gt;s_{ij}&amp;lt;/math&amp;gt; является значением результирующего элемента.&lt;br /&gt;
&lt;br /&gt;
(Б)Вторая группа вершин расположена в двумерной области, соответствующая ей операция – вычисление &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Информационный граф с входными и выходными данными вычисления пункта (Б) представлен на рисунке 2.&lt;br /&gt;
&lt;br /&gt;
(В)Третья группа вершин расположена в двумерной области, соответствующая ей операция вычисление значений элементов &amp;lt;math&amp;gt;g_{jm}^{(i+1)} = g_{mj}^{(i+1)}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;g_{km}^{(i+1)} = g_{mk}^{(i+1)}&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;m \ne j,k&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
(Г)Четвертая группа вершин расположена в двумерной области, соответствующая ей операция вычисление значений элементов &amp;lt;math&amp;gt;g_{jj}^{(i+1)} &amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;g_{kk}^{(i+1)} &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;R&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Jacobi_2.jpg|none|thumb|1000px|Рисунок 1. Информационный граф с входными и выходными параметрами вычисления каждого элемента матрицы произведения.]]&lt;br /&gt;
&lt;br /&gt;
[[File:Jacobi_1.jpg|none|thumb|1000px|Рисунок 2. Информационный граф с входными и выходными параметрами вычисления значений &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt;.]]&lt;br /&gt;
&lt;br /&gt;
Обобщенный граф метода Якоби представлен на рисунке 3, где буквами А, Б, В и Г обозначено вычисление групп вершин (А), (Б), (В) и (Г).&lt;br /&gt;
&lt;br /&gt;
[[File:Graf21.png|none|thumb|2000px|Рисунок 3. Обобщенный граф алгоритма вычисления сингулярных чисел и векторов методом Якоби.]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
Для выполнения одной итерации метода Якоби требуется последовательно выполнить несколько ярусов:&lt;br /&gt;
# вычислить &amp;lt;math&amp;gt;\tau&amp;lt;/math&amp;gt; &lt;br /&gt;
# вычислить &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; &lt;br /&gt;
# вычислить &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; &lt;br /&gt;
# выполнить поворот матрицы &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt; с параллельным выполнением &amp;lt;math&amp;gt;2(n-2)&amp;lt;/math&amp;gt; операций вычисления элементов матрицы &amp;lt;math&amp;gt;g_{jm}^{(i+1)} = g_{mj}^{(i+1)}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;g_{km}^{(i+1)} = g_{mk}^{(i+1)}&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;m \ne j,k&amp;lt;/math&amp;gt;, а также вычисления элементов &amp;lt;math&amp;gt;g_{jj}^{(i+1)} &amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;g_{kk}^{(i+1)} &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Суммарное количество итераций равно &amp;lt;math&amp;gt;\frac{n(n-1)}{2}&amp;lt;/math&amp;gt; (Различные варианты выбора &amp;lt;math&amp;gt;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;O(n^2)&amp;lt;/math&amp;gt;&amp;amp;nbsp;, а при классификации по ширине ЯПФ — к алгоритмам со сложностью &amp;lt;math&amp;gt;O(n)&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;G&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;A= G^TG&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; (т.к. для хранения матрицы &amp;lt;math&amp;gt;G&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;)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные&#039;&#039;&#039;: &amp;lt;math&amp;gt;\sigma_{i}&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;2n^2 + n&amp;lt;/math&amp;gt; (т.к. необходимо хранить вектор сингулярных чисел длины &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;, а также две матрицы &amp;lt;math&amp;gt;U, V&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;).&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
Метод Якоби является самым медленным из имеющихся алгоритмов вычисления сингулярных чисел и сингулярных векторов матрицы. Тем не менее, для  матриц &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;, которые могут быть представлены в виде &amp;lt;math&amp;gt;G=DX&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; - диагональная, а &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt; - хорошо обусловлена, он способен вычислять сингулярные числа и вектора намного точнее, чем другие методы&amp;lt;ref&amp;gt;Дж. Деммель «Вычислительная линейная алгебра» (стр. 261-264)&amp;lt;/ref&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Вычислительная мощность алгоритма, как отношение числа операций к суммарному объему входных и выходных данных является линейной.&lt;br /&gt;
&lt;br /&gt;
Метод Якоби не детерминирован, так как является итерационным алгоритмом с выходом по точности (число итераций зависит от входных данных и порогового значения).&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
Метод Якоби нахождения сингулярных значений и векторов реализован в таких пакетах, как LAPACK, LINPACK/EISPACK, Intel MKL &amp;lt;ref&amp;gt;http://www.alglib.net/matrixops/general/svd.php&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Так же функция Якоби реализована в пакете Matlab.&lt;br /&gt;
&lt;br /&gt;
Т. к. алгоритм считается медленным, он не включен во многие известные пакеты.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
1. Дж. Деммель Вычислительная линейная алгебра. Изд. Мир, 2001.&lt;/div&gt;</summary>
		<author><name>Lineprinter</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:%D0%90%D0%BD%D0%B4%D1%80%D0%B5%D0%B9_%D0%A2%D1%83%D0%BC%D0%B0%D0%BD%D0%BE%D0%B2/%D0%90%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_%D0%BA%D0%B0%D1%82%D0%B5%D0%B3%D0%BE%D1%80%D0%B8%D0%B0%D0%BB%D1%8C%D0%BD%D1%8B%D1%85_%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85_CLOPE&amp;diff=16216</id>
		<title>Участник:Андрей Туманов/Алгоритм кластеризации категориальных данных CLOPE</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:%D0%90%D0%BD%D0%B4%D1%80%D0%B5%D0%B9_%D0%A2%D1%83%D0%BC%D0%B0%D0%BD%D0%BE%D0%B2/%D0%90%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_%D0%BA%D0%B0%D1%82%D0%B5%D0%B3%D0%BE%D1%80%D0%B8%D0%B0%D0%BB%D1%8C%D0%BD%D1%8B%D1%85_%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85_CLOPE&amp;diff=16216"/>
		<updated>2016-11-09T00:06:10Z</updated>

		<summary type="html">&lt;p&gt;Lineprinter: Lineprinter переименовал страницу Алгоритм кластеризации категориальных данных CLOPE в [[Участник:Андрей Туманов/Алгоритм кластеризации кате…&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment}}&lt;br /&gt;
&lt;br /&gt;
Основные авторы описания: [[Участник:Андрей_Туманов|А.Л.Туманов]] ([[#Общее описание алгоритма|1.1]], [[#Математическое описание алгоритма|1.2]], [[#Вычислительное ядро алгоритма|1.3]], [[#Макроструктура алгоритма|1.4]], [[#Схема реализации последовательного алгоритма|1.5]], [[#Входные и выходные данные алгоритма|1.9]]), [[Участник:Boltonidze|А.В.Александров]] ([[#Последовательная сложность алгоритма|1.6]], [[#Информационный граф|1.7]], [[#Ресурс параллелизма алгоритма|1.8]], [[#Свойства алгоритма|1.10]] [[#Существующие реализации алгоритма|2.7]])&lt;br /&gt;
&lt;br /&gt;
= Свойства и структура алгоритмов =&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
Алгоритм CLOPE (Clustering with sLOPE&amp;lt;ref&amp;gt;Yang, Y., Guan, H., You. J. CLOPE: A fast and Effective Clustering Algorithm for Transactional Data In Proc. of SIGKDD’02, July 23-26, 2002, Edmonton, Alberta, Canada.&amp;lt;/ref&amp;gt;) неирархический метод кластеризации, предназначенный для кластеризации огромных наборов категориальных данных. К достоинствам алгоритма относятся высокие масштабируемость и скорость работы и качество кластеризации, что достигается использованием глобального критерия оптимизации на основе максимизации градиента высоты гистограммы кластера. Отличается простотой программной реализации. &lt;br /&gt;
&lt;br /&gt;
Во время работы алгоритм хранит в памяти небольшое количество информации по каждому кластеру и требует минимальное число сканирований набора данных. CLOPE автоматически подбирает количество кластеров, причем это регулируется одним единственным параметром - коэффициентом отталкивания &amp;lt;math&amp;gt; r &amp;lt;/math&amp;gt;. При этом он обеспечивает более высокую производительность и лучшее качество кластеризации в сравнении с многими иерархическими алгоритмами.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
Пусть имеется база транзакций &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt;, состоящая из множества транзакций &amp;lt;math&amp;gt;\{t_1,t_2,...,t_n\}&amp;lt;/math&amp;gt;. Каждая транзакция есть набор объектов &amp;lt;math&amp;gt;\{i_1,...,i_m\}&amp;lt;/math&amp;gt;. Множество кластеров &amp;lt;math&amp;gt;\{C_1,...,C_k\}&amp;lt;/math&amp;gt; есть разбиение множества &amp;lt;math&amp;gt;\{t_1,...,t_n\}&amp;lt;/math&amp;gt;, такое, что &amp;lt;math&amp;gt;C_1 \cup \dots \cup C_k=\{t_1,...,t_n\}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;C_i \ne \empty&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;C_i \cap C_j = \empty &amp;lt;/math&amp;gt;, для &amp;lt;math&amp;gt;i \ge 1, k \ge j&amp;lt;/math&amp;gt;. Каждый элемент &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt; называется кластером, а &amp;lt;math&amp;gt;n, m, k&amp;lt;/math&amp;gt; – количество транзакций, количество объектов в базе транзакций и число кластеров соответственно.&lt;br /&gt;
&lt;br /&gt;
Каждый кластер &amp;lt;math&amp;gt;C&amp;lt;/math&amp;gt; имеет следующие характеристики:&lt;br /&gt;
[[Файл:ExampleCLOPE.jpg|500px|thumb|right]]&lt;br /&gt;
&amp;lt;math&amp;gt;D(C)&amp;lt;/math&amp;gt; – множество уникальных объектов;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;Occ(i,C)&amp;lt;/math&amp;gt; – количество вхождений (частота) объекта &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; в кластер &amp;lt;math&amp;gt;C&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;S(C)= \sum_{i \in D(C)} Occ(i,C)= \sum_{t_i \in C}  \mid t_i  \mid , &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;W(C)=  \mid D(C) \mid ,H(C)=S(C)/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; Profit(C) = \frac{\sum^{k}_{i=1} G(C_i) \times \mid C_i \mid} {\sum^{k}_{i=1} \mid C_i \mid } = \frac{\sum^{k}_{i=1} \frac{S(C_i)}{W(C_i)^r} \times \mid C_i \mid} {\sum^{k}_{i=1} \mid C_i \mid }  &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;\mid C_i \mid&amp;lt;/math&amp;gt;  количество объектов в &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-ом кластере, &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; – количество кластеров, &amp;lt;math&amp;gt;r&amp;lt;/math&amp;gt; – коэффициент отталкивания &amp;lt;math&amp;gt;(0 &amp;lt; r \le 1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
С помощью параметра &amp;lt;math&amp;gt;r&amp;lt;/math&amp;gt; регулируется уровень сходства транзакций внутри кластера, и, как&lt;br /&gt;
следствие, финальное количество кластеров. Этот коэффициент подбирается пользователем. Чем больше &amp;lt;math&amp;gt;r&amp;lt;/math&amp;gt;, тем ниже уровень сходства и тем больше кластеров будет сгенерировано.&lt;br /&gt;
&lt;br /&gt;
Постановка задачи кластеризации алгоритмом CLOPE выглядит следующим образом: &lt;br /&gt;
&lt;br /&gt;
для заданных &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;r&amp;lt;/math&amp;gt; найти разбиение &amp;lt;math&amp;gt;C&amp;lt;/math&amp;gt; такое, что: &lt;br /&gt;
&amp;lt;math&amp;gt;Profit(C) \longrightarrow max &amp;lt;/math&amp;gt; &amp;lt;ref&amp;gt;Нейский И.М. Классификация и сравнение методов кластеризации.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро алгоритма состоит в вычислении на каждой итерации стоимости добавления и удаления транзакции из кластера: &lt;br /&gt;
&lt;br /&gt;
*вычисление нового размера кластера &amp;lt;math&amp;gt;S_{new}(C_i)=S(C_i) \pm \mid(t_n)\mid &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\mid(t_n)\mid&amp;lt;/math&amp;gt; - длина транзакции&lt;br /&gt;
&lt;br /&gt;
*вычисление новой ширины кластера &amp;lt;math&amp;gt;W_{new}(C_i)=\mid D(C_i \cup t_n)|&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*вычисление стоимости &amp;lt;math&amp;gt;\frac{S_{new}(C_i)(\mid C_i \mid \pm 1)}{W_{new}(C_i)^r} - \frac{S(C_i) \mid C_i \mid }{W(C_i)^r}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
Алгоритм состоит из следующих этапов:&lt;br /&gt;
&lt;br /&gt;
1. &#039;&#039;&#039;Инициализация&#039;&#039;&#039;. На этом этапе происходит первый проход по таблице с транзакциями для построения начального разбиения, для каждой транзакции определяется кластер исходя из максимизации стоимости.&lt;br /&gt;
&lt;br /&gt;
2. &#039;&#039;&#039;Итерация&#039;&#039;&#039;. На данном этапе для каждой транзакции проводится попытка перемещения в другой кластер для максимизации функции стоимости.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Первая часть алгоритма - инициализация: для каждой транзакции вычисляется кластер, к которому она будет отнесена исходя из максимизации функции стоимости. Для этого вычисляется вспомогательная функция DeltaAdd, которая считает стоимость добавления транзакции к одному из существующих кластеров, либо к пустому. В итоге транзакция помещается в кластер, для которого цена добавления максимальна.&lt;br /&gt;
&lt;br /&gt;
Вторая часть алгоритма - уточняющие итерации: здесь происходит попытка улучшить вычисленные на первом этапе кластеры. В каждой итерации происходит перебор всех транзакций, и для каждой транзакции вычисляется цена удаления ее из текущего кластера и цена добавления в другой кластер. Суммарная стоимость перемещения будет являться суммой стоимостей удаления и добавления. Если максимальная стоимость перемещения данной транзакции в какой-либо больше нуля, то она перемещается в этот кластер. &lt;br /&gt;
&lt;br /&gt;
Вычисления происходят до тех пор, пока за итерацию происходит хотя бы одно перемещение транзакции. Если за итерацию не было произведено ни одного перемещения, то текущее состояние кластеров является устойчивым и оптимальным. &amp;lt;ref&amp;gt;Павлин Н. Кластеризация категорийных данных: масштабируемый алгоритм CLOPE, https://basegroup.ru/community/articles/clope&amp;lt;/ref&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;
Входные параметры:&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;r&amp;lt;/math&amp;gt; - коэффициент отталкивания&lt;br /&gt;
&lt;br /&gt;
Выходной параметр:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;C&amp;lt;/math&amp;gt; кластеры, в начале алгоритма - пустое множество&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Инициализация:&#039;&#039;&#039;&lt;br /&gt;
    &#039;&#039;&#039;for&#039;&#039;&#039; t_i in t:&lt;br /&gt;
    max = 0&lt;br /&gt;
        &#039;&#039;&#039;for&#039;&#039;&#039; C_j in C:&lt;br /&gt;
            &#039;&#039;&#039;if&#039;&#039;&#039; DeltaAdd(C_j,t_i,r) &amp;gt; max:&lt;br /&gt;
                max = DeltaAdd(C_j,t_i,r)&lt;br /&gt;
                C_best = C_j&lt;br /&gt;
    &#039;&#039;&#039;put&#039;&#039;&#039; t_i in C_best&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Итерации:&#039;&#039;&#039;&lt;br /&gt;
    move = True&lt;br /&gt;
    &#039;&#039;&#039;while&#039;&#039;&#039; move == True:&lt;br /&gt;
    move = False&lt;br /&gt;
        &#039;&#039;&#039;for&#039;&#039;&#039; t_i in t:&lt;br /&gt;
            max = 0&lt;br /&gt;
            C_now = t_i.cluster&lt;br /&gt;
            removeCost = DeltaRemove(C_now,t_i,r)&lt;br /&gt;
            &#039;&#039;&#039;for&#039;&#039;&#039; C_j in C:&lt;br /&gt;
                &#039;&#039;&#039;if&#039;&#039;&#039; DeltaAdd(C_j,t_i,r) + removeCost &amp;gt; max:&lt;br /&gt;
                    max = DeltaAdd(C_j,t_i,r) + removeCost&lt;br /&gt;
                    C_best = C_j&lt;br /&gt;
        if max &amp;gt; 0&lt;br /&gt;
            &#039;&#039;&#039;del&#039;&#039;&#039; t_i from C_now&lt;br /&gt;
            &#039;&#039;&#039;put&#039;&#039;&#039; t_i in C_best&lt;br /&gt;
            move = True&lt;br /&gt;
&lt;br /&gt;
Вспомогательная функция &#039;&#039;&#039;DeltaAdd&#039;&#039;&#039;:&lt;br /&gt;
    &#039;&#039;&#039;function&#039;&#039;&#039; DeltaAdd(C,t,r)&lt;br /&gt;
        S_new=C.size + t.itemCount&lt;br /&gt;
        W_new=C.wigth&lt;br /&gt;
        &#039;&#039;&#039;for&#039;&#039;&#039; item in t_i:&lt;br /&gt;
            &#039;&#039;&#039;if&#039;&#039;&#039; C_j.occ(item) = 0&lt;br /&gt;
                &#039;&#039;W_new = W_new + 1&lt;br /&gt;
        &#039;&#039;&#039;return&#039;&#039;&#039; S_new * (C.count+1) / (W_new ^ r) - (C.size * C.count) / (C.wigth ^ r)&lt;br /&gt;
&lt;br /&gt;
Вспомогательная функция &#039;&#039;&#039;DeltaRemove&#039;&#039;&#039;:&lt;br /&gt;
    &#039;&#039;&#039;function&#039;&#039;&#039; DeltaRemove(C,t,r)&lt;br /&gt;
        S_new=C.size - t.itemCount&lt;br /&gt;
        W_new=C.wigth&lt;br /&gt;
        &#039;&#039;&#039;for&#039;&#039;&#039; item in t:&lt;br /&gt;
            &#039;&#039;&#039;if&#039;&#039;&#039; C_j.occ(item) = 1&lt;br /&gt;
                &#039;&#039;W_new = W_new - 1&lt;br /&gt;
        &#039;&#039;&#039;return&#039;&#039;&#039; S_new * (C.count-1) / (W_new ^ r) - (C.size * C.count) / (C.wigth ^ r)&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;k&amp;lt;/math&amp;gt; - количество кластеров.&lt;br /&gt;
&lt;br /&gt;
Основные вычисления восполняются в функции DeltaAdd/DeltaRemove: производится 1 сложение, не более &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; сложений/вычитаний, 2 умножения, 2 деления, 2 возведения в степень и 1 сложение - итого &amp;lt;math&amp;gt;m + 8&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;n*k*(m+8)&amp;lt;/math&amp;gt; операций.&lt;br /&gt;
&lt;br /&gt;
В добавок к этому при добавлении/удалении транзакции к кластеру производится пересчет его характеристик: &amp;lt;math&amp;gt;m+2&amp;lt;/math&amp;gt; операции.&lt;br /&gt;
&lt;br /&gt;
Из того можно сделать вывод, что оценка сложность алгоритма равна &amp;lt;math&amp;gt;O(n*k*m)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
На этапе инициализации при обработке каждой транзакции количество кластеров может как увеличиться, так и остаться неизменным. Это показано пунктирными линиями:&lt;br /&gt;
[[Файл:InitCLOPE.jpg|400px|center|Инициализация]]&lt;br /&gt;
На этапе итерации функции DeltaAdd/DeltaRemove могут считать параллельно для каждой транзакции&lt;br /&gt;
[[Файл:IterCLOPE.jpg ‎|400px|center|Итерации]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Вычисление стоимости добавления/удаления транзакции, которые выполняются функциями DeltaAdd/DeltaRemove, происходит независимо для всех &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров. Поэтому все вычисления можно производить параллельно. Таким образом сложность алгоритма с &amp;lt;math&amp;gt;O(n*k*m)&amp;lt;/math&amp;gt; понижается до &amp;lt;math&amp;gt;O(n*m)&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;n&amp;lt;/math&amp;gt; транзакций длины &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; и коэффициент отталкивания &amp;lt;math&amp;gt;r&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;n&amp;lt;/math&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
* Соотношение последовательной и параллельной сложности алгоритма определяется количеством кластеров: &amp;lt;math&amp;gt;\dfrac{O(n*k*m)}{O(n*m)} = k&amp;lt;/math&amp;gt;&lt;br /&gt;
* [[глоссарий#Вычислительная мощность|&#039;&#039;Вычислительная мощность&#039;&#039;]] алгоритма равна &amp;lt;math&amp;gt;\dfrac{nk(m+8) + m + 2}{nm + n + 1} \cong k&amp;lt;/math&amp;gt;&lt;br /&gt;
* Алгоритм не является [[глоссарий#Устойчивость|&#039;&#039;устойчивым&#039;&#039;]], так как результат работы зависит от порядка чтения транзакций и от текущего набора транзакций в кластере.&lt;br /&gt;
* Выполняется свойство сбалансированности операций между параллельными ветвями алгоритма, так как для каждой транзакции выполняются функции DeltaAdd/DeltaRemove, считающие стоимость добавления/удаления транзакции.&lt;br /&gt;
* Свойство [[глоссарий#Детерминированность|&#039;&#039;детерминированности алгоритмов&#039;&#039;]] не выполняется, так как результат алгоритма зависит от порядка прохождения транзакций. Но если рассматривать транзакции как нумерованный список, то результат будет определен однозначно от запуска к запуску. Однако существует расширение алгоритма, обеспечивающее свойство детерминированности. &amp;lt;ref&amp;gt;Li, Y., Le, J., Wang, M. Improving CLOPE’s Profit Value and Stability with an Optimized Agglomerative Approach. Algorithms 2015, 8, 380-394.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
* Так как алгоритм предназначен для кластеризации категориальных данных, то он практически неприменим для числовой кластеризации.&lt;br /&gt;
* Быстро работает даже для большого количества данных, обеспечивая при этом хорошее качество кластеризации&lt;br /&gt;
* Имеет простую реализацию&lt;br /&gt;
&lt;br /&gt;
= Программная реализация алгоритма =&lt;br /&gt;
&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
* http://weka.sourceforge.net/doc.stable/weka/clusterers/CLOPE.html - реализация в open source библиотеке для анализа данных WEKA(Waikato Environment for Knowledge Analysis) на языке Java&lt;br /&gt;
Также существуют пользовательские реализации алгоритма:&lt;br /&gt;
* https://github.com/infozor/php_mysql_clustering_clope - реализация алгоритма на PHP&lt;br /&gt;
* https://github.com/Tolsi/scala-clope - реализация алгоритма Scala&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;/div&gt;</summary>
		<author><name>Lineprinter</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:%D0%90%D0%BD%D0%B4%D1%80%D0%B5%D0%B9_%D0%A2%D1%83%D0%BC%D0%B0%D0%BD%D0%BE%D0%B2/%D0%90%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_%D0%BA%D0%B0%D1%82%D0%B5%D0%B3%D0%BE%D1%80%D0%B8%D0%B0%D0%BB%D1%8C%D0%BD%D1%8B%D1%85_%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85_CLOPE&amp;diff=16215</id>
		<title>Участник:Андрей Туманов/Алгоритм кластеризации категориальных данных CLOPE</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:%D0%90%D0%BD%D0%B4%D1%80%D0%B5%D0%B9_%D0%A2%D1%83%D0%BC%D0%B0%D0%BD%D0%BE%D0%B2/%D0%90%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_%D0%BA%D0%B0%D1%82%D0%B5%D0%B3%D0%BE%D1%80%D0%B8%D0%B0%D0%BB%D1%8C%D0%BD%D1%8B%D1%85_%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85_CLOPE&amp;diff=16215"/>
		<updated>2016-11-09T00:05:48Z</updated>

		<summary type="html">&lt;p&gt;Lineprinter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment}}&lt;br /&gt;
&lt;br /&gt;
Основные авторы описания: [[Участник:Андрей_Туманов|А.Л.Туманов]] ([[#Общее описание алгоритма|1.1]], [[#Математическое описание алгоритма|1.2]], [[#Вычислительное ядро алгоритма|1.3]], [[#Макроструктура алгоритма|1.4]], [[#Схема реализации последовательного алгоритма|1.5]], [[#Входные и выходные данные алгоритма|1.9]]), [[Участник:Boltonidze|А.В.Александров]] ([[#Последовательная сложность алгоритма|1.6]], [[#Информационный граф|1.7]], [[#Ресурс параллелизма алгоритма|1.8]], [[#Свойства алгоритма|1.10]] [[#Существующие реализации алгоритма|2.7]])&lt;br /&gt;
&lt;br /&gt;
= Свойства и структура алгоритмов =&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
Алгоритм CLOPE (Clustering with sLOPE&amp;lt;ref&amp;gt;Yang, Y., Guan, H., You. J. CLOPE: A fast and Effective Clustering Algorithm for Transactional Data In Proc. of SIGKDD’02, July 23-26, 2002, Edmonton, Alberta, Canada.&amp;lt;/ref&amp;gt;) неирархический метод кластеризации, предназначенный для кластеризации огромных наборов категориальных данных. К достоинствам алгоритма относятся высокие масштабируемость и скорость работы и качество кластеризации, что достигается использованием глобального критерия оптимизации на основе максимизации градиента высоты гистограммы кластера. Отличается простотой программной реализации. &lt;br /&gt;
&lt;br /&gt;
Во время работы алгоритм хранит в памяти небольшое количество информации по каждому кластеру и требует минимальное число сканирований набора данных. CLOPE автоматически подбирает количество кластеров, причем это регулируется одним единственным параметром - коэффициентом отталкивания &amp;lt;math&amp;gt; r &amp;lt;/math&amp;gt;. При этом он обеспечивает более высокую производительность и лучшее качество кластеризации в сравнении с многими иерархическими алгоритмами.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
Пусть имеется база транзакций &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt;, состоящая из множества транзакций &amp;lt;math&amp;gt;\{t_1,t_2,...,t_n\}&amp;lt;/math&amp;gt;. Каждая транзакция есть набор объектов &amp;lt;math&amp;gt;\{i_1,...,i_m\}&amp;lt;/math&amp;gt;. Множество кластеров &amp;lt;math&amp;gt;\{C_1,...,C_k\}&amp;lt;/math&amp;gt; есть разбиение множества &amp;lt;math&amp;gt;\{t_1,...,t_n\}&amp;lt;/math&amp;gt;, такое, что &amp;lt;math&amp;gt;C_1 \cup \dots \cup C_k=\{t_1,...,t_n\}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;C_i \ne \empty&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;C_i \cap C_j = \empty &amp;lt;/math&amp;gt;, для &amp;lt;math&amp;gt;i \ge 1, k \ge j&amp;lt;/math&amp;gt;. Каждый элемент &amp;lt;math&amp;gt;C_i&amp;lt;/math&amp;gt; называется кластером, а &amp;lt;math&amp;gt;n, m, k&amp;lt;/math&amp;gt; – количество транзакций, количество объектов в базе транзакций и число кластеров соответственно.&lt;br /&gt;
&lt;br /&gt;
Каждый кластер &amp;lt;math&amp;gt;C&amp;lt;/math&amp;gt; имеет следующие характеристики:&lt;br /&gt;
[[Файл:ExampleCLOPE.jpg|500px|thumb|right]]&lt;br /&gt;
&amp;lt;math&amp;gt;D(C)&amp;lt;/math&amp;gt; – множество уникальных объектов;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;Occ(i,C)&amp;lt;/math&amp;gt; – количество вхождений (частота) объекта &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; в кластер &amp;lt;math&amp;gt;C&amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;S(C)= \sum_{i \in D(C)} Occ(i,C)= \sum_{t_i \in C}  \mid t_i  \mid , &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;W(C)=  \mid D(C) \mid ,H(C)=S(C)/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; Profit(C) = \frac{\sum^{k}_{i=1} G(C_i) \times \mid C_i \mid} {\sum^{k}_{i=1} \mid C_i \mid } = \frac{\sum^{k}_{i=1} \frac{S(C_i)}{W(C_i)^r} \times \mid C_i \mid} {\sum^{k}_{i=1} \mid C_i \mid }  &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
где &amp;lt;math&amp;gt;\mid C_i \mid&amp;lt;/math&amp;gt;  количество объектов в &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt;-ом кластере, &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; – количество кластеров, &amp;lt;math&amp;gt;r&amp;lt;/math&amp;gt; – коэффициент отталкивания &amp;lt;math&amp;gt;(0 &amp;lt; r \le 1)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
С помощью параметра &amp;lt;math&amp;gt;r&amp;lt;/math&amp;gt; регулируется уровень сходства транзакций внутри кластера, и, как&lt;br /&gt;
следствие, финальное количество кластеров. Этот коэффициент подбирается пользователем. Чем больше &amp;lt;math&amp;gt;r&amp;lt;/math&amp;gt;, тем ниже уровень сходства и тем больше кластеров будет сгенерировано.&lt;br /&gt;
&lt;br /&gt;
Постановка задачи кластеризации алгоритмом CLOPE выглядит следующим образом: &lt;br /&gt;
&lt;br /&gt;
для заданных &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;r&amp;lt;/math&amp;gt; найти разбиение &amp;lt;math&amp;gt;C&amp;lt;/math&amp;gt; такое, что: &lt;br /&gt;
&amp;lt;math&amp;gt;Profit(C) \longrightarrow max &amp;lt;/math&amp;gt; &amp;lt;ref&amp;gt;Нейский И.М. Классификация и сравнение методов кластеризации.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Вычислительное ядро алгоритма состоит в вычислении на каждой итерации стоимости добавления и удаления транзакции из кластера: &lt;br /&gt;
&lt;br /&gt;
*вычисление нового размера кластера &amp;lt;math&amp;gt;S_{new}(C_i)=S(C_i) \pm \mid(t_n)\mid &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;\mid(t_n)\mid&amp;lt;/math&amp;gt; - длина транзакции&lt;br /&gt;
&lt;br /&gt;
*вычисление новой ширины кластера &amp;lt;math&amp;gt;W_{new}(C_i)=\mid D(C_i \cup t_n)|&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*вычисление стоимости &amp;lt;math&amp;gt;\frac{S_{new}(C_i)(\mid C_i \mid \pm 1)}{W_{new}(C_i)^r} - \frac{S(C_i) \mid C_i \mid }{W(C_i)^r}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
Алгоритм состоит из следующих этапов:&lt;br /&gt;
&lt;br /&gt;
1. &#039;&#039;&#039;Инициализация&#039;&#039;&#039;. На этом этапе происходит первый проход по таблице с транзакциями для построения начального разбиения, для каждой транзакции определяется кластер исходя из максимизации стоимости.&lt;br /&gt;
&lt;br /&gt;
2. &#039;&#039;&#039;Итерация&#039;&#039;&#039;. На данном этапе для каждой транзакции проводится попытка перемещения в другой кластер для максимизации функции стоимости.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Первая часть алгоритма - инициализация: для каждой транзакции вычисляется кластер, к которому она будет отнесена исходя из максимизации функции стоимости. Для этого вычисляется вспомогательная функция DeltaAdd, которая считает стоимость добавления транзакции к одному из существующих кластеров, либо к пустому. В итоге транзакция помещается в кластер, для которого цена добавления максимальна.&lt;br /&gt;
&lt;br /&gt;
Вторая часть алгоритма - уточняющие итерации: здесь происходит попытка улучшить вычисленные на первом этапе кластеры. В каждой итерации происходит перебор всех транзакций, и для каждой транзакции вычисляется цена удаления ее из текущего кластера и цена добавления в другой кластер. Суммарная стоимость перемещения будет являться суммой стоимостей удаления и добавления. Если максимальная стоимость перемещения данной транзакции в какой-либо больше нуля, то она перемещается в этот кластер. &lt;br /&gt;
&lt;br /&gt;
Вычисления происходят до тех пор, пока за итерацию происходит хотя бы одно перемещение транзакции. Если за итерацию не было произведено ни одного перемещения, то текущее состояние кластеров является устойчивым и оптимальным. &amp;lt;ref&amp;gt;Павлин Н. Кластеризация категорийных данных: масштабируемый алгоритм CLOPE, https://basegroup.ru/community/articles/clope&amp;lt;/ref&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;
Входные параметры:&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;r&amp;lt;/math&amp;gt; - коэффициент отталкивания&lt;br /&gt;
&lt;br /&gt;
Выходной параметр:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;C&amp;lt;/math&amp;gt; кластеры, в начале алгоритма - пустое множество&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Инициализация:&#039;&#039;&#039;&lt;br /&gt;
    &#039;&#039;&#039;for&#039;&#039;&#039; t_i in t:&lt;br /&gt;
    max = 0&lt;br /&gt;
        &#039;&#039;&#039;for&#039;&#039;&#039; C_j in C:&lt;br /&gt;
            &#039;&#039;&#039;if&#039;&#039;&#039; DeltaAdd(C_j,t_i,r) &amp;gt; max:&lt;br /&gt;
                max = DeltaAdd(C_j,t_i,r)&lt;br /&gt;
                C_best = C_j&lt;br /&gt;
    &#039;&#039;&#039;put&#039;&#039;&#039; t_i in C_best&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Итерации:&#039;&#039;&#039;&lt;br /&gt;
    move = True&lt;br /&gt;
    &#039;&#039;&#039;while&#039;&#039;&#039; move == True:&lt;br /&gt;
    move = False&lt;br /&gt;
        &#039;&#039;&#039;for&#039;&#039;&#039; t_i in t:&lt;br /&gt;
            max = 0&lt;br /&gt;
            C_now = t_i.cluster&lt;br /&gt;
            removeCost = DeltaRemove(C_now,t_i,r)&lt;br /&gt;
            &#039;&#039;&#039;for&#039;&#039;&#039; C_j in C:&lt;br /&gt;
                &#039;&#039;&#039;if&#039;&#039;&#039; DeltaAdd(C_j,t_i,r) + removeCost &amp;gt; max:&lt;br /&gt;
                    max = DeltaAdd(C_j,t_i,r) + removeCost&lt;br /&gt;
                    C_best = C_j&lt;br /&gt;
        if max &amp;gt; 0&lt;br /&gt;
            &#039;&#039;&#039;del&#039;&#039;&#039; t_i from C_now&lt;br /&gt;
            &#039;&#039;&#039;put&#039;&#039;&#039; t_i in C_best&lt;br /&gt;
            move = True&lt;br /&gt;
&lt;br /&gt;
Вспомогательная функция &#039;&#039;&#039;DeltaAdd&#039;&#039;&#039;:&lt;br /&gt;
    &#039;&#039;&#039;function&#039;&#039;&#039; DeltaAdd(C,t,r)&lt;br /&gt;
        S_new=C.size + t.itemCount&lt;br /&gt;
        W_new=C.wigth&lt;br /&gt;
        &#039;&#039;&#039;for&#039;&#039;&#039; item in t_i:&lt;br /&gt;
            &#039;&#039;&#039;if&#039;&#039;&#039; C_j.occ(item) = 0&lt;br /&gt;
                &#039;&#039;W_new = W_new + 1&lt;br /&gt;
        &#039;&#039;&#039;return&#039;&#039;&#039; S_new * (C.count+1) / (W_new ^ r) - (C.size * C.count) / (C.wigth ^ r)&lt;br /&gt;
&lt;br /&gt;
Вспомогательная функция &#039;&#039;&#039;DeltaRemove&#039;&#039;&#039;:&lt;br /&gt;
    &#039;&#039;&#039;function&#039;&#039;&#039; DeltaRemove(C,t,r)&lt;br /&gt;
        S_new=C.size - t.itemCount&lt;br /&gt;
        W_new=C.wigth&lt;br /&gt;
        &#039;&#039;&#039;for&#039;&#039;&#039; item in t:&lt;br /&gt;
            &#039;&#039;&#039;if&#039;&#039;&#039; C_j.occ(item) = 1&lt;br /&gt;
                &#039;&#039;W_new = W_new - 1&lt;br /&gt;
        &#039;&#039;&#039;return&#039;&#039;&#039; S_new * (C.count-1) / (W_new ^ r) - (C.size * C.count) / (C.wigth ^ r)&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;k&amp;lt;/math&amp;gt; - количество кластеров.&lt;br /&gt;
&lt;br /&gt;
Основные вычисления восполняются в функции DeltaAdd/DeltaRemove: производится 1 сложение, не более &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; сложений/вычитаний, 2 умножения, 2 деления, 2 возведения в степень и 1 сложение - итого &amp;lt;math&amp;gt;m + 8&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;n*k*(m+8)&amp;lt;/math&amp;gt; операций.&lt;br /&gt;
&lt;br /&gt;
В добавок к этому при добавлении/удалении транзакции к кластеру производится пересчет его характеристик: &amp;lt;math&amp;gt;m+2&amp;lt;/math&amp;gt; операции.&lt;br /&gt;
&lt;br /&gt;
Из того можно сделать вывод, что оценка сложность алгоритма равна &amp;lt;math&amp;gt;O(n*k*m)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
На этапе инициализации при обработке каждой транзакции количество кластеров может как увеличиться, так и остаться неизменным. Это показано пунктирными линиями:&lt;br /&gt;
[[Файл:InitCLOPE.jpg|400px|center|Инициализация]]&lt;br /&gt;
На этапе итерации функции DeltaAdd/DeltaRemove могут считать параллельно для каждой транзакции&lt;br /&gt;
[[Файл:IterCLOPE.jpg ‎|400px|center|Итерации]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Вычисление стоимости добавления/удаления транзакции, которые выполняются функциями DeltaAdd/DeltaRemove, происходит независимо для всех &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; кластеров. Поэтому все вычисления можно производить параллельно. Таким образом сложность алгоритма с &amp;lt;math&amp;gt;O(n*k*m)&amp;lt;/math&amp;gt; понижается до &amp;lt;math&amp;gt;O(n*m)&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;n&amp;lt;/math&amp;gt; транзакций длины &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; и коэффициент отталкивания &amp;lt;math&amp;gt;r&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;n&amp;lt;/math&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
* Соотношение последовательной и параллельной сложности алгоритма определяется количеством кластеров: &amp;lt;math&amp;gt;\dfrac{O(n*k*m)}{O(n*m)} = k&amp;lt;/math&amp;gt;&lt;br /&gt;
* [[глоссарий#Вычислительная мощность|&#039;&#039;Вычислительная мощность&#039;&#039;]] алгоритма равна &amp;lt;math&amp;gt;\dfrac{nk(m+8) + m + 2}{nm + n + 1} \cong k&amp;lt;/math&amp;gt;&lt;br /&gt;
* Алгоритм не является [[глоссарий#Устойчивость|&#039;&#039;устойчивым&#039;&#039;]], так как результат работы зависит от порядка чтения транзакций и от текущего набора транзакций в кластере.&lt;br /&gt;
* Выполняется свойство сбалансированности операций между параллельными ветвями алгоритма, так как для каждой транзакции выполняются функции DeltaAdd/DeltaRemove, считающие стоимость добавления/удаления транзакции.&lt;br /&gt;
* Свойство [[глоссарий#Детерминированность|&#039;&#039;детерминированности алгоритмов&#039;&#039;]] не выполняется, так как результат алгоритма зависит от порядка прохождения транзакций. Но если рассматривать транзакции как нумерованный список, то результат будет определен однозначно от запуска к запуску. Однако существует расширение алгоритма, обеспечивающее свойство детерминированности. &amp;lt;ref&amp;gt;Li, Y., Le, J., Wang, M. Improving CLOPE’s Profit Value and Stability with an Optimized Agglomerative Approach. Algorithms 2015, 8, 380-394.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
* Так как алгоритм предназначен для кластеризации категориальных данных, то он практически неприменим для числовой кластеризации.&lt;br /&gt;
* Быстро работает даже для большого количества данных, обеспечивая при этом хорошее качество кластеризации&lt;br /&gt;
* Имеет простую реализацию&lt;br /&gt;
&lt;br /&gt;
= Программная реализация алгоритма =&lt;br /&gt;
&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
* http://weka.sourceforge.net/doc.stable/weka/clusterers/CLOPE.html - реализация в open source библиотеке для анализа данных WEKA(Waikato Environment for Knowledge Analysis) на языке Java&lt;br /&gt;
Также существуют пользовательские реализации алгоритма:&lt;br /&gt;
* https://github.com/infozor/php_mysql_clustering_clope - реализация алгоритма на PHP&lt;br /&gt;
* https://github.com/Tolsi/scala-clope - реализация алгоритма Scala&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;/div&gt;</summary>
		<author><name>Lineprinter</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:Bagnikita/Face_Recognition&amp;diff=15885</id>
		<title>Участник:Bagnikita/Face Recognition</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:Bagnikita/Face_Recognition&amp;diff=15885"/>
		<updated>2016-11-03T12:00:19Z</updated>

		<summary type="html">&lt;p&gt;Lineprinter: Lineprinter переименовал страницу Face Recognition в Участник:Bagnikita/Face Recognition без оставления перенаправления&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment|Algoman}}&lt;br /&gt;
&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Распознавание лиц&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(E(w,h,c)+n*d+n*logn+n*logk+CNNConst)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O(k)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;w*h*c&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;O(k)&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Основные авторы описания: [[Участник:Bagnikita|Н.Ю.Багров]]&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Распознавание лиц при помощи компьютера было впервые предложено в 1966 г. Woody Bledsoe и др. В первой системе использовался графический планшет, на котором было необходимо вручную отметить ключевые особенности на лице (брови, губы и тд). Система в дальнешейм использовала расстояния между этими точками для сравнения разных изображений. Поскольку требовалось много ручной работы, то оператор мог обработать около 40 изображений в час. В 1997 была создана система, которая непосредственно работала с видеоданными и могла применяться на практике. В дальшейшем с увеличением производительности вычислительных систем появились более качественные алгоритмы, но они заметно уступали человеку на данной задаче. С появлением высокороизводительных видеокарт в 2014 году появились системы&amp;lt;ref&amp;gt;Taigman Y. et al. Deepface: Closing the gap to human-level performance in face verification //Proceedings of the IEEE Conference on Computer Vision and Pattern Recognition. – 2014. – С. 1701-1708.&amp;lt;/ref&amp;gt;, точность которых уже превышала точность оператора (человека). Основное преимущество таких систем в том, что они могут обрабатывать большие объемы данных - производительность человека намного ниже (требуется несколько секунд на сравнение пары изображений). &lt;br /&gt;
&lt;br /&gt;
Задачу распознавания лиц можно разбить на несколько этапов:&lt;br /&gt;
&lt;br /&gt;
* обучение нейронной сети&lt;br /&gt;
* построение биометрического шаблона&lt;br /&gt;
* сравнение биометрических шаблонов&lt;br /&gt;
* поиск в базе и формирование результата&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм состоит из построения биометрических шаблонов (дескрипторов) по изображению и поиску заданного шаблона в базе уже вычисленных дескрипторов. &lt;br /&gt;
&lt;br /&gt;
В дальшейшем будем рассматривать только сверточные нейронные сети. Классические архитектуры нейронных сетей с полносвязными слоями неприменимы на практике для анализа изображений, поскольку количество параметров такой сети экспоненциально возрастает с размером входных данных и количеством слоев. В таких сетях каждый нейрон не связан со всеми нейронами предыдущего слоя, а домножается на некоторый коэффициент (ядро свертки) по всему изображению. Таким образом, каждый такой слой применяет операцию свертки ко входам со своими коэффициентами, что заметно уменьшает число весов в нейронной сети. Это позволяет уменьшить потребление памяти и эффект переобучения сети, который проявляется в том, что сеть хорошо работает на классификации элементов обучающей выборки и хуже на валидационной.&lt;br /&gt;
&lt;br /&gt;
==== Обучение сверточной нейронной сети ====&lt;br /&gt;
&lt;br /&gt;
Для обучения нейронной сети используется стохастический градиентный спуск. Начальные веса сети устанавливаются случайными. Заметим, что существенное время занимают сверточные и полносвязные слои. Их можно свести к операции перемножения матриц следуюим образом:&lt;br /&gt;
* пусть размерность фильтра свертки k*k&lt;br /&gt;
* скопируем блоки исходного изображения размером k*k в столбцы новой матрицы&lt;br /&gt;
* умножим эту матрицу на коэффициеты фильтра свертки&lt;br /&gt;
* полученная матрица будет результатом применения свертки к входному изображению&lt;br /&gt;
&lt;br /&gt;
Сложность задачи вычисления свертки зависит квадратично от размера фильтра и линейно от количества выходных данных.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;FeatureMap = A^{n, w * h} * B^{w * h, c * k^2}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;FeatureMap&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;
Существуют библиотеки, например, NVIDIA cuDNN&amp;lt;ref&amp;gt;https://developer.nvidia.com/cudnn&amp;lt;/ref&amp;gt;) которые решают данную задачу более эффективно. Например, свертки размером 3x3, 5x5 могут быть реализованы с использованием FFT (что работает на видеокарте быстрее, чем умножение матриц)&lt;br /&gt;
&lt;br /&gt;
==== Построение биометрического шаблона ====&lt;br /&gt;
&lt;br /&gt;
Будем называть биометрическим шаблоном вектор &amp;lt;math&amp;gt;{R}^d&amp;lt;/math&amp;gt;, который является представлением лица человека. Этот вектор получается после применения нейронной сети к изображению - в качестве такого вектора используют отклики с одного последних слоев нейронной сети. Для обеспечения возможности сравнивать между собой эти шаблоны вектор нормализуют.&lt;br /&gt;
&lt;br /&gt;
==== Сравнение биометрических шаблонов ====&lt;br /&gt;
&lt;br /&gt;
На предыдущем этапе был получен вектор значений &amp;lt;math&amp;gt;D={A}^d&amp;lt;/math&amp;gt;, где d - размерность дескриптора. Можно рассматривать достаточно много различных оценивающих функций (метриками их называть некорректно, т.к для них не всегда выполняется неравенство треугольника). Например, в качестве такой функции можно рассматривать cosine distance - косинус угла между векторами-признаками в d-мерном пространстве. Также достаточно часто используют L2 метрику, либо более сложные контрукции, такие как JointBayesian&amp;lt;ref&amp;gt;Chen D. et al. Bayesian face revisited: A joint formulation //European Conference on Computer Vision. – Springer Berlin Heidelberg, 2012. – С. 566-579.&amp;lt;/ref&amp;gt;. У всех таких &amp;quot;метрик&amp;quot; есть преимущества и недостатки, например в L2 можно легко проводить кластеризацию, тогда как в JointBayes и cosine metric нет. JointBayes можно дообучить на целевой выборке и повысить точность работы алгоритма.&lt;br /&gt;
&lt;br /&gt;
==== Поиск в базе и формирование результата ====&lt;br /&gt;
&lt;br /&gt;
Предположим, что для каждого человека в базе есть несколько фотографий. Тогда после выполнения операции сравнения биометрических шаблона изображения-запроса &amp;lt;math&amp;gt;P&amp;lt;/math&amp;gt; и всех изображений в базе &amp;lt;math&amp;gt;G={g1,g2,...,gn}&amp;lt;/math&amp;gt; необходимо выбрать результат с максимальным значением оценивающей функции для данного человека. Т.е необходимо оставить в результате только уникальные по людям совпадения, выбрав при этом максимальное значение при удалении дубликатов по людям.&lt;br /&gt;
&lt;br /&gt;
После выполнения поиска в базе необходимо произвести нормализацию полученных значений оценивающей функции. В системах распознавания лиц обычно используют диапазон [0.0 - 1.0], где 0.0 означает минимальное сходство двух изображений лиц, а 1.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;
Обозначим за &amp;lt;math&amp;gt;E(w,h,c)&amp;lt;/math&amp;gt; сложность операции вычисления биометричсекого шаблона по входному изображению лица человека. Поскольку сверточной нейронной сети с полносвязными слоями должно подаваться на вход изображения фиксированного размера, то необходимо изменить разрешение исходного изображения. В данной реализации алгоритма распознавания лиц используется билинейная интерполяция. Нейронная сеть получает на вход одноканальные изображения размером &amp;lt;math&amp;gt;100*100&amp;lt;/math&amp;gt;. Поэтому число необходимых операций (по операциям умножения) можно оценить как &amp;lt;math&amp;gt;O(w*h*c) + O(100 * 100 * 6) = O(w*h*c) + Const&amp;lt;/math&amp;gt;. На практике эта константа существенна, поскольку эти операции неоднородны по памяти.&lt;br /&gt;
&lt;br /&gt;
Непосредственно время работы нейронной сети существенно зависит от предложенной архитектуры (глубины сети, типов слоев и числа выходов каждого слоя).&lt;br /&gt;
&lt;br /&gt;
В реализации, предлолженной в практической части, используются более 10 сверточных слоев. Количестве операций можно оценить исходя из количества весов связей в нейронной сети - 80M и размера сверток &amp;lt;math&amp;gt;3*3&amp;lt;/math&amp;gt;. Оценка числа операций получилась ~2 GFlops на одну операцию применения нейросети (forward pass, 180ms). Она согласуется с реальным временем работы системы и производительностью системы на тесте linpack (130GFlops 4 cores) на ноутбуке.&lt;br /&gt;
&lt;br /&gt;
==== Поиск в базе ====&lt;br /&gt;
&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(x,y)=\frac{(x,y)}{\lVert x \lVert * \lVert y \lVert}&amp;lt;/math&amp;gt;. Заметим, что вектора &amp;lt;math&amp;gt;x, y&amp;lt;/math&amp;gt; уже нормированы на предыдущем этапе. Поэтому число операций для вычисления метрики составляет &amp;lt;math&amp;gt;d&amp;lt;/math&amp;gt; умножений и &amp;lt;math&amp;gt;d - 1&amp;lt;/math&amp;gt; сложений, где d - размерность дескриптора. Если размер базы &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;, то число операций &amp;lt;math&amp;gt;O(n*d+n*(d-1))=O(n*d)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===== Агрегация результатов по людям =====&lt;br /&gt;
&lt;br /&gt;
Для выполнения операции unique (без использования дополнительной памяти) необходимо отсортировать элементы (person_id, match_score) массива по ключу person_id, а при совпадении по score. После этого можно за линейное время удалить дубликаты по person_id, оставив первый элемент (который будет максимальным по match_score). Сложность таких операций &amp;lt;math&amp;gt;O(n*logn+n)=O(n*logn)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===== Cортировка по убыванию метрики сходства двух изображений =====&lt;br /&gt;
&lt;br /&gt;
Финальная сортировка по match_score будет выполнена за O(n*logk), где k - количество результатов в выдаче. Обычно на практике число k не более 1000, поскольку обработка результата чаще всего производится вручную оператором (человеком).&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм содержит вычисление биометричиского шаблона, который на этой странице не приводится. Используется готовая бибилотека распознавания лиц [http://tevian.ru Tevian FaceSDK]. Предложенная параллельная реализация может работать с любой системой распознавания лиц, для которой выполнены следующие условия:&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++):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&#039;python&#039;&amp;gt;&lt;br /&gt;
desc = enroll(Image) # Image - изображение лица человека&lt;br /&gt;
matches = {}&lt;br /&gt;
for gallery_entry in G:&lt;br /&gt;
    if matches.get(gallery_entry.person_id, None) is None:&lt;br /&gt;
        matches[gallery_entry.person_id] = 0.0&lt;br /&gt;
    matches[gallery_entry.person_id] = max(matches[gallery_entry.person_id], match(desc, gallery_entry.descriptor))&lt;br /&gt;
matches = sorted(key = lambda x: x[1], matches.items())&lt;br /&gt;
return [x[0], applyMapping(x[1]) for x in matches]&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Если рассматривать всю последовательность действий (построение шаблона, поиск в базе), то сложность алгоритма &amp;lt;math&amp;gt;O(E(w,h,c)+n*d+n*logn+n*logk+CNNConst)&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;d&amp;lt;/math&amp;gt; - размерность биометрического шаблона&lt;br /&gt;
*&amp;lt;math&amp;gt;E(w,h,c)&amp;lt;/math&amp;gt; - сложность обработки изображения размером (width*height*channels) до необходимого размера для нейросети&lt;br /&gt;
*&amp;lt;math&amp;gt;n*d&amp;lt;/math&amp;gt; - число операций для вычисления метрики между запросом и базой&lt;br /&gt;
*&amp;lt;math&amp;gt;n*logn&amp;lt;/math&amp;gt; - сортировка результата для удаления дубликатов&lt;br /&gt;
*&amp;lt;math&amp;gt;n*logk&amp;lt;/math&amp;gt; - сортировка результата по значению оценивающей функции&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
На первом шаге необходимо построить биометрический шаблон - поскольку внути алгоритма используется нейросеть, то эти вычисления обычно выполняются на видеокартах. В этом разделе подробно не рассматривается работа нейросети, поэтому можно считать, что ему соответствует одна вершина в графе.&lt;br /&gt;
&lt;br /&gt;
На втором шаге уже есть посчитанный биометрический шаблон (дескриптор) - его необходимо сопоставить в базой (пусть в ней N дескрипторов). Предположим, что доступно Q исполнителей (процессов или потоков). Тогда возможна паралельная обработка Q диапазонов базы размером N/Q. Для каждого такого вычисления длина последовательной части &amp;lt;math&amp;gt;N/Q*d&amp;lt;/math&amp;gt;, где d - размерность дескриптора.&lt;br /&gt;
&lt;br /&gt;
На третьем шаге производится агрегация полученных результатов сопоставнения длины k с один общий список, отсортированный по убыванию меры сходства изображений. Здесь аналогично можно рассматривать параллельную сортировку на Q вычислителях.&lt;br /&gt;
&lt;br /&gt;
Видно, что у алгоритма большой ресурс по возможности распараллеливания за счет возможности параллельной реализации практически любой его части.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Facerecognition-graph.png|thumb|center|700px|Рисунок 0. Граф параллельного алгоритма]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Сложность параллельного алгоритма, в предположении доступности q узлов: &amp;lt;math&amp;gt;O(k*logk+q*(log(k*q))^2+\frac{n}{q}*(d+log(\frac{n}{q}))+\lambda*(k*log(q)+q*(log(q*k))^2))&amp;lt;/math&amp;gt;, где&lt;br /&gt;
* n - размер базы изображений лиц&lt;br /&gt;
* d - размерность биометрического шаблона&lt;br /&gt;
* k - количество результатов в выдаче (k &amp;lt;&amp;lt; n)&lt;br /&gt;
* &amp;lt;math&amp;gt;\lambda&amp;lt;/math&amp;gt; - флаг (0 или 1), нужно ли производить агрегацию результатов по людям&lt;br /&gt;
&lt;br /&gt;
Данная оценка получена с применением [http://darwin.bth.rwth-aachen.de/opus3/volltexte/2010/3502/pdf/3502.pdf параллельной сортировки].&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Входные данные:&lt;br /&gt;
* изображение &amp;lt;math&amp;gt;P\in{R}^{w*h*c}&amp;lt;/math&amp;gt;, где w,h,c - ширина, высота, число каналов&lt;br /&gt;
* база изображений лиц &amp;lt;math&amp;gt;G={g_1,g_2,g_3,...,g_n}, g_i\in{T}&amp;lt;/math&amp;gt;&lt;br /&gt;
* T - объект, состоящий из биометрического шаблона и порядкового номера человека&lt;br /&gt;
&lt;br /&gt;
Выходные данные*&lt;br /&gt;
* список &amp;lt;math&amp;gt;L={l_1,l_2,...,l_k}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;l_i\in{T}&amp;lt;/math&amp;gt;&lt;br /&gt;
* T - объект, состоящий из числа (мера сходства двух изображений) и id (порядкового номера) человека в базе&lt;br /&gt;
&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Основная проблема при реализации параллельной версии данного алгоритма - необходимость сортировки результатов. Исходя из размеров базы важно правильно выбрать количество вычислителей, поскольку в ряде случаев сортировка будет работать очень быстро (число элементов будет значительно меньше объема базы) и не потребуется реализация параллельной сортировки. &lt;br /&gt;
&lt;br /&gt;
Другая проблема - низкая вычислительная сложность самого алгоритма и большое число обращений к памяти. При реализации важно выделять память большими блоками для успешного чтения из кэша процессора.&lt;br /&gt;
&lt;br /&gt;
Из преимуществ данного алгоритма можно назвать низкую нагрузку на сеть при обмене данными, возможность работы на множестве систем (MPI, hadoop и тд), посколько объем данных, передаваемых между узлами небольшой. Для практической реализации не требуется использования специальных коммуникаций с низкой задержкой (обмены происходят достаточно редко, время доставки сообщений не критично).&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Построение биометрических шаблонов выполнено при помощи библиотеки распознавания лиц Tevian FaceSDK&amp;lt;ref&amp;gt;http://tevian.ru/product/facesdk&amp;lt;/ref&amp;gt;. В этом разделе приводится эффективность параллельной реализации сравнения изображени лица с некоторой базой лиц. &lt;br /&gt;
&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
В текущей версии алгоритма применяется векторизация с использованием FMA3 инструкций, что позволяет значительно ускорить работу программы. При выполнении оценки производительности системы использовалась простая реализация cosine метрики (цикл для вычисления скалярного произведения) и с использованием FMA инстукций.&lt;br /&gt;
&lt;br /&gt;
Псевдокод операции mm256_fmadd (intel)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;fortran&amp;quot;&amp;gt;&lt;br /&gt;
FOR j := 0 to 7&lt;br /&gt;
    i := j*32&lt;br /&gt;
    dst[i+31:i] := (a[i+31:i] * b[i+31:i]) + c[i+31:i]&lt;br /&gt;
ENDFOR&lt;br /&gt;
dst[MAX:256] := 0&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В результате тестирования (процессор 6300HQ, ноутбук, база 30М биометрических шаблонов) однопоточной программы было установлено, что FMA3 версия в 5 раз быстрее по сравнению с обычным циклом (880ms vs 4600ms). Все дальнейшие экспериенты будут проводиться с FMA3 реализацией (если не указано иначе). Компиляция производится gcc 5.4 с флагами &amp;lt;code&amp;gt;-O3 -static-libgcc -static-libstdc++ -mavx -mavx2 -mfma -fopenmp&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ниже приводится пример кода для вычисления дескриптора:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c++&amp;quot;&amp;gt;&lt;br /&gt;
pp = descriptor;&lt;br /&gt;
pg = gallery_ptr;&lt;br /&gt;
__m256 sum = _mm256_setzero_ps();&lt;br /&gt;
for(int j = 0; j &amp;lt; 16; ++j, pp += 8, pg += 8) {&lt;br /&gt;
    sum = _mm256_fmadd_ps(_mm256_load_ps(pp), _mm256_load_ps(pg), sum);&lt;br /&gt;
}&lt;br /&gt;
const __m128 x128 = _mm_add_ps(_mm256_extractf128_ps(sum, 1), _mm256_castps256_ps128(sum));&lt;br /&gt;
const __m128 x64 = _mm_add_ps(x128, _mm_movehl_ps(x128, x128));&lt;br /&gt;
const __m128 x32 = _mm_add_ss(x64, _mm_shuffle_ps(x64, x64, 0x55));&lt;br /&gt;
score = _mm_cvtss_f32(x32);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Для сортировки результатов и агрегации по людям используются функции из stl - вполне возможно, что можно реализовать более эффективно.&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
Данный алгоритм производит вычисления скалярных произведений, поэтому на этом этапе все обращения к памяти обладают свойством локальности. Биометрический шаблон полностью помещается в кэш память любого современного процессора, данные под базу изобрежний выделяются в виде больших непрерывных блоков памяти. &lt;br /&gt;
&lt;br /&gt;
Сортировка результатов также обладает свойством локальности, поскольку массив хранится непрерывно в памяти. При небольшом размере базы (1-10M изображений) он может полностью поместиться в кэш памяти.&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Алгоритм построен по модели map/reduce. В map происходит вычисление скалярных произведений дескриптора-запроса и элементов базы. В reduce происходит сортировка и удаление повторов.&lt;br /&gt;
&lt;br /&gt;
Исходная база разбирается на блоки (chunks) по 1M биометрических шаблонов, каждый такой блок обрабатывается независимо в своем потоке (или процессе). Важно то, что в результате работы алгоритма на каждом блоке в результате получается небольшой объем данных - &amp;lt;math&amp;gt;k*(sizeof(float)+sizeof(int32_t))&amp;lt;/math&amp;gt;. Такие данные можно легко передать по медленному каналу с высокой задержкой (не требуется использование InfiniBand). Также данный алгоритм можно запускать как на MPI кластерах с передачей сообщений, так и на hadoop кластерах.&lt;br /&gt;
&lt;br /&gt;
Для сортировки результатов можно применять параллельную сортировку (которая ассимптотически) лучше, но на практике это не нужно из-за разумного ограчения на число результатов. Часто их просматривает вручную оператор системы, поэтому больше нескольких тысяч ставить бессмысленно.&lt;br /&gt;
&lt;br /&gt;
В текущей версии системы реализован параллелизм с использованием OpenMP. Вместо OpenMP можно использовать любой метод многопоточной обработки данных, например, pthread. Сложность модификации кода минимальна, поскольку map/reduce подразумевает отсутствие зависимостей между различными блоками.&lt;br /&gt;
&lt;br /&gt;
При реализации распределенной системы можно размещать разные chunks на вычислительных узлах и агрегировать результаты. Из-за небольшого размера сообщений такую систему можно легко построить в ЦОД без использования специальных коммуникаций на обычных серверах.&lt;br /&gt;
&lt;br /&gt;
CUDA можно применять на этапе построения биометрического шаблона, что позволит значительно ускорить построение дескрипторов (например, во время добавления в базу большого числа изображений новых людей).&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
В дальнейшем во всех тестах будет использоваться сервер с 2x E5-2683v3 128GB RAM. MOPS - миллионы операций (для map -скалярное произведение, reduce - сортировка) в секунду.&lt;br /&gt;
&lt;br /&gt;
Каждый тест проводится 20 раз, результаты усредняются.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Facerecognition-matcher.png|thumb|center|700px|Рисунок 1. Результаты работы map функции в зависимости от размера базы и числа потоков в openmp]]&lt;br /&gt;
&lt;br /&gt;
Т.к скорость чтения из DDR4 памяти 17GB/s (на практике немного меньше), то с 4 каналов памяти (по 2 на каждый процессор) нельзя считывать быстрее, чем 68GB/s. База 200M дескрипторов занимает 98GB ram, поэтому время 2300ms для вычисления всех скалярных произведений близко к нижней границе. Дальнейшее увеличение числа потоков на одном сервере не будет приводить к существенному ускорению работы программы.&lt;br /&gt;
&lt;br /&gt;
Результаты работы reduce функции в завсимости от размера базы и числа потоков в openmp:&lt;br /&gt;
[[Файл:Facerecognition_reduce.png|thumb|center|700px|Рисунок 2. Результаты работы reduce функции в зависимости от размера базы и числа потоков в openmp]]&lt;br /&gt;
&lt;br /&gt;
Reduce этап лучше распараллеливается, поскольку в нем намного меньше число обращений к памяти. Его также можно ускорять, например подбирая оптимальный размер блока (chunk size). Для базы в 200M лиц лучшая скорость 1400ms (достигается на 20 потоках, дальше ускорения нет).&lt;br /&gt;
&lt;br /&gt;
Значительное ускорение при размере базы в 100M (значение близко к 64GB) возникает предположительно из-за использование памяти второго процессора (суммарно 4 канала памяти, а не 2).&lt;br /&gt;
&lt;br /&gt;
[[Файл:Facerecognition_overall.png|thumb|center|700px|Рисунок 3. Суммарная скорость работы системы в зависимости от размера базы и числа потоков в openmp]]&lt;br /&gt;
&lt;br /&gt;
==== Исследование влияния HyperThreading на работу алгоритма ====&lt;br /&gt;
&lt;br /&gt;
Поскольку могут возникать проблемы из-за низкой пропускной способности памяти, можно предположить, что HyperThreading позволит ускорить работу программы. Во время ожидания данных из памяти может быть задействован второй вычислитель в ядре.&lt;br /&gt;
&lt;br /&gt;
Синяя точка на графике - лучший результат c HyperThreading, красная - HyperThreading выключен.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Facerecognition-ht-matcher.png|thumb|center|700px|Рисунок 4. Результаты работы map функции в зависимости от размера базы и числа потоков в openmp (10-56 потоков)]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:Facerecognition-ht-reduce.png|thumb|center|700px|Рисунок 5. Результаты работы reduce функции в зависимости от размера базы и числа потоков в openmp (10-56 потоков)]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:Facerecognition-ht-overall.png|thumb|center|700px|Рисунок 6. Суммарная скорость работы системы в зависимости от размера базы и числа потоков в openmp (10-56 потоков)]]&lt;br /&gt;
&lt;br /&gt;
Видно, что включение HyperThreading повышает производительность системы. Для reduce шага это ускорение значительно.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Эксперименты проводились на сервере с 2x E5-2683v3 128GB RAM. &lt;br /&gt;
&lt;br /&gt;
Для семейства процессоров на ядре haswell возможно выполнение 32 операций умножения+сложения за такт.&lt;br /&gt;
&lt;br /&gt;
Поэтому пиковая производительность &amp;lt;math&amp;gt;Rpeak=2*14*32*2.0*10^9&amp;lt;/math&amp;gt;, где&lt;br /&gt;
* 2 - число процессоров&lt;br /&gt;
* 14 - количество ядер на каждом из процессоров&lt;br /&gt;
* 32 - количество операций за такт&lt;br /&gt;
* &amp;lt;math&amp;gt;2.0*10^9&amp;lt;/math&amp;gt; - частота ядра без turbo boost&lt;br /&gt;
&lt;br /&gt;
FLOPS алгоритма &amp;lt;math&amp;gt;k=100*10^6*128*2&amp;lt;/math&amp;gt;, где&lt;br /&gt;
* &amp;lt;math&amp;gt;100*10^6&amp;lt;/math&amp;gt; - число операций в секунду&lt;br /&gt;
* 128*2 - количество действий, необходимых для проведения одного сравнения&lt;br /&gt;
&lt;br /&gt;
Эффективность для map = k / peak = 0.014 (1.42%)&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
Данный алгоритм должен хорошо работать как на суперкомпьютерах со специализированной сетевой частью, так и на кластерах серверов с 1/10G Ethernet за счет низкой частоты обменов. Также при необходимость его можно использовать на мобильных рабочих станциях (ноутбуках) и специализированных серверах (4x xeon E7-8XXX), которые смогут обрабатывать в реальном времени большие объемы данных (например, биометрические для всех людей в стране).&lt;br /&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;/div&gt;</summary>
		<author><name>Lineprinter</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%A0%D0%BE%D0%BC%D0%B0%D0%BD_%D0%97%D0%B5%D0%BC%D0%BB%D1%8F%D0%BD%D1%81%D0%BA%D0%B8%D0%B9/Zavolskov/Zemlyanskiy&amp;diff=15884</id>
		<title>Обсуждение участника:Роман Землянский/Zavolskov/Zemlyanskiy</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%A0%D0%BE%D0%BC%D0%B0%D0%BD_%D0%97%D0%B5%D0%BC%D0%BB%D1%8F%D0%BD%D1%81%D0%BA%D0%B8%D0%B9/Zavolskov/Zemlyanskiy&amp;diff=15884"/>
		<updated>2016-11-03T11:58:17Z</updated>

		<summary type="html">&lt;p&gt;Lineprinter: Lineprinter переименовал страницу Обсуждение:Zavolskov/Zemlyanskiy в Обсуждение участника:Роман Землянский/Zavolskov/Zemlyanskiy без оставления перенаправл…&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Lineprinter</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:%D0%A0%D0%BE%D0%BC%D0%B0%D0%BD_%D0%97%D0%B5%D0%BC%D0%BB%D1%8F%D0%BD%D1%81%D0%BA%D0%B8%D0%B9/Zavolskov/Zemlyanskiy&amp;diff=15883</id>
		<title>Участник:Роман Землянский/Zavolskov/Zemlyanskiy</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:%D0%A0%D0%BE%D0%BC%D0%B0%D0%BD_%D0%97%D0%B5%D0%BC%D0%BB%D1%8F%D0%BD%D1%81%D0%BA%D0%B8%D0%B9/Zavolskov/Zemlyanskiy&amp;diff=15883"/>
		<updated>2016-11-03T11:58:16Z</updated>

		<summary type="html">&lt;p&gt;Lineprinter: Lineprinter переименовал страницу Zavolskov/Zemlyanskiy в Участник:Роман Землянский/Zavolskov/Zemlyanskiy без оставления перенаправления&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt; {{Assignment|IgorS}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Метод «разделяй и властвуй» вычисления собственных значений и векторов симметричной трехдиагональной матрицы&#039;&#039;&#039;&lt;br /&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;
&lt;br /&gt;
[[#Макроструктура алгоритма]]&lt;br /&gt;
&lt;br /&gt;
[[#Схема реализации последовательного алгоритма]]&lt;br /&gt;
&lt;br /&gt;
[[#Информационный граф]]&lt;br /&gt;
&lt;br /&gt;
[[#Существующие реализации алгоритма]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Землянский Роман&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[#Общее описание алгоритма]]&lt;br /&gt;
&lt;br /&gt;
[[#Математическое описание алгоритма]]&lt;br /&gt;
&lt;br /&gt;
[[#Последовательная сложность алгоритма]]&lt;br /&gt;
&lt;br /&gt;
[[#Ресурс параллелизма алгоритма]]&lt;br /&gt;
&lt;br /&gt;
[[#Входные и выходные данные алгоритма]]&lt;br /&gt;
&lt;br /&gt;
[[#Свойства алгоритма]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
614 группа&lt;br /&gt;
&lt;br /&gt;
= ЧАСТЬ. Свойства и структура алгоритмов =&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
&#039;&#039;&#039;Метод разделяй и властвуй вычисления собственных значений и векторов трёхдиагональной матрицы&#039;&#039;&#039; - это наиболее быстрый из существующих методов, если нужны все собственные значения и собственные векторы трехдиагональной матрицы, начиная с порядка n, примерно равного 26. (Точное значение этого порогового порядка зависит от компьютера.) Его численно устойчивая реализация весьма не тривиальна. В самом деле, хотя впервые метод был предложен еще в 1981 г., &amp;quot;правильный&amp;quot; способ его реализации был найден лишь в 1992 г. Этот способ воплощен LAPACK-программами ssyevd (для плотных матриц) и sstevd (для трехдиагональных матриц). В них стратегия &amp;quot;разделяй-и-влавствуй&amp;quot; используется для матриц порядка, большего чем 25. Для матриц меньшего порядка (или если нужны только собственные значения) происходит автоматический переход к QR-итерации.&lt;br /&gt;
&lt;br /&gt;
== Математическое описание алгоритма ==&lt;br /&gt;
Пусть &lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
L = \begin{bmatrix}&lt;br /&gt;
 a_{1} &amp;amp; b_{1}&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp; \\&lt;br /&gt;
 b_{1} &amp;amp; \ddots  &amp;amp; \ddots \\&lt;br /&gt;
   &amp;amp; \ddots &amp;amp; a_{m-1} &amp;amp; b_{m-1} \\&lt;br /&gt;
 &amp;amp;&amp;amp; b_{m-1} &amp;amp; a_{m} &amp;amp; b_{m} \\&lt;br /&gt;
 &amp;amp;&amp;amp;&amp;amp; b_{m} &amp;amp; a_{m+1} &amp;amp; b_{m+1} \\&lt;br /&gt;
 &amp;amp;&amp;amp;&amp;amp;&amp;amp; b_{m+1} &amp;amp; \ddots \\&lt;br /&gt;
 &amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp; \ddots &amp;amp; b_{n-1} \\&lt;br /&gt;
 &amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp; b_{n-1} &amp;amp; a_{n} \\&lt;br /&gt;
\end{bmatrix}&lt;br /&gt;
= \begin{bmatrix}&lt;br /&gt;
  a_{1} &amp;amp; b_{1} &amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp; \\&lt;br /&gt;
 b_{1} &amp;amp; \ddots  &amp;amp; \ddots \\&lt;br /&gt;
   &amp;amp; \ddots &amp;amp; a_{m-1} &amp;amp; b_{m-1} \\&lt;br /&gt;
 &amp;amp;&amp;amp; b_{m-1} &amp;amp; a_{m} - b_{m} \\&lt;br /&gt;
 &amp;amp;&amp;amp;&amp;amp;&amp;amp; a_{m+1} - b_{m} &amp;amp; b_{m+1} \\&lt;br /&gt;
 &amp;amp;&amp;amp;&amp;amp;&amp;amp; b_{m+1} &amp;amp; \ddots \\&lt;br /&gt;
 &amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp; \ddots &amp;amp; b_{n-1} \\&lt;br /&gt;
 &amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp; b_{n-1} &amp;amp; a_{n} \\&lt;br /&gt;
\end{bmatrix} +&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
+ \begin{bmatrix}&lt;br /&gt;
 &amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp; \\&lt;br /&gt;
 &amp;amp;&amp;amp;b_{m} &amp;amp; b_{m} \\&lt;br /&gt;
 &amp;amp;&amp;amp;b_{m} &amp;amp; b_{m} \\&lt;br /&gt;
 &amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp; \\&lt;br /&gt;
\end{bmatrix}&lt;br /&gt;
&lt;br /&gt;
= \begin{bmatrix} T_{1} &amp;amp; 0 \\ 0 &amp;amp; T_{2}\end{bmatrix} + b_{m} * \begin{bmatrix} 0 \\ \vdots \\ 0 \\ 1 \\ 1 \\ 0 \\ \vdots \\ 0 \end{bmatrix} \begin{bmatrix} 0 , \ldots  ,  0 , 1 , 1 , 0 \ldots , 0 \end{bmatrix} &lt;br /&gt;
&lt;br /&gt;
\equiv \begin{bmatrix} T_{1} &amp;amp; 0 \\ 0 &amp;amp; T_{2}\end{bmatrix} + b_{m}vv^{T}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Предположим, что нам известны спектральные разложения матриц &amp;lt;math&amp;gt;T_{1}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt; T_{2} &amp;lt;/math&amp;gt;: &lt;br /&gt;
&amp;lt;math&amp;gt; T_{i} = Q_{i} \Lambda_{i} Q_{i}^{T} &amp;lt;/math&amp;gt;. В действительности, они будут рекурсивно вычисляться тем же самым алгоритмом. Установим связь между собственными значениями матрицы Т и собственными значениями матриц&lt;br /&gt;
&amp;lt;math&amp;gt;T_{1}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;T_{2}&amp;lt;/math&amp;gt;. Имеем:&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;math&amp;gt; &lt;br /&gt;
T = \begin{bmatrix} T_{1} &amp;amp; 0 \\ 0 &amp;amp; T_{2}\end{bmatrix} + b_{m}vv^{T} &lt;br /&gt;
= \begin{bmatrix} Q_{1} \Lambda_{1} Q_{1}^{T} &amp;amp; 0 \\ 0 &amp;amp; Q_{2} L_{2} Q_{2}^{T}\end{bmatrix} + b_{m}vv^{T} &lt;br /&gt;
= \begin{bmatrix} Q_{1} &amp;amp; 0 \\ 0 &amp;amp; Q_{2}\end{bmatrix}(\begin{bmatrix} \Lambda_{1} &amp;amp; \\  &amp;amp; \Lambda_{2}\end{bmatrix} + b_{m}vv^{T})\begin{bmatrix} Q_{1}^{T} &amp;amp; 0 \\ 0 &amp;amp; Q_{2}^{T}\end{bmatrix}&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;
u = \begin{bmatrix} Q_{1}^{T} &amp;amp; 0 \\ 0 &amp;amp; Q_{2}^{T}\end{bmatrix}v&lt;br /&gt;
  &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
так как &amp;lt;math&amp;gt;v = \begin{bmatrix} 0 , \ldots  ,  0 , 1 , 1 , 0 \ldots , 0 \end{bmatrix}^T&amp;lt;/math&amp;gt;, получим матрицу, состоящую из последнего столбца матрицы &amp;lt;math&amp;gt; Q_{1}^{T}&amp;lt;/math&amp;gt; и первого столбца матрицы &amp;lt;math&amp;gt; Q_{2}^{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;D + \rho uu^{T}&amp;lt;/math&amp;gt;, &lt;br /&gt;
где &amp;lt;math&amp;gt;D = \begin{bmatrix} L_{1} &amp;amp; 0 \\ 0 &amp;amp; L_{2}\end{bmatrix}&amp;lt;/math&amp;gt; - диагональная матрица, &lt;br /&gt;
&amp;lt;math&amp;gt;\rho = b_{m}&amp;lt;/math&amp;gt; - число, а &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; - вектор. &lt;br /&gt;
&lt;br /&gt;
Будем предполагать, не ограничивая общности, что диагональные элементы &amp;lt;math&amp;gt;d_{1}, \ldots, d_{n}&amp;lt;/math&amp;gt; матрицы &amp;lt;math&amp;gt;D&amp;lt;/math&amp;gt; упорядочены по убыванию: &amp;lt;math&amp;gt;d_{n} &amp;lt;= \ldots &amp;lt;=d_{1}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Чтобы найти собственные значения матрицы &amp;lt;math&amp;gt;D + \rho uu^{T}&amp;lt;/math&amp;gt;, вычислим её характеристический многочлен, считая пока матрицу &amp;lt;math&amp;gt;D - \lambda I&amp;lt;/math&amp;gt; невырожденной.&lt;br /&gt;
Тогда&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;math&amp;gt;det(D + \rho uu^{T} - \lambda I) = det((D - \lambda I)(I + \rho (D- \lambda I)^{-1} uu^{T}))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Поскольку &amp;lt;math&amp;gt;D - \lambda I&amp;lt;/math&amp;gt; невырожденна, &amp;lt;math&amp;gt;det(I + \rho (D - \lambda I)^{-1}uu^{T}) = 0&amp;lt;/math&amp;gt; тогда и только тогда, когда &amp;lt;math&amp;gt;\lambda&amp;lt;/math&amp;gt; - собственное значение. Заметим, что матрица &amp;lt;math&amp;gt;I + \rho (D - \lambda I)^{-1}uu^{T}&amp;lt;/math&amp;gt; получается из единичной добавлением матрицы ранга 1. Определитель такой матрицы легко вычислить.&lt;br /&gt;
&lt;br /&gt;
  &#039;&#039;&#039;Лемма 1.&#039;&#039;&#039; Справедливо равенство &amp;lt;math&amp;gt;det(I + xy^{T}) = 1 + y^{T}x&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;y&amp;lt;/math&amp;gt; - векторы.&lt;br /&gt;
&lt;br /&gt;
Таким образом,&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;math&amp;gt;det(I + \rho (D - \lambda I)^{-1}uu^{T}) = 1 + \rho u^{T}(D - \lambda I)^{-1}u&amp;lt;/math&amp;gt; &amp;lt;math&amp;gt; = 1 + \rho \sum_{i=1, n} \frac{u_{i}^{2}} {d_{i}-\lambda} \equiv f(\lambda)&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;f(\lambda) = 0&amp;lt;/math&amp;gt;. Если все числа &amp;lt;math&amp;gt;d_{i}&amp;lt;/math&amp;gt; различны и все &amp;lt;math&amp;gt;u_{i} &amp;lt;&amp;gt; 0&amp;lt;/math&amp;gt; (случай общего положения), то &amp;lt;math&amp;gt;f(\lambda)&amp;lt;/math&amp;gt; имеет график типа показанного на рис.1(где &amp;lt;math&amp;gt;n = 4&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;\rho &amp;gt; 0&amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
[[File:Graphics.PNG|thumb|center|800px|Рис. 1. График функции &amp;lt;math&amp;gt; f(\lambda) = 1 + \frac{0.5}{1 - \lambda} + \frac{0.5}{2 - \lambda} + \frac{0.5}{3 - \lambda} + \frac{0.5}{4 - \lambda}&amp;lt;/math&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
Можно видеть, что прямая &amp;lt;math&amp;gt;y = 1&amp;lt;/math&amp;gt; является горизонтальной асимптотой для этого графика, а прямые &amp;lt;math&amp;gt;\lambda = d_{i}&amp;lt;/math&amp;gt; есть вертикальные асимптоты. Поскольку &amp;lt;math&amp;gt;f^{&#039;}(\lambda) =  \rho \sum_{i=1, n} \frac{u_{i}^{2}} {(d_{i}-\lambda)^{2}}&amp;gt; 0 &amp;lt;/math&amp;gt;, функция возрастает всюду, кроме точек &amp;lt;math&amp;gt;\lambda = d_{i}&amp;lt;/math&amp;gt;. Поэтому корни функции разделяются числами &amp;lt;math&amp;gt;d_{i}&amp;lt;/math&amp;gt; и ещё один корень находится справа от точки &amp;lt;math&amp;gt;d_{1}&amp;lt;/math&amp;gt; (на рис. 1 &amp;lt;math&amp;gt;d_{1} = 4&amp;lt;/math&amp;gt;). (При &amp;lt;math&amp;gt;\rho&amp;lt;0&amp;lt;/math&amp;gt; функция &amp;lt;math&amp;gt;f(\lambda)&amp;lt;/math&amp;gt; всюду убывает и соответствующий корень находится слева от точки &amp;lt;math&amp;gt;d_{n}&amp;lt;/math&amp;gt;). Для функции &amp;lt;math&amp;gt;f(\lambda)&amp;lt;/math&amp;gt;, монотонной и гладкой на каждом из интервалов &amp;lt;math&amp;gt;(d_{i+1},d_{i})&amp;lt;/math&amp;gt;, можно найти вариант метода Ньютона, который быстро и монотонно сходится к каждому из корней, если начальная точка взята в &amp;lt;math&amp;gt;(d_{i+1},d_{i})&amp;lt;/math&amp;gt;. Нам достаточно знать, что на практике метод сходится к каждому собственному значению за ограниченное число шагов. Поскольку вычисление &amp;lt;math&amp;gt;f(\lambda)&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;f^{&#039;}(\lambda)&amp;lt;/math&amp;gt; стоит &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;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;D + \rho uu^{T}&amp;lt;/math&amp;gt; требуется &amp;lt;math&amp;gt;O(n^{2})&amp;lt;/math&amp;gt; флопов.&lt;br /&gt;
Для собственных векторов матрицы &amp;lt;math&amp;gt;D + \rho uu^{T}&amp;lt;/math&amp;gt; мы легко можем получить явные выражения.&lt;br /&gt;
&lt;br /&gt;
  &#039;&#039;&#039;Лемма 2.&#039;&#039;&#039; Если &amp;lt;math&amp;gt;\alpha&amp;lt;/math&amp;gt; - собственное значение матрицы &amp;lt;math&amp;gt;D + \rho uu^{T}&amp;lt;/math&amp;gt;, то соответствующий вектор равен &amp;lt;math&amp;gt;(D - \alpha I)^{-1}u&amp;lt;/math&amp;gt;. Поскольку матрица &amp;lt;math&amp;gt;D - \alpha I&amp;lt;/math&amp;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;
  &amp;lt;math&amp;gt;(D + \rho uu^{T})[(D - \alpha I)^{-1}u] = (D - \alpha I + \alpha I + \rho uu^{T})(D - \alpha I)^{-1}u&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;math&amp;gt;=u + \alpha (D - \alpha I)^{-1}u + u[\rho u^{T}(D - \alpha I)^{-1}u] &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;math&amp;gt;=u + \alpha(D - \alpha I)^{-1}u - u&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
                                                       поскольку &amp;lt;math&amp;gt; \rho u^{T}(D - \alpha I)^{-1}u + 1 = f(\alpha) = 0 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;math&amp;gt;=\alpha [(D - \alpha I)^{-1}u]&amp;lt;/math&amp;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;O(n^{2})&amp;lt;/math&amp;gt; флопов. К сожалению, формула не обеспечивает численной устойчивости, так как для двух очень близких значений &amp;lt;math&amp;gt;\alpha_{i}&amp;lt;/math&amp;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;
=== Дефляция ===&lt;br /&gt;
&lt;br /&gt;
До сих пор полагалось, что все &amp;lt;math&amp;gt;d_{i}&amp;lt;/math&amp;gt; различны и все &amp;lt;math&amp;gt;u_{i}&amp;lt;/math&amp;gt; отличны от нуля. Если это не так, вековое уравнение &amp;lt;math&amp;gt;f(\lambda)=0&amp;lt;/math&amp;gt; имеет &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; вертикальных асимптот, где &amp;lt;math&amp;gt;k&amp;lt;n&amp;lt;/math&amp;gt;, а потому &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; корней. Однако оказывается, что остальные &amp;lt;math&amp;gt;n - k&amp;lt;/math&amp;gt; собственных значений могут быть определены без каких-либо усилий: если &amp;lt;math&amp;gt;d_{i}=d_{i+1}&amp;lt;/math&amp;gt; или &amp;lt;math&amp;gt;u_{i}=0&amp;lt;/math&amp;gt;, то легко показать, что &amp;lt;math&amp;gt;d_{i}&amp;lt;/math&amp;gt; является собственным значением и для матрицы &amp;lt;math&amp;gt;D + \rho uu^{T}&amp;lt;/math&amp;gt;. В такой ситуации мы говорим о &#039;&#039;дефляции&#039;&#039;. На практике выбирается некоторое пороговое значение и дефляция для числа &amp;lt;math&amp;gt;d_{i}&amp;lt;/math&amp;gt; регистрируется, если в смысле этого порога &amp;lt;math&amp;gt;d_{i}&amp;lt;/math&amp;gt; достаточно близко к &amp;lt;math&amp;gt;d_{i+1}&amp;lt;/math&amp;gt; либо &amp;lt;math&amp;gt;u_{i}&amp;lt;/math&amp;gt; достаточно мало.&lt;br /&gt;
&lt;br /&gt;
Основной выигрыш от использования дефляции состоит не в том, что убыстряется решение векового уравнения - этот этап в любом случае стоит лишь &amp;lt;math&amp;gt;O(n^{2})&amp;lt;/math&amp;gt; операций. Выигрыш заключается в ускорении матричного умножения на последнем шаге алгоритма. Действительно, если &amp;lt;math&amp;gt;u_{i}=0&amp;lt;/math&amp;gt;, то соответствующий собственный вектор есть i-й столбец &amp;lt;math&amp;gt;e_{i}&amp;lt;/math&amp;gt; единичной матрицы. Это означает, что &amp;lt;math&amp;gt;e_{i}&amp;lt;/math&amp;gt; является i-м столбцом в матрице &amp;lt;math&amp;gt;Q_{&#039;}&amp;lt;/math&amp;gt;, поэтому при формировании матрицы &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; посредством левого умножения &amp;lt;math&amp;gt;Q_{1}&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;Q_{2}&amp;lt;/math&amp;gt; вычисление i-го столбца не требует никаких затрат. Аналогичное упрощение имеет место в случае &amp;lt;math&amp;gt;d_{i} = d_{i+1}&amp;lt;/math&amp;gt;. При дефляции многих собственных значений устраняется большая часть работы, связанной с матричным умножением.&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
Вычислительным ядром последовательной схемы решения является вычисление матрицы &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; собственных векторов путём умножения матрицы &amp;lt;math&amp;gt;Q = \begin{bmatrix} Q_{1} &amp;amp; 0 \\ 0 &amp;amp; Q_{2}\end{bmatrix}&amp;lt;/math&amp;gt; на матрицу &amp;lt;math&amp;gt;Q^{&#039;}&amp;lt;/math&amp;gt; Данная операция имеет сложность &amp;lt;math&amp;gt;cn^{3}&amp;lt;/math&amp;gt; о чём и говорится в разделе [[#Последовательная сложность алгоритма]] .&lt;br /&gt;
Ей предшествует вычисление собственных значений и векторов матрицы &amp;lt;math&amp;gt; D + \rho uu^{T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
Ниже описана структура алгоритма, в которой есть блок умножения матриц для вычисления собственных векторов, являющийся вычислительным ядром алгоритма. В соответствующем разделе ([[#Вычислительное ядро алгоритма]]) мы упоминали о том, что данному блоку предшествует вычисление собственных значений, которое производится [https://ru.wikipedia.org/wiki/%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D0%9D%D1%8C%D1%8E%D1%82%D0%BE%D0%BD%D0%B0 методом Ньютона].&lt;br /&gt;
&lt;br /&gt;
[[File:DivideAndConquerTree.png|thumb|center|left|800px|Рис. 2. Дерево алгоритма &amp;quot;Разделяй и властвуй&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
[[File:InfoGraph.jpg|thumb|center|1200px|Рис. 3. Детальное описание одного блока алгоритма]]&lt;br /&gt;
&lt;br /&gt;
=== Решение векового уравнения ===&lt;br /&gt;
Подробно стоит поговорить о решении векового уравнения, которое является одной из основных частей алгоритма.&lt;br /&gt;
&lt;br /&gt;
Предположим, что некоторое &amp;lt;math&amp;gt;u_{i}&amp;lt;/math&amp;gt;, хотя и мало, все же недостаточно мало для того, чтобы была зарегистрирована дефляция. В этом случае применение метода Ньютона к решению векового уравнения встречается с затруднениями. Вспомним, что пересчет приближённого решения &amp;lt;math&amp;gt;u_{j}&amp;lt;/math&amp;gt; уравнения &amp;lt;math&amp;gt;f(\lambda) = 0&amp;lt;/math&amp;gt; в методе Ньютона основан на следующих положениях:&lt;br /&gt;
&lt;br /&gt;
 1. Вблизи точки &amp;lt;math&amp;gt;\lambda = \lambda_{j}&amp;lt;/math&amp;gt; функция &amp;lt;math&amp;gt;f(\lambda)&amp;lt;/math&amp;gt; аппроксимируется линейной функцией &amp;lt;math&amp;gt;l(\lambda)&amp;lt;/math&amp;gt;; график есть прямая линия, касающаяся графика функции &amp;lt;math&amp;gt;f(\lambda)&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;\lambda = \lambda_{j}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 2. В качестве &amp;lt;math&amp;gt;\lambda_{j+1}&amp;lt;/math&amp;gt; берётся нуль этого линейного приближения, т.е. &amp;lt;math&amp;gt;l(\lambda_{j+1})=0&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Функция, показанная на рис.1, не доставляет видимых трудностей методу Ньютона, поскольку вблизи каждого своего нуля &amp;lt;math&amp;gt;f(\lambda)&amp;lt;/math&amp;gt; достаточно хорошо аппроксимируется линейными функциями. Однако рассмотрим график функции на рис. 4. Она получена из функции на рис. 1 заменой значения .5 для &amp;lt;math&amp;gt;u_{i}^{2}&amp;lt;/math&amp;gt; на .001. Это новое значение недостаточно мало для того, чтобы вызвать дефляцию. График функции в левой части рис.4 визуально не отличим от её вертикальных и горизонтальных асимптот, поэтому в правой части укрупненно воспроизведён фрагмент графика, прилегающий к вертикальной асимптоте &amp;lt;math&amp;gt;\lambda = 2&amp;lt;/math&amp;gt;. Видно, что график слишком быстро &amp;quot;выполняет поворот&amp;quot; и для большей части значений &amp;lt;math&amp;gt;\lambda&amp;lt;/math&amp;gt; почти горизонтален. Поэтому, применяя метод Ньютона почти к любому начальному приближению &amp;lt;math&amp;gt;\lambda_{0}&amp;lt;/math&amp;gt;, мы получаем линейное приближение &amp;lt;math&amp;gt;l(\lambda)&amp;lt;/math&amp;gt; с почти горизонтальным графиком и малым положительным угловым коэффициентом. В результате &amp;lt;math&amp;gt;\lambda_{1}&amp;lt;/math&amp;gt; является отрицательным числом, огромным по абсолютной величине, которое совершенно бесполезно в качестве приближения к истинному корню.&lt;br /&gt;
&lt;br /&gt;
[[File:GraphNewton.png|thumb|center|800px|Рис. 4. График функции &amp;lt;math&amp;gt; f(\lambda) = 1 + \frac{10^{-3}}{1 - \lambda} + \frac{10^{-3}}{2 - \lambda} + \frac{10^{-3}}{3 - \lambda} + \frac{10^{-3}}{4 - \lambda}&amp;lt;/math&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
Чтобы найти выход из этого положения, можно модифицировать метод Ньютона следующим образом: раз &amp;lt;math&amp;gt;f(\lambda)&amp;lt;/math&amp;gt; нельзя хорошо приблизить линейной функцией &amp;lt;math&amp;gt;l(\lambda)&amp;lt;/math&amp;gt;, попробуем взять в качестве приближения какую-нибудь другую простую функцию &amp;lt;math&amp;gt;h(\lambda)&amp;lt;/math&amp;gt;. Нет ничего особого именно в прямых линиях: для метода Ньютона вместо &amp;lt;math&amp;gt;l(\lambda)&amp;lt;/math&amp;gt; можно взять любое приближение &amp;lt;math&amp;gt;h(\lambda)&amp;lt;/math&amp;gt;, значения и нули которого легко вычисляются. Функция &amp;lt;math&amp;gt;f(\lambda)&amp;lt;/math&amp;gt; имеет полюсы в точках &amp;lt;math&amp;gt;d_{i}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;d_{i+1}&amp;lt;/math&amp;gt;, которые определяют её поведение в соответствующих окрестностях. Поэтому при поиске корня в интервале &amp;lt;math&amp;gt;(d_{i+1}, d_{i})&amp;lt;/math&amp;gt; естественно выбрать функцию &amp;lt;math&amp;gt;h(\lambda)&amp;lt;/math&amp;gt;, также имеющую эти полюсы, т.е. функцию вида&lt;br /&gt;
 &amp;lt;math&amp;gt;h(\lambda)= \frac{c_{1}}{d_{i}-\lambda} + \frac{c_{2}}{d_{i+1}-\lambda} + c_{3}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Константы &amp;lt;math&amp;gt;c_{1},c_{2}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;c_{3}&amp;lt;/math&amp;gt; обеспечивающие, что &amp;lt;math&amp;gt;h(\lambda)&amp;lt;/math&amp;gt; есть приближение к &amp;lt;math&amp;gt;f(\lambda)&amp;lt;/math&amp;gt;, можно выбрать несколькими способами. Отметим, что если &amp;lt;math&amp;gt;c_{1},c_{2}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;c_{3}&amp;lt;/math&amp;gt; уже известны, то уравнение &amp;lt;math&amp;gt;h(\lambda)=0&amp;lt;/math&amp;gt; легко решается относительно &amp;lt;math&amp;gt;\lambda&amp;lt;/math&amp;gt;, поскольку сводится к эквивалентному квадратному уравнению&lt;br /&gt;
 &amp;lt;math&amp;gt;c_{1}(d_{i+1}-\lambda)+c_{2}(d_{i}-\lambda)+c_{3}(d_{i}-\lambda)(d_{i+1}-\lambda)=0&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;\lambda_{j}&amp;lt;/math&amp;gt; - приближённое значение корня. определим &amp;lt;math&amp;gt;c_{1},c_{2}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;c_{3}&amp;lt;/math&amp;gt; так, чтобы&lt;br /&gt;
 &amp;lt;math&amp;gt;\frac{c_{1}}{d_{i}-\lambda} + \frac{c_{2}}{d_{i+1}-\lambda} + c_{3} = h(\lambda) \approx f(\lambda) = 1 + \rho \sum_{k=1, n} \frac{u_{k}^{2}} {d_{k}-\lambda} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
для &amp;lt;math&amp;gt;\lambda&amp;lt;/math&amp;gt; в окрестности &amp;lt;math&amp;gt;\lambda_{j}&amp;lt;/math&amp;gt;. Заметим, что&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;math&amp;gt;f(\lambda) = 1 + \rho \sum_{k=1, i} \frac{u_{k}^{2}} {d_{k}-\lambda} + \rho \sum_{k=i+1, n} \frac{u_{k}^{2}} {d_{k}-\lambda} \equiv 1 + \psi_{1}(\lambda) + \psi_{2}(\lambda)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;\lambda \in (d_{i+1},d_{i})&amp;lt;/math&amp;gt;, то &amp;lt;math&amp;gt;\psi_{1}(\lambda)&amp;lt;/math&amp;gt; есть сумма положительных слагаемых, а &amp;lt;math&amp;gt;\psi_{2}(\lambda)&amp;lt;/math&amp;gt; - сумма отрицательных. Поэтому и &amp;lt;math&amp;gt;\psi_{1}(\lambda)&amp;lt;/math&amp;gt;, и &amp;lt;math&amp;gt;\psi_{2}(\lambda)&amp;lt;/math&amp;gt; могут быть вычислены с высокой точностью; однако при их сложении вполне вероятно взаимное уничтожение верных разрядов и потеря относительной точности в сумме. Возьмем числа &amp;lt;math&amp;gt;c_{1}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;\hat{c_{1}}&amp;lt;/math&amp;gt;, такие, что функция &lt;br /&gt;
&lt;br /&gt;
 &amp;lt;math&amp;gt;h_{1}(\lambda) \equiv \hat{c_{1}} + \frac{c_{1}}{d_{i}-\lambda}&amp;lt;/math&amp;gt;   удовлетворяет условиям  &amp;lt;math&amp;gt;h_{1}(\lambda_{j}) = \psi_{1}(\lambda_{j})&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;h_{1}^{&#039;}(\lambda_{j})=\psi_{1}^{&#039;}(\lambda_{j})&amp;lt;/math&amp;gt; (1)&lt;br /&gt;
&lt;br /&gt;
Это означает, что гипербола, являющаяся графиком функции &amp;lt;math&amp;gt;h_{1}(\lambda)&amp;lt;/math&amp;gt;, касается графика функции &amp;lt;math&amp;gt;\psi_{i}(\lambda)&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt;\lambda = \lambda_{j}&amp;lt;/math&amp;gt;. Два условия в (1) - это обычные условия метода Ньютона, за исключением того, что вместо прямой в качестве приближения используется гипербола. Легко проверить, что &amp;lt;math&amp;gt;c_{1}=\psi_{1}^{&#039;}(\lambda_{j})(d_{i} - \lambda_{j})^{2}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;\hat{c_{1}}=\psi_{1}(\lambda_{j}) - \psi_{1}^{&#039;}(\lambda_{j})(d_{i} - \lambda_{j})&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Подобным же образом выбираем &amp;lt;math&amp;gt;c_{2}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;\hat{c_{2}}&amp;lt;/math&amp;gt; так, чтобы функция &lt;br /&gt;
&lt;br /&gt;
 &amp;lt;math&amp;gt;h_{2}(\lambda) \equiv \hat{c_{2}} + \frac{c_{2}}{d_{i+1}-\lambda}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Удовлетворяла условиям&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;math&amp;gt;h_{2}(\lambda_{j}) = \psi_{2}(\lambda_{j})&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;h_{2}^{&#039;}(\lambda_{j})=\psi_{2}^{&#039;}(\lambda_{j})&amp;lt;/math&amp;gt; (2)&lt;br /&gt;
&lt;br /&gt;
Наконец, полагаем&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;math&amp;gt;h(\lambda) = 1 + h_{1}(\lambda) + h_{2}(\lambda) = (1 + \hat{c_{1}} + \hat{c_{2}} + \frac{c_{1}}{d_{i}-\lambda} + \frac{c_{2}}{d_{i+1}-\lambda} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;math&amp;gt;\equiv c_{3} + \frac{c_{1}}{d_{i}-\lambda} + \frac{c_{2}}{d_{i+1}-\lambda}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Схема реализации последовательного алгоритма ==&lt;br /&gt;
&#039;&#039;Вычисление собственных значений и собственных векторов симметричной трехдиагональной матрицы посредством стратегии &amp;quot;разделяй и властвуй&amp;quot;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;proc dc_eig&#039;&#039;&amp;lt;math&amp;gt;(T,Q,\Lambda)...&amp;lt;/math&amp;gt; по входной матрице &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; вычисляются выходные матрицы &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;\Lambda&amp;lt;/math&amp;gt;, такие, что &amp;lt;math&amp;gt;T = Q\Lambda Q^{T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Если &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; - матрица размера &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; x &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
  1. Присвоить выходным параметрам значения &amp;lt;math&amp;gt; Q = 1, \Lambda = T&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Иначе&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
  1. Представить &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; в виде &amp;lt;math&amp;gt; T = \begin{bmatrix} T_{1} &amp;amp; 0 \\ 0 &amp;amp; T_{2}\end{bmatrix} + b_{m}vv^{T} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  2. &#039;&#039;call dc_eig&#039;&#039;&amp;lt;math&amp;gt;(T_{1},Q_{1},\Lambda_{1})&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  3. &#039;&#039;call dc_eig&#039;&#039;&amp;lt;math&amp;gt;(T_{2},Q_{2},\Lambda_{2})&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  4. Построить &amp;lt;math&amp;gt;D+\rho uu^{T}&amp;lt;/math&amp;gt; по &amp;lt;math&amp;gt; \Lambda_{1},\Lambda_{2}, Q_{1}, Q_{2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  5. Найти матрицу собственных значений &amp;lt;math&amp;gt;\Lambda&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  6. Найти матрицу собственных векторов &amp;lt;math&amp;gt;Q^{&#039;}&amp;lt;/math&amp;gt; для матрицы &amp;lt;math&amp;gt;D+\rho uu^{T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  7. Построить матрицу собственных векторов &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; для матрицы &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt; : &amp;lt;math&amp;gt; Q = \begin{bmatrix} Q_{1} &amp;amp; 0 \\ 0 &amp;amp; Q_{2}\end{bmatrix}* Q^{&#039;} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  8. Присвоить выходным параметрам значения &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;\Lambda&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;endif&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
Проанализируем сложность алгоритма. Пусть &amp;lt;math&amp;gt;t(n)&amp;lt;/math&amp;gt; - число флопов при обработке матрицы размера &amp;lt;math&amp;gt; n x n&amp;lt;/math&amp;gt; процедурой &#039;&#039;dc_eig&#039;&#039;. Тогда &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; t(n) = 2t(n/2) &amp;lt;/math&amp;gt; два рекурсивных обращения к &#039;&#039;dc_eig&#039;&#039;&amp;lt;math&amp;gt;(T_{i},Q_{i},\Lambda_{i})&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; +O(n^{2})&amp;lt;/math&amp;gt; вычисление собственных значений матрицы &amp;lt;math&amp;gt;D+\rho uu^{T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; +O(n^{2})&amp;lt;/math&amp;gt; вычисление собственных векторов матрицы &amp;lt;math&amp;gt;D+\rho uu^{T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; +c*n^{3}&amp;lt;/math&amp;gt; вычисление матрицы &amp;lt;math&amp;gt;Q = \begin{bmatrix} Q_{1} &amp;amp; 0 \\ 0 &amp;amp; Q_{2}\end{bmatrix}*Q^{&#039;}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt; Q_{1}, Q_{2}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;Q^{&#039;}&amp;lt;/math&amp;gt; рассматриваются как плотные матрицы и используется стандартный алгоритм матричного умножения, то константа &amp;lt;math&amp;gt; c &amp;lt;/math&amp;gt; в последней строке равна 1. Таким образом, именно это умножение составляет наиболее трудоёмкую часть алгоритма в целом. Игнорируя члены порядка &amp;lt;math&amp;gt;n^{2}&amp;lt;/math&amp;gt;, получаем &amp;lt;math&amp;gt;t(n) = 2t(n/2) + cn^{3}&amp;lt;/math&amp;gt;. Решая это разностное уравнение, находим &amp;lt;math&amp;gt; t \approx c\frac{4}{3}n^{3} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
На практике константа &amp;lt;math&amp;gt;c&amp;lt;/math&amp;gt; обычно гораздо меньше 1, потому что матрица &amp;lt;math&amp;gt;Q^{&#039;}&amp;lt;/math&amp;gt; весьма разрежена вследствие явления, называемого дефляцией.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
В данном разделе представлен информационный граф алгоритма: на рисунке 5 изображена часть алгоритма, в которой происходит деление - &amp;quot;Divide&amp;quot;, а на рисунке 6 описана часть &amp;quot;Conquer&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[[File:Divide.jpg|thumb|center|left|800px|Рис. 5. Информационный граф алгоритма &amp;quot;Разделяй и властвуй&amp;quot; - шаг &amp;lt;b&amp;gt;Divide&amp;lt;/b&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
[[File:ConquerPart.jpg|thumb|center|1200px|Рис. 6. Информационный граф алгоритма &amp;quot;Разделяй и властвуй&amp;quot; - шаг &amp;lt;b&amp;gt;Conquer&amp;lt;/b&amp;gt;. Qk*k - матрица собственных значений размерности &amp;lt;b&amp;gt;k&amp;lt;/b&amp;gt;. Lk*k - матрица собственных векторов размерности &amp;lt;b&amp;gt;k&amp;lt;/b&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; - операция Divide разделения матрицы &amp;lt;math&amp;gt; T &amp;lt;/math&amp;gt; на матрицы &amp;lt;math&amp;gt;T_{1}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;T_{2}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Индексы - размерность &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;S_{k}&amp;lt;/math&amp;gt; - операция Conquer сбора матрицы &amp;lt;math&amp;gt; D &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; L_{k} &amp;lt;/math&amp;gt; - операция поиска собственных значений &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; S^{&#039;} &amp;lt;/math&amp;gt; - операция поиска собственных векторов матрицы &amp;lt;math&amp;gt; Q^{&#039;} &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; Q &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Ресурс параллелизма алгоритма ==&lt;br /&gt;
Рассмотрев информационный граф алгоритма, можно заметить, что в структуре имеется два параллельных блока - вызов рекурсивных функций &#039;&#039;dc_eig&#039;&#039; для вычисления собственных значений и векторов матриц &amp;lt;math&amp;gt;T_{1}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;T_{2}&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;dc_eig&#039;&#039; отрабатывают последовательно - сначала со входными параметрами &amp;lt;math&amp;gt;T_{1}, Q_{1}, \Lambda_{1}&amp;lt;/math&amp;gt;, затем - &amp;lt;math&amp;gt;T_{2}, Q_{2}, \Lambda_{2}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Таким образом, благодаря параллелизму высота алгоритма параллельного составляет &amp;lt;math&amp;gt;log_{2}(n)&amp;lt;/math&amp;gt;, когда как при последовательной реализации высота алгоритма равна сумме геометрической прогрессии &amp;lt;math&amp;gt;1+2+4+8+...+2^{n-1} = 2^{n} - 1&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;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
L = \begin{bmatrix}&lt;br /&gt;
 a_{1} &amp;amp; b_{1}&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp; \\&lt;br /&gt;
 b_{1} &amp;amp; \ddots  &amp;amp; \ddots \\&lt;br /&gt;
   &amp;amp; \ddots &amp;amp; a_{m-1} &amp;amp; b_{m-1} \\&lt;br /&gt;
 &amp;amp;&amp;amp; b_{m-1} &amp;amp; a_{m} &amp;amp; b_{m} \\&lt;br /&gt;
 &amp;amp;&amp;amp;&amp;amp; b_{m} &amp;amp; a_{m+1} &amp;amp; b_{m+1} \\&lt;br /&gt;
 &amp;amp;&amp;amp;&amp;amp;&amp;amp; b_{m+1} &amp;amp; \ddots \\&lt;br /&gt;
 &amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp; \ddots &amp;amp; b_{n-1} \\&lt;br /&gt;
 &amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp;&amp;amp; b_{n-1} &amp;amp; a_{n} \\&lt;br /&gt;
\end{bmatrix}&lt;br /&gt;
&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;\frac{n (n + 1)}{2}&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; 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; , соответствующих собственным значениям исходной матрицы.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объем выходных данных:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; n + n^{2} = n(n + 1) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
Описанный в данной статье алгоритм является самым быстрым алгоритмом, среди существующих: QR / Бисекция и обратная итерация / Разделяй и властвуй.&lt;br /&gt;
&lt;br /&gt;
Последовательная сложность алгоритма: &amp;lt;math&amp;gt; t = c\frac{4}{3}n^{3} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Параллельная сложность алгоритма (в силу использования дефляции): &amp;lt;math&amp;gt; O(n^{2.3})&amp;lt;/math&amp;gt;  или в самых редких случаях &amp;lt;math&amp;gt; O(n^{2}) &amp;lt;/math&amp;gt; (см. [3] стр. 8).&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;
Вторая часть описания алгоритмов в рамках AlgoWiki рассматривает все составные части процесса их реализации. Рассматривается как последовательная реализация алгоритма, так и параллельная. Описывается взаимосвязь свойств программ, реализующих алгоритм, и особенностей архитектуры компьютера, на которой они выполняются. Исследуется работа с памятью, локальность данных и вычислений, описывается масштабируемость и эффективность параллельных программ, производительность компьютеров, достигаемая на данной программе. Обсуждаются особенности реализации для разных классов архитектур компьютеров, приводятся ссылки на реализации в существующих библиотеках.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Особенности реализации последовательного алгоритма ==&lt;br /&gt;
&lt;br /&gt;
== Локальность данных и вычислений ==&lt;br /&gt;
&lt;br /&gt;
== Возможные способы и особенности параллельной реализации алгоритма ==&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&lt;br /&gt;
TBD&lt;br /&gt;
&lt;br /&gt;
== Динамические характеристики и эффективность реализации алгоритма ==&lt;br /&gt;
&lt;br /&gt;
== Выводы для классов архитектур ==&lt;br /&gt;
&lt;br /&gt;
== Существующие реализации алгоритма ==&lt;br /&gt;
Большого количества реализаций данного алгоритма найдено не было: единственный пример описан на страницах 7, 19 и 20 в [http://www.netlib.org/lapack/lawnspdf/lawn132.pdf этой статье], которая указана в списке литературы.&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
[1] Дж. Деммель, «Вычислительная линейная алгебра» //С. 230-235&lt;br /&gt;
&lt;br /&gt;
[2] [http://www.netlib.org/utk/people/JackDongarra/PAPERS/104_1999_a-parallel-divide-and-conquer-algorithm.pdf Francoise Tisseury, Jack Dongarra, A Parallel Divide and Conquer Algorithm for the Symmetric Eigenvalue Problem on Distributed Memory Architectures]&lt;br /&gt;
&lt;br /&gt;
[3] [http://www.netlib.org/lapack/lawnspdf/lawn132.pdf Francoise Tisseury, Jack Dongarra, Parallelizing the Divide and Conquer Algorithm for the Symmetric Tridiagonal Eigenvalue Problem on Distributed Memory Architectures]&lt;br /&gt;
&lt;br /&gt;
[4] [https://en.wikipedia.org/wiki/Divide-and-conquer_eigenvalue_algorithm Алгоритм &amp;quot;Разделяй и властвуй&amp;quot; - Wikipedia]&lt;br /&gt;
&lt;br /&gt;
[5] [http://www.cscamm.umd.edu/tadmor/pub/linear-stability/Gill_Tadmor_SISC90.pdf Doron Grill and Eitan Tadmor AN &amp;lt;math&amp;gt;O(N2)&amp;lt;/math&amp;gt; METHOD FOR COMPUTING THE EIGENSYSTEM OF &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;x&amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt; SYMMETRIC TRIDIAGONAL MATRICES BY THE DIVIDE AND CONQUER APPROACH]&lt;br /&gt;
&lt;br /&gt;
[[en:Description of algorithm properties and structure]]&lt;/div&gt;</summary>
		<author><name>Lineprinter</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:Elijah/%D0%9D%D0%B0%D1%85%D0%BE%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D1%81%D0%BE%D0%B1%D1%81%D1%82%D0%B2%D0%B5%D0%BD%D0%BD%D1%8B%D1%85_%D1%87%D0%B8%D1%81%D0%B5%D0%BB_%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%BC%D0%B5%D1%82%D0%BE%D0%B4%D0%BE%D0%BC_QR_%D1%80%D0%B0%D0%B7%D0%BB%D0%BE%D0%B6%D0%B5%D0%BD%D0%B8%D1%8F&amp;diff=15612</id>
		<title>Участник:Elijah/Нахождение собственных чисел квадратной матрицы методом QR разложения</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:Elijah/%D0%9D%D0%B0%D1%85%D0%BE%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D1%81%D0%BE%D0%B1%D1%81%D1%82%D0%B2%D0%B5%D0%BD%D0%BD%D1%8B%D1%85_%D1%87%D0%B8%D1%81%D0%B5%D0%BB_%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%BC%D0%B5%D1%82%D0%BE%D0%B4%D0%BE%D0%BC_QR_%D1%80%D0%B0%D0%B7%D0%BB%D0%BE%D0%B6%D0%B5%D0%BD%D0%B8%D1%8F&amp;diff=15612"/>
		<updated>2016-11-01T15:46:16Z</updated>

		<summary type="html">&lt;p&gt;Lineprinter: Lineprinter переименовал страницу Нахождение собственных чисел квадратной матрицы методом QR разложения в [[Участник:Elijah/Нахождение собстве…&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment}}&lt;br /&gt;
&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Нахождение собственных чисел квадратной матрицы методом QR разложения&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;N * O(n^3)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt; N * (12n - 15) &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;n^2&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Основные авторы описания: &amp;lt;br&amp;gt;&lt;br /&gt;
[[Участник:Elijah|И.В.Афанасьев]], разделы [[#Вычислительное ядро алгоритма|1.3]] - [[#Свойства алгоритма|1.10]] (базовый алгоритм), [[#Особенности реализации последовательного алгоритма|2.1]], [[#Возможные способы и особенности параллельной реализации алгоритма|2.3]], [[#Масштабируемость алгоритма и его реализации|2.4]], [[#Существующие реализации алгоритма|2.7]] &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Участник:Vladbrim|В.А.Шишватов]], разделы [[#Общее описание алгоритма|1.1]], [[#Математическое описание алгоритма|1.2]], [[#Вычислительное ядро алгоритма|1.3]] - [[#Свойства алгоритма|1.10]] ((алгоритм с преобразованием к матрице Хессенберга) &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
QR-алгоритм — это численный метод в линейной алгебре, предназначенный для решения полной проблемы собственных значений, то есть отыскания всех собственных чисел и собственных векторов матрицы. Был разработан в конце 1950-х годов независимо В. Н. Кублановской и Дж. Фрэнсисом. [https://ru.wikipedia.org/wiki/QR-алгоритм] &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Данный алгоритм хорошо подходит для поиска собственных значений матриц общего вида, порядок которых составляет порядке тысячи. &lt;br /&gt;
&lt;br /&gt;
Суть алгоритма состоит в итеративном приведении матрицы A к верхнетреугольном виду с помощью последовательных QR разложений и матричный умножений. Данные матрицы на каждой итерации подобны между собой, так как данные умножения равносильны ортогональным преобразованиям. В следствии подобия, собственные значения этих матриц равны между собой.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
&lt;br /&gt;
==== QR-алгоритм нахождения собственных чисел====&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; — вещественная матрица, для которой мы хотим найти собственные числа. Положим &amp;lt;math&amp;gt;A_0 = A&amp;lt;/math&amp;gt;. На &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;-ом шаге (начиная с &amp;lt;math&amp;gt;k = 0&amp;lt;/math&amp;gt;) вычислим QR-разложение &amp;lt;math&amp;gt;A_k = Q_k R_k&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;Q_k&amp;lt;/math&amp;gt; — ортогональная матрица (то есть &amp;lt;math&amp;gt;Q_k^\text{T} = Q_k^{-1}&amp;lt;/math&amp;gt;), а &amp;lt;math&amp;gt;R_k&amp;lt;/math&amp;gt; — верхняя треугольная матрица. Затем мы определяем &amp;lt;math&amp;gt;A_{k+1} = R_k Q_k&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что&lt;br /&gt;
: &amp;lt;math&amp;gt; A_{k+1} = R_k Q_k = Q_k^{-1} Q_k R_k Q_k = Q_k^{-1} A_k Q_k = Q_k^{T} A_k Q_k, &amp;lt;/math&amp;gt;&lt;br /&gt;
то есть все матрицы &amp;lt;math&amp;gt;A_k&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&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt; k \rightarrow \infty&amp;lt;/math&amp;gt; сходится по форме к клеточному правому треугольному виду, соответствующему клеткам с одинаковыми по модулю собственными значениями.&lt;br /&gt;
&amp;lt;ref&amp;gt;Численные методы / Н. С. Бахвалов, Н. П. Жидков, Г. М. Кобельков. — 3-е изд. — М: БИНОМ, Лаборатория знаний, 2004. — С. 321. — 636 с.&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Сходимость QR-алгоритма нахождения собственных чисел ====&lt;br /&gt;
&lt;br /&gt;
Сходимость QR-алгоритма определяется как сходимость поддиагональной подматрицы матрицы &amp;lt;math&amp;gt;A_k&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;A_k = R_{k-1}Q_{k-1}&amp;lt;/math&amp;gt;) к нулевой матрице.&lt;br /&gt;
&lt;br /&gt;
Предположим, что для матрицы &amp;lt;math&amp;gt;A \in \mathbb{C}^{n \times n}&amp;lt;/math&amp;gt; выполнены следующие условия:&lt;br /&gt;
&lt;br /&gt;
# &amp;lt;math&amp;gt;A = X \Lambda X^{-1}, \quad \Lambda = \begin{bmatrix} \Lambda_1 &amp;amp; 0 \\ 0 &amp;amp; \Lambda_2 \end{bmatrix}, \quad \Lambda_1 \in \mathbb{C}^{m \times m}, \quad \Lambda_2 \in \mathbb{C}^{r \times r}&amp;lt;/math&amp;gt;;&lt;br /&gt;
# &amp;lt;math&amp;gt;\left | \lambda_1  \right | \ge \ldots \ge \left | \lambda_m  \right | &amp;gt; \left | \lambda_{m+1} \right | \ge \ldots \ge  \left | \lambda_{m+r} \right | &amp;gt; 0, \quad \{ \lambda_1, \ldots, \lambda_m \} = \lambda(\Lambda_1), \quad \{ \lambda_{m+1}, \ldots, \lambda_{m+r} \} = \lambda(\Lambda_2)&amp;lt;/math&amp;gt;;&lt;br /&gt;
# ведущая подматрица порядка &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; в &amp;lt;math&amp;gt;X^{-1}&amp;lt;/math&amp;gt; невырожденная.&lt;br /&gt;
&lt;br /&gt;
Пусть QR-алгоритм порождает последовательность матриц вида:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;A_k = \begin{bmatrix} A_{11}^{(k)} &amp;amp; A_{12}^{(k)} \\ A_{21}^{(k)} &amp;amp; A_{22}^{(k)} \end{bmatrix}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Тогда &amp;lt;math&amp;gt;A_{21}^{(k)} \rightarrow O&amp;lt;/math&amp;gt; при &amp;lt;math&amp;gt; k \rightarrow \infty&amp;lt;/math&amp;gt;. При достижении необходимой точности матрицу &amp;lt;math&amp;gt;A_{21}^{(k)}&amp;lt;/math&amp;gt; можно заменит нулевой матрицей. В таком случае собственные значения необходимо искать для матриц &amp;lt;math&amp;gt;A_{11}^{(k)}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;A_{22}^{(k)}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Если условия 1-3 выполнены для всех &amp;lt;math&amp;gt;m \in \{1, ..., n-1\}&amp;lt;/math&amp;gt;, то все поддиагональные матрицы сходятся к нулевым матрицам. Таким образом, все элементы, лежащие ниже главной диагонали стремятся к нулю, и диагональные элементы матриц &amp;lt;math&amp;gt;A_k&amp;lt;/math&amp;gt; сходятся к искомым собственным значениям матрицы &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt;&amp;lt;ref&amp;gt;Тыртышников Е.Е. Методы численного анализа. — М.: Академия, 2007. — 320 c.&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Приведение матрицы к матрице Хессенберга ====&lt;br /&gt;
&lt;br /&gt;
Для преобразования матрицы к матрице Хессенберга&amp;lt;ref&amp;gt;[https://en.wikipedia.org/wiki/Hessenberg_matrix| Hessenberg matrix]&amp;lt;/ref&amp;gt; используется преобразование Хаусхолдера&amp;lt;ref&amp;gt;[https://en.wikipedia.org/wiki/Householder_transformation#Tridiagonalization| Tridiagonalization using Householder transformation]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для каждого &amp;lt;math&amp;gt; k = 1,2,... n-1 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt; \displaystyle \alpha = -\operatorname{sgn}(a^k_{k+1,k})\sqrt{\sum_{j=k+1}^{n}(a^k_{jk})^2} &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt; r = \sqrt{\frac{1}{2}(\alpha^{2}-a^k_{k+1,k}\alpha)} &amp;lt;/math&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;v^k_1 = v^{k}_2 = \cdots = v^k_k=0;&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt; v^{k}_{k+1} = \frac{a^{k}_{k+1,k}-\alpha}{2r}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt; v^{k}_j = \frac{a^{k}_{jk}}{2r}&amp;lt;/math&amp;gt;, для &amp;lt;math&amp;gt;j = k+2; k+3, ..., n&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt; \displaystyle P^{k} = I - 2v^{(k)}(v^{(k)})^\text{T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;\displaystyle A^{(k+1)} = P^{k}A^{(k)}(P^{k})^\text{T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
При этом матрица &amp;lt;math&amp;gt;P&amp;lt;/math&amp;gt; не вычисляется и используется напрямую. Вместо этого используются следующие вычисления&lt;br /&gt;
:&amp;lt;math&amp;gt;\displaystyle B^{(k)} = P^{k}A^{(k)} = (I - 2v^{(k)}(v^{(k)})^\text{T}) A^{(k+1)} = A^{(k+1)} - 2A^{(k+1)}v^{(k)}(v^{(k)})^\text{T}&amp;lt;/math&amp;gt;&lt;br /&gt;
:&amp;lt;math&amp;gt;\displaystyle A^{(k+1)} &lt;br /&gt;
= P^{k}A^{(k)}(P^{k})^\text{T} &lt;br /&gt;
= B^{(k)}(P^{k})^\text{T} &lt;br /&gt;
= ((B^{(k)})^\text{T})^\text{T}(P^{k})^\text{T} &lt;br /&gt;
= (P^{k}(B^{(k)})^\text{T})^\text{T} &lt;br /&gt;
= ((B^{(k)})^\text{T} - 2B^{(k)}v^{(k)}(v^{(k)})^\text{T})^\text{T}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом перемножение матриц &amp;lt;math&amp;gt;P^{k}A^{(k)}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;B^{k}P^{(k)}&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^3)&amp;lt;/math&amp;gt; (перемножение матриц)&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
&lt;br /&gt;
==== Базовый алгоритм ====&lt;br /&gt;
&lt;br /&gt;
У базового QR алгоритма есть два вычислительных ядра:&lt;br /&gt;
# операция поиска QR разложения с использованием метода Гивенса&lt;br /&gt;
# матричное умножение полученных матриц&lt;br /&gt;
&lt;br /&gt;
Для получения &amp;lt;math&amp;gt;QR&amp;lt;/math&amp;gt;-разложения методом Гивенса &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; матрицу приводят к правой треугольной &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; последовательностью умножений её слева на матрицы вращения &amp;lt;math&amp;gt;T_{1 2}, T_{1 3}, ..., T_{1 n}, T_{2 3}, T_{2 4}, ..., T_{2 n}, ... , T_{n-2 n}, T_{n-1 n}&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}&amp;lt;/math&amp;gt;) и &amp;lt;math&amp;gt;B&amp;lt;/math&amp;gt; (элементы &amp;lt;math&amp;gt;b_{ij}&amp;lt;/math&amp;gt;) вычисляется плотная матрица &amp;lt;math&amp;gt;C&amp;lt;/math&amp;gt;с элементами &amp;lt;math&amp;gt;c_{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;
c_{ij} = \sum_{k = 1}^{n} a_{ik} b_{kj}, \quad i \in [1, m], \quad i \in [1, l].&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;
Для QR алгоритма с использования матрицы Хессенберга:&lt;br /&gt;
# получение матрицы Хессенберга из исходной матрицы&lt;br /&gt;
# операция поиска QR разложения с использованием модифицированного метода Гивенса&lt;br /&gt;
&lt;br /&gt;
Для преобразования исходной матрицы к матрице Хессенберга используются преобразования Хаусхолдера (см. [[#Приведение матрицы к матрице Хессенберга| раздел 1.2.3]])&lt;br /&gt;
&lt;br /&gt;
Для получения &amp;lt;math&amp;gt;QR&amp;lt;/math&amp;gt;-разложения методом Гивенса для матрицы Хессенберга &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt;, матрицу приводят к правой треугольной &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; последовательностью умножений её слева на матрицы вращения &amp;lt;math&amp;gt;T_{1 2}, T_{2 3}, T_{3 4}, ..., T_{(n-1)\ n}&amp;lt;/math&amp;gt;. &amp;lt;br&amp;gt; &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Более подробное описание вычислительных ядер можно найти по соответствующим ссылкам в [[#Макроструктура алгоритма| разделе 1.4]].&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Как уже описано в описании ядра алгоритма, базовая версия QR-алгоритма на каждой итерации использует следующие алгоритмы: &lt;br /&gt;
# [[Метод_Гивенса_(вращений)_QR-разложения_квадратной_матрицы_(вещественный_вариант)|Метод Гивенса (вращений) QR-разложения квадратной матрицы]] или [[Метод_Хаусхолдера_(отражений)_QR-разложения_квадратной_матрицы,_вещественный_вариант|Метод Хаусхолдера(отражений) QR-разложения_квадратной_матрицы]]&lt;br /&gt;
# [[Перемножение_плотных_неособенных_матриц_(последовательный_вещественный_вариант)|Перемножение плотных неособенных матриц]]&lt;br /&gt;
&lt;br /&gt;
В случае использования матрицы Хессенберга:&lt;br /&gt;
# Получение матрицы Хессенберга из исходной матрицы с помощью преобразований Хаусхолдера&amp;lt;ref&amp;gt;[https://en.wikipedia.org/wiki/Householder_transformation| Householder transformation]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
# [[Метод_Гивенса_(вращений)_QR-разложения_квадратной_матрицы_(вещественный_вариант)|Метод Гивенса (вращений) 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;
Каждая итерация состоит из следующих действий (с начальным условием &amp;lt;math&amp;gt; A_0 = A&amp;lt;/math&amp;gt;):&lt;br /&gt;
# Для матрицы &amp;lt;math&amp;gt; A_{n} &amp;lt;/math&amp;gt; строится QR разложение любым доступным последовательным алгоритмом на матрицы &amp;lt;math&amp;gt; Q_{n} &amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt; R_{n} &amp;lt;/math&amp;gt; &amp;lt;br&amp;gt;&lt;br /&gt;
# Матрицы &amp;lt;math&amp;gt; R_{n} &amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt; Q_{n} &amp;lt;/math&amp;gt; перемножаются, таким образом получается матрица &amp;lt;math&amp;gt; A_{n+1} = R_{n} * Q_{n}  &amp;lt;/math&amp;gt;  для следующей итерации &amp;lt;math&amp;gt; n + 1 &amp;lt;/math&amp;gt; &lt;br /&gt;
# Проверятся, приведена ли матрица к верхнетреугольной форме, и если да, то производится выход из цикла.&lt;br /&gt;
&lt;br /&gt;
==== Алгоритм с преобразованием к матрице Хессенберга ====&lt;br /&gt;
&lt;br /&gt;
На первом этапе исходная матрица &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; преобразуется к матрице Хессенберга &amp;lt;math&amp;gt;H&amp;lt;/math&amp;gt;, которая будет иметь такие же собственные значения, что и изначальная матрица.&lt;br /&gt;
&lt;br /&gt;
Затем производится некоторое число итераций, состоящих из следующих действий (с начальным условием &amp;lt;math&amp;gt; A_0 = H&amp;lt;/math&amp;gt;): &lt;br /&gt;
# Для матрицы &amp;lt;math&amp;gt; A_{n} &amp;lt;/math&amp;gt; строится QR разложение с помощью метода вращения Гивенса. При этом занулять нужно не все элементы, а только &amp;lt;math&amp;gt; a_{i + 1, i} &amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;i \in {1, n - 1}&amp;lt;/math&amp;gt;. Полученная матрица будет опять являться матрицей Хессенберга.&lt;br /&gt;
# Проверятся, приведена ли матрица к верхнетреугольной форме, и если да, то производится выход из цикла.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
&lt;br /&gt;
==== Последовательная сложность базового алгоритма ====&lt;br /&gt;
Рассчитаем последовательную сложность базового алгоритма. Пусть &amp;lt;math style=&amp;quot;vertical-align:0%&amp;quot;&amp;gt; A \in \mathbb{R}^{n \times n}&amp;lt;/math&amp;gt;, и для приведения матрицы к треугольной форме необходимо произвести N итераций алгоритма. &amp;lt;br&amp;gt;&lt;br /&gt;
На каждой итерации алгоритма производится QR разложение (сложностью &amp;lt;math style=&amp;quot;vertical-align:0%&amp;quot;&amp;gt; 2 * n^3 &amp;lt;/math&amp;gt;) и матричное умножение (сложностью &amp;lt;math style=&amp;quot;vertical-align:0%&amp;quot;&amp;gt; n^3 &amp;lt;/math&amp;gt;). Проверка того, имеет ли матрица треугольную форму, может быть проведена за &amp;lt;math style=&amp;quot;vertical-align:0%&amp;quot;&amp;gt; n^2 &amp;lt;/math&amp;gt; операций. &amp;lt;br&amp;gt;&lt;br /&gt;
Таким образом итоговая сложность одной итерации составляет: &amp;lt;math&amp;gt; 3*n^3 + n^2 = O(n^3)&amp;lt;/math&amp;gt; &amp;lt;br&amp;gt;&lt;br /&gt;
Общая сложность алгоритма при  &amp;lt;math&amp;gt; N &amp;lt;/math&amp;gt; итерациях составляет &amp;lt;math&amp;gt; N * O(n^3) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Последовательная сложность алгоритма с приведением матрицы к матрице Хессенбрга ====&lt;br /&gt;
&lt;br /&gt;
Данный алгоритм состоит из двух этапов:&lt;br /&gt;
# преобразование исходной матрицы к матрице Хессенберга&lt;br /&gt;
# QR-разложение методом Гивенса&lt;br /&gt;
&lt;br /&gt;
===== Сложность алгоритма приведения матрицы к матрице Хессенбрга =====&lt;br /&gt;
&lt;br /&gt;
На каждой итерации алгоритма приведения матрицы к матрице Хессенбрга требуется вычислить: &lt;br /&gt;
# &amp;lt;math&amp;gt;\alpha&amp;lt;/math&amp;gt; со сложность &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;&lt;br /&gt;
# &amp;lt;math&amp;gt;r&amp;lt;/math&amp;gt; со сложностью &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
# &amp;lt;math&amp;gt;v^k&amp;lt;/math&amp;gt; со сложностью &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;&lt;br /&gt;
# &amp;lt;math&amp;gt;A^{(k+1)}&amp;lt;/math&amp;gt; со сложностью &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt;&lt;br /&gt;
Таким образом вычислительная сложность одной итерации равна &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
Всего таких итераций &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;, поэтому общая сложность алгоритма равна &amp;lt;math&amp;gt;O(n^3)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===== Сложность алгоритма QR-разложения методом Гивенса =====&lt;br /&gt;
&lt;br /&gt;
Метод Гивенса использует матрицы вращения, чтобы занулить ненулевые элементы матрицы под главной диагональю на каждой итерации. Умножение на одну матрицу вращения имеет вычислительную сложность &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;. Для случая произвольной матрицы в худшем случае потребуется занулить &amp;lt;math&amp;gt;\frac{n^2 - n}{2}&amp;lt;/math&amp;gt; элементов. Если матрица является матрицей Хессенберга, то таких элементов будет всего лишь &amp;lt;math&amp;gt;n - 1&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Таким образом, для произвольной матрицы одна итерация алгоритма QR-разложения имеет вычислительную сложность равную &amp;lt;math&amp;gt;O(n^3)&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^2)&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 * O(n^3)&amp;lt;/math&amp;gt;, для матрицы Хессенберга - &amp;lt;math&amp;gt;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^3) + N * O(n^2)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
&lt;br /&gt;
Макрограф алгоритма изображён на рисунке 1. Он представляет из себя верхний уровень алгоритма, на котором в цикле итеративно вызываются следующие операции: &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Инициализация матриц (&#039;&#039;&#039;init matrices&#039;&#039;&#039;)&lt;br /&gt;
# QR разложение (&#039;&#039;&#039;QR decomposition&#039;&#039;&#039;)&lt;br /&gt;
# Матричное умножение (&#039;&#039;&#039;matrix multiplication&#039;&#039;&#039;)&lt;br /&gt;
# Проверка, приведена ли матрица к верхней треугольной форме (&#039;&#039;&#039;check if matrix is upper triangular&#039;&#039;&#039;)&lt;br /&gt;
# Получение собственных значений из диагонали матрицы (&#039;&#039;&#039;get eigenvalues from matrix diagonal&#039;&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
Информационные графы [[Метод_Гивенса_(вращений)_QR-разложения_квадратной_матрицы_(вещественный_вариант)#.D0.98.D0.BD.D1.84.D0.BE.D1.80.D0.BC.D0.B0.D1.86.D0.B8.D0.BE.D0.BD.D0.BD.D1.8B.D0.B9_.D0.B3.D1.80.D0.B0.D1.84|QR разложения]] и [[Перемножение_плотных_неособенных_матриц_(последовательный_вещественный_вариант)#.D0.98.D0.BD.D1.84.D0.BE.D1.80.D0.BC.D0.B0.D1.86.D0.B8.D0.BE.D0.BD.D0.BD.D1.8B.D0.B9_.D0.B3.D1.80.D0.B0.D1.84|матричного умножения]] могут быть найдены по приведенным ссылкам. Информационный граф проверки того, приведена ли матрица к верхнетреугольной форме, будет приведен далее на рисунке 2. Информационные графы инициализации и получения собственных значений представляют собой работу с вводом и выводом входных/выходных данных, и, поэтому, рассматриваться не будут. &lt;br /&gt;
&lt;br /&gt;
[[Файл:Screen_Shot_2016-10-15_at_12.43.32_PM.png|thumb|center|600px|Рисунок 1. Информационный граф базовой версии QR-алгоритма без отображения входных и выходных данных. N итераций.]]&lt;br /&gt;
&lt;br /&gt;
[[Файл:Check_if_matrix_is_upper_triangular.png|thumb|center|300px|Рисунок 2. Информационный граф проверки приведенности матрицы к верхнетреугольной форме.]] &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В случае оптимизации алгоритма с приведением матрицы к форме Хессенберга, инициализация матрицы (int matrices (1)), будет включать в себя преобразование хаусхолдера, имеющее следующий граф алгоритма:&lt;br /&gt;
&lt;br /&gt;
[[Файл:Hessenberg_transformation.png|thumb|center|500px|Рисунок 3. Информационный граф проверки хаусхолдера.]] &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Все итерации базового QR-алгоритма производятся последовательно, поэтому на верхнем уровне алгоритм чисто последователен. &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Основной ресурс параллелизма представлен на нижнем уровне, при реализации различных операций, используемых в алгоритме, таких как QR разложение методом вращений и матричное умножение. &amp;lt;br&amp;gt; &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Параллельная сложность базового QR-алгоритма ====&lt;br /&gt;
Посчитаем параллельную сложность каждой операции по отдельности, а затем, по полученным данным, и всего алгоритма целиком: &amp;lt;br&amp;gt;&lt;br /&gt;
# Параллельная сложность QR разложения методом вращений составляет &amp;lt;math&amp;gt; 11n - 16 &amp;lt;/math&amp;gt; &amp;lt;ref name=&amp;quot;QRGivens&amp;quot;&amp;gt;[[Метод Гивенса (вращений) QR-разложения квадратной матрицы (вещественный вариант)|Метод Гивенса (вращений) QR-разложения квадратной матрицы]]&amp;lt;/ref&amp;gt; &amp;lt;br&amp;gt;&lt;br /&gt;
# Параллельная сложность матричного умножения составляет &amp;lt;math&amp;gt; n &amp;lt;/math&amp;gt;&amp;lt;ref name=&amp;quot;MatrixMultiplication&amp;quot;&amp;gt;[[Перемножение_плотных_неособенных_матриц_(последовательный_вещественный_вариант)|Перемножение плотных неособенных матриц]]&amp;lt;/ref&amp;gt;.&amp;lt;br&amp;gt;&lt;br /&gt;
# Параллельная сложность проверки, является ли матрица верхнетреугольной, равна n. &amp;lt;br&amp;gt;&lt;br /&gt;
Таким образом параллельная сложность каждой итерации составляет &amp;lt;math&amp;gt; 13n - 15 &amp;lt;/math&amp;gt;. При N итерациях, которые необходимо производить последовательно, итоговая сложность базового алгоритма составляет &amp;lt;math&amp;gt; N * (13n - 15) &amp;lt;/math&amp;gt;, что в упрощенном виде равно &amp;lt;math&amp;gt; N * O(n) &amp;lt;/math&amp;gt; &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Параллельная сложность алгоритма с приведением матрицы к форме Хессенберга: ====&lt;br /&gt;
Снова вычислим параллельную сложность каждой операции по отдельности, а затем, по полученным данным, и всего алгоритма целиком: &amp;lt;br&amp;gt;&lt;br /&gt;
# Параллельная сложность приведения матрицы к форме Хессенберга составляет &amp;lt;math&amp;gt; O(n^2) &amp;lt;/math&amp;gt;&lt;br /&gt;
# Параллельная сложность каждой итерации с приведенной матрицы составляет &amp;lt;math&amp;gt; O(n) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом параллельная сложность всего алгоритма при N итерациях составляет &amp;lt;math&amp;gt; O(n^2) + N*O(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 style=&amp;quot;vertical-align:0%&amp;quot;&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 style=&amp;quot;vertical-align:0%&amp;quot;&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&amp;lt;/math&amp;gt; вещественных собственных чисел &amp;lt;math&amp;gt; | l_{i} | &amp;lt;/math&amp;gt; матрицы &amp;lt;math style=&amp;quot;vertical-align:0%&amp;quot;&amp;gt;A&amp;lt;/math&amp;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;
&lt;br /&gt;
Вычислительная мощность, равная отношению числа операций &amp;lt;math&amp;gt; N * O(n^3) &amp;lt;/math&amp;gt; к суммарному объему входных и выходных данных &amp;lt;math&amp;gt; n^2 + n &amp;lt;/math&amp;gt;, для каждой итерации линейна, а для всего алгоритма в целом равна &amp;lt;math&amp;gt; N*n &amp;lt;/math&amp;gt;, что позволяет сделать вывод о том, что перемещение данных для обработки не играет важной роли в данном алгоритме. &lt;br /&gt;
&lt;br /&gt;
Для оптимизированного алгоритма вычислительная мощность так же линейна (приведение матрицы к форме Хессенберга имеет сложность &amp;lt;math&amp;gt; O(n^3) &amp;lt;/math&amp;gt;, а объем выходных данных &amp;lt;math&amp;gt; O(n^2) &amp;lt;/math&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Вычислительная погрешность растет линейно, из-за использования метода вращений для QR разложения. &lt;br /&gt;
&lt;br /&gt;
Данный алгоритм недетерминирован, так как число итераций зависит от значений элементов матрицы и выход из основного цикла происходит по достижению некоторой точности. В зависимости от этого, вычислительный поток имеет различную длину. &lt;br /&gt;
На каждой отдельной итерации в свою очередь алгоритм полностью детерминирован, так как детерминированы его обе базовые макрооперации - QR разложение и матричное умножение.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация ==&lt;br /&gt;
&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Основным типом данных для QR алгоритма является матрица. В данной статье представлена реализация на языке C++, что накладывает определенные ограничения на формат хранени матриц: все матрицы хранятся в одномерном массиве в построчном (ROW_MAJOR) формате. &lt;br /&gt;
&lt;br /&gt;
В простейшем случае QR-алгоритм выглядит следующим образом:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C++&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int sequential_QR_algorithm(float *A, int size)&lt;br /&gt;
{&lt;br /&gt;
    float *Q = new double[size * size];&lt;br /&gt;
    float *R = new double[size * size];&lt;br /&gt;
    while(!check_if_upper_triangular(A, size)))&lt;br /&gt;
    {&lt;br /&gt;
        sgeqrf (ROW_MAJOR, size, size, A, size, tau);&lt;br /&gt;
        set_Q_and_R(A, Q, R, size);&lt;br /&gt;
        sorgqr(ROW_MAJOR, size, size, size, Q, size, tau);&lt;br /&gt;
        sgemm (RowMajor, NoTrans, NoTrans, size, size, size, 1, R, size, Q, size, 0, A, size);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В последовательном случае функции dgeqrf, dorgqr и dgemm представляют собой последовательные функции стандарта BLAS, вычисляющие QR разложение(geqrf), матрицу Q после разложения (dorgqr) и матричное умножение (dgemm). &lt;br /&gt;
Так же здесь используются простейшие функции set_Q_and_R и check_if_upper_triangular. Функция set_Q_and_R получает после QR разложения из матрицы A данные о матрицах Q и R. Функция check_if_upper_triangular проверяет, приведена ли матрица A к треугольном виду, и если да, происходит выход из цикла.&lt;br /&gt;
&lt;br /&gt;
Для работы программы в памяти необходимо хранить 3 матрицы, таким образом общий объем составляет 3 * n^2. Так же необходим вспомогательный массив tau для QR разложения, однако его размер составляет всего n. Так же важно заметить, что различные реализации функций dgeqrf, dorgqr и dgemm обычно используют внутренние вспомогательные массивы для блочной обработки, однако, их размер не превышает n * C, где C - некоторая архитектурно зависимая константа. &lt;br /&gt;
&lt;br /&gt;
Все вычисления рекомендуется производить в одинарной точности.&lt;br /&gt;
&lt;br /&gt;
Как уже говорилось ранее, внешний цикл представленной программы сугубо последователен (итеративен), и основной ресурс параллелизма расположен в вызываемых функциях (geqrf, gemm). Их реализации бывают крайне различны, поэтому данный алгоритм можно легко преобразовать для любой архитектуры, используя набор стандартных библиотечных функций. К примеру, для реализации алгоритма на GPU можно использовать библиотеку MAGMA, на многоядерных CPU - Intel MKL.&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Как уже говорилось в [[#Особенности реализации последовательного алгоритма| разделе 2.1]], при использовании различных библиотек можно реализовать данный алгоритм практически на всех архитектурах. Далее более подробно будут рассмотрены примеры для некоторых из них. Особенности параллельных реализаций для базовых макроопераций (QR разложения и матричного умножения), уже описаны в соответствующих статьях (см. ссылки в [[#Макроструктура алгоритма| разделе 1.4]]) &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Реализация на многоядерных CPU (одном узле)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Для распарарллеливания на многоядерных CPU используются функции LAPACKE_sgeqrf, LAPACKE_sorgqr и cblas_sgemm. Распараллеливание оставшихся функций может быть произведено с использованием openMP, так как внутренние циклы в них независимы по данным.&lt;br /&gt;
&lt;br /&gt;
Исходный код предложенной реализации представлен далее:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C++&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int CPU_QR_algorithm(float *A, int size)&lt;br /&gt;
{&lt;br /&gt;
    float *Q = new double[size * size];&lt;br /&gt;
    float *R = new double[size * size];&lt;br /&gt;
    while(!check_if_upper_triangular(A, size)))&lt;br /&gt;
    {&lt;br /&gt;
        LAPACKE_sgeqrf (LAPACK_ROW_MAJOR, size, size, A, size, tau);&lt;br /&gt;
        set_Q_and_R(A, Q, R, size);&lt;br /&gt;
        LAPACKE_sorgqr(LAPACK_ROW_MAJOR, size, size, size, Q, size, tau);&lt;br /&gt;
        cblas_sgemm (CblasRowMajor, CblasNoTrans, CblasNoTrans, size, size, size, 1, R, size, Q, size, 0, A, size);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Реализация на GPU (и multi-GPU)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Для реализации алгоритма на GPU и multi-GPU можно использовать аналогичные функции dgemm и geqrf из библиотеки [[ Magma. http://icl.cs.utk.edu/magma/ | magma]].&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
&lt;br /&gt;
==== Масштабируемость реализации алгоритма ====&lt;br /&gt;
&lt;br /&gt;
Для данного алгоритма была проверена масштабируемость оптимизированной версии с приведением матрицы к форме Хессенберга. Тесты проводились для случайно сгенерированной матрицы размера 8000x8000 на 1-14 параллельных вычислительных потоках.&lt;br /&gt;
&lt;br /&gt;
Код, для которого проверлась масштабируемость, реализован с помощью функций библиотеки intel MKL, выглядит следующим образом: &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C++&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;stdio.h&amp;quot;&lt;br /&gt;
#include &amp;quot;stdlib.h&amp;quot;&lt;br /&gt;
#include &amp;quot;mkl.h&amp;quot;&lt;br /&gt;
#include &amp;quot;math.h&amp;quot;&lt;br /&gt;
#include &amp;lt;iostream&amp;gt;&lt;br /&gt;
&lt;br /&gt;
using namespace std;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char* argv[])&lt;br /&gt;
{&lt;br /&gt;
    int max_threads;&lt;br /&gt;
    float *h, *Wr, *Wi, *z, *scalar, *h_copy;&lt;br /&gt;
    double s_initial, s_elapsed;&lt;br /&gt;
    lapack_int n = atoi(argv[1]); //size=n&lt;br /&gt;
    &lt;br /&gt;
    h=(float*)mkl_malloc(n*n*sizeof(float),32);&lt;br /&gt;
    h_copy=(float*)mkl_malloc(n*n*sizeof(float),32);&lt;br /&gt;
    z=(float*)mkl_malloc(n*n*sizeof(float),32); //z will not be referenced due to set compz=&amp;quot;N&amp;quot;&lt;br /&gt;
    Wr=(float*)mkl_malloc(n*sizeof(float),32);&lt;br /&gt;
    Wi=(float*)mkl_malloc(n*sizeof(float),32);&lt;br /&gt;
    scalar=(float*)mkl_malloc((n-1)*sizeof(float),32);&lt;br /&gt;
    if(h==NULL) //initilization for hessenberg matrix&lt;br /&gt;
    {&lt;br /&gt;
        for(int i=0;i&amp;lt;n*n;i++)&lt;br /&gt;
        {&lt;br /&gt;
            h[i]=(float)rand()*2000;&lt;br /&gt;
            h_copy[i]=h[i];&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    for(int j=0;j&amp;lt;n*n;j++)&lt;br /&gt;
    {&lt;br /&gt;
        z[j]=0.0;&lt;br /&gt;
    }&lt;br /&gt;
    max_threads=mkl_get_max_threads();&lt;br /&gt;
    &lt;br /&gt;
    for(int t = 1;t &amp;lt;= max_threads;t++)&lt;br /&gt;
    {&lt;br /&gt;
        double t1 = dsecnd();&lt;br /&gt;
        mkl_set_num_threads(t);&lt;br /&gt;
        LAPACKE_sgehrd(LAPACK_ROW_MAJOR, n, 1, n, h, n, scalar);&lt;br /&gt;
        LAPACKE_shseqr(LAPACK_ROW_MAJOR, &#039;E&#039;, &#039;N&#039;, n, 1, n, h, n, Wr, Wi, z, n);&lt;br /&gt;
        double t2 = dsecnd();&lt;br /&gt;
&lt;br /&gt;
        cout &amp;lt;&amp;lt; &amp;quot;total time for &amp;quot; &amp;lt;&amp;lt; t &amp;lt;&amp;lt; &amp;quot; threads: &amp;quot; &amp;lt;&amp;lt; t2 - t1 &amp;lt;&amp;lt; endl &amp;lt;&amp;lt; endl;&lt;br /&gt;
&lt;br /&gt;
        for(int k=0;k&amp;lt;n*n;k++)&lt;br /&gt;
        {&lt;br /&gt;
            h[k]=h_copy[k];&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Далее приводятся графики сильной и слабой масштабируемости. &lt;br /&gt;
&lt;br /&gt;
[[file:QR_algorithm_strong_scalability.png|thumb|center|500px|Рис.4 Сильная масштабируемость оптимизированного QR алгоритма с приведением матрицы к форме Хессенберга]]&lt;br /&gt;
&lt;br /&gt;
Из данных графика видно, что алгоритм хорошо масштабируется при небольшом числе процессов (1-4), однако дальнейшее прибавление процессов не ведет к линейному ускорению.&lt;br /&gt;
&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Последовательный&#039;&#039;&#039; QR алгоритм, а так же различные базовые функции для его реализации, можно найти в [http://www.netlib.org/scalapack/ библиотеке LAPACK] и её аналогах и расширениях.&lt;br /&gt;
&lt;br /&gt;
Базовая версия QR алгоритма может быть реализована описанным в [[#Особенности реализации последовательного алгоритма| разделе 2.1]] способом при помощи функций:&lt;br /&gt;
# &#039;&#039;&#039;?geqrf&#039;&#039;&#039; - вычисляет QR разложение произвольной матрицы&lt;br /&gt;
# &#039;&#039;&#039;?gemm&#039;&#039;&#039; - вычисляет произведение двух плотных матриц&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;?steqr&#039;&#039;&#039; - вычисляет собственные числа и собственные вектора для случая тридиагональной матрицы. &lt;br /&gt;
&lt;br /&gt;
Для реализации оптимизированного алгоритма с приведением матрицы к матрице Хессенберга можно использовать следующие функции:&lt;br /&gt;
# &#039;&#039;&#039;?gehrd&#039;&#039;&#039; - приводят матрицу общего вида к верхней матрице Хессенберга с использованием ортогональных/унитарных трансформаций&lt;br /&gt;
# &#039;&#039;&#039;?hseqr&#039;&#039;&#039; - вычисляют собственные значения матрицы в верхней матрице Хессенберга  с использованием QR алгоритма&lt;br /&gt;
&lt;br /&gt;
Подробная документация по упомянутым функциям может быть найдена [http://physics.oregonstate.edu/~landaur/nacphy/lapack/eigen.html здесь].&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Параллельный&#039;&#039;&#039; QR алгоритм может быть реализован с помощью библиотек [http://www.netlib.org/scalapack/ ScaLAPACK] и [http://www.cs.utexas.edu/users/plapack/ PlaLAPACK].&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;p?geqrf&#039;&#039;&#039;, &#039;&#039;&#039;p?gemm&#039;&#039;&#039;, &#039;&#039;&#039;p?hseqr&#039;&#039;&#039; - аналогичные функции ScaLAPACK.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;PLA_QR&#039;&#039;&#039;, &#039;&#039;&#039;PLA_Gemm&#039;&#039;&#039; - аналогичные функции из PlaLAPACK.&lt;br /&gt;
&lt;br /&gt;
== Примечания ==&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;/div&gt;</summary>
		<author><name>Lineprinter</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:Timokhinivan/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=15611</id>
		<title>Участник:Timokhinivan/Быстрое преобразование Фурье</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:Timokhinivan/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=15611"/>
		<updated>2016-11-01T15:45:48Z</updated>

		<summary type="html">&lt;p&gt;Lineprinter: Lineprinter переименовал страницу Быстрое преобразование Фурье в Участник:Timokhinivan/Быстрое преобразование Фурье без оставления перенапра…&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment}}&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Быстрое преобразование Фурье&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(N \log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O(\log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Автор описания: [[U:Timokhinivan|Тимохин И.В.]], ВМК МГУ.&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритмов ==&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
Преобразование Фурье переводит сигнал в его спектр и обратно, и широко&lt;br /&gt;
применяется в самых различных областях вычислительной математики —&lt;br /&gt;
собственно спектральный анализ, сжатие данных, вычисление свёрток и&lt;br /&gt;
т.д.  В связи с этим, особый интерес представляют быстры алгоритмы для&lt;br /&gt;
вычисления этого преобразование.  На сегодняшний день, наилучшие из&lt;br /&gt;
них имеют сложность &amp;lt;math&amp;gt;O(n \log n)&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;
  Y_k = \sum_{l = 0}^{N-1} X_l \epsilon^{kl}_N,\qquad k = \overline{0,N-1}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
где &amp;lt;math&amp;gt;\epsilon_N = e^{-\frac{2i\pi}{N}}&amp;lt;/math&amp;gt; — первообразный корень&lt;br /&gt;
из 1 степени &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;F = \left\{ \epsilon^{kl}_N \right\}_{k\,l = 0}^{N-1}&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;
  Y(l_1,\ldots,l_d) = \sum_{k_1 = 0}^{N_1-1} \cdots \sum_{k_d = 0}^{N_d-1} X(k_1,\ldots,k_d) \prod_{j=1}^{d} \epsilon_{N_j}^{l_j k_j}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Многомерное преобразование Фурье эквивалентно последовательному выполнению одномерных преобразований по каждому измерению; при использовании для этого рекурсивного алгоритма, общая сложность вычислений также составляет &amp;lt;math&amp;gt;O(N\log N)&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;N = \prod_{j=1}^d N_j&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; — составное, т.е. &amp;lt;math&amp;gt;N = mn&amp;lt;/math&amp;gt;, то возможно&lt;br /&gt;
существенно сократить вычислительные расходы за счёт сведения к&lt;br /&gt;
двумерному преобразованию Фурье.  А именно, положим &amp;lt;math&amp;gt;X(l_1, l_2)&lt;br /&gt;
= X_{l_1 n + l_2}&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt; Y(k_1, k_2) = Y_{k_1 m + k_2}&amp;lt;/math&amp;gt;,&lt;br /&gt;
где &amp;lt;math&amp;gt;l_1, k_2 = \overline{0, m-1}&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;l_2, k_1 =&lt;br /&gt;
\overline{0, n-1}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В этом случае&amp;lt;ref name=&amp;quot;bachv&amp;quot;&amp;gt;Н.С. Бахвалов, Н.П. Жидков, Г.М. Кобельков, &#039;&#039;Численные методы&#039;&#039;, 2003 г., ISBN: 5-94774-060-5&amp;lt;/ref&amp;gt;&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
  \begin{align}&lt;br /&gt;
    Y(k_1, k_2) &amp;amp;=&amp;amp; \sum_{l_2=0}^{n-1} (\epsilon^{k_2 l_2}_N \hat{X}(k_2, l_2)) \epsilon^{k_1 l_2}_n \\&lt;br /&gt;
    \hat{X}(k_2, l_2) &amp;amp;=&amp;amp; \sum_{l_1=0}^{m-1} X(l_1, l_2) \epsilon^{k_2 l_1}_m&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;m\times n&amp;lt;/math&amp;gt; по строкам.&lt;br /&gt;
# Применяем к каждому столбцу преобразование Фурье.&lt;br /&gt;
# Умножаем элемент в позиции &amp;lt;math&amp;gt;(i,j)&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;\epsilon^{ij}_N&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Применяем к каждой строке преобразование Фурье.&lt;br /&gt;
# Результат записан в получившейся матрице по столбцам.&lt;br /&gt;
&lt;br /&gt;
Данный алгоритм уже даёт существенный выигрыш по сравнению с обычным&lt;br /&gt;
умножением матрицы на вектор: &amp;lt;math&amp;gt; O(m^2 n + n^2 m) &amp;lt;/math&amp;gt; против&lt;br /&gt;
&amp;lt;math&amp;gt;O(m^2 n^2)&amp;lt;/math&amp;gt;.  Однако наилучших результатов можно добиться,&lt;br /&gt;
если применять этот алгоритм рекурсивно на этапах 2 и 4.&lt;br /&gt;
&lt;br /&gt;
Так, если &amp;lt;math&amp;gt;N = \prod_{i=1}^{K} p_i&amp;lt;/math&amp;gt;, то целесообразно&lt;br /&gt;
на каждом уровне рекурсии «отщеплять» одно &amp;lt;math&amp;gt;p_i&amp;lt;/math&amp;gt;.  В этом&lt;br /&gt;
случае задача сводится к вычислению &amp;lt;math&amp;gt; \prod_{i\neq j} p_i &amp;lt;/math&amp;gt;&lt;br /&gt;
преобразований Фурье порядка &amp;lt;math&amp;gt;p_j&amp;lt;/math&amp;gt; для всех&lt;br /&gt;
&amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;.  При небольших &amp;lt;math&amp;gt;p_i&amp;lt;/math&amp;gt; (например, 2), это&lt;br /&gt;
можно проделывать «в лоб».&lt;br /&gt;
&lt;br /&gt;
==== Преобразование Фурье с большим простым порядком (алгоритм Радера) ====&lt;br /&gt;
&lt;br /&gt;
В случае, если порядок преобразования простой, но ещё слишком большой, чтобы вычислять его непосредственно, задача усложняется.  Алгоритм в этом случае такой:&lt;br /&gt;
&lt;br /&gt;
# &amp;lt;math&amp;gt; Y_0 = \sum_{l=0}^{N-1} X_l&amp;lt;/math&amp;gt; вычисляется непосредственно.&lt;br /&gt;
# Любым способом вычисляется &amp;lt;math&amp;gt;g&amp;lt;/math&amp;gt; — первообразный корень мультипликативной группы поля &amp;lt;math&amp;gt;\mathbb{Z}_{N}&amp;lt;/math&amp;gt;.&lt;br /&gt;
# &amp;lt;math&amp;gt; Y_{g^{N-p} \bmod N} = X_0 + \sum_{q=0}^{N-2} X_{g^q \bmod N} \epsilon_N^{g^{q-p}\bmod N} &amp;lt;/math&amp;gt;.  &lt;br /&gt;
&lt;br /&gt;
Последнее выражение представляет собой свёртку и вычисляется с помощью всё того же преобразования Фурье.  В принципе, это можно проделывать непосредственно, поскольку порядок свёртки &amp;lt;math&amp;gt;N - 1&amp;lt;/math&amp;gt; уже составной (по меньшей мере чётный), но, поскольку свёртка, в отличие от самого преобразования Фурье, допускает увеличение порядка, имеет смысл воспользоваться этим и выбрать порядок с небольшими простыми множителями.&lt;br /&gt;
&lt;br /&gt;
Таким образом, поскольку при правильной реализации подавляющее большинство рекурсивных вызовов будет использовать алгоритм Кули-Тьюки, в дальнейшем будет рассматриваться именно он.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
&lt;br /&gt;
На каждом уровне рекурсии наиболее дорогостоящими этапами являются&lt;br /&gt;
рекурсивные вызовы преобразования Фурье: они требуют в сумме&lt;br /&gt;
&amp;lt;math&amp;gt;O(mn (\log m + \log n))&amp;lt;/math&amp;gt; операций против&lt;br /&gt;
&amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt; для умножения на поправочные коэффициенты (шаг 3).&lt;br /&gt;
&lt;br /&gt;
То же верно и для алгоритма в целом в случае &amp;lt;math&amp;gt;N = \prod_{i=1}^{K} p_i&amp;lt;/math&amp;gt;;&lt;br /&gt;
а именно, суммарно наибольшие вычислительные затраты связаны с&lt;br /&gt;
вычислением преобразований Фурье порядка &amp;lt;math&amp;gt;p_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;
# &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; рекурсивных вызовов алгоритма.&lt;br /&gt;
# Умножение всех элементов рабочего вектора на поправочные коэффициенты.&lt;br /&gt;
# Ещё &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; рекурсивных вызовов.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Псевдокод (для метода Кули-Тьюки):&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;fft(X, N)&lt;br /&gt;
begin&lt;br /&gt;
  (m, n) = factor(N)&lt;br /&gt;
&lt;br /&gt;
  if (n == 1) // Предполагается, что n &amp;lt;= m&lt;br /&gt;
    direct_fft(X, N) // Через умножение на матрицу&lt;br /&gt;
&lt;br /&gt;
  // 1&lt;br /&gt;
  transpose(X, m, n) //Транспонирует X как матрицу m x n, хранящуюся по строкам&lt;br /&gt;
   &lt;br /&gt;
  // 2&lt;br /&gt;
  do i = 0, n - 1&lt;br /&gt;
    fft(X(i * m : (i + 1) * m - 1), m)&lt;br /&gt;
&lt;br /&gt;
  // 3&lt;br /&gt;
  do i = 0, n - 1&lt;br /&gt;
    do j = 0, m - 1&lt;br /&gt;
      X(i * m + j) *= eps(i * j, N)&lt;br /&gt;
&lt;br /&gt;
  // 4&lt;br /&gt;
  transpose(X, n, m)&lt;br /&gt;
&lt;br /&gt;
  // 5&lt;br /&gt;
  do j = 0, m - 1&lt;br /&gt;
    fft(X(j * n : (j + 1) * n - 1), n)&lt;br /&gt;
&lt;br /&gt;
  // 6&lt;br /&gt;
  transpose(X, m, n)&lt;br /&gt;
end&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Здесь &amp;lt;code&amp;gt;eps&amp;lt;/code&amp;gt; может как вычислять соответствующие коэффициенты, так и брать их из заранее заготовленной таблицы.  Последний способ требует увеличения затрат по памяти, но позволяет существенно сэкономить на вычислениях, а потому является предпочтительным.&lt;br /&gt;
&lt;br /&gt;
Кроме того, шаги 3 и 4 можно переставлять произвольным образом (с очевидными изменениями в шаге 3).&lt;br /&gt;
&lt;br /&gt;
Особо следует отметить, что если порядок элементов в выходном массиве не важен, шаг 6 можно пропустить.  &lt;br /&gt;
&lt;br /&gt;
Так, например, если преобразование Фурье используется для вычисления свёртки векторов, то конкретный порядок элементов в их образах Фурье совершенно не важен, лишь бы он был одинаков для обоих векторов.  В этом случае в прямом преобразовании можно опустить шаг 6, а в обратном — 1.&lt;br /&gt;
&lt;br /&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;N&amp;lt;/math&amp;gt; большое, переключиться на алгоритм Радера.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Непосредственно из описания алгоритма получаем, что если сложность&lt;br /&gt;
вычисления преобразования Фурье обозначить &amp;lt;math&amp;gt;f_N&amp;lt;/math&amp;gt;, то&lt;br /&gt;
&amp;lt;math&amp;gt;f_{mn} = n f_{m} + m f_{n} + mn&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;N = \prod_{i=1}^K p_i&amp;lt;/math&amp;gt;, и на каждом шаге&lt;br /&gt;
«отщеплять» от него одно &amp;lt;math&amp;gt;p_i&amp;lt;/math&amp;gt;, а преобразование Фурье для&lt;br /&gt;
&amp;lt;math&amp;gt;p_i&amp;lt;/math&amp;gt; вычислять «в лоб», то общее количество операций&lt;br /&gt;
составит &amp;lt;math&amp;gt;O\left(N\sum_{i=1}^K p_i\right)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В частности, если &amp;lt;math&amp;gt;p_i \leq P&amp;lt;/math&amp;gt;, то &amp;lt;math&amp;gt;K \leq \log_P N&amp;lt;/math&amp;gt;&lt;br /&gt;
и для сложности получаем &amp;lt;math&amp;gt;O(NP\log_P 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;O(N \log N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
[[Файл:FourierRecursiveGraph.svg|center|thumb|600px|&#039;&#039;&#039;Рисунок 1.&#039;&#039;&#039; Информационный граф алгоритма Кули-Тьюки для &amp;lt;math&amp;gt;N = 15&amp;lt;/math&amp;gt;. Исходные данные обозначены фиолетовым, результат — красным.  Преобразования Фурье для &amp;lt;math&amp;gt;m = 5&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n = 3&amp;lt;/math&amp;gt; представлены как «чёрные ящики». Умножение на поворотные коэффициенты представлено оранжевыми узлами.]]&lt;br /&gt;
&lt;br /&gt;
Каждый уровень рекурсивного вызова состоит из трёх этапов: рекурсивный вызов по столбцам, умножение на поправочные коэффициенты и рекурсивного вызова по строкам. Таким образом для высоты ЯПФ имеем&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
 h(mn) = 1 + h(m) + h(n)&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;N&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;h(N) = O\left(\sum_{i=1}^K p_i\right) = O(P \log_P N) = O(\log 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;
&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;
&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;
Для описания этих случаев нам понадобится матрица&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
  Q = \begin{bmatrix} &lt;br /&gt;
     1 &amp;amp;   &amp;amp;   &amp;amp;        &amp;amp;   &amp;amp;   \\ &lt;br /&gt;
       &amp;amp;   &amp;amp;   &amp;amp;        &amp;amp;   &amp;amp; 1 \\&lt;br /&gt;
       &amp;amp;   &amp;amp;   &amp;amp;        &amp;amp; 1 &amp;amp;   \\&lt;br /&gt;
       &amp;amp;   &amp;amp;   &amp;amp; \cdots &amp;amp;   &amp;amp;   \\&lt;br /&gt;
       &amp;amp;   &amp;amp; 1 &amp;amp;        &amp;amp;   &amp;amp;   \\&lt;br /&gt;
       &amp;amp; 1 &amp;amp;   &amp;amp;        &amp;amp;   &amp;amp;&lt;br /&gt;
 \end{bmatrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Если &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt; — вещественный, то &amp;lt;math&amp;gt;Y = Q\bar{Y}&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 = -Q\bar{Y}&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Если &amp;lt;math&amp;gt;X = Q\bar{X}&amp;lt;/math&amp;gt;, то &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; — вещественный.&lt;br /&gt;
# Если &amp;lt;math&amp;gt;X = -Q\bar{X}&amp;lt;/math&amp;gt;, то &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; — чисто мнимый.&lt;br /&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; требуют для своего хранения в два раза меньше памяти, чем в общем случае.&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;, имеем&amp;lt;ref name=&amp;quot;voevodtyrt&amp;quot;&amp;gt;В.В. Воеводин, Е.Е. Тыртышников, &#039;&#039;Вычислительные процессы с теплицевыми матрицами&#039;&#039;, Москва, «Наука», 1987 г.&amp;lt;/ref&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
# &amp;lt;math&amp;gt;F = F^T&amp;lt;/math&amp;gt;&lt;br /&gt;
# &amp;lt;math&amp;gt;F^{-1} = \tfrac{1}{N} F^*&amp;lt;/math&amp;gt;&lt;br /&gt;
# &amp;lt;math&amp;gt;F^* = QF &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Свойство 2 представляет особый интерес, поскольку позволяет вычислять обратное преобразование Фурье по тем же алгоритмам, что и прямое, поскольку &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;\bar{\epsilon}&amp;lt;/math&amp;gt; вместо &amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt;.  В сущности, свойство 3 позволяет свести задачу к исходной непосредственно, но ценой дополнительных пересылок.&lt;br /&gt;
&lt;br /&gt;
Кроме того, из второго свойства следует&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
 \|F x\|_2 = \sqrt{N} \|x\|_2&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
что свидетельствует о численной устойчивости алгоритма.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
При реализации последовательной версии алгоритма (а равно и параллельной, поскольку на определённом уровне рекурсии алгоритм всё равно будет выполняться на каждом узле независимо) стоит иметь ввиду несколько вещей:&lt;br /&gt;
&lt;br /&gt;
==== Поворотные коэффициенты ====&lt;br /&gt;
&lt;br /&gt;
Как уже упоминалось, поворотные коэффициенты имеет смысл вычислить заранее и хранить в специальном массиве.  Отдельное внимание при этом стоит обратить на передачу этого массива в рекурсивные вызовы.&lt;br /&gt;
&lt;br /&gt;
В самом деле, поскольку &amp;lt;math&amp;gt;\epsilon^{l}_{n} = \epsilon^{lm}_{mn} = \epsilon^{lm}_{N}&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; на &#039;&#039;совсем&#039;&#039; мелкие множители (&amp;lt;math&amp;gt;p_i \equiv 2&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;
&#039;&#039;Многие&#039;&#039; используемые в алгоритме данные зависят только от порядка преобразования — массив поворотных коэффициентов, факторизация &amp;lt;math&amp;gt;N&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;
Поскольку все преобразования в алгоритме являются, с точностью до константы &amp;lt;math&amp;gt;\leq \sqrt{N}&amp;lt;/math&amp;gt;, унитарными, катастрофического роста ошибок округления не предвидится, а потому все вычисления возможно проводить с целевой точностью.&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
[[Файл:IT_FFTMemaccess.png|center|thumb|800px|Рисунок 2. Профиль обращений в память простого алгоритма Кули-Тьюки с &amp;lt;math&amp;gt;p = (2, 2, 3, 5, 2)&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;N&amp;lt;/math&amp;gt; с &#039;&#039;достаточно&#039;&#039; большими делителями, наиболее очевидным и простым вариантом является распределение по процессорам рекурсивных вызовов алгоритма Кули-Тьюки. В этом случае на весь алгоритм приходится только три пересылки, но зато они проходят по схеме «все-со-всеми» (транспонирование матрицы) и не могут выполняться параллельно с вычислениями.&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;N = mn&amp;lt;/math&amp;gt; всегда мал, ситуация усложняется. В этом случае придётся применять алгоритм Радера на межпроцессорном уровне, что связано с более сложной схемой пересылок и большим их количеством (т.к. приходится осуществлять преобразование Фурье дважды: прямое и обратное). &lt;br /&gt;
&lt;br /&gt;
В связи с этим, некоторые существующие реализации (например, Intel® MKL) просто не поддерживают этот случай. Впрочем, некоторые реализации не поддерживают большие простые делители &#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;
&lt;br /&gt;
* [//software.intel.com/en-us/intel-mkl Intel® Math Kernel Library] — содержит в том числе и версию для распределённых систем.  Имеет интерфейсы для C, C++ и Fortran.&lt;br /&gt;
* [http://www.fftw.org FFTW] — содержит распределённую реализацию начиная с версии 3.3.  Имеет интерфейсы для C и Fortran.&lt;br /&gt;
* [//www.gnu.org/software/gsl GNU Scientific Library] — имеет интерфейс для C.&lt;br /&gt;
* [//developer.nvidia.com/cufft cuFFT] — реализация алгоритма для GPU от NVIDIA.&lt;br /&gt;
* [http://gpuopen.com/compute-product/clfft clFFT] — реализация алгоритма на OpenCL для CPU и GPU.  Поддерживает только &amp;lt;math&amp;gt;N = 2^p 3^q 5^r 7^s&amp;lt;/math&amp;gt;&lt;br /&gt;
* [http://www.ibm.com/systems/power/software/essl IBM® Engineering and Scientific Subroutine Library] — для процессоров IBM® POWER™, имеет интерфейсы для C, C++ и Fortran, существует версия для многопроцессорных систем.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Lineprinter</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:Timokhinivan/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=15610</id>
		<title>Участник:Timokhinivan/Быстрое преобразование Фурье</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:Timokhinivan/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5_%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%A4%D1%83%D1%80%D1%8C%D0%B5&amp;diff=15610"/>
		<updated>2016-11-01T15:44:55Z</updated>

		<summary type="html">&lt;p&gt;Lineprinter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment}}&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Быстрое преобразование Фурье&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(N \log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O(\log N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;O(N)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Автор описания: [[U:Timokhinivan|Тимохин И.В.]], ВМК МГУ.&lt;br /&gt;
&lt;br /&gt;
== Свойства и структура алгоритмов ==&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
Преобразование Фурье переводит сигнал в его спектр и обратно, и широко&lt;br /&gt;
применяется в самых различных областях вычислительной математики —&lt;br /&gt;
собственно спектральный анализ, сжатие данных, вычисление свёрток и&lt;br /&gt;
т.д.  В связи с этим, особый интерес представляют быстры алгоритмы для&lt;br /&gt;
вычисления этого преобразование.  На сегодняшний день, наилучшие из&lt;br /&gt;
них имеют сложность &amp;lt;math&amp;gt;O(n \log n)&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;
  Y_k = \sum_{l = 0}^{N-1} X_l \epsilon^{kl}_N,\qquad k = \overline{0,N-1}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
где &amp;lt;math&amp;gt;\epsilon_N = e^{-\frac{2i\pi}{N}}&amp;lt;/math&amp;gt; — первообразный корень&lt;br /&gt;
из 1 степени &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;F = \left\{ \epsilon^{kl}_N \right\}_{k\,l = 0}^{N-1}&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;
  Y(l_1,\ldots,l_d) = \sum_{k_1 = 0}^{N_1-1} \cdots \sum_{k_d = 0}^{N_d-1} X(k_1,\ldots,k_d) \prod_{j=1}^{d} \epsilon_{N_j}^{l_j k_j}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Многомерное преобразование Фурье эквивалентно последовательному выполнению одномерных преобразований по каждому измерению; при использовании для этого рекурсивного алгоритма, общая сложность вычислений также составляет &amp;lt;math&amp;gt;O(N\log N)&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;N = \prod_{j=1}^d N_j&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; — составное, т.е. &amp;lt;math&amp;gt;N = mn&amp;lt;/math&amp;gt;, то возможно&lt;br /&gt;
существенно сократить вычислительные расходы за счёт сведения к&lt;br /&gt;
двумерному преобразованию Фурье.  А именно, положим &amp;lt;math&amp;gt;X(l_1, l_2)&lt;br /&gt;
= X_{l_1 n + l_2}&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt; Y(k_1, k_2) = Y_{k_1 m + k_2}&amp;lt;/math&amp;gt;,&lt;br /&gt;
где &amp;lt;math&amp;gt;l_1, k_2 = \overline{0, m-1}&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;l_2, k_1 =&lt;br /&gt;
\overline{0, n-1}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В этом случае&amp;lt;ref name=&amp;quot;bachv&amp;quot;&amp;gt;Н.С. Бахвалов, Н.П. Жидков, Г.М. Кобельков, &#039;&#039;Численные методы&#039;&#039;, 2003 г., ISBN: 5-94774-060-5&amp;lt;/ref&amp;gt;&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
  \begin{align}&lt;br /&gt;
    Y(k_1, k_2) &amp;amp;=&amp;amp; \sum_{l_2=0}^{n-1} (\epsilon^{k_2 l_2}_N \hat{X}(k_2, l_2)) \epsilon^{k_1 l_2}_n \\&lt;br /&gt;
    \hat{X}(k_2, l_2) &amp;amp;=&amp;amp; \sum_{l_1=0}^{m-1} X(l_1, l_2) \epsilon^{k_2 l_1}_m&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;m\times n&amp;lt;/math&amp;gt; по строкам.&lt;br /&gt;
# Применяем к каждому столбцу преобразование Фурье.&lt;br /&gt;
# Умножаем элемент в позиции &amp;lt;math&amp;gt;(i,j)&amp;lt;/math&amp;gt; на &amp;lt;math&amp;gt;\epsilon^{ij}_N&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Применяем к каждой строке преобразование Фурье.&lt;br /&gt;
# Результат записан в получившейся матрице по столбцам.&lt;br /&gt;
&lt;br /&gt;
Данный алгоритм уже даёт существенный выигрыш по сравнению с обычным&lt;br /&gt;
умножением матрицы на вектор: &amp;lt;math&amp;gt; O(m^2 n + n^2 m) &amp;lt;/math&amp;gt; против&lt;br /&gt;
&amp;lt;math&amp;gt;O(m^2 n^2)&amp;lt;/math&amp;gt;.  Однако наилучших результатов можно добиться,&lt;br /&gt;
если применять этот алгоритм рекурсивно на этапах 2 и 4.&lt;br /&gt;
&lt;br /&gt;
Так, если &amp;lt;math&amp;gt;N = \prod_{i=1}^{K} p_i&amp;lt;/math&amp;gt;, то целесообразно&lt;br /&gt;
на каждом уровне рекурсии «отщеплять» одно &amp;lt;math&amp;gt;p_i&amp;lt;/math&amp;gt;.  В этом&lt;br /&gt;
случае задача сводится к вычислению &amp;lt;math&amp;gt; \prod_{i\neq j} p_i &amp;lt;/math&amp;gt;&lt;br /&gt;
преобразований Фурье порядка &amp;lt;math&amp;gt;p_j&amp;lt;/math&amp;gt; для всех&lt;br /&gt;
&amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;.  При небольших &amp;lt;math&amp;gt;p_i&amp;lt;/math&amp;gt; (например, 2), это&lt;br /&gt;
можно проделывать «в лоб».&lt;br /&gt;
&lt;br /&gt;
==== Преобразование Фурье с большим простым порядком (алгоритм Радера) ====&lt;br /&gt;
&lt;br /&gt;
В случае, если порядок преобразования простой, но ещё слишком большой, чтобы вычислять его непосредственно, задача усложняется.  Алгоритм в этом случае такой:&lt;br /&gt;
&lt;br /&gt;
# &amp;lt;math&amp;gt; Y_0 = \sum_{l=0}^{N-1} X_l&amp;lt;/math&amp;gt; вычисляется непосредственно.&lt;br /&gt;
# Любым способом вычисляется &amp;lt;math&amp;gt;g&amp;lt;/math&amp;gt; — первообразный корень мультипликативной группы поля &amp;lt;math&amp;gt;\mathbb{Z}_{N}&amp;lt;/math&amp;gt;.&lt;br /&gt;
# &amp;lt;math&amp;gt; Y_{g^{N-p} \bmod N} = X_0 + \sum_{q=0}^{N-2} X_{g^q \bmod N} \epsilon_N^{g^{q-p}\bmod N} &amp;lt;/math&amp;gt;.  &lt;br /&gt;
&lt;br /&gt;
Последнее выражение представляет собой свёртку и вычисляется с помощью всё того же преобразования Фурье.  В принципе, это можно проделывать непосредственно, поскольку порядок свёртки &amp;lt;math&amp;gt;N - 1&amp;lt;/math&amp;gt; уже составной (по меньшей мере чётный), но, поскольку свёртка, в отличие от самого преобразования Фурье, допускает увеличение порядка, имеет смысл воспользоваться этим и выбрать порядок с небольшими простыми множителями.&lt;br /&gt;
&lt;br /&gt;
Таким образом, поскольку при правильной реализации подавляющее большинство рекурсивных вызовов будет использовать алгоритм Кули-Тьюки, в дальнейшем будет рассматриваться именно он.&lt;br /&gt;
&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
&lt;br /&gt;
На каждом уровне рекурсии наиболее дорогостоящими этапами являются&lt;br /&gt;
рекурсивные вызовы преобразования Фурье: они требуют в сумме&lt;br /&gt;
&amp;lt;math&amp;gt;O(mn (\log m + \log n))&amp;lt;/math&amp;gt; операций против&lt;br /&gt;
&amp;lt;math&amp;gt;O(mn)&amp;lt;/math&amp;gt; для умножения на поправочные коэффициенты (шаг 3).&lt;br /&gt;
&lt;br /&gt;
То же верно и для алгоритма в целом в случае &amp;lt;math&amp;gt;N = \prod_{i=1}^{K} p_i&amp;lt;/math&amp;gt;;&lt;br /&gt;
а именно, суммарно наибольшие вычислительные затраты связаны с&lt;br /&gt;
вычислением преобразований Фурье порядка &amp;lt;math&amp;gt;p_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;
# &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; рекурсивных вызовов алгоритма.&lt;br /&gt;
# Умножение всех элементов рабочего вектора на поправочные коэффициенты.&lt;br /&gt;
# Ещё &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; рекурсивных вызовов.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Псевдокод (для метода Кули-Тьюки):&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;fft(X, N)&lt;br /&gt;
begin&lt;br /&gt;
  (m, n) = factor(N)&lt;br /&gt;
&lt;br /&gt;
  if (n == 1) // Предполагается, что n &amp;lt;= m&lt;br /&gt;
    direct_fft(X, N) // Через умножение на матрицу&lt;br /&gt;
&lt;br /&gt;
  // 1&lt;br /&gt;
  transpose(X, m, n) //Транспонирует X как матрицу m x n, хранящуюся по строкам&lt;br /&gt;
   &lt;br /&gt;
  // 2&lt;br /&gt;
  do i = 0, n - 1&lt;br /&gt;
    fft(X(i * m : (i + 1) * m - 1), m)&lt;br /&gt;
&lt;br /&gt;
  // 3&lt;br /&gt;
  do i = 0, n - 1&lt;br /&gt;
    do j = 0, m - 1&lt;br /&gt;
      X(i * m + j) *= eps(i * j, N)&lt;br /&gt;
&lt;br /&gt;
  // 4&lt;br /&gt;
  transpose(X, n, m)&lt;br /&gt;
&lt;br /&gt;
  // 5&lt;br /&gt;
  do j = 0, m - 1&lt;br /&gt;
    fft(X(j * n : (j + 1) * n - 1), n)&lt;br /&gt;
&lt;br /&gt;
  // 6&lt;br /&gt;
  transpose(X, m, n)&lt;br /&gt;
end&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Здесь &amp;lt;code&amp;gt;eps&amp;lt;/code&amp;gt; может как вычислять соответствующие коэффициенты, так и брать их из заранее заготовленной таблицы.  Последний способ требует увеличения затрат по памяти, но позволяет существенно сэкономить на вычислениях, а потому является предпочтительным.&lt;br /&gt;
&lt;br /&gt;
Кроме того, шаги 3 и 4 можно переставлять произвольным образом (с очевидными изменениями в шаге 3).&lt;br /&gt;
&lt;br /&gt;
Особо следует отметить, что если порядок элементов в выходном массиве не важен, шаг 6 можно пропустить.  &lt;br /&gt;
&lt;br /&gt;
Так, например, если преобразование Фурье используется для вычисления свёртки векторов, то конкретный порядок элементов в их образах Фурье совершенно не важен, лишь бы он был одинаков для обоих векторов.  В этом случае в прямом преобразовании можно опустить шаг 6, а в обратном — 1.&lt;br /&gt;
&lt;br /&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;N&amp;lt;/math&amp;gt; большое, переключиться на алгоритм Радера.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
&lt;br /&gt;
Непосредственно из описания алгоритма получаем, что если сложность&lt;br /&gt;
вычисления преобразования Фурье обозначить &amp;lt;math&amp;gt;f_N&amp;lt;/math&amp;gt;, то&lt;br /&gt;
&amp;lt;math&amp;gt;f_{mn} = n f_{m} + m f_{n} + mn&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;math&amp;gt;N = \prod_{i=1}^K p_i&amp;lt;/math&amp;gt;, и на каждом шаге&lt;br /&gt;
«отщеплять» от него одно &amp;lt;math&amp;gt;p_i&amp;lt;/math&amp;gt;, а преобразование Фурье для&lt;br /&gt;
&amp;lt;math&amp;gt;p_i&amp;lt;/math&amp;gt; вычислять «в лоб», то общее количество операций&lt;br /&gt;
составит &amp;lt;math&amp;gt;O\left(N\sum_{i=1}^K p_i\right)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В частности, если &amp;lt;math&amp;gt;p_i \leq P&amp;lt;/math&amp;gt;, то &amp;lt;math&amp;gt;K \leq \log_P N&amp;lt;/math&amp;gt;&lt;br /&gt;
и для сложности получаем &amp;lt;math&amp;gt;O(NP\log_P 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;O(N \log N)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
[[Файл:FourierRecursiveGraph.svg|center|thumb|600px|&#039;&#039;&#039;Рисунок 1.&#039;&#039;&#039; Информационный граф алгоритма Кули-Тьюки для &amp;lt;math&amp;gt;N = 15&amp;lt;/math&amp;gt;. Исходные данные обозначены фиолетовым, результат — красным.  Преобразования Фурье для &amp;lt;math&amp;gt;m = 5&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;n = 3&amp;lt;/math&amp;gt; представлены как «чёрные ящики». Умножение на поворотные коэффициенты представлено оранжевыми узлами.]]&lt;br /&gt;
&lt;br /&gt;
Каждый уровень рекурсивного вызова состоит из трёх этапов: рекурсивный вызов по столбцам, умножение на поправочные коэффициенты и рекурсивного вызова по строкам. Таким образом для высоты ЯПФ имеем&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
 h(mn) = 1 + h(m) + h(n)&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;N&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;h(N) = O\left(\sum_{i=1}^K p_i\right) = O(P \log_P N) = O(\log 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;
&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;
&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;
Для описания этих случаев нам понадобится матрица&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
  Q = \begin{bmatrix} &lt;br /&gt;
     1 &amp;amp;   &amp;amp;   &amp;amp;        &amp;amp;   &amp;amp;   \\ &lt;br /&gt;
       &amp;amp;   &amp;amp;   &amp;amp;        &amp;amp;   &amp;amp; 1 \\&lt;br /&gt;
       &amp;amp;   &amp;amp;   &amp;amp;        &amp;amp; 1 &amp;amp;   \\&lt;br /&gt;
       &amp;amp;   &amp;amp;   &amp;amp; \cdots &amp;amp;   &amp;amp;   \\&lt;br /&gt;
       &amp;amp;   &amp;amp; 1 &amp;amp;        &amp;amp;   &amp;amp;   \\&lt;br /&gt;
       &amp;amp; 1 &amp;amp;   &amp;amp;        &amp;amp;   &amp;amp;&lt;br /&gt;
 \end{bmatrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Если &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt; — вещественный, то &amp;lt;math&amp;gt;Y = Q\bar{Y}&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 = -Q\bar{Y}&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Если &amp;lt;math&amp;gt;X = Q\bar{X}&amp;lt;/math&amp;gt;, то &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; — вещественный.&lt;br /&gt;
# Если &amp;lt;math&amp;gt;X = -Q\bar{X}&amp;lt;/math&amp;gt;, то &amp;lt;math&amp;gt;Y&amp;lt;/math&amp;gt; — чисто мнимый.&lt;br /&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; требуют для своего хранения в два раза меньше памяти, чем в общем случае.&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;, имеем&amp;lt;ref name=&amp;quot;voevodtyrt&amp;quot;&amp;gt;В.В. Воеводин, Е.Е. Тыртышников, &#039;&#039;Вычислительные процессы с теплицевыми матрицами&#039;&#039;, Москва, «Наука», 1987 г.&amp;lt;/ref&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
# &amp;lt;math&amp;gt;F = F^T&amp;lt;/math&amp;gt;&lt;br /&gt;
# &amp;lt;math&amp;gt;F^{-1} = \tfrac{1}{N} F^*&amp;lt;/math&amp;gt;&lt;br /&gt;
# &amp;lt;math&amp;gt;F^* = QF &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Свойство 2 представляет особый интерес, поскольку позволяет вычислять обратное преобразование Фурье по тем же алгоритмам, что и прямое, поскольку &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;\bar{\epsilon}&amp;lt;/math&amp;gt; вместо &amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt;.  В сущности, свойство 3 позволяет свести задачу к исходной непосредственно, но ценой дополнительных пересылок.&lt;br /&gt;
&lt;br /&gt;
Кроме того, из второго свойства следует&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
 \|F x\|_2 = \sqrt{N} \|x\|_2&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
что свидетельствует о численной устойчивости алгоритма.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
&lt;br /&gt;
При реализации последовательной версии алгоритма (а равно и параллельной, поскольку на определённом уровне рекурсии алгоритм всё равно будет выполняться на каждом узле независимо) стоит иметь ввиду несколько вещей:&lt;br /&gt;
&lt;br /&gt;
==== Поворотные коэффициенты ====&lt;br /&gt;
&lt;br /&gt;
Как уже упоминалось, поворотные коэффициенты имеет смысл вычислить заранее и хранить в специальном массиве.  Отдельное внимание при этом стоит обратить на передачу этого массива в рекурсивные вызовы.&lt;br /&gt;
&lt;br /&gt;
В самом деле, поскольку &amp;lt;math&amp;gt;\epsilon^{l}_{n} = \epsilon^{lm}_{mn} = \epsilon^{lm}_{N}&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; на &#039;&#039;совсем&#039;&#039; мелкие множители (&amp;lt;math&amp;gt;p_i \equiv 2&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;
&#039;&#039;Многие&#039;&#039; используемые в алгоритме данные зависят только от порядка преобразования — массив поворотных коэффициентов, факторизация &amp;lt;math&amp;gt;N&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;
Поскольку все преобразования в алгоритме являются, с точностью до константы &amp;lt;math&amp;gt;\leq \sqrt{N}&amp;lt;/math&amp;gt;, унитарными, катастрофического роста ошибок округления не предвидится, а потому все вычисления возможно проводить с целевой точностью.&lt;br /&gt;
&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
[[Файл:IT_FFTMemaccess.png|center|thumb|800px|Рисунок 2. Профиль обращений в память простого алгоритма Кули-Тьюки с &amp;lt;math&amp;gt;p = (2, 2, 3, 5, 2)&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;N&amp;lt;/math&amp;gt; с &#039;&#039;достаточно&#039;&#039; большими делителями, наиболее очевидным и простым вариантом является распределение по процессорам рекурсивных вызовов алгоритма Кули-Тьюки. В этом случае на весь алгоритм приходится только три пересылки, но зато они проходят по схеме «все-со-всеми» (транспонирование матрицы) и не могут выполняться параллельно с вычислениями.&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;N = mn&amp;lt;/math&amp;gt; всегда мал, ситуация усложняется. В этом случае придётся применять алгоритм Радера на межпроцессорном уровне, что связано с более сложной схемой пересылок и большим их количеством (т.к. приходится осуществлять преобразование Фурье дважды: прямое и обратное). &lt;br /&gt;
&lt;br /&gt;
В связи с этим, некоторые существующие реализации (например, Intel® MKL) просто не поддерживают этот случай. Впрочем, некоторые реализации не поддерживают большие простые делители &#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;
&lt;br /&gt;
* [//software.intel.com/en-us/intel-mkl Intel® Math Kernel Library] — содержит в том числе и версию для распределённых систем.  Имеет интерфейсы для C, C++ и Fortran.&lt;br /&gt;
* [http://www.fftw.org FFTW] — содержит распределённую реализацию начиная с версии 3.3.  Имеет интерфейсы для C и Fortran.&lt;br /&gt;
* [//www.gnu.org/software/gsl GNU Scientific Library] — имеет интерфейс для C.&lt;br /&gt;
* [//developer.nvidia.com/cufft cuFFT] — реализация алгоритма для GPU от NVIDIA.&lt;br /&gt;
* [http://gpuopen.com/compute-product/clfft clFFT] — реализация алгоритма на OpenCL для CPU и GPU.  Поддерживает только &amp;lt;math&amp;gt;N = 2^p 3^q 5^r 7^s&amp;lt;/math&amp;gt;&lt;br /&gt;
* [http://www.ibm.com/systems/power/software/essl IBM® Engineering and Scientific Subroutine Library] — для процессоров IBM® POWER™, имеет интерфейсы для C, C++ и Fortran, существует версия для многопроцессорных систем.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Lineprinter</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:Maria_Zaitseva/PAM_(Partitioning_Around_Medoids)&amp;diff=15609</id>
		<title>Участник:Maria Zaitseva/PAM (Partitioning Around Medoids)</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:Maria_Zaitseva/PAM_(Partitioning_Around_Medoids)&amp;diff=15609"/>
		<updated>2016-11-01T15:43:53Z</updated>

		<summary type="html">&lt;p&gt;Lineprinter: Lineprinter переименовал страницу PAM (Partitioning Around Medoids) в Участник:Maria Zaitseva/PAM (Partitioning Around Medoids) без оставления перенаправления&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment|ASA}}&lt;br /&gt;
&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Partitioning Around Medoids&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O(Tkn^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;k&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Авторы: [[Участник: Maria Zaitseva|Зайцева М.Ф.]], [[Участник: kovalexal|Ковальчук А.А.]].&lt;br /&gt;
&lt;br /&gt;
Авторы работали над каждым разделом статьи совместно (совместно искали и изучали материалы, формулировали описание и заполняли описание статьи на сайте). За содержание каждого из пунктов отвечают оба автора.    &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;PAM (Partitioning Around Medoids)&#039;&#039;&#039; – алгоритм разбиения множества объектов на [https://ru.wikipedia.org/wiki/Кластер кластеры], базирующийся на использовании наиболее представительных объектов каждого кластера ([https://ru.wikipedia.org/wiki/Медоид медоидов]) для их описания. Был разработан в 1987 году Леонардом Кауфманом и Питером Русивом &amp;lt;ref&amp;gt;Kaufman L., Rousseeuw P. J. Finding groups in data: an introduction to cluster analysis. – John Wiley &amp;amp; Sons, 2009. – Т. 344.&amp;lt;/ref&amp;gt;. Показывает хорошие результаты разбиения на небольших наборах данных, однако имеет высокую вычислительную сложность, ввиду чего неэффективно работает на больших наборах данных.&lt;br /&gt;
&lt;br /&gt;
= Свойства и структура алгоритма =&lt;br /&gt;
&lt;br /&gt;
== Общее описание алгоритма ==&lt;br /&gt;
Данный алгоритм используется для решения задачи [https://ru.wikipedia.org/wiki/Кластерный_анализ кластеризации] множества объектов на заранее известное число кластеров &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. Шаг &#039;&#039;BUILD&#039;&#039;. Построение начального набора &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; медоидов путем их последовательного выбора. Первый из них выбирается как объект, сумма дистанций которого до всех других объектов минимальна (центральный объект). Каждый следующий выбирается из оставшихся объектов как лучше всего минимизирующий сумму расстояний от объектов до ближайших к ним медоидов. Выбранный набор медоидов характеризует &amp;quot;конфигурацию&amp;quot; кластеров, для которой может быть вычислена ее стоимость.&lt;br /&gt;
&lt;br /&gt;
2. Шаг &#039;&#039;SWAP&#039;&#039;. На данном шаге производится попытка улучшить стоимость текущей конфигурации путем перебора всех возможных замен (медоид-немедоид). Среди рассматриваемых конфигураций ищется наиболее оптимальная. В случае, если данная конфигурация по стоимости лучше текущей, она принимается в качестве текущей и шаг повторяется с начала, иначе алгоритм завершается.&lt;br /&gt;
&lt;br /&gt;
Преимущества данного алгоритма по сравнению с подобными ему:&lt;br /&gt;
&lt;br /&gt;
* В отличие от более популярного алгоритма [https://ru.wikipedia.org/wiki/K-means k-means], PAM позволяет работать с произвольными объектами с заданной мерой рассчета расстояний (различий), не требует хранения всего множества объектов (достаточно знать лишь матрицу дистанций, так как медоиды выбираются из множества объектов), а также более устойчив к выбросам в данных (за счет рассмотрения медоидов вместо центроидов);&lt;br /&gt;
* В отличие от традиционного [https://en.wikipedia.org/wiki/K-medoids k-medoids] на шаге &#039;&#039;BUILD&#039;&#039; начальные медоиды выбираются более оптимальным образом по сравнению со случайным выбором. Это позволяет уменьшить количество итераций шага &#039;&#039;SWAP&#039;&#039;, тем самым сокращая время работы алгоритма.&lt;br /&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;o = \{1,\dots,n\} \subset \mathbb{Z}&amp;lt;/math&amp;gt; –– множество индексов объектов&lt;br /&gt;
* &amp;lt;math&amp;gt;O = \{O_1,\dots,O_n\}&amp;lt;/math&amp;gt; –– множество объектов&lt;br /&gt;
* &amp;lt;math&amp;gt;k \leq n&amp;lt;/math&amp;gt; –– количество кластеров для разбиения&lt;br /&gt;
* &amp;lt;math&amp;gt;d:O\times O\rightarrow \mathbb{R}^{1}&amp;lt;/math&amp;gt; –– метрика расчета расстояний между объектами, &amp;lt;math&amp;gt;d_{ij} = d(O_i,O_j)&amp;lt;/math&amp;gt;&lt;br /&gt;
* &amp;lt;math&amp;gt;D\in \mathbb{R}^{n\times n}&amp;lt;/math&amp;gt; –– симметрическая матрица дистанций объектов, где &amp;lt;math&amp;gt;D=(d_{ij}), i \in o, j \in o&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Выходные данные&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;m = \{m_1,\dots,m_k\}\subset o&amp;lt;/math&amp;gt; –– множество индексов объектов, принятых в качестве медоидов&lt;br /&gt;
&lt;br /&gt;
Принадлежность объекта к определенному кластеру определяется как индекс ближайшего к нему медоида:&lt;br /&gt;
:&amp;lt;math&amp;gt;g_m(i)=\arg \min_{m_j\in m} d_{im_j}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Основная задача состоит в выборе такого множества индексов медоидов &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt;, наилучшим образом минимизирующего функционал:&lt;br /&gt;
:&amp;lt;math&amp;gt;F_o(m) = \sum_{j \in o} d_{jg_m(j)}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Введем обозначения:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;D_j = d_{jg_m(j)}&amp;lt;/math&amp;gt; –– индекс первого по близости к j медоида&lt;br /&gt;
* &amp;lt;math&amp;gt;E_j = d_{jg_{m \backslash g_m(j)}(j)}&amp;lt;/math&amp;gt; –– индекс второго по близости к j медоида&lt;br /&gt;
&lt;br /&gt;
Решение задачи состоит из следующих шагов:&lt;br /&gt;
&lt;br /&gt;
1. Шаг &#039;&#039;BUILD&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
  1.1. Выбор первого медоида&lt;br /&gt;
:&amp;lt;math&amp;gt;m=\{ \arg \min_{i \in o} \sum_{j \in o} d_{ij} \}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  1.2 Последовательный выбор оставшихся медоидов до тех пор, пока &amp;lt;math&amp;gt;\left\vert{m}\right\vert \neq k&amp;lt;/math&amp;gt;&lt;br /&gt;
:&amp;lt;math&amp;gt;m = m \cup \{ \arg \max_{i \in o \backslash m} \sum_{j \in o \backslash \{m \cup i\} } \max (D_j - d_{ji}, 0) \}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2. Шаг &#039;&#039;SWAP&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
  2.1. Производится перебор всех возможных пар медоид-немедоид &amp;lt;math&amp;gt;(i, h): i \in m, h \in o \backslash m&amp;lt;/math&amp;gt;, для каждой из которых рассчитывается стоимость смены текущей конфигурации &amp;lt;math&amp;gt;T_m(i,h)&amp;lt;/math&amp;gt;. При смене конфигурации множество медоидов изменяется &amp;lt;math&amp;gt;m = m \cup \{h\} \backslash \{i\}&amp;lt;/math&amp;gt;.&lt;br /&gt;
:&amp;lt;math&amp;gt;T_m(i, h) = \sum_{j \in o \backslash \{m \cup h\}} C_{jih}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;C_{jih}&amp;lt;/math&amp;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;h&amp;lt;/math&amp;gt;.&lt;br /&gt;
  При этом &amp;lt;math&amp;gt;C_{jih}&amp;lt;/math&amp;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;h&amp;lt;/math&amp;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;h&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;; &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;h&amp;lt;/math&amp;gt;, чем к своему медоиду).&lt;br /&gt;
:&amp;lt;math&amp;gt;C_{jih} = \left\{\begin{matrix}&lt;br /&gt;
0, &amp;amp; d_{ji} &amp;gt;  D_j, d_{jh} &amp;gt;  D_j \\ &lt;br /&gt;
d_{jh} - d_{ji}, &amp;amp; d_{ji} = D_j, d_{jh} &amp;lt; E_j \\ &lt;br /&gt;
E_j - D_j, &amp;amp; d_{ji} = D_j, d_{jh} \geqslant  E_j \\ &lt;br /&gt;
d_{jh} - D_j, &amp;amp; d_{ji} &amp;gt;  D_j, d_{jh} &amp;lt;  D_j &lt;br /&gt;
\end{matrix}\right.&amp;lt;/math&amp;gt;&lt;br /&gt;
  Среди всех пар находится такая пара &amp;lt;math&amp;gt;(i_s, h_s)&amp;lt;/math&amp;gt;, что &amp;lt;math&amp;gt;T_m(i_s, h_s)&amp;lt;/math&amp;gt; минимально.&lt;br /&gt;
&lt;br /&gt;
  2.2 В случае, если &amp;lt;math&amp;gt;T_m(i_s, h_s) \geq 0&amp;lt;/math&amp;gt;, смена конфигурации медоидов не производится и решение задачи окончено. Иначе производится смена конфигурации и повторяется шаг 2.1.&lt;br /&gt;
&lt;br /&gt;
== Вычислительное ядро алгоритма ==&lt;br /&gt;
Вычислительное ядро алгоритма на этапе &#039;&#039;BUILD&#039;&#039; состоит последовательного выбора такого объекта из числа немедоидов, добавление которого к множеству медоидов максимизирует следующую функцию:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;F_{m} = \max_{i \in o \backslash m} \sum_{j \in o \backslash \{m \cup i\} } \max (D_j - d_{ji}, 0)&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;n - i&amp;lt;/math&amp;gt; немедоидов.&lt;br /&gt;
&lt;br /&gt;
Вычислительное ядро алгоритма на этапе &#039;&#039;SWAP&#039;&#039; состоит в расчете функции минимизации стоимости изменения конфигурации &amp;lt;math&amp;gt;T_m(i, h)&amp;lt;/math&amp;gt; при замене медоида &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; на немедоид &amp;lt;math&amp;gt;h&amp;lt;/math&amp;gt; в текущей конфигурации для всех возможных &amp;lt;math&amp;gt;(i, h): i \in m, h \in o \backslash m&amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;T_m(i, h) = \sum_{j \in o \backslash \{m \cup h\}} C_{jih}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Макроструктура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
1. Макрооперация &amp;quot;Выбор следующего медоида&amp;quot;&lt;br /&gt;
:&amp;lt;math&amp;gt;m = m \cup \{ \arg \max_{i \in o \backslash m} \sum_{j \in o \backslash \{m \cup i\} } \max (D_j - d_{ji}, 0) \}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2. Макрооперация &amp;quot;Вычисление стоимости смены текущей конфигурации&amp;quot;&lt;br /&gt;
:&amp;lt;math&amp;gt;T_m(i, h) = \sum_{j \in o \backslash \{m \cup h\}} C_{jih}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Таким образом, алгоритм, в терминах макроопераций выглядит следующим образом:&lt;br /&gt;
&lt;br /&gt;
1. Фаза &#039;&#039;BUILD&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
На данном шаге осуществляется выбор первого медоида. Далее осуществляется макрооперация &amp;quot;Выбор следующего медоида&amp;quot; &amp;lt;math&amp;gt;(k - 1)&amp;lt;/math&amp;gt; раз.&lt;br /&gt;
&lt;br /&gt;
2. Фаза &#039;&#039;SWAP&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
На данном шаге для всех возможных комбинаций медоид-немедоид осуществляется макрооперация &amp;quot;Вычисление стоимости смены текущей конфигурации&amp;quot;. Данная операция производится &amp;lt;math&amp;gt;k(n-k)&amp;lt;/math&amp;gt; раз для каждой итерации. Среди всех комбинаций выбирается та замена, стоимость которой максимальна.&lt;br /&gt;
&lt;br /&gt;
3. &#039;&#039;Критерий останова&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;T_m(i, h) &amp;lt; 0&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;&#039;&#039;: Объем входных данных: &amp;lt;math&amp;gt;n\times n + 2&amp;lt;/math&amp;gt;. Матрица дистанций D (&amp;lt;math&amp;gt;n\times n&amp;lt;/math&amp;gt; вещественных чисел); количество кластеров &amp;lt;math&amp;gt;k \in \mathbb{Z}&amp;lt;/math&amp;gt;; максимальное количество итераций алгоритма &amp;lt;math&amp;gt;maxIter \in \mathbb{Z}&amp;lt;/math&amp;gt;&lt;br /&gt;
 &#039;&#039;&#039;Выход&#039;&#039;&#039;: Объем выходных данных: &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;. Множество индексов медоидов &amp;lt;math&amp;gt;m = \{m_1, \dots, m_k\} \subset \mathbb{Z}&amp;lt;/math&amp;gt;&lt;br /&gt;
 1 # Фаза BUILD&lt;br /&gt;
 2 Выбор стартового объекта в качестве начального медоида: m = {первый медоид}&lt;br /&gt;
 3 Пока |m| != k:&lt;br /&gt;
 4     Выбор следующего медоида i_m&lt;br /&gt;
 5     Добавление найденного медоида в множество: m = m + {i_m}&lt;br /&gt;
 6 # Фаза SWAP&lt;br /&gt;
 7 Пока (maxIter--):&lt;br /&gt;
 8     Лучшая стоимость замены: Tbest = MAXINT&lt;br /&gt;
 9     Кандидаты на замену: (i_s, h_s) = (-1, -1)&lt;br /&gt;
 10     Для всех i из множества медоидов:&lt;br /&gt;
 11         Для всех j из множества немедоидов:&lt;br /&gt;
 12             Расчет стоимости замены Tm(i,h)&lt;br /&gt;
 13             Если Tm(i, h) &amp;lt; Tbest:&lt;br /&gt;
 14                 Tbest = Tm(i, j)&lt;br /&gt;
 15                 Запомнить найденные кандидаты: (i_s, j_s) = (i, h)&lt;br /&gt;
 16     Если Tbest &amp;gt;= 0:&lt;br /&gt;
 17         Выход&lt;br /&gt;
 18     Иначе:&lt;br /&gt;
 19         m = m + {h} - {i}&lt;br /&gt;
&lt;br /&gt;
На фазе &#039;&#039;BUILD&#039;&#039; осуществляется построение начального множества медоидов. Данный процесс производится итерационно до тех пор, пока не будет найдено необходимое число медоидов.&lt;br /&gt;
На фазе &#039;&#039;SWAP&#039;&#039; производится попытка улучшить множество выбранных медоидов. Данный процесс также производится итерационно то тех пор, пока целевая функция не перестанет улучшаться. Также, для данного шага можно ввести ограничение по числу производимых алгоритмом итераций.&lt;br /&gt;
&lt;br /&gt;
== Последовательная сложность алгоритма ==&lt;br /&gt;
&lt;br /&gt;
Для осуществления шага &#039;&#039;BUILD&#039;&#039; потребуется:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;n^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;\sum_{l=1}^{k-1} 2(n-l) \cdot (n-l-1)=\frac{2}{3}(k-1)(k^2 - 3kn + k + 3(n-1)n)&amp;lt;/math&amp;gt; операций сложения и вычитания для всех итераций выбора медоидов;&lt;br /&gt;
* &amp;lt;math&amp;gt;\sum_{l=1}^{k-1} n-l = \frac{1}{2}(k-1)(2n-k)&amp;lt;/math&amp;gt; операций сравнения для всех итераций выбора медоидов.&lt;br /&gt;
&lt;br /&gt;
Таким образом, последовательная сложность шага &#039;&#039;BUILD&#039;&#039; оценивается как &amp;lt;math&amp;gt;O(kn^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для осуществления каждого шага &#039;&#039;SWAP&#039;&#039; потребуется:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;2k(n-k)(n-k-1)&amp;lt;/math&amp;gt; операций вычитания и сложения;&lt;br /&gt;
* &amp;lt;math&amp;gt;k(n-k)&amp;lt;/math&amp;gt; операций сравнения;&lt;br /&gt;
&lt;br /&gt;
Таким образом, последовательная сложность каждого шага &#039;&#039;SWAP&#039;&#039; оценивается как &amp;lt;math&amp;gt;O(kn^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
Заранее число итераций шага &#039;&#039;SWAP&#039;&#039; неизвестно, поэтому принимаем его равным &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Отсюда, общая последовательная сложность алгоритма PAM оценивается как &amp;lt;math&amp;gt;O(Tkn^2)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Информационный граф ==&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф шага BUILD ===&lt;br /&gt;
На рис.1 показана информационная структура шага BUILD алгоритма PAM на некотором шаге.&lt;br /&gt;
&lt;br /&gt;
[[file:Pam_build.png|thumb|center|553px|Рис.1. Информационная структура шага BUILD алгоритма PAM на шаге &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Операции&#039;&#039;:&lt;br /&gt;
* &amp;lt;math&amp;gt;sum_{o,m_t}(i) = \sum_{j \in o \backslash \{m \cup i\}} \max (D_j, d_{ji})&amp;lt;/math&amp;gt; –– оценка конфигурации при внесении &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; в множество индексов медоидов &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; с множеством объектов &amp;lt;math&amp;gt;o&amp;lt;/math&amp;gt;;&lt;br /&gt;
* &amp;lt;math&amp;gt;\arg \max&amp;lt;/math&amp;gt; –– индекс объекта, дающего наибольшую оценку конфигурации при его внесении в множество индексов медоидов &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt;;&lt;br /&gt;
* &amp;lt;math&amp;gt;update(m)&amp;lt;/math&amp;gt; –– операция добавления индекса объекта в множество индексов медоидов &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф шага SWAP ===&lt;br /&gt;
На рис.2 показана информационная структура шага SWAP алгоритма PAM на некотором шаге.&lt;br /&gt;
&lt;br /&gt;
[[file:Pam_swap.png|thumb|center|553px|Рис.2. Информационная структура шага SWAP алгоритма PAM на шаге &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Операции&#039;&#039;:&lt;br /&gt;
* &amp;lt;math&amp;gt;T_m(i, h) = \sum_{j \in o \backslash \{m \cup h\}} C_{jih} &amp;lt;/math&amp;gt; –– стоимость смены конфигурации при замене немедоида &amp;lt;math&amp;gt;h&amp;lt;/math&amp;gt; медоидом &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; в текущей конфигурации &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt;;&lt;br /&gt;
* &amp;lt;math&amp;gt;\arg \min&amp;lt;/math&amp;gt; –– пара &amp;lt;math&amp;gt;(i, h)&amp;lt;/math&amp;gt; (медоид-немедоид), замена которой в текущей конфигурации &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; дает наименьшую стоимость смены;&lt;br /&gt;
* &amp;lt;math&amp;gt;swap&amp;lt;/math&amp;gt; –– операция замены медоида &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; на немедоид &amp;lt;math&amp;gt;h&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;
Для осуществления шага &#039;&#039;BUILD&#039;&#039; потребуется:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; параллельных операций сложения и &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; последовательных операций сравнения для выбора начального медоида;&lt;br /&gt;
* &amp;lt;math&amp;gt;\sum_{l=1}^{k-1} 2(n-l) = (k-1)(2n-k)&amp;lt;/math&amp;gt; параллельных операций сложения и вычитания для всех итераций выбора медоидов;&lt;br /&gt;
* &amp;lt;math&amp;gt;\sum_{l=1}^{k-1} n-l = \frac{1}{2}(k-1)(2n-k)&amp;lt;/math&amp;gt; последовательных операций сравнения для всех итераций выбора медоидов.&lt;br /&gt;
&lt;br /&gt;
Таким образом, параллельная сложность шага &#039;&#039;BUILD&#039;&#039; оценивается как &amp;lt;math&amp;gt;O(kn)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для осуществления каждого шага &#039;&#039;SWAP&#039;&#039; потребуется:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;k(n-k) &amp;lt;/math&amp;gt; параллельных операций вычитания и сложения;&lt;br /&gt;
* &amp;lt;math&amp;gt;k(n-k)&amp;lt;/math&amp;gt; последовательных операций сравнения.&lt;br /&gt;
&lt;br /&gt;
Таким образом, параллельная сложность каждого шага &#039;&#039;SWAP&#039;&#039; оценивается как &amp;lt;math&amp;gt;O(kn)&amp;lt;/math&amp;gt;.&lt;br /&gt;
Заранее число итераций шага &#039;&#039;SWAP&#039;&#039; неизвестно, поэтому принимаем его равным &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;. Отсюда, общая параллельная сложность алгоритма PAM оценивается как &amp;lt;math&amp;gt;O(Tkn)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Входные и выходные данные алгоритма ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Входные данные&#039;&#039;:&lt;br /&gt;
* Массив &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; из &amp;lt;math&amp;gt;\frac{n(n-1)}{2}&amp;lt;/math&amp;gt; вещественных чисел, определяющий набор дистанций между всеми парами объектов (сжатая матрица дистанций). Симметрическая матрица дистанций  &amp;lt;math&amp;gt;D\in \mathbb{R}^{n\times n}&amp;lt;/math&amp;gt;  восстанавливается из этого массива однозначным образом (для восстановления необходимо знать число объектов &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;):&lt;br /&gt;
:&amp;lt;math&amp;gt;D(i, j)=\left\{\begin{matrix}&lt;br /&gt;
V[ni - \frac{i(i + 1)}{2} + (j - i - 1)], &amp;amp; i &amp;lt; j\\ &lt;br /&gt;
V[nj - \frac{j(j + 1)}{2} + (i - j - 1)], &amp;amp; i &amp;gt; j \\&lt;br /&gt;
0, &amp;amp; i = j&lt;br /&gt;
\end{matrix}\right.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Целое число кластеров &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;, на которое необходимо разбить множество объектов.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Выходные данные&#039;&#039;:&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;i&amp;lt;/math&amp;gt; можно восстановить с использованием функции &amp;lt;math&amp;gt;g_m(i)=\arg \min_{m_j\in m} d_{im_j}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Свойства алгоритма ==&lt;br /&gt;
&lt;br /&gt;
1. &#039;&#039;Соотношение последовательной и параллельной сложности&#039;&#039; в случае неограниченных ресурсов является &#039;&#039;линейным&#039;&#039; как отношение квадратичной сложности к линейной.  &lt;br /&gt;
&lt;br /&gt;
2. &#039;&#039;Вычислительная мощность&#039;&#039; последовательного алгоритма, как отношение числа операций к суммарному объему входных и выходных данных: &amp;lt;math&amp;gt; O(Tk) &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
3. Алгоритм является &#039;&#039;устойчивым&#039;&#039;, так как все операции в алгоритме не подвержены накоплению ошибки.&lt;br /&gt;
&lt;br /&gt;
4. Алгоритм является &#039;&#039;не детерминированным&#039;&#039;, так как  не детерминирован выбор набора медоидов, на которых достигается наилучшая конфигурация.&lt;br /&gt;
&lt;br /&gt;
5. Имеет высокую вычислительную сложность (квадратичную) ввиду необходимости полного перебора, поэтому неэффективно работает на больших наборах данных.&lt;br /&gt;
&lt;br /&gt;
6. Позволяет работать с произвольными объектами с заданной мерой рассчета расстояний (различий), не требует хранения всего множества объектов.&lt;br /&gt;
&lt;br /&gt;
7. Устойчив к выбросам данных (за счет рассмотрения медоидов).&lt;br /&gt;
&lt;br /&gt;
= Программная реализация алгоритма =&lt;br /&gt;
&lt;br /&gt;
== Особенности реализации последовательного алгоритма ==&lt;br /&gt;
&lt;br /&gt;
== Локальность данных и вычислений ==&lt;br /&gt;
&lt;br /&gt;
== Возможные способы и особенности параллельной реализации алгоритма ==&lt;br /&gt;
&lt;br /&gt;
== Масштабируемость алгоритма и его реализации ==&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;
* В [https://www.mathworks.com/help/stats/kmedoids.html?requestedDomain=www.mathworks.com#namevaluepairs MATLAB] ([https://ru.wikipedia.org/wiki/Коммерческое_программное_обеспечение Commercial software])&lt;br /&gt;
* [http://elki.dbs.ifi.lmu.de/browser/elki/elki/src/main/java/de/lmu/ifi/dbs/elki/algorithm/clustering/kmeans/KMedoidsPAM.java ELKI] на языке JAVA (Свободное распространение ([https://ru.wikipedia.org/wiki/GNU_Affero_General_Public_License AGPL]))&lt;br /&gt;
* В пакете [https://cran.r-project.org/package=cluster Cluster] на языке R (Свободное распространение ([https://ru.wikipedia.org/wiki/GNU_General_Public_License#GPL_v1 GPL]))&lt;br /&gt;
* В пакете [https://cran.r-project.org/package=ClusterR ClusterR] на языке R (Свободное распространение ([https://ru.wikipedia.org/wiki/Лицензия_MIT MIT]))&lt;br /&gt;
&lt;br /&gt;
2. Параллельные реализации&lt;br /&gt;
&lt;br /&gt;
* В пакете [https://cran.r-project.org/package=sprint SPRINT] на языке R (Свободное распространение ([https://ru.wikipedia.org/wiki/GNU_General_Public_License#GPL_v1 GPL]))&lt;br /&gt;
&lt;br /&gt;
= Литература =&lt;br /&gt;
&lt;br /&gt;
[[en:Description of algorithm properties and structure]]&lt;/div&gt;</summary>
		<author><name>Lineprinter</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:%D0%AE%D0%BB%D0%B8%D1%8F_%D0%96%D0%BE%D1%81%D0%B0%D0%BD/%D0%A0%D0%B0%D0%B7%D0%B1%D0%B8%D0%B5%D0%BD%D0%B8%D0%B5_%D0%B2%D0%B5%D1%80%D1%88%D0%B8%D0%BD_%D0%B3%D1%80%D0%B0%D1%84%D0%B0_%D0%BD%D0%B0_%D1%80%D0%B0%D0%B2%D0%BD%D1%8B%D0%B5_%D0%BF%D0%BE_%D0%BC%D0%BE%D1%89%D0%BD%D0%BE%D1%81%D1%82%D0%B8_%D0%B1%D0%BB%D0%BE%D0%BA%D0%B8_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4%D0%BE%D0%BC_%D1%80%D0%B5%D0%BA%D1%83%D1%80%D1%81%D0%B8%D0%B2%D0%BD%D0%BE%D0%B9_%D0%BA%D0%BE%D0%BE%D1%80%D0%B4%D0%B8%D0%BD%D0%B0%D1%82%D0%BD%D0%BE%D0%B9_%D0%B1%D0%B8%D1%81%D0%B5%D0%BA%D1%86%D0%B8%D0%B8&amp;diff=15607</id>
		<title>Участник:Юлия Жосан/Разбиение вершин графа на равные по мощности блоки методом рекурсивной координатной бисекции</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:%D0%AE%D0%BB%D0%B8%D1%8F_%D0%96%D0%BE%D1%81%D0%B0%D0%BD/%D0%A0%D0%B0%D0%B7%D0%B1%D0%B8%D0%B5%D0%BD%D0%B8%D0%B5_%D0%B2%D0%B5%D1%80%D1%88%D0%B8%D0%BD_%D0%B3%D1%80%D0%B0%D1%84%D0%B0_%D0%BD%D0%B0_%D1%80%D0%B0%D0%B2%D0%BD%D1%8B%D0%B5_%D0%BF%D0%BE_%D0%BC%D0%BE%D1%89%D0%BD%D0%BE%D1%81%D1%82%D0%B8_%D0%B1%D0%BB%D0%BE%D0%BA%D0%B8_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4%D0%BE%D0%BC_%D1%80%D0%B5%D0%BA%D1%83%D1%80%D1%81%D0%B8%D0%B2%D0%BD%D0%BE%D0%B9_%D0%BA%D0%BE%D0%BE%D1%80%D0%B4%D0%B8%D0%BD%D0%B0%D1%82%D0%BD%D0%BE%D0%B9_%D0%B1%D0%B8%D1%81%D0%B5%D0%BA%D1%86%D0%B8%D0%B8&amp;diff=15607"/>
		<updated>2016-11-01T15:43:22Z</updated>

		<summary type="html">&lt;p&gt;Lineprinter: Lineprinter переименовал страницу Юлия Жосан/Разбиение вершин графа на равные по мощности блоки методом рекурсивной координатной бисекции…&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Автор: [[User:Юлия Жосан|Юлия Жосан]]&lt;br /&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;
&lt;br /&gt;
Покоординатная декомпозиция основана на рекурсивном делении пополам сети по координатной оси, вдоль которой область наиболее протяженная, соотношение размеров полученных частей зависит от требуемого количества доменов в каждой из них. Этот метод применяется для обработки сеток больших размеров (объем описания сетки превышает объем оперативной памяти одного процессорного узла).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Основные этапы работы алгоритма:&#039;&#039;&#039;&lt;br /&gt;
#Выбор направления, вдоль которого будут упорядочиваться вершины домена&lt;br /&gt;
#Упорядочивание вершин вдоль выбранного в п.1 направления&lt;br /&gt;
#Вычисление номера m первой вершины второго домена&lt;br /&gt;
#Формирование двух новых доменов, первый из которых включает вершины от первой до m-1 входящего домена, а второй включает вершины с m до последней вершины входящего домена&lt;br /&gt;
#Запуск алгоритма для полученных в п.4 доменов&lt;br /&gt;
&lt;br /&gt;
Выделяют следующие &#039;&#039;&#039;достоинства&#039;&#039;&#039; данного алгоритма:&lt;br /&gt;
*высокая скорость работы&lt;br /&gt;
*низкий уровень дисбаланса распределения вершин между доменами&lt;br /&gt;
*экономичность по памяти&lt;br /&gt;
===Математическое описание алгоритма===&lt;br /&gt;
----&lt;br /&gt;
Пусть на &#039;&#039;&#039;вход&#039;&#039;&#039; алгоритму подаются следующие данные: &lt;br /&gt;
*неориентированный граф &amp;lt;math&amp;gt;G=(V, E)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; - множество вершин, &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt; - множество ребер&lt;br /&gt;
*Требуемое число доменов &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
На &#039;&#039;&#039;выходе&#039;&#039;&#039; алгоритма требуется получить множество доменов &amp;lt;math&amp;gt;\{S_{p}\}&amp;lt;/math&amp;gt;.Это множество &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt; непересекающихся подмножеств &amp;lt;math&amp;gt;\{V_{i}\}&amp;lt;/math&amp;gt;:&lt;br /&gt;
&amp;lt;br /&amp;gt; &amp;lt;math&amp;gt;V=\bigcup\limits_{i=1}^{p} V_{i}&amp;lt;/math&amp;gt;,  &amp;lt;math&amp;gt;V_{i}\cap V_{j}=\emptyset&amp;lt;/math&amp;gt; для &amp;lt;math&amp;gt;i\not=j&amp;lt;/math&amp;gt;&lt;br /&gt;
при котором минимизируются следующие функционалы: &lt;br /&gt;
*суммарный вес вершин в домене: &amp;lt;math&amp;gt;\vert S_{p}\vert=\sum\limits_{i=1}^{p} \vert v_{i} \vert\rightarrow min&amp;lt;/math&amp;gt;&lt;br /&gt;
*суммарный вес разрезанных ребер:&amp;lt;math&amp;gt;\sum\limits_{ij} \vert e_{ij} \vert\rightarrow min , v_{i}\in S_{l}, v_{j}\in S_{r},  S_{l}\cap S_{r}=\emptyset&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;l_{k}=max\left( v_{i}^{k}\right)-min\left(v_{i}^{k}\right) , v_{i}^{k}\in V , k &amp;lt;/math&amp;gt; - координатная ось, и выбирается ее максимальное значение. &lt;br /&gt;
#Сортировка множества вершин &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; по выбранной в п.1 координате по возрастанию номеров коодинат вершин&lt;br /&gt;
#Вычисляется номер вершины, по которой пойдет разбиение множества: &amp;lt;math&amp;gt;m=(iend-istart+1)*\frac{\frac{p+1}{2}}{p}&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;iend, istart&amp;lt;/math&amp;gt; - номер первой вершины в множестве и номер последней вершины в множестве соответственно&lt;br /&gt;
#Множество вершин делится на две части: &amp;lt;math&amp;gt;V_{l}=|v_{l}|, l=istart..m ,V_{r}=|v_{r}|, r=m+1..iend &amp;lt;/math&amp;gt;&lt;br /&gt;
#Алгоритм запускается для &amp;lt;math&amp;gt;V_{l}&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;V_{r}&amp;lt;/math&amp;gt;&lt;br /&gt;
#Алгоритм останавливается на итерации, для которой &amp;lt;math&amp;gt;|V_{l}|=1 , |V_{r}|=1&amp;lt;/math&amp;gt; &lt;br /&gt;
=== Вычислительное ядро алгоритма===&lt;br /&gt;
----&lt;br /&gt;
Вычислительное ядро алгоритма составляют:&lt;br /&gt;
*сортировка массива координат вершин &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; (могут применяться различные виды сортировок) &lt;br /&gt;
*вычисление величины &amp;lt;math&amp;gt;l_{k}&amp;lt;/math&amp;gt; - координатной оси&lt;br /&gt;
*вычисление величины &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; - точки раздела множества&lt;br /&gt;
&lt;br /&gt;
=== Макроструктура алгоритма===&lt;br /&gt;
----&lt;br /&gt;
Основной составляющей метода является повторяющиеся сортировки множества координат вершин &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; на каждом рекурсивном шаге. Всего таких шагов &amp;lt;math&amp;gt;p-1&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма===&lt;br /&gt;
----&lt;br /&gt;
Представим последовательную реализацию метода рекурсивной координатной бисекции с помощью псевдокода:&lt;br /&gt;
&amp;lt;br/&amp;gt;&amp;lt;math&amp;gt;BisectCoordRec\{V, iend, istart , p\}&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;{&lt;br /&gt;
&amp;lt;br/&amp;gt;&#039;&#039;&#039;Шаг 1:&#039;&#039;&#039;   &amp;lt;math&amp;gt;l_{k}=argmax{max\left( v_{i}^{k}\right)-min\left(v_{i}^{k}\right)} &amp;lt;/math&amp;gt; - //определение оси для сортировки&lt;br /&gt;
&amp;lt;br/&amp;gt;&#039;&#039;&#039;Шаг 2:&#039;&#039;&#039;   &amp;lt;math&amp;gt;Sort(V, iend, istart ,l_{k})&amp;lt;/math&amp;gt;- //сортировка вершин по выбранной координате&lt;br /&gt;
&amp;lt;br/&amp;gt;&#039;&#039;&#039;Шаг 3:&#039;&#039;&#039;   &amp;lt;math&amp;gt;m=(iend-istart+1)*\frac{\frac{p+1}{2}}{p}&amp;lt;/math&amp;gt; - //поиск точки для разделения множества вершин&lt;br /&gt;
&amp;lt;br/&amp;gt;&#039;&#039;&#039;Шаг 4:&#039;&#039;&#039;   &amp;lt;math&amp;gt;Bisect\left(V,(iend-istart) , V_{l}, \frac{p+1}{2}, V_{r}, \frac{\frac{(iend-istart)*(p+1)}{2}}{p}\right)&amp;lt;/math&amp;gt;- //разделение массива вершин на два&lt;br /&gt;
&amp;lt;br/&amp;gt;&#039;&#039;&#039;Шаг 5:&#039;&#039;&#039;   &amp;lt;math&amp;gt;if |V_{l}|&amp;gt;1 : BisectCoordRec\{V_{l},\frac{(iend-istart)*\frac{p+1}{2}}{p} , \frac{p+1}{2}, p\}&amp;lt;/math&amp;gt; &lt;br /&gt;
&amp;lt;br/&amp;gt;&#039;&#039;&#039;Шаг 6:&#039;&#039;&#039;   &amp;lt;math&amp;gt;if |V_{r}|&amp;gt;1 : BisectCoordRec\{V_{r}, (iend-istart) - \frac{(iend-istart)*\frac{p+1}{2}}{p} , p - \frac{p+1}{2}, p\}&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;}&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма===&lt;br /&gt;
----&lt;br /&gt;
Пусть в графе &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt; содержится &amp;lt;math&amp;gt;|V|=n&amp;lt;/math&amp;gt; вершин. &lt;br /&gt;
*Для определения координат &amp;lt;math&amp;gt;iend, istart&amp;lt;/math&amp;gt; требуется &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; попарных сравнений&lt;br /&gt;
*Для сортировки массива вершин по координате требуется &amp;lt;math&amp;gt;O(nlog_{2}n)&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;-log_{2}p+1&amp;lt;/math&amp;gt;&lt;br /&gt;
Значит можно определить сложность алгоритма следующим образом:&lt;br /&gt;
&amp;lt;math&amp;gt;O(nlog_{2}nlog_{2}p)&amp;lt;/math&amp;gt;&lt;br /&gt;
=== Информационный граф===&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
[[File: bisec_gr.jpg]]&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма===&lt;br /&gt;
----&lt;br /&gt;
Структура параллельного алгоритма:&lt;br /&gt;
#Подготовительный этап:&lt;br /&gt;
#*определение числа и объема требуемых доменов по процессорам&lt;br /&gt;
#**Пусть имеется граф с n вершинами, он распределяется по k доменам. Тогда глубина рекурсии - &amp;lt;math&amp;gt;log_{2}k&amp;lt;/math&amp;gt;, что равняется числу ярусов.&lt;br /&gt;
#Разделение доменов по процессорам:&lt;br /&gt;
#*вызов рекурсивной покоординатной бисекции по процессорам:&lt;br /&gt;
#**сортировка вершин графа (на i-м ярусе выполняется &amp;lt;math&amp;gt;2^i&amp;lt;/math&amp;gt; операций)&lt;br /&gt;
#**разделение на два подграфа для разделения по доменам&lt;br /&gt;
#Параллельные вызовы алгоритма для каждого процессора&lt;br /&gt;
#*вызов локальной координатной бисекции по доменам&lt;br /&gt;
&lt;br /&gt;
Пусть сложность сортировки равна &amp;lt;math&amp;gt;O(n\log_2{n})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&amp;lt;br/&amp;gt;Тогда сложность алгоритма можно представить в следующем виде:&lt;br /&gt;
&amp;lt;math&amp;gt;O(\sum\limits_{i=0}^{log_2{k}}\frac{n}{2^i}\log_2{\frac{n}{2^i}})=&lt;br /&gt;
O(\sum\limits_{i=0}^{log_2{k}}(n\frac{(log_2{n}-i)}{2^i}))=&lt;br /&gt;
O(n(\sum\limits_{i=0}^{log_2{k}}(\frac{log_2{n}}{2^i})-\sum\limits_{i=0}^{log_2{k}}(\frac{i}{2^i})))=&lt;br /&gt;
O(n\log_2{n})-O(n)=O(n\log_2{n})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Входные и выходные данные алгоритма===&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Вход алгоритма:&#039;&#039;&#039;&lt;br /&gt;
*неориентированный граф &amp;lt;math&amp;gt;G=(V, E)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt; - множество вершин, &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt; - множество ребер&lt;br /&gt;
*Требуемое число доменов &amp;lt;math&amp;gt;p&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;p&amp;lt;/math&amp;gt; доменов &amp;lt;math&amp;gt;\{S_{p}\}&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;
&#039;&#039;&#039;Параллельные&#039;&#039;&#039; реализации: &lt;br /&gt;
*[http://glaros.dtc.umn.edu/gkhome/metis/parmetis/overview ParMETIS]&lt;br /&gt;
*JOSTLE&lt;br /&gt;
*[https://www.labri.fr/perso/pelegrin/scotch/ PT-SCOTCH]&lt;br /&gt;
*ZOLTAN&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Последовательные&#039;&#039;&#039; реализации: &lt;br /&gt;
*[http://glaros.dtc.umn.edu/gkhome/metis/metis/overview METIS]&lt;br /&gt;
*JOSTLE&lt;br /&gt;
*[https://www.labri.fr/perso/pelegrin/scotch/ SCOTCH]&lt;br /&gt;
*CHACO&lt;br /&gt;
*PARTY&lt;br /&gt;
&lt;br /&gt;
== Литература==&lt;br /&gt;
----&lt;br /&gt;
[1] Якобовский М.В. Введение в параллельные методы решения задач: Учебное пособие / Предисл.: В. А. Садовничий. – М.: Издательство Московского университета, 2012. – 328 с., илл. – (Серия «Суперкомпьютерное образование»), ISBN 978-5-211-06382-2 URL: http://lira.imamod.ru/ITTPMOPS/&lt;br /&gt;
&amp;lt;br/&amp;gt;[2]  Головченко Е.Н. Декомпозиция расчетных сеток для решения задач механики сплошных сред на высокопроиводительных вычислительных системах. Дисс ... канд. ф.-м.наук. Институт прикладной математики им. М. В. Келдыша РАН, г. Москва URL: http://keldysh.ru/council/3/D00202403/golovchenko_diss.pdf&lt;/div&gt;</summary>
		<author><name>Lineprinter</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:Fokina/%D0%A0%D0%B5%D0%BA%D1%83%D1%80%D1%81%D0%B8%D0%B2%D0%BD%D0%B0%D1%8F_%D0%BA%D0%BE%D0%BE%D1%80%D0%B4%D0%B8%D0%BD%D0%B0%D1%82%D0%BD%D0%B0%D1%8F_%D0%B1%D0%B8%D1%81%D0%B5%D0%BA%D1%86%D0%B8%D1%8F&amp;diff=15605</id>
		<title>Участник:Fokina/Рекурсивная координатная бисекция</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:Fokina/%D0%A0%D0%B5%D0%BA%D1%83%D1%80%D1%81%D0%B8%D0%B2%D0%BD%D0%B0%D1%8F_%D0%BA%D0%BE%D0%BE%D1%80%D0%B4%D0%B8%D0%BD%D0%B0%D1%82%D0%BD%D0%B0%D1%8F_%D0%B1%D0%B8%D1%81%D0%B5%D0%BA%D1%86%D0%B8%D1%8F&amp;diff=15605"/>
		<updated>2016-11-01T15:42:41Z</updated>

		<summary type="html">&lt;p&gt;Lineprinter: Lineprinter переименовал страницу Рекурсивная координатная бисекция в Участник:Fokina/Рекурсивная координатная бисекция без оставления пе…&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment}}&lt;br /&gt;
{{level-m}}&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Рекурсивная координатная бисекция&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O((k-1)*n*log(n))&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O(log_2 k)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;O(k/2)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;n*m&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;n*m&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
Автор: [[U:Fokina|Фокина Н.Ю.]]&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
Метод рекурсивной координатной бисекции был предложен Бергером (M. Berger) и Бохари (S. Bokhari) в 1987 году&amp;lt;ref&amp;gt;M. Berger and S. Bokhari. &amp;quot;A partitioning strategy for nonuniform problems on multiprocessors.&amp;quot; IEEE Trans. Computers, C-36 (1987) 570-580.&amp;lt;/ref&amp;gt; для решения задачи статической баланисировки загрузки. В основе метода лежит идея равномерного разбиения точек в пространстве с помощью гиперплоскостей.&lt;br /&gt;
&lt;br /&gt;
На каждом этапе обрабатываемая область разбивается на две части, содержащие равное количество элементов. Секущая плоскость перпендикулярна координатной оси и выбирается таким образом, чтобы протяженность разрезаемой области вдоль этой оси была наибольшей.&lt;br /&gt;
&lt;br /&gt;
Наиболее широко метод применяется при декомпозиции сеточных графов. В особенности полезен он при предварительном распределении по нескольким процессорам больших сеток, т.е. таких, описание которых не может быть полностью размещено в оперативной памяти одного процессорного узла. Это обусловлено высокой скоростью работы метода, его простота и ориентированность на распределенную обработку.&lt;br /&gt;
&lt;br /&gt;
Наилучшие результаты метод демонстрирует на сетках, в которых вершины равномерно распределены по области простой формы. В других случаях могут возникать длинные границы с несвязанными поддоменами. Чтобы избежать этого, вместо деления доменов пополам на каждом этапе можно разбивать их таким образом, чтобы их размер был пропорционален итоговому количеству кластеров.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
В начале работы имеется домен -- множество вершин графа &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;, заданных в виде векторов координат &amp;lt;math&amp;gt;(v_1,..,v_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;n/2&amp;lt;/math&amp;gt; или &amp;lt;math&amp;gt;n*k/2&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;
Основное время работы алгоритма приходится на сортировку данных на каждой итерации. В связи с этим целесообразно использовать алгоритмы сортировки, имеющие наименьшую среднюю сложность и показавшие высокую эффективность на практике. В последовательном случае такой сортировкой может являться комбинация из пирамидальной сортировки и сортировки слиянием, имеющая сложность &amp;lt;math&amp;gt;O(n*\log(n))&amp;lt;/math&amp;gt;. Параллельная версия данной сортировки также эффективна и имеет сложность &amp;lt;math&amp;gt;O(\frac{n}{p} \big\lbrack \log(\frac{n}{p})+\frac{\lceil \log (p) \rceil^2}{2} \big\rbrack)&amp;lt;/math&amp;gt;&amp;lt;ref&amp;gt;Якобовский М.В. Параллельные алгоритмы сортировки больших объемов данных // Фундаментальные физико-математические проблемы и моделирование технико-технологических систем: Сб. науч. тр. : Янус-К, 2004, c. 235-249.&amp;lt;/ref&amp;gt;. Еще больший прирост производительности может дать битоническая сортировка, сложность которой оценивается как &amp;lt;math&amp;gt;O(\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;
# Определяются границы наименьшего параллелепипеда, охватывающего все вершины, которые делятся на данном этапе. Вычисляются максимумы по каждой из координат. Определяется координата &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, вдоль которой параллелепипед имеет наибольшую протяженность.&lt;br /&gt;
# Выполняется сортировка вершин по координате &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Создается два новых поддомена. Первая половина вершин из отсортированного домена помещается в первый поддомен, все последующие -- в второй.&lt;br /&gt;
&lt;br /&gt;
Стоит отметить, что разбиение целесообразно производить одновременно по нескольким координатам. Для этого вершины сортируются сначала по одной координате, потом внутри нее по следующей координате в циклическом порядке и т.д., что позволяет обрабатывать ситуации наличия нескольких узлов с одним значением координаты. В результате медиана проводится точно, и в разбиении на равные домены числа вершин в доменах отличаются не больше чем на единицу&amp;lt;ref&amp;gt;Головченко, Е. Н., Якобовский, М. В. (2015). Пакет параллельной декомпозиции больших сеток GridSpiderPar. вычислительные методы и программирование, 16, 507.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
Реализация метода рекурсивной бисекции на языке C в общем виде изображена на следующем рисунке:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void&lt;br /&gt;
recursive_bisect(struct Vertex *graph, int n, int k) {&lt;br /&gt;
    int                             k1, k2;&lt;br /&gt;
    int                             n1, n2;&lt;br /&gt;
    struct Vertex *                 subgraph1;&lt;br /&gt;
    struct Vertex *                 subgraph2;&lt;br /&gt;
&lt;br /&gt;
    k1 = (k + 1) / 2;&lt;br /&gt;
    k2 = k - k1;&lt;br /&gt;
    n1 = n * k1 / k;&lt;br /&gt;
    n2 = n - n1;&lt;br /&gt;
&lt;br /&gt;
    bisect(graph, n, subgraph1, n1, subgraph2, n2);&lt;br /&gt;
    &lt;br /&gt;
    if (k1 &amp;gt; 1) recursive_bisect(subgraph1, n1, k1);&lt;br /&gt;
    if (k2 &amp;gt; 1) recursive_bisect(subgraph2, n2, k2);&lt;br /&gt;
&lt;br /&gt;
    return;&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;br /&gt;
В качестве входных параметров функция принимает массив вершин, содержащихся в текущем домене (&amp;lt;code&amp;gt;graph&amp;lt;/code&amp;gt;), числа &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt; -- количество вершин, &amp;lt;code&amp;gt;k&amp;lt;/code&amp;gt; -- количество поддоменов, на который требуется разбить данный домен. &lt;br /&gt;
&lt;br /&gt;
В теле функции вычисляется отношение между количеством поддоменов, на которые будут впоследствии разбиты полученные на текущей итерации поддомены (&amp;lt;code&amp;gt;k1&amp;lt;/code&amp;gt; и &amp;lt;code&amp;gt;k2&amp;lt;/code&amp;gt;), и размеры поддоменов, которые будут получены на данной итерации (&amp;lt;code&amp;gt;n1&amp;lt;/code&amp;gt; и &amp;lt;code&amp;gt;n2&amp;lt;/code&amp;gt;). С помощью вызова функции &amp;lt;code&amp;gt;bisect&amp;lt;/code&amp;gt; осуществляется непосредственно разбиение на два поддомена. Затем функция &amp;lt;code&amp;gt;recursive_bisect&amp;lt;/code&amp;gt; рекурсивно вызывается на полученных поддоменах до тех пор, пока остается необходимость в дальнейшем их разбиении.&lt;br /&gt;
&lt;br /&gt;
Выбор конкретного алгоритма зависит от использованной функции &amp;lt;code&amp;gt;bisect&amp;lt;/code&amp;gt;, которой осуществляется разбиение вершин на два подграфа на каждом из этапов. В случае координатной бисекции она имеет следующий вид:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void&lt;br /&gt;
coord_bisect(struct Vertex *graph, int n, struct Vertex *subgraph1, int n1, struct Vertex *subgraph2, int n2) {&lt;br /&gt;
    int                             axis;&lt;br /&gt;
&lt;br /&gt;
    /* Calculate direction in which the initial graph is most elongated. */&lt;br /&gt;
    axis = calculate_max_distance(graph, n);&lt;br /&gt;
&lt;br /&gt;
    /* Sort vertices along the selected axis. */&lt;br /&gt;
    sort(graph, n, axis);&lt;br /&gt;
&lt;br /&gt;
    /* Create subgraphs. */&lt;br /&gt;
    subgraph1 = create_subgraph(graph, 0, n1);&lt;br /&gt;
    subgraph2 = create_subgraph(graph, n1, n);&lt;br /&gt;
&lt;br /&gt;
    return;&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;br /&gt;
Входными параметрами функции являются массив вершин в домене, его размер, а также размеры полученных в результате разбиения поддоменов. Выходными параметрами являются массивы вершин, входящих в полученные поддомены. Следует отметить, что на вход достаточно передавать размер лишь одного из поддоменов, например, первого, поскольку размер второго явно из него следует, т.к. &amp;lt;math&amp;gt;n1 + n2 == n&amp;lt;/math&amp;gt;. В данном примере оба размера передаются для наглядности и единообразия.&lt;br /&gt;
&lt;br /&gt;
В процессе работы функции с помощью вызова функции &amp;lt;code&amp;gt;calculate_max_distance&amp;lt;/code&amp;gt; выбирается координатная ось, вдоль которой сетка имеет наибольшую протяженность. Затем вершины сортируются вдоль этой оси, и создаются два поддомена: в первый входят вершины с индексами &amp;lt;code&amp;gt;[0..n1)&amp;lt;/code&amp;gt; в отсортированном массиве, в второй -- &amp;lt;code&amp;gt;[n1..n)&amp;lt;/code&amp;gt;, где &amp;lt;code&amp;gt;n1&amp;lt;/code&amp;gt; равно &amp;lt;math&amp;gt;\frac{n*\frac{(k+1)}{2}}{2}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
В течение каждого этапа выполняются следующие действия:&lt;br /&gt;
# Определение диаметра графа по каждой из координат -- &amp;lt;math&amp;gt;m*O(n)&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; -- количество вершин. Поскольку величина &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt;, как правило, очень мала, ей можно пренебречь, оставив в качестве оценки сложности данного действия &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Поиск максимального расстояния -- осуществляется за &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt;, также пренебрежимо мало.&lt;br /&gt;
# Сортировка. Наиболее эффективным алгоритмом последовательной сортировки является комбинация сортировки слиянием и пирамидальной сортировки &amp;lt;ref&amp;gt;Якобовский М.В. Введение в параллельные методы решения задач: Учебное пособие / Предисл.: В. А. Садовничий. – М.: Издательство Московского университета, 2012. – 328 с., илл. – (Серия «Суперкомпьютерное образование»), ISBN 978-5-211-06382-2&amp;lt;/ref&amp;gt;. Сложность как пирамидальной сортировки, так и сортировки слиянием оценивается как &amp;lt;math&amp;gt;O(nlog_2(n))&amp;lt;/math&amp;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(nlog_2(n))&amp;lt;/math&amp;gt;, а их количество составляет &amp;lt;math&amp;gt;(k - 1)&amp;lt;/math&amp;gt;. Таким образом, последовательная сложность метода -- &amp;lt;math&amp;gt;O((k-1) *n*log(n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
На рисунке 1 приведена общая схема работы метода. Входными данными является граф -- массив вершин, заданных в виде векторов координат. Выходными данными является множество массивов, элементами которых являются исходные вершины.&lt;br /&gt;
[[File:Rcb1.png|320px|thumb|center|Рис. 1. Общая схема рекурсивной координатной бисекции]]&lt;br /&gt;
На рисунке 2 изображен информационный граф метода при распределении домена из 4 вершин, заданных в двумерном пространстве, на 2 поддомена. Входными данными являются вектора &amp;lt;code&amp;gt;A1&amp;lt;/code&amp;gt;-&amp;lt;code&amp;gt;A4&amp;lt;/code&amp;gt;, которые могут содержаться либо в исходном домене, либо в поддомене, полученном на предыдущей итерации. Выходные данные -- непересекающиеся множества векторов &amp;lt;code&amp;gt;B1&amp;lt;/code&amp;gt; и &amp;lt;code&amp;gt;B2&amp;lt;/code&amp;gt;. В них содержатся вектора &amp;lt;code&amp;gt;A1&amp;lt;/code&amp;gt;-&amp;lt;code&amp;gt;A4&amp;lt;/code&amp;gt;. Вершины с меткой &amp;lt;code&amp;gt;CD&amp;lt;/code&amp;gt; обозначают операцию вычисления диаметра домена (т.е. наибольшего расстояния между содержащимися в домене вершинами) по координатам &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;y&amp;lt;/math&amp;gt; соответственно. Вершина &amp;lt;code&amp;gt;MAX&amp;lt;/code&amp;gt; обозначает нахождение максимума, &amp;lt;code&amp;gt;SORT&amp;lt;/code&amp;gt; -- сортировку, &amp;lt;code&amp;gt;SPLIT&amp;lt;/code&amp;gt; -- разбиение домена на два поддомена равного размера.&lt;br /&gt;
[[File:Rcb2.png|799px|thumb|center|Рис. 2. Информационный граф для одной итерации]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
При разбиении графа на &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; подграфов глубина рекурсии составляет &amp;lt;math&amp;gt;\lceil log_2 k \rceil&amp;lt;/math&amp;gt;, а на каждом &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; шаге решается до &amp;lt;math&amp;gt;2^i&amp;lt;/math&amp;gt; подзадач. Поскольку подзадачи, возникающие на каждом шаге рекурсии, не имеют информационных зависимостей друг между другом, их решение можно распараллелить, получив таким образом некоторый прирост производительности. Исходя из того, что сложность этих подзадач примерно одинакова, можно считать, что &lt;br /&gt;
их решение занимает примерно одинаковое время &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; и не зависит от яруса, на котором решается конкретная подзадача. При разбиении графа на &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; подграфов требуется решить &amp;lt;math&amp;gt;k-1&amp;lt;/math&amp;gt; подзадач. Время их последовательного выполнения составляет &amp;lt;math&amp;gt;T_1 = t*(k-1)&amp;lt;/math&amp;gt;. Пусть доступно бесконечное число одинаковых процессоров. При распараллеливании затраченное время сокращается до &amp;lt;math&amp;gt;T_\infty(k) = t * \lceil log_2 k \rceil&amp;lt;/math&amp;gt;. Таким образом, предельное ускорение от распараллеливания рекурсивной бисекции составляет &amp;lt;math&amp;gt;S(k) = \frac{T_1(k)}{T_\infty(k)} = \frac{k-1}{\lceil log_2 k \rceil}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Если количество доступных процессоров ограничено некоторым числом &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt;, то имеет место следующее соотношение:&lt;br /&gt;
&amp;lt;math&amp;gt;S(k, p) = \frac{p * \lfloor log_2 k \rfloor}{2*p - 2 + \lfloor log_2 k \rfloor - \lceil log_2 p \rceil}&amp;lt;/math&amp;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;n&amp;lt;/math&amp;gt;. Как правило, в прикладных задачах &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; составляет 2 или 3, т.е. заданы координаты на плоскости или в пространстве.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Входные данные алгоритма:&#039;&#039;&#039; Исходный граф &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;, натуральное число &amp;lt;math&amp;gt; k \in \N &amp;lt;/math&amp;gt; -- количество подграфов, на которое нужно осуществить разбиение.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объем входных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;n*m&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; -- количество векторов, &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; -- размерность пространства.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные алгоритма:&#039;&#039;&#039; Множество &amp;lt;math&amp;gt;G_1 .. G_k&amp;lt;/math&amp;gt; подграфов исходного графа &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объем выходных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;n*m&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n&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;
Метод рекурсивной координатной бисекции является устойчивым и детерминированным. Его вычислительная мощность пропорциональна &amp;lt;math&amp;gt;log(n)&amp;lt;/math&amp;gt;. Вычислительная сложность определяется, главным образом, использованным алгоритмом сортировки и составляет &amp;lt;math&amp;gt;O((k-1)*n*log(n))&amp;lt;/math&amp;gt; в последовательном и &amp;lt;math&amp;gt;O(\log(k)*\log^2(n))&amp;lt;/math&amp;gt; в параллельном случае.&lt;br /&gt;
&lt;br /&gt;
Можно также отметить следующие особенности метода:&lt;br /&gt;
* Использование параллельного алгоритма рекурсивной координатной бисекции целесообразно при разбиении на большое число подграфов.&lt;br /&gt;
&lt;br /&gt;
* Наибольшее ускорение достигается при разбиении на число подграфов, являющееся степенью двойки.&lt;br /&gt;
&lt;br /&gt;
* Наибольшее ускорение достигается при использовании числа процессоров, являющегося степенью двойки.&lt;br /&gt;
&lt;br /&gt;
* Небольшие накладные расходы, возникающие при обращении процессора к полной информации о разбиении.&lt;br /&gt;
&lt;br /&gt;
* Отсутствие явного контроля над накладными расходами на передачу данных.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
==== Масштабируемость алгоритма ====&lt;br /&gt;
==== Масштабируемость реализации алгоритма ====&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
Наиболее широко в настоящее время применяется параллельный алгоритм рекурсивной координатной бисекции, реализованный в пакете ZOLTAN (Zoltan2) [https://trilinos.org/docs/dev/packages/zoltan2/doc/html/rcbPage.html].&lt;br /&gt;
 &lt;br /&gt;
Метод рекурсивной координатной бисекции сеток реализован в пакете GridSpiderPar [http://lira.imamod.ru/FondProgramm/Decomposition/]. &lt;br /&gt;
Использованный в нем алгоритм отличается от аналогичного алгоритма в пакете ZOLTAN тем, что в нем секущая плоскость (медиана) при необходимости разрезается по нескольким координатам. Это позволяет обрабатывать ситуации наличия на одной плоскости множества узлов с одинаковым значением координаты, что характерно для регулярных сеток. В пакете ZOLTAN вершины из медианы распределяются по областям произвольным образом, что увеличивает число разрезанных ребер.&lt;br /&gt;
&lt;br /&gt;
Помимо приведенных выше инструментов, метод рекурсивной координатной бисекции (как последовательный, так и параллельный) часто реализуется в научных и практических работах авторами самостоятельно, что обусловлено его простотой и достаточно высокой эффективностью, а также возможностью легко адаптировать к требованиям, возникающим при решении конкретной задачи.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;/div&gt;</summary>
		<author><name>Lineprinter</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:Fokina/%D0%A0%D0%B5%D0%BA%D1%83%D1%80%D1%81%D0%B8%D0%B2%D0%BD%D0%B0%D1%8F_%D0%BA%D0%BE%D0%BE%D1%80%D0%B4%D0%B8%D0%BD%D0%B0%D1%82%D0%BD%D0%B0%D1%8F_%D0%B1%D0%B8%D1%81%D0%B5%D0%BA%D1%86%D0%B8%D1%8F&amp;diff=15604</id>
		<title>Участник:Fokina/Рекурсивная координатная бисекция</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:Fokina/%D0%A0%D0%B5%D0%BA%D1%83%D1%80%D1%81%D0%B8%D0%B2%D0%BD%D0%B0%D1%8F_%D0%BA%D0%BE%D0%BE%D1%80%D0%B4%D0%B8%D0%BD%D0%B0%D1%82%D0%BD%D0%B0%D1%8F_%D0%B1%D0%B8%D1%81%D0%B5%D0%BA%D1%86%D0%B8%D1%8F&amp;diff=15604"/>
		<updated>2016-11-01T15:42:18Z</updated>

		<summary type="html">&lt;p&gt;Lineprinter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Assignment}}&lt;br /&gt;
{{level-m}}&lt;br /&gt;
{{algorithm&lt;br /&gt;
| name              = Рекурсивная координатная бисекция&lt;br /&gt;
| serial_complexity = &amp;lt;math&amp;gt;O((k-1)*n*log(n))&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_height         = &amp;lt;math&amp;gt;O(log_2 k)&amp;lt;/math&amp;gt;&lt;br /&gt;
| pf_width          = &amp;lt;math&amp;gt;O(k/2)&amp;lt;/math&amp;gt;&lt;br /&gt;
| input_data        = &amp;lt;math&amp;gt;n*m&amp;lt;/math&amp;gt;&lt;br /&gt;
| output_data       = &amp;lt;math&amp;gt;n*m&amp;lt;/math&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
Автор: [[U:Fokina|Фокина Н.Ю.]]&lt;br /&gt;
== Свойства и структура алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
Метод рекурсивной координатной бисекции был предложен Бергером (M. Berger) и Бохари (S. Bokhari) в 1987 году&amp;lt;ref&amp;gt;M. Berger and S. Bokhari. &amp;quot;A partitioning strategy for nonuniform problems on multiprocessors.&amp;quot; IEEE Trans. Computers, C-36 (1987) 570-580.&amp;lt;/ref&amp;gt; для решения задачи статической баланисировки загрузки. В основе метода лежит идея равномерного разбиения точек в пространстве с помощью гиперплоскостей.&lt;br /&gt;
&lt;br /&gt;
На каждом этапе обрабатываемая область разбивается на две части, содержащие равное количество элементов. Секущая плоскость перпендикулярна координатной оси и выбирается таким образом, чтобы протяженность разрезаемой области вдоль этой оси была наибольшей.&lt;br /&gt;
&lt;br /&gt;
Наиболее широко метод применяется при декомпозиции сеточных графов. В особенности полезен он при предварительном распределении по нескольким процессорам больших сеток, т.е. таких, описание которых не может быть полностью размещено в оперативной памяти одного процессорного узла. Это обусловлено высокой скоростью работы метода, его простота и ориентированность на распределенную обработку.&lt;br /&gt;
&lt;br /&gt;
Наилучшие результаты метод демонстрирует на сетках, в которых вершины равномерно распределены по области простой формы. В других случаях могут возникать длинные границы с несвязанными поддоменами. Чтобы избежать этого, вместо деления доменов пополам на каждом этапе можно разбивать их таким образом, чтобы их размер был пропорционален итоговому количеству кластеров.&lt;br /&gt;
&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
В начале работы имеется домен -- множество вершин графа &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;, заданных в виде векторов координат &amp;lt;math&amp;gt;(v_1,..,v_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;n/2&amp;lt;/math&amp;gt; или &amp;lt;math&amp;gt;n*k/2&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;
Основное время работы алгоритма приходится на сортировку данных на каждой итерации. В связи с этим целесообразно использовать алгоритмы сортировки, имеющие наименьшую среднюю сложность и показавшие высокую эффективность на практике. В последовательном случае такой сортировкой может являться комбинация из пирамидальной сортировки и сортировки слиянием, имеющая сложность &amp;lt;math&amp;gt;O(n*\log(n))&amp;lt;/math&amp;gt;. Параллельная версия данной сортировки также эффективна и имеет сложность &amp;lt;math&amp;gt;O(\frac{n}{p} \big\lbrack \log(\frac{n}{p})+\frac{\lceil \log (p) \rceil^2}{2} \big\rbrack)&amp;lt;/math&amp;gt;&amp;lt;ref&amp;gt;Якобовский М.В. Параллельные алгоритмы сортировки больших объемов данных // Фундаментальные физико-математические проблемы и моделирование технико-технологических систем: Сб. науч. тр. : Янус-К, 2004, c. 235-249.&amp;lt;/ref&amp;gt;. Еще больший прирост производительности может дать битоническая сортировка, сложность которой оценивается как &amp;lt;math&amp;gt;O(\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;
# Определяются границы наименьшего параллелепипеда, охватывающего все вершины, которые делятся на данном этапе. Вычисляются максимумы по каждой из координат. Определяется координата &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;, вдоль которой параллелепипед имеет наибольшую протяженность.&lt;br /&gt;
# Выполняется сортировка вершин по координате &amp;lt;math&amp;gt;j&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Создается два новых поддомена. Первая половина вершин из отсортированного домена помещается в первый поддомен, все последующие -- в второй.&lt;br /&gt;
&lt;br /&gt;
Стоит отметить, что разбиение целесообразно производить одновременно по нескольким координатам. Для этого вершины сортируются сначала по одной координате, потом внутри нее по следующей координате в циклическом порядке и т.д., что позволяет обрабатывать ситуации наличия нескольких узлов с одним значением координаты. В результате медиана проводится точно, и в разбиении на равные домены числа вершин в доменах отличаются не больше чем на единицу&amp;lt;ref&amp;gt;Головченко, Е. Н., Якобовский, М. В. (2015). Пакет параллельной декомпозиции больших сеток GridSpiderPar. вычислительные методы и программирование, 16, 507.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
Реализация метода рекурсивной бисекции на языке C в общем виде изображена на следующем рисунке:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void&lt;br /&gt;
recursive_bisect(struct Vertex *graph, int n, int k) {&lt;br /&gt;
    int                             k1, k2;&lt;br /&gt;
    int                             n1, n2;&lt;br /&gt;
    struct Vertex *                 subgraph1;&lt;br /&gt;
    struct Vertex *                 subgraph2;&lt;br /&gt;
&lt;br /&gt;
    k1 = (k + 1) / 2;&lt;br /&gt;
    k2 = k - k1;&lt;br /&gt;
    n1 = n * k1 / k;&lt;br /&gt;
    n2 = n - n1;&lt;br /&gt;
&lt;br /&gt;
    bisect(graph, n, subgraph1, n1, subgraph2, n2);&lt;br /&gt;
    &lt;br /&gt;
    if (k1 &amp;gt; 1) recursive_bisect(subgraph1, n1, k1);&lt;br /&gt;
    if (k2 &amp;gt; 1) recursive_bisect(subgraph2, n2, k2);&lt;br /&gt;
&lt;br /&gt;
    return;&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;br /&gt;
В качестве входных параметров функция принимает массив вершин, содержащихся в текущем домене (&amp;lt;code&amp;gt;graph&amp;lt;/code&amp;gt;), числа &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt; -- количество вершин, &amp;lt;code&amp;gt;k&amp;lt;/code&amp;gt; -- количество поддоменов, на который требуется разбить данный домен. &lt;br /&gt;
&lt;br /&gt;
В теле функции вычисляется отношение между количеством поддоменов, на которые будут впоследствии разбиты полученные на текущей итерации поддомены (&amp;lt;code&amp;gt;k1&amp;lt;/code&amp;gt; и &amp;lt;code&amp;gt;k2&amp;lt;/code&amp;gt;), и размеры поддоменов, которые будут получены на данной итерации (&amp;lt;code&amp;gt;n1&amp;lt;/code&amp;gt; и &amp;lt;code&amp;gt;n2&amp;lt;/code&amp;gt;). С помощью вызова функции &amp;lt;code&amp;gt;bisect&amp;lt;/code&amp;gt; осуществляется непосредственно разбиение на два поддомена. Затем функция &amp;lt;code&amp;gt;recursive_bisect&amp;lt;/code&amp;gt; рекурсивно вызывается на полученных поддоменах до тех пор, пока остается необходимость в дальнейшем их разбиении.&lt;br /&gt;
&lt;br /&gt;
Выбор конкретного алгоритма зависит от использованной функции &amp;lt;code&amp;gt;bisect&amp;lt;/code&amp;gt;, которой осуществляется разбиение вершин на два подграфа на каждом из этапов. В случае координатной бисекции она имеет следующий вид:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void&lt;br /&gt;
coord_bisect(struct Vertex *graph, int n, struct Vertex *subgraph1, int n1, struct Vertex *subgraph2, int n2) {&lt;br /&gt;
    int                             axis;&lt;br /&gt;
&lt;br /&gt;
    /* Calculate direction in which the initial graph is most elongated. */&lt;br /&gt;
    axis = calculate_max_distance(graph, n);&lt;br /&gt;
&lt;br /&gt;
    /* Sort vertices along the selected axis. */&lt;br /&gt;
    sort(graph, n, axis);&lt;br /&gt;
&lt;br /&gt;
    /* Create subgraphs. */&lt;br /&gt;
    subgraph1 = create_subgraph(graph, 0, n1);&lt;br /&gt;
    subgraph2 = create_subgraph(graph, n1, n);&lt;br /&gt;
&lt;br /&gt;
    return;&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;br /&gt;
Входными параметрами функции являются массив вершин в домене, его размер, а также размеры полученных в результате разбиения поддоменов. Выходными параметрами являются массивы вершин, входящих в полученные поддомены. Следует отметить, что на вход достаточно передавать размер лишь одного из поддоменов, например, первого, поскольку размер второго явно из него следует, т.к. &amp;lt;math&amp;gt;n1 + n2 == n&amp;lt;/math&amp;gt;. В данном примере оба размера передаются для наглядности и единообразия.&lt;br /&gt;
&lt;br /&gt;
В процессе работы функции с помощью вызова функции &amp;lt;code&amp;gt;calculate_max_distance&amp;lt;/code&amp;gt; выбирается координатная ось, вдоль которой сетка имеет наибольшую протяженность. Затем вершины сортируются вдоль этой оси, и создаются два поддомена: в первый входят вершины с индексами &amp;lt;code&amp;gt;[0..n1)&amp;lt;/code&amp;gt; в отсортированном массиве, в второй -- &amp;lt;code&amp;gt;[n1..n)&amp;lt;/code&amp;gt;, где &amp;lt;code&amp;gt;n1&amp;lt;/code&amp;gt; равно &amp;lt;math&amp;gt;\frac{n*\frac{(k+1)}{2}}{2}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
В течение каждого этапа выполняются следующие действия:&lt;br /&gt;
# Определение диаметра графа по каждой из координат -- &amp;lt;math&amp;gt;m*O(n)&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; -- количество вершин. Поскольку величина &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt;, как правило, очень мала, ей можно пренебречь, оставив в качестве оценки сложности данного действия &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Поиск максимального расстояния -- осуществляется за &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt;, также пренебрежимо мало.&lt;br /&gt;
# Сортировка. Наиболее эффективным алгоритмом последовательной сортировки является комбинация сортировки слиянием и пирамидальной сортировки &amp;lt;ref&amp;gt;Якобовский М.В. Введение в параллельные методы решения задач: Учебное пособие / Предисл.: В. А. Садовничий. – М.: Издательство Московского университета, 2012. – 328 с., илл. – (Серия «Суперкомпьютерное образование»), ISBN 978-5-211-06382-2&amp;lt;/ref&amp;gt;. Сложность как пирамидальной сортировки, так и сортировки слиянием оценивается как &amp;lt;math&amp;gt;O(nlog_2(n))&amp;lt;/math&amp;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(nlog_2(n))&amp;lt;/math&amp;gt;, а их количество составляет &amp;lt;math&amp;gt;(k - 1)&amp;lt;/math&amp;gt;. Таким образом, последовательная сложность метода -- &amp;lt;math&amp;gt;O((k-1) *n*log(n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
На рисунке 1 приведена общая схема работы метода. Входными данными является граф -- массив вершин, заданных в виде векторов координат. Выходными данными является множество массивов, элементами которых являются исходные вершины.&lt;br /&gt;
[[File:Rcb1.png|320px|thumb|center|Рис. 1. Общая схема рекурсивной координатной бисекции]]&lt;br /&gt;
На рисунке 2 изображен информационный граф метода при распределении домена из 4 вершин, заданных в двумерном пространстве, на 2 поддомена. Входными данными являются вектора &amp;lt;code&amp;gt;A1&amp;lt;/code&amp;gt;-&amp;lt;code&amp;gt;A4&amp;lt;/code&amp;gt;, которые могут содержаться либо в исходном домене, либо в поддомене, полученном на предыдущей итерации. Выходные данные -- непересекающиеся множества векторов &amp;lt;code&amp;gt;B1&amp;lt;/code&amp;gt; и &amp;lt;code&amp;gt;B2&amp;lt;/code&amp;gt;. В них содержатся вектора &amp;lt;code&amp;gt;A1&amp;lt;/code&amp;gt;-&amp;lt;code&amp;gt;A4&amp;lt;/code&amp;gt;. Вершины с меткой &amp;lt;code&amp;gt;CD&amp;lt;/code&amp;gt; обозначают операцию вычисления диаметра домена (т.е. наибольшего расстояния между содержащимися в домене вершинами) по координатам &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;y&amp;lt;/math&amp;gt; соответственно. Вершина &amp;lt;code&amp;gt;MAX&amp;lt;/code&amp;gt; обозначает нахождение максимума, &amp;lt;code&amp;gt;SORT&amp;lt;/code&amp;gt; -- сортировку, &amp;lt;code&amp;gt;SPLIT&amp;lt;/code&amp;gt; -- разбиение домена на два поддомена равного размера.&lt;br /&gt;
[[File:Rcb2.png|799px|thumb|center|Рис. 2. Информационный граф для одной итерации]]&lt;br /&gt;
&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
При разбиении графа на &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; подграфов глубина рекурсии составляет &amp;lt;math&amp;gt;\lceil log_2 k \rceil&amp;lt;/math&amp;gt;, а на каждом &amp;lt;math&amp;gt;i&amp;lt;/math&amp;gt; шаге решается до &amp;lt;math&amp;gt;2^i&amp;lt;/math&amp;gt; подзадач. Поскольку подзадачи, возникающие на каждом шаге рекурсии, не имеют информационных зависимостей друг между другом, их решение можно распараллелить, получив таким образом некоторый прирост производительности. Исходя из того, что сложность этих подзадач примерно одинакова, можно считать, что &lt;br /&gt;
их решение занимает примерно одинаковое время &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt; и не зависит от яруса, на котором решается конкретная подзадача. При разбиении графа на &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; подграфов требуется решить &amp;lt;math&amp;gt;k-1&amp;lt;/math&amp;gt; подзадач. Время их последовательного выполнения составляет &amp;lt;math&amp;gt;T_1 = t*(k-1)&amp;lt;/math&amp;gt;. Пусть доступно бесконечное число одинаковых процессоров. При распараллеливании затраченное время сокращается до &amp;lt;math&amp;gt;T_\infty(k) = t * \lceil log_2 k \rceil&amp;lt;/math&amp;gt;. Таким образом, предельное ускорение от распараллеливания рекурсивной бисекции составляет &amp;lt;math&amp;gt;S(k) = \frac{T_1(k)}{T_\infty(k)} = \frac{k-1}{\lceil log_2 k \rceil}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Если количество доступных процессоров ограничено некоторым числом &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt;, то имеет место следующее соотношение:&lt;br /&gt;
&amp;lt;math&amp;gt;S(k, p) = \frac{p * \lfloor log_2 k \rfloor}{2*p - 2 + \lfloor log_2 k \rfloor - \lceil log_2 p \rceil}&amp;lt;/math&amp;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;n&amp;lt;/math&amp;gt;. Как правило, в прикладных задачах &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; составляет 2 или 3, т.е. заданы координаты на плоскости или в пространстве.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Входные данные алгоритма:&#039;&#039;&#039; Исходный граф &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;, натуральное число &amp;lt;math&amp;gt; k \in \N &amp;lt;/math&amp;gt; -- количество подграфов, на которое нужно осуществить разбиение.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объем входных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;n*m&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; -- количество векторов, &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; -- размерность пространства.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Выходные данные алгоритма:&#039;&#039;&#039; Множество &amp;lt;math&amp;gt;G_1 .. G_k&amp;lt;/math&amp;gt; подграфов исходного графа &amp;lt;math&amp;gt;G&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Объем выходных данных:&#039;&#039;&#039; &amp;lt;math&amp;gt;n*m&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;n&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;
Метод рекурсивной координатной бисекции является устойчивым и детерминированным. Его вычислительная мощность пропорциональна &amp;lt;math&amp;gt;log(n)&amp;lt;/math&amp;gt;. Вычислительная сложность определяется, главным образом, использованным алгоритмом сортировки и составляет &amp;lt;math&amp;gt;O((k-1)*n*log(n))&amp;lt;/math&amp;gt; в последовательном и &amp;lt;math&amp;gt;O(\log(k)*\log^2(n))&amp;lt;/math&amp;gt; в параллельном случае.&lt;br /&gt;
&lt;br /&gt;
Можно также отметить следующие особенности метода:&lt;br /&gt;
* Использование параллельного алгоритма рекурсивной координатной бисекции целесообразно при разбиении на большое число подграфов.&lt;br /&gt;
&lt;br /&gt;
* Наибольшее ускорение достигается при разбиении на число подграфов, являющееся степенью двойки.&lt;br /&gt;
&lt;br /&gt;
* Наибольшее ускорение достигается при использовании числа процессоров, являющегося степенью двойки.&lt;br /&gt;
&lt;br /&gt;
* Небольшие накладные расходы, возникающие при обращении процессора к полной информации о разбиении.&lt;br /&gt;
&lt;br /&gt;
* Отсутствие явного контроля над накладными расходами на передачу данных.&lt;br /&gt;
&lt;br /&gt;
== Программная реализация алгоритма ==&lt;br /&gt;
&lt;br /&gt;
=== Особенности реализации последовательного алгоритма ===&lt;br /&gt;
=== Локальность данных и вычислений ===&lt;br /&gt;
=== Возможные способы и особенности параллельной реализации алгоритма ===&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
==== Масштабируемость алгоритма ====&lt;br /&gt;
==== Масштабируемость реализации алгоритма ====&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
Наиболее широко в настоящее время применяется параллельный алгоритм рекурсивной координатной бисекции, реализованный в пакете ZOLTAN (Zoltan2) [https://trilinos.org/docs/dev/packages/zoltan2/doc/html/rcbPage.html].&lt;br /&gt;
 &lt;br /&gt;
Метод рекурсивной координатной бисекции сеток реализован в пакете GridSpiderPar [http://lira.imamod.ru/FondProgramm/Decomposition/]. &lt;br /&gt;
Использованный в нем алгоритм отличается от аналогичного алгоритма в пакете ZOLTAN тем, что в нем секущая плоскость (медиана) при необходимости разрезается по нескольким координатам. Это позволяет обрабатывать ситуации наличия на одной плоскости множества узлов с одинаковым значением координаты, что характерно для регулярных сеток. В пакете ZOLTAN вершины из медианы распределяются по областям произвольным образом, что увеличивает число разрезанных ребер.&lt;br /&gt;
&lt;br /&gt;
Помимо приведенных выше инструментов, метод рекурсивной координатной бисекции (как последовательный, так и параллельный) часто реализуется в научных и практических работах авторами самостоятельно, что обусловлено его простотой и достаточно высокой эффективностью, а также возможностью легко адаптировать к требованиям, возникающим при решении конкретной задачи.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;/div&gt;</summary>
		<author><name>Lineprinter</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%D0%B2%D0%B8%D0%BA%D0%B8:%D0%9E%D0%B1%D1%89%D0%B8%D0%B9_%D1%84%D0%BE%D1%80%D1%83%D0%BC&amp;diff=15598</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%D0%B2%D0%B8%D0%BA%D0%B8:%D0%9E%D0%B1%D1%89%D0%B8%D0%B9_%D1%84%D0%BE%D1%80%D1%83%D0%BC&amp;diff=15598"/>
		<updated>2016-11-01T15:24:14Z</updated>

		<summary type="html">&lt;p&gt;Lineprinter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{project internals nav}}&lt;br /&gt;
== Обустраивание проекта ==&lt;br /&gt;
Админы, скопируйте пожалуйста [https://ru.wikipedia.org/wiki/MediaWiki:Edittools] в [[MediaWiki:Edittools]] - удобнее работать с текстом будет. [[Участник:AntonChupin|AntonChupin]] ([[Обсуждение участника:AntonChupin|обсуждение]]) 14:03, 8 июля 2016 (MSK)&lt;br /&gt;
: Сделано. — [[S:Contributions/Lineprinter|lineprinter]] 11:13, 1 ноября 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
== Список описаний алгоритмов ==&lt;br /&gt;
&lt;br /&gt;
{{:Список описаний алгоритмов}}&lt;br /&gt;
&lt;br /&gt;
== Словарик ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Русский&lt;br /&gt;
!English&lt;br /&gt;
|-&lt;br /&gt;
|алгоритм&lt;br /&gt;
|algorithm&lt;br /&gt;
|-&lt;br /&gt;
|вершина (графа)&lt;br /&gt;
|vertex&lt;br /&gt;
|-&lt;br /&gt;
|входные/выходные данные&lt;br /&gt;
|input/output data&lt;br /&gt;
|-&lt;br /&gt;
|вычислительная мощность&lt;br /&gt;
|computational power&lt;br /&gt;
|-&lt;br /&gt;
|вычислительное ядро (алгоритма)&lt;br /&gt;
|computational kernel&lt;br /&gt;
|-&lt;br /&gt;
|вычислительное ядро (процессора)&lt;br /&gt;
|computational core&lt;br /&gt;
|-&lt;br /&gt;
|вычислительный узел&lt;br /&gt;
|computational node&lt;br /&gt;
|-&lt;br /&gt;
|граф алгоритма&lt;br /&gt;
|algorithm graph&lt;br /&gt;
|-&lt;br /&gt;
|дуга (графа)&lt;br /&gt;
|edge&lt;br /&gt;
|-&lt;br /&gt;
|избыточные вычисления &lt;br /&gt;
|redundant computation&lt;br /&gt;
|-&lt;br /&gt;
|информационный граф&lt;br /&gt;
|information graph&lt;br /&gt;
|-&lt;br /&gt;
|каноническая ярусно-параллельная форма&lt;br /&gt;
|canonical parallel form&lt;br /&gt;
|-&lt;br /&gt;
|кэш-строка (строка кэш-памяти)&lt;br /&gt;
|cache line&lt;br /&gt;
|-&lt;br /&gt;
|локальность&lt;br /&gt;
|locality&lt;br /&gt;
|-&lt;br /&gt;
|макроструктура&lt;br /&gt;
|macro structure&lt;br /&gt;
|-&lt;br /&gt;
|масштабируемость&lt;br /&gt;
|scalability&lt;br /&gt;
|-&lt;br /&gt;
|метод сдваивания&lt;br /&gt;
|pairwise summation&lt;br /&gt;
|-&lt;br /&gt;
|накладные расходы&lt;br /&gt;
|overhead&lt;br /&gt;
|-&lt;br /&gt;
|параллельная сложность&lt;br /&gt;
|parallel complexity&lt;br /&gt;
|-&lt;br /&gt;
|последовательный (алгоритм, программа)&lt;br /&gt;
|serial (sequential)&lt;br /&gt;
|-&lt;br /&gt;
|последовательная сложность&lt;br /&gt;
|serial complexity&lt;br /&gt;
|-&lt;br /&gt;
|производительность&lt;br /&gt;
|performance&lt;br /&gt;
|-&lt;br /&gt;
|реализация&lt;br /&gt;
|implementation&lt;br /&gt;
|-&lt;br /&gt;
|ресурс параллелизма&lt;br /&gt;
|parallelism resource&lt;br /&gt;
|-&lt;br /&gt;
|сопроцессор&lt;br /&gt;
|co-processor&lt;br /&gt;
|-&lt;br /&gt;
|ускорение&lt;br /&gt;
|speedup&lt;br /&gt;
|-&lt;br /&gt;
|ускоритель&lt;br /&gt;
|accelerator&lt;br /&gt;
|-&lt;br /&gt;
|функциональное устройство&lt;br /&gt;
|functional unit&lt;br /&gt;
|-&lt;br /&gt;
|цикл (программы)&lt;br /&gt;
|loop&lt;br /&gt;
|-&lt;br /&gt;
|эффективность&lt;br /&gt;
|efficiency&lt;br /&gt;
|-&lt;br /&gt;
|ярус&lt;br /&gt;
|layer&lt;br /&gt;
|-&lt;br /&gt;
|ярусно-параллельная форма&lt;br /&gt;
|parallel form&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Lineprinter</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9D%D0%B0_%D0%BF%D0%B5%D1%80%D0%B5%D0%B2%D0%BE%D0%B4&amp;diff=15597</id>
		<title>На перевод</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9D%D0%B0_%D0%BF%D0%B5%D1%80%D0%B5%D0%B2%D0%BE%D0%B4&amp;diff=15597"/>
		<updated>2016-11-01T15:23:50Z</updated>

		<summary type="html">&lt;p&gt;Lineprinter: Откат правок AntonChupin (обсуждение) к версии ASA&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Приложение 1]]&lt;br /&gt;
[[Приложение 2]]&lt;br /&gt;
[[Приложение 3]]&lt;br /&gt;
[[Приложение 4]]&lt;br /&gt;
[[Приложение 5]]&lt;br /&gt;
[[Приложение 6]]&lt;br /&gt;
[[Приложение 7]]&lt;br /&gt;
[[Приложение 8]]&lt;br /&gt;
[[Приложение 9]]&lt;br /&gt;
[[Приложение 10]]&lt;/div&gt;</summary>
		<author><name>Lineprinter</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=Teplov_sandbox&amp;diff=15596</id>
		<title>Teplov sandbox</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=Teplov_sandbox&amp;diff=15596"/>
		<updated>2016-11-01T15:23:29Z</updated>

		<summary type="html">&lt;p&gt;Lineprinter: Откат правок AntonChupin (обсуждение) к версии Teplov&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{| class=&amp;quot;simple&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
 ||Оценка по числу процессоров&lt;br /&gt;
 |Программа&lt;br /&gt;
 |-&lt;br /&gt;
 ||-0,061256&lt;br /&gt;
 |Linpack Benchmark&lt;br /&gt;
 |-&lt;br /&gt;
 ||-0,0436&lt;br /&gt;
 |Перемножение матриц&lt;br /&gt;
 |-&lt;br /&gt;
 ||-0,01517&lt;br /&gt;
 |Умножение матрицы на вектор&lt;br /&gt;
 |-&lt;br /&gt;
 ||-0,00059&lt;br /&gt;
 |Метод Холецкого&lt;br /&gt;
 |-&lt;br /&gt;
 ||-0,0001&lt;br /&gt;
 |Метод Гаусса Прямой ход&lt;br /&gt;
 |-&lt;br /&gt;
 ||-3,06E-06&lt;br /&gt;
 |Схема сдваивания&lt;br /&gt;
 |-&lt;br /&gt;
 ||-9,43E-07&lt;br /&gt;
 |Суммирование элементов вектора&lt;br /&gt;
 |-&lt;br /&gt;
 ||0,00414&lt;br /&gt;
 |Скалярное произведение векторов&lt;br /&gt;
 |-&lt;br /&gt;
 |}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;simple&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
 ||Оценка по размеру задачи&lt;br /&gt;
 |Программа&lt;br /&gt;
 |-&lt;br /&gt;
 ||-0,0255&lt;br /&gt;
 |Перемножение матриц&lt;br /&gt;
 |-&lt;br /&gt;
 ||-0,01385&lt;br /&gt;
 |Скалярное произведение векторов&lt;br /&gt;
 |-&lt;br /&gt;
 ||-0,00674&lt;br /&gt;
 |Метод Гаусса прямой ход&lt;br /&gt;
 |-&lt;br /&gt;
 ||-0,0014&lt;br /&gt;
 |Умножение матрицы на вектор&lt;br /&gt;
 |-&lt;br /&gt;
 ||6,43E-09&lt;br /&gt;
 |Схема сдваивания&lt;br /&gt;
 |-&lt;br /&gt;
 ||1,88E-06&lt;br /&gt;
 |Суммирование элементов вектора&lt;br /&gt;
 |-&lt;br /&gt;
 ||0,010134&lt;br /&gt;
 |Linpack Benchmark&lt;br /&gt;
 |-&lt;br /&gt;
 ||0,06017&lt;br /&gt;
 |Метод Холецкого&lt;br /&gt;
 |-&lt;br /&gt;
 |}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;simple&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
 ||Общая оценка &lt;br /&gt;
 |Программа&lt;br /&gt;
 |-&lt;br /&gt;
 ||-0,00097&lt;br /&gt;
 |Перемножение матриц&lt;br /&gt;
 |-&lt;br /&gt;
 ||-0,00017&lt;br /&gt;
 |Скалярное произведение векторов&lt;br /&gt;
 |-&lt;br /&gt;
 ||-0,00015&lt;br /&gt;
 |Умножение матрицы на вектор&lt;br /&gt;
 |-&lt;br /&gt;
 ||-6,62E-05&lt;br /&gt;
 |Метод Гаусса прямой ход&lt;br /&gt;
 |-&lt;br /&gt;
 ||-1,42E-07&lt;br /&gt;
 |Суммирование элементов вектора&lt;br /&gt;
 |-&lt;br /&gt;
 ||-8,05E-08&lt;br /&gt;
 |Схема сдваивания&lt;br /&gt;
 |-&lt;br /&gt;
 ||2,84E-05&lt;br /&gt;
 |Linpack Benchmark&lt;br /&gt;
 |-&lt;br /&gt;
 ||0,000403&lt;br /&gt;
 |Метод Холецкого&lt;br /&gt;
 |-&lt;br /&gt;
 |}&lt;/div&gt;</summary>
		<author><name>Lineprinter</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=Sandbox&amp;diff=15595</id>
		<title>Sandbox</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=Sandbox&amp;diff=15595"/>
		<updated>2016-11-01T15:23:22Z</updated>

		<summary type="html">&lt;p&gt;Lineprinter: Откат правок AntonChupin (обсуждение) к версии Algoman&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&amp;lt;math&amp;gt; \sum \left ( \mathbf{{\widetilde{\gamma \prod \alpha ^{test} }}&#039;} \right )&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\frac{1}{\displaystyle 1+&lt;br /&gt;
\frac{1}{\displaystyle 2+&lt;br /&gt;
\frac{1}{\displaystyle 3+x}}} +&lt;br /&gt;
\frac{1}{1+\frac{1}{2+\frac{1}{3+x}}}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;fortran&amp;quot;&amp;gt;&lt;br /&gt;
 C     FORTRAN IV WAS ONE OF THE FIRST PROGRAMMING&lt;br /&gt;
 C     LANGUAGES TO SUPPORT SOURCE COMMENTS&lt;br /&gt;
       WRITE (6,7)&lt;br /&gt;
     7 FORMAT(13H HELLO, WORLD)&lt;br /&gt;
       STOP&lt;br /&gt;
       END&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;&lt;br /&gt;
int main(int argc, char** argv)&lt;br /&gt;
{&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Файл:Example.jpg]]&lt;/div&gt;</summary>
		<author><name>Lineprinter</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=Algorithm_classification&amp;diff=15594</id>
		<title>Algorithm classification</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=Algorithm_classification&amp;diff=15594"/>
		<updated>2016-11-01T15:23:10Z</updated>

		<summary type="html">&lt;p&gt;Lineprinter: Откат правок AntonChupin (обсуждение) к версии ASA&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;# &amp;lt;div id=&amp;quot;Vector operations&amp;quot;&amp;gt;&#039;&#039;&#039;Vector operations&#039;&#039;&#039;&amp;lt;/div&amp;gt;&lt;br /&gt;
## &#039;&#039;Pairwise summation&#039;&#039;&lt;br /&gt;
### {{level|Summing an array by pairwise summation}}&lt;br /&gt;
### {{level|Finding partial sums of an array by pairwise summation}}&lt;br /&gt;
## {{level|Uniform norm of a vector: Real version, serial-parallel variant}}&lt;br /&gt;
## {{level|Dot product: Real version, serial-parallel variant}}&lt;br /&gt;
## {{level|Serial-parallel summation method}}&lt;br /&gt;
# &amp;lt;div id=&amp;quot;Matrix-vector operations&amp;quot;&amp;gt;&#039;&#039;&#039;Matrix-vector operations&#039;&#039;&#039;&amp;lt;/div&amp;gt;&lt;br /&gt;
## {{level|Dense matrix-vector multiplication}}&lt;br /&gt;
# &amp;lt;div id=&amp;quot;Matrix operations&amp;quot;&amp;gt;&#039;&#039;&#039;Matrix operations&#039;&#039;&#039;&amp;lt;/div&amp;gt;&lt;br /&gt;
## {{level|Dense matrix multiplication}}&lt;br /&gt;
# &amp;lt;div id=&amp;quot;Matrix decompositions&amp;quot;&amp;gt;&#039;&#039;&#039;Matrix decompositions&#039;&#039;&#039;&amp;lt;/div&amp;gt;&lt;br /&gt;
## &#039;&#039;Triangular decompositions&#039;&#039;&lt;br /&gt;
### Gaussian elimination (finding the LU decomposition)&lt;br /&gt;
#### {{level|Gaussian elimination without pivoting}}&lt;br /&gt;
##### LU decomposition via Gaussian elimination &lt;br /&gt;
##### Compact scheme for Gaussian elimination and its modifications &lt;br /&gt;
###### Compact scheme for Gaussian elimination: Dense matrix &lt;br /&gt;
###### {{level|Compact scheme for Gaussian elimination and its modifications: Tridiagonal matrix}}&lt;br /&gt;
####### {{level|Compact scheme for Gaussian elimination: Tridiagonal matrix, serial variant}}&lt;br /&gt;
####### {{level|Stone doubling algorithm for the LU decomposition of a tridiagonal matrix}}&lt;br /&gt;
####### {{level|Serial-parallel algorithm for the LU decomposition of a tridiagonal matrix}}&lt;br /&gt;
#### Gaussian elimination with pivoting &lt;br /&gt;
##### Gaussian elimination with column pivoting &lt;br /&gt;
##### Gaussian elimination with row pivoting &lt;br /&gt;
##### Gaussian elimination with diagonal pivoting &lt;br /&gt;
##### Gaussian elimination with complete pivoting &lt;br /&gt;
### {{level|Cholesky method (finding the symmetric triangular decomposition)}}&lt;br /&gt;
#### {{level|Cholesky decomposition (square root method)}}: Basic pointwise real variant, dense symmetric positive definite matrix &lt;br /&gt;
### {{level|Available triangular decompositions for matrices of special form}}&lt;br /&gt;
## &#039;&#039;Unitary-triangular decompositions&#039;&#039;&lt;br /&gt;
### {{level|Givens method (rotation method) for the QR-decomposition of a matrix}}&lt;br /&gt;
### {{level|Householder method (reflection method) for the QR-decomposition of a matrix}}&lt;br /&gt;
## &#039;&#039;Unitary Hessenberg decompositions&#039;&#039;&lt;br /&gt;
### {{level|Householder method (reflection method) for reducing a matrix to Hessenberg (bidiagonal) form}}&lt;br /&gt;
### {{level|Givens method (rotation method) for reducing a matrix to Hessenberg (bidiagonal) form}}&lt;br /&gt;
## &#039;&#039;Unitary-diagonal decompositions&#039;&#039;&lt;br /&gt;
### {{level|Eigenvalue decomposition (finding eigenvalues and eigenvectors)}}&lt;br /&gt;
### {{level|Singular value decomposition (finding singular values and singular vectors)}}&lt;br /&gt;
# &amp;lt;div id=&amp;quot;Solving systems of linear algebraic equations (SLAEs)&amp;quot;&amp;gt;&#039;&#039;&#039;Solving systems of linear algebraic equations (SLAEs)&#039;&#039;&#039;&amp;lt;/div&amp;gt;&lt;br /&gt;
## Direct methods for solving SLAEs &lt;br /&gt;
### {{level|Linpack benchmark}}&lt;br /&gt;
### Methods for solving SLAEs of special forms &lt;br /&gt;
#### Methods for solving triangular SLAEs  &lt;br /&gt;
##### {{level|Forward substitution (real version)|Forward substitution}}&lt;br /&gt;
##### {{level|Backward substitution (real version)|Backward substitution}}&lt;br /&gt;
##### Methods for solving bidiagonal SLAEs &lt;br /&gt;
###### {{level|Forward and backward substitution for bidiagonal SLAEs}}&lt;br /&gt;
###### {{level|Stone doubling algorithm for solving bidiagonal SLAEs}}&lt;br /&gt;
###### {{level|Serial-parallel variant of the backward substitution}}&lt;br /&gt;
#### {{level|Methods for solving tridiagonal SLAEs}}&lt;br /&gt;
##### Methods based on the conventional LU decomposition &lt;br /&gt;
###### {{level|Thomas algorithm}}&lt;br /&gt;
####### {{level|Thomas algorithm, pointwise version|Classical monotone elimination algorithm}}&lt;br /&gt;
####### {{level|Repeated Thomas algorithm, pointwise version|Thomas algorithm repeated for a new right-hand side: Classical scheme}}&lt;br /&gt;
###### {{level|Stone doubling algorithm}}&lt;br /&gt;
###### {{level|Serial-parallel method for solving tridiagonal SLAEs based on the LU decomposition and backward substitutions}}&lt;br /&gt;
##### Other methods &lt;br /&gt;
###### {{level|Reduction method}}&lt;br /&gt;
####### {{level|Complete reduction method}}&lt;br /&gt;
####### {{level|Reduction method repeated for a new right-hand side}}&lt;br /&gt;
###### Two-sided Thomas algorithm &lt;br /&gt;
####### {{level|Two-sided Thomas algorithm, pointwise version|Classical two-sided scheme}}&lt;br /&gt;
####### {{level|Repeated two-sided Thomas algorithm, pointwise version|Thomas algorithm repeated for a new right-hand side: Two-sided scheme }}&lt;br /&gt;
###### {{level|Cyclic reduction}}&lt;br /&gt;
####### {{level|Complete cyclic reduction}}&lt;br /&gt;
####### {{level|Cyclic reduction repeated for a new right-hand side}}&lt;br /&gt;
###### {{level|Bordering method}}&lt;br /&gt;
#### Methods for solving block triangular SLAEs &lt;br /&gt;
##### {{level|Block forward substitution (real version)|Block forward substitution}}&lt;br /&gt;
##### {{level|Block backward substitution (real version))|Block backward substitution}}&lt;br /&gt;
##### Methods for solving block bidiagonal SLAEs &lt;br /&gt;
###### {{level|Forward and backward substitution for block bidiagonal SLAEs}}&lt;br /&gt;
###### {{level|Stone doubling algorithm for solving block bidiagonal SLAEs}}&lt;br /&gt;
###### {{level|Serial-parallel variant of the block backward substitution for solving block bidiagonal SLAEs}}&lt;br /&gt;
#### {{level|Methods for solving block tridiagonal SLAEs}}&lt;br /&gt;
##### Methods based on the conventional LU decomposition &lt;br /&gt;
###### {{level|Block Thomas algorithm}}&lt;br /&gt;
###### {{level|Serial-parallel method for solving block SLAEs based on the LU decomposition and backward substitutions}}&lt;br /&gt;
##### Other methods &lt;br /&gt;
###### {{level|Two-sided Thomas algorithm, block variant}}&lt;br /&gt;
###### {{level|Block cyclic reduction}}&lt;br /&gt;
###### {{level|Block bordering method}}&lt;br /&gt;
### {{level|Solving SLAEs with coefficient matrices of special form whose inverses are known}}&lt;br /&gt;
## Iterative methods for solving SLAEs &lt;br /&gt;
### {{level|High Performance Conjugate Gradient (HPCG) benchmark}}&lt;br /&gt;
# &amp;lt;div id=&amp;quot;Solving eigenvalue problems&amp;quot;&amp;gt;&#039;&#039;&#039;Solving eigenvalue problems&#039;&#039;&#039;&amp;lt;/div&amp;gt;&lt;br /&gt;
## {{level|Eigenvalue decomposition (finding eigenvalues and eigenvectors)}}&lt;br /&gt;
### {{level|QR algorithm}}&lt;br /&gt;
### {{level|Givens method (rotation method) for solving the symmetric eigenvalue problem}}&lt;br /&gt;
## {{level|Singular value decomposition (finding singular values and singular vectors)}}&lt;br /&gt;
# &amp;lt;div id=&amp;quot;Computer performance tests&amp;quot;&amp;gt;&#039;&#039;&#039;Computer performance tests&#039;&#039;&#039;&amp;lt;/div&amp;gt;&lt;br /&gt;
## {{level|High Performance Conjugate Gradient (HPCG) benchmark}}&lt;br /&gt;
## {{level|Linpack benchmark}}&lt;br /&gt;
# &amp;lt;div id=&amp;quot;Fourier transform&amp;quot;&amp;gt;&#039;&#039;&#039;Fourier transform&#039;&#039;&#039;&amp;lt;/div&amp;gt;&lt;br /&gt;
## {{level|Fast Fourier transform for powers of two}}&lt;br /&gt;
# &amp;lt;div id=&amp;quot;Algebra of polynomials&amp;quot;&amp;gt;&#039;&#039;&#039;Algebra of polynomials&#039;&#039;&#039;&amp;lt;/div&amp;gt;&lt;br /&gt;
## {{level|Horner&#039;s rule: Real version, serial variant|Horner&#039;s rule}}&lt;br /&gt;
# &amp;lt;div id=&amp;quot;Numerical quadrature&amp;quot;&amp;gt;&#039;&#039;&#039;Numerical quadrature&#039;&#039;&#039;&amp;lt;/div&amp;gt;&lt;br /&gt;
## {{level|Cubature rules}}&lt;br /&gt;
## {{level|Numerical quadrature (cubature) rules on an interval (for a multidimensional cube)}}&lt;br /&gt;
### [[Numerical quadrature (cubature) rules on an interval (for a multidimensional cube)#Rectangle rule|Rectangle rule]]&lt;br /&gt;
### [[Numerical quadrature (cubature) rules on an interval (for a multidimensional cube)#Trapezoid rule|Trapezoid rule]]&lt;br /&gt;
### [[Numerical quadrature (cubature) rules on an interval (for a multidimensional cube)#Simpson rule|Simpson rule]]&lt;br /&gt;
### [[Numerical quadrature (cubature) rules on an interval (for a multidimensional cube)#Gauss rule|Gauss rule]]&lt;br /&gt;
# &amp;lt;div id=&amp;quot;Graph algorithms&amp;quot;&amp;gt;&#039;&#039;&#039;Graph algorithms&#039;&#039;&#039;&amp;lt;/div&amp;gt;&lt;br /&gt;
## Traversing a graph &lt;br /&gt;
### {{level|Breadth-first search (BFS)}}&lt;br /&gt;
### {{level|Depth-first search (DFS)}}&lt;br /&gt;
## {{level|Single source shortest path (SSSP)}}&lt;br /&gt;
### {{level|Breadth-first search (BFS)}} (for unweighted graphs)&lt;br /&gt;
### {{level|Dijkstra&#039;s algorithm}}&lt;br /&gt;
### {{level|Bellman-Ford algorithm}}&lt;br /&gt;
### {{level|Δ-stepping algorithm}}&lt;br /&gt;
## {{level|All pairs shortest path (APSP)}}&lt;br /&gt;
### {{level|Johnson&#039;s algorithm}}&lt;br /&gt;
### {{level|Floyd-Warshall algorithm}}&lt;br /&gt;
## {{level|Transitive closure of a directed graph}}&lt;br /&gt;
### {{level|Purdom&#039;s algorithm}}&lt;br /&gt;
## {{level|Longest shortest path}}&lt;br /&gt;
## {{level|Construction of the minimum spanning tree (MST)}}&lt;br /&gt;
### {{level|Boruvka&#039;s akgorithm}}&lt;br /&gt;
### {{level|Kruskal&#039;s algorithm}}&lt;br /&gt;
### {{level|Prim&#039;s algorithm}}&lt;br /&gt;
### {{level|GHS algorithm}}&lt;br /&gt;
## {{level|Search for isomorphic subgraphs}}&lt;br /&gt;
### {{level|Ullman&#039;s algorithm}}&lt;br /&gt;
### {{level|VF2 algorithm}}&lt;br /&gt;
## {{level|Graph connectivity}}&lt;br /&gt;
### {{level|Shiloach-Vishkin algorithm for finding the connected components}}&lt;br /&gt;
### {{level|Disjoint set union}}&lt;br /&gt;
### {{level|Tarjan&#039;s strongly connected components algorithm}}&lt;br /&gt;
### {{level|DCSC algorithm for finding the strongly connected components}}&lt;br /&gt;
### {{level|Tarjan&#039;s biconnected components algorithm}}&lt;br /&gt;
### {{level|Tarjan-Vishkin biconnected components algorithm}}&lt;br /&gt;
### {{level|Tarjan&#039;s algorithm for finding the bridges of a graph}}&lt;br /&gt;
### {{level|Vertex connectivity of a graph}}&lt;br /&gt;
### {{level|Gabow&#039;s edge connectivity algorithm}}&lt;br /&gt;
## {{level|Finding maximal flow in a transportation network}}&lt;br /&gt;
### {{level|Ford–Fulkerson algorithm}}&lt;br /&gt;
### {{level|Preflow-Push algorithm}}&lt;br /&gt;
## {{level|Finding minimal-cost flow in a transportation network}}&lt;br /&gt;
## {{level|Assignment problem}}&lt;br /&gt;
### {{level|Hungarian algorithm}}&lt;br /&gt;
### {{level|Auction algorithm}}&lt;br /&gt;
### {{level|Hopcroft–Karp algorithm}}&lt;br /&gt;
## {{level|Betweenness centrality algorithm|Finding the betweenness centrality of vertices}}&lt;br /&gt;
# &amp;lt;div id=&amp;quot;Search algorithms&amp;quot;&amp;gt;&#039;&#039;&#039;Search algorithms&#039;&#039;&#039;&amp;lt;/div&amp;gt;&lt;br /&gt;
## {{level|Binary search: Finding the position of a target value within a sorted array}}, &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt;&lt;br /&gt;
# &amp;lt;div id=&amp;quot;Sorting algorithms&amp;quot;&amp;gt;&#039;&#039;&#039;Sorting algorithms&#039;&#039;&#039;&amp;lt;/div&amp;gt;&lt;br /&gt;
## {{level|Binary tree sort}}&lt;br /&gt;
## {{level|Bubble sort}}&lt;br /&gt;
## {{level|Merge sort (serial and parallel variants)}}&lt;br /&gt;
# &amp;lt;div id=&amp;quot;Computational geometry&amp;quot;&amp;gt;&#039;&#039;&#039;Computational geometry&#039;&#039;&#039;&amp;lt;/div&amp;gt;&lt;br /&gt;
## {{level|Finding the diameter of a point set}}&lt;br /&gt;
## {{level|Finding the convex hull of a point set}}&lt;br /&gt;
## {{level|Delaunay triangulation}}&lt;br /&gt;
## {{level|Voronoi diagram}}&lt;br /&gt;
## {{level|Point-in-polygon problem}}&lt;br /&gt;
## {{level|Convex polygon intersection}} - complexity &amp;lt;math&amp;gt;O(n_1 + n_2)&amp;lt;/math&amp;gt;&lt;br /&gt;
## {{level|Star-shaped polygon intersection}} - complexity &amp;lt;math&amp;gt;O(n_1 * n_2)&amp;lt;/math&amp;gt;&lt;br /&gt;
# &amp;lt;div id=&amp;quot;Computer graphics&amp;quot;&amp;gt;&#039;&#039;&#039;Computer graphics&#039;&#039;&#039;&amp;lt;/div&amp;gt;&lt;br /&gt;
## {{level|Line drawing algorithms: Approximating a line segment on discrete graphical media}}&lt;br /&gt;
## {{level|Determining visible parts of a three-dimensional scene}}&lt;br /&gt;
## {{level|Ray tracing: Rendering realistic images}}&lt;br /&gt;
## {{level|Global illumination: Regarding direct illumination and reflection from other objects}}&lt;br /&gt;
# &amp;lt;div id=&amp;quot;Cryptographic algorithms&amp;quot;&amp;gt;&#039;&#039;&#039;Cryptographic algorithms&#039;&#039;&#039;&amp;lt;/div&amp;gt;&lt;br /&gt;
# &amp;lt;div id=&amp;quot;Нейронные сети&amp;quot;&amp;gt;&#039;&#039;&#039;Neural networks&#039;&#039;&#039;&amp;lt;/div&amp;gt;&lt;br /&gt;
# &amp;lt;div id=&amp;quot;Optimization algorithms&amp;quot;&amp;gt;&#039;&#039;&#039;Optimization algorithms&#039;&#039;&#039;&amp;lt;/div&amp;gt;&lt;br /&gt;
## {{level|Linear programming}}&lt;br /&gt;
## {{level|Simplex algorithm}}&lt;br /&gt;
## {{level|Branch and bound method (serial and parallel variants)}}&lt;br /&gt;
## {{level|Genetic algorithms}}&lt;br /&gt;
## {{level|Ant colony algorithms}}&lt;br /&gt;
## {{level|Hybrid algorithms}}&lt;br /&gt;
## {{level|Finding extrema of a function}}&lt;br /&gt;
# &amp;lt;div id=&amp;quot;Game theory algorithms&amp;quot;&amp;gt;&#039;&#039;&#039;Game theory algorithms&#039;&#039;&#039;&amp;lt;/div&amp;gt;&lt;br /&gt;
# &amp;lt;div id=&amp;quot;Algorithms for quantum system simulation&amp;quot;&amp;gt;&#039;&#039;&#039;Algorithms for quantum system simulation&#039;&#039;&#039;&amp;lt;/div&amp;gt;&lt;br /&gt;
## &#039;&#039;Algorithms for quantum computation simulation&#039;&#039;&lt;br /&gt;
### {{level|Single-qubit transform of a state vector}}&lt;br /&gt;
### {{level|Two-qubit transform of a state vector}}&lt;br /&gt;
### {{level|Quantum Fourier transform simulation}}&lt;br /&gt;
# &amp;lt;div id=&amp;quot;Algorithms for solving equations of mathematical physics&amp;quot;&amp;gt;&#039;&#039;&#039;Algorithms for solving equations of mathematical physics&#039;&#039;&#039;&amp;lt;/div&amp;gt;&lt;br /&gt;
## {{level|Poisson equation: Solving with DFT}}&lt;br /&gt;
# &amp;lt;div id=&amp;quot;Other algorithms&amp;quot;&amp;gt;&#039;&#039;&#039;Other algorithms&#039;&#039;&#039;&amp;lt;/div&amp;gt;&lt;/div&gt;</summary>
		<author><name>Lineprinter</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%D0%B2%D0%B8%D0%BA%D0%B8:%D0%9F%D1%80%D0%B0%D0%B2%D0%B8%D0%BB%D0%B0&amp;diff=15591</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%D0%B2%D0%B8%D0%BA%D0%B8:%D0%9F%D1%80%D0%B0%D0%B2%D0%B8%D0%BB%D0%B0&amp;diff=15591"/>
		<updated>2016-11-01T15:18:47Z</updated>

		<summary type="html">&lt;p&gt;Lineprinter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Роли в проекте =&lt;br /&gt;
&lt;br /&gt;
== Кто может стать автором AlgoWiki? ==&lt;br /&gt;
Автором AlgoWiki может стать любой пользователь сети Интернет, желающий внести свой вклад в описание структуры и/или особенностей реализации алгоритмов как для традиционных, так и для перспективных вычислительных систем.&lt;br /&gt;
&lt;br /&gt;
= Регистрация в проекте = &lt;br /&gt;
&lt;br /&gt;
== Как зарегистрироваться на сайте? ==&lt;br /&gt;
Для регистрации на сайте нажмите ссылку &amp;quot;Войти&amp;quot; на главной странице и далее ссылку &amp;quot;запросить&amp;quot; (можно сразу зайти по ссылке http://algowiki-project.org/ru/Служебная:Запросить_учётную_запись.&lt;br /&gt;
&lt;br /&gt;
== Для чего нужна регистрация? ==&lt;br /&gt;
Регистрация в проекте нужна для отделения реальных пользователей от ботов.&lt;br /&gt;
&lt;br /&gt;
== Что может быть причиной отказа в регистрации? ==&lt;br /&gt;
Причин для отказа в регистрации нет.&lt;br /&gt;
&lt;br /&gt;
= Правка существующих страниц =&lt;br /&gt;
&lt;br /&gt;
== Кто может вносить правки в существующие страницы? ==&lt;br /&gt;
Правки в существующие страницы может вносить любой зарегистрированный пользователь.&lt;br /&gt;
&lt;br /&gt;
== Когда внесённые правки появятся на странице? ==&lt;br /&gt;
Внесённые правки станут видны всем пользователям после утверждения редактором сайта.&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;nowiki&amp;gt;[[Новая страница]]&amp;lt;/nowiki&amp;gt;. Затем надо перейти по получившейся ссылке и отредактировать созданную станицу. Подробнее про способы создания новой страницы в MediaWiki можно прочитать [https://www.mediawiki.org/wiki/Help:Starting_a_new_page/ru здесь].&lt;br /&gt;
&lt;br /&gt;
== Какой должна быть структура создаваемой страницы? ==&lt;br /&gt;
Структура описания алгоритма должна строго соответствовать структуре, описанной [[Структура_описания_свойств_алгоритмов|здесь]]. Если возникнут предложения по модификации данной структуры, соответствующие предложения присылайте [mailto:info@algowiki-project.org авторам проекта]. Если автор не обладает информацией по некоторым разделам описания, данные разделы могут быть оставлены незаполненными.&lt;br /&gt;
&lt;br /&gt;
== Как вписать новую страницу в структуру проекта? ==&lt;br /&gt;
Все новые страницы с описаниями алгоритмов требуется вписать в [[Классификация_алгоритмов|классификацию алгоритмов]]. Для каждого алгоритма нужно найти наиболее подходящий раздел в классификации либо создать новый раздел. Возможно вхождение алгоритмов одновременно в несколько разделов.&lt;br /&gt;
&lt;br /&gt;
[[Категория:Algowiki:Справка]]&lt;/div&gt;</summary>
		<author><name>Lineprinter</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%D0%B2%D0%B8%D0%BA%D0%B8:%D0%A2%D0%B5%D1%85%D0%BD%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B9_%D1%84%D0%BE%D1%80%D1%83%D0%BC&amp;diff=15590</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%D0%B2%D0%B8%D0%BA%D0%B8:%D0%A2%D0%B5%D1%85%D0%BD%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B9_%D1%84%D0%BE%D1%80%D1%83%D0%BC&amp;diff=15590"/>
		<updated>2016-11-01T15:18:24Z</updated>

		<summary type="html">&lt;p&gt;Lineprinter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{project internals nav}}&lt;br /&gt;
&lt;br /&gt;
== Свежие на момент 2016-11-01 изменения ==&lt;br /&gt;
* Починен redirect loop на страницах с символами +, &#039;, &amp;quot;, =, \.&lt;br /&gt;
* Mediawiki обновлена до 1.26.4.&lt;br /&gt;
* Появился [[Ш:Doc]] для документации шаблонов.&lt;br /&gt;
* Заполнен [[MediaWiki:Edittools]] на основе [[ru-wikipedia:MediaWiki:Edittools]].&lt;br /&gt;
&lt;br /&gt;
— [[S:Contributions/Lineprinter|lineprinter]] 11:11, 1 ноября 2016 (MSK)&lt;br /&gt;
&lt;br /&gt;
== Свежие на момент 2016-10-07 изменения ==&lt;br /&gt;
* Введен обязательный https.&lt;br /&gt;
* Сертификат заменен на Let&#039;s Encrypt&#039;овский.&lt;br /&gt;
* Введена капча на регистрацию и на повторные попытки входа в систему.&lt;br /&gt;
* Открыта регистрация.&lt;br /&gt;
* Введены дополнительные, короткие, редиректы на неймспейсы:&lt;br /&gt;
** &#039;&#039;&#039;Ш&#039;&#039;&#039; и &#039;&#039;&#039;T&#039;&#039;&#039; для &#039;&#039;&#039;Ш&#039;&#039;&#039;аблонов (&#039;&#039;&#039;T&#039;&#039;&#039;emplate)&lt;br /&gt;
** &#039;&#039;&#039;К&#039;&#039;&#039; и &#039;&#039;&#039;C&#039;&#039;&#039; для &#039;&#039;&#039;К&#039;&#039;&#039;атегорий (&#039;&#039;&#039;C&#039;&#039;&#039;ategory)&lt;br /&gt;
** &#039;&#039;&#039;С&#039;&#039;&#039; и &#039;&#039;&#039;S&#039;&#039;&#039; для &#039;&#039;&#039;С&#039;&#039;&#039;лужебных (&#039;&#039;&#039;S&#039;&#039;&#039;pecial)&lt;br /&gt;
** &#039;&#039;&#039;U&#039;&#039;&#039; для пользователей (&#039;&#039;&#039;U&#039;&#039;&#039;ser)&lt;br /&gt;
** &#039;&#039;&#039;Ф&#039;&#039;&#039; и &#039;&#039;&#039;F&#039;&#039;&#039; для &#039;&#039;&#039;Ф&#039;&#039;&#039;айлов (&#039;&#039;&#039;F&#039;&#039;&#039;ile)&lt;br /&gt;
** &#039;&#039;&#039;А&#039;&#039;&#039; и &#039;&#039;&#039;A&#039;&#039;&#039; для &#039;&#039;&#039;А&#039;&#039;&#039;лговики (&#039;&#039;&#039;A&#039;&#039;&#039;lgowiki)&lt;br /&gt;
: Таким образом, на страницу [[Алговики:Общий форум]] можно попасть по [[А:Общий форум]], например.&lt;br /&gt;
* Появился новый [[Ш:Плашка]], который предполагается использовать для создания производных от него шаблонов (например [[Ш:Assignment]])&lt;br /&gt;
&lt;br /&gt;
— [[S:Contributions/Lineprinter|lineprinter]] 14:27, 7 октября 2016 (MSK)&lt;/div&gt;</summary>
		<author><name>Lineprinter</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9A%D0%B0%D1%82%D0%B5%D0%B3%D0%BE%D1%80%D0%B8%D1%8F:%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D1%8B&amp;diff=15589</id>
		<title>Категория:Алгоритмы</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9A%D0%B0%D1%82%D0%B5%D0%B3%D0%BE%D1%80%D0%B8%D1%8F:%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D1%8B&amp;diff=15589"/>
		<updated>2016-11-01T15:17:23Z</updated>

		<summary type="html">&lt;p&gt;Lineprinter: Полностью удалено содержимое страницы&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Lineprinter</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9A%D0%B0%D1%82%D0%B5%D0%B3%D0%BE%D1%80%D0%B8%D1%8F:Algowiki:%D0%A1%D0%BF%D1%80%D0%B0%D0%B2%D0%BA%D0%B0&amp;diff=15588</id>
		<title>Категория:Algowiki:Справка</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9A%D0%B0%D1%82%D0%B5%D0%B3%D0%BE%D1%80%D0%B8%D1%8F:Algowiki:%D0%A1%D0%BF%D1%80%D0%B0%D0%B2%D0%BA%D0%B0&amp;diff=15588"/>
		<updated>2016-11-01T15:17:16Z</updated>

		<summary type="html">&lt;p&gt;Lineprinter: Полностью удалено содержимое страницы&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Lineprinter</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9A%D0%B0%D1%82%D0%B5%D0%B3%D0%BE%D1%80%D0%B8%D1%8F:%D0%91%D0%BB%D0%BE%D1%87%D0%BD%D1%8B%D0%B5_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D1%8B&amp;diff=15586</id>
		<title>Категория:Блочные алгоритмы</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9A%D0%B0%D1%82%D0%B5%D0%B3%D0%BE%D1%80%D0%B8%D1%8F:%D0%91%D0%BB%D0%BE%D1%87%D0%BD%D1%8B%D0%B5_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D1%8B&amp;diff=15586"/>
		<updated>2016-11-01T15:15:37Z</updated>

		<summary type="html">&lt;p&gt;Lineprinter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;&#039;Блочные алгоритмы&#039;&#039;&#039; - алгоритмы, имеющие какой-либо точечный аналог, но в которых основными операциями являются не операции над числами, а операции над блоками (фрагментами матриц).&lt;br /&gt;
&lt;br /&gt;
[[Категория:Алгоритмы для матриц]]&lt;/div&gt;</summary>
		<author><name>Lineprinter</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9A%D0%B0%D1%82%D0%B5%D0%B3%D0%BE%D1%80%D0%B8%D1%8F:%D0%9C%D0%B5%D1%82%D0%BE%D0%B4%D1%8B_%D0%BA%D0%BE%D0%BD%D1%81%D1%82%D1%80%D1%83%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F_%D0%BF%D0%B0%D1%80%D0%B0%D0%BB%D0%BB%D0%B5%D0%BB%D1%8C%D0%BD%D1%8B%D1%85_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%BE%D0%B2&amp;diff=15585</id>
		<title>Категория:Методы конструирования параллельных алгоритмов</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9A%D0%B0%D1%82%D0%B5%D0%B3%D0%BE%D1%80%D0%B8%D1%8F:%D0%9C%D0%B5%D1%82%D0%BE%D0%B4%D1%8B_%D0%BA%D0%BE%D0%BD%D1%81%D1%82%D1%80%D1%83%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F_%D0%BF%D0%B0%D1%80%D0%B0%D0%BB%D0%BB%D0%B5%D0%BB%D1%8C%D0%BD%D1%8B%D1%85_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%BE%D0%B2&amp;diff=15585"/>
		<updated>2016-11-01T15:14:37Z</updated>

		<summary type="html">&lt;p&gt;Lineprinter: Откат правок AntonChupin (обсуждение) к версии Frolov&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;&#039;Методы конструирования параллельных алгоритмов&#039;&#039;&#039; - общие приёмы, с помощью которых на основе одних алгоритмов конструируются другие, специально для параллельных вычислительных систем.&lt;/div&gt;</summary>
		<author><name>Lineprinter</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9A%D0%B0%D1%82%D0%B5%D0%B3%D0%BE%D1%80%D0%B8%D1%8F:%D0%A3%D1%80%D0%BE%D0%B2%D0%B5%D0%BD%D1%8C_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4%D0%B0&amp;diff=15584</id>
		<title>Категория:Уровень метода</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9A%D0%B0%D1%82%D0%B5%D0%B3%D0%BE%D1%80%D0%B8%D1%8F:%D0%A3%D1%80%D0%BE%D0%B2%D0%B5%D0%BD%D1%8C_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4%D0%B0&amp;diff=15584"/>
		<updated>2016-11-01T15:14:12Z</updated>

		<summary type="html">&lt;p&gt;Lineprinter: Откат правок AntonChupin (обсуждение) к версии Nebaruzdin&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;&#039;Уровень метода&#039;&#039;&#039; в категории статьи означает, что в статье описан математический метод решения некоторой задачи. Для этого метода вполне возможно существование разных алгоритмов, реализующих его.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[en:Category:Method level]]&lt;/div&gt;</summary>
		<author><name>Lineprinter</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9A%D0%B0%D1%82%D0%B5%D0%B3%D0%BE%D1%80%D0%B8%D1%8F:%D0%A3%D1%80%D0%BE%D0%B2%D0%B5%D0%BD%D1%8C_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B8&amp;diff=15583</id>
		<title>Категория:Уровень задачи</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9A%D0%B0%D1%82%D0%B5%D0%B3%D0%BE%D1%80%D0%B8%D1%8F:%D0%A3%D1%80%D0%BE%D0%B2%D0%B5%D0%BD%D1%8C_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B8&amp;diff=15583"/>
		<updated>2016-11-01T15:14:00Z</updated>

		<summary type="html">&lt;p&gt;Lineprinter: Откат правок AntonChupin (обсуждение) к версии Nebaruzdin&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;&#039;Уровень задачи&#039;&#039;&#039; в категории означает, что в статье описана задача, которую, возможно, решают несколько методов.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[en:Category:Problem level]]&lt;/div&gt;</summary>
		<author><name>Lineprinter</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9A%D0%B0%D1%82%D0%B5%D0%B3%D0%BE%D1%80%D0%B8%D1%8F:%D0%A3%D1%80%D0%BE%D0%B2%D0%B5%D0%BD%D1%8C_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0&amp;diff=15582</id>
		<title>Категория:Уровень алгоритма</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9A%D0%B0%D1%82%D0%B5%D0%B3%D0%BE%D1%80%D0%B8%D1%8F:%D0%A3%D1%80%D0%BE%D0%B2%D0%B5%D0%BD%D1%8C_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0&amp;diff=15582"/>
		<updated>2016-11-01T15:13:39Z</updated>

		<summary type="html">&lt;p&gt;Lineprinter: Откат правок AntonChupin (обсуждение) к версии Nebaruzdin&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;&#039;Уровень алгоритма&#039;&#039;&#039; в категории страницы означает, что на ней описан конкретный алгоритм, реализующий решение задачи некоторым методом.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[en:Category:Algorithm level]]&lt;/div&gt;</summary>
		<author><name>Lineprinter</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9E_%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D0%B5&amp;diff=15581</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%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D0%B5&amp;diff=15581"/>
		<updated>2016-11-01T15:12:32Z</updated>

		<summary type="html">&lt;p&gt;Lineprinter: Откат правок AntonChupin (обсуждение) к версии ASA&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;AlgoWiki - это открытая энциклопедия в сети Интернет по свойствам алгоритмов и особенностям их реализации на различных программно-аппаратных платформах от мобильных до экзафлопсных суперкомпьютерных систем с возможностью коллективной работы всего мирового вычислительного сообщества.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Компьютеры быстро меняются. За последние сорок лет сменилось, по крайней мере, шесть поколений архитектур, вызвавших необходимость кардинальных изменений в программном обеспечении. Векторные компьютеры, векторно-параллельные, массивно-параллельные, компьютеры с общей памятью, кластеры из машин с общей памятью, компьютеры с ускорителями... Вычислительное сообщество через это прошло, но каждый раз все программное обеспечение нужно было, по сути, переписывать заново: очередное поколение машин требовало выделения новых свойств в алгоритмах, что отражалось и в программах.&lt;br /&gt;
&lt;br /&gt;
Увы, но нет никаких оснований надеяться, что ситуация в будущем изменится к лучшему. Уже сейчас рассматриваются перспективные варианты архитектур, где будут использованы легкие и/или тяжелые вычислительные ядра, ускорители разного типа, идеи SIMD и data-flow обработки. В такой ситуации, для полного использования потенциала будущих компьютеров нужно будет опять переписывать код. Бесконечный процесс, который оптимизма у разработчиков программного обеспечения не вызывает.&lt;br /&gt;
&lt;br /&gt;
Но ситуация, все же, не столь безнадежна. Да, появление новых вычислительных систем вызывает необходимость пересмотра алгоритмического багажа. Однако сами алгоритмы при этом, как правило, остаются неизменными: меняются лишь требования, которые новые компьютеры предъявляют к структуре алгоритмов и программ. Для поддержки векторизации необходим параллелизм по данным в самых внутренних циклах программы. Для векторно-параллельных машин дополнительно к этому необходим небольшой ресурс параллелизма на верхнем уровне циклического профиля для использования нескольких независимых процессоров. Для эффективного использования массивно-параллельных компьютеров предмет основной заботы – это поиск такого представления алгоритма, при котором большое число вычислительных узлов могло бы долго работать независимо друг от друга, сведя обмен данными к минимуму. И так для каждого поколения параллельных вычислительных систем: архитектура компьютеров заставляет по-новому взглянуть на свойства существующих алгоритмов, чтобы найти эффективный способ реализации.&lt;br /&gt;
&lt;br /&gt;
В такой ситуации для нас важны два факта: сами алгоритмы меняются мало, и их свойства никак не зависят от вычислительных систем. Это значит, что описав свойства алгоритмов один раз, этими сведениями можно будет пользоваться многократно для любых компьютерных платформ, существующих сейчас или будущих. Каждый раз, когда будет возникать задача эффективной реализации алгоритма на некотором компьютере, ответ на вопрос о потенциальных возможностях самого алгоритма можно будет найти в этом описании.&lt;br /&gt;
&lt;br /&gt;
Идея глубокого априорного анализа свойств алгоритмов и их реализаций легла в основу проекта AlgoWiki, начатого в НИВЦ МГУ. Основная задача проекта – составить такое описание фундаментальных свойств алгоритмов, которое даст полное понимание как их теоретического потенциала, так и особенностей их реализации на различных классах параллельных вычислительных систем.&lt;br /&gt;
Описание всех алгоритмов в AlgoWiki строится из двух частей. В первой части описываются собственно алгоритмы и их свойства. Вторая часть посвящена описанию особенностей их программной реализации с учетом конкретных программно-аппаратных платформ. Такое деление на две части сделано для того, чтобы машинно-независимые свойства алгоритмов, определяющие их потенциал и качество реализации на параллельных вычислительных системах, были бы выделены и описаны отдельно от множества вопросов, связанных с последующими этапами программирования алгоритмов и исполнения результирующих программ.&lt;br /&gt;
&lt;br /&gt;
Цель AlgoWiki - дать исчерпывающее описание алгоритма, которое поможет оценить его потенциал применительно к конкретной параллельной вычислительной платформе. Кроме классических свойств алгоритмов, например, последовательной сложности, в AlgoWiki должен быть представлен и целый набор дополнительных сведений, составляющих в совокупности полную картину об алгоритме: параллельная сложность, параллельная структура, детерминированность, оценки локальности данных, эффективности и масштабируемости, коммуникационный профиль конкретных реализаций и многие другие.&lt;br /&gt;
&lt;br /&gt;
В AlgoWiki очень важны детали. Классические базовые алгоритмы должны быть дополнены важными на практике модификациями. В некоторых случаях для описания модификаций достаточно одного небольшого комментария со ссылкой на базовый вариант алгоритма, поясняющего идею его модификации и получаемый эффект. Иногда же изменения алгоритма значительны и требуют полноценного самостоятельного описания. Так в AlgoWiki есть описание базового точечного вещественного варианта разложения Холецкого для плотной симметричной положительно-определенной матрицы. Но на практике не менее важны модификации алгоритма: блочный вариант, вариант для плотной комплексно-симметричной матрицы (как точечный, так и блочный), варианты алгоритма для разреженных матриц, важны соображения относительно использования разложения Холецкого в итерационных методах и т.д.&lt;br /&gt;
&lt;br /&gt;
Точно также важны и детали, касающиеся особенностей реализации алгоритмов на конкретных параллельных вычислительных платформах: многоядерных процессорах, SMP, кластерах, разного рода ускорителях, с использованием векторной обработки. Во многих случаях нужно спускаться еще на уровень ниже, описывая особенности реализации алгоритма не просто для ускорителя, а выделяя отдельно важные частные случаи, например, GPU и Xeon Phi. Полезными являются и данные вычислительных экспериментов на похожих платформах, различающихся, например, разными объемами кэш-памяти, деталями реализации векторной обработки или латентностью коммуникационной сети. При этом, приводя данные о времени выполнения, эффективности и масштабируемости, мы не только даем некоторые ориентиры возможного качества реализации алгоритма на конкретном компьютере, но и формируем базу для сравнительного анализа различных компьютерных платформ на алгоритмах, представленных в AlgoWiki. &lt;br /&gt;
&lt;br /&gt;
Результатом проекта AlgoWiki является открытая энциклопедия по свойствам алгоритмов и особенностям их реализации на различных компьютерах. В реализации сразу была сделана ориентация на использование wiki-технологий с возможностью коллективной работы над энциклопедией всего вычислительного сообщества. На первом этапе проекта основное внимание уделялось отработке именно структуры описания алгоритмов. В настоящее время энциклопедия активно расширяется за счет описаний алгоритмов, составленных внешними экспертами, а также создается многоязычная версия, которая затем и станет основной.&lt;br /&gt;
&lt;br /&gt;
Работа поддержана грантом [http://РНФ.рф Российского научного фонда] (проект N14-11-00190).&lt;br /&gt;
&lt;br /&gt;
== Основные публикации по проекту ==&lt;br /&gt;
&lt;br /&gt;
* Теплов А.М. Об одном подходе к сравнению масштабируемости параллельных программ // Вычислительные методы и программирование: Новые вычислительные технологии (Электронный научный журнал). Том 15, выпуск 4, 2014. С. 697-711. http://num-meth.srcc.msu.ru/zhurnal/tom_2014/pdf/v15r161.pdf&lt;br /&gt;
* Вл.В.Воеводин &amp;quot;Открытая энциклопедия свойств алгоритмов AlgoWiki: от мобильных платформ до экзафлопсных суперкомпьютерных систем&amp;quot; // Вычислительные методы и программирование: Новые вычислительные технологии (Электронный научный журнал). Том 16, раздел 1, 2015. С. 99-111. http://num-meth.srcc.msu.ru/zhurnal/tom_2015/pdf/v16r111.pdf&lt;br /&gt;
* А.С.Антонов, Вад.В.Воеводин, Вл.В.Воеводин, А.М.Теплов, А.В.Фролов. Первая версия Открытой энциклопедии свойств алгоритмов // Вестник УГАТУ. Серия управление, вычислительная техника и информатика. Том 19, N 2(68), 2015. С. 150-159. http://journal.ugatu.ac.ru/index.php/vestnik/article/view/1460&lt;br /&gt;
* А.В.Фролов, Вад.В.Воеводин, И.Н.Коньшин, А.М.Теплов. Исследование структурных свойств алгоритма разложения Холецкого: от давно известных фактов до новых выводов // Параллельные вычислительные технологии (ПаВТ&#039;2015): труды международной научной конференции (31 марта - 2 апреля 2015 г., г. Екатеринбург). Челябинск: Издательский центр ЮУрГУ, 2015. С. 320-331. http://omega.sp.susu.ac.ru/books/conference/PaVT2015/full/058.pdf&lt;br /&gt;
* V.Voevodin, A.Antonov, J.Dongarra. AlgoWiki: an Open Encyclopedia of Parallel Algorithmic Features // Supercomputing Frontiers and Innovations, Vol.2, No.1 (2015). Pp.4-18. http://superfri.org/superfri/article/view/69&lt;br /&gt;
&lt;br /&gt;
[[En: About Project]]&lt;/div&gt;</summary>
		<author><name>Lineprinter</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9A%D0%BB%D0%B0%D1%81%D1%81%D0%B8%D1%84%D0%B8%D0%BA%D0%B0%D1%86%D0%B8%D1%8F_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%BE%D0%B2&amp;diff=15580</id>
		<title>Классификация алгоритмов</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9A%D0%BB%D0%B0%D1%81%D1%81%D0%B8%D1%84%D0%B8%D0%BA%D0%B0%D1%86%D0%B8%D1%8F_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%BE%D0%B2&amp;diff=15580"/>
		<updated>2016-11-01T15:12:18Z</updated>

		<summary type="html">&lt;p&gt;Lineprinter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;# &amp;lt;div id=&amp;quot;Векторные операции&amp;quot;&amp;gt;&#039;&#039;&#039;Векторные операции&#039;&#039;&#039;&amp;lt;/div&amp;gt;&lt;br /&gt;
## &#039;&#039;Суммирование сдваиванием&#039;&#039;&lt;br /&gt;
### {{level|Нахождение суммы элементов массива сдваиванием}}&lt;br /&gt;
### {{level|Нахождение частных сумм элементов массива сдваиванием}}&lt;br /&gt;
## {{level|Равномерная норма вектора, вещественная версия, последовательно-параллельный вариант}}&lt;br /&gt;
## {{level|Скалярное произведение векторов, вещественная версия, последовательно-параллельный вариант}}&lt;br /&gt;
## {{level|Последовательно-параллельный метод суммирования}}&lt;br /&gt;
# &amp;lt;div id=&amp;quot;Матрично-векторные операции&amp;quot;&amp;gt;&#039;&#039;&#039;Матрично-векторные операции&#039;&#039;&#039;&amp;lt;/div&amp;gt;&lt;br /&gt;
## {{level|Умножение плотной матрицы на вектор}}&lt;br /&gt;
# &amp;lt;div id=&amp;quot;Матричные операции&amp;quot;&amp;gt;&#039;&#039;&#039;Матричные операции&#039;&#039;&#039;&amp;lt;/div&amp;gt;&lt;br /&gt;
## {{level|Умножение плотных матриц}}&lt;br /&gt;
# &amp;lt;div id=&amp;quot;Разложения матриц&amp;quot;&amp;gt;&#039;&#039;&#039;Разложения матриц&#039;&#039;&#039;&amp;lt;/div&amp;gt;&lt;br /&gt;
## &#039;&#039;Треугольные разложения&#039;&#039;&lt;br /&gt;
### Метод Гаусса (нахождение LU-разложения)&lt;br /&gt;
#### {{level|Метод Гаусса без перестановок}}&lt;br /&gt;
##### LU-разложение методом Гаусса&lt;br /&gt;
##### Компактная схема метода Гаусса и её модификации&lt;br /&gt;
###### Компактная схема метода Гаусса для плотной матрицы&lt;br /&gt;
###### {{level|Компактная схема метода Гаусса для трёхдиагональной матрицы и её модификации}}&lt;br /&gt;
####### {{level|Компактная схема метода Гаусса для трёхдиагональной матрицы, последовательный вариант}}&lt;br /&gt;
####### {{level|Алгоритм сдваивания Стоуна для LU-разложения трёхдиагональной матрицы}}&lt;br /&gt;
####### {{level|Последовательно-параллельный алгоритм для LU-разложения трёхдиагональной матрицы}}&lt;br /&gt;
#### Метод Гаусса с перестановками&lt;br /&gt;
##### Метод Гаусса с выбором ведущего элемента по столбцу&lt;br /&gt;
##### Метод Гаусса с выбором ведущего элемента по строке&lt;br /&gt;
##### Метод Гаусса с выбором ведущего элемента по главной диагонали&lt;br /&gt;
##### Метод Гаусса с выбором ведущего элемента по всей матрице&lt;br /&gt;
### {{level|Метод Холецкого (нахождение симметричного треугольного разложения)}}&lt;br /&gt;
#### {{level|Разложение Холецкого (метод квадратного корня)}} базовый точечный вещественный вариант для плотной симметричной положительно-определённой матрицы&lt;br /&gt;
### {{level|Известные треугольные разложения для матриц специального вида}}&lt;br /&gt;
## &#039;&#039;Унитарно-треугольные разложения&#039;&#039;&lt;br /&gt;
### {{level|Метод Гивенса (вращений) QR-разложения матрицы}}&lt;br /&gt;
### {{level|Метод Хаусхолдера (отражений) QR-разложения матрицы}}&lt;br /&gt;
## &#039;&#039;Разложения на унитарные и хессенберговы матрицы&#039;&#039;&lt;br /&gt;
### {{level|Метод Хаусхолдера (отражений) приведения матрицы к хессенберговой (двухдиагональной) форме}}&lt;br /&gt;
### {{level|Метод Гивенса (вращений) приведения матрицы к хессенберговой (двухдиагональной) форме}}&lt;br /&gt;
## &#039;&#039;Разложения на унитарные и диагональные матрицы&#039;&#039;&lt;br /&gt;
### {{level|Спектральное разложение (нахождение собственных значений и векторов)}}&lt;br /&gt;
### {{level|Сингулярное разложение (нахождение сингулярных значений и векторов)}}&lt;br /&gt;
# &amp;lt;div id=&amp;quot;Решение систем линейных уравнений&amp;quot;&amp;gt;&#039;&#039;&#039;Решение систем линейных уравнений&#039;&#039;&#039;&amp;lt;/div&amp;gt;&lt;br /&gt;
## Прямые методы решения СЛАУ&lt;br /&gt;
### {{level|Linpack benchmark}}&lt;br /&gt;
### Методы решения СЛАУ с матрицами специального вида&lt;br /&gt;
#### Методы решения СЛАУ с треугольными матрицами&lt;br /&gt;
##### {{level|Прямая подстановка (вещественный вариант)|Прямая подстановка}}&lt;br /&gt;
##### {{level|Обратная подстановка (вещественный вариант)|Обратная подстановка}}&lt;br /&gt;
##### Методы решения СЛАУ с двухдиагональными матрицами&lt;br /&gt;
###### {{level|Прямая и обратная подстановка в СЛАУ с двухдиагональной матрицей}}&lt;br /&gt;
###### {{level|Метод сдваивания Стоуна для решения двухдиагональных СЛАУ}}&lt;br /&gt;
###### {{level|Последовательно-параллельный вариант обратной подстановки}}&lt;br /&gt;
#### {{level|Методы решения СЛАУ с трёхдиагональными матрицами}}&lt;br /&gt;
##### Методы, основанные на стандартном LU-разложении матрицы&lt;br /&gt;
###### {{level|Прогонка}}&lt;br /&gt;
####### {{level|Прогонка, точечный вариант|Классическая монотонная прогонка}}&lt;br /&gt;
####### {{level|Повторная прогонка, точечный вариант|Повторная прогонка для новой правой части, классическая схема}}&lt;br /&gt;
###### {{level|Метод сдваивания Стоуна}}&lt;br /&gt;
###### {{level|Последовательно-параллельный вариант решения трёхдиагональной СЛАУ с LU-разложением и обратными подстановками}}&lt;br /&gt;
##### Другие методы&lt;br /&gt;
###### {{level|Метод редукции}}&lt;br /&gt;
####### {{level|Полный метод редукции}}&lt;br /&gt;
####### {{level|Повторный метод редукции для новой правой части}}&lt;br /&gt;
###### Встречная прогонка&lt;br /&gt;
####### {{level|Встречная прогонка, точечный вариант|Классическая встречная прогонка}}&lt;br /&gt;
####### {{level|Повторная встречная прогонка, точечный вариант|Повторная прогонка для новой правой части, встречная схема}}&lt;br /&gt;
###### {{level|Метод циклической редукции}}&lt;br /&gt;
####### {{level|Полный метод циклической редукции}}&lt;br /&gt;
####### {{level|Повторный метод циклической редукции для новой правой части}}&lt;br /&gt;
###### {{level|Метод окаймления}}&lt;br /&gt;
#### Методы решения СЛАУ с блочно-треугольными матрицами&lt;br /&gt;
##### {{level|Блочная прямая подстановка (вещественный вариант)|Блочная прямая подстановка}}&lt;br /&gt;
##### {{level|Блочная обратная подстановка (вещественный вариант)|Блочная обратная подстановка}}&lt;br /&gt;
##### Методы решения СЛАУ с блочно-двухдиагональными матрицами&lt;br /&gt;
###### {{level|Прямая и обратная подстановка в СЛАУ с блочно-двухдиагональной матрицей}}&lt;br /&gt;
###### {{level|Метод сдваивания Стоуна для решения блочно-двухдиагональных СЛАУ}}&lt;br /&gt;
###### {{level|Блочный последовательно-параллельный вариант обратной подстановки для решения блочно-двухдиагональных СЛАУ}}&lt;br /&gt;
#### {{level|Методы решения СЛАУ с блочно-трёхдиагональными матрицами}}&lt;br /&gt;
##### Методы, основанные на стандартном LU-разложении матрицы&lt;br /&gt;
###### {{level|Блочная прогонка}}&lt;br /&gt;
###### {{level|Блочный последовательно-параллельный вариант решения с LU-разложением и обратными подстановками}}&lt;br /&gt;
##### Другие методы&lt;br /&gt;
###### {{level|Встречная прогонка, блочный вариант}}&lt;br /&gt;
###### {{level|Блочный метод циклической редукции}}&lt;br /&gt;
###### {{level|Блочный метод окаймления}}&lt;br /&gt;
### {{level|Решения СЛАУ с матрицами специального вида, имеющими известные обратные матрицы}}&lt;br /&gt;
## Итерационные методы решения СЛАУ&lt;br /&gt;
### {{level|High Performance Conjugate Gradient (HPCG) benchmark}}&lt;br /&gt;
# &amp;lt;div id=&amp;quot;Решения спектральных задач&amp;quot;&amp;gt;&#039;&#039;&#039;Решения спектральных задач&#039;&#039;&#039;&amp;lt;/div&amp;gt;&lt;br /&gt;
## {{level|Спектральное разложение (нахождение собственных значений и векторов)}}&lt;br /&gt;
### {{level|QR-алгоритм}}&lt;br /&gt;
### {{level|Метод Гивенса (вращений) для решения спектральной задачи у симметричных матриц}}&lt;br /&gt;
## {{level|Сингулярное разложение (нахождение сингулярных значений и векторов)}}&lt;br /&gt;
# &amp;lt;div id=&amp;quot;Тесты производительности компьютеров&amp;quot;&amp;gt;&#039;&#039;&#039;Тесты производительности компьютеров&#039;&#039;&#039;&amp;lt;/div&amp;gt;&lt;br /&gt;
## {{level|High Performance Conjugate Gradient (HPCG) benchmark}}&lt;br /&gt;
## {{level|Linpack benchmark}}&lt;br /&gt;
# &amp;lt;div id=&amp;quot;Преобразование Фурье&amp;quot;&amp;gt;&#039;&#039;&#039;Преобразование Фурье&#039;&#039;&#039;&amp;lt;/div&amp;gt;&lt;br /&gt;
## {{level|Быстрое преобразование Фурье для степеней двойки}}&lt;br /&gt;
# &amp;lt;div id=&amp;quot;Алгебра многочленов&amp;quot;&amp;gt;&#039;&#039;&#039;Алгебра многочленов&#039;&#039;&#039;&amp;lt;/div&amp;gt;&lt;br /&gt;
## {{level|Схема Горнера, вещественная версия, последовательный вариант|Схема Горнера}}&lt;br /&gt;
# &amp;lt;div id=&amp;quot;Численные методы интегрирования&amp;quot;&amp;gt;&#039;&#039;&#039;Численные методы интегрирования&#039;&#039;&#039;&amp;lt;/div&amp;gt;&lt;br /&gt;
## {{level|Квадратурные формулы}}&lt;br /&gt;
## {{level|Квадратурные (кубатурные) методы численного интегрирования по отрезку (многомерному кубу)}}&lt;br /&gt;
### [[Квадратурные_(кубатурные)_методы_численного_интегрирования_по_отрезку_(многомерному_кубу)#Метод прямоугольников|Метод прямоугольников]]&lt;br /&gt;
### [[Квадратурные_(кубатурные)_методы_численного_интегрирования_по_отрезку_(многомерному_кубу)#Метод трапеций|Метод трапеций]]&lt;br /&gt;
### [[Квадратурные_(кубатурные)_методы_численного_интегрирования_по_отрезку_(многомерному_кубу)#Метод парабол (метод Симпсона)|Метод парабол (метод Симпсона)]]&lt;br /&gt;
### [[Квадратурные_(кубатурные)_методы_численного_интегрирования_по_отрезку_(многомерному_кубу)#Метод Гаусса|Метод Гаусса]]&lt;br /&gt;
# &amp;lt;div id=&amp;quot;Алгоритмы на графах&amp;quot;&amp;gt;&#039;&#039;&#039;Алгоритмы на графах&#039;&#039;&#039;&amp;lt;/div&amp;gt;&lt;br /&gt;
## Обход графа&lt;br /&gt;
### {{level|Поиск в ширину (BFS)}}&lt;br /&gt;
### {{level|Поиск в глубину (DFS)}}&lt;br /&gt;
## {{level|Поиск кратчайшего пути от одной вершины (SSSP)}}&lt;br /&gt;
### {{level|Поиск в ширину (BFS)}} (для невзвешенных графов)&lt;br /&gt;
### {{level|Алгоритм Дейкстры}}&lt;br /&gt;
### {{level|Алгоритм Беллмана-Форда}}&lt;br /&gt;
### {{level|Алгоритм Δ-шагания}}&lt;br /&gt;
## {{level|Поиск кратчайшего пути для всех пар вершин (APSP)}}&lt;br /&gt;
### {{level|Алгоритм Джонсона}}&lt;br /&gt;
### {{level|Алгоритм Флойда-Уоршелла}}&lt;br /&gt;
## {{level|Поиск транзитивного замыкания орграфа}}&lt;br /&gt;
### {{level|Алгоритм Пурдома}}&lt;br /&gt;
## {{level|Определение диаметра графа}}&lt;br /&gt;
## {{level|Построение минимального остовного дерева (MST)}}&lt;br /&gt;
### {{level|Алгоритм Борувки}}&lt;br /&gt;
### {{level|Алгоритм Крускала}}&lt;br /&gt;
### {{level|Алгоритм Прима}}&lt;br /&gt;
### {{level|Алгоритм GHS}}&lt;br /&gt;
## {{level|Поиск изоморфных подграфов}}&lt;br /&gt;
### {{level|Алгоритм Ульмана}}&lt;br /&gt;
### {{level|Алгоритм VF2}}&lt;br /&gt;
## {{level|Связность в графах}}&lt;br /&gt;
### {{level|Алгоритм Шилоаха-Вишкина поиска компонент связности}}&lt;br /&gt;
### {{level|Система непересекающихся множеств}}&lt;br /&gt;
### {{level|Алгоритм Тарьяна поиска компонент сильной связности}}&lt;br /&gt;
### {{level|Алгоритм DCSC поиска компонент сильной связности}}&lt;br /&gt;
### {{level|Алгоритм Тарьяна поиска компонент двусвязности}}&lt;br /&gt;
### {{level|Алгоритм Тарьяна-Вишкина поиска компонент двусвязности}}&lt;br /&gt;
### {{level|Алгоритм Тарьяна поиска «мостов» в графе}}&lt;br /&gt;
### {{level|Определение вершинной связности графа}}&lt;br /&gt;
### {{level|Алгоритм Габова определения рёберной связности графа}}&lt;br /&gt;
## {{level|Поиск максимального потока в транспортной сети}}&lt;br /&gt;
### {{level|Алгоритм Форда-Фалкерсона}}&lt;br /&gt;
### {{level|Алгоритм проталкивания предпотока}}&lt;br /&gt;
## {{level|Поиск потока минимальной стоимости в транспортной сети}}&lt;br /&gt;
## {{level|Задача о назначениях}}&lt;br /&gt;
### {{level|Венгерский алгоритм}}&lt;br /&gt;
### {{level|Алгоритм аукциона}}&lt;br /&gt;
### {{level|Алгоритм Гопкрофта-Карпа}}&lt;br /&gt;
## {{level|Вычисление betweenness centrality|Вычисление центральности вершин}}&lt;br /&gt;
# &amp;lt;div id=&amp;quot;Алгоритмы поиска&amp;quot;&amp;gt;&#039;&#039;&#039;Алгоритмы поиска&#039;&#039;&#039;&amp;lt;/div&amp;gt;&lt;br /&gt;
## {{level|Линейный поиск - находит элемент в любом списке|Линейный поиск}}, &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;&lt;br /&gt;
## {{level|Двоичный поиск - находит элемент в отсортированном списке|Двоичный поиск}}, &amp;lt;math&amp;gt;O(log(n))&amp;lt;/math&amp;gt;&lt;br /&gt;
# &amp;lt;div id=&amp;quot;Алгоритмы сортировки&amp;quot;&amp;gt;&#039;&#039;&#039;Алгоритмы сортировки&#039;&#039;&#039;&amp;lt;/div&amp;gt;&lt;br /&gt;
## {{level|Сортировка с помощью двоичного дерева}}&lt;br /&gt;
## {{level|Сортировка пузырьком}}&lt;br /&gt;
## {{level|Сортировка слиянием (последовательный и параллельный варианты)}}&lt;br /&gt;
# &amp;lt;div id=&amp;quot;Вычислительная геометрия&amp;quot;&amp;gt;&#039;&#039;&#039;Вычислительная геометрия&#039;&#039;&#039;&amp;lt;/div&amp;gt;&lt;br /&gt;
## {{level|Поиск диаметра множества точек}}&lt;br /&gt;
## {{level|Построение выпуклой оболочки набора точек}}&lt;br /&gt;
## {{level|Триангуляция Делоне}}&lt;br /&gt;
## {{level|Диаграмма Вороного}}&lt;br /&gt;
## {{level|Принадлежность точки многоугольнику}}&lt;br /&gt;
## {{level|Пересечения выпуклых многоугольников}} - трудоёмкость &amp;lt;math&amp;gt;O(n_1 + n_2)&amp;lt;/math&amp;gt;&lt;br /&gt;
## {{level|Пересечение звёздных многоугольников}} - трудоёмкость &amp;lt;math&amp;gt;O(n_1 * n_2)&amp;lt;/math&amp;gt;&lt;br /&gt;
# &amp;lt;div id=&amp;quot;Компьютерная графика&amp;quot;&amp;gt;&#039;&#039;&#039;Компьютерная графика&#039;&#039;&#039;&amp;lt;/div&amp;gt;&lt;br /&gt;
## {{level|Алгоритмы построения отрезка - алгоритмы для аппроксимации отрезка на дискретной графической поверхности}}&lt;br /&gt;
## {{level|Алгоритм определения видимых частей трёхмерной сцены}}&lt;br /&gt;
## {{level|Трассировка лучей - рендеринг реалистичных изображений}}&lt;br /&gt;
## {{level|Глобальное освещение - рассматривает прямое освещение и отражение от других объектов}}&lt;br /&gt;
# &amp;lt;div id=&amp;quot;Криптографические алгоритмы&amp;quot;&amp;gt;&#039;&#039;&#039;Криптографические алгоритмы&#039;&#039;&#039;&amp;lt;/div&amp;gt;&lt;br /&gt;
# &amp;lt;div id=&amp;quot;Нейронные сети&amp;quot;&amp;gt;&#039;&#039;&#039;Нейронные сети&#039;&#039;&#039;&amp;lt;/div&amp;gt;&lt;br /&gt;
# &amp;lt;div id=&amp;quot;Алгоритмы оптимизации&amp;quot;&amp;gt;&#039;&#039;&#039;Алгоритмы оптимизации&#039;&#039;&#039;&amp;lt;/div&amp;gt;&lt;br /&gt;
## {{level|Линейное программирование}}&lt;br /&gt;
## {{level|Симплекс-метод}}&lt;br /&gt;
## {{level|Метод ветвей и границ (последовательный и параллельный варианты)}}&lt;br /&gt;
## {{level|Генетические алгоритмы}}&lt;br /&gt;
## {{level|Муравьиные алгоритмы}}&lt;br /&gt;
## {{level|Комбинированные алгоритмы}}&lt;br /&gt;
## {{level|Нахождение экстремума функции}}&lt;br /&gt;
# &amp;lt;div id=&amp;quot;Алгоритмы теории игр&amp;quot;&amp;gt;&#039;&#039;&#039;Алгоритмы теории игр&#039;&#039;&#039;&amp;lt;/div&amp;gt;&lt;br /&gt;
# &amp;lt;div id=&amp;quot;Алгоритмы моделирования квантовых систем&amp;quot;&amp;gt;&#039;&#039;&#039;Алгоритмы моделирования квантовых систем&#039;&#039;&#039;&amp;lt;/div&amp;gt;&lt;br /&gt;
## &#039;&#039;Алгоритмы моделирования квантовых вычислений&#039;&#039;&lt;br /&gt;
### {{level|Однокубитное преобразование вектора-состояния}}&lt;br /&gt;
### {{level|Двухкубитное преобразование вектора-состояния}}&lt;br /&gt;
### {{level|Моделирование квантового преобразования Фурье}}&lt;br /&gt;
# &amp;lt;div id=&amp;quot;Алгоритмы решения уравнений математической физики&amp;quot;&amp;gt;&#039;&#039;&#039;Алгоритмы решения уравнений математической физики&#039;&#039;&#039;&amp;lt;/div&amp;gt;&lt;br /&gt;
## {{level|Уравнение Пуассона, решение дискретным преобразованием Фурье}}&lt;br /&gt;
# &amp;lt;div id=&amp;quot;Другие алгоритмы&amp;quot;&amp;gt;&#039;&#039;&#039;Другие алгоритмы&#039;&#039;&#039;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[en:Algorithm classification]]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Алгоритмы|*]]&lt;/div&gt;</summary>
		<author><name>Lineprinter</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%93%D0%BE%D1%82%D0%BE%D0%B2%D0%BD%D0%BE%D1%81%D1%82%D1%8C_%D1%81%D1%82%D0%B0%D1%82%D0%B5%D0%B9&amp;diff=15577</id>
		<title>Готовность статей</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%93%D0%BE%D1%82%D0%BE%D0%B2%D0%BD%D0%BE%D1%81%D1%82%D1%8C_%D1%81%D1%82%D0%B0%D1%82%D0%B5%D0%B9&amp;diff=15577"/>
		<updated>2016-11-01T15:11:36Z</updated>

		<summary type="html">&lt;p&gt;Lineprinter: Откат правок AntonChupin (обсуждение) к версии Nebaruzdin&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{| width=&amp;quot;100%&amp;quot;&lt;br /&gt;
|- valign=&amp;quot;top&amp;quot;&lt;br /&gt;
| &#039;&#039;&#039;Начатые статьи&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;DynamicPageList&amp;gt;&lt;br /&gt;
category = Начатые статьи&lt;br /&gt;
&amp;lt;/DynamicPageList&amp;gt;&lt;br /&gt;
| &#039;&#039;&#039;Статьи в работе&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;DynamicPageList&amp;gt;&lt;br /&gt;
category = Статьи в работе&lt;br /&gt;
&amp;lt;/DynamicPageList&amp;gt;&lt;br /&gt;
| &#039;&#039;&#039;Законченные статьи&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;DynamicPageList&amp;gt;&lt;br /&gt;
category = Законченные статьи&lt;br /&gt;
&amp;lt;/DynamicPageList&amp;gt;&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Lineprinter</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9A%D0%B0%D1%82%D0%B5%D0%B3%D0%BE%D1%80%D0%B8%D1%8F:%D0%A1%D1%82%D0%B0%D1%82%D1%8C%D0%B8_%D0%BF%D0%BE_%D0%B3%D0%BE%D1%82%D0%BE%D0%B2%D0%BD%D0%BE%D1%81%D1%82%D0%B8&amp;diff=15576</id>
		<title>Категория:Статьи по готовности</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%9A%D0%B0%D1%82%D0%B5%D0%B3%D0%BE%D1%80%D0%B8%D1%8F:%D0%A1%D1%82%D0%B0%D1%82%D1%8C%D0%B8_%D0%BF%D0%BE_%D0%B3%D0%BE%D1%82%D0%BE%D0%B2%D0%BD%D0%BE%D1%81%D1%82%D0%B8&amp;diff=15576"/>
		<updated>2016-11-01T15:11:32Z</updated>

		<summary type="html">&lt;p&gt;Lineprinter: Откат правок AntonChupin (обсуждение) к версии Nebaruzdin&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Подкатегории данной категории используются в шаблоне [[Шаблон:Main page/Readiness|Main page/Readiness]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[en:Category:Articles by readiness]]&lt;/div&gt;</summary>
		<author><name>Lineprinter</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A8%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD:Lang&amp;diff=15575</id>
		<title>Шаблон:Lang</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A8%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD:Lang&amp;diff=15575"/>
		<updated>2016-11-01T15:10:25Z</updated>

		<summary type="html">&lt;p&gt;Lineprinter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;span lang=&amp;quot;{{{1}}}&amp;quot; xml:lang=&amp;quot;{{{1}}}&amp;quot;&amp;gt;{{{2}}}&amp;lt;/span&amp;gt;&lt;/div&gt;</summary>
		<author><name>Lineprinter</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A8%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD:Lang-en&amp;diff=15574</id>
		<title>Шаблон:Lang-en</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A8%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD:Lang-en&amp;diff=15574"/>
		<updated>2016-11-01T15:10:17Z</updated>

		<summary type="html">&lt;p&gt;Lineprinter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[https://ru.wikipedia.org/wiki/английский_язык англ.]&amp;amp;nbsp;&#039;&#039;{{lang|en|{{{1}}}}}&#039;&#039;&lt;/div&gt;</summary>
		<author><name>Lineprinter</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A8%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD:Lang&amp;diff=15573</id>
		<title>Шаблон:Lang</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A8%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD:Lang&amp;diff=15573"/>
		<updated>2016-11-01T15:10:02Z</updated>

		<summary type="html">&lt;p&gt;Lineprinter: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;span lang=&amp;quot;{{{1}}}&amp;quot; xml:lang=&amp;quot;{{{1}}}&amp;quot;&amp;gt;{{{2}}}&amp;lt;/span&amp;gt;&amp;lt;noinclude&amp;gt;&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;/div&gt;</summary>
		<author><name>Lineprinter</name></author>
	</entry>
</feed>