CC++ & Algorithm

矩阵的定义与基本运算

困难2
语言版本:通用
概述:认识矩阵这个“数字表格”,学会加法和乘法,为矩阵快速幂打基础。

矩阵——整齐排列的数字军团

同学们,你们玩过俄罗斯方块吗?里面的方块是 2×2、3×3 的方形阵列。在数学里,也有一种类似的“方块”,叫做矩阵(Matrix)。它就是一个由数字按行和列排列成的矩形方阵。比如下面这个就是一个 2 行 3 列的矩阵(也叫 2×3 矩阵):

[ 1  2  3 ]
[ 4  5  6 ]

矩阵里的每个数字叫做“元素”。我们通常用大写字母表示矩阵,比如 A、B、M 等。矩阵在计算机图形学、物理模拟、机器学习、图论中都有重要应用。今天我们就来学习矩阵的基本运算。

矩阵为什么这么有用?

想象一下:你有一张班级成绩表,行是同学,列是语文、数学、英语分数。这就是一个矩阵!如果每个同学每门课再加5分,那就是给矩阵每个元素加5——对应矩阵加法。如果所有成绩都乘以1.2(相当于提高难度后的加权),那就是数乘。如果你有两张表:一张是各科成绩,另一张是每门课的学分,把成绩和学分对应乘起来再求和,就能得到每个同学的总学分绩——这就是矩阵乘法的缩影。

生活中的矩阵无处不在:超市价目表、照片的像素点(每个像素是一个RGB值)、地图上城市间的距离……学会矩阵,就等于学会用一种高效的方式处理成批的数字。


矩阵的定义

一个 m 行 n 列 的矩阵可以表示为:

A = [a_{ij}],其中 i=1..m, j=1..n,a_{ij} 表示第 i 行第 j 列的元素。

例如 2×2 矩阵:

A = [ a  b ]
    [ c  d ]

行和列:我们常说“行(row)在前,列(column)在后”。比如一个 3×2 矩阵有3行2列,就像排队时3排每排2个人。

矩阵的大小(也叫“型”)用“行数×列数”表示。例如:

  • 2×3 矩阵:2行3列
  • 1×4 矩阵:只有一行,也叫行向量
  • 4×1 矩阵:只有一列,也叫列向量

矩阵的基本运算

1. 矩阵加法(必须同型)

两个行数和列数都相同的矩阵才能相加。对应位置元素相加:

C = A + B,其中 c_{ij} = a_{ij} + b_{ij}

例子:

A = [1 2]    B = [5 6]    A+B = [6 8]
    [3 4]        [7 8]          [10 12]

生活中的加法:假如你和小明都有两份零食清单(巧克力、薯片数量),合并后就是两个矩阵对应格子相加。

2. 数乘矩阵

一个数 k 乘以矩阵,就是用 k 乘每个元素:

C = k * A,其中 c_{ij} = k * a_{ij}

例子:

k=3, A=[1 2] => 3A = [3 6]
       [3 4]        [9 12]

数乘就像复制:如果你有3份同样的价目表,总价就是价目表乘以3。

3. 矩阵乘法(重点)

矩阵乘法比加法复杂一些,要求左边矩阵的列数等于右边矩阵的行数。设 A 是 m×n 矩阵,B 是 n×p 矩阵,乘积 C 是 m×p 矩阵。C 的第 i 行第 j 列元素等于 A 的第 i 行和 B 的第 j 列对应元素乘积之和:

c_{ij} = sum_{k=1}^{n} a_{ik} * b_{kj}

通俗地说:取 A 的第 i 行,取 B 的第 j 列,把对应位置乘起来,然后相加。

例子:

A = [1 2]    B = [5 6]    A*B 是 2×2 矩阵
    [3 4]        [7 8]
c11 = 1*5 + 2*7 = 19
c12 = 1*6 + 2*8 = 22
c21 = 3*5 + 4*7 = 43
c22 = 3*6 + 4*8 = 50
所以 A*B = [19 22]
          [43 50]

为什么叫“行乘列”?想象一个班级有 m 个学生(A的m行),每人有 n 门考试成绩(A的n列);B是 n 门课的学分矩阵(n行 p 列,这里p=1表示总学分绩)。那么 A×B 的结果就是每个学生的总学分绩——正好是“行”与“列”的点积。

注意:矩阵乘法不满足交换律,即 AB 不一定等于 BA。甚至可能一方能乘另一方不能乘。比如 A 是 2×3,B 是 3×2,那么 A×B 是 2×2,而 B×A 是 3×3,两者完全不同。

4. 矩阵转置(补充知识)

转置就是把矩阵的行和列互换。记作 Aᵀ。例如:

A = [1 2]    Aᵀ = [1 3 5]
    [3 4]         [2 4 6]
    [5 6]

转置在矩阵运算中经常用到,比如某些乘法条件不满足时,可以用转置来调整。


新手最容易犯的错误

  1. 矩阵加法维度不匹配:两个矩阵行数或列数不同,就强行相加。代码中会报错或结果乱套。
  2. 矩阵乘法顺序搞反:以为 A×B 和 B×A 一样,其实维度可能不同。一定要检查:A的列数等于B的行数。
  3. 乘法时索引搞混:计算 c[i][j] 时,要对 A[i][k] 和 B[k][j] 求和,别把 i、j、k 的位置记反。
  4. 忘记初始化结果矩阵:在代码中,结果矩阵 C 要先全部赋值为 0,否则累加时可能包含垃圾值。
  5. 用单个数字乘以矩阵时,把所有元素乘以该数字(正确),但有人错误地只乘一行或一列

代码实现

我们先实现一个矩阵类或者用二维数组表示矩阵,然后编写乘法和加法函数。为了简单,我们直接使用二维数组,并写函数操作。

C++ 代码

#include <iostream>
#include <vector>
using namespace std;

// 矩阵乘法:A (m x n) * B (n x p) 返回 C (m x p)
vector<vector<long long>> matrixMultiply(
    const vector<vector<long long>>& A,
    const vector<vector<long long>>& B) {
    int m = A.size();           // A的行数
    int n = A[0].size();        // A的列数,也是B的行数
    int p = B[0].size();        // B的列数
    // 初始化结果矩阵为全0
    vector<vector<long long>> C(m, vector<long long>(p, 0));
    for (int i = 0; i < m; i++) {
        for (int j = 0; j < p; j++) {
            // 计算C[i][j] = sum_k A[i][k]*B[k][j]
            for (int k = 0; k < n; k++) {
                C[i][j] += A[i][k] * B[k][j];
            }
        }
    }
    return C;
}

// 矩阵加法:A和B同型,返回 C = A + B
vector<vector<long long>> matrixAdd(
    const vector<vector<long long>>& A,
    const vector<vector<long long>>& B) {
    int rows = A.size();        // 行数
    int cols = A[0].size();     // 列数
    vector<vector<long long>> C(rows, vector<long long>(cols, 0));
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            C[i][j] = A[i][j] + B[i][j];
        }
    }
    return C;
}

// 打印矩阵
void printMatrix(const vector<vector<long long>>& M) {
    for (auto& row : M) {
        for (auto val : row) {
            cout << val << " ";
        }
        cout << endl;
    }
}

int main() {
    vector<vector<long long>> A = {{1, 2}, {3, 4}};
    vector<vector<long long>> B = {{5, 6}, {7, 8}};
    auto C = matrixMultiply(A, B);
    printMatrix(C);  // 输出 19 22 / 43 50

    // 加法测试
    auto D = matrixAdd(A, B);
    printMatrix(D);  // 输出 6 8 / 10 12
    return 0;
}

Python 代码

def matrix_multiply(A: list, B: list) -> list:
    """
    矩阵乘法,A是m×n,B是n×p,返回m×p矩阵
    """
    m = len(A)
    n = len(A[0]) if A else 0
    p = len(B[0]) if B else 0
    # 初始化结果矩阵为全0
    C = [[0 for _ in range(p)] for _ in range(m)]
    for i in range(m):
        for j in range(p):
            total = 0
            for k in range(n):
                total += A[i][k] * B[k][j]
            C[i][j] = total
    return C

def matrix_add(A: list, B: list) -> list:
    """
    矩阵加法,要求A和B同型
    """
    rows = len(A)
    cols = len(A[0])
    C = [[0 for _ in range(cols)] for _ in range(rows)]
    for i in range(rows):
        for j in range(cols):
            C[i][j] = A[i][j] + B[i][j]
    return C

# 测试
if __name__ == "__main__":
    A = [[1, 2], [3, 4]]
    B = [[5, 6], [7, 8]]
    C = matrix_multiply(A, B)
    for row in C:
        print(row)  # 输出 [19,22] [43,50]

    D = matrix_add(A, B)
    for row in D:
        print(row)  # 输出 [6,8] [10,12]

为啥要学矩阵?

因为矩阵可以方便地表示线性变换(比如平移、旋转、缩放),可以编码递推关系(比如斐波那契数列),还能表示图的邻接关系。我们的下一个知识点——矩阵快速幂——就要用到矩阵乘法来加速递推。比如计算斐波那契数列的第 1000 项,如果用普通循环是 O(n),而用矩阵快速幂可以降到 O(log n),快得惊人!

矩阵也是机器学习的基础:一张图片可以看作一个像素值矩阵;神经网络的每一层其实都是矩阵乘法。


练习

  1. 自己手动计算两个 3×3 矩阵的乘法。
    • 提示:写一个 3×3 矩阵 A(比如全1),另一个 B(比如对角线为2),计算 A×B 并验证。
  2. 写一个函数实现矩阵减法(A - B)。
  3. 写一个函数实现矩阵的数乘(k * A)。
  4. 思考:如果 A 是 2×3,B 是 3×2,那么 A×B 和 B×A 结果一样吗?为什么?

总结

矩阵就是一个二维表格,有加法和乘法两种基本运算。乘法是最重要的,它需要“行乘列”后求和。理解矩阵乘法的规则,是学习矩阵快速幂的基石。下一节,我们将学习如何用矩阵乘法快速计算递推数列,敬请期待!

例题精讲

1单选题

设有矩阵A是2×3矩阵,B是3×4矩阵,C是4×2矩阵,则下列运算中,结果矩阵的维度为2×2的是?

AA(B+C)
B(AB)C
CA(BC)
D(AB)C^T
2单选题

矩阵的转置运算中,下列哪个性质是正确的?

A(A+B)^T = A^T + B^T
B(AB)^T = A^T B^T
C(kA)^T = kA (k为标量)
D(A^T)^T = -A
3判断题

矩阵乘法满足交换律,即对于任意两个矩阵A和B,总有AB = BA。

4填空题
以下是一个矩阵乘法函数的实现,请补全缺失部分。假设矩阵用二维列表表示,且维度匹配。

def matrix_multiply(A, B):
    m = len(A)
    n = len(B[0])
    k = len(B)
    result = [[0 for _ in range(n)] for _ in range(m)]
    for i in range(m):
        for j in range(n):
            for t in range(k):
                result[i][j] += ___ 
    return result
5填空题
以下是矩阵快速幂函数的一部分,请补全矩阵乘法调用:

def mat_pow(mat, n):
    size = len(mat)
    res = [[1 if i==j else 0 for j in range(size)] for i in range(size)]
    base = mat
    while n > 0:
        if n & 1:
            res = multiply(____)  # 填空,填写两个参数,用逗号分隔
        base = multiply(base, base)
        n >>= 1
    return res