资讯详情 线性代数 [ 逆矩阵、列空间、秩与零空间 ]
📅 2026/10/29 11:06:23
线性代数前面一直在变换向量现在我们倒过来找输入前面几篇我们已经知道了一个矩阵怎样移动基向量怎样改变整个空间也知道了行列式为什么能描述面积和体积的变化。接下来我们把问题倒过来。原来是给一个向量让矩阵把它送到某个位置。现在是先给一个目标位置再问哪个输入向量经过这次变换以后能够到达这里这其实就是求解线性方程组已知矩阵、目标位置求满足等式的输入向量✅ 本质以分量为未知数的线性方程组✅ 可逆时先从方程进入几何再把同一套理解推广到不同维度之间的变换。内容接着上一篇《行列式》展开。这里使用实数、列向量和标准坐标系。把方程组写成矩阵究竟多看见了什么系数、未知数、目标各自放在什么位置我们继续使用上一篇出现过的矩阵来看这个方程组把每个未知数对应的系数排好方程组就可以写成也就是这里的是待寻找的输入是已经指定的输出。某个未知数在某条方程里没有出现就把对应系数填成不能直接跳过那个位置。“线性”也有明确要求未知数以一次项出现系数是固定的数。、、这样的项就不能直接放进这里的线性方程组。矩阵方程把求解变成了寻找原像按照前面讲过的列向量观点于是求解方程组又有了一个说法第一列取多少份第二列取多少份才能组合出目标取就可以因为大家注意现在“求未知数”“寻找线性组合的系数”“寻找变换前的向量”说的是同一件事。先用代码算一下。下面使用 NumPy首次运行可安装python3 -m pip install numpy。各代码块按文章顺序执行创建脚本 inverse_spaces_demo.py。import numpy as np np.set_printoptions(precision3, suppressTrue) A np.array([[3., 1.], [1., 2.]]) b np.array([5., 5.]) x np.linalg.solve(A, b) print(解 x , x) print(A x , A x) print(代回成立, np.allclose(A x, b))输出结果解 x [1. 2.] A x [5. 5.] 代回成立 True表示矩阵乘法。NumPy 这里用形状为(1, 2)的数组表示向量数学表达仍按列向量理解。np.allclose允许浮点运算产生的小误差本篇这些简单数值例子使用默认容差实际数据需要结合量级和误差要求设定。逆矩阵能不能把整个变换倒回去从一个目标倒回去要有一套统一的规则刚才我们找到了的来源。接下来再问如果换一个目标我们能不能也把它倒着送回去而且这套倒回去的规则对所有目标都有效对于这里的方阵答案是可以。上一篇已经算出平面没有被压进一条直线每一个目标都有且只有一个来源。把的变换完整倒回去对应的矩阵就叫作的逆矩阵记作。图中黄色向量先从到达再回到。阴影区域和网格也一起恢复逆矩阵要撤销的是整个变换。我们还可以用熟悉的变换来想象逆时针旋转可以用顺时针旋转撤销。水平剪切可以用撤销。这里是把剪切倒回去原点始终固定。所以我们可以理解为逆矩阵的几何入口把变换倒着播放再追踪目标回到哪里。为什么乘起来是单位矩阵先做再做所有向量都回到原处所以这里的是单位矩阵。二维时对于可逆方阵反过来的顺序也成立于是在两侧左乘这里要从左边乘保持矩阵乘法的顺序。当前这个例子的逆矩阵为不是把的每个元素分别取倒数。它需要满足的是矩阵乘积等于。A_inv np.linalg.inv(A) print(A 的逆矩阵\n, A_inv, sep) print(先 A 后逆变换, np.allclose(A_inv A, np.eye(2))) print(先逆变换后 A, np.allclose(A A_inv, np.eye(2))) print(逆变换恢复 x, np.allclose(A_inv b, x))输出结果A 的逆矩阵 [[ 0.4 -0.2] [-0.2 0.6]] 先 A 后逆变换 True 先逆变换后 A True 逆变换恢复 x True公式帮助我们理解求解。实际只需要解这个方阵方程时可以直接使用前面的np.linalg.solve无需先显式构造逆矩阵。solve要求系数矩阵为满秩方阵inv则返回方阵的逆具体接口见 NumPysolve与 NumPyinv。没有逆矩阵方程就一定没有解吗空间压缩以后先看目标还在不在我们换成前面出现过的压缩矩阵它对任意输入的作用是输出的两个坐标总是相等所以所有输出都在直线上。整个输入平面被压进了一条直线。如果我们指定目标那就到不了因为它不在这条直线上。但如果目标是呢取输入、、都可以。它们的都等于。因此没有逆矩阵不能直接推出某个具体方程没有解。它告诉我们的是无法建立一套对整个输出空间有效、并且唯一恢复原输入的逆规则。同一个输出可能对应多个输入。我们即使人为挑选其中一个也无法把其他原输入同时恢复出来。列空间就是所有可能输出的集合把问题推广一下。假设矩阵的列依次是那么让输入的各个坐标任意变化得到的就是这些列向量的所有线性组合。这个集合叫作矩阵的列空间所以列空间把“矩阵的列”和“变换能到达哪里”连接起来了。形式定义也可对照教材 Interactive Linear AlgebraSubspaces。对当前矩阵第二列是第一列的倍没有带来新的独立方向。现在判断方程是否有解就有了一个准确条件【有解的话成立】列空间总包含零向量因为。因此目标为零时至少有零向量这个解。秩输出究竟还剩几个独立方向行列式为零以后还需要描述压缩到什么程度三维空间被压成一个平面三维体积变为。被压成一条直线体积也变为。如果全部压到原点体积还是。这些变换的行列式都是但压缩的程度显然不同。这就需要另一个数秩就是列空间的维数。这里的“维数”可以先理解为需要几个线性无关的向量才能张成所有可能输出。图中的例子分别使用单位矩阵、保留前两个坐标的矩阵、只保留第一个坐标的矩阵以及零矩阵。每个面板里蓝色采样点表示输出我们用有限点显示方向实际列空间会无限延伸零子空间除外。回到二维刚才的的秩为的秩为。矩阵有两列并不意味着秩一定为还要看这些列是否独立。用秩检查目标是否增加了新方向把目标接到矩阵右边得到增广矩阵。如果已经在列空间里增加这一列不会增加秩如果它带来了新的独立方向秩就会增加。也就是【有解的话成立】用我们的两个目标检查一下C np.array([[1., 2.], [1., 2.]]) print(C 的秩, np.linalg.matrix_rank(C)) for target in [np.array([3., 3.]), np.array([3., 2.])]: augmented np.column_stack((C, target)) rank_aug np.linalg.matrix_rank(augmented) print(fb{target}增广矩阵的秩{rank_aug}有解{rank_aug np.linalg.matrix_rank(C)})输出结果C 的秩 1 b[3. 3.]增广矩阵的秩1有解True b[3. 2.]增广矩阵的秩2有解False第一种目标没有带来新的方向第二种目标让原来的一维列空间扩展为二维所以它不可能由原来的列组合出来。这里的等价关系是精确数学结论np.linalg.matrix_rank使用奇异值和容差估计数值秩。对这些简单例子很清楚遇到带噪声或接近相关的数据时要结合数据尺度选择容差。接口说明见 NumPymatrix_rank。零空间哪些输入被送到了原点列空间看输出零空间回头看输入列空间已经告诉我们目标能不能到达。接下来我们研究那些被压缩掉的输入方向。对于让输出等于零因此所有这样的输入都可以写成这些输入组成的集合叫作的零空间也叫核。一般定义为右侧为零的线性方程组叫作齐次线性方程组。因此零空间也就是齐次方程的全部解。对零空间是输入平面里沿方向的一整条直线。注意它是所有被送到零的输入组成的直线这些输入的共同输出才是原点。列空间和零空间都包含零向量但它们在回答不同的问题列空间收集输出零空间收集特定的一类输入。秩与零空间维数为什么要加起来看对于一个的矩阵输入有个坐标。秩记为则有也就是这称为秩—零度定理。可以先用消元来理解秩等于主元的个数齐次方程里其余变量是自由变量。每一个自由变量提供一个独立的解方向所以零空间维数是总变量数减去主元数。更正式的陈述见 Interactive Linear AlgebraThe Rank Theorem。前面三维图中的四种情况对应的零空间维数也就依次为 0,1,2,30,1,2,3for matrix in [np.eye(3), np.diag([1., 1., 0.]), np.diag([1., 0., 0.]), np.zeros((3, 3))]: rank np.linalg.matrix_rank(matrix) print(f秩{rank}列空间维数{rank}零空间维数{matrix.shape[1] - rank})输出结果秩3列空间维数3零空间维数0 秩2列空间维数2零空间维数1 秩1列空间维数1零空间维数2 秩0列空间维数0零空间维数3比如保留前两个坐标、丢掉第三个坐标时输出张成一个平面秩为输入的整个轴都被送到原点所以零空间是一维。这里相加得到的是输入维数也就是列数。后面讨论非方阵时这一点尤其要记住。找到一个解以后怎样看见所有解沿零空间的方向移动输出不会改变我们已经知道那么把第二个向量的任意倍加到第一个输入上会怎样由于线性变换保持加法和数乘所以这一整条直线上的输入都会到达同一个目标。选定的一个解叫作特解记为。这里取。我们再反过来检查一下有没有漏掉其他解。如果也是一个解这说明任何解与特解的差都在零空间里。这个推理对任意矩阵都成立因此有解时全部解可以写成这个表达有一个前提方程已经有解。没有特解就不能写出这样的解集。用代码观察其中三个输入。循环里的t对应公式中的参数k np.array([-2., 1.]) x_p np.array([1., 1.]) print(C k , C k) for t in [-1, 0, 1]: candidate x_p t * k print(ft{t:2d}x{candidate}C x{C candidate})输出结果C k [0. 0.] t-1x[3. 0.]C x[3. 3.] t 0x[1. 1.]C x[3. 3.] t 1x[-1. 2.]C x[3. 3.]当时方程组叫作非齐次线性方程组。这里有一个几何上的区别零空间经过原点是向量子空间非齐次方程有解时解集是零空间的一个平移通常称为仿射子空间它不经过原点。这个关系也可以对照教材 Interactive Linear AlgebraSolution Sets。非方阵输入和输出为什么可以有不同维度行数记录输出列数记录输入到这里我们主要用二维到二维、三维到三维的例子。接下来把输入和输出放在两个不同的空间里。一个的矩阵表示为什么列数是输入维数因为输入空间有个标准基向量每一个基向量的去向占一列。为什么行数是输出维数因为每一个输出向量需要个坐标来记录所以每一列要写个数。因此列空间位于输出空间中零空间位于输入空间中。方阵让两边恰好有相同维数非方阵则把它们的区别直接显露出来。3×2 矩阵把二维平面送进三维空间举个例子第一列告诉我们二维输入空间的 被送到三维坐标第二列告诉我们 被送到。任意二维输入的输出为输出总满足所以列空间是三维空间里的平面。反过来任意都能通过取得到因此整个平面都能到达。大家看清楚左边是二维输入空间右边是三维输出空间。这里用两个面板是为了分别标清它们的坐标。这个矩阵的两列线性无关所以秩为。输入只有两个维度已经没有输入方向被压掉零空间就是。但它的输出也只铺满一个平面到不了整个三维空间。比如可以到达就到不了。E np.array([[2., 0.], [-1., 1.], [-1., 1.]]) u np.array([1., 2.]) print(E 的形状, E.shape) print(E u , E u) print(秩, np.linalg.matrix_rank(E)) print(零空间维数, E.shape[1] - np.linalg.matrix_rank(E))输出结果E 的形状 (3, 2) E u [2. 1. 1.] 秩 2 零空间维数 0这说明输入信息可以完整保留但输出未必覆盖整个指定的输出空间。对落在这个平面上的目标输入唯一对平面外的目标没有输入。2×3 矩阵三维输入变成二维输出再来看一个直接丢掉第三个坐标的变换三列分别记录三个输入基向量的去向 和 保留为两个二维标准基 被送到零。它能到达整个二维输出空间所以列空间是秩为。但输入的轴都被送到原点所以零空间是一维给任意二维目标它的所有输入都是其中任取实数。每一个目标都能到达而且每一个目标都有无穷多个来源。P np.array([[1., 0., 0.], [0., 1., 0.]]) print(P 的形状, P.shape) print(秩, np.linalg.matrix_rank(P)) print(零空间维数, P.shape[1] - np.linalg.matrix_rank(P)) for z in [-1, 0, 1]: candidate np.array([2., 3., z]) print(f输入{candidate}输出{P candidate})输出结果P 的形状 (2, 3) 秩 2 零空间维数 1 输入[ 2. 3. -1.]输出[2. 3.] 输入[2. 3. 0.]输出[2. 3.] 输入[2. 3. 1.]输出[2. 3.]“满秩”要分清楚是行满秩还是列满秩对一般的矩阵秩达到时通常称为满秩。但刚才的和都满秩零空间却不同情况含义方程 AxbAxb 的表现列满秩矩阵的列向量线性无关零空间只有零向量只要有解解就唯一目标仍可能不可达行满秩列空间覆盖整个输出空间任意目标都有解若解不唯一方阵满秩上面两个条件同时满足任意目标都有唯一解存在逆矩阵所以“满秩就没有非零输入被送到零”这句话需要有列满秩这个条件。对刚才的矩阵它行满秩但仍然有一维零空间。本篇所说的逆矩阵指满足的通常意义下的双侧逆它属于可逆方阵。非方阵在相应满秩条件下可能有左逆或右逆但不能同时满足这两个单位矩阵条件。伪逆也属于另一套概念这里先不展开。回到方程什么时候无解什么时候唯一什么时候无穷多解现在我们可以把前面的内容接起来看了。给定实矩阵和目标先检查目标是否在列空间里有解以后再检查零空间有没有非零向量。条件解的情况几何原因无解变换到不了这个目标且唯一解目标可达而且不存在保持输出不变的非零输入方向且零空间含非零向量无穷多解从任意特解出发沿零空间方向移动仍是解这里讨论的是实数上的方程。只要存在一个非零零空间向量它的任意实数倍就会给出无穷多个输入。如果是方阵我们还可以把熟悉的结论串在一起【可逆才有下列等价】不能只看“方程个数和未知数个数相同”就断言一定有唯一解。我们的也是两条方程、两个未知数但两行重复结果完全取决于目标是否相容。程序返回一个向量就代表原方程解出来了吗solve报错与具体目标是否有解是两个问题对奇异矩阵np.linalg.solve会报错它处理的是满秩方阵的求解任务。我们不能据此说没有解因为前面已经找到了无穷多个。对于非方阵或奇异矩阵实践中还会遇到np.linalg.lstsq。它求的是让残差范数尽可能小的向量如果原方程有解最小残差可以为零如果没有精确解它仍然可能返回一个向量。存在多个最小残差解时NumPy 返回其中长度最小的那个。接口说明见 NumPylstsq。我们用同一个再做一次实验for target in [np.array([3., 3.]), np.array([3., 2.])]: x_ls, _, rank, _ np.linalg.lstsq(C, target, rcondNone) error np.linalg.norm(C x_ls - target) print(fb{target}返回 x{x_ls}秩{rank}残差范数{error:.3f})输出结果b[3. 3.]返回 x[0.6 1.2]秩1残差范数0.000 b[3. 2.]返回 x[0.5 1. ]秩1残差范数0.707第一行程序找到的特解是和我们手选的不同但二者相差恰好在零空间里。因此它们对应同一个输出。第二行返回的输入经过后到达。它在列空间上距离目标最近但并没有到达目标。残差范数为原方程仍然无解。这里主动计算C x_ls - target还有一个原因lstsq返回的residuals在某些矩阵形状或秩不足时是空数组空数组并不意味着残差为零。实际判断还需要结合浮点容差打印出的0.000也只是保留三位小数后的显示。再看一个只有一行的矩阵最后把输出维数继续减少到一个二维输入现在被送到实数轴上的一个数。第一列说明 被送到数第二列说明 被送到数。它的列空间是整个秩为零空间仍然是那条输入直线。实际上前面的先计算同一个数再把这个数同时放进两个输出坐标里。这一看我们就能把整篇文章接回开头的问题给定目标列空间告诉我们到不到得了到得了以后零空间给出所有保持目标不变的输入方向。逆矩阵对应其中最完整的情况每一个目标都可达而且来源唯一。下一篇讲点积时我们会继续研究这种“把一个向量变成一个数”的线性规则。