OpenDSA 完整目录

Chapter 26 Number Problems

| 关于   «  1. 数值问题   ::   目录   ::   3. 概率算法导论  »

2. 矩阵乘法

2.1. 标准方法

我们通常怎么做矩阵乘法? 给定 \(n \times n\) 矩阵 \(A\) 和 \(B\), 标准方法是按如下方式计算 \(C = A \times B\) 中的元素 \(c_{ij}\):

\[c_{ij} = \sum_{k=1}^n a_{ik}b_{kj}.\]

这需要 \(\Theta(n^3)\) 次乘法和 \(\Theta(n^3)\) 次加法。 不过,它并不像乍看起来那么糟,因为输入规模 不是 \(n\),而是 \(n^2\)。

但这真的是我们能做得最好的吗? 考虑一下:任何矩阵乘法算法的朴素下界 都是 \(\Omega(n^2)\),因为我们要创建 \(n^2\) 个输出。

暂时先考虑两个 \(2 \times 2\) 矩阵相乘。 基本算法需要恰好 8 次乘法和 4 次加法。 但考虑下面这个替代方案。 计算:

\[\begin{split}\begin{eqnarray*} m_1 &=& (a_{12} - a_{22})(b_{21} + b_{22})\\ m_2 &=& (a_{11} + a_{22})(b_{11} + b_{22})\\ m_3 &=& (a_{11} - a_{21})(b_{11} + b_{12})\\ m_4 &=& (a_{11} + a_{12})b_{22}\\ m_5 &=& a_{11}(b_{12} - b_{22})\\ m_6 &=& a_{22}(b_{21} - b_{11})\\ m_7 &=& (a_{21} + a_{22})b_{11} \end{eqnarray*}\end{split}\]

然后:

\[\begin{split}\begin{eqnarray*} c_{11} &=& m_1 + m_2 - m_4 + m_6\\ c_{12} &=& m_4 + m_5\\ c_{21} &=& m_6 + m_7\\ c_{22} &=& m_2 - m_3 + m_5 - m_7 \end{eqnarray*}\end{split}\]

要验证这个看起来是正确的,让我们检查其中一个:

\[\begin{split}\begin{eqnarray*} c_{11} &=& m_1 + m_2 - m_4 + m_6\\ &=& (a_{12} - a_{22})(b_{21} + b_{22}) + (a_{11} + a_{22})(b_{11} + b_{22})\\ &&\qquad- (a_{11} + a_{12})b_{22} + a_{22}(b_{21} - b_{11})\\ &=& a_{12}b_{21} + a_{12}b_{22} - a_{22}b_{21} - a_{22}b_{22} + a_{11}b_{22} + a_{22}b_{11}\\ &&\qquad + a_{22}b_{22} - a_{11}b_{22} - a_{12}b_{22} + a_{22}b_{21} - a_{22}b_{11}\\ &=& a_{11}b_{11} + a_{12}b_{21} \end{eqnarray*}\end{split}\]

这需要 7 次乘法和 18 次加法/减法。 现在,如果我们实际在乘法 \(2 \times 2\) 数组,那 看起来不像是一种胜利,因为在乘 整数(甚至浮点数)时,乘法与加法的代价差别 并不大。 但请考虑,这在乘两个规模为 \(2n \times 2n\) 的矩阵时 同样适用。 在这种情况下,乘法是对半规模(\(n \times n\))矩阵进行的。 而在此情形下,矩阵乘法远比矩阵加法昂贵 (\(\Theta(n^3)\) 对 \(\Theta(n^2)\))。

2.2. 斯特拉森算法

上面展示的、乘法次数减少的这种安排在数学上 众所周知,就是斯特拉森算法。 斯特拉森算法在 \(2n \times 2n\) 的情形下,用更多的加法/减法 换取更少的乘法。 它使用分治,试图减少乘大矩阵的总工作量。

正如我们已经看到的,在直接实现中,$2 times 2$ 的情况将完成这项工作:

\[\begin{split}\begin{eqnarray*} c_{11} = a_{11}b_{11} + a_{12}b_{21}\\ c_{12} = a_{11}b_{12} + a_{12}b_{22}\\ c_{21} = a_{21}b_{11} + a_{22}b_{21}\\ c_{22} = a_{21}b_{12} + a_{22}b_{22} \end{eqnarray*}\end{split}\]

把两个 \(2n \times 2n\) 矩阵相乘需要 8 次(矩阵)乘法 和 4 次(矩阵)加法。

考虑这个分治步骤: 假设 \(n\) 是 2 的幂。 用 \(\frac{n}{2} \times \frac{n}{2}\) 的矩阵来表示 \(C = A \times B\)。

\[\begin{split}\left[ \begin{array}{ll} C_{11} & C_{12}\\ C_{21} & C_{22} \end{array} \right] ========== \left[ \begin{array}{ll} A_{11} & A_{12}\\ A_{21} & A_{22} \end{array} \right] \left[ \begin{array}{ll} B_{11} & B_{12}\\ B_{21} & B_{22} \end{array} \right]\end{split}\]

按照斯特拉森算法,这可以用 7 次乘法 和 18 次加法/减法计算 \(n/2 \times n/2\) 矩阵来实现。

这给出下面的递推关系,以及 应用主定理得到的这个解:

\[\begin{split}\begin{eqnarray*} T(n) &=& 7T(n/2) + 18(n/2)^2\\ T(n) &=& \Theta(n^{\log_2 7}) = \Theta(n^{2.81}). \end{eqnarray*}\end{split}\]

这显然比 \(\Theta(n^3)\) 增长得慢。 然而,由于加法次数的增加,存在大得多的常数。 斯特拉森算法只有在矩阵非常巨大时 才切实可行。

还有一些其他安排可以进一步压低指数。 当前"最快"的算法(在渐近意义上)是 \(\Theta(n^{2.376})\)。 但由于大量加法带来的额外开销, 它甚至比斯特拉森算法更不实用。

矩阵乘法能否以 \(O(n^2)\) 时间完成, 在理论上仍是一个悬而未决的问题。

   «  1. 数值问题   ::   目录   ::   3. 概率算法导论  »

关闭窗口