相关笔记: 2.3 分治法 | 4.2 Strassen算法

概览

本节介绍了如何使用分治法进行方阵乘法。首先给出了标准的三重循环矩阵乘法算法 MATRIX-MULTIPLY,其时间复杂度为 。随后将分治策略应用于矩阵乘法:将 矩阵划分为四个 子矩阵,递归地执行 8 次子矩阵乘法,得到递归关系式 ,其解仍为 。本节还讨论了矩阵分区的两种实现方式(复制法与索引计算法),并分析了为什么递归矩阵乘法的”更茂密”递归树导致了 而非 的解。

  • 矩阵乘法的标准定义要求计算 个元素,每个元素是 对输入元素的乘积之和,朴素实现需要 时间
  • MATRIX-MULTIPLY 使用三重嵌套循环直接实现矩阵乘法,运行时间为
  • 分治方法将矩阵分为四个子矩阵,产生递归关系式
  • 该递归关系式的解仍为 ,与朴素方法渐近相同
  • 递归树中每个内部节点有 8 个子节点(而非归并排序的 2 个),导致树”更茂密”,叶子数量巨大
  • 矩阵分区可通过索引计算 时间内完成,无需实际复制矩阵元素

知识结构总览

graph TB
    A["4.1 矩阵乘法"] --> B["矩阵乘法的标准定义"]
    A --> C["MATRIX-MULTIPLY 算法"]
    A --> D["分治矩阵乘法"]
    A --> E["递归关系式分析"]
    A --> F["矩阵分区策略"]

    B --> B1["矩阵乘积 C = A·B 的定义"]
    B --> B2["c_ij = Σ a_ik · b_kj"]
    B --> B3["稠密矩阵 vs 稀疏矩阵"]

    C --> C1["三重嵌套循环伪代码"]
    C --> C2["时间复杂度 Θ(n³)"]
    C --> C3["C = C + A·B 的累加形式"]

    D --> D1["子矩阵划分"]
    D --> D2["8 次子矩阵乘法"]
    D --> D3["4 次子矩阵加法"]
    D --> D4["MATRIX-MULTIPLY-RECURSIVE 伪代码"]

    E --> E1["T(n) = 8T(n/2) + Θ(1)"]
    E --> E2["递归树分析:8 叉树"]
    E --> E3["解为 Θ(n³)"]
    E --> E4["与归并排序递归树的对比"]

    F --> F1["复制法:Θ(n²) 时间"]
    F --> F2["索引计算法:Θ(1) 时间"]
    F --> F3["对总运行时间的影响"]

核心思想

核心思想

本节的核心思想是将分治策略应用于矩阵乘法问题。虽然朴素的三重循环矩阵乘法已经给出了 的直接实现,但通过分治的视角重新审视矩阵乘法,可以揭示更深层的结构:将 矩阵递归地分解为 子矩阵后,需要执行 8 次子矩阵乘法。这一观察直接引出了一个关键问题——能否减少递归乘法的次数?这正是 4.2 Strassen算法 的出发点。本节的分治矩阵乘法虽然渐近复杂度不变,但它为理解 Strassen 算法的突破奠定了基础。

1. 矩阵乘法的标准定义

矩阵乘积(Matrix Product)

为两个 的方阵,则矩阵乘积 也是一个 矩阵,其中每个元素定义为:

的第 行第 列元素等于 的第 行与 的第 列的内积。计算 的全部 个元素,每个需要 次乘法和 次加法,因此朴素方法需要 次标量运算。

我们通常假设矩阵是稠密的(dense),即大部分 个元素不为 0;与之相对的是稀疏矩阵(sparse),其非零元素可以比 数组更紧凑地存储。

2. MATRIX-MULTIPLY 标准算法

MATRIX-MULTIPLY 算法

MATRIX-MULTIPLY 是矩阵乘法的直接实现,使用三重嵌套循环。该过程计算 (将 累加到 中),若只需计算 ,则在调用前将 初始化为零矩阵,额外花费 时间。

MATRIX-MULTIPLY(A, B, C, n)
1  for i = 1 to n                   // 计算每一行
2      for j = 1 to n               // 计算行 i 中的每个元素
3          for k = 1 to n
4              c_ij = c_ij + a_ik · b_kj  // 累加内积的每一项

时间复杂度分析: 三重嵌套循环各执行 次迭代,第 4 行每次执行为常数时间,因此总时间为 。即使加上初始化 时间,运行时间仍为

3. 分治矩阵乘法

分治矩阵乘法

分治矩阵乘法 矩阵乘法问题递归地分解为更小的子问题。假设 是 2 的幂,将每个矩阵划分为四个 子矩阵:

矩阵乘积 可以用子矩阵表示为:

展开后得到四个等式,每个等式涉及 2 次子矩阵乘法和 1 次子矩阵加法,共 8 次乘法4 次加法

MATRIX-MULTIPLY-RECURSIVE 算法

MATRIX-MULTIPLY-RECURSIVE 是分治矩阵乘法的递归实现:

MATRIX-MULTIPLY-RECURSIVE(A, B, C, n)
 1  if n == 1
 2      // 基准情况
 3      c_11 = c_11 + a_11 · b_11
 4      return
 5  // 分解
 6  partition A, B, C into n/2 × n/2 submatrices
 7  // 解决(8 次递归调用)
 8  MATRIX-MULTIPLY-RECURSIVE(A_11, B_11, C_11, n/2)
 9  MATRIX-MULTIPLY-RECURSIVE(A_11, B_12, C_12, n/2)
10  MATRIX-MULTIPLY-RECURSIVE(A_21, B_11, C_21, n/2)
11  MATRIX-MULTIPLY-RECURSIVE(A_21, B_12, C_22, n/2)
12  MATRIX-MULTIPLY-RECURSIVE(A_12, B_21, C_11, n/2)
13  MATRIX-MULTIPLY-RECURSIVE(A_12, B_22, C_12, n/2)
14  MATRIX-MULTIPLY-RECURSIVE(A_22, B_21, C_21, n/2)
15  MATRIX-MULTIPLY-RECURSIVE(A_22, B_22, C_22, n/2)

注意:第 8-11 行计算四个等式的第一项,第 12-15 行计算并累加第二项。由于使用索引计算,结果直接更新到 中(原地更新),因此没有合并步骤

4. 递归关系式分析

分治矩阵乘法的递归关系式

为使用 MATRIX-MULTIPLY-RECURSIVE 乘以两个 矩阵的最坏情况运行时间:

  • 基准情况: 时,执行一次标量乘法和一次加法,
  • 递归情况: 分区耗时 (索引计算),8 次递归调用各贡献 ,无合并步骤

因此递归关系式为:

由主定理(第 4.5 节)可知,其解为 ,与朴素 MATRIX-MULTIPLY 的渐近运行时间相同。

递归树对比:为什么 而非

归并排序的递归关系式 的解为 ,而分治矩阵乘法的 的解为 。关键区别在于递归树的”茂密程度”:

特征归并排序分治矩阵乘法
每个内部节点的子节点数28
递归树形态”苗条”的二叉树”茂密”的八叉树
叶子节点数
每层代价(第 层)
总层数
总代价

虽然分治矩阵乘法的内部节点代价更小( vs ),但 8 叉分支使得叶子数量呈 增长,远超归并排序的 个叶子。叶子层的巨大代价主导了总运行时间。

5. 矩阵分区策略

矩阵分区的两种实现方式

将矩阵划分为子矩阵有两种常见策略:

策略一:复制法

  • 分配临时存储空间,将 的元素复制到各自的子矩阵中
  • 递归求解后,将结果从子矩阵复制回 的对应位置
  • 复制 个元素,耗时

策略二:索引计算法

  • 通过算术运算指定子矩阵在原矩阵中的位置,不实际复制任何元素
  • 分区仅涉及对位置信息的算术运算,与矩阵大小无关,耗时
  • 对子矩阵元素的修改直接更新原矩阵(共享存储)

对于矩阵乘法,两种策略不影响渐近运行时间(因为乘法代价 主导 的复制代价)。但对于矩阵加法等代价较低的运算,复制法会增加渐近复杂度。


补充理解与拓展

矩阵乘法的计算复杂度历史

矩阵乘法的计算复杂度是理论计算机科学中最经典的问题之一。1969 年之前,数学界普遍认为 是矩阵乘法的最优渐近复杂度,因为矩阵乘法的标准定义本身就涉及 次标量乘法。Volker Strassen 在 1969 年发表的突破性论文打破了这一认知,证明只需 7 次而非 8 次递归子矩阵乘法即可完成乘积计算,将复杂度降至 。此后,矩阵乘法的指数不断被降低:Coppersmith-Winograd 算法(1990)达到 ,Virginia Vassilevska Williams(2012)改进至 ,最新的 Duan-Wu-Zhou 算法(2023)进一步降至 。目前尚不确定矩阵乘法的理论下界是多少——是否存在 的算法仍是开放问题。

来源:T. H. Cormen et al., Introduction to Algorithms, 4th ed., MIT Press, 2022, Section 4.1; V. Strassen, “Gaussian elimination is not optimal”, Numerische Mathematik, 1969.

稠密矩阵与稀疏矩阵的乘法策略

本节讨论的算法假设矩阵是稠密的(dense),即大部分元素非零。对于稀疏矩阵(sparse matrix),其中大部分元素为 0,使用 的二维数组存储会浪费大量空间。稀疏矩阵通常采用更紧凑的表示方法,如 COO(坐标格式)、CSR(压缩稀疏行格式)或 CSC(压缩稀疏列格式)。稀疏矩阵乘法可以利用零元素跳过不必要的乘法运算,在某些情况下将复杂度降至接近 ,其中 是非零元素的数量。在实际应用中(如推荐系统、图论中的邻接矩阵乘法),稀疏矩阵乘法算法的选择对性能有决定性影响。

来源:T. H. Cormen et al., Introduction to Algorithms, 4th ed., MIT Press, 2022, Section 4.1; R. A. Horn, C. R. Johnson, Matrix Analysis, 2nd ed., Cambridge University Press, 2013.


易混淆点与辨析

"分治矩阵乘法更快"的误解

初学者常认为将分治法应用于矩阵乘法后,运行时间会像归并排序一样从 降至

  • ❌ “分治法总是能降低算法的时间复杂度,矩阵乘法用分治后应该比朴素方法更快”
  • ✅ “分治法能否降低复杂度取决于递归关系式的结构。矩阵乘法的分治产生 ,8 叉递归树的叶子数量为 ,远超归并排序的 个叶子,因此解仍为 。分治矩阵乘法的价值不在于降低复杂度本身,而在于揭示了一种可能减少乘法次数的结构——这正是 4.2 Strassen算法 的突破口”

直觉理解:分治法将一个大问题分解为多个小问题,但如果子问题的数量太多(8 个),即使每个子问题减半,总工作量仍然很大。关键在于子问题数量 与规模缩减因子 的关系——当 时(此处 ),复杂度为

"复制法不影响性能"的误解

初学者可能认为矩阵分区的实现方式(复制法 vs 索引计算法)对任何分治矩阵算法都没有影响。

  • ❌ “矩阵分区用复制法还是索引计算法都一样,反正都是
  • ✅ “对于矩阵乘法,复制法的 的乘法代价主导,确实不影响渐近复杂度。但对于矩阵加法等低代价运算,复制法会使递归关系式从 变为 ,反而增加了复杂度。因此索引计算法是更优的通用策略”

关键原则:当分解/合并步骤的代价与递归乘法的代价处于同一量级或更大时,分区策略的选择会显著影响渐近复杂度。


习题精选

题号核心考点难度
4.1-1放宽 为 2 的幂的限制⭐⭐
4.1-2非方阵的矩阵乘法复杂度⭐⭐⭐
4.1-3复制法对递归关系式的影响⭐⭐
4.1-4分治矩阵加法的递归分析⭐⭐
思考题为什么 8 次乘法不能减少?⭐⭐⭐

视频学习指南

资源链接对应内容备注
MIT 6.006 Lecture 9: Matrix Multiplicationhttps://www.youtube.com/watch?v=O4V6IlxiWp8矩阵乘法、分治方法、Strassen 算法Erik Demaine 教授
Abdul Bari - Strassen’s Matrix Multiplicationhttps://www.youtube.com/watch?v=e8j7dzY2S6IStrassen 算法动画演示直观的逐步讲解
河南大学《算法导论》中文字幕版https://www.bilibili.com/video/BV1H4411B7FY4.1 矩阵乘法、分治策略中文授课,适合入门
3Blue1Brown - Essence of Linear Algebrahttps://www.youtube.com/playlist?list=PLZHQObOWTQDPD3MizzM2xVFitgF8hE_ab线性代数基础(矩阵乘法的几何直觉)可视化极佳

教材原文

教材原文摘录

“We can use the divide-and-conquer method to multiply square matrices. If you’ve seen matrices before, then you probably know how to multiply them.”

“Computing the matrix C requires computing matrix entries, each of which is the sum of n pairwise products of input elements from A and B.”

“Because each of the triply nested for loops runs for exactly n iterations, and each execution of line 4 takes constant time, the MATRIX-MULTIPLY procedure operates in time.”

“Why is the solution to this recurrence so much larger than the solution to the merge-sort recurrence? The factor of 2 in the merge-sort recurrence determines how many children each tree node has, which in turn determines how many terms contribute to the sum at each level of the tree. In comparison, for the recurrence for MATRIX-MULTIPLY-RECURSIVE, each internal node in the recursion tree has eight children, not two, leading to a ‘bushier’ recursion tree with many more leaves.”


参见 Wiki

矩阵乘法