线性代数 [ 线性组合·张成的空间与基 ]

线性代数

从两个坐标,重新理解一个向量

看到一个向量的坐标 ,大家现在应该能够想到:从原点出发,向右移动 个单位,再向下移动 个单位。那么,这两个数字还能怎样理解?

这一篇,我们把坐标看成缩放两个基础向量的系数 。沿着这个想法继续往下,就能把线性组合、张成的空间、线性相关与无关,以及基联系起来。

前面在《向量究竟是什么》中,我们已经讲解了向量加法和数乘。接下来用到的操作,仍然是这两种,只是我们开始关心:改变数乘的系数之后,到底能够得到哪些向量?

文中的具体计算、Python 示例与配图为配套补充;本篇的向量分量和组合系数都取实数。

坐标,也是在告诉我们怎样缩放基向量

先看最熟悉的两个方向

我们先回到二维直角坐标系。这里有两个特别方便的向量:一个向右,长度为 ;另一个向上,长度也为 。

把它们分别记为:

它们合起来构成二维空间的标准基 。课程中也会使用 、​ 来表示这两个方向的单位向量。

现在,我们重新看向量 。数字 可以看成一个标量,把 伸长到原来的三倍;数字 则把 ​ 反向,并伸长到原来的两倍。

最后,把这两段位移相加:

这时候,坐标就不只是"横着走多少、竖着走多少",还可以理解成:第一个数缩放第一个基向量,第二个数缩放第二个基向量,然后把结果相加。

我们先把这个过程写成代码。新建 span_basis_demo.py,如果还没有安装依赖,先执行:

python 复制代码
python3 -m pip install numpy

导入 NumPy,再创建标准基向量:

python 复制代码
import numpy as np

e1 = np.array([1.0, 0.0])
e2 = np.array([0.0, 1.0])
p = 3 * e1 - 2 * e2

print("水平方向的位移:", 3 * e1)
print("竖直方向的位移:", -2 * e2)
print("相加得到的向量:", p)

运行代码,输出结果如下:

python 复制代码
水平方向的位移: [3. 0.]
竖直方向的位移: [-0. -2.]
相加得到的向量: [ 3. -2.]

这里的 -0. 是浮点数运算中可能出现的负零,数值上仍然等于零,不表示额外的位移。

到这里,前面学习的坐标表示、向量数乘和向量加法,就组合成了同一个过程。

换一组基向量,坐标还一样吗?

接下来,我们不再只使用水平和竖直的两个单位向量,而是选择:

这两个向量同样可以作为二维空间的一组基。为什么它们符合要求,后面讲完张成和线性无关之后,我们再回来解释。

现在先观察,同一个 也可以写成:

图中左右两边使用相同的画图尺度,蓝色箭头的终点都在 。变化的是我们用来组合它的两个基础向量,以及相应的系数。

按照顺序,把新基记为 ,那么:

这里的 ​ 表示"向量 在基 下的坐标"。两个坐标列不同,但它们在各自的基下,描述的是同一个几何向量。

坐标依赖于选用的基,也依赖于基向量的排列顺序。 如果交换 ​ 和 ​,对应系数的顺序也需要交换。

我们继续在前面的程序中添加:

python 复制代码
b1 = np.array([1.0, 1.0])
b2 = np.array([1.0, -1.0])
a, b = 0.5, 2.5
combined = a * b1 + b * b2

print("第一项:", a * b1)
print("第二项:", b * b2)
print("组合结果:", combined)
print("是否等于原向量:", np.allclose(combined, p))

输出结果:

python 复制代码
第一项: [0.5 0.5]
第二项: [ 2.5 -2.5]
组合结果: [ 3. -2.]
是否等于原向量: True

这里 b1、b2 仍然存储它们在标准坐标系中的分量。代码先分别完成数乘,再把两个数组对应位置相加,所以 combined 得到的也是标准坐标。

np.allclose() 用来检查两个数值数组是否在给定容差内接近。当前系数恰好能够精确表示,后面遇到更一般的浮点数计算时,也适合用这种方式核对重建结果。

线性组合:分别缩放,然后相加

先把前面的操作写成一般形式

前面我们已经做了两次相同类型的事情:选择几个标量,分别缩放给定向量,再把结果相加。这个过程,就叫作这些向量的线性组合。

对于两个向量,写成:

这里 和 称为组合系数。它们可以是正数、负数,也可以是零,并不要求加起来等于 。

如果有更多向量,写法也一样:

大家先注意这个顺序:给定向量保持不变,改变的是它们前面的系数。 每选定一组系数,就得到一个具体的线性组合结果。

固定一个系数,会看到什么?

我们暂时固定 ,只改变 。这时候得到:

随着 在所有实数中变化,终点沿着一条与 ​ 平行的直线移动。在这个例子中,这条直线满足 。

图中只标出了几个终点作为示例,真实的系数可以连续变化,并不局限于整数。我们也可以运行下面的代码,先观察几个结果:

python 复制代码
for coefficient in [-1, 0, 1, 2]:
    endpoint = coefficient * b1 + b2
    print(f"a = {coefficient:2d},终点:", endpoint)

输出结果:

python 复制代码
a = -1,终点: [ 0. -2.]
a =  0,终点: [ 1. -1.]
a =  1,终点: [2. 0.]
a =  2,终点: [3. 1.]

这里还有一个区别需要提前说明:固定了一个系数得到的这条直线,不一定经过原点。 它只是全部线性组合中的一部分;后面所说的张成空间,需要让所有组合系数都自由变化。

张成的空间:所有可能结果组成的集合

从一个结果,变成一整组结果

如果每次只选择一组系数,我们得到的是一个向量。现在把所有可能的系数组合都考虑进去,就得到一个向量集合,也就是给定向量张成的空间 ,英文叫作 span。

对于前面的两个向量,定义为:

公式中的竖线可以读作"其中"。整句话就是:所有能够写成 ​,且系数 都是实数的向量,组成的集合。

更一般地,张成空间包含给定向量的所有线性组合。这个定义也可以对照教材 Interactive Linear Algebra:Vector Equations and Spans 阅读。

所以,当我们问两个向量张成什么空间,其实是在问:只使用这两个向量,允许反复数乘和相加,最终能够到达哪些位置?

二维平面里,有三种情况

先看前面的 ​ 和 。它们不共线,改变两个系数,就能够得到平面中的任意向量。

这件事可以直接计算。假设我们想得到任意一个 ,那么需要:

只要选择:

就一定能够到达目标。因此,它们张成整个 。这里 表示所有实数二维向量组成的空间。

接下来,如果我们把第二个向量换成 ​,会怎么样?

无论怎样选择 ,结果都只是 ​ 的倍数。两个向量的箭头共线,最终只能到达一条过原点的直线,无法离开这条线。

最后,如果给定的两个向量都是零向量,数乘和相加的结果也只能是零向量。

图中使用终点来代表从原点出发的向量,省去了大量重叠的箭头。左图的点只是有限采样,真实的张成空间包含点与点之间的所有位置,并向画面外无限延伸。

大家可以把这三种情况放在一起看:

给定的两个二维向量 张成的空间 空间的维数
不共线 整个二维平面 2
共线,且至少一个非零 一条过原点的直线 1
都是零向量 只有零向量 0

这里的维数,可以先理解为独立方向的个数,后面会用基给出正式解释。向量有几个分量、给定了几个向量、张成空间是几维,是三个不同的问题。

还可以检查一个始终成立的特点:把全部系数取为零,一定得到零向量。因此,张成空间一定包含原点。前面固定 得到的 ,就不能直接当成这两个向量的张成空间。

三维空间里,增加一个向量能走得更远吗?

两个不共线的向量,先确定一个平面

我们把前面的想法放到三维空间中。选择两个三维向量:

它们的任意线性组合可以写成:

从结果可以看到,第三个分量始终等于前两个分量之和。也就是说,所有结果都落在平面 上。

反过来,这个平面中的任意一点,都能通过取 、 得到。因此, 和 张成的空间,就是这个过原点的平面。

它们都是三维向量,但它们张成的空间只有二维。 三个坐标分量是在描述向量所处的环境,并不意味着这两个向量已经提供了三个独立方向。

第三个向量在平面内,空间不变

现在增加一个向量:

它已经能够由前两个向量组合出来。于是,加入它之后,任意线性组合都可以重新整理成:

右边依然只是 和 的线性组合,结果仍然在原来的平面中。

所以,增加了第三个向量,并没有增加新的可达位置。它对于张成这个平面来说,是可以去掉的。

第三个向量在平面外,才能补上新的方向

我们换成另一个向量:

​​

它不满足 ,所以不在前面的平面内。加入这个向量后:

现在,对于任意目标 ,只要取 、、,就能够得到它。

这一次,三个向量确实张成了整个 。

图中平面的边缘只是绘图范围,实际平面会无限延伸。右图中的几层平面,表示固定不同的 时,原平面沿 方向平移后的位置;让 连续取遍实数,才会扫过整个空间。

大家要注意,这里说的"新方向",是指不在已有向量张成的空间中 。只看箭头的朝向是否不同,还不够。前面的 ,, 两两都不是倍数关系,但第三个仍然没有扩大张成空间。

线性相关与线性无关:有没有可以去掉的向量?

线性相关,表示组合中存在冗余

前面的 可以写成 ,去掉它也不影响张成空间。我们就说,这一组向量线性相关。

对于有限个向量,线性相关意味着:至少有一个向量能够由其余向量线性组合出来。换句话说,至少可以移除一个向量,而不缩小它们张成的空间。

这里说的是"至少一个",不是任意一个都能去掉。比如 ​ 线性相关,但如果去掉 ,原本的整个平面就会缩小为一条直线。

我们再用二维例子看一个更直接的影响。令:

那么,同一个向量 ,可以有两种写法:

这说明,把 都留下来,虽然仍然能张成平面,但表示同一个向量时,系数不再唯一。它们适合用来举例说明冗余,不能直接作为二维空间的一组基。

我们继续运行下面的代码:

python 复制代码
r = b1 + b2
first = 1 * b1 + 1 * b2 + 0 * r
second = 0 * b1 + 0 * b2 + 1 * r

print("系数为 (1, 1, 0):", first)
print("系数为 (0, 0, 1):", second)
print("两种写法是否相同:", np.allclose(first, second))

输出结果:

python 复制代码
系数为 (1, 1, 0): [2. 0.]
系数为 (0, 0, 1): [2. 0.]
两种写法是否相同: True
线性无关,表示每个向量都不可缺少

如果没有任何一个向量可以由其余向量线性组合出来,这组向量就称为线性无关。去掉其中任意一个,张成空间都会缩小。

把这个几何理解写成正式条件,就是考察:

如果只有 才能成立,这组向量线性无关。如果存在不全为零的系数,使结果等于零向量,就线性相关。

比如 ,系数为 ,这就给出了前面那组向量线性相关的明确证据。

这里"不全为零"的意思是至少一个系数非零,不要求每个系数都非零。任何包含零向量的向量组,也一定线性相关:给零向量一个非零系数,其余系数取零,就满足了条件。

几何上的"存在冗余",与公式中的"存在非零系数关系",描述的是同一件事。正式定义和等价条件可以参考 Interactive Linear Algebra:Linear Independence。

使用代码,核对张成空间的维数

前面我们通过公式,已经说明了各组向量能张成什么空间。接下来,再用程序核对这些结果。

这里先借用一个后面会展开的概念:矩阵的秩。把给定向量分别放到矩阵的各列中,矩阵的秩就等于这些列向量张成空间的维数。

python 复制代码
u = np.array([1.0, 0.0, 1.0])
v = np.array([0.0, 1.0, 1.0])
w = u + v
q = np.array([0.0, 0.0, 1.0])

groups = {
    "二维:不共线": np.column_stack((b1, b2)),
    "二维:共线": np.column_stack((b1, 2 * b1)),
    "二维:全为零": np.zeros((2, 2)),
    "三维:只有 u、v": np.column_stack((u, v)),
    "三维:加入平面内的 w": np.column_stack((u, v, w)),
    "三维:加入平面外的 q": np.column_stack((u, v, q)),
}

for name, matrix in groups.items():
    print(f"{name},张成空间的维数:{np.linalg.matrix_rank(matrix)}")

输出结果:

python 复制代码
二维:不共线,张成空间的维数:2
二维:共线,张成空间的维数:1
二维:全为零,张成空间的维数:0
三维:只有 u、v,张成空间的维数:2
三维:加入平面内的 w,张成空间的维数:2
三维:加入平面外的 q,张成空间的维数:3

np.column_stack() 把这些一维数组按列拼在一起。比如三个三维向量会得到一个形状为 (3, 3) 的矩阵;两个三维向量则得到 (3, 2)。

从结果可以看到,加入 后,向量个数变成了三个,秩仍然是 ;换成平面外的 ,秩才增加到 。

如果秩等于列数,说明这些列向量线性无关;如果小于列数,就存在冗余。秩与环境空间维数相等时,这些列向量才能张成整个环境空间。比如两个三维向量可以线性无关,但仍然无法张成整个三维空间。

对于浮点数组,matrix_rank() 通过奇异值与容差判断数值秩。接近线性相关的实际测量数据,结果可能受误差和容差影响,具体说明见 NumPy 官方文档。当前例子的关系已经在前面用公式明确给出,代码用来核对这些关系,不需要现在就掌握奇异值分解。

基:既能覆盖整个空间,又没有冗余

现在回到基的定义

前面我们先使用了"基"这个词,现在可以把正式定义补齐了:一个向量空间的一组基,是这个空间中一组线性无关、并且能够张成该空间的向量。 教材中的定义可以对照 Basis and Dimension 阅读。

这个定义包含两个条件:

条件 它解决什么问题?
张成整个目标空间 空间中的每个向量,都能够表示出来
线性无关 表示同一个向量时,组合系数唯一

如果只有第一个条件,就可能像前面的 一样,虽然覆盖了平面,却保留了冗余。如果只有第二个条件,也不一定够,例如单独一个非零向量线性无关,但只能张成一条直线。

所以,判断一组向量是不是基,要先明确:我们准备描述的,到底是哪一个空间?

前面的 是平面 的一组基,因为它们能够覆盖这个平面,而且彼此独立。但它们不是整个 的基。它们虽然都有三个分量,描述这个平面中的向量时,却只需要两个组合系数。

一组基 中向量的个数,就称为该空间的维数。同一个有限维空间可以有不同的基,但每组基包含的向量个数相同。因此,平面的基包含两个向量,整个三维空间的基包含三个向量。

为什么坐标能够唯一确定?

我们用前面的二维基说明。假设同一个向量能用两组系数表示:

把两种表示相减,得到:

因为 线性无关,只有两个系数都为零才成立。因此 、,两组系数其实完全相同。

张成保证能表示,线性无关保证只有一种表示。 给基向量固定顺序后,我们才得到一套既完整又明确的坐标系统。

基向量也不要求一定是单位长度,或者一定两两垂直。正交单位基计算起来很方便,但这并不是基的定义要求。例如 ​ 和 并不垂直,仍然可以构成二维空间的一组基。

已知向量,怎样求它在新基下的坐标?

前面我们直接给出了 和 两个系数。现在,我们反过来,让程序求出这组坐标。

按照顺序把两个基向量放在矩阵的列中:

如果新坐标记为 ,那么我们要解决的就是:

这里的矩阵乘法,正是在把矩阵各列按照 缩放后相加。暂时先把它和线性组合联系起来,下一篇再详细讲矩阵与空间变换。

继续在程序中添加:

python 复制代码
B = np.column_stack((b1, b2))
coordinates = np.linalg.solve(B, p)
rebuilt = B @ coordinates

print("基向量按列组成的矩阵:")
print(B)
print("在新基下的坐标:", coordinates)
print("重建后的标准坐标:", rebuilt)
print("是否重建成功:", np.allclose(rebuilt, p))

输出结果:

python 复制代码
基向量按列组成的矩阵:
[[ 1.  1.]
 [ 1. -1.]]
在新基下的坐标: [0.5 2.5]
重建后的标准坐标: [ 3. -2.]
是否重建成功: True

这里 coordinates 存储的是新基下的组合系数;B @ coordinates 才是把这些系数与基向量组合起来,重新得到标准坐标。两种数组表示的意义不同,需要根据使用的基进行区分。

按照 NumPy 官方 solve 文档,这个接口用于求解方阵系数矩阵的线性方程组,矩阵需要非奇异。当前 的两列构成二维空间的一组基,符合要求。如果两列共线,就不能继续把它当成一组基,也不能指望 solve() 给出唯一坐标。

先检查能否张成,再检查有没有冗余

到这里,我们再遇到一组向量,就可以按这样的顺序去理解:先看它们的所有线性组合能到达哪里,再看有没有向量可以去掉。能够张成目标空间,又没有冗余,才是这个空间的一组基。

大家可以先做三个练习,再运行程序核对:

  1. 仍然使用 、。向量 在这组有序基下的坐标是多少?
  2. 对于前面的 ,判断 与 ,哪一个在它们张成的空间里?
  3. 能否构成二维空间的一组基?去掉哪个向量后,剩下的两个仍然是一组基?

第一题的系数是 和 ,也就是 。第二题可以检查 :前者在平面内,后者不在。第三题的三个向量线性相关,不能一起作为基;例如去掉 ,保留 即可。

安装 NumPy 后,执行:

复制代码
python3 examples/span_basis_demo.py

看结果时,试着把每组数字重新读成一句话:它们在缩放哪些向量?所有系数变化之后,能够覆盖哪一个空间?如果换一组基,同一个几何向量的坐标会怎样改变?

下一篇,我们继续学习线性变换与矩阵,看看这些基向量发生变化时,怎样决定整个空间的变化。

参考资料与配套内容

相关推荐
这是谁的博客?1 小时前
AI 芯片存算一体(PIM/CIM)架构深度解析:从冯·诺依曼瓶颈与近存计算到 RRAM 模拟矩阵乘的 LLM 推理显存墙突破原理
ai·pim·1024程序员节·cim·存算一体·hbm-pim·lpddr5x-pim
WebKoalaBoy1 小时前
一台电脑同时配置多个 Git 账号:从原理到实战
1024程序员节
啊哈一半醒1 小时前
Redis 三大缓存问题:缓存穿透、缓存击穿、缓存雪崩
运维·redis·1024程序员节
啵啵啵鱼1 小时前
用 Claude Code 重构遗留系统:从评估到落地的完整实践
数据结构·1024程序员节
I Am a robert girl1 小时前
当扩散编辑器“看不见”参考图:从 RefGAP 拆解参考注意力分配问题
计算机视觉·注意力机制·扩散模型·1024程序员节·参考引导编辑·refgap·免训练方法
jinbao_x1 小时前
关于git提交代码时的LF、CRLF提示的问题
1024程序员节
troy1281 小时前
构建金融系统 Reference Data Asset 的完整指南
1024程序员节
鲁Q同志1 小时前
IDEA无限重置激活三十天
intellij-idea·1024程序员节
紧跟先前的步伐1 小时前
Go AI Agent 实战 14|多轮对话与上下文历史管理
人工智能·ai·golang·1024程序员节