矩阵的定义与基本运算
困难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]
转置在矩阵运算中经常用到,比如某些乘法条件不满足时,可以用转置来调整。
新手最容易犯的错误
- 矩阵加法维度不匹配:两个矩阵行数或列数不同,就强行相加。代码中会报错或结果乱套。
- 矩阵乘法顺序搞反:以为 A×B 和 B×A 一样,其实维度可能不同。一定要检查:A的列数等于B的行数。
- 乘法时索引搞混:计算 c[i][j] 时,要对 A[i][k] 和 B[k][j] 求和,别把 i、j、k 的位置记反。
- 忘记初始化结果矩阵:在代码中,结果矩阵 C 要先全部赋值为 0,否则累加时可能包含垃圾值。
- 用单个数字乘以矩阵时,把所有元素乘以该数字(正确),但有人错误地只乘一行或一列。
代码实现
我们先实现一个矩阵类或者用二维数组表示矩阵,然后编写乘法和加法函数。为了简单,我们直接使用二维数组,并写函数操作。
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),快得惊人!
矩阵也是机器学习的基础:一张图片可以看作一个像素值矩阵;神经网络的每一层其实都是矩阵乘法。
练习
- 自己手动计算两个 3×3 矩阵的乘法。
- 提示:写一个 3×3 矩阵 A(比如全1),另一个 B(比如对角线为2),计算 A×B 并验证。
- 写一个函数实现矩阵减法(A - B)。
- 写一个函数实现矩阵的数乘(k * A)。
- 思考:如果 A 是 2×3,B 是 3×2,那么 A×B 和 B×A 结果一样吗?为什么?
总结
矩阵就是一个二维表格,有加法和乘法两种基本运算。乘法是最重要的,它需要“行乘列”后求和。理解矩阵乘法的规则,是学习矩阵快速幂的基石。下一节,我们将学习如何用矩阵乘法快速计算递推数列,敬请期待!
例题精讲
设有矩阵A是2×3矩阵,B是3×4矩阵,C是4×2矩阵,则下列运算中,结果矩阵的维度为2×2的是?
矩阵的转置运算中,下列哪个性质是正确的?
矩阵乘法满足交换律,即对于任意两个矩阵A和B,总有AB = BA。
以下是一个矩阵乘法函数的实现,请补全缺失部分。假设矩阵用二维列表表示,且维度匹配。
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以下是矩阵快速幂函数的一部分,请补全矩阵乘法调用:
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