矩阵乘法的定义
矩阵乘法的定义要求相乘的两个矩阵A和B,A的列必须等于B的行,结果为一个矩阵C。C的每个元素是A的行和B的列对应元素的乘积的和,也就是他们的标准内积(标准内积还没学到,可以点这个链接看看)。用数学语言描述就是:
A
m
×
n
=
(
a
11
⋯
a
1
n
⋮
⋱
⋮
a
m
1
⋯
a
m
n
)
B
n
×
p
=
(
b
11
⋯
b
1
p
⋮
⋱
⋮
b
n
1
⋯
b
n
p
)
C
m
×
p
=
(
∑
k
=
0
n
a
1
k
b
k
1
⋯
∑
k
=
0
n
a
1
k
b
k
p
⋮
⋱
⋮
∑
k
=
0
n
a
m
k
b
k
1
⋯
∑
k
=
0
n
a
m
k
b
k
p
)
A^{m\times n}=\begin{pmatrix}a_{11} & \cdots & a_{1n}\\ \vdots & \ddots & \vdots\\ a_{m1} & \cdots & a_{mn} \end{pmatrix}\\ B^{n\times p}=\begin{pmatrix} b_{11} & \cdots & b_{1p}\\ \vdots & \ddots & \vdots\\ b_{n1} & \cdots & b_{np} \end{pmatrix}\\ C^{m\times p}=\begin{pmatrix} \sum_{k=0}^{n} a_{1k}b_{k1} & \cdots & \sum_{k=0}^{n} a_{1k}b_{kp} \\ \vdots & \ddots & \vdots\\ \sum_{k=0}^{n} a_{mk}b_{k1} & \cdots & \sum_{k=0}^{n} a_{mk}b_{kp} \end{pmatrix}
Am×n=
a11⋮am1⋯⋱⋯a1n⋮amn
Bn×p=
b11⋮bn1⋯⋱⋯b1p⋮bnp
Cm×p=
∑k=0na1kbk1⋮∑k=0namkbk1⋯⋱⋯∑k=0na1kbkp⋮∑k=0namkbkp
结果C的任意一个元素是这样计算出来的:
c
i
j
=
∑
k
=
0
n
a
i
k
b
k
j
c_{ij}=\sum_{k=0}^{n} a_{ik}b_{kj}
cij=k=0∑naikbkj
矩阵乘法有很多意义,但是我只写三种:一、线性方程组;二、线性变换;三、线性组合。因为这三种对于初学者来说,比较容易理解。
线性方程组
线性方程组可以看作矩阵与向量的乘积,比如下列线性方程组:
5
x
+
3
y
+
2
x
=
10
−
x
+
4
y
+
6
z
=
−
10
−
2
x
−
3
y
−
4
z
=
9
5x+3y+2x=10\\ -x+4y+6z=-10\\ -2x-3y-4z=9
5x+3y+2x=10−x+4y+6z=−10−2x−3y−4z=9
可以表示为矩阵和一个未知向量相乘:
(
5
3
2
−
1
4
6
−
2
−
3
−
4
)
x
=
(
10
−
10
9
)
x
=
(
19
8
7
8
−
9
4
)
\begin{pmatrix} 5 & 3 & 2\\ -1 & 4 & 6 \\ -2 & -3 & -4 \end{pmatrix}x=\begin{pmatrix} 10\\ -10\\ 9 \end{pmatrix}\\ x=\begin{pmatrix} \frac{19}8\\ \frac{7}8\\ -\frac{9}4 \end{pmatrix}
5−1−234−326−4
x=
10−109
x=
81987−49
线性变换
至于什么样的变换才能叫线性变换,这个是纯代数的问题,比较难,我这里就不过多讲了,但是行变换是一个线性变换,我举过例子,那么多个行变换就可以通过乘法组合起来,这点在后续的矩阵的LU分解中特别重要!尤其是多个线性变换先后作用,可以连乘起来,组成一个线性变换,比如下列行变换:
x
1
=
(
1
0
0
0
2
1
0
0
0
0
1
0
0
0
0
1
)
x
2
=
(
1
0
0
0
0
1
0
0
2
0
1
0
0
0
0
1
)
x_1= \begin{pmatrix} 1 & 0 & 0 & 0\\ 2 & 1 & 0 & 0\\ 0 & 0 & 1 & 0\\ 0 & 0 & 0 & 1 \end{pmatrix}\\ x_2= \begin{pmatrix} 1 & 0 & 0 & 0\\ 0 & 1 & 0 & 0\\ 2 & 0 & 1 & 0\\ 0 & 0 & 0 & 1 \end{pmatrix}
x1=
1200010000100001
x2=
1020010000100001
x
1
x_1
x1的作用是把第一行乘以两倍加到第二行,
x
2
x_2
x2的作用是把第一行乘以两倍加到第三行,那么先后进行这两种作用就是
x
1
x
2
x_1x_2
x1x2,把这两个矩阵乘起来使用就行了。
x
1
x
2
=
(
1
0
0
0
2
1
0
0
2
0
1
0
0
0
0
1
)
x_1x_2=\begin{pmatrix} 1 & 0 & 0 & 0\\ 2 & 1 & 0 & 0\\ 2 & 0 & 1 & 0\\ 0 & 0 & 0 & 1 \end{pmatrix}
x1x2=
1220010000100001
这个乘完的矩阵会把矩阵第一行乘以两倍,加到第二行和第三行。利用矩阵乘法,可以把多个线性变换组合起来,节省了大量计算量。
线性组合
线性组合在几何空间里用得特别多。比如一个向量的坐标是
(
1
,
1
,
1
)
(1,1,1)
(1,1,1),那么就可以看成是自然基和
(
1
,
1
,
1
)
T
(1,1,1)^T
(1,1,1)T这个向量的乘法,于是有:
(
1
0
0
0
1
0
0
0
1
)
(
1
1
1
)
=
(
1
1
1
)
\begin{pmatrix} 1 & 0 & 0\\ 0 & 1 & 0\\ 0 & 0 & 1 \end{pmatrix}\begin{pmatrix} 1\\ 1 \\ 1 \end{pmatrix}=\begin{pmatrix} 1 \\ 1 \\ 1 \end{pmatrix}
100010001
111
=
111
标准坐标系,计算没什么意义,假如坐标系换了呢?比如说坐标系换成了这样:
x
1
=
(
1
0
0
)
,
x
2
=
(
1
2
0
)
,
x
3
=
(
1
2
3
)
x_1=\begin{pmatrix} 1 \\ 0 \\ 0 \end{pmatrix}, x_2=\begin{pmatrix} 1 \\ 2 \\ 0 \end{pmatrix}, x_3=\begin{pmatrix} 1 \\ 2 \\ 3 \end{pmatrix}
x1=
100
,x2=
120
,x3=
123
那么其实就是要求三个比例
α
1
,
α
2
,
α
3
\alpha_1,\alpha_2,\alpha_3
α1,α2,α3组成一个线性组合,使得下式成立:
α
1
(
1
0
0
)
+
α
2
(
1
2
0
)
+
α
3
(
1
2
3
)
=
(
1
1
1
)
\alpha_1\begin{pmatrix} 1 \\ 0 \\ 0 \end{pmatrix}+\alpha_2 \begin{pmatrix} 1 \\ 2\\ 0 \end{pmatrix}+ \alpha_3\begin{pmatrix} 1 \\ 2 \\ 3 \end{pmatrix}=\begin{pmatrix} 1 \\ 1 \\ 1 \end{pmatrix}
α1
100
+α2
120
+α3
123
=
111
其实也可以转为矩阵乘法,就是:
(
1
1
1
0
2
2
0
0
3
)
(
α
1
α
2
α
3
)
=
(
1
1
1
)
α
1
=
1
2
,
α
2
=
1
6
,
α
3
=
1
3
\begin{pmatrix} 1 & 1 & 1\\ 0 & 2 & 2\\ 0 & 0 & 3 \end{pmatrix}\begin{pmatrix} \alpha_1 \\ \alpha_2 \\ \alpha_3 \end{pmatrix}=\begin{pmatrix} 1 \\ 1 \\ 1 \end{pmatrix}\\ \alpha_1=\frac12,\alpha_2=\frac16,\alpha_3=\frac13
100120123
α1α2α3
=
111
α1=21,α2=61,α3=31
python实现
按照定义实现就是这样:
def __mul__(self, other):
if isinstance(other, sympy.Expr):
return Matrix([[sympy.simplify(e * other) for e in line] for line in self.__lines])
columns = len(self.__lines)
rows = len(other.__lines[0])
if columns != rows:
raise Exception("矩阵A列数%d != 矩阵B的行数%d" % (columns, rows))
# 弄一个m行p列的新矩阵
m = len(self.__lines[0])
n = columns
p = len(other.__lines)
result = [[0] * m for _ in range(0, p)]
# i 代表 A矩阵的行
for i in range(0, m):
# j 代表 B 矩阵的列
for j in range(0, p):
# 第一个矩阵的行 与第二个矩阵列的乘积和
# k 代表 A矩阵的列和B矩阵的行
for k in range(0, n):
mul = self.__lines[k][i] * other.__lines[j][k]
result[j][i] += mul
if not isinstance(result[j][i], complex):
result[j][i] = round(result[j][i], 2)
return Matrix(result)
有没有更快的算法呢?是有的,我的下一篇文章就是专门介绍比常规算法更快的算法:Strassen算法。