在上一篇文章中,有一个看待矩阵和向量乘法的视角,就是可以把矩阵理解成向量的函数,可以把一个向量,转换成另外的一个向量。
矩阵做这种向量间的转换,最典型的应用就是在图形中:
介绍矩阵数量乘的时候,举过这样的一个例子:有一个矩阵P,每一行都代表二维平面中的一个点的话,我们把这个矩阵P数量乘于2以后,得到的结果就相当于是把其中的每一个点的x坐标和y坐标都扩大2倍,画出图像化界面就是三角形扩大为原来的2倍
但是这里,换一个复杂的问题,我们要扩大,但是并不是x坐标和y坐标都扩大为原来的2倍,而是x坐标扩大为1.5倍,y坐标扩大为2倍,怎么做呢?
这里就是典型的,把一个向量,转换为另一个向量的问题
每一个点就是一个2维的列向量,通过矩阵T(向量函数的视角),转为另外一种的2维列向量
所以我们要找出这个矩阵T
由上图,根据矩阵乘于向量的定义,以及右边的向量,可以推导出:
矩阵T一定是有2列的,因为列向量(x,y)有2行,不然没法相乘
矩阵T一定是有2行的,因为右边的列向量(1.5x,2y)有2行
所以就有:(设a,b,c,d)
根据对应的关系,可以求出b=0,c=0
那么现在问题来了,上图的坐标轴上面,是有很多点的(3个点),一个点(向量)和这个矩阵T做乘法是完全可以的,但是有没有批量的操作呢???
我们只需要把所有的点(向量)集合在一起作为一个矩阵就可以了:矩阵中,使用列向量来表示
然后用矩阵T依次去乘于矩阵P中的每一个列向量即可(矩阵就是由一组向量组成的)
新矩阵就可以理解为:原来每一个点(向量),根据矩阵T,转换成新的向量组成的
基于矩阵和向量的乘法定义,就可以定义出矩阵和矩阵乘法的定义:
我们把两个矩阵的乘法,可以拆分另外一个矩阵(拆分为很多的列向量),然后矩阵再去分别乘于每一个列向量(一个矩阵和一个列向量相乘,还是得到另外一个列向量(函数视角))
两个矩阵相乘的条件:
所以:
实现矩阵的乘法
定义一个内部使用的文件_globals,用来存储全局使用的变量 EPSILON,用来判断精度用的
EPSILON = 1e-8
向量Vector类的代码:
import math
from ._globals import EPSILON
class Vector:
def __init__(self, lst):
self._values = list(lst)
@classmethod
def zero(cls, dim):
"""返回一个dim维的零向量"""
return cls([0] * dim)
def __add__(self, another):
"""向量加法,返回结果向量"""
assert len(self) == len(another), \
"Error in adding. Length of vectors must be same."
return Vector([a + b for a, b in zip(self, another)])
def __sub__(self, another):
"""向量减法,返回结果向量"""
assert len(self) == len(another), \
"Error in subtracting. Length of vectors must be same."
return Vector([a - b for a, b in zip(self, another)])
def norm(self):
"""返回向量的模"""
return math.sqrt(sum(e**2 for e in self))
def normalize(self):
"""返回向量的单位向量"""
if self.norm() < EPSILON:
raise ZeroDivisionError("Normalize error! norm is zero.")
return Vector(self._values) / self.norm()
def dot(self, another):
"""向量点乘,返回结果标量"""
assert len(self) == len(another), \
"Error in dot product. Length of vectors must be same."
return sum(a * b for a, b in zip(self, another))
def __mul__(self, k):
"""返回数量乘法的结果向量:self * k"""
return Vector([k * e for e in self])
def __rmul__(self, k):
"""返回数量乘法的结果向量:k * self"""
return self * k
def __truediv__(self, k):
"""返回数量除法的结果向量:self / k"""
return (1 / k) * self
def __pos__(self):
"""返回向量取正的结果向量"""
return 1 * self
def __neg__(self):
"""返回向量取负的结果向量"""
return -1 * self
def __iter__(self):
"""返回向量的迭代器"""
return self._values.__iter__()
def __getitem__(self, index):
"""取向量的第index个元素"""
return self._values[index]
def __len__(self):
"""返回向量长度(有多少个元素)"""
return len(self._values)
def __repr__(self):
return "Vector({})".format(self._values)
def __str__(self):
return "({})".format(", ".join(str(e) for e in self._values))
矩阵 Matrix类的代码:
from .Vector import Vector
class Matrix:
def __init__(self, list2d):
self._values = [row[:] for row in list2d]
@classmethod
def zero(cls, r, c):
"""返回一个r行c列的零矩阵"""
return cls([[0] * c for _ in range(r)])
def __add__(self, another):
"""返回两个矩阵的加法结果"""
assert self.shape() == another.shape(), \
"Error in adding. Shape of matrix must be same."
return Matrix([[a + b for a, b in zip(self.row_vector(i), another.row_vector(i))]
for i in range(self.row_num())])
def __sub__(self, another):
"""返回两个矩阵的减法结果"""
assert self.shape() == another.shape(), \
"Error in subtracting. Shape of matrix must be same."
return Matrix([[a - b for a, b in zip(self.row_vector(i), another.row_vector(i))]
for i in range(self.row_num())])
def dot(self, another):
"""返回矩阵乘法的结果"""
if isinstance(another, Vector):
# 矩阵和向量的乘法
assert self.col_num() == len(another), \
"Error in Matrix-Vector Multiplication."
return Vector([self.row_vector(i).dot(another) for i in range(self.row_num())])
if isinstance(another, Matrix):
# 矩阵和矩阵的乘法
assert self.col_num() == another.row_num(), \
"Error in Matrix-Matrix Multiplication."
return Matrix([[self.row_vector(i).dot(another.col_vector(j)) for j in range(another.col_num())]
for i in range(self.row_num())])
def __mul__(self, k):
"""返回矩阵的数量乘结果: self * k"""
return Matrix([[e * k for e in self.row_vector(i)]
for i in range(self.row_num())])
def __rmul__(self, k):
"""返回矩阵的数量乘结果: k * self"""
return self * k
def __truediv__(self, k):
"""返回数量除法的结果矩阵:self / k"""
return (1 / k) * self
def __pos__(self):
"""返回矩阵取正的结果"""
return 1 * self
def __neg__(self):
"""返回矩阵取负的结果"""
return -1 * self
def row_vector(self, index):
"""返回矩阵的第index个行向量"""
return Vector(self._values[index])
def col_vector(self, index):
"""返回矩阵的第index个列向量"""
return Vector([row[index] for row in self._values])
def __getitem__(self, pos):
"""返回矩阵pos位置的元素"""
r, c = pos
return self._values[r][c]
def size(self):
"""返回矩阵的元素个数"""
r, c = self.shape()
return r * c
def row_num(self):
"""返回矩阵的行数"""
return self.shape()[0]
__len__ = row_num
def col_num(self):
"""返回矩阵的列数"""
return self.shape()[1]
def shape(self):
"""返回矩阵的形状: (行数, 列数)"""
return len(self._values), len(self._values[0])
def __repr__(self):
return "Matrix({})".format(self._values)
__str__ = __repr__
测试代码:
from playLA.Vector import Vector
from playLA.Matrix import Matrix
if __name__ == "__main__":
matrix = Matrix([[1, 2], [3, 4]])
print(matrix)
print("matrix.shape ={}".format(matrix.shape()))
print("matrix.size ={}".format(matrix.size()))
print("len(matrix) ={}".format(len(matrix)))
print("matrix[0][0] ={}".format(matrix[0, 0]))
matrix2 = Matrix([[5, 6], [7, 8]])
print(matrix2)
print("add:{}".format(matrix + matrix2))
print("subtract:{}".format(matrix - matrix2))
print("scalar-mul:{}".format(2 * matrix))
print("scalar-mul:{}".format(matrix * 2))
print("zero_2_3:{}".format(Matrix.zero(2, 3)))
//测试矩阵和向量相乘
T = Matrix([[1.5, 0], [0, 2]])
p = Vector([5, 3])
print("T.dot(p) ={}".format(T.dot(p)))
//测试矩阵和矩阵相乘
P = Matrix([[0, 4, 5], [0, 0, 3]])
print("T.dot(P) ={}".format(T.dot(P)))
//验证两个矩阵的交换律
print("A.dot(B) ={}".format(matrix.dot(matrix2)))
print("B.dot(A) ={}".format(matrix2.dot(matrix)))