Уровень алгоритма

Классический метод ортогонализации: различия между версиями

Материал из Алговики
Перейти к навигации Перейти к поиску
[непроверенная версия][досмотренная версия]
мНет описания правки
Нет описания правки
 
(не показано 5 промежуточных версий 2 участников)
Строка 2: Строка 2:
| name              = Классическая ортогонализация Грама-Шмидта
| name              = Классическая ортогонализация Грама-Шмидта
| serial_complexity = <math>O(N^3)</math>
| serial_complexity = <math>O(N^3)</math>
| pf_height        = <math>O(N)</math>
| pf_height        = <math>O(N^2)</math>
| pf_width          = <math>O(N^2)</math>
| pf_width          = <math>O(N)</math>
| input_data        = <math>N^2</math>
| input_data        = <math>N^2</math>
| output_data      = <math>3N^2/2</math>
| output_data      = <math>3N^2/2</math>
}}
}}
Основные авторы описания: [[Участник:DVIN|Инжелевская Дарья Валерьевна]] (свойства и структура алгоритмов), [[Участник:Frolov|А.В.Фролов]](общая редактура текста)


== Свойства и структура алгоритмов ==
== Свойства и структура алгоритмов ==
=== Общее описание алгоритма ===
=== Общее описание алгоритма ===
Создатели алгоритма Эрхард Шмидт и Йорген Педерсен Грам.


'''Эрхард Шмидт''' (14 января 1876 — 6 декабря 1959) — немецкий математик, с 1917 года профессор Берлинского университета. В 1946—58 первый директор Института математики АН ГДР. Основные труды по теории функций, интегральным уравнениям, функциональному анализу. Определил и изучил геометрически гильбертово пространство, используя аналогии с геометрией Евклида. Занимался квадратичными формами. Известен оператор Гильберта-Шмидта и процесс Грама ― Шмидта.
Процесс ортогонализации Грама-Шмидта — это один из алгоритмов, в которых на основе множества линейно независимых векторов <math>{\displaystyle \mathbf {a} _{1},\;\ldots ,\;\mathbf {a} _{N}}</math> строится множество ортогональных <math>{\displaystyle \mathbf {b}_{1},\;\ldots ,\;\mathbf {b} _{N}} </math> или ортонормированных векторов <math>{\displaystyle \mathbf {e} _{1},\;\ldots ,\;\mathbf {e}_{N}} </math>, причём так, что каждый вектор <math>{\displaystyle \mathbf {b} _{j}} </math> или <math>{\displaystyle \mathbf {e} _{j}}</math> может быть выражен линейной комбинацией векторов <math>{\displaystyle \mathbf {a} _{1},\;\ldots ,\; \mathbf {a} _{j}}</math>.
 
'''Йорген Педерсен Грам''' (27 июня 1850 - 29 апреля 1916) датский математик, родился в герцогстве Шлезвиг, Дания и умер в Копенгагене, Дания. Основные его работы "On series expansions determined by the methods of least squares", "Investigations of the number of primes less than a given number". Математический метод, который носит его имя, процесс Грама-Шмидта, впервые был опубликован в 1883. Теорема Грама и Определитель Грама также назван в его честь.
 
Процесс Грама-Шмидта — это один из алгоритмов, в которых на основе множества линейно независимых векторов <math>{\displaystyle \mathbf {a} _{1},\;\ldots ,\;\mathbf {a} _{N}}</math> строится множество ортогональных векторов <math>{\displaystyle \mathbf {b}_{1},\;\ldots ,\;\mathbf {b} _{N}} </math> или ортонормированных векторов <math>{\displaystyle \mathbf {e} _{1},\;\ldots ,\;\mathbf {e}_{N}} </math>, причём так, что каждый вектор <math>{\displaystyle \mathbf {b} _{j}} </math> или <math>{\displaystyle \mathbf {e} _{j}}</math> может быть выражен линейной комбинацией векторов <math>{\displaystyle \mathbf {a} _{1},\;\ldots ,\; \mathbf {a} _{j}}</math>.


=== Математическое описание алгоритма ===
=== Математическое описание алгоритма ===
==== Классический процесс Грама — Шмидта ====


===== Алгоритм =====
Пусть имеются линейно независимые векторы <math>\mathbf{a}_1,\;\ldots,\;\mathbf{a}_N</math>.
Пусть имеются линейно независимые векторы <math>\mathbf{a}_1,\;\ldots,\;\mathbf{a}_N</math>.


Определим оператор проекции следующим образом: <math>\mathbf{proj}_{\mathbf{b}}\,\mathbf{a} = {\langle \mathbf{a}, \mathbf{b} \rangle \over \langle \mathbf{b}, \mathbf{b}\rangle} \mathbf{b} ,</math>  
Если определить оператор проекции следующим образом: <math>\mathbf{proj}_{\mathbf{b}}\,\mathbf{a} = {\langle \mathbf{a}, \mathbf{b} \rangle \over \langle \mathbf{b}, \mathbf{b}\rangle} \mathbf{b} ,</math>  
 
где <math>\langle \mathbf{a}, \mathbf{b} \rangle</math> — скалярное произведение векторов <math>\mathbf{a}</math> и <math>\mathbf{b}</math>.
 
Скалярное произведение для двух векторов  <math>\mathbf{ a= [a_1, a_2, ...,a_k]}</math> и <math>\mathbf{ b= [b_1, b_2, ..., b_k]}</math>  в '''''k'''''-мерном действительном пространстве определяется как:
:<math>\langle \mathbf{a}, \mathbf{b} \rangle=\sum_{i=1}^k a_ib_i=a_1b_1+a_2b_2+\cdots+ a_kb_k</math>.


Этот оператор проецирует вектор <math>\mathbf{a}</math> коллинеарно вектору <math>\mathbf{b}</math>.
где <math>\langle \mathbf{a}, \mathbf{b} \rangle</math> — скалярное произведение векторов <math>\mathbf{a}</math> и <math>\mathbf{b}</math>,


Ортогональность векторов <math>\mathbf{a}</math> и <math>\mathbf{b}</math> достигается на шаге (2).
то классический процесс Грама — Шмидта выполняется следующим образом:
 
Классический процесс Грама — Шмидта выполняется следующим образом:


: <math>
: <math>
Строка 48: Строка 37:




На основе каждого вектора <math>\mathbf{b}_j \;(j = 1 \ldots N)</math> может быть получен нормированный вектор: <math>\mathbf{e}_j = {\mathbf{b}_j\over \| \mathbf{b}_j \|}</math> (у нормированного вектора направление будет таким же, как у исходного, а длина — единичной). Причем берется норма согласованная со скалярным произведением <math>\| x \| = \sqrt{\langle x, x \rangle}</math>
На основе каждого вектора <math>\mathbf{b}_j \;(j = 1 \ldots N)</math> может быть получен нормированный вектор: <math>\mathbf{e}_j = {\mathbf{b}_j\over \| \mathbf{b}_j \|_2}</math> (у нормированного вектора направление будет таким же, как у исходного, а длина — единичной).


Результаты процесса Грама — Шмидта:
Результаты процесса Грама — Шмидта:
Строка 55: Строка 44:


<math>\mathbf{e}_1,\;\ldots,\;\mathbf{e}_N</math> — система ортонормированных векторов.
<math>\mathbf{e}_1,\;\ldots,\;\mathbf{e}_N</math> — система ортонормированных векторов.
Вычисление <math>\mathbf{b}_1,\;\ldots,\;\mathbf{b}_N</math> носит название ортогонализации Грама — Шмидта, а <math>\mathbf{e}_1,\;\ldots,\;\mathbf{e}_N</math> — ортонормализации Грама — Шмидта.
===== Доказательство =====
Докажем ортогональность векторов <math>\mathbf{b}_1,\;\ldots,\;\mathbf{b}_N</math>.
Для этого вычислим скалярное произведение <math>\langle \mathbf{b}_1, \mathbf{b}_2 \rangle</math>, подставив в него формулу (2). Мы получим ноль. Равенство нулю скалярного произведения векторов означает, что эти векторы ортогональны. Затем вычислим скалярное произведение <math>\langle \mathbf{b}_1, \mathbf{b}_3 \rangle</math>, используя результат для <math>\langle \mathbf{b}_1, \mathbf{b}_2 \rangle</math> и формулу (3). Мы снова получим ноль, то есть векторы <math>\mathbf{b}_1</math> и <math>\mathbf{b}_3</math> ортогональны. Общее доказательство выполняется методом математической индукции.
===== Геометрическая интерпретация — вариант 1 =====
[[Файл:Gram-Schmidt-step2.svg|right|frame|Рис. 1. Второй шаг процесса Грама — Шмидта]]
Рассмотрим формулу (2) — второй шаг алгоритма.
Её геометрическое представление изображено на рис. 1:
1 — получение проекции вектора <math>\mathbf{a}_2</math> на <math>\mathbf{b}_1</math>;
2 — вычисление <math>\mathbf{a}_2-\mathbf{proj}_{\mathbf{b}_1}\mathbf{a}_2</math>, то есть [[перпендикуляр]]а, которым выполняется проецирование конца <math>\mathbf{a}_2</math> на <math>\mathbf{b}_1</math>.
Этот перпендикуляр — вычисляемый в формуле (2) вектор <math>\mathbf{b}_2</math>;
3 — перемещение полученного на шаге 2 вектора <math>\mathbf{b}_2</math> в начало координат. Это перемещение сделано на рисунке лишь для наглядности. Оно не является математическим действием и поэтому не отражается в формуле (2).
На рисунке видно, что вектор <math>\mathbf{b}_2</math> ортогонален вектору <math>\mathbf{b}_1</math>, так как <math>\mathbf{b}_2</math> является перпендикуляром, по которому <math>\mathbf{a}_2</math> проецируется на <math>\mathbf{b}_1</math>.
Рассмотрим формулу (3) — третий шаг алгоритма — в следующем варианте:
: <math>
\begin{array}{lcr}
\mathbf{b}_3 = \mathbf{a}_3-(\mathbf{proj}_{\mathbf{b}_1}\mathbf{a}_3+\mathbf{proj}_{\mathbf{b}_2}\mathbf{a}_3) & & (6) \\
\end{array}
</math>
Её геометрическое представление изображено на рис. 2:
[[Файл:Gram-schmidt-step3base1.png|center|frame|Рис. 2. Третий шаг процесса Грама — Шмидта]]
1 — получение проекции вектора <math>\mathbf{a}_3</math> на <math>\mathbf{b}_1</math>;
2 — получение проекции вектора <math>\mathbf{a}_3</math> на <math>\mathbf{b}_2</math>;
3 — вычисление суммы <math>\mathbf{proj}_{\mathbf{b}_1}\mathbf{a}_3 + \mathbf{proj}_{\mathbf{b}_2}\mathbf{a}_3</math>, то есть проекции вектора <math>\mathbf{a}_3</math> на плоскость, образуемую векторами <math>\mathbf{b}_1</math> и <math>\mathbf{b}_2</math>. Эта плоскость закрашена на рисунке серым цветом;
4 — вычисление <math>\mathbf{a}_3-(\mathbf{proj}_{\mathbf{b}_1}\mathbf{a}_3 + \mathbf{proj}_{\mathbf{b}_2}\mathbf{a}_3)</math>, то есть перпендикуляра, которым выполняется проецирование конца <math>\mathbf{a}_3</math> на плоскость, образуемую векторами <math>\mathbf{b}_1</math> и <math>\mathbf{b}_2</math>. Этот перпендикуляр — вычисляемый в формуле (6) вектор <math>\mathbf{b}_3</math>;
5 — перемещение полученного <math>\mathbf{b}_3</math> в начало координат. Это перемещение сделано на рисунке лишь для наглядности. Оно не является математическим действием и поэтому не отражается в формуле (6).
На рисунке видно, что вектор <math>\mathbf{b}_3</math> ортогонален векторам <math>\mathbf{b}_1</math> и <math>\mathbf{b}_2</math>, так как <math>\mathbf{b}_3</math> является перпендикуляром, по которому <math>\mathbf{a}_3</math> проецируется на плоскость, образуемую векторами <math>\mathbf{b}_1</math> и <math>\mathbf{b}_2</math>.
Таким образом, в процессе Грама — Шмидта для вычисления <math>\mathbf{b}_j</math> выполняется проецирование <math>\mathbf{a}_j</math> ортогонально на [[гиперплоскость]]?, формируемую векторами <math>\mathbf{b}_1,\;\ldots,\;\mathbf{b}_{j-1}</math>. Вектор <math>\mathbf{b}_j</math> затем вычисляется как разность между <math>\mathbf{a}_j</math> и его проекцией. То есть <math>\mathbf{b}_j</math> — это перпендикуляр? от конца <math>\mathbf{a}_j</math> к гиперплоскости?, формируемой векторами <math>\mathbf{b}_1,\;\ldots,\;\mathbf{b}_{j-1}</math>. Поэтому <math>\mathbf{b}_j</math> ортогонален векторам, образующим эту гиперплоскость?.
===== Численная неустойчивость =====
При вычислении на ЭВМ по формулам (1) — (5) векторы <math>\mathbf{b}_1,\;\ldots,\;\mathbf{b}_{N-1}</math> часто не точно ортогональны из-за [[Ошибка квантования|ошибок округления]]. Из-за потери ортогональности в процессе вычислений классический процесс Грама — Шмидта называют [[Численная устойчивость|численно неустойчивым]].
==== Модифицированный процесс Грама — Шмидта ====
Процесс Грама — Шмидта может быть сделан более вычислительно устойчивым путём небольшой модификации. Вместо вычисления <math>\mathbf{b}_j</math> как: <math> \mathbf{b}_j = \mathbf{a}_j - \mathbf{proj}_{\mathbf{b}_1}\,\mathbf{a}_j - \mathbf{proj}_{\mathbf{b}_2}\,\mathbf{a}_j - \ldots - \mathbf{proj}_{\mathbf{b}_{j-1}}\,\mathbf{a}_j  \; (7) </math> этот вектор вычисляется следующим образом:
: <math> \begin{array}{lclr}
\mathbf{a}_j^{(1)} & = &\mathbf{a}_j - \mathbf{proj}_{\mathbf{b}_1}\,\mathbf{a}_j & (8) \\
\mathbf{a}_j^{(2)} & = &\mathbf{a}_j^{(1)} - \mathbf{proj}_{\mathbf{b}_2} \, \mathbf{a}_j^{(1)} & (9) \\& \vdots & & \\
\mathbf{a}_j^{(j-2)} & = & \mathbf{a}_j^{(j-3)} - \mathbf{proj}_{\mathbf{b}_{j-2}} \, \mathbf{a}_j^{(j-3)} & (10) \\
\mathbf{b}_j & = & \mathbf{a}_j^{(j-2)} - \mathbf{proj}_{\mathbf{b}_{j-1}} \, \mathbf{a}_j^{(j-2)} & (11)
\end{array} </math>


=== Вычислительное ядро алгоритма ===
=== Вычислительное ядро алгоритма ===
Строка 119: Строка 50:


=== Макроструктура алгоритма ===
=== Макроструктура алгоритма ===
Данный алгоритм использует в качестве составных частей другие алгоритмы. В дальнейшем имеет смысл описывать алгоритм не в максимально детализированном виде (т.е. на уровне арифметических операций), а давать только его макроструктуру, то есть описывает структуру и состав макроопераций. Типичной макрооперацией, часто встречающиеся в алгоритме является оператор проекции векторов. Как записано и в описании ядра алгоритма, основную часть метода ортогонализации Грамма-Шмидта составляют множественные (всего их <math>\frac{N(N-1)}{2}</math>) вычисления оператора проекции.
 
Данный алгоритм использует в качестве составных частей другие, более мелкие. Далее описание будет не в максимально детализированном виде (т.е. на уровне арифметических операций), а только на уровне его макроструктуры. Типичной макрооперацией, часто встречающиеся в алгоритме является оператор проекции векторов. Как записано и в описании ядра алгоритма, основную часть метода ортогонализации Грамма-Шмидта составляют множественные (всего их <math>\frac{N(N-1)}{2}</math>) вычисления оператора проекции.


=== Схема реализации последовательного алгоритма ===
=== Схема реализации последовательного алгоритма ===


Следующий алгоритм реализует нормализацию Грамма-Шмидта. Векторы <math>v_1,...,v_k</math> заменяются набором ортонормированных векторов, которые имеют ту же линейную оболочку.
Следующий алгоритм реализует нормализацию Грамма-Шмидта. Векторы <math>v_1,...,v_k</math> заменяются набором ортонормированных векторов, которые имеют ту же линейную оболочку.
[[File:Gram-Schmidt ortho.png|none|thumb]]
[[File:Gram-Schmidt ortho.png|none|thumb]]


Строка 132: Строка 65:
1. <math>\mathbf  {b}_{1}=\mathbf  {a}_{1}</math>
1. <math>\mathbf  {b}_{1}=\mathbf  {a}_{1}</math>


2. Далее для всех векторов <math>\mathbf  {b}_{i}</math> для <math>i=2 ... N</math> производим вычисление по следующей формуле:
2. Далее для всех векторов <math>\mathbf  {b}_{i}</math> для <math>i=2 ... N</math> производится вычисление по следующей формуле:
<math>{\mathbf  {b}}_{i}={\mathbf  {a}}_{i}-\displaystyle \sum _{{j=1}}^{{i-1}}{\mathbf  {proj}}_{{{\mathbf  {b}}_{j}}}\,{\mathbf  {a}}_{i}</math>.  
<math>{\mathbf  {b}}_{i}={\mathbf  {a}}_{i}-\displaystyle \sum _{{j=1}}^{{i-1}}{\mathbf  {proj}}_{{{\mathbf  {b}}_{j}}}\,{\mathbf  {a}}_{i}</math>.  


В ней на каждом шаге <math>i</math> по очереди вычисляются все <math>{\mathbf  {proj}}_{{{\mathbf  {b}}_{j}}}\,{\mathbf  {a}}_{i}</math> для <math>j=1 ... i-1</math>
В ней на каждом шаге <math>i</math> по очереди вычисляются все <math>{\mathbf  {proj}}_{{{\mathbf  {b}}_{j}}}\,{\mathbf  {a}}_{i}</math> для <math>j=1 ... i-1</math>


Пример реализации на Python. Функция работает для произвольного количества векторов любой размерности. При этом если количество векторов <math>\mathbf{a}_1,\;\ldots,\;\mathbf{a}_N</math> больше их размерности или они линейно зависимы то функция возвращает максимально возможное число линейно независимых векторов <math>\mathbf{b}_1,\;\ldots,\;\mathbf{b}_n</math>, а остальные векторы <math>\mathbf{b}_{n+1},\;\ldots,\;\mathbf{b}_N</math> нулевые.
==== Пример реализации на Python ====
 
Функция работает для произвольного количества векторов любой размерности. При этом если количество векторов <math>\mathbf{a}_1,\;\ldots,\;\mathbf{a}_N</math> больше их размерности или они линейно зависимы то функция возвращает максимально возможное число линейно независимых векторов <math>\mathbf{b}_1,\;\ldots,\;\mathbf{b}_n</math>, а остальные векторы <math>\mathbf{b}_{n+1},\;\ldots,\;\mathbf{b}_N</math> нулевые.
<source lang="python">
<source lang="python">
import random
import random
Строка 192: Строка 127:


=== Информационный граф ===
=== Информационный граф ===
Опишем граф алгоритма как аналитически, так и в виде рисунка.
Опишем граф алгоритма как аналитически, так и в виде рисунка.


Строка 235: Строка 171:
=== Ресурс параллелизма алгоритма ===
=== Ресурс параллелизма алгоритма ===


В параллельном варианте, в отличие от последовательного, вычисления  <math>\mathbf{proj}_{\mathbf{b_j}}\,\mathbf{a_i}</math>  можно выполнять параллельно. То есть после каждого вычисления <math>b_j</math> можно запускать параллельное вычисление <math>\mathbf{proj}_{\mathbf{b_j}}\,\mathbf{a_i}</math> для всех <math>i=j+1..N</math>. Кроме того можно параллельно производить вычитание соответствующего <math>\mathbf{proj}_{\mathbf{b_j}}\,\mathbf{a_i}</math> для всех <math>i=j+1..N</math>.
В параллельном варианте, в отличие от последовательного, можно параллельно производить вычитание соответствующего <math>\mathbf{proj}_{\mathbf{b_j}}\,\mathbf{a_i}</math> для всех <math>i=j+1..N</math>. Параллельное же вычисление проекций соответствует другому алгоритму.


Для ортогонализации в параллельном варианте требуется последовательно выполнить следующие ярусы:
При классификации по высоте (количество ярусов в ЯПФ ) ЯПФ, таким образом, ортогонализация Грамма-Шмидта относится к алгоритмам со сложностью <math>O(N^2)</math>.
* <math>N</math> ярусов в каждом из которых будет выполнятся,
* по <math>N - 1</math> операторов проекций (в каждом из ярусов линейное количество проекций, в зависимости от яруса — от <math>N-1</math> до <math>1</math>),
* по <math>N - 1</math> сложений (в каждом из ярусов линейное количество сложений, в зависимости от яруса — от <math>N-1</math> до <math>1</math>),


При классификации по высоте (количество ярусов в ЯПФ ) ЯПФ, таким образом, ортогонализация Грамма-Шмидта относится к алгоритмам со сложностью <math>O(N)</math>.
При классификации по ширине (максимальное количество вершин в ярусе) ЯПФ его сложность будет <math>O(N)</math>.
 
При классификации по ширине (максимальное количество вершин в ярусе) ЯПФ его сложность будет <math>O(N^2)</math>.


=== Входные и выходные данные алгоритма ===
=== Входные и выходные данные алгоритма ===
Строка 265: Строка 196:
При этом вычислительная мощность алгоритма, как отношение числа операций к суммарному объему входных и выходных данных, также линейна.
При этом вычислительная мощность алгоритма, как отношение числа операций к суммарному объему входных и выходных данных, также линейна.


Алгоритм почти полностью детерминирован — единственность результата выполнения гарантирована, однако возможно накопление ошибок округления при использовании классического процесса Грама-Шмидта. При этом модифицированный алгоритм дает более хороший результат.
Алгоритм почти полностью детерминирован — единственность результата выполнения гарантирована, однако возможно накопление ошибок округления при использовании классического процесса Грама-Шмидта.  


Алгоритм является численно неустойчивым — ошибки округления могут привести к неортогональности полученных векторов.
Алгоритм является численно неустойчивым — ошибки округления могут привести к неортогональности полученных векторов.
Строка 274: Строка 205:


Процесс Грама ― Шмидта может быть истолкован как разложение невырожденной квадратной матрицы в произведение ортогональной (или унитарной матрицы в случае эрмитова пространства) и верхнетреугольной матрицы с положительными диагональными элементами ― QR-разложение, что есть частный случай разложения Ивасавы.
Процесс Грама ― Шмидта может быть истолкован как разложение невырожденной квадратной матрицы в произведение ортогональной (или унитарной матрицы в случае эрмитова пространства) и верхнетреугольной матрицы с положительными диагональными элементами ― QR-разложение, что есть частный случай разложения Ивасавы.
== Программная реализация алгоритма ==
=== Особенности реализации последовательного алгоритма ===
=== Возможные способы и особенности параллельной реализации алгоритма ===
=== Результаты прогонов ===
=== Выводы для классов архитектур ===
== Литература ==
<references />
[[Категория:Статьи в работе]]
[[en:Classical orthogonalization method]]

Текущая версия от 05:47, 9 июля 2022


Классическая ортогонализация Грама-Шмидта
Последовательный алгоритм
Последовательная сложность [math]\displaystyle{ O(N^3) }[/math]
Объём входных данных [math]\displaystyle{ N^2 }[/math]
Объём выходных данных [math]\displaystyle{ 3N^2/2 }[/math]
Параллельный алгоритм
Высота ярусно-параллельной формы [math]\displaystyle{ O(N^2) }[/math]
Ширина ярусно-параллельной формы [math]\displaystyle{ O(N) }[/math]


Основные авторы описания: Инжелевская Дарья Валерьевна (свойства и структура алгоритмов), А.В.Фролов(общая редактура текста)

Свойства и структура алгоритмов

Общее описание алгоритма

Процесс ортогонализации Грама-Шмидта — это один из алгоритмов, в которых на основе множества линейно независимых векторов [math]\displaystyle{ {\displaystyle \mathbf {a} _{1},\;\ldots ,\;\mathbf {a} _{N}} }[/math] строится множество ортогональных [math]\displaystyle{ {\displaystyle \mathbf {b}_{1},\;\ldots ,\;\mathbf {b} _{N}} }[/math] или ортонормированных векторов [math]\displaystyle{ {\displaystyle \mathbf {e} _{1},\;\ldots ,\;\mathbf {e}_{N}} }[/math], причём так, что каждый вектор [math]\displaystyle{ {\displaystyle \mathbf {b} _{j}} }[/math] или [math]\displaystyle{ {\displaystyle \mathbf {e} _{j}} }[/math] может быть выражен линейной комбинацией векторов [math]\displaystyle{ {\displaystyle \mathbf {a} _{1},\;\ldots ,\; \mathbf {a} _{j}} }[/math].

Математическое описание алгоритма

Пусть имеются линейно независимые векторы [math]\displaystyle{ \mathbf{a}_1,\;\ldots,\;\mathbf{a}_N }[/math].

Если определить оператор проекции следующим образом: [math]\displaystyle{ \mathbf{proj}_{\mathbf{b}}\,\mathbf{a} = {\langle \mathbf{a}, \mathbf{b} \rangle \over \langle \mathbf{b}, \mathbf{b}\rangle} \mathbf{b} , }[/math]

где [math]\displaystyle{ \langle \mathbf{a}, \mathbf{b} \rangle }[/math] — скалярное произведение векторов [math]\displaystyle{ \mathbf{a} }[/math] и [math]\displaystyle{ \mathbf{b} }[/math],

то классический процесс Грама — Шмидта выполняется следующим образом:

[math]\displaystyle{ {\begin{array}{lclr} {\mathbf {b}}_{1}&=&{\mathbf {a}}_{1}&(1)\\ {\mathbf {b}}_{2}&=&{\mathbf {a}}_{2}-{\mathbf {proj}}_{{{\mathbf {b}}_{1}}}\,{\mathbf {a}}_{2}&(2)\\ {\mathbf {b}}_{3}&=&{\mathbf {a}}_{3}-{\mathbf {proj}}_{{{\mathbf {b}}_{1}}}\,{\mathbf {a}}_{3}-{\mathbf {proj}}_{{{\mathbf {b}}_{2}}}\,{\mathbf {a}}_{3}&(3)\\ {\mathbf {b}}_{4}&=&{\mathbf {a}}_{4}-{\mathbf {proj}}_{{{\mathbf {b}}_{1}}}\,{\mathbf {a}}_{4}-{\mathbf {proj}}_{{{\mathbf {b}}_{2}}}\,{\mathbf {a}}_{4}-{\mathbf {proj}}_{{{\mathbf {b}}_{3}}}\,{\mathbf {a}}_{4}&(4)\\ &\vdots &&\\{\mathbf {b}}_{N}&=&{\mathbf {a}}_{N}-\displaystyle \sum _{{j=1}}^{{N-1}}{\mathbf {proj}}_{{{\mathbf {b}}_{j}}}\,{\mathbf {a}}_{N}&(N) \end{array}} }[/math]


На основе каждого вектора [math]\displaystyle{ \mathbf{b}_j \;(j = 1 \ldots N) }[/math] может быть получен нормированный вектор: [math]\displaystyle{ \mathbf{e}_j = {\mathbf{b}_j\over \| \mathbf{b}_j \|_2} }[/math] (у нормированного вектора направление будет таким же, как у исходного, а длина — единичной).

Результаты процесса Грама — Шмидта:

[math]\displaystyle{ \mathbf{b}_1,\;\ldots,\;\mathbf{b}_N }[/math] — система ортогональных векторов либо

[math]\displaystyle{ \mathbf{e}_1,\;\ldots,\;\mathbf{e}_N }[/math] — система ортонормированных векторов.

Вычислительное ядро алгоритма

Вычислительное ядро последовательной версии метода ортогонализации Грамма-Шмидта можно составить из множественных (всего их [math]\displaystyle{ \frac{N(N-1)}{2} }[/math]) вычислений проекций : [math]\displaystyle{ \mathbf{proj}_{\mathbf{b_j}}\,\mathbf{a_i} }[/math]

Макроструктура алгоритма

Данный алгоритм использует в качестве составных частей другие, более мелкие. Далее описание будет не в максимально детализированном виде (т.е. на уровне арифметических операций), а только на уровне его макроструктуры. Типичной макрооперацией, часто встречающиеся в алгоритме является оператор проекции векторов. Как записано и в описании ядра алгоритма, основную часть метода ортогонализации Грамма-Шмидта составляют множественные (всего их [math]\displaystyle{ \frac{N(N-1)}{2} }[/math]) вычисления оператора проекции.

Схема реализации последовательного алгоритма

Следующий алгоритм реализует нормализацию Грамма-Шмидта. Векторы [math]\displaystyle{ v_1,...,v_k }[/math] заменяются набором ортонормированных векторов, которые имеют ту же линейную оболочку.

Вычислительная сложность этого [math]\displaystyle{ 2Nk^2 }[/math] операции с плавающей точкой, где N - размерность векторов.

Последовательность исполнения метода следующая:

1. [math]\displaystyle{ \mathbf {b}_{1}=\mathbf {a}_{1} }[/math]

2. Далее для всех векторов [math]\displaystyle{ \mathbf {b}_{i} }[/math] для [math]\displaystyle{ i=2 ... N }[/math] производится вычисление по следующей формуле: [math]\displaystyle{ {\mathbf {b}}_{i}={\mathbf {a}}_{i}-\displaystyle \sum _{{j=1}}^{{i-1}}{\mathbf {proj}}_{{{\mathbf {b}}_{j}}}\,{\mathbf {a}}_{i} }[/math].

В ней на каждом шаге [math]\displaystyle{ i }[/math] по очереди вычисляются все [math]\displaystyle{ {\mathbf {proj}}_{{{\mathbf {b}}_{j}}}\,{\mathbf {a}}_{i} }[/math] для [math]\displaystyle{ j=1 ... i-1 }[/math]

Пример реализации на Python

Функция работает для произвольного количества векторов любой размерности. При этом если количество векторов [math]\displaystyle{ \mathbf{a}_1,\;\ldots,\;\mathbf{a}_N }[/math] больше их размерности или они линейно зависимы то функция возвращает максимально возможное число линейно независимых векторов [math]\displaystyle{ \mathbf{b}_1,\;\ldots,\;\mathbf{b}_n }[/math], а остальные векторы [math]\displaystyle{ \mathbf{b}_{n+1},\;\ldots,\;\mathbf{b}_N }[/math] нулевые.

import random

def GramSchmidt(*a):
    k=len(a[0])
    N=len(a);
    b = [[0] * k for i in range(N)]
    b[0]=a[0]
    for i in range(1,N):
        sum=a[i]
        for j in range(0,i):
            scolar_ab=0
            scolar_bb=0
            proj=[i for i in range(k)]
            for n in range(k):
                scolar_ab+=b[j][n]*a[i][n]
                scolar_bb+=b[j][n]*b[j][n]
            for n in range(k):
                proj[n]=(scolar_ab/scolar_bb)*b[j][n]
            for n in range(k):
                sum[n]-=proj[n]
        b[i]=sum
    return b;

l1=[random.randrange(0,10) for i in range(3)]
l2=[random.randrange(0,10) for i in range(3)]
l3=[random.randrange(0,10) for i in range(3)]
print(l1,l2,l3)
print(GramSchmidt(l1,l2,l3))

Последовательная сложность алгоритма

Для построения ортогонального набора векторов в последовательном варианте требуется:

При k=N:

  • [math]\displaystyle{ \frac{N(N-1)}{2} }[/math] делений,
  • [math]\displaystyle{ \frac{N(N-1)(3N-1)}{2} }[/math] сложений (вычитаний),
  • [math]\displaystyle{ \frac{3N^2 (N-1)}{2} }[/math] умножений.

При k≠N

  • [math]\displaystyle{ \frac{N(N-1)}{2} }[/math] делений,
  • [math]\displaystyle{ \frac{N(N-1)(3k-1)}{2} }[/math] сложений (вычитаний),
  • [math]\displaystyle{ \frac{3Nk (N-1)}{2} }[/math] умножений.

При классификации по последовательной сложности, таким образом, ортогонализация Грамма-Шмидта относится к алгоритмам с кубической сложностью.

Информационный граф

Опишем граф алгоритма как аналитически, так и в виде рисунка.

Граф алгоритма состоит из двух групп вершин, расположенных в целочисленных узлах двух областей.

Первая группа вершин расположена в двумерной области, соответствующая ей операция [math]\displaystyle{ \mathbf{proj}_{\mathbf{b_j}}\,\mathbf{a_i} = {\langle \mathbf{a_i}, \mathbf{b_j} \rangle \over \langle \mathbf{b_j}, \mathbf{b_j}\rangle} \mathbf{b_j} , }[/math] .

Естественно введённые координаты области таковы:

i — меняется в диапазоне от 2 до N, принимая все целочисленные значения;

j — меняется в диапазоне от 1 до i-1, принимая все целочисленные значения.

Аргументы операции следующие:

[math]\displaystyle{ a_i }[/math]: элементы входных данных, а именно [math]\displaystyle{ a_i }[/math];

[math]\displaystyle{ b_j }[/math]: результат срабатывания операции, соответствующей вершине из второй группы, с координатой j.

Вторая группа вершин расположена в двухмерной области, соответствующая ей операция [math]\displaystyle{ a - b }[/math].

Естественно введённые координаты области таковы:

i — меняется в диапазоне от 2 до N, принимая все целочисленные значения;

j — меняется в диапазоне от 1 до i-1, принимая все целочисленные значения.

Аргументы операции следующие:

a:

j=1 - входные данные [math]\displaystyle{ a_j }[/math]

j>1 - результат срабатывания операции, соответствующей вершине из второй группы, с координатой j-1

b:

результат срабатывания операции, соответствующей вершине из первой группы, с координатой j

Рис. 5. Граф алгоритма с отображением входных и выходных данных. Proj - вычисление оператора проекции, F - операция a-b, In - входные данные, Out - результаты.

Ресурс параллелизма алгоритма

В параллельном варианте, в отличие от последовательного, можно параллельно производить вычитание соответствующего [math]\displaystyle{ \mathbf{proj}_{\mathbf{b_j}}\,\mathbf{a_i} }[/math] для всех [math]\displaystyle{ i=j+1..N }[/math]. Параллельное же вычисление проекций соответствует другому алгоритму.

При классификации по высоте (количество ярусов в ЯПФ ) ЯПФ, таким образом, ортогонализация Грамма-Шмидта относится к алгоритмам со сложностью [math]\displaystyle{ O(N^2) }[/math].

При классификации по ширине (максимальное количество вершин в ярусе) ЯПФ его сложность будет [math]\displaystyle{ O(N) }[/math].

Входные и выходные данные алгоритма

Входные данные: множества линейно независимых векторов [math]\displaystyle{ {\displaystyle \mathbf {a} _{1},\;\ldots ,\;\mathbf {a} _{N}} }[/math], каждый из которых описывается [math]\displaystyle{ \mathbf{ a_i= [a^i_1, a^i_2, ..., a^i_k]} }[/math] .

Дополнительные ограничения:

  • вектора [math]\displaystyle{ {\displaystyle \mathbf {a} _{1},\;\ldots ,\;\mathbf {a} _{N}} }[/math] линейно независимы, поэтому [math]\displaystyle{ k \geqslant N }[/math].

Объём входных данных: [math]\displaystyle{ Nk }[/math]

Выходные данные: множество ортогональных векторов [math]\displaystyle{ {\displaystyle \mathbf {b} _{1},\;\ldots ,\;\mathbf {b} _{N}} }[/math], каждый из которых описывается [math]\displaystyle{ \mathbf{ b_i= [b^i_1, b^i_2, ..., b^i_k]} }[/math] .

Объём выходных данных: [math]\displaystyle{ Nk }[/math]

Свойства алгоритма

Соотношение последовательной и параллельной сложности в случае неограниченных ресурсов, как хорошо видно, является линейным (отношение кубической к квадратичной).

При этом вычислительная мощность алгоритма, как отношение числа операций к суммарному объему входных и выходных данных, также линейна.

Алгоритм почти полностью детерминирован — единственность результата выполнения гарантирована, однако возможно накопление ошибок округления при использовании классического процесса Грама-Шмидта.

Алгоритм является численно неустойчивым — ошибки округления могут привести к неортогональности полученных векторов.

Процесс Грама — Шмидта может применяться также к бесконечной последовательности линейно независимых векторов.

Кроме того, процесс Грама — Шмидта может применяться к линейно зависимым векторам. В этом случае он выдаёт [math]\displaystyle{ \mathbf{0} }[/math] (нулевой вектор) на шаге [math]\displaystyle{ j }[/math], если [math]\displaystyle{ \mathbf{a}_j }[/math] является линейной комбинацией векторов [math]\displaystyle{ \mathbf{a}_1,\;\ldots,\;\mathbf{a}_{j-1} }[/math]. Если это может случиться, то для сохранения ортогональности выходных векторов и для предотвращения деления на ноль при ортонормировании алгоритм должен делать проверку на нулевые векторы и отбрасывать их. Количество векторов, выдаваемых алгоритмом, будет равно размерности подпространства, порождённого векторами (т.е. количеству линейно независимых векторов, которые можно выделить среди исходных векторов).

Процесс Грама ― Шмидта может быть истолкован как разложение невырожденной квадратной матрицы в произведение ортогональной (или унитарной матрицы в случае эрмитова пространства) и верхнетреугольной матрицы с положительными диагональными элементами ― QR-разложение, что есть частный случай разложения Ивасавы.

Программная реализация алгоритма

Особенности реализации последовательного алгоритма

Возможные способы и особенности параллельной реализации алгоритма

Результаты прогонов

Выводы для классов архитектур

Литература