人工智能必备数学知识学习笔记11:向量空间,维度,和四大子空间
- 空间,向量空间和欧几里得空间
- 广义向量空间
- 子空间
S满足十大性质 >>
存在O属于S,使得u+O = u
对于每一个u存在-u属于S,使得u+(-u)=0
对于u属于S
a)0u 属于 S
b)-1u 属于 S
- 直观理解欧几里得空间的子空间
穿过坐标原点的直线为该欧几里得二位空间的子空间
非子空间: (不过原点)不过坐标原点的直线,不满足向量相加和数乘,所以并不是二位欧几里得子空间
非子空间:(过原点但射线不满足)黑色射线不满足子空间的性质
对于n维空间来说:
过原点的一个m维空间(m < n),是n维空间的一个子空间
- 维度
dim : 维度的英文缩写
一个(欧几里得)空间的任何一组基,其中的向量个数是相同的
这个非零向量,二位空间子空间的一组基
这个子空间的维度唯一
dim : 维度的英文缩写
过原点的一个平面,是三维空间的一个子空间 S2 dim(S2 ) = 2
过原点的一个直线,是三维空间的一个子空间 S1 dim(S1) = 1
原点本身,是三维空间的一个子空间!S0 dim(S0) = 0
对于三维空间来说:过原点的一个平面是三维空间的子空间
dim(S2 ) = 2
是n维空间的子空间
是n维空间的子空间
- 行空间和矩阵的行秩
其声称空间的维度是多少
找到这组向量有多少和其他向量线性相关
行空间是4维空间的子集
m行n列,行空间是n维空间的子集
列空间是三维空间的子集
m行n列,列空间是m维空间的子集
求矩阵行最简形是的非零行数量
一个矩阵的航最简形式的非零行数量
一个矩阵的行最简形式的非零行数量称为矩阵的行秩(Row Rank)
行空间的维度,为矩阵的行秩
- 列空间
非主元列为自由列
一个矩阵的行最简形式的主元列数量称为矩阵的列秩
列空间的维度为矩阵的列秩
行最简形式主元列的对应原矩阵的列,是列空间中的一组基
行最简形式的非零行个数为矩阵的行秩 行最简形式主元的列数为矩阵的列秩
行空间的维度,为矩阵的行秩 列空间的维度,为矩阵的列秩
行最简形式的非零行,是行空间的一组基 主元列对应原矩阵的列,是列空间的一组基
- 矩阵的秩和矩阵的逆
列
代码实现:
1.文件Vector.py 编写代码:返回向量是否相等(__eq__)、返回向量是否不等(__neq__)
1 #向量类 2 #__values() 与 _values()区别更多体现在继承上,如果是在类的内部使用时官方建议使用_values()发方法 3 from ._global import is_zero,is_equal 4 import math 5 6 7 8 class Vector: 9 10 def __init__(self,lst): 11 self._values = list(lst)#将数组赋值给向量类中(注:使用list方法将列表lst复制一份保证他的不被外界调用时修改) 12 13 #零向量类方法:参数1:为当前的类(cls) 参数2:维度(dim) 返回dim维的零向量 14 @classmethod 15 def zero(cls,dim): 16 return cls([0] * dim) 17 18 #返回向量的模(向量长度): 19 def norm(self): 20 return math.sqrt(sum(e**2 for e in self))#sqrt()方法为开平方根,sum()求和方法,e**2 e的平方 21 22 #返回向量的单位向量 23 def normalize(self): 24 if is_zero(self.norm()): #此处判断一个精度范围(该精度范围调用内部自定义全局变量文件 _global.py)(由于浮点数计算机含有误差所以无法使用 == 0 的操作) 25 raise ZeroDivisionError("Normalize error! norm is zero.") #考虑零向量时报此处异常即可 26 #方案一:return Vector(1/self.norm() * [e for e in self])#循环遍历向量中的每个元素分别除以该向量的模即为单位向量 27 #方案二:return 1/self.norm() * Vector(self._values)#当前向量除以模即为单位向量 28 return Vector(self._values) / self.norm() 29 30 #返回当前向量的底层列表 TODO:2020-08-12 17:04:00 31 def underlying_list(self): 32 return self._values[:]#此时为返回该列表的一个副本 33 34 #向量加法,返回结果向量 35 def __add__(self, another): 36 # assert判断传入的向量维度是否相等 37 assert len(self) == len(another),\ 38 "Error in adding. Length of vectors must be same." 39 return Vector([a+b for a,b in zip(self,another)])#使用zip()方法将两个向量取出来 40 41 # 向量减法 42 def __sub__(self, another): 43 #判断维度相等 44 assert len(self) == len(another), \ 45 "Error in adding. Length of vectors must be same." 46 return Vector([a - b for a, b in zip(self, another)]) 47 48 #向量点乘(向量之间相乘):返回结果标量 49 def dot(self,another): 50 #判断维度相等 51 assert len(self) == len(another), \ 52 "Error in dot product. Length of vectors must be same." 53 return sum(a * b for a,b in zip(self,another))# 方法zip()将两组向量配成对应位置的数据对 54 55 # 向量数量乘法(数乘数组),返回数量乘法的结果向量:self * k 56 def __mul__(self, k): 57 return Vector([k * e for e in self]) 58 59 # 向量数量乘法(数组乘数),返回数量乘法的结果向量:k * self 60 def __rmul__(k, self): 61 return k * self #此处直接调用的是上方的乘法函数 62 63 # 向量除法:返回数量除法的结果向量 self / k 64 def __truediv__(self, k): 65 return (1 / k) * self 66 67 #返回向量取正的结果向量 68 def __pos__(self): 69 return 1 * self 70 71 # 返回向量取负的结果向量 72 def __neg__(self): 73 return -1 * self 74 75 #返回向量是否相等 76 def __eq__(self,other): 77 other_list = other.underlying_list() 78 if (len(other_list) != len(self._values)): 79 return False 80 return all(is_equal(x,y) for x,y in zip(self._values,other_list)) # 返回全部相等向量的值 81 82 #判断向量是否不等 83 def __neq__(self,other): 84 return not(self == other) 85 86 #返回向量迭代器(当有迭代器时,zip()方法中就不用再次传入两个向量数组,直接传入向量对象即可<zip(self._values,another._values)>) 87 def __iter__(self): 88 return self._values.__iter__() 89 90 #取向量的index个元素 91 def __getitem__(self, index): 92 return self._values[index] 93 94 #返回向量的长度(有多少个元素) 95 def __len__(self): 96 return len(self._values) 97 98 # 向量展示(系统调用) 99 def __repr__(self): 100 return "Vector({})".format(self._values) 101 102 # 向量展示(用户调用) 103 def __str__(self): 104 return "({})".format(", ".join(str(e) for e in self._values))#通过遍历 self.__values 将e转成字符串通过逗号加空格来链接放入大括号中 105 106 # u = Vector([5,2]) 107 # print(u)
2.文件main_vector.py编写代码:判断两个向量是否相等
1 from playLA.Vector import Vector 2 3 if __name__ == "__main__": 4 5 vec = Vector([5,2]) 6 print(vec) 7 print(len(vec))#打印向量的维度 8 print("vec[0] = {}, vec[1] = {}".format(vec[0],vec[1])) 9 10 #向量加法 11 vec2 = Vector([3,1]) 12 print("{} + {} = {}".format(vec,vec2,vec+vec2)) 13 #向量减法 14 print("{} - {} = {}".format(vec, vec2, vec - vec2)) 15 #向量乘法(向量乘以数) 16 print("{} * {} = {}".format(vec,3,vec * 3)) 17 # 向量乘法(数乘以向量) 18 print("{} * {} = {}".format(3, vec, 3 * vec)) 19 # 向量取正 20 print("+{} = {}".format(vec, +vec)) 21 # 向量取负 22 print("-{} = {}".format(vec, -vec)) 23 24 #零向量 25 zero2 = Vector.zero(2) 26 print(zero2) 27 #向量加上零向量 28 print("{} + {} = {}".format(vec, zero2, vec + zero2)) 29 30 #向量的模 31 print("norm({}) = {}".format(vec,vec.norm())) 32 print("norm({}) = {}".format(vec2,vec2.norm())) 33 print("norm({}) = {}".format(zero2, zero2.norm())) 34 35 #单位向量 36 print("normalize({}) is {}".format(vec,vec.normalize())) 37 print(vec.normalize().norm())#单位向量的模(长度)基本为数字1 38 print("normalize({}) is {}".format(vec2, vec2.normalize())) 39 print(vec2.normalize().norm()) # 单位向量的模(长度)基本为数字1(计算机中会有误差0.9999...) 40 #print(zero2.normalize()) 41 #捕捉异常 ZeroDivisionError 42 try: 43 zero2.normalize() 44 except ZeroDivisionError: 45 print("Cannot normalize zero vector {}".format(zero2)) 46 47 #向量点乘(向量乘以向量) 48 print(vec.dot(vec2)) 49 50 #判断两个向量是否相等 51 vec3 = Vector([0,0]) 52 print("{} == {}?{}".format(zero2, vec3, vec3 == zero2)) 53 print("{} == {}?{}".format(vec2, vec3, vec2 == vec3)) 54 print("{} != {}?{}".format(vec2, vec3, vec2 != vec3))
3.文件main_vector.py运行结果为:
1 /Users/liuxiaoming/PycharmProjects/LinearAlgebra/venv/bin/python /Applications/PyCharm.app/Contents/plugins/python/helpers/pydev/pydevconsole.py --mode=client --port=61711 2 import sys; print('Python %s on %s' % (sys.version, sys.platform)) 3 sys.path.extend(['/Users/liuxiaoming/PycharmProjects/LinearAlgebra']) 4 PyDev console: starting. 5 Python 3.8.2 (v3.8.2:7b3ab5921f, Feb 24 2020, 17:52:18) 6 [Clang 6.0 (clang-600.0.57)] on darwin 7 >>> runfile('/Users/liuxiaoming/PycharmProjects/LinearAlgebra/main_vector.py', wdir='/Users/liuxiaoming/PycharmProjects/LinearAlgebra') 8 (5, 2) 9 2 10 vec[0] = 5, vec[1] = 2 11 (5, 2) + (3, 1) = (8, 3) 12 (5, 2) - (3, 1) = (2, 1) 13 (5, 2) * 3 = (15, 6) 14 3 * (5, 2) = (15, 6) 15 +(5, 2) = (5, 2) 16 -(5, 2) = (-5, -2) 17 (0, 0) 18 (5, 2) + (0, 0) = (5, 2) 19 norm((5, 2)) = 5.385164807134504 20 norm((3, 1)) = 3.1622776601683795 21 norm((0, 0)) = 0.0 22 normalize((5, 2)) is (0.9284766908852593, 0.3713906763541037) 23 1.0 24 normalize((3, 1)) is (0.9486832980505138, 0.31622776601683794) 25 0.9999999999999999 26 Cannot normalize zero vector (0, 0) 27 17 28 (0, 0) == (0, 0)?True 29 (3, 1) == (0, 0)?False 30 (3, 1) != (0, 0)?True
4.文件LinearSystem.py编写代码为:
4.1修改初始化方法(__init__增加:判断若b为空时,则该矩阵为A矩阵 TODO:2020-08-22)
4.2新增方法( rank(A) 求矩阵的秩)
1 from playLA.Matrix import Matrix 2 from playLA.Vector import Vector 3 from playLA._global import is_zero 4 5 6 #线性系统 7 class LinearSystem: 8 9 #初始化函数:参数A:增广矩阵的等号左边的系数 参数b:增广矩阵的等号右边的值(方程右边的结果) 10 def __init__(self, A, b): 11 #判断 b为None 或者 矩阵A的行数等于b的列数 12 assert b is None or A.row_num() == len(b),\ 13 "row number of A must be equal to the length of b" 14 self._m = A.row_num()#行数 15 self._n = A.col_num()#列数 16 17 #判断若b为空时,则该矩阵为A矩阵 TODO:2020-08-22 18 if b is None: 19 self.Ab = [A.row_vector(i) for i in range(self._m)] 20 21 if isinstance(b, Vector):#如果等号右侧方程结果为列向量时 22 #增广矩阵 23 self.Ab = [Vector(A.row_vector(i).underlying_list() + [b[i]]) 24 for i in range(self._m)] 25 26 if isinstance(b, Matrix):#如果等号右侧方程结果为单位矩阵时 27 #增广矩阵 28 self.Ab = [Vector(A.row_vector(i).underlying_list() + b.row_vector(i).underlying_list()) 29 for i in range(self._m)] 30 #主元 31 self.pivots = [] 32 33 #寻找最大主源系数 34 def _max_row(self, index_i, index_j, n): 35 best, ret = self.Ab[index_i][index_j], index_i #存储第index行index列的元素值与当前index的值 36 for i in range(index_i + 1, n): #从index+1开始一直遍历到n 37 if self.Ab[i][index_j] > best: 38 best, ret = self.Ab[i][index_j], i 39 return ret 40 41 #高斯—约旦消元法-前向过程 42 def _forward(self): 43 44 i,k = 0,0 45 while i < self._m and k < self._n: 46 #看Ab[i][k]位置是否为主元 47 max_row = self._max_row(i, k, self._m)#寻找最大主源系数的行数 48 self.Ab[i], self.Ab[max_row] = self.Ab[max_row],self.Ab[i] #行交换操作 49 50 if is_zero(self.Ab[i][k]):#判断此时该值是否为0 51 k += 1 52 else: 53 #将主元归为一 54 self.Ab[i] = self.Ab[i] / self.Ab[i][k] 55 #将当前主源下的所有行对应主源列的元素全部归为0 :也就是第一次循环会将该主源下的所有对应列变为0,第二次循环会将第二次主源列下的所有对应列变为0 56 for j in range(i + 1, self._m): 57 self.Ab[j] =self.Ab[j] -self.Ab[j][k] * self.Ab[i]#该主源行下的所有行减去主源行 58 self.pivots.append(k) 59 i += 1 60 61 #高斯-约旦消元法-后向过程 62 def _backward(self): 63 n = len(self.pivots) 64 #n = self._m #行数 65 for i in range(n-1, -1, -1): #反向遍历且 从参数1的位置遍历到参数2的位置,也就是从倒数第一个位置遍历到第一个位置,参数3为步长(遍历的方向及单位) 66 k = self.pivots[i] 67 #Ab[i][k]为主源 68 for j in range(i-1, -1, -1): 69 self.Ab[j] = self.Ab[j] - self.Ab[j][k] * self.Ab[i]#该主源行上的所有行减去主源行 70 71 #高斯-约旦消元法:如果有解,返回True;如果没有解,返回False 72 def gauss_jordan_elimination(self): 73 # 高斯—约旦消元法-前向过程 74 self._forward() 75 # 高斯-约旦消元法-后向过程 76 self._backward() 77 for i in range(len(self.pivots), self._m):#此处为最后一行的非零行下一行的全零行的结果值不为零的判断 0.0 0.0 | 5.0 78 if not is_zero(self.Ab[i][-1]): 79 return False 80 return True 81 82 # 打印结果(求每个未知数的值) 83 def fancy_print(self): 84 for i in range(self._m): 85 print(" ".join(str(self.Ab[i][j]) for j in range(self._n)),end=" ") 86 print("|",self.Ab[i][-1]) 87 88 89 #求增广矩阵的逆 90 def inv(A): 91 92 if A.row_num() != A.col_num():#判断矩阵行是否等于列,若不等于无解 93 return None 94 95 n = A.row_num() # TODO:A.row_num 无括号代表方法,类型为method A.row_num() 有括号代表具体的返回值 96 print(n,type(n)) 97 ls = LinearSystem(A, Matrix.identity(n))#实例化一个线性系统(创建一个增广矩阵) 98 if not ls.gauss_jordan_elimination(): #判断是否有逆矩阵(是否有解) 99 return None 100 101 invA = [[row[i] for i in range(n, n*n)] for row in ls.Ab] #将右侧的单位矩阵拼接到矩阵中 102 103 return Matrix(invA) 104 105 106 #求矩阵的秩 107 def rank(A): 108 109 ls = LinearSystem(A,None) #声明一个矩阵A 110 ls.gauss_jordan_elimination() #将矩阵进行高斯消元 111 112 zero = Vector.zero(A.col_num()) #创建零向量(维度为该矩阵的列数即可,每一行的元素个数) 113 return sum([row != zero for row in ls.Ab]) #判断有多少非零行 (不等指的是向量间是否不等)
5.文件 main_linear_system.py 编写代码:求矩阵的秩
1 from playLA.Matrix import Matrix 2 from playLA.Vector import Vector 3 from playLA.LinearSystem import LinearSystem 4 from playLA.LinearSystem import inv,rank 5 6 if __name__ == "__main__": 7 8 # 高斯—约旦消元法 9 A = Matrix([[1,2,4],[3,7,2],[2,3,3]]) 10 b = Vector([7,-11,1]) 11 ls = LinearSystem(A,b) #线性系统赋值成一个增广矩阵 12 ls.gauss_jordan_elimination() #高斯—约旦消元 13 ls.fancy_print() #打印出结果(求每个未知数的值) 14 print("-" * 50)#分隔线 15 16 # 高斯—约旦消元法 17 A2 = Matrix([[1, -3, 5], [2, -1, -3], [3, 1, 4]]) 18 b2 = Vector([-9, 19, 13]) 19 ls2 = LinearSystem(A2, b2) # 线性系统赋值成一个增广矩阵 20 ls2.gauss_jordan_elimination() # 高斯—约旦消元 21 ls2.fancy_print() # 打印出结果(求每个未知数的值) 22 print("-" * 50) # 分隔线 23 24 # 高斯—约旦消元法 25 A3 = Matrix([[1, 2, -2], [2, -3, 1], [3, -1, 3]]) 26 b3 = Vector([6, -10, -16]) 27 ls3 = LinearSystem(A3, b3) # 线性系统赋值成一个增广矩阵 28 ls3.gauss_jordan_elimination() # 高斯—约旦消元 29 ls3.fancy_print() # 打印出结果(求每个未知数的值) 30 print("-" * 50) # 分隔线 31 32 # 高斯—约旦消元法 33 A4 = Matrix([[3, 1, -2], [5, -3, 10], [7, 4, 16]]) 34 b4 = Vector([4, 32, 13]) 35 ls4 = LinearSystem(A4, b4) # 线性系统赋值成一个增广矩阵 36 ls4.gauss_jordan_elimination() # 高斯—约旦消元 37 ls4.fancy_print() # 打印出结果(求每个未知数的值) 38 print("-" * 50) # 分隔线 39 40 # 高斯—约旦消元法 41 A5 = Matrix([[6, -3, 2], [5, 1, 12], [8, 5, 1]]) 42 b5 = Vector([31, 36, 11]) 43 ls5 = LinearSystem(A5, b5) # 线性系统赋值成一个增广矩阵 44 ls5.gauss_jordan_elimination() # 高斯—约旦消元 45 ls5.fancy_print() # 打印出结果(求每个未知数的值) 46 print("-" * 50) # 分隔线 47 48 # 高斯—约旦消元法 49 A6 = Matrix([[1, 1, 1], [1, -1, -1], [2, 1, 5]]) 50 b6 = Vector([3, -1, 8]) 51 ls6 = LinearSystem(A6, b6) # 线性系统赋值成一个增广矩阵 52 ls6.gauss_jordan_elimination() # 高斯—约旦消元 53 ls6.fancy_print() # 打印出结果(求每个未知数的值) 54 print("-" * 50) # 分隔线 55 56 # 高斯—约旦消元法 57 A7 = Matrix([[1, -1, 2, 0, 3], 58 [-1, 1, 0, 2, -5], 59 [1, -1, 4, 2, 4], 60 [-2, 2, -5, -1, -3]]) 61 b7 = Vector([1, 5, 13, -1]) 62 ls7 = LinearSystem(A7, b7) # 线性系统赋值成一个增广矩阵 63 ls7.gauss_jordan_elimination() # 高斯—约旦消元 64 ls7.fancy_print() # 打印出结果(求每个未知数的值) 65 print("-" * 50) # 分隔线 66 print(ls7.pivots) #查看主源列数值 (从零开始算) 67 68 # 高斯—约旦消元法 69 A8 = Matrix([[2, 2], 70 [2, 1], 71 [1, 2]]) 72 b8 = Vector([3, 2.5, 7]) 73 ls8 = LinearSystem(A8, b8) # 线性系统赋值成一个增广矩阵 74 if not ls8.gauss_jordan_elimination(): 75 print("主元:",ls8.pivots 76 ,len(ls8.pivots)) # 查看主源列数值 (从零开始算) 77 print("No Solution!") 78 79 ls8.fancy_print() # 打印出结果(求每个未知数的值) 80 print("-" * 50) # 分隔线 81 82 #求矩阵的逆 83 A = Matrix([[1,2],[3,4]]) 84 invA = inv(A) 85 print("invA = {}".format(invA)) 86 print("A.dot(invA) = {}".format(A.dot(invA))) 87 print("invA.dot(A) = {}".format(invA.dot(A))) 88 89 #求矩阵的秩 90 print("rank(A8) = {}".format(rank(A8))) 91 print("rank(A7) = {}".format(rank(A7))) 92 print("rank(A6) = {}".format(rank(A6)))
5.文件 main_linear_system.py 运行结果为:
1 /Users/liuxiaoming/PycharmProjects/LinearAlgebra/venv/bin/python /Users/liuxiaoming/PycharmProjects/LinearAlgebra/main_linear_system.py 2 1.0 0.0 0.0 | -1.0 3 -0.0 1.0 0.0 | -2.0 4 -0.0 -0.0 1.0 | 3.0 5 -------------------------------------------------- 6 1.0 0.0 0.0 | 6.853333333333333 7 -0.0 1.0 0.0 | 1.506666666666665 8 0.0 0.0 1.0 | -2.266666666666666 9 -------------------------------------------------- 10 1.0 0.0 0.0 | -1.9999999999999998 11 0.0 1.0 0.0 | 1.0 12 -0.0 -0.0 1.0 | -3.0 13 -------------------------------------------------- 14 1.0 0.0 0.0 | 2.9999999999999996 15 -0.0 1.0 0.0 | -3.9999999999999996 16 0.0 0.0 1.0 | 0.4999999999999999 17 -------------------------------------------------- 18 1.0 0.0 0.0 | 3.0 19 -0.0 1.0 0.0 | -3.0 20 -0.0 -0.0 1.0 | 2.0 21 -------------------------------------------------- 22 1.0 0.0 0.0 | 1.0 23 0.0 1.0 0.0 | 1.0 24 -0.0 -0.0 1.0 | 1.0 25 -------------------------------------------------- 26 1.0 -1.0 0.0 -2.0 0.0 | -15.0 27 0.0 0.0 1.0 1.0 0.0 | 5.0 28 0.0 0.0 0.0 0.0 1.0 | 2.0 29 0.0 0.0 0.0 0.0 0.0 | 0.0 30 -------------------------------------------------- 31 [0, 2, 4] 32 主元: [0, 1] 2 33 No Solution! 34 1.0 0.0 | -4.0 35 0.0 1.0 | 5.5 36 0.0 0.0 | 5.0 37 -------------------------------------------------- 38 2 <class 'int'> 39 invA = Matrix([[-1.9999999999999996, 0.9999999999999998], [1.4999999999999998, -0.4999999999999999]]) 40 A.dot(invA) = Matrix([[1.0, 0.0], [8.881784197001252e-16, 0.9999999999999996]]) 41 invA.dot(A) = Matrix([[0.9999999999999996, 0.0], [2.220446049250313e-16, 1.0]]) 42 rank(A8) = 2 43 rank(A7) = 3 44 rank(A6) = 3 45 46 Process finished with exit code 0
- 零空间与看待零空间的三个视角
把A看作是函数(变换):A的零空间,所有被A变化为0的向量所组成的空间
把A看作是空间:A的零空间,是和A的行空间正交的向量空间
- 零空间 与 秩-零化度定理
零空间的维度为4
零空间的一组基 四个向量且线性无关
列空间是m维空间的子空间 零空间是n维空间的子空间
列空间的维度,为行最简形式中主元列数 零空间的维度,为行最简形式自由列数
主元列的对应原矩阵的列,是列空间的一组基 求零空间的基需要消元
- 左零空间,四大空间和为什么研究子空间
维度为m-r
列空间与左零空间是正交(垂直)关系
零空间与行空间是正交(垂直)关系