Skip to content

Commit 272a196

Browse files
committed
Наивный алгоритм перемножения матриц и алгоритм Штрассена.
1 parent cef6530 commit 272a196

File tree

1 file changed

+65
-24
lines changed

1 file changed

+65
-24
lines changed

tex/LinearAlgebra.tex

Lines changed: 65 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -607,47 +607,88 @@ \section{Матрицы и вектора}
607607

608608
\section{Теоретическая сложность умножения матриц}
609609

610-
В рамках такого раздела теории сложности, как мелкозернистая сложность (fine-grained complexity) задача умножения двух матриц оказалась достаточно важной, так как через вычислительную сложность этой задачи можно оценить сложность большого класса различных задач. С примерами таких задач можно ознакомиться в работе~\cite{Williams:2010:SEP:1917827.1918339}. Поэтому рассмотрим алгоритмы нахождения произведения двух матриц более подробно.
610+
В рамках такого раздела теории сложности, как мелкозернистая сложность (fine-grained complexity) задача умножения двух матриц оказалась достаточно важной, так как через вычислительную сложность этой задачи можно оценить сложность большого класса различных задач. С примерами таких задач можно ознакомиться в работе~\cite{Williams:2010:SEP:1917827.1918339}. Поэтому рассмотрим алгоритмы нахождения произведения двух матриц более подробно. Далее для простоты мы будем предполагать, что перемножаются две квадратные матрицы одинакового размера $n \times n$.
611611

612-
Для начал построим наивный алгоритм, сконструированный на основе определении произведения матриц~\ref{algo:MxM}.
612+
Для начал построим наивный алгоритм, сконструированный на основе определении произведения матриц. Такой алгоритм представлен на листинге~\ref{algo:MxM}. Его работу можно описать следующим образом: для каждой строки в первой матрице и для каждого столбца в второй матрице найти сумму произведений соответствующих элеиентов. Данная сумма будет значением соответствующей ячейки результирующей матрицы.
613613

614614
\begin{algorithm}
615615
\floatname{algorithm}{Listing}
616616
\begin{algorithmic}[1]\label{algo:MxM}
617617
\caption{Наивное перемножение матриц}
618618
\Function{MatrixMult}{$M_1, M_2, G=(S,\oplus,\otimes)$}
619-
\If {(len$(\omega)=0$)}
620-
\Comment{{\footnotesize Пустая цепочка выводима из $S$}}
621-
\State{\Return \textit{(true, $\omega$)}}
622-
\EndIf
623-
624-
\If{$(\omega = a :: tl)$}
625-
\Comment{{\footnotesize Выводимая из $S$ подстрока должна начинаться с $a$}}
626-
\State{$res,tl' = $ S($tl$)}
627-
\Comment{{\footnotesize Затем должна идти подстрока, выводимая из $S$}}
628-
\If{res \&\& $tl' = b :: tl''$}
629-
\Comment{{\footnotesize Если вызов закончился успешно, то надо проверить, что следующий символ --- это $b$}}
630-
\State{\Return $S(tl'')$}
631-
\Comment{{\footnotesize И снова попробовать вывести перфикс из $S$}}
632-
\Else
633-
\State{\Return \textit{(false, $tl'$)}}
634-
\EndIf
635-
\Else
636-
\State{\Return \textit{(false, $\omega$)}}
637-
\EndIf
619+
\State{$M_3 =$ empty matrix of size $n \times n$}
620+
\For {$i \in 0..n-1$}
621+
\For {$j \in 0..n-1$}
622+
\For {$k \in 0..n-1$}
623+
\State{$M_3[i,j] = M_3[i,j] \oplus ( M_1[i,k] \otimes M_2[k,j])$}
624+
\EndFor
625+
\EndFor
626+
\EndFor
627+
\State{\Return $M_3$}
628+
638629
\EndFunction
639630
\end{algorithmic}
640631
\end{algorithm}
641632

642-
Сложность наивного произведения двух матриц составляет $O(n^3)$, что очевидным образом следует из псевдокода. Но можно ли улучшить этот алгоритм? Первый положительный ответ был опубликовал Ф. Штрассен в 1969 году~\cite{Strassen1969}. Сложность предложенного им алгоритма --- $O(n^{\log_2 7}) \approx O(n^{2.81})$. Основная идея --- рекурсивное разбиение на блоки $2 \times 2$ и вычисление их произведения с помощью только 7 умножений, а не 8.
633+
Сложность наивного произведения двух матриц составляет $O(n^3)$, что очевидным образом следует из псевдокода. Но можно ли улучшить этот алгоритм? Первый положительный ответ был опубликовал Ф. Штрассен в 1969 году~\cite{Strassen1969}. Сложность предложенного им алгоритма --- $O(n^{\log_2 7}) \approx O(n^{2.81})$. Основная идея --- рекурсивное разбиение исходных матриц на блоки $2 \times 2$ и вычисление их произведения с помощью только 7 умножений, а не 8.
634+
635+
Рассмотрим алгоритм Штрассена более подробно. Пусть $A$ и $B$ --- две квадратные матрицы размера $2^n \times 2^n$\footnote{Если размер умножаемых матриц не является натуральной степенью двойки, мы дополняем исходные матрицы дополнительными нулевыми строками и столбцами.} над кольцом $R=(S,\oplus,\otimes)$. Наша задача найти матрицу $C = A \cdot B$.
636+
637+
Разделим матрицы $A, B$ и $C$ на четрые равные по размеру блока.
638+
$$
639+
A =
640+
\begin{pmatrix}
641+
A_{1,1} & A_{1,2} \\
642+
A_{2,1} & A_{2,2}
643+
\end{pmatrix} \mbox{ , }
644+
B =
645+
\begin{pmatrix}
646+
B_{1,1} & B_{1,2} \\
647+
B_{2,1} & B_{2,2}
648+
\end{pmatrix} \mbox{ , }
649+
C =
650+
\begin{pmatrix}
651+
C_{1,1} & C_{1,2} \\
652+
C_{2,1} & C_{2,2}
653+
\end{pmatrix}
654+
$$
643655

644-
Рассмотрим предложенный им алгоритм более подробно !!!. Про вычитание блоков, чтобы было видно, откуда требуются обратные.
656+
На основе определения произведения матриц легко убедиться, что выполняются следующие равенства.
657+
\begin {align*}
658+
C_{1,1}&= A_{1,1} \cdot B_{1,1} + A_{1,2} \cdot B_{2,1} \\
659+
C_{1,2}&= A_{1,1} \cdot B_{1,2} + A_{1,2} \cdot B_{2,2} \\
660+
C_{2,1}&= A_{2,1} \cdot B_{1,1} + A_{2,2} \cdot B_{2,1} \\
661+
C_{2,2}&= A_{2,1} \cdot B_{1,2} + A_{2,2} \cdot B_{2,2}
662+
\end {align*}
663+
664+
Данная процедура не даёт нам ничего нового с точки зрения вычислительной сложности. Но мы можем двинуться дальше и определить следующие элементы.
665+
666+
\begin {align*}
667+
P_1 & \equiv (A_{1,1} + A_{2,2}) \cdot (B_{1,1} + B_{2,2}) \\
668+
P_2 & \equiv (A_{2,1} + A_{2,2}) \cdot B_{1,1} \\
669+
P_3 & \equiv A_{1,1} \cdot (B_{1,2} - B_{2,2}) \\
670+
P_4 & \equiv A_{2,2} \cdot (B_{2,1} - B_{1,1}) \\
671+
P_5 & \equiv (A_{1,1} + A_{1,2}) \cdot B_{2,2} \\
672+
P_6 & \equiv (A_{2,1} - A_{1,1}) \cdot (B_{1,1} + B_{1,2}) \\
673+
P_7 & \equiv (A_{1,2} - A_{2,2}) \cdot (B_{2,1} + B_{2,2})
674+
\end {align*}
675+
676+
Используя эти элементы мы можем выразить блоки результирующей матрицы следующим образом.
677+
678+
\begin {align*}
679+
C_{1,1} & = P_1 + P_4 - P_5 + P_7 \\
680+
C_{1,2} & = P_3 + P_5 \\
681+
C_{2,1} & = P_2 + P_4 \\
682+
C_{2,2} & = P_1 - P_2 + P_3 + P_6
683+
\end {align*}
684+
685+
При таком способе вычисления мы получаем на одно умножение подматриц меньше, чем при наивном подходе. Это и приводит, в конечном итоге, к улучшению сложности всего алгоритма, который основывается на рекурсивном повторении проделанной выше процедуры.
645686

646687
Впоследствии сложность постепенно понижалась в ряде работ, таких как ~\cite{Pan1978,BiniCapoRoma1979,Schonhage1981,CoppWino1982,CoppWino1990}. Было введено специальное обозначение для показателя степени в данной оценке: $\omega$. То есть сложность умножения матриц --- это $O(n^\omega)$, и задача сводится к уменьшению значения $\omega$. В настоящее время работа над уменьшением показателя степени продолжается и сейчас уже предложены решения с $\omega < 2.373$\footnote{В данной области достаточно регулярно появляются новые результаты, дающие сравнительно небольшие, в терминах абсолютных величин, изменения. Так, в 2021 была педставлена работа, улучшающая значение $\omega$ в пятом знаке после запятой~\cite{alman2020refined}. Несмотря на кажущуюся несерьёзность результата, подобные работы имеют большое теоретическое значение, так как улучшают наше понимание исходной задачи и её свойств.}.
647688

648689
Всё тем же Ф. Штрассеном ещё в 1969 году была выдвинута гипотеза о том, что для достатого больших $n$ существует алгоритм, который для любого сколь угодно маленького наперёд заданного $\varepsilon$ перемножает матрицы за $O(n^{2+\varepsilon})$. На текущий момент ни доказательства, ни опровержения этой гипотезы не предъявлено.
649690

650-
Важной особенностью указанного выше направления улучшения алгоритмов является то, что оно допускает использования более богатых алгебраических структур, чем требуется для определения умножения двух матриц. Так, уже алгоритм Штрасеена использует операцию вычитания, что приводит к необходимости иметь обратные элеиенты по сложению, а значит определять матрицы над кольцом. Хотя для исходного определения (\ref{def:MxM}) достаточно более бедной структуры. При этом, часто, структуры, возникающие в прикладных задачах кольцами не являются. Напмриер, тропическое (или $\{min,+\}$) полукольцо, играющее ключевую роль в тропической математике, или булево ($\{\vee,\wedge\}$) полукольцо, возникающее, например, при работе с отношениями\footnote{Вообще говоря, в некоторых прикладных задачах возникают структуры, не являющиеся даже полукольцом. Предположим, что есть три различных множества $S_1, S_2$ и $S_3$ и две двухместные функции $f:S_1 \times S_2 \to S_3$ и $g: S_3 \times S_3 \to S_3$. Этого достаточно, чтобы определить произведение двух матриц $M_1$ и $M_2$, построенных из элементов множеств $S_1$ и $S_2$ соответственно. Результирующая матрица будет состоять из элеиентов $S_3$. Как видно, функции не являются бинарными операциями в смысле нашего определения. Несотря на кажущуюся экзотичность, подобные структуры возникают на практике при работе с графами и учитываются, например, в стандарте GraphBLAS (\url{https://graphblas.github.io/}), где, кстати, называются полукольцами, что выглядит не вполне корректно.}. Значит, описанные выше решения не применимы и вопрос о существовании алгоритма с менее чем кубической сложностью снова актуален.
691+
Важной особенностью указанного выше направления улучшения алгоритмов является то, что оно допускает использования (и даже основывается на использовании) более богатых алгебраических структур, чем требуется для определения умножения двух матриц. Так, уже алгоритм Штрасеена использует операцию вычитания, что приводит к необходимости иметь обратные элементы по сложению, а значит определять матрицы над кольцом. Хотя для исходного определения (\ref{def:MxM}) достаточно более бедной структуры. При этом, часто, структуры, возникающие в прикладных задачах кольцами не являются. Примерами могут служить тропическое (или $\{min,+\}$) полукольцо, играющее ключевую роль в тропической математике, или булево ($\{\vee,\wedge\}$) полукольцо, возникающее, например, при работе с отношениями\footnote{Вообще говоря, в некоторых прикладных задачах возникают структуры, не являющиеся даже полукольцом. Предположим, что есть три различных множества $S_1, S_2$ и $S_3$ и две двухместные функции $f:S_1 \times S_2 \to S_3$ и $g: S_3 \times S_3 \to S_3$. Этого достаточно, чтобы определить произведение двух матриц $M_1$ и $M_2$, построенных из элементов множеств $S_1$ и $S_2$ соответственно. Результирующая матрица будет состоять из элеиентов $S_3$. Как видно, функции не являются бинарными операциями в смысле нашего определения. Несотря на кажущуюся экзотичность, подобные структуры возникают на практике при работе с графами и учитываются, например, в стандарте GraphBLAS (\url{https://graphblas.github.io/}), где, кстати, называются полукольцами, что выглядит не вполне корректно.}. Значит, описанные выше решения не применимы и вопрос о существовании алгоритма с менее чем кубической сложностью снова актуален.
651692

652693
В попытках ответить на этот вопрос появились так называемые комбинаторные алгориты умножения матриц\footnote{В противовес описанным выше, не являющимся комбинаторными. Стоит отметить, что строгое опредедение комбинаторных алгоритмов отсутствует, хотя этот термин и получил широкое употребление. В частности, Н.~Бансал (Nikhil Bansal) и Р.~Уильямс (Ryan Williams) в работе~\cite{5438580} дают определение комбинаторного алгоритма, но тут же замечают следющее: ``We would like to give a definition of ``combinatorial algorithm'', but this appears elusive. Although the term has been used in many of the cited references, nothing in the literature resembles a definition. For the purposes of this paper, let us think of a ``combinatorial algorithm'' simply as one that does not call an oracle for ring matrix multiplication.''. Ещё один вариант определения с обсуждением можно найти в~\cite{das2018lower}.}. Классический результат в данной области --- это алгоритм чеиырёх русских, предложенный В. Л. Арлазаровым, Е. А. Диницем, М. А. Кронродом и И. А. Фараджевым в 1970 году~\cite{ArlDinKro70}, позволяющий перемножить матрицы над конечным полукольцом за $O(n^3/\log n)$. Лучшим результатом\footnote{В работе~\cite{das2018lower} предложен алгоритм со сложностью $\Omega(n^{7/3}/2^{O(\sqrt{\log n})})$, однако авторы утверждают, тчо сами не уверены в комбинаторности предложенного решения. По-видимому, полученные результаты ещё должны быть проверены сообществом.} в настоящее время является алгоритм со сложностью $\hat{O}(n^3/\log^4 n)$\footnote{Нотация $\hat{O}$ скрывает $poly(\log\log)$ коэффициенты.}~\cite{10.1007/978-3-662-47672-7_89}.
653694

0 commit comments

Comments
 (0)