<?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=Egor+Luckjanow+403</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=Egor+Luckjanow+403"/>
	<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/Egor_Luckjanow_403"/>
	<updated>2026-10-22T22:58:31Z</updated>
	<subtitle>Вклад</subtitle>
	<generator>MediaWiki 1.43.5</generator>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Egor_Luckjanow_403/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%93%D1%80%D0%B0%D0%BC%D0%BC%D0%B0-%D0%A8%D0%BC%D0%B8%D0%B4%D1%82%D0%B0&amp;diff=28765</id>
		<title>Участник:Egor Luckjanow 403/Алгоритм Грамма-Шмидта</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:Egor_Luckjanow_403/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%93%D1%80%D0%B0%D0%BC%D0%BC%D0%B0-%D0%A8%D0%BC%D0%B8%D0%B4%D1%82%D0%B0&amp;diff=28765"/>
		<updated>2021-12-12T18:03:48Z</updated>

		<summary type="html">&lt;p&gt;Egor Luckjanow 403: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Автор: Егор Лукьянов, студент 403 группы ВМК МГУ&lt;br /&gt;
== Свойства и структура алгоритмов ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
Алгоритм Грама-Шмидта предназначен для построения QR-разложения матрицы.&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
В основе работы алгоритма лежит метод Грама-Шмидта для построения ортонормированного базиса на основе уже существующего:&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;q_1 = \frac{a_1}{||a_1||}&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;p_2 = a_2 - (a_2, q_1)q_1&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;q_2 = \frac{p_2}{||p_2||}&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;p_3 = a_3 - (a_3, q_1)q_1 - (a_3, q_2)q_2&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;q_3 = \frac{p_3}{||p_3||}&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;...&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
Мы применяем этот метод к столбцам исходной матрицы A и получаем разложение A = QR, где Q составлена из столбцов q_1,...,q_n, полученных в результате применения метода Грама-Шмидта, а R -- матрица коэффициентов, которая будет верхнетреугольной в силу вида метода.&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Ядро алгоритма -- последовательное нахождение Q и R, как в математическом описании.&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
Макрооперации: скалярное произведение ddot; умножение матрицы на вектор с прибавлением другого вектора daxpy.&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;&lt;br /&gt;
void QR(int N, double* A, double* Q, double* R)&lt;br /&gt;
{&lt;br /&gt;
	int one = 1;&lt;br /&gt;
	for (int i = 0; i &amp;lt; N; ++i) {&lt;br /&gt;
		R[i * N + i] = 1;&lt;br /&gt;
		for (int k = 1; k &amp;lt; i + 1; ++k) {&lt;br /&gt;
			R[i - k + i * N] = ddot(&amp;amp;N, A + i * N, &amp;amp;one, Q + (i - k) * N, &amp;amp;one) / ddot(&amp;amp;N, Q + (i - k) * N, &amp;amp;one, Q + (i - k) * N, &amp;amp;one);&lt;br /&gt;
		}&lt;br /&gt;
		for (int j = 0; j &amp;lt; N; ++j) {&lt;br /&gt;
			Q[j + i * N] = A[j + i * N];&lt;br /&gt;
		}&lt;br /&gt;
		for (int k = 0; k &amp;lt; i; ++k) {&lt;br /&gt;
			double tmp = -R[i - k - 1 + i * N];&lt;br /&gt;
			daxpy(&amp;amp;N, &amp;amp;tmp, Q + (i - k - 1) * N, &amp;amp;one, Q + i * N, &amp;amp;one);&lt;br /&gt;
		}&lt;br /&gt;
		for (int k = i + 1; k &amp;lt; N; ++k) {&lt;br /&gt;
			R[k + i * N] = 0;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	for (int i = 0; i &amp;lt; N; ++i) {&lt;br /&gt;
		double norm = dnrm2(&amp;amp;N, Q + i * N, &amp;amp;one);&lt;br /&gt;
		for (int j = 0; j &amp;lt; N; ++j) {&lt;br /&gt;
			Q[j + i * N] /= norm;&lt;br /&gt;
		}&lt;br /&gt;
		for (int j = 0; j &amp;lt; N; ++j) {&lt;br /&gt;
			R[i + j * N] *= norm;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Сложность последовательного алгоритма -- &amp;lt;math&amp;gt;O(n^3)&amp;lt;/math&amp;gt;.&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
[[Файл:Информационный граффф.png|слева|Информационный граф]]&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&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;Q&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; из QR-разложения поданной на вход матрицы.&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
Алгоритм Грамма-Шмидта обладает свойством потери ортогональности. Поэтому для корректной его реализации необходимо время от времени делать реортогонализацию, а также обеспечить максимально возможную точность вычислений с плавающей точкой.&lt;br /&gt;
Можно реализовать алгоритм для работы с рациональными матрицами. Тогда проблема с потерей ортогонализации пропадает (в виду идеальной точности вычислений в кольцах вида &amp;lt;math&amp;gt;\mathbb{Z}/&amp;lt;m&amp;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;
Есть вариант отдать каждому процессу по группе столбцов &amp;lt;math&amp;gt;q_i&amp;lt;/math&amp;gt; для вычисления.&lt;br /&gt;
Число пересылок тогда составляет &amp;lt;math&amp;gt;n(n+1)/2&amp;lt;/math&amp;gt;.&lt;br /&gt;
==== Код возможной реализации ====&lt;br /&gt;
&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;iostream&amp;gt;&lt;br /&gt;
#include &amp;lt;mpi.h&amp;gt;&lt;br /&gt;
#include &amp;lt;omp.h&amp;gt;&lt;br /&gt;
#include &amp;lt;mkl.h&amp;gt;&lt;br /&gt;
#include &amp;lt;cstdlib&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define world MPI_COMM_WORLD&lt;br /&gt;
&lt;br /&gt;
void gather(int n, double *A, int id, double *q, double *r, int nmon)&lt;br /&gt;
{&lt;br /&gt;
	double *Q = new double[n * n], *R = new double[n * n];&lt;br /&gt;
	MPI_Gather(q, n * nmon, MPI_DOUBLE, Q, n * nmon, MPI_DOUBLE, 0, world);&lt;br /&gt;
	MPI_Gather(r, n * nmon, MPI_DOUBLE, R, n * nmon, MPI_DOUBLE, 0, world);&lt;br /&gt;
	if (id != 0)&lt;br /&gt;
		return;&lt;br /&gt;
	#pragma omp parallel for&lt;br /&gt;
	for (int i = 0; i &amp;lt; n; ++i)&lt;br /&gt;
	{&lt;br /&gt;
		int ione = 1;&lt;br /&gt;
		double norm = dnrm2(&amp;amp;n, Q + i * n, &amp;amp;ione);&lt;br /&gt;
		for (int j = 0; j &amp;lt; n; ++j)&lt;br /&gt;
		{&lt;br /&gt;
			Q[j + i * n] /= norm;&lt;br /&gt;
			R[i + j * n] *= norm;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	double one = 1;&lt;br /&gt;
	double zero = 0;&lt;br /&gt;
	char trans = &#039;N&#039;;&lt;br /&gt;
	double *tmpA = new double[n * n];&lt;br /&gt;
	dgemm(&amp;amp;trans, &amp;amp;trans, &amp;amp;n, &amp;amp;n, &amp;amp;n, &amp;amp;one, Q, &amp;amp;n, R, &amp;amp;n, &amp;amp;zero, tmpA, &amp;amp;n);&lt;br /&gt;
	int n2 = n * n;&lt;br /&gt;
	double mone = -1;&lt;br /&gt;
	int ione = 1;&lt;br /&gt;
	daxpy(&amp;amp;n2, &amp;amp;mone, A, &amp;amp;ione, tmpA, &amp;amp;ione);&lt;br /&gt;
	std:: cout &amp;lt;&amp;lt; dnrm2(&amp;amp;n2, tmpA, &amp;amp;ione) &amp;lt;&amp;lt; std::endl &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
	char trans2 = &#039;T&#039;;&lt;br /&gt;
	dgemm(&amp;amp;trans, &amp;amp;trans2, &amp;amp;n, &amp;amp;n, &amp;amp;n, &amp;amp;one, Q, &amp;amp;n, Q, &amp;amp;n, &amp;amp;zero, A, &amp;amp;n);&lt;br /&gt;
	double *I = new double[n * n];&lt;br /&gt;
	for (int i = 0; i &amp;lt; n; ++i)&lt;br /&gt;
	for (int j = 0; j &amp;lt; n; ++j)&lt;br /&gt;
		if (i == j)&lt;br /&gt;
			I[j + i * n] = 1;&lt;br /&gt;
		else&lt;br /&gt;
			I[j + i * n] = 0;&lt;br /&gt;
	daxpy(&amp;amp;n2, &amp;amp;mone, I, &amp;amp;ione, A, &amp;amp;ione);&lt;br /&gt;
	std::cout &amp;lt;&amp;lt; dnrm2(&amp;amp;n2, A, &amp;amp;ione) &amp;lt;&amp;lt; std::endl &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char *argv[])&lt;br /&gt;
{&lt;br /&gt;
	int rinit;&lt;br /&gt;
	MPI_Init(&amp;amp;argc, &amp;amp;argv);&lt;br /&gt;
	sscanf(argv[1], &amp;quot;%d&amp;quot;, &amp;amp;rinit);&lt;br /&gt;
	srand(rinit);&lt;br /&gt;
	int nproc;&lt;br /&gt;
	MPI_Comm_size(world, &amp;amp;nproc);&lt;br /&gt;
	int nmon = 20;&lt;br /&gt;
	int n = nmon * nproc;&lt;br /&gt;
	MPI_Status status;&lt;br /&gt;
	double *A = new double[n * n];&lt;br /&gt;
	#pragma omp parallel for&lt;br /&gt;
	for (int i = 0; i &amp;lt; n; ++i)&lt;br /&gt;
	for (int j = 0; j &amp;lt; n; ++j)&lt;br /&gt;
		if (i == j)&lt;br /&gt;
			A[j + i * n] = rand();&lt;br /&gt;
		else&lt;br /&gt;
			A[j + i * n] = rand();&lt;br /&gt;
	int id;&lt;br /&gt;
	int ione = 1;&lt;br /&gt;
	MPI_Comm_rank(world, &amp;amp;id);&lt;br /&gt;
	double *s = new double[n * nmon];&lt;br /&gt;
	double *q = new double[n * nmon], *r = new double[n * nmon];&lt;br /&gt;
	for (int i1 = 0; i1 &amp;lt; nmon; ++i1)&lt;br /&gt;
	for (int i = 0; i &amp;lt; n; ++i)&lt;br /&gt;
		q[i1 * n + i] = A[i + (id * nmon + i1) * n];&lt;br /&gt;
	for (int i1 = 0; i1 &amp;lt; nmon; ++i1)&lt;br /&gt;
	{&lt;br /&gt;
		for (int i = 0; i &amp;lt; id; ++i)&lt;br /&gt;
		{&lt;br /&gt;
		MPI_Recv(s, n * nmon, MPI_DOUBLE, i, 1, world, &amp;amp;status);&lt;br /&gt;
		for (int j = 0; j &amp;lt; nmon; ++j)&lt;br /&gt;
		{&lt;br /&gt;
			double arg = -ddot(&amp;amp;n, A + (id * nmon + i1) * n, &amp;amp;ione, s + j * n, &amp;amp;ione) / ddot(&amp;amp;n, s + j * n, &amp;amp;ione, s + j * n, &amp;amp;ione);&lt;br /&gt;
			daxpy(&amp;amp;n, &amp;amp;arg, s + j * n, &amp;amp;ione, q + i1 * n, &amp;amp;ione);&lt;br /&gt;
			r[i1 * n + i * nmon + j] = -arg;&lt;br /&gt;
		}&lt;br /&gt;
		}&lt;br /&gt;
		for (int i = 0; i &amp;lt; i1; ++i)&lt;br /&gt;
		{&lt;br /&gt;
			double arg = -ddot(&amp;amp;n, A + (id * nmon + i1) * n, &amp;amp;ione, q + i * n, &amp;amp;ione) / ddot(&amp;amp;n, q + i * n, &amp;amp;ione, q + i * n, &amp;amp;ione);&lt;br /&gt;
			daxpy(&amp;amp;n, &amp;amp;arg, q + i * n, &amp;amp;ione, q + i1 * n, &amp;amp;ione);&lt;br /&gt;
			r[i1 * n + id * nmon + i] = -arg;&lt;br /&gt;
&lt;br /&gt;
		}&lt;br /&gt;
		r[i1 * n + id * nmon + i1] = 1;&lt;br /&gt;
		for (int i = id * nmon + i1 + 1; i &amp;lt; n; ++i)&lt;br /&gt;
			r[i1 * n + i] = 0;&lt;br /&gt;
		MPI_Request req;&lt;br /&gt;
		#pragma omp parallel for&lt;br /&gt;
		for (int i1 = 0; i1 &amp;lt; nmon; ++i1)&lt;br /&gt;
		for (int i = id + 1; i &amp;lt; nproc; ++i)&lt;br /&gt;
			MPI_Isend(q, n * nmon, MPI_DOUBLE, i, 1, world, &amp;amp;req);&lt;br /&gt;
	}&lt;br /&gt;
	MPI_Barrier(world);&lt;br /&gt;
	gather(n, A, id, q, r, nmon);&lt;br /&gt;
	MPI_Finalize();&lt;br /&gt;
	return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
==== Результаты работы данного кода ====&lt;br /&gt;
[[Файл:Результаты запусков.png|никакой|слева|Результаты запусков]]&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
Алгоритм увеличивает время своей работы при добавлении независимых вычислительных ядер.&lt;br /&gt;
Алгоритм обладает сильной масштабируемостью.&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
При выборе системы для запуска предпочтение следует отдавать скорости обмена данными между процессами.&lt;br /&gt;
При выборе архитектуры необходимо учесть наличие возможности вычислений с высокой точностью.&lt;br /&gt;
Параллельный алгоритм не дает разительного превосходства по времени исполнения в сравнении с последовательным, но все же имеет место заметное уменьшение времени исполнения при выполнении указанных условий.&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
Реализация архитектурно независима. Зависимы только макрооперации, которые предполагаются уже реализованными.&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
В силу потери ортогональности алгоритм Грамма-Шмидта пользуется небольшой популярностью в сравнении с альтернативными методами QR-разложения, так что популярных параллельных реализаций нет.&lt;br /&gt;
Альтернативный (не Грамм-Шмидт) метод представлен, например, в пакете LAPACK и является одним из лучших по производительности.&lt;br /&gt;
== Литература ==&lt;br /&gt;
#Е. Е. Тыртышников, Методы численного анализа&lt;br /&gt;
#Ф. Р. Гантмахер, Теория матриц&lt;br /&gt;
#С. Ленг, Алгебра&lt;br /&gt;
#Е. Е. Тыртышников, Основы алгебры&lt;/div&gt;</summary>
		<author><name>Egor Luckjanow 403</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:Egor_Luckjanow_403/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%93%D1%80%D0%B0%D0%BC%D0%BC%D0%B0-%D0%A8%D0%BC%D0%B8%D0%B4%D1%82%D0%B0&amp;diff=28764</id>
		<title>Участник:Egor Luckjanow 403/Алгоритм Грамма-Шмидта</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:Egor_Luckjanow_403/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%93%D1%80%D0%B0%D0%BC%D0%BC%D0%B0-%D0%A8%D0%BC%D0%B8%D0%B4%D1%82%D0%B0&amp;diff=28764"/>
		<updated>2021-12-12T17:58:15Z</updated>

		<summary type="html">&lt;p&gt;Egor Luckjanow 403: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Автор: Егор Лукьянов, студент 403 группы ВМК МГУ&lt;br /&gt;
== Свойства и структура алгоритмов ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
Алгоритм Грама-Шмидта предназначен для построения QR-разложения матрицы.&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
В основе работы алгоритма лежит метод Грама-Шмидта для построения ортонормированного базиса на основе уже существующего:&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;q_1 = \frac{a_1}{||a_1||}&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;p_2 = a_2 - (a_2, q_1)q_1&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;q_2 = \frac{p_2}{||p_2||}&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;p_3 = a_3 - (a_3, q_1)q_1 - (a_3, q_2)q_2&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;q_3 = \frac{p_3}{||p_3||}&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;...&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
Мы применяем этот метод к столбцам исходной матрицы A и получаем разложение A = QR, где Q составлена из столбцов q_1,...,q_n, полученных в результате применения метода Грама-Шмидта, а R -- матрица коэффициентов, которая будет верхнетреугольной в силу вида метода.&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Ядро алгоритма -- последовательное нахождение Q и R, как в математическом описании.&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
Макрооперации: скалярное произведение ddot; умножение матрицы на вектор с прибавлением другого вектора daxpy.&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;&lt;br /&gt;
void QR(int N, double* A, double* Q, double* R)&lt;br /&gt;
{&lt;br /&gt;
	int one = 1;&lt;br /&gt;
	for (int i = 0; i &amp;lt; N; ++i) {&lt;br /&gt;
		R[i * N + i] = 1;&lt;br /&gt;
		for (int k = 1; k &amp;lt; i + 1; ++k) {&lt;br /&gt;
			R[i - k + i * N] = ddot(&amp;amp;N, A + i * N, &amp;amp;one, Q + (i - k) * N, &amp;amp;one) / ddot(&amp;amp;N, Q + (i - k) * N, &amp;amp;one, Q + (i - k) * N, &amp;amp;one);&lt;br /&gt;
		}&lt;br /&gt;
		for (int j = 0; j &amp;lt; N; ++j) {&lt;br /&gt;
			Q[j + i * N] = A[j + i * N];&lt;br /&gt;
		}&lt;br /&gt;
		for (int k = 0; k &amp;lt; i; ++k) {&lt;br /&gt;
			double tmp = -R[i - k - 1 + i * N];&lt;br /&gt;
			daxpy(&amp;amp;N, &amp;amp;tmp, Q + (i - k - 1) * N, &amp;amp;one, Q + i * N, &amp;amp;one);&lt;br /&gt;
		}&lt;br /&gt;
		for (int k = i + 1; k &amp;lt; N; ++k) {&lt;br /&gt;
			R[k + i * N] = 0;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	for (int i = 0; i &amp;lt; N; ++i) {&lt;br /&gt;
		double norm = dnrm2(&amp;amp;N, Q + i * N, &amp;amp;one);&lt;br /&gt;
		for (int j = 0; j &amp;lt; N; ++j) {&lt;br /&gt;
			Q[j + i * N] /= norm;&lt;br /&gt;
		}&lt;br /&gt;
		for (int j = 0; j &amp;lt; N; ++j) {&lt;br /&gt;
			R[i + j * N] *= norm;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Сложность последовательного алгоритма -- &amp;lt;math&amp;gt;O(n^3)&amp;lt;/math&amp;gt;.&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
[[Файл:Информационный граффф.png|слева|Информационный граф]]&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&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;Q&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; из QR-разложения поданной на вход матрицы.&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
Алгоритм Грамма-Шмидта обладает свойством потери ортогональности. Поэтому для корректной его реализации необходимо время от времени делать реортогонализацию, а также обеспечить максимально возможную точность вычислений с плавающей точкой.&lt;br /&gt;
Можно реализовать алгоритм для работы с рациональными матрицами. Тогда проблема с потерей ортогонализации пропадает (в виду идеальной точности вычислений в кольцах вида &amp;lt;math&amp;gt;\mathbb{Z}/&amp;lt;m&amp;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;q_i&amp;lt;/math&amp;gt; для вычисления.&lt;br /&gt;
Число пересылок тогда составляет &amp;lt;math&amp;gt;n(n+1)/2&amp;lt;/math&amp;gt;.&lt;br /&gt;
==== Код возможной реализации ====&lt;br /&gt;
&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;iostream&amp;gt;&lt;br /&gt;
#include &amp;lt;mpi.h&amp;gt;&lt;br /&gt;
#include &amp;lt;omp.h&amp;gt;&lt;br /&gt;
#include &amp;lt;mkl.h&amp;gt;&lt;br /&gt;
#include &amp;lt;cstdlib&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define world MPI_COMM_WORLD&lt;br /&gt;
&lt;br /&gt;
void gather(int n, double *A, int id, double *q, double *r, int nmon)&lt;br /&gt;
{&lt;br /&gt;
	double *Q = new double[n * n], *R = new double[n * n];&lt;br /&gt;
	MPI_Gather(q, n * nmon, MPI_DOUBLE, Q, n * nmon, MPI_DOUBLE, 0, world);&lt;br /&gt;
	MPI_Gather(r, n * nmon, MPI_DOUBLE, R, n * nmon, MPI_DOUBLE, 0, world);&lt;br /&gt;
	if (id != 0)&lt;br /&gt;
		return;&lt;br /&gt;
	#pragma omp parallel for&lt;br /&gt;
	for (int i = 0; i &amp;lt; n; ++i)&lt;br /&gt;
	{&lt;br /&gt;
		int ione = 1;&lt;br /&gt;
		double norm = dnrm2(&amp;amp;n, Q + i * n, &amp;amp;ione);&lt;br /&gt;
		for (int j = 0; j &amp;lt; n; ++j)&lt;br /&gt;
		{&lt;br /&gt;
			Q[j + i * n] /= norm;&lt;br /&gt;
			R[i + j * n] *= norm;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	double one = 1;&lt;br /&gt;
	double zero = 0;&lt;br /&gt;
	char trans = &#039;N&#039;;&lt;br /&gt;
	double *tmpA = new double[n * n];&lt;br /&gt;
	dgemm(&amp;amp;trans, &amp;amp;trans, &amp;amp;n, &amp;amp;n, &amp;amp;n, &amp;amp;one, Q, &amp;amp;n, R, &amp;amp;n, &amp;amp;zero, tmpA, &amp;amp;n);&lt;br /&gt;
	int n2 = n * n;&lt;br /&gt;
	double mone = -1;&lt;br /&gt;
	int ione = 1;&lt;br /&gt;
	daxpy(&amp;amp;n2, &amp;amp;mone, A, &amp;amp;ione, tmpA, &amp;amp;ione);&lt;br /&gt;
	std:: cout &amp;lt;&amp;lt; dnrm2(&amp;amp;n2, tmpA, &amp;amp;ione) &amp;lt;&amp;lt; std::endl &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
	char trans2 = &#039;T&#039;;&lt;br /&gt;
	dgemm(&amp;amp;trans, &amp;amp;trans2, &amp;amp;n, &amp;amp;n, &amp;amp;n, &amp;amp;one, Q, &amp;amp;n, Q, &amp;amp;n, &amp;amp;zero, A, &amp;amp;n);&lt;br /&gt;
	double *I = new double[n * n];&lt;br /&gt;
	for (int i = 0; i &amp;lt; n; ++i)&lt;br /&gt;
	for (int j = 0; j &amp;lt; n; ++j)&lt;br /&gt;
		if (i == j)&lt;br /&gt;
			I[j + i * n] = 1;&lt;br /&gt;
		else&lt;br /&gt;
			I[j + i * n] = 0;&lt;br /&gt;
	daxpy(&amp;amp;n2, &amp;amp;mone, I, &amp;amp;ione, A, &amp;amp;ione);&lt;br /&gt;
	std::cout &amp;lt;&amp;lt; dnrm2(&amp;amp;n2, A, &amp;amp;ione) &amp;lt;&amp;lt; std::endl &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char *argv[])&lt;br /&gt;
{&lt;br /&gt;
	int rinit;&lt;br /&gt;
	MPI_Init(&amp;amp;argc, &amp;amp;argv);&lt;br /&gt;
	sscanf(argv[1], &amp;quot;%d&amp;quot;, &amp;amp;rinit);&lt;br /&gt;
	srand(rinit);&lt;br /&gt;
	int nproc;&lt;br /&gt;
	MPI_Comm_size(world, &amp;amp;nproc);&lt;br /&gt;
	int nmon = 20;&lt;br /&gt;
	int n = nmon * nproc;&lt;br /&gt;
	MPI_Status status;&lt;br /&gt;
	double *A = new double[n * n];&lt;br /&gt;
	#pragma omp parallel for&lt;br /&gt;
	for (int i = 0; i &amp;lt; n; ++i)&lt;br /&gt;
	for (int j = 0; j &amp;lt; n; ++j)&lt;br /&gt;
		if (i == j)&lt;br /&gt;
			A[j + i * n] = rand();&lt;br /&gt;
		else&lt;br /&gt;
			A[j + i * n] = rand();&lt;br /&gt;
	int id;&lt;br /&gt;
	int ione = 1;&lt;br /&gt;
	MPI_Comm_rank(world, &amp;amp;id);&lt;br /&gt;
	double *s = new double[n * nmon];&lt;br /&gt;
	double *q = new double[n * nmon], *r = new double[n * nmon];&lt;br /&gt;
	for (int i1 = 0; i1 &amp;lt; nmon; ++i1)&lt;br /&gt;
	for (int i = 0; i &amp;lt; n; ++i)&lt;br /&gt;
		q[i1 * n + i] = A[i + (id * nmon + i1) * n];&lt;br /&gt;
	for (int i1 = 0; i1 &amp;lt; nmon; ++i1)&lt;br /&gt;
	{&lt;br /&gt;
		for (int i = 0; i &amp;lt; id; ++i)&lt;br /&gt;
		{&lt;br /&gt;
		MPI_Recv(s, n * nmon, MPI_DOUBLE, i, 1, world, &amp;amp;status);&lt;br /&gt;
		for (int j = 0; j &amp;lt; nmon; ++j)&lt;br /&gt;
		{&lt;br /&gt;
			double arg = -ddot(&amp;amp;n, A + (id * nmon + i1) * n, &amp;amp;ione, s + j * n, &amp;amp;ione) / ddot(&amp;amp;n, s + j * n, &amp;amp;ione, s + j * n, &amp;amp;ione);&lt;br /&gt;
			daxpy(&amp;amp;n, &amp;amp;arg, s + j * n, &amp;amp;ione, q + i1 * n, &amp;amp;ione);&lt;br /&gt;
			r[i1 * n + i * nmon + j] = -arg;&lt;br /&gt;
		}&lt;br /&gt;
		}&lt;br /&gt;
		for (int i = 0; i &amp;lt; i1; ++i)&lt;br /&gt;
		{&lt;br /&gt;
			double arg = -ddot(&amp;amp;n, A + (id * nmon + i1) * n, &amp;amp;ione, q + i * n, &amp;amp;ione) / ddot(&amp;amp;n, q + i * n, &amp;amp;ione, q + i * n, &amp;amp;ione);&lt;br /&gt;
			daxpy(&amp;amp;n, &amp;amp;arg, q + i * n, &amp;amp;ione, q + i1 * n, &amp;amp;ione);&lt;br /&gt;
			r[i1 * n + id * nmon + i] = -arg;&lt;br /&gt;
&lt;br /&gt;
		}&lt;br /&gt;
		r[i1 * n + id * nmon + i1] = 1;&lt;br /&gt;
		for (int i = id * nmon + i1 + 1; i &amp;lt; n; ++i)&lt;br /&gt;
			r[i1 * n + i] = 0;&lt;br /&gt;
		MPI_Request req;&lt;br /&gt;
		#pragma omp parallel for&lt;br /&gt;
		for (int i1 = 0; i1 &amp;lt; nmon; ++i1)&lt;br /&gt;
		for (int i = id + 1; i &amp;lt; nproc; ++i)&lt;br /&gt;
			MPI_Isend(q, n * nmon, MPI_DOUBLE, i, 1, world, &amp;amp;req);&lt;br /&gt;
	}&lt;br /&gt;
	MPI_Barrier(world);&lt;br /&gt;
	gather(n, A, id, q, r, nmon);&lt;br /&gt;
	MPI_Finalize();&lt;br /&gt;
	return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
==== Результаты работы данного кода ====&lt;br /&gt;
[[Файл:Результаты запусков.png|никакой|слева|Результаты запусков]]&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
Алгоритм увеличивает время своей работы при добавлении независимых вычислительных ядер.&lt;br /&gt;
Алгоритм обладает сильной масштабируемостью.&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
При выборе системы для запуска предпочтение следует отдавать скорости обмена данными между процессами.&lt;br /&gt;
При выборе архитектуры необходимо учесть наличие возможности вычислений с высокой точностью.&lt;br /&gt;
Параллельный алгоритм не дает разительного превосходства по времени исполнения в сравнении с последовательным, но все же имеет место заметное уменьшение времени исполнения при выполнении указанных условий.&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
Реализация архитектурно независима. Зависимы только макрооперации, которые предполагаются уже реализованными.&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
В силу потери ортогональности алгоритм Грамма-Шмидта пользуется небольшой популярностью в сравнении с альтернативными методами QR-разложения, так что популярных параллельных реализаций нет.&lt;br /&gt;
Альтернативный (не Грамм-Шмидт) метод представлен, например, в пакете LAPACK и является одним из лучших по производительности.&lt;br /&gt;
== Литература ==&lt;br /&gt;
#Е. Е. Тыртышников, Методы численного анализа&lt;br /&gt;
#Ф. Р. Гантмахер, Теория матриц&lt;br /&gt;
#С. Ленг, Алгебра&lt;br /&gt;
#Е. Е. Тыртышников, Основы алгебры&lt;/div&gt;</summary>
		<author><name>Egor Luckjanow 403</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:%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%D1%84%D1%84.png&amp;diff=28763</id>
		<title>Файл:Информационный граффф.png</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:%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%D1%84%D1%84.png&amp;diff=28763"/>
		<updated>2021-12-12T17:57:02Z</updated>

		<summary type="html">&lt;p&gt;Egor Luckjanow 403: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Информационный граффф&lt;/div&gt;</summary>
		<author><name>Egor Luckjanow 403</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:Egor_Luckjanow_403/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%93%D1%80%D0%B0%D0%BC%D0%BC%D0%B0-%D0%A8%D0%BC%D0%B8%D0%B4%D1%82%D0%B0&amp;diff=28762</id>
		<title>Участник:Egor Luckjanow 403/Алгоритм Грамма-Шмидта</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:Egor_Luckjanow_403/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%93%D1%80%D0%B0%D0%BC%D0%BC%D0%B0-%D0%A8%D0%BC%D0%B8%D0%B4%D1%82%D0%B0&amp;diff=28762"/>
		<updated>2021-12-12T17:07:28Z</updated>

		<summary type="html">&lt;p&gt;Egor Luckjanow 403: /* Результаты работы данного кода */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Автор: Егор Лукьянов, студент 403 группы ВМК МГУ&lt;br /&gt;
== Свойства и структура алгоритмов ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
Алгоритм Грама-Шмидта предназначен для построения QR-разложения матрицы.&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
В основе работы алгоритма лежит метод Грама-Шмидта для построения ортонормированного базиса на основе уже существующего:&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;q_1 = \frac{a_1}{||a_1||}&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;p_2 = a_2 - (a_2, q_1)q_1&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;q_2 = \frac{p_2}{||p_2||}&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;p_3 = a_3 - (a_3, q_1)q_1 - (a_3, q_2)q_2&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;q_3 = \frac{p_3}{||p_3||}&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;...&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
Мы применяем этот метод к столбцам исходной матрицы A и получаем разложение A = QR, где Q составлена из столбцов q_1,...,q_n, полученных в результате применения метода Грама-Шмидта, а R -- матрица коэффициентов, которая будет верхнетреугольной в силу вида метода.&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Ядро алгоритма -- последовательное нахождение Q и R, как в математическом описании.&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
Макрооперации: скалярное произведение ddot; умножение матрицы на вектор с прибавлением другого вектора daxpy.&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;&lt;br /&gt;
void QR(int N, double* A, double* Q, double* R)&lt;br /&gt;
{&lt;br /&gt;
	int one = 1;&lt;br /&gt;
	for (int i = 0; i &amp;lt; N; ++i) {&lt;br /&gt;
		R[i * N + i] = 1;&lt;br /&gt;
		for (int k = 1; k &amp;lt; i + 1; ++k) {&lt;br /&gt;
			R[i - k + i * N] = ddot(&amp;amp;N, A + i * N, &amp;amp;one, Q + (i - k) * N, &amp;amp;one) / ddot(&amp;amp;N, Q + (i - k) * N, &amp;amp;one, Q + (i - k) * N, &amp;amp;one);&lt;br /&gt;
		}&lt;br /&gt;
		for (int j = 0; j &amp;lt; N; ++j) {&lt;br /&gt;
			Q[j + i * N] = A[j + i * N];&lt;br /&gt;
		}&lt;br /&gt;
		for (int k = 0; k &amp;lt; i; ++k) {&lt;br /&gt;
			double tmp = -R[i - k - 1 + i * N];&lt;br /&gt;
			daxpy(&amp;amp;N, &amp;amp;tmp, Q + (i - k - 1) * N, &amp;amp;one, Q + i * N, &amp;amp;one);&lt;br /&gt;
		}&lt;br /&gt;
		for (int k = i + 1; k &amp;lt; N; ++k) {&lt;br /&gt;
			R[k + i * N] = 0;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	for (int i = 0; i &amp;lt; N; ++i) {&lt;br /&gt;
		double norm = dnrm2(&amp;amp;N, Q + i * N, &amp;amp;one);&lt;br /&gt;
		for (int j = 0; j &amp;lt; N; ++j) {&lt;br /&gt;
			Q[j + i * N] /= norm;&lt;br /&gt;
		}&lt;br /&gt;
		for (int j = 0; j &amp;lt; N; ++j) {&lt;br /&gt;
			R[i + j * N] *= norm;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Сложность последовательного алгоритма -- &amp;lt;math&amp;gt;O(n^3)&amp;lt;/math&amp;gt;.&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
Параллельная сложность (в терминах операций сложения и умножения) -- &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;Q&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; из QR-разложения поданной на вход матрицы.&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
Алгоритм Грамма-Шмидта обладает свойством потери ортогональности. Поэтому для корректной его реализации необходимо время от времени делать реортогонализацию, а также обеспечить максимально возможную точность вычислений с плавающей точкой.&lt;br /&gt;
Можно реализовать алгоритм для работы с рациональными матрицами. Тогда проблема с потерей ортогонализации пропадает (в виду идеальной точности вычислений в кольцах вида &amp;lt;math&amp;gt;\mathbb{Z}/&amp;lt;m&amp;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;q_i&amp;lt;/math&amp;gt; для вычисления.&lt;br /&gt;
Число пересылок тогда составляет &amp;lt;math&amp;gt;n(n+1)/2&amp;lt;/math&amp;gt;.&lt;br /&gt;
==== Код возможной реализации ====&lt;br /&gt;
&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;iostream&amp;gt;&lt;br /&gt;
#include &amp;lt;mpi.h&amp;gt;&lt;br /&gt;
#include &amp;lt;omp.h&amp;gt;&lt;br /&gt;
#include &amp;lt;mkl.h&amp;gt;&lt;br /&gt;
#include &amp;lt;cstdlib&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define world MPI_COMM_WORLD&lt;br /&gt;
&lt;br /&gt;
void gather(int n, double *A, int id, double *q, double *r, int nmon)&lt;br /&gt;
{&lt;br /&gt;
	double *Q = new double[n * n], *R = new double[n * n];&lt;br /&gt;
	MPI_Gather(q, n * nmon, MPI_DOUBLE, Q, n * nmon, MPI_DOUBLE, 0, world);&lt;br /&gt;
	MPI_Gather(r, n * nmon, MPI_DOUBLE, R, n * nmon, MPI_DOUBLE, 0, world);&lt;br /&gt;
	if (id != 0)&lt;br /&gt;
		return;&lt;br /&gt;
	#pragma omp parallel for&lt;br /&gt;
	for (int i = 0; i &amp;lt; n; ++i)&lt;br /&gt;
	{&lt;br /&gt;
		int ione = 1;&lt;br /&gt;
		double norm = dnrm2(&amp;amp;n, Q + i * n, &amp;amp;ione);&lt;br /&gt;
		for (int j = 0; j &amp;lt; n; ++j)&lt;br /&gt;
		{&lt;br /&gt;
			Q[j + i * n] /= norm;&lt;br /&gt;
			R[i + j * n] *= norm;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	double one = 1;&lt;br /&gt;
	double zero = 0;&lt;br /&gt;
	char trans = &#039;N&#039;;&lt;br /&gt;
	double *tmpA = new double[n * n];&lt;br /&gt;
	dgemm(&amp;amp;trans, &amp;amp;trans, &amp;amp;n, &amp;amp;n, &amp;amp;n, &amp;amp;one, Q, &amp;amp;n, R, &amp;amp;n, &amp;amp;zero, tmpA, &amp;amp;n);&lt;br /&gt;
	int n2 = n * n;&lt;br /&gt;
	double mone = -1;&lt;br /&gt;
	int ione = 1;&lt;br /&gt;
	daxpy(&amp;amp;n2, &amp;amp;mone, A, &amp;amp;ione, tmpA, &amp;amp;ione);&lt;br /&gt;
	std:: cout &amp;lt;&amp;lt; dnrm2(&amp;amp;n2, tmpA, &amp;amp;ione) &amp;lt;&amp;lt; std::endl &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
	char trans2 = &#039;T&#039;;&lt;br /&gt;
	dgemm(&amp;amp;trans, &amp;amp;trans2, &amp;amp;n, &amp;amp;n, &amp;amp;n, &amp;amp;one, Q, &amp;amp;n, Q, &amp;amp;n, &amp;amp;zero, A, &amp;amp;n);&lt;br /&gt;
	double *I = new double[n * n];&lt;br /&gt;
	for (int i = 0; i &amp;lt; n; ++i)&lt;br /&gt;
	for (int j = 0; j &amp;lt; n; ++j)&lt;br /&gt;
		if (i == j)&lt;br /&gt;
			I[j + i * n] = 1;&lt;br /&gt;
		else&lt;br /&gt;
			I[j + i * n] = 0;&lt;br /&gt;
	daxpy(&amp;amp;n2, &amp;amp;mone, I, &amp;amp;ione, A, &amp;amp;ione);&lt;br /&gt;
	std::cout &amp;lt;&amp;lt; dnrm2(&amp;amp;n2, A, &amp;amp;ione) &amp;lt;&amp;lt; std::endl &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char *argv[])&lt;br /&gt;
{&lt;br /&gt;
	int rinit;&lt;br /&gt;
	MPI_Init(&amp;amp;argc, &amp;amp;argv);&lt;br /&gt;
	sscanf(argv[1], &amp;quot;%d&amp;quot;, &amp;amp;rinit);&lt;br /&gt;
	srand(rinit);&lt;br /&gt;
	int nproc;&lt;br /&gt;
	MPI_Comm_size(world, &amp;amp;nproc);&lt;br /&gt;
	int nmon = 20;&lt;br /&gt;
	int n = nmon * nproc;&lt;br /&gt;
	MPI_Status status;&lt;br /&gt;
	double *A = new double[n * n];&lt;br /&gt;
	#pragma omp parallel for&lt;br /&gt;
	for (int i = 0; i &amp;lt; n; ++i)&lt;br /&gt;
	for (int j = 0; j &amp;lt; n; ++j)&lt;br /&gt;
		if (i == j)&lt;br /&gt;
			A[j + i * n] = rand();&lt;br /&gt;
		else&lt;br /&gt;
			A[j + i * n] = rand();&lt;br /&gt;
	int id;&lt;br /&gt;
	int ione = 1;&lt;br /&gt;
	MPI_Comm_rank(world, &amp;amp;id);&lt;br /&gt;
	double *s = new double[n * nmon];&lt;br /&gt;
	double *q = new double[n * nmon], *r = new double[n * nmon];&lt;br /&gt;
	for (int i1 = 0; i1 &amp;lt; nmon; ++i1)&lt;br /&gt;
	for (int i = 0; i &amp;lt; n; ++i)&lt;br /&gt;
		q[i1 * n + i] = A[i + (id * nmon + i1) * n];&lt;br /&gt;
	for (int i1 = 0; i1 &amp;lt; nmon; ++i1)&lt;br /&gt;
	{&lt;br /&gt;
		for (int i = 0; i &amp;lt; id; ++i)&lt;br /&gt;
		{&lt;br /&gt;
		MPI_Recv(s, n * nmon, MPI_DOUBLE, i, 1, world, &amp;amp;status);&lt;br /&gt;
		for (int j = 0; j &amp;lt; nmon; ++j)&lt;br /&gt;
		{&lt;br /&gt;
			double arg = -ddot(&amp;amp;n, A + (id * nmon + i1) * n, &amp;amp;ione, s + j * n, &amp;amp;ione) / ddot(&amp;amp;n, s + j * n, &amp;amp;ione, s + j * n, &amp;amp;ione);&lt;br /&gt;
			daxpy(&amp;amp;n, &amp;amp;arg, s + j * n, &amp;amp;ione, q + i1 * n, &amp;amp;ione);&lt;br /&gt;
			r[i1 * n + i * nmon + j] = -arg;&lt;br /&gt;
		}&lt;br /&gt;
		}&lt;br /&gt;
		for (int i = 0; i &amp;lt; i1; ++i)&lt;br /&gt;
		{&lt;br /&gt;
			double arg = -ddot(&amp;amp;n, A + (id * nmon + i1) * n, &amp;amp;ione, q + i * n, &amp;amp;ione) / ddot(&amp;amp;n, q + i * n, &amp;amp;ione, q + i * n, &amp;amp;ione);&lt;br /&gt;
			daxpy(&amp;amp;n, &amp;amp;arg, q + i * n, &amp;amp;ione, q + i1 * n, &amp;amp;ione);&lt;br /&gt;
			r[i1 * n + id * nmon + i] = -arg;&lt;br /&gt;
&lt;br /&gt;
		}&lt;br /&gt;
		r[i1 * n + id * nmon + i1] = 1;&lt;br /&gt;
		for (int i = id * nmon + i1 + 1; i &amp;lt; n; ++i)&lt;br /&gt;
			r[i1 * n + i] = 0;&lt;br /&gt;
		MPI_Request req;&lt;br /&gt;
		#pragma omp parallel for&lt;br /&gt;
		for (int i1 = 0; i1 &amp;lt; nmon; ++i1)&lt;br /&gt;
		for (int i = id + 1; i &amp;lt; nproc; ++i)&lt;br /&gt;
			MPI_Isend(q, n * nmon, MPI_DOUBLE, i, 1, world, &amp;amp;req);&lt;br /&gt;
	}&lt;br /&gt;
	MPI_Barrier(world);&lt;br /&gt;
	gather(n, A, id, q, r, nmon);&lt;br /&gt;
	MPI_Finalize();&lt;br /&gt;
	return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
==== Результаты работы данного кода ====&lt;br /&gt;
[[Файл:Результаты запусков.png|обрамить|слева|Результаты запусков]]&lt;br /&gt;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
Алгоритм увеличивает время своей работы при добавлении независимых вычислительных ядер.&lt;br /&gt;
Алгоритм обладает сильной масштабируемостью.&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
При выборе системы для запуска предпочтение следует отдавать скорости обмена данными между процессами.&lt;br /&gt;
При выборе архитектуры необходимо учесть наличие возможности вычислений с высокой точностью.&lt;br /&gt;
Параллельный алгоритм не дает разительного превосходства по времени исполнения в сравнении с последовательным, но все же имеет место заметное уменьшение времени исполнения при выполнении указанных условий.&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
Реализация архитектурно независима. Зависимы только макрооперации, которые предполагаются уже реализованными.&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
В силу потери ортогональности алгоритм Грамма-Шмидта пользуется небольшой популярностью в сравнении с альтернативными методами QR-разложения, так что популярных параллельных реализаций нет.&lt;br /&gt;
Альтернативный (не Грамм-Шмидт) метод представлен, например, в пакете LAPACK и является одним из лучших по производительности.&lt;br /&gt;
== Литература ==&lt;br /&gt;
#Е. Е. Тыртышников, Методы численного анализа&lt;br /&gt;
#Ф. Р. Гантмахер, Теория матриц&lt;br /&gt;
#С. Ленг, Алгебра&lt;br /&gt;
#Е. Е. Тыртышников, Основы алгебры&lt;/div&gt;</summary>
		<author><name>Egor Luckjanow 403</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:Egor_Luckjanow_403/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%93%D1%80%D0%B0%D0%BC%D0%BC%D0%B0-%D0%A8%D0%BC%D0%B8%D0%B4%D1%82%D0%B0&amp;diff=28761</id>
		<title>Участник:Egor Luckjanow 403/Алгоритм Грамма-Шмидта</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:Egor_Luckjanow_403/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%93%D1%80%D0%B0%D0%BC%D0%BC%D0%B0-%D0%A8%D0%BC%D0%B8%D0%B4%D1%82%D0%B0&amp;diff=28761"/>
		<updated>2021-12-12T17:06:54Z</updated>

		<summary type="html">&lt;p&gt;Egor Luckjanow 403: /* Результаты работы данного кода */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Автор: Егор Лукьянов, студент 403 группы ВМК МГУ&lt;br /&gt;
== Свойства и структура алгоритмов ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
Алгоритм Грама-Шмидта предназначен для построения QR-разложения матрицы.&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
В основе работы алгоритма лежит метод Грама-Шмидта для построения ортонормированного базиса на основе уже существующего:&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;q_1 = \frac{a_1}{||a_1||}&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;p_2 = a_2 - (a_2, q_1)q_1&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;q_2 = \frac{p_2}{||p_2||}&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;p_3 = a_3 - (a_3, q_1)q_1 - (a_3, q_2)q_2&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;q_3 = \frac{p_3}{||p_3||}&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;...&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
Мы применяем этот метод к столбцам исходной матрицы A и получаем разложение A = QR, где Q составлена из столбцов q_1,...,q_n, полученных в результате применения метода Грама-Шмидта, а R -- матрица коэффициентов, которая будет верхнетреугольной в силу вида метода.&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Ядро алгоритма -- последовательное нахождение Q и R, как в математическом описании.&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
Макрооперации: скалярное произведение ddot; умножение матрицы на вектор с прибавлением другого вектора daxpy.&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;&lt;br /&gt;
void QR(int N, double* A, double* Q, double* R)&lt;br /&gt;
{&lt;br /&gt;
	int one = 1;&lt;br /&gt;
	for (int i = 0; i &amp;lt; N; ++i) {&lt;br /&gt;
		R[i * N + i] = 1;&lt;br /&gt;
		for (int k = 1; k &amp;lt; i + 1; ++k) {&lt;br /&gt;
			R[i - k + i * N] = ddot(&amp;amp;N, A + i * N, &amp;amp;one, Q + (i - k) * N, &amp;amp;one) / ddot(&amp;amp;N, Q + (i - k) * N, &amp;amp;one, Q + (i - k) * N, &amp;amp;one);&lt;br /&gt;
		}&lt;br /&gt;
		for (int j = 0; j &amp;lt; N; ++j) {&lt;br /&gt;
			Q[j + i * N] = A[j + i * N];&lt;br /&gt;
		}&lt;br /&gt;
		for (int k = 0; k &amp;lt; i; ++k) {&lt;br /&gt;
			double tmp = -R[i - k - 1 + i * N];&lt;br /&gt;
			daxpy(&amp;amp;N, &amp;amp;tmp, Q + (i - k - 1) * N, &amp;amp;one, Q + i * N, &amp;amp;one);&lt;br /&gt;
		}&lt;br /&gt;
		for (int k = i + 1; k &amp;lt; N; ++k) {&lt;br /&gt;
			R[k + i * N] = 0;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	for (int i = 0; i &amp;lt; N; ++i) {&lt;br /&gt;
		double norm = dnrm2(&amp;amp;N, Q + i * N, &amp;amp;one);&lt;br /&gt;
		for (int j = 0; j &amp;lt; N; ++j) {&lt;br /&gt;
			Q[j + i * N] /= norm;&lt;br /&gt;
		}&lt;br /&gt;
		for (int j = 0; j &amp;lt; N; ++j) {&lt;br /&gt;
			R[i + j * N] *= norm;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Сложность последовательного алгоритма -- &amp;lt;math&amp;gt;O(n^3)&amp;lt;/math&amp;gt;.&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
Параллельная сложность (в терминах операций сложения и умножения) -- &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;Q&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; из QR-разложения поданной на вход матрицы.&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
Алгоритм Грамма-Шмидта обладает свойством потери ортогональности. Поэтому для корректной его реализации необходимо время от времени делать реортогонализацию, а также обеспечить максимально возможную точность вычислений с плавающей точкой.&lt;br /&gt;
Можно реализовать алгоритм для работы с рациональными матрицами. Тогда проблема с потерей ортогонализации пропадает (в виду идеальной точности вычислений в кольцах вида &amp;lt;math&amp;gt;\mathbb{Z}/&amp;lt;m&amp;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;q_i&amp;lt;/math&amp;gt; для вычисления.&lt;br /&gt;
Число пересылок тогда составляет &amp;lt;math&amp;gt;n(n+1)/2&amp;lt;/math&amp;gt;.&lt;br /&gt;
==== Код возможной реализации ====&lt;br /&gt;
&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;iostream&amp;gt;&lt;br /&gt;
#include &amp;lt;mpi.h&amp;gt;&lt;br /&gt;
#include &amp;lt;omp.h&amp;gt;&lt;br /&gt;
#include &amp;lt;mkl.h&amp;gt;&lt;br /&gt;
#include &amp;lt;cstdlib&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define world MPI_COMM_WORLD&lt;br /&gt;
&lt;br /&gt;
void gather(int n, double *A, int id, double *q, double *r, int nmon)&lt;br /&gt;
{&lt;br /&gt;
	double *Q = new double[n * n], *R = new double[n * n];&lt;br /&gt;
	MPI_Gather(q, n * nmon, MPI_DOUBLE, Q, n * nmon, MPI_DOUBLE, 0, world);&lt;br /&gt;
	MPI_Gather(r, n * nmon, MPI_DOUBLE, R, n * nmon, MPI_DOUBLE, 0, world);&lt;br /&gt;
	if (id != 0)&lt;br /&gt;
		return;&lt;br /&gt;
	#pragma omp parallel for&lt;br /&gt;
	for (int i = 0; i &amp;lt; n; ++i)&lt;br /&gt;
	{&lt;br /&gt;
		int ione = 1;&lt;br /&gt;
		double norm = dnrm2(&amp;amp;n, Q + i * n, &amp;amp;ione);&lt;br /&gt;
		for (int j = 0; j &amp;lt; n; ++j)&lt;br /&gt;
		{&lt;br /&gt;
			Q[j + i * n] /= norm;&lt;br /&gt;
			R[i + j * n] *= norm;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	double one = 1;&lt;br /&gt;
	double zero = 0;&lt;br /&gt;
	char trans = &#039;N&#039;;&lt;br /&gt;
	double *tmpA = new double[n * n];&lt;br /&gt;
	dgemm(&amp;amp;trans, &amp;amp;trans, &amp;amp;n, &amp;amp;n, &amp;amp;n, &amp;amp;one, Q, &amp;amp;n, R, &amp;amp;n, &amp;amp;zero, tmpA, &amp;amp;n);&lt;br /&gt;
	int n2 = n * n;&lt;br /&gt;
	double mone = -1;&lt;br /&gt;
	int ione = 1;&lt;br /&gt;
	daxpy(&amp;amp;n2, &amp;amp;mone, A, &amp;amp;ione, tmpA, &amp;amp;ione);&lt;br /&gt;
	std:: cout &amp;lt;&amp;lt; dnrm2(&amp;amp;n2, tmpA, &amp;amp;ione) &amp;lt;&amp;lt; std::endl &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
	char trans2 = &#039;T&#039;;&lt;br /&gt;
	dgemm(&amp;amp;trans, &amp;amp;trans2, &amp;amp;n, &amp;amp;n, &amp;amp;n, &amp;amp;one, Q, &amp;amp;n, Q, &amp;amp;n, &amp;amp;zero, A, &amp;amp;n);&lt;br /&gt;
	double *I = new double[n * n];&lt;br /&gt;
	for (int i = 0; i &amp;lt; n; ++i)&lt;br /&gt;
	for (int j = 0; j &amp;lt; n; ++j)&lt;br /&gt;
		if (i == j)&lt;br /&gt;
			I[j + i * n] = 1;&lt;br /&gt;
		else&lt;br /&gt;
			I[j + i * n] = 0;&lt;br /&gt;
	daxpy(&amp;amp;n2, &amp;amp;mone, I, &amp;amp;ione, A, &amp;amp;ione);&lt;br /&gt;
	std::cout &amp;lt;&amp;lt; dnrm2(&amp;amp;n2, A, &amp;amp;ione) &amp;lt;&amp;lt; std::endl &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char *argv[])&lt;br /&gt;
{&lt;br /&gt;
	int rinit;&lt;br /&gt;
	MPI_Init(&amp;amp;argc, &amp;amp;argv);&lt;br /&gt;
	sscanf(argv[1], &amp;quot;%d&amp;quot;, &amp;amp;rinit);&lt;br /&gt;
	srand(rinit);&lt;br /&gt;
	int nproc;&lt;br /&gt;
	MPI_Comm_size(world, &amp;amp;nproc);&lt;br /&gt;
	int nmon = 20;&lt;br /&gt;
	int n = nmon * nproc;&lt;br /&gt;
	MPI_Status status;&lt;br /&gt;
	double *A = new double[n * n];&lt;br /&gt;
	#pragma omp parallel for&lt;br /&gt;
	for (int i = 0; i &amp;lt; n; ++i)&lt;br /&gt;
	for (int j = 0; j &amp;lt; n; ++j)&lt;br /&gt;
		if (i == j)&lt;br /&gt;
			A[j + i * n] = rand();&lt;br /&gt;
		else&lt;br /&gt;
			A[j + i * n] = rand();&lt;br /&gt;
	int id;&lt;br /&gt;
	int ione = 1;&lt;br /&gt;
	MPI_Comm_rank(world, &amp;amp;id);&lt;br /&gt;
	double *s = new double[n * nmon];&lt;br /&gt;
	double *q = new double[n * nmon], *r = new double[n * nmon];&lt;br /&gt;
	for (int i1 = 0; i1 &amp;lt; nmon; ++i1)&lt;br /&gt;
	for (int i = 0; i &amp;lt; n; ++i)&lt;br /&gt;
		q[i1 * n + i] = A[i + (id * nmon + i1) * n];&lt;br /&gt;
	for (int i1 = 0; i1 &amp;lt; nmon; ++i1)&lt;br /&gt;
	{&lt;br /&gt;
		for (int i = 0; i &amp;lt; id; ++i)&lt;br /&gt;
		{&lt;br /&gt;
		MPI_Recv(s, n * nmon, MPI_DOUBLE, i, 1, world, &amp;amp;status);&lt;br /&gt;
		for (int j = 0; j &amp;lt; nmon; ++j)&lt;br /&gt;
		{&lt;br /&gt;
			double arg = -ddot(&amp;amp;n, A + (id * nmon + i1) * n, &amp;amp;ione, s + j * n, &amp;amp;ione) / ddot(&amp;amp;n, s + j * n, &amp;amp;ione, s + j * n, &amp;amp;ione);&lt;br /&gt;
			daxpy(&amp;amp;n, &amp;amp;arg, s + j * n, &amp;amp;ione, q + i1 * n, &amp;amp;ione);&lt;br /&gt;
			r[i1 * n + i * nmon + j] = -arg;&lt;br /&gt;
		}&lt;br /&gt;
		}&lt;br /&gt;
		for (int i = 0; i &amp;lt; i1; ++i)&lt;br /&gt;
		{&lt;br /&gt;
			double arg = -ddot(&amp;amp;n, A + (id * nmon + i1) * n, &amp;amp;ione, q + i * n, &amp;amp;ione) / ddot(&amp;amp;n, q + i * n, &amp;amp;ione, q + i * n, &amp;amp;ione);&lt;br /&gt;
			daxpy(&amp;amp;n, &amp;amp;arg, q + i * n, &amp;amp;ione, q + i1 * n, &amp;amp;ione);&lt;br /&gt;
			r[i1 * n + id * nmon + i] = -arg;&lt;br /&gt;
&lt;br /&gt;
		}&lt;br /&gt;
		r[i1 * n + id * nmon + i1] = 1;&lt;br /&gt;
		for (int i = id * nmon + i1 + 1; i &amp;lt; n; ++i)&lt;br /&gt;
			r[i1 * n + i] = 0;&lt;br /&gt;
		MPI_Request req;&lt;br /&gt;
		#pragma omp parallel for&lt;br /&gt;
		for (int i1 = 0; i1 &amp;lt; nmon; ++i1)&lt;br /&gt;
		for (int i = id + 1; i &amp;lt; nproc; ++i)&lt;br /&gt;
			MPI_Isend(q, n * nmon, MPI_DOUBLE, i, 1, world, &amp;amp;req);&lt;br /&gt;
	}&lt;br /&gt;
	MPI_Barrier(world);&lt;br /&gt;
	gather(n, A, id, q, r, nmon);&lt;br /&gt;
	MPI_Finalize();&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;
&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
Алгоритм увеличивает время своей работы при добавлении независимых вычислительных ядер.&lt;br /&gt;
Алгоритм обладает сильной масштабируемостью.&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
При выборе системы для запуска предпочтение следует отдавать скорости обмена данными между процессами.&lt;br /&gt;
При выборе архитектуры необходимо учесть наличие возможности вычислений с высокой точностью.&lt;br /&gt;
Параллельный алгоритм не дает разительного превосходства по времени исполнения в сравнении с последовательным, но все же имеет место заметное уменьшение времени исполнения при выполнении указанных условий.&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
Реализация архитектурно независима. Зависимы только макрооперации, которые предполагаются уже реализованными.&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
В силу потери ортогональности алгоритм Грамма-Шмидта пользуется небольшой популярностью в сравнении с альтернативными методами QR-разложения, так что популярных параллельных реализаций нет.&lt;br /&gt;
Альтернативный (не Грамм-Шмидт) метод представлен, например, в пакете LAPACK и является одним из лучших по производительности.&lt;br /&gt;
== Литература ==&lt;br /&gt;
#Е. Е. Тыртышников, Методы численного анализа&lt;br /&gt;
#Ф. Р. Гантмахер, Теория матриц&lt;br /&gt;
#С. Ленг, Алгебра&lt;br /&gt;
#Е. Е. Тыртышников, Основы алгебры&lt;/div&gt;</summary>
		<author><name>Egor Luckjanow 403</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:Egor_Luckjanow_403/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%93%D1%80%D0%B0%D0%BC%D0%BC%D0%B0-%D0%A8%D0%BC%D0%B8%D0%B4%D1%82%D0%B0&amp;diff=28760</id>
		<title>Участник:Egor Luckjanow 403/Алгоритм Грамма-Шмидта</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:Egor_Luckjanow_403/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%93%D1%80%D0%B0%D0%BC%D0%BC%D0%B0-%D0%A8%D0%BC%D0%B8%D0%B4%D1%82%D0%B0&amp;diff=28760"/>
		<updated>2021-12-12T17:04:57Z</updated>

		<summary type="html">&lt;p&gt;Egor Luckjanow 403: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Автор: Егор Лукьянов, студент 403 группы ВМК МГУ&lt;br /&gt;
== Свойства и структура алгоритмов ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
Алгоритм Грама-Шмидта предназначен для построения QR-разложения матрицы.&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
В основе работы алгоритма лежит метод Грама-Шмидта для построения ортонормированного базиса на основе уже существующего:&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;q_1 = \frac{a_1}{||a_1||}&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;p_2 = a_2 - (a_2, q_1)q_1&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;q_2 = \frac{p_2}{||p_2||}&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;p_3 = a_3 - (a_3, q_1)q_1 - (a_3, q_2)q_2&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;q_3 = \frac{p_3}{||p_3||}&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;...&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
Мы применяем этот метод к столбцам исходной матрицы A и получаем разложение A = QR, где Q составлена из столбцов q_1,...,q_n, полученных в результате применения метода Грама-Шмидта, а R -- матрица коэффициентов, которая будет верхнетреугольной в силу вида метода.&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Ядро алгоритма -- последовательное нахождение Q и R, как в математическом описании.&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
Макрооперации: скалярное произведение ddot; умножение матрицы на вектор с прибавлением другого вектора daxpy.&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;&lt;br /&gt;
void QR(int N, double* A, double* Q, double* R)&lt;br /&gt;
{&lt;br /&gt;
	int one = 1;&lt;br /&gt;
	for (int i = 0; i &amp;lt; N; ++i) {&lt;br /&gt;
		R[i * N + i] = 1;&lt;br /&gt;
		for (int k = 1; k &amp;lt; i + 1; ++k) {&lt;br /&gt;
			R[i - k + i * N] = ddot(&amp;amp;N, A + i * N, &amp;amp;one, Q + (i - k) * N, &amp;amp;one) / ddot(&amp;amp;N, Q + (i - k) * N, &amp;amp;one, Q + (i - k) * N, &amp;amp;one);&lt;br /&gt;
		}&lt;br /&gt;
		for (int j = 0; j &amp;lt; N; ++j) {&lt;br /&gt;
			Q[j + i * N] = A[j + i * N];&lt;br /&gt;
		}&lt;br /&gt;
		for (int k = 0; k &amp;lt; i; ++k) {&lt;br /&gt;
			double tmp = -R[i - k - 1 + i * N];&lt;br /&gt;
			daxpy(&amp;amp;N, &amp;amp;tmp, Q + (i - k - 1) * N, &amp;amp;one, Q + i * N, &amp;amp;one);&lt;br /&gt;
		}&lt;br /&gt;
		for (int k = i + 1; k &amp;lt; N; ++k) {&lt;br /&gt;
			R[k + i * N] = 0;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	for (int i = 0; i &amp;lt; N; ++i) {&lt;br /&gt;
		double norm = dnrm2(&amp;amp;N, Q + i * N, &amp;amp;one);&lt;br /&gt;
		for (int j = 0; j &amp;lt; N; ++j) {&lt;br /&gt;
			Q[j + i * N] /= norm;&lt;br /&gt;
		}&lt;br /&gt;
		for (int j = 0; j &amp;lt; N; ++j) {&lt;br /&gt;
			R[i + j * N] *= norm;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Сложность последовательного алгоритма -- &amp;lt;math&amp;gt;O(n^3)&amp;lt;/math&amp;gt;.&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
Параллельная сложность (в терминах операций сложения и умножения) -- &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;Q&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; из QR-разложения поданной на вход матрицы.&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
Алгоритм Грамма-Шмидта обладает свойством потери ортогональности. Поэтому для корректной его реализации необходимо время от времени делать реортогонализацию, а также обеспечить максимально возможную точность вычислений с плавающей точкой.&lt;br /&gt;
Можно реализовать алгоритм для работы с рациональными матрицами. Тогда проблема с потерей ортогонализации пропадает (в виду идеальной точности вычислений в кольцах вида &amp;lt;math&amp;gt;\mathbb{Z}/&amp;lt;m&amp;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;q_i&amp;lt;/math&amp;gt; для вычисления.&lt;br /&gt;
Число пересылок тогда составляет &amp;lt;math&amp;gt;n(n+1)/2&amp;lt;/math&amp;gt;.&lt;br /&gt;
==== Код возможной реализации ====&lt;br /&gt;
&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;iostream&amp;gt;&lt;br /&gt;
#include &amp;lt;mpi.h&amp;gt;&lt;br /&gt;
#include &amp;lt;omp.h&amp;gt;&lt;br /&gt;
#include &amp;lt;mkl.h&amp;gt;&lt;br /&gt;
#include &amp;lt;cstdlib&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define world MPI_COMM_WORLD&lt;br /&gt;
&lt;br /&gt;
void gather(int n, double *A, int id, double *q, double *r, int nmon)&lt;br /&gt;
{&lt;br /&gt;
	double *Q = new double[n * n], *R = new double[n * n];&lt;br /&gt;
	MPI_Gather(q, n * nmon, MPI_DOUBLE, Q, n * nmon, MPI_DOUBLE, 0, world);&lt;br /&gt;
	MPI_Gather(r, n * nmon, MPI_DOUBLE, R, n * nmon, MPI_DOUBLE, 0, world);&lt;br /&gt;
	if (id != 0)&lt;br /&gt;
		return;&lt;br /&gt;
	#pragma omp parallel for&lt;br /&gt;
	for (int i = 0; i &amp;lt; n; ++i)&lt;br /&gt;
	{&lt;br /&gt;
		int ione = 1;&lt;br /&gt;
		double norm = dnrm2(&amp;amp;n, Q + i * n, &amp;amp;ione);&lt;br /&gt;
		for (int j = 0; j &amp;lt; n; ++j)&lt;br /&gt;
		{&lt;br /&gt;
			Q[j + i * n] /= norm;&lt;br /&gt;
			R[i + j * n] *= norm;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	double one = 1;&lt;br /&gt;
	double zero = 0;&lt;br /&gt;
	char trans = &#039;N&#039;;&lt;br /&gt;
	double *tmpA = new double[n * n];&lt;br /&gt;
	dgemm(&amp;amp;trans, &amp;amp;trans, &amp;amp;n, &amp;amp;n, &amp;amp;n, &amp;amp;one, Q, &amp;amp;n, R, &amp;amp;n, &amp;amp;zero, tmpA, &amp;amp;n);&lt;br /&gt;
	int n2 = n * n;&lt;br /&gt;
	double mone = -1;&lt;br /&gt;
	int ione = 1;&lt;br /&gt;
	daxpy(&amp;amp;n2, &amp;amp;mone, A, &amp;amp;ione, tmpA, &amp;amp;ione);&lt;br /&gt;
	std:: cout &amp;lt;&amp;lt; dnrm2(&amp;amp;n2, tmpA, &amp;amp;ione) &amp;lt;&amp;lt; std::endl &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
	char trans2 = &#039;T&#039;;&lt;br /&gt;
	dgemm(&amp;amp;trans, &amp;amp;trans2, &amp;amp;n, &amp;amp;n, &amp;amp;n, &amp;amp;one, Q, &amp;amp;n, Q, &amp;amp;n, &amp;amp;zero, A, &amp;amp;n);&lt;br /&gt;
	double *I = new double[n * n];&lt;br /&gt;
	for (int i = 0; i &amp;lt; n; ++i)&lt;br /&gt;
	for (int j = 0; j &amp;lt; n; ++j)&lt;br /&gt;
		if (i == j)&lt;br /&gt;
			I[j + i * n] = 1;&lt;br /&gt;
		else&lt;br /&gt;
			I[j + i * n] = 0;&lt;br /&gt;
	daxpy(&amp;amp;n2, &amp;amp;mone, I, &amp;amp;ione, A, &amp;amp;ione);&lt;br /&gt;
	std::cout &amp;lt;&amp;lt; dnrm2(&amp;amp;n2, A, &amp;amp;ione) &amp;lt;&amp;lt; std::endl &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char *argv[])&lt;br /&gt;
{&lt;br /&gt;
	int rinit;&lt;br /&gt;
	MPI_Init(&amp;amp;argc, &amp;amp;argv);&lt;br /&gt;
	sscanf(argv[1], &amp;quot;%d&amp;quot;, &amp;amp;rinit);&lt;br /&gt;
	srand(rinit);&lt;br /&gt;
	int nproc;&lt;br /&gt;
	MPI_Comm_size(world, &amp;amp;nproc);&lt;br /&gt;
	int nmon = 20;&lt;br /&gt;
	int n = nmon * nproc;&lt;br /&gt;
	MPI_Status status;&lt;br /&gt;
	double *A = new double[n * n];&lt;br /&gt;
	#pragma omp parallel for&lt;br /&gt;
	for (int i = 0; i &amp;lt; n; ++i)&lt;br /&gt;
	for (int j = 0; j &amp;lt; n; ++j)&lt;br /&gt;
		if (i == j)&lt;br /&gt;
			A[j + i * n] = rand();&lt;br /&gt;
		else&lt;br /&gt;
			A[j + i * n] = rand();&lt;br /&gt;
	int id;&lt;br /&gt;
	int ione = 1;&lt;br /&gt;
	MPI_Comm_rank(world, &amp;amp;id);&lt;br /&gt;
	double *s = new double[n * nmon];&lt;br /&gt;
	double *q = new double[n * nmon], *r = new double[n * nmon];&lt;br /&gt;
	for (int i1 = 0; i1 &amp;lt; nmon; ++i1)&lt;br /&gt;
	for (int i = 0; i &amp;lt; n; ++i)&lt;br /&gt;
		q[i1 * n + i] = A[i + (id * nmon + i1) * n];&lt;br /&gt;
	for (int i1 = 0; i1 &amp;lt; nmon; ++i1)&lt;br /&gt;
	{&lt;br /&gt;
		for (int i = 0; i &amp;lt; id; ++i)&lt;br /&gt;
		{&lt;br /&gt;
		MPI_Recv(s, n * nmon, MPI_DOUBLE, i, 1, world, &amp;amp;status);&lt;br /&gt;
		for (int j = 0; j &amp;lt; nmon; ++j)&lt;br /&gt;
		{&lt;br /&gt;
			double arg = -ddot(&amp;amp;n, A + (id * nmon + i1) * n, &amp;amp;ione, s + j * n, &amp;amp;ione) / ddot(&amp;amp;n, s + j * n, &amp;amp;ione, s + j * n, &amp;amp;ione);&lt;br /&gt;
			daxpy(&amp;amp;n, &amp;amp;arg, s + j * n, &amp;amp;ione, q + i1 * n, &amp;amp;ione);&lt;br /&gt;
			r[i1 * n + i * nmon + j] = -arg;&lt;br /&gt;
		}&lt;br /&gt;
		}&lt;br /&gt;
		for (int i = 0; i &amp;lt; i1; ++i)&lt;br /&gt;
		{&lt;br /&gt;
			double arg = -ddot(&amp;amp;n, A + (id * nmon + i1) * n, &amp;amp;ione, q + i * n, &amp;amp;ione) / ddot(&amp;amp;n, q + i * n, &amp;amp;ione, q + i * n, &amp;amp;ione);&lt;br /&gt;
			daxpy(&amp;amp;n, &amp;amp;arg, q + i * n, &amp;amp;ione, q + i1 * n, &amp;amp;ione);&lt;br /&gt;
			r[i1 * n + id * nmon + i] = -arg;&lt;br /&gt;
&lt;br /&gt;
		}&lt;br /&gt;
		r[i1 * n + id * nmon + i1] = 1;&lt;br /&gt;
		for (int i = id * nmon + i1 + 1; i &amp;lt; n; ++i)&lt;br /&gt;
			r[i1 * n + i] = 0;&lt;br /&gt;
		MPI_Request req;&lt;br /&gt;
		#pragma omp parallel for&lt;br /&gt;
		for (int i1 = 0; i1 &amp;lt; nmon; ++i1)&lt;br /&gt;
		for (int i = id + 1; i &amp;lt; nproc; ++i)&lt;br /&gt;
			MPI_Isend(q, n * nmon, MPI_DOUBLE, i, 1, world, &amp;amp;req);&lt;br /&gt;
	}&lt;br /&gt;
	MPI_Barrier(world);&lt;br /&gt;
	gather(n, A, id, q, r, nmon);&lt;br /&gt;
	MPI_Finalize();&lt;br /&gt;
	return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
==== Результаты работы данного кода ====&lt;br /&gt;
[[Файл:Результаты запусков.png|мини]]&lt;br /&gt;
=== Масштабируемость алгоритма и его реализации ===&lt;br /&gt;
Алгоритм увеличивает время своей работы при добавлении независимых вычислительных ядер.&lt;br /&gt;
Алгоритм обладает сильной масштабируемостью.&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
При выборе системы для запуска предпочтение следует отдавать скорости обмена данными между процессами.&lt;br /&gt;
При выборе архитектуры необходимо учесть наличие возможности вычислений с высокой точностью.&lt;br /&gt;
Параллельный алгоритм не дает разительного превосходства по времени исполнения в сравнении с последовательным, но все же имеет место заметное уменьшение времени исполнения при выполнении указанных условий.&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
Реализация архитектурно независима. Зависимы только макрооперации, которые предполагаются уже реализованными.&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
В силу потери ортогональности алгоритм Грамма-Шмидта пользуется небольшой популярностью в сравнении с альтернативными методами QR-разложения, так что популярных параллельных реализаций нет.&lt;br /&gt;
Альтернативный (не Грамм-Шмидт) метод представлен, например, в пакете LAPACK и является одним из лучших по производительности.&lt;br /&gt;
== Литература ==&lt;br /&gt;
#Е. Е. Тыртышников, Методы численного анализа&lt;br /&gt;
#Ф. Р. Гантмахер, Теория матриц&lt;br /&gt;
#С. Ленг, Алгебра&lt;br /&gt;
#Е. Е. Тыртышников, Основы алгебры&lt;/div&gt;</summary>
		<author><name>Egor Luckjanow 403</name></author>
	</entry>
	<entry>
		<id>https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:%D0%A0%D0%B5%D0%B7%D1%83%D0%BB%D1%8C%D1%82%D0%B0%D1%82%D1%8B_%D0%B7%D0%B0%D0%BF%D1%83%D1%81%D0%BA%D0%BE%D0%B2.png&amp;diff=28759</id>
		<title>Файл:Результаты запусков.png</title>
		<link rel="alternate" type="text/html" href="https://xn--80aecvaldz.xn--80ahdrnmvk.xn--p1ai/w/ru/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:%D0%A0%D0%B5%D0%B7%D1%83%D0%BB%D1%8C%D1%82%D0%B0%D1%82%D1%8B_%D0%B7%D0%B0%D0%BF%D1%83%D1%81%D0%BA%D0%BE%D0%B2.png&amp;diff=28759"/>
		<updated>2021-12-12T17:04:27Z</updated>

		<summary type="html">&lt;p&gt;Egor Luckjanow 403: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Результаты запусков&lt;/div&gt;</summary>
		<author><name>Egor Luckjanow 403</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:Egor_Luckjanow_403/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%93%D1%80%D0%B0%D0%BC%D0%BC%D0%B0-%D0%A8%D0%BC%D0%B8%D0%B4%D1%82%D0%B0&amp;diff=28758</id>
		<title>Участник:Egor Luckjanow 403/Алгоритм Грамма-Шмидта</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:Egor_Luckjanow_403/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%93%D1%80%D0%B0%D0%BC%D0%BC%D0%B0-%D0%A8%D0%BC%D0%B8%D0%B4%D1%82%D0%B0&amp;diff=28758"/>
		<updated>2021-12-12T16:49:05Z</updated>

		<summary type="html">&lt;p&gt;Egor Luckjanow 403: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Автор: Егор Лукьянов, студент 403 группы ВМК МГУ&lt;br /&gt;
== Свойства и структура алгоритмов ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
Алгоритм Грама-Шмидта предназначен для построения QR-разложения матрицы.&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
В основе работы алгоритма лежит метод Грама-Шмидта для построения ортонормированного базиса на основе уже существующего:&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;q_1 = \frac{a_1}{||a_1||}&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;p_2 = a_2 - (a_2, q_1)q_1&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;q_2 = \frac{p_2}{||p_2||}&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;p_3 = a_3 - (a_3, q_1)q_1 - (a_3, q_2)q_2&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;q_3 = \frac{p_3}{||p_3||}&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;...&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
Мы применяем этот метод к столбцам исходной матрицы A и получаем разложение A = QR, где Q составлена из столбцов q_1,...,q_n, полученных в результате применения метода Грама-Шмидта, а R -- матрица коэффициентов, которая будет верхнетреугольной в силу вида метода.&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Ядро алгоритма -- последовательное нахождение Q и R, как в математическом описании.&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
Макрооперации: скалярное произведение ddot; умножение матрицы на вектор с прибавлением другого вектора daxpy.&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;&lt;br /&gt;
void QR(int N, double* A, double* Q, double* R)&lt;br /&gt;
{&lt;br /&gt;
	int one = 1;&lt;br /&gt;
	for (int i = 0; i &amp;lt; N; ++i) {&lt;br /&gt;
		R[i * N + i] = 1;&lt;br /&gt;
		for (int k = 1; k &amp;lt; i + 1; ++k) {&lt;br /&gt;
			R[i - k + i * N] = ddot(&amp;amp;N, A + i * N, &amp;amp;one, Q + (i - k) * N, &amp;amp;one) / ddot(&amp;amp;N, Q + (i - k) * N, &amp;amp;one, Q + (i - k) * N, &amp;amp;one);&lt;br /&gt;
		}&lt;br /&gt;
		for (int j = 0; j &amp;lt; N; ++j) {&lt;br /&gt;
			Q[j + i * N] = A[j + i * N];&lt;br /&gt;
		}&lt;br /&gt;
		for (int k = 0; k &amp;lt; i; ++k) {&lt;br /&gt;
			double tmp = -R[i - k - 1 + i * N];&lt;br /&gt;
			daxpy(&amp;amp;N, &amp;amp;tmp, Q + (i - k - 1) * N, &amp;amp;one, Q + i * N, &amp;amp;one);&lt;br /&gt;
		}&lt;br /&gt;
		for (int k = i + 1; k &amp;lt; N; ++k) {&lt;br /&gt;
			R[k + i * N] = 0;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	for (int i = 0; i &amp;lt; N; ++i) {&lt;br /&gt;
		double norm = dnrm2(&amp;amp;N, Q + i * N, &amp;amp;one);&lt;br /&gt;
		for (int j = 0; j &amp;lt; N; ++j) {&lt;br /&gt;
			Q[j + i * N] /= norm;&lt;br /&gt;
		}&lt;br /&gt;
		for (int j = 0; j &amp;lt; N; ++j) {&lt;br /&gt;
			R[i + j * N] *= norm;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Сложность последовательного алгоритма -- &amp;lt;math&amp;gt;O(n^3)&amp;lt;/math&amp;gt;.&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
Параллельная сложность (в терминах операций сложения и умножения) -- &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;Q&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; из QR-разложения поданной на вход матрицы.&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
Алгоритм Грамма-Шмидта обладает свойством потери ортогональности. Поэтому для корректной его реализации необходимо время от времени делать реортогонализацию, а также обеспечить максимально возможную точность вычислений с плавающей точкой.&lt;br /&gt;
Можно реализовать алгоритм для работы с рациональными матрицами. Тогда проблема с потерей ортогонализации пропадает (в виду идеальной точности вычислений в кольцах вида &amp;lt;math&amp;gt;\mathbb{Z}/&amp;lt;m&amp;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;q_i&amp;lt;/math&amp;gt; для вычисления.&lt;br /&gt;
Число пересылок тогда составляет &amp;lt;math&amp;gt;n(n+1)/2&amp;lt;/math&amp;gt;.&lt;br /&gt;
==== Код возможной реализации ====&lt;br /&gt;
&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;iostream&amp;gt;&lt;br /&gt;
#include &amp;lt;mpi.h&amp;gt;&lt;br /&gt;
#include &amp;lt;omp.h&amp;gt;&lt;br /&gt;
#include &amp;lt;mkl.h&amp;gt;&lt;br /&gt;
#include &amp;lt;cstdlib&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define world MPI_COMM_WORLD&lt;br /&gt;
&lt;br /&gt;
void gather(int n, double *A, int id, double *q, double *r, int nmon)&lt;br /&gt;
{&lt;br /&gt;
	double *Q = new double[n * n], *R = new double[n * n];&lt;br /&gt;
	MPI_Gather(q, n * nmon, MPI_DOUBLE, Q, n * nmon, MPI_DOUBLE, 0, world);&lt;br /&gt;
	MPI_Gather(r, n * nmon, MPI_DOUBLE, R, n * nmon, MPI_DOUBLE, 0, world);&lt;br /&gt;
	if (id != 0)&lt;br /&gt;
		return;&lt;br /&gt;
	#pragma omp parallel for&lt;br /&gt;
	for (int i = 0; i &amp;lt; n; ++i)&lt;br /&gt;
	{&lt;br /&gt;
		int ione = 1;&lt;br /&gt;
		double norm = dnrm2(&amp;amp;n, Q + i * n, &amp;amp;ione);&lt;br /&gt;
		for (int j = 0; j &amp;lt; n; ++j)&lt;br /&gt;
		{&lt;br /&gt;
			Q[j + i * n] /= norm;&lt;br /&gt;
			R[i + j * n] *= norm;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	double one = 1;&lt;br /&gt;
	double zero = 0;&lt;br /&gt;
	char trans = &#039;N&#039;;&lt;br /&gt;
	double *tmpA = new double[n * n];&lt;br /&gt;
	dgemm(&amp;amp;trans, &amp;amp;trans, &amp;amp;n, &amp;amp;n, &amp;amp;n, &amp;amp;one, Q, &amp;amp;n, R, &amp;amp;n, &amp;amp;zero, tmpA, &amp;amp;n);&lt;br /&gt;
	int n2 = n * n;&lt;br /&gt;
	double mone = -1;&lt;br /&gt;
	int ione = 1;&lt;br /&gt;
	daxpy(&amp;amp;n2, &amp;amp;mone, A, &amp;amp;ione, tmpA, &amp;amp;ione);&lt;br /&gt;
	std:: cout &amp;lt;&amp;lt; dnrm2(&amp;amp;n2, tmpA, &amp;amp;ione) &amp;lt;&amp;lt; std::endl &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
	char trans2 = &#039;T&#039;;&lt;br /&gt;
	dgemm(&amp;amp;trans, &amp;amp;trans2, &amp;amp;n, &amp;amp;n, &amp;amp;n, &amp;amp;one, Q, &amp;amp;n, Q, &amp;amp;n, &amp;amp;zero, A, &amp;amp;n);&lt;br /&gt;
	double *I = new double[n * n];&lt;br /&gt;
	for (int i = 0; i &amp;lt; n; ++i)&lt;br /&gt;
	for (int j = 0; j &amp;lt; n; ++j)&lt;br /&gt;
		if (i == j)&lt;br /&gt;
			I[j + i * n] = 1;&lt;br /&gt;
		else&lt;br /&gt;
			I[j + i * n] = 0;&lt;br /&gt;
	daxpy(&amp;amp;n2, &amp;amp;mone, I, &amp;amp;ione, A, &amp;amp;ione);&lt;br /&gt;
	std::cout &amp;lt;&amp;lt; dnrm2(&amp;amp;n2, A, &amp;amp;ione) &amp;lt;&amp;lt; std::endl &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char *argv[])&lt;br /&gt;
{&lt;br /&gt;
	int rinit;&lt;br /&gt;
	MPI_Init(&amp;amp;argc, &amp;amp;argv);&lt;br /&gt;
	sscanf(argv[1], &amp;quot;%d&amp;quot;, &amp;amp;rinit);&lt;br /&gt;
	srand(rinit);&lt;br /&gt;
	int nproc;&lt;br /&gt;
	MPI_Comm_size(world, &amp;amp;nproc);&lt;br /&gt;
	int nmon = 20;&lt;br /&gt;
	int n = nmon * nproc;&lt;br /&gt;
	MPI_Status status;&lt;br /&gt;
	double *A = new double[n * n];&lt;br /&gt;
	#pragma omp parallel for&lt;br /&gt;
	for (int i = 0; i &amp;lt; n; ++i)&lt;br /&gt;
	for (int j = 0; j &amp;lt; n; ++j)&lt;br /&gt;
		if (i == j)&lt;br /&gt;
			A[j + i * n] = rand();&lt;br /&gt;
			//A[j + i * n] = 1;&lt;br /&gt;
		else&lt;br /&gt;
			A[j + i * n] = rand();&lt;br /&gt;
			//A[j + i * n] = 0;&lt;br /&gt;
	int id;&lt;br /&gt;
	int ione = 1;&lt;br /&gt;
	MPI_Comm_rank(world, &amp;amp;id);&lt;br /&gt;
	double *s = new double[n * nmon];&lt;br /&gt;
	double *q = new double[n * nmon], *r = new double[n * nmon];&lt;br /&gt;
	for (int i1 = 0; i1 &amp;lt; nmon; ++i1)&lt;br /&gt;
	for (int i = 0; i &amp;lt; n; ++i)&lt;br /&gt;
		q[i1 * n + i] = A[i + (id * nmon + i1) * n];&lt;br /&gt;
	for (int i1 = 0; i1 &amp;lt; nmon; ++i1)&lt;br /&gt;
	{&lt;br /&gt;
		for (int i = 0; i &amp;lt; id; ++i)&lt;br /&gt;
		{&lt;br /&gt;
		MPI_Recv(s, n * nmon, MPI_DOUBLE, i, 1, world, &amp;amp;status);&lt;br /&gt;
		for (int j = 0; j &amp;lt; nmon; ++j)&lt;br /&gt;
		{&lt;br /&gt;
			//MPI_Recv(s + i1 * n, n, MPI_DOUBLE, i / nmon, 1, world, &amp;amp;status);&lt;br /&gt;
			double arg = -ddot(&amp;amp;n, A + (id * nmon + i1) * n, &amp;amp;ione, s + j * n, &amp;amp;ione) / ddot(&amp;amp;n, s + j * n, &amp;amp;ione, s + j * n, &amp;amp;ione);&lt;br /&gt;
			daxpy(&amp;amp;n, &amp;amp;arg, s + j * n, &amp;amp;ione, q + i1 * n, &amp;amp;ione);&lt;br /&gt;
			r[i1 * n + i * nmon + j] = -arg;&lt;br /&gt;
		}&lt;br /&gt;
		}&lt;br /&gt;
		for (int i = 0; i &amp;lt; i1; ++i)&lt;br /&gt;
		{&lt;br /&gt;
			double arg = -ddot(&amp;amp;n, A + (id * nmon + i1) * n, &amp;amp;ione, q + i * n, &amp;amp;ione) / ddot(&amp;amp;n, q + i * n, &amp;amp;ione, q + i * n, &amp;amp;ione);&lt;br /&gt;
			daxpy(&amp;amp;n, &amp;amp;arg, q + i * n, &amp;amp;ione, q + i1 * n, &amp;amp;ione);&lt;br /&gt;
			r[i1 * n + id * nmon + i] = -arg;&lt;br /&gt;
&lt;br /&gt;
		}&lt;br /&gt;
		r[i1 * n + id * nmon + i1] = 1;&lt;br /&gt;
		for (int i = id * nmon + i1 + 1; i &amp;lt; n; ++i)&lt;br /&gt;
			r[i1 * n + i] = 0;&lt;br /&gt;
		MPI_Request req;&lt;br /&gt;
		#pragma omp parallel for&lt;br /&gt;
		for (int i1 = 0; i1 &amp;lt; nmon; ++i1)&lt;br /&gt;
		for (int i = id + 1; i &amp;lt; nproc; ++i)&lt;br /&gt;
			MPI_Isend(q, n * nmon, MPI_DOUBLE, i, 1, world, &amp;amp;req);&lt;br /&gt;
	}&lt;br /&gt;
	MPI_Barrier(world);&lt;br /&gt;
	gather(n, A, id, q, r, nmon);&lt;br /&gt;
	MPI_Finalize();&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;
Алгоритм обладает сильной масштабируемостью.&lt;br /&gt;
=== Динамические характеристики и эффективность реализации алгоритма ===&lt;br /&gt;
При выборе системы для запуска предпочтение следует отдавать скорости обмена данными между процессами.&lt;br /&gt;
При выборе архитектуры необходимо учесть наличие возможности вычислений с высокой точностью.&lt;br /&gt;
Параллельный алгоритм не дает разительного превосходства по времени исполнения в сравнении с последовательным, но все же имеет место заметное уменьшение времени исполнения при выполнении указанных условий.&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
Реализация архитектурно независима. Зависимы только макрооперации, которые предполагаются уже реализованными.&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
В силу потери ортогональности алгоритм Грамма-Шмидта пользуется небольшой популярностью в сравнении с альтернативными методами QR-разложения, так что популярных параллельных реализаций нет.&lt;br /&gt;
Альтернативный (не Грамм-Шмидт) метод представлен, например, в пакете LAPACK и является одним из лучших по производительности.&lt;br /&gt;
== Литература ==&lt;br /&gt;
#Е. Е. Тыртышников, Методы численного анализа&lt;br /&gt;
#Ф. Р. Гантмахер, Теория матриц&lt;br /&gt;
#С. Ленг, Алгебра&lt;br /&gt;
#Е. Е. Тыртышников, Основы алгебры&lt;/div&gt;</summary>
		<author><name>Egor Luckjanow 403</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:Egor_Luckjanow_403/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%93%D1%80%D0%B0%D0%BC%D0%BC%D0%B0-%D0%A8%D0%BC%D0%B8%D0%B4%D1%82%D0%B0&amp;diff=28724</id>
		<title>Участник:Egor Luckjanow 403/Алгоритм Грамма-Шмидта</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:Egor_Luckjanow_403/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%93%D1%80%D0%B0%D0%BC%D0%BC%D0%B0-%D0%A8%D0%BC%D0%B8%D0%B4%D1%82%D0%B0&amp;diff=28724"/>
		<updated>2021-12-06T10:36:00Z</updated>

		<summary type="html">&lt;p&gt;Egor Luckjanow 403: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Автор: Егор Лукьянов, студент 403 группы ВМК МГУ&lt;br /&gt;
== Свойства и структура алгоритмов ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
Алгоритм Грама-Шмидта предназначен для построения QR-разложения матрицы.&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
В основе работы алгоритма лежит метод Грама-Шмидта для построения ортонормированного базиса на основе уже существующего:&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;q_1 = \frac{a_1}{||a_1||}&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;p_2 = a_2 - (a_2, q_1)q_1&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;q_2 = \frac{p_2}{||p_2||}&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;p_3 = a_3 - (a_3, q_1)q_1 - (a_3, q_2)q_2&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;q_3 = \frac{p_3}{||p_3||}&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;...&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
Мы применяем этот метод к столбцам исходной матрицы A и получаем разложение A = QR, где Q составлена из столбцов q_1,...,q_n, полученных в результате применения метода Грама-Шмидта, а R -- матрица коэффициентов, которая будет верхнетреугольной в силу вида метода.&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Ядро алгоритма -- последовательное нахождение Q и R, как в математическом описании.&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
Макрооперации: скалярное произведение ddot; умножение матрицы на вектор с прибавлением другого вектора daxpy.&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;&lt;br /&gt;
void QR(int N, double* A, double* Q, double* R)&lt;br /&gt;
{&lt;br /&gt;
	int one = 1;&lt;br /&gt;
	for (int i = 0; i &amp;lt; N; ++i) {&lt;br /&gt;
		R[i * N + i] = 1;&lt;br /&gt;
		for (int k = 1; k &amp;lt; i + 1; ++k) {&lt;br /&gt;
			R[i - k + i * N] = ddot(&amp;amp;N, A + i * N, &amp;amp;one, Q + (i - k) * N, &amp;amp;one) / ddot(&amp;amp;N, Q + (i - k) * N, &amp;amp;one, Q + (i - k) * N, &amp;amp;one);&lt;br /&gt;
		}&lt;br /&gt;
		for (int j = 0; j &amp;lt; N; ++j) {&lt;br /&gt;
			Q[j + i * N] = A[j + i * N];&lt;br /&gt;
		}&lt;br /&gt;
		for (int k = 0; k &amp;lt; i; ++k) {&lt;br /&gt;
			double tmp = -R[i - k - 1 + i * N];&lt;br /&gt;
			daxpy(&amp;amp;N, &amp;amp;tmp, Q + (i - k - 1) * N, &amp;amp;one, Q + i * N, &amp;amp;one);&lt;br /&gt;
		}&lt;br /&gt;
		for (int k = i + 1; k &amp;lt; N; ++k) {&lt;br /&gt;
			R[k + i * N] = 0;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	for (int i = 0; i &amp;lt; N; ++i) {&lt;br /&gt;
		double norm = dnrm2(&amp;amp;N, Q + i * N, &amp;amp;one);&lt;br /&gt;
		for (int j = 0; j &amp;lt; N; ++j) {&lt;br /&gt;
			Q[j + i * N] /= norm;&lt;br /&gt;
		}&lt;br /&gt;
		for (int j = 0; j &amp;lt; N; ++j) {&lt;br /&gt;
			R[i + j * N] *= norm;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Сложность последовательного алгоритма -- &amp;lt;math&amp;gt;O(n^3)&amp;lt;/math&amp;gt;.&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
Параллельная сложность (в терминах операций сложения и умножения) -- &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;Q&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; из QR-разложения поданной на вход матрицы.&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
Алгоритм Грамма-Шмидта обладает свойством потери ортогональности. Поэтому для корректной его реализации необходимо время от времени делать реортогонализацию, а также обеспечить максимально возможную точность вычислений с плавающей точкой.&lt;br /&gt;
Можно реализовать алгоритм для работы с рациональными матрицами. Тогда проблема с потерей ортогонализации пропадает (в виду идеальной точности вычислений в кольцах вида &amp;lt;math&amp;gt;\mathbb{Z}/&amp;lt;m&amp;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;q_i&amp;lt;/math&amp;gt; для вычисления.&lt;br /&gt;
Число пересылок тогда составляет &amp;lt;math&amp;gt;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;
Параллельный алгоритм не дает разительного превосходства по времени исполнения в сравнении с последовательным, но все же имеет место заметное уменьшение времени исполнения при выполнении указанных условий.&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
Реализация архитектурно независима. Зависимы только макрооперации, которые предполагаются уже реализованными.&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
В силу потери ортогональности алгоритм Грамма-Шмидта пользуется небольшой популярностью в сравнении с альтернативными методами QR-разложения, так что популярных параллельных реализаций нет.&lt;br /&gt;
Альтернативный (не Грамм-Шмидт) метод представлен, например, в пакете LAPACK и является одним из лучших по производительности.&lt;br /&gt;
== Литература ==&lt;br /&gt;
#Е. Е. Тыртышников, Методы численного анализа&lt;br /&gt;
#Ф. Р. Гантмахер, Теория матриц&lt;br /&gt;
#С. Ленг, Алгебра&lt;br /&gt;
#Е. Е. Тыртышников, Основы алгебры&lt;/div&gt;</summary>
		<author><name>Egor Luckjanow 403</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:Egor_Luckjanow_403/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%93%D1%80%D0%B0%D0%BC%D0%BC%D0%B0-%D0%A8%D0%BC%D0%B8%D0%B4%D1%82%D0%B0&amp;diff=28723</id>
		<title>Участник:Egor Luckjanow 403/Алгоритм Грамма-Шмидта</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:Egor_Luckjanow_403/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%93%D1%80%D0%B0%D0%BC%D0%BC%D0%B0-%D0%A8%D0%BC%D0%B8%D0%B4%D1%82%D0%B0&amp;diff=28723"/>
		<updated>2021-12-06T10:26:10Z</updated>

		<summary type="html">&lt;p&gt;Egor Luckjanow 403: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Автор: Егор Лукьянов, студент 403 группы ВМК МГУ&lt;br /&gt;
== Свойства и структура алгоритмов ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
Алгоритм Грама-Шмидта предназначен для построения QR-разложения матрицы.&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
В основе работы алгоритма лежит метод Грама-Шмидта для построения ортонормированного базиса на основе уже существующего:&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;q_1 = \frac{a_1}{||a_1||}&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;p_2 = a_2 - (a_2, q_1)q_1&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;q_2 = \frac{p_2}{||p_2||}&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;p_3 = a_3 - (a_3, q_1)q_1 - (a_3, q_2)q_2&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;q_3 = \frac{p_3}{||p_3||}&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;...&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
Мы применяем этот метод к столбцам исходной матрицы A и получаем разложение A = QR, где Q составлена из столбцов q_1,...,q_n, полученных в результате применения метода Грама-Шмидта, а R -- матрица коэффициентов, которая будет верхнетреугольной в силу вида метода.&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Ядро алгоритма -- последовательное нахождение Q и R, как в математическом описании.&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
Макрооперации: скалярное произведение ddot; умножение матрицы на вектор с прибавлением другого вектора daxpy.&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;&lt;br /&gt;
void QR(int N, double* A, double* Q, double* R)&lt;br /&gt;
{&lt;br /&gt;
	int one = 1;&lt;br /&gt;
	for (int i = 0; i &amp;lt; N; ++i) {&lt;br /&gt;
		R[i * N + i] = 1;&lt;br /&gt;
		//int one = 1;&lt;br /&gt;
		for (int k = 1; k &amp;lt; i + 1; ++k) {&lt;br /&gt;
			R[i - k + i * N] = ddot(&amp;amp;N, A + i * N, &amp;amp;one, Q + (i - k) * N, &amp;amp;one) / ddot(&amp;amp;N, Q + (i - k) * N, &amp;amp;one, Q + (i - k) * N, &amp;amp;one);&lt;br /&gt;
		}&lt;br /&gt;
		for (int j = 0; j &amp;lt; N; ++j) {&lt;br /&gt;
			Q[j + i * N] = A[j + i * N];&lt;br /&gt;
			/*for (int k = 0; k &amp;lt; i; ++k) {&lt;br /&gt;
				Q[j + i * N] -= R[i - k - 1 + i * N] * Q[j + (i - k - 1) * N];&lt;br /&gt;
			}*/&lt;br /&gt;
		}&lt;br /&gt;
		for (int k = 0; k &amp;lt; i; ++k) {&lt;br /&gt;
			double tmp = -R[i - k - 1 + i * N];&lt;br /&gt;
			daxpy(&amp;amp;N, &amp;amp;tmp, Q + (i - k - 1) * N, &amp;amp;one, Q + i * N, &amp;amp;one);&lt;br /&gt;
		}&lt;br /&gt;
		/*&lt;br /&gt;
		double norm = dnrm2(&amp;amp;N, Q + i * N, &amp;amp;one);&lt;br /&gt;
		for (int j = 0; j &amp;lt; N; ++j) {&lt;br /&gt;
			Q[j + i * N] /= norm;&lt;br /&gt;
		}&lt;br /&gt;
		for (int k = 0; k &amp;lt; i + 1; ++k) {&lt;br /&gt;
			R[k + i * N] *= norm;&lt;br /&gt;
		}*/&lt;br /&gt;
		for (int k = i + 1; k &amp;lt; N; ++k) {&lt;br /&gt;
			R[k + i * N] = 0;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	for (int i = 0; i &amp;lt; N; ++i) {&lt;br /&gt;
		double norm = dnrm2(&amp;amp;N, Q + i * N, &amp;amp;one);&lt;br /&gt;
		for (int j = 0; j &amp;lt; N; ++j) {&lt;br /&gt;
			Q[j + i * N] /= norm;&lt;br /&gt;
		}&lt;br /&gt;
		for (int j = 0; j &amp;lt; N; ++j) {&lt;br /&gt;
			R[i + j * N] *= norm;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Сложность последовательного алгоритма -- &amp;lt;math&amp;gt;O(n^3)&amp;lt;/math&amp;gt;.&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
Параллельная сложность (в терминах операций сложения и умножения) -- &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;Q&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; из QR-разложения поданной на вход матрицы.&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
Алгоритм Грамма-Шмидта обладает свойством потери ортогональности. Поэтому для корректной его реализации необходимо время от времени делать реортогонализацию, а также обеспечить максимально возможную точность вычислений с плавающей точкой.&lt;br /&gt;
Можно реализовать алгоритм для работы с рациональными матрицами. Тогда проблема с потерей ортогонализации пропадает (в виду идеальной точности вычислений в поле частных колец вида &amp;lt;math&amp;gt;\mathbb{Z}/&amp;lt;m&amp;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;
Метод Грамма-Шмидта плохо приспособлен для параллельной реализации. Единственный способ, известный автору -- отдать каждому процессу по группе столбцов &amp;lt;math&amp;gt;q_i&amp;lt;/math&amp;gt; для вычисления.&lt;br /&gt;
Число пересылок тогда составляет &amp;lt;math&amp;gt;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;
Параллельный алгоритм не дает разительного превосходства по времени исполнения в сравнении с последовательным, но все же имеет место заметное уменьшение времени исполнения при выполнении указанных условий.&lt;br /&gt;
=== Выводы для классов архитектур ===&lt;br /&gt;
Реализация архитектурно независима. Зависимы только макрооперации, которые предполагаются уже реализованными.&lt;br /&gt;
=== Существующие реализации алгоритма ===&lt;br /&gt;
В силу потери ортогональности алгоритм Грамма-Шмидта пользуется небольшой популярностью в сравнении с альтернативными методами QR-разложения, так что популярных параллельных реализаций нет.&lt;br /&gt;
Альтернативный (не Грамм-Шмидт) метод представлен, например, в пакете LAPACK и является одним из лучших по производительности.&lt;br /&gt;
== Литература ==&lt;br /&gt;
#Е. Е. Тыртышников, Методы численного анализа&lt;br /&gt;
#Ф. Р. Гантмахер, Теория матриц&lt;br /&gt;
#С. Ленг, Алгебра&lt;br /&gt;
#Е. Е. Тыртышников, Основы алгебры&lt;/div&gt;</summary>
		<author><name>Egor Luckjanow 403</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:Egor_Luckjanow_403/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%93%D1%80%D0%B0%D0%BC%D0%BC%D0%B0-%D0%A8%D0%BC%D0%B8%D0%B4%D1%82%D0%B0&amp;diff=28722</id>
		<title>Участник:Egor Luckjanow 403/Алгоритм Грамма-Шмидта</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:Egor_Luckjanow_403/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%93%D1%80%D0%B0%D0%BC%D0%BC%D0%B0-%D0%A8%D0%BC%D0%B8%D0%B4%D1%82%D0%B0&amp;diff=28722"/>
		<updated>2021-12-06T10:11:28Z</updated>

		<summary type="html">&lt;p&gt;Egor Luckjanow 403: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Автор: Егор Лукьянов, студент 403 группы ВМК МГУ&lt;br /&gt;
== Свойства и структура алгоритмов ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
Алгоритм Грама-Шмидта предназначен для построения QR-разложения матрицы.&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
В основе работы алгоритма лежит метод Грама-Шмидта для построения ортонормированного базиса на основе уже существующего:&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;q_1 = \frac{a_1}{||a_1||}&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;p_2 = a_2 - (a_2, q_1)q_1&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;q_2 = \frac{p_2}{||p_2||}&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;p_3 = a_3 - (a_3, q_1)q_1 - (a_3, q_2)q_2&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;q_3 = \frac{p_3}{||p_3||}&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;...&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
Мы применяем этот метод к столбцам исходной матрицы A и получаем разложение A = QR, где Q составлена из столбцов q_1,...,q_n, полученных в результате применения метода Грама-Шмидта, а R -- матрица коэффициентов, которая будет верхнетреугольной в силу вида метода.&lt;br /&gt;
=== Вычислительное ядро алгоритма ===&lt;br /&gt;
Ядро алгоритма -- последовательное нахождение Q и R, как в математическом описании.&lt;br /&gt;
=== Макроструктура алгоритма ===&lt;br /&gt;
Макрооперации: скалярное произведение ddot; умножение матрицы на вектор с прибавлением другого вектора daxpy.&lt;br /&gt;
=== Схема реализации последовательного алгоритма ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;cpp&amp;quot;&amp;gt;&lt;br /&gt;
void QR(int N, double* A, double* Q, double* R)&lt;br /&gt;
{&lt;br /&gt;
	int one = 1;&lt;br /&gt;
	for (int i = 0; i &amp;lt; N; ++i) {&lt;br /&gt;
		R[i * N + i] = 1;&lt;br /&gt;
		//int one = 1;&lt;br /&gt;
		for (int k = 1; k &amp;lt; i + 1; ++k) {&lt;br /&gt;
			R[i - k + i * N] = ddot(&amp;amp;N, A + i * N, &amp;amp;one, Q + (i - k) * N, &amp;amp;one) / ddot(&amp;amp;N, Q + (i - k) * N, &amp;amp;one, Q + (i - k) * N, &amp;amp;one);&lt;br /&gt;
		}&lt;br /&gt;
		for (int j = 0; j &amp;lt; N; ++j) {&lt;br /&gt;
			Q[j + i * N] = A[j + i * N];&lt;br /&gt;
			/*for (int k = 0; k &amp;lt; i; ++k) {&lt;br /&gt;
				Q[j + i * N] -= R[i - k - 1 + i * N] * Q[j + (i - k - 1) * N];&lt;br /&gt;
			}*/&lt;br /&gt;
		}&lt;br /&gt;
		for (int k = 0; k &amp;lt; i; ++k) {&lt;br /&gt;
			double tmp = -R[i - k - 1 + i * N];&lt;br /&gt;
			daxpy(&amp;amp;N, &amp;amp;tmp, Q + (i - k - 1) * N, &amp;amp;one, Q + i * N, &amp;amp;one);&lt;br /&gt;
		}&lt;br /&gt;
		/*&lt;br /&gt;
		double norm = dnrm2(&amp;amp;N, Q + i * N, &amp;amp;one);&lt;br /&gt;
		for (int j = 0; j &amp;lt; N; ++j) {&lt;br /&gt;
			Q[j + i * N] /= norm;&lt;br /&gt;
		}&lt;br /&gt;
		for (int k = 0; k &amp;lt; i + 1; ++k) {&lt;br /&gt;
			R[k + i * N] *= norm;&lt;br /&gt;
		}*/&lt;br /&gt;
		for (int k = i + 1; k &amp;lt; N; ++k) {&lt;br /&gt;
			R[k + i * N] = 0;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	for (int i = 0; i &amp;lt; N; ++i) {&lt;br /&gt;
		double norm = dnrm2(&amp;amp;N, Q + i * N, &amp;amp;one);&lt;br /&gt;
		for (int j = 0; j &amp;lt; N; ++j) {&lt;br /&gt;
			Q[j + i * N] /= norm;&lt;br /&gt;
		}&lt;br /&gt;
		for (int j = 0; j &amp;lt; N; ++j) {&lt;br /&gt;
			R[i + j * N] *= norm;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
=== Последовательная сложность алгоритма ===&lt;br /&gt;
Сложность последовательного алгоритма -- &amp;lt;math&amp;gt;O(n^3)&amp;lt;/math&amp;gt;.&lt;br /&gt;
=== Информационный граф ===&lt;br /&gt;
=== Ресурс параллелизма алгоритма ===&lt;br /&gt;
Параллельная сложность (в терминах операций сложения и умножения) -- &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;Q&amp;lt;/math&amp;gt; и &amp;lt;math&amp;gt;R&amp;lt;/math&amp;gt; из QR-разложения поданной на вход матрицы.&lt;br /&gt;
=== Свойства алгоритма ===&lt;br /&gt;
Алгоритм Грама-Шмидта обладает свойством потери ортогональности. Поэтому для корректной его реализации необходимо время от времени делать реортогонализацию, а также обеспечить максимально возможную точность вычислений с плавающей точкой.&lt;br /&gt;
Можно реализовать алгоритм для работы с рациональными матрицами. Тогда проблема с потерей ортогонализации пропадает (в виду идеальной точности вычислений в поле частных колец вида &amp;lt;math&amp;gt;\mathbb{Z}/&amp;lt;m&amp;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;
Метод Грама-Шмидта плохо приспособлен для параллельной реализации. Единственный способ, известный автору -- отдать каждому процессу по группе столбцов &amp;lt;math&amp;gt;q_i&amp;lt;/math&amp;gt; для вычисления.&lt;br /&gt;
Число пересылок тогда составляет &amp;lt;math&amp;gt;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;/div&gt;</summary>
		<author><name>Egor Luckjanow 403</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:Egor_Luckjanow_403/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%93%D1%80%D0%B0%D0%BC%D0%BC%D0%B0-%D0%A8%D0%BC%D0%B8%D0%B4%D1%82%D0%B0&amp;diff=28612</id>
		<title>Участник:Egor Luckjanow 403/Алгоритм Грамма-Шмидта</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:Egor_Luckjanow_403/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%93%D1%80%D0%B0%D0%BC%D0%BC%D0%B0-%D0%A8%D0%BC%D0%B8%D0%B4%D1%82%D0%B0&amp;diff=28612"/>
		<updated>2021-10-26T18:37:01Z</updated>

		<summary type="html">&lt;p&gt;Egor Luckjanow 403: Автора добавили, ибо мерзко как-то, когда у статьи автора нету...&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Автор: Егор Лукьянов, студент 403 группы ВМК МГУ&lt;br /&gt;
== Свойства и структура алгоритмов ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
Алгоритм Грама-Шмидта предназначен для построения QR-разложения матрицы.&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
В основе работы алгоритма лежит метод Грама-Шмидта для построения ортонормированного базиса на основе уже существующего:&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;q_1 = \frac{a_1}{||a_1||}&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;p_2 = a_2 - (a_2, q_1)q_1&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;q_2 = \frac{p_2}{||p_2||}&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;p_3 = a_3 - (a_3, q_1)q_1 - (a_3, q_2)q_2&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;q_3 = \frac{p_3}{||p_3||}&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;...&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
Мы применяем этот метод к столбцам исходной матрицы A и получаем разложение A = QR, где Q составлена из столбцов q_1,...,q_n, полученных в результате применения метода Грама-Шмидта, а R -- матрица коэффициентов, которая будет верхнетреугольной в силу вида метода.&lt;br /&gt;
== Литература ==&lt;br /&gt;
#Е. Е. Тыртышников, Методы численного анализа&lt;br /&gt;
#Ф. Р. Гантмахер, Теория матриц&lt;br /&gt;
#С. Ленг, Алгебра&lt;br /&gt;
#Е. Е. Тыртышников, Основы алгебры&lt;/div&gt;</summary>
		<author><name>Egor Luckjanow 403</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:Egor_Luckjanow_403/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%93%D1%80%D0%B0%D0%BC%D0%BC%D0%B0-%D0%A8%D0%BC%D0%B8%D0%B4%D1%82%D0%B0&amp;diff=28611</id>
		<title>Участник:Egor Luckjanow 403/Алгоритм Грамма-Шмидта</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:Egor_Luckjanow_403/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%93%D1%80%D0%B0%D0%BC%D0%BC%D0%B0-%D0%A8%D0%BC%D0%B8%D0%B4%D1%82%D0%B0&amp;diff=28611"/>
		<updated>2021-10-26T18:35:33Z</updated>

		<summary type="html">&lt;p&gt;Egor Luckjanow 403: Создание&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Свойства и структура алгоритмов ==&lt;br /&gt;
=== Общее описание алгоритма ===&lt;br /&gt;
Алгоритм Грама-Шмидта предназначен для построения QR-разложения матрицы.&lt;br /&gt;
=== Математическое описание алгоритма ===&lt;br /&gt;
В основе работы алгоритма лежит метод Грама-Шмидта для построения ортонормированного базиса на основе уже существующего:&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;q_1 = \frac{a_1}{||a_1||}&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;p_2 = a_2 - (a_2, q_1)q_1&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;q_2 = \frac{p_2}{||p_2||}&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;p_3 = a_3 - (a_3, q_1)q_1 - (a_3, q_2)q_2&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;q_3 = \frac{p_3}{||p_3||}&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;...&amp;lt;/math&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
Мы применяем этот метод к столбцам исходной матрицы A и получаем разложение A = QR, где Q составлена из столбцов q_1,...,q_n, полученных в результате применения метода Грама-Шмидта, а R -- матрица коэффициентов, которая будет верхнетреугольной в силу вида метода.&lt;br /&gt;
== Литература ==&lt;br /&gt;
#Е. Е. Тыртышников, Методы численного анализа&lt;br /&gt;
#Ф. Р. Гантмахер, Теория матриц&lt;br /&gt;
#С. Ленг, Алгебра&lt;br /&gt;
#Е. Е. Тыртышников, Основы алгебры&lt;/div&gt;</summary>
		<author><name>Egor Luckjanow 403</name></author>
	</entry>
</feed>