线性代数(二)线性组合与向量空间
Chen Kai BOSS

想象你有一盒只有红、绿、蓝三支的彩色铅笔,你能画出多少种颜色?答案是:无穷多种。通过混合不同比例的 RGB,从深紫到浅黄,任何颜色都能被创造出来。这就是线性组合的威力——用有限的"原料"构建无限的可能。本章将揭示这个神奇的数学机制,以及它如何支撑起整个线性代数的大厦。

从调色说起:什么是线性组合?

在第 1 章中,我们理解了向量是带有方向和大小的量。现在,让我们思考一个更有趣的问题:如果给你几个向量,你能用它们"到达"空间中的哪些位置?

生活中的线性组合

在回答这个抽象问题之前,先看几个熟悉的场景:

场景一:调鸡尾酒

假设你是调酒师,有两种基酒:

  • 基酒 A: 40 度酒精,每升含 10 克糖
  • 基酒 B: 20 度酒精,每升含 30 克糖

你想调出一杯 30 度酒精、 20 克糖的鸡尾酒。怎么办?

如果用 升 A 和 升 B,那么:

  • 酒精度:
  • 含糖量:

解这个方程组,得——半升 A 加半升 B 。

这里, 就是向量线性组合,系数分别是

场景二:走路导航

你站在路口,朋友告诉你:"往东走 300 米,再往北走 400 米。"

用向量表示: -:向东的单位向量 -:向北的单位向量

你的位移是: 这也是一个线性组合!系数是 300 和 400 。

场景三: RGB 颜色

计算机显示器上的每个像素,都是红(R)、绿(G)、蓝(B)三种光的混合:绿

其中。比如:

- = 纯红 - = 黄色(红+绿) - = 紫色

每种颜色都是红、绿、蓝三个"基础向量"的线性组合。

线性组合的数学定义

现在我们可以给出严格的定义了:

定义(线性组合):给定向量 和标量,它们的线性组合是:

这里 称为系数权重

关键理解

  • 线性组合只涉及两种运算:标量乘法向量加法
  • 系数 可以是任意实数(正、负、零都行)
  • "线性"这个词意味着没有平方、立方、乘积等非线性项

为什么叫"线性"?

考虑二维平面中的一个向量

它的所有标量倍数 构成什么?

变到 时:

-:原点 - - -

所有这些点连起来,构成一条过原点的直线

这就是"线性"的几何来源:单个向量的标量倍数形成一条线

二维空间中的线性组合

现在考虑两个不平行的向量

它们的线性组合 能到达哪里?

取遍所有实数时, 覆盖了整个二维平面

让我们验证几个点: - - -

结论:两个不平行的向量的线性组合可以覆盖整个二维平面。

但如果两个向量平行呢?比如

注意到,它们的线性组合:

无论 怎么取,结果都是 的标量倍数——只能覆盖一条直线!

这引出了一个关键问题:给定一组向量,它们能"到达"的所有位置是什么?

张成空间( Span):向量能到达的所有地方

Span 的定义

定义(Span/张成空间):向量集合span是它们所有可能的线性组合的集合:

直觉: - Span 是你用这些向量能"到达"的所有位置 - 想象你有一个"向量遥控器",可以调节每个向量的系数 - 调来调去,你能到达的所有位置,就是 span

不同情况的 Span

让我们系统地看看不同向量组合的 span:

情况一:单个非零向量:一条过原点的直线( 的方向)

例如: 这是斜率为 2 的过原点直线。

情况二:两个共线向量(平行) 其中:还是一条直线

虽然有两个向量,但第二个提供不了新的"方向",所以 span 不会变大。

情况三:两个不共线的二维向量 其中 不平行:整个 平面

例如: 情况四:两个三维向量 中:一个过原点的平面

例如: 平面( 的所有点)

情况五:三个共面的三维向量

仍然只是一个平面。第三个向量如果可以用前两个表示,就不会增加 span 。

情况六:三个不共面的三维向量:整个三维空间

重要观察: Span 的形状

从上面的例子可以看出: - Span 总是过原点的(因为所有系数取 0 时得到零向量) - Span 是封闭的( span 内两个点的线性组合还在 span 内) - Span 的"大小"取决于向量之间的"独立程度"

这些性质使得 span 成为一种特殊的几何对象——子空间(后面详述)。

Span 的实际意义

例 1:能否用现有材料配出目标?

化学实验室里有三种溶液: - 溶液 A:含 5%酸、 10%盐 - 溶液 B:含 10%酸、 5%盐 - 溶液 C:含 2%酸、 2%盐

问:能否配出 15%酸、 12%盐的混合液?

把每种溶液看作向量: 问题变成: 是否在 内?

注意(验证:),实际上 不平行,所以

因此 一定在 span 内,可以配出来!

例 2:图形学中的坐标系

在 3D 游戏中,每个物体有自己的局部坐标系,由三个向量 定义。

物体表面的任何一点,都可以表示为这三个向量的线性组合。

如果,说明这个局部坐标系是"完整的",能描述三维空间中的任何位置。

线性独立:没有冗余的向量组

从前面的讨论中,我们注意到一个现象:有时候增加向量并不会增加 span 。比如,当新向量可以被现有向量"表示"时。

这引出了线性代数中最重要的概念之一:线性独立

从冗余说起

考虑三个向量: - - - 这三个向量的 span 是什么?

注意到,所以:

所以 冗余的——移除它不影响 span 。

线性独立的定义

定义(线性独立):向量集合线性独立的,当且仅当:

只有 时才成立。

等价地说:如果存在不全为零的系数使得线性组合等于零向量,则向量组线性相关

直觉理解: - 线性独立 = 没有向量是"多余的" - 线性独立 = 每个向量都提供了新的"方向" - 线性独立 = 不能用其他向量"凑出"任何一个向量

几何理解

二维情况: - 两个向量线性独立 它们不平行 - 两个向量线性相关 它们共线

三维情况: - 三个向量线性独立 它们不共面 - 三个向量线性相关 它们共面

判断线性独立的方法

方法一:定义法

,解这个齐次方程组。 - 如果只有零解:线性独立 - 如果有非零解:线性相关

例子:判断 是否线性独立。

得方程组:

解得:(验证:

存在非零解,所以这三个向量线性相关

实际上,,所以

方法二:行列式法(仅适用于 维向量)

维向量作为列(或行)组成矩阵,计算行列式: - 行列式:线性独立 - 行列式:线性相关

例如上面的例子:

所以线性相关。(行列式的计算方法将在第 4 章详述)

线性独立的重要性质

性质 1:如果线性独立,则它的任何子集也线性独立。

反过来:如果某个子集线性相关,则整体也线性相关。

性质 2:在 中,超过 个向量一定线性相关。

直觉: 维空间最多只有 个"独立方向"。

性质 3:如果线性独立且,则 的表示方式唯一

这个性质极其重要——它保证了坐标的唯一性

线性相关的等价条件

以下条件等价(任选一个来判断):

1.线性相关 2. 存在不全为零的 使得 3. 某个向量可以写成其他向量的线性组合 4. 移除某个向量不改变 span 5. (对 维向量)组成的矩阵行列式为 0

基( Basis):最小的完整集合

现在我们可以定义线性代数中最核心的概念之一:

基的定义

定义(基):向量空间 的一组是满足以下两个条件的向量集合: 1. 线性独立:没有冗余 2. 张成 直觉: - 基是"最小的完整工具箱" - "完整"指能表示空间中任何向量 - "最小"指没有多余的向量

标准基

标准基 个向量组成:

每个 在第 个位置是 1,其余位置是 0 。

例如 的标准基:

标准基的特点: - 相互垂直(正交) - 长度都是 1(单位向量) - 坐标 就是在标准基下的系数:

非标准基

基并不唯一! 的以下向量组都是基:

例 1 验证: - 线性独立?设,得,所以 - 张成?两个不平行的二维向量张成整个

例 2 这是标准基的"拉伸版"——沿 轴拉伸 2 倍,沿 轴拉伸 3 倍。

例 3 这是一个"斜的"基,但仍然有效。

坐标:同一向量在不同基下的表示

向量 在不同基下的坐标是什么?

标准基,坐标是 : 设,解得 坐标是 关键理解: - 同一个向量(空间中的同一个箭头)在不同基下有不同的坐标 - 坐标只有在指定基之后才有意义 - 我们平时写的隐含了标准基

基的存在性与唯一性

存在性定理:每个非零向量空间都有基。

唯一性:基不唯一,但基中的向量个数是唯一的(这就是维度)。

如何找基?

方法:从空间中任选向量,逐个添加,每次检查是否还线性独立。当无法再添加时(任何新向量都会导致线性相关),就得到了一组基。

维度:空间的"自由度"

维度的定义

定义(维度):向量空间维度 的任意一组基所含的向量个数。 记作

为什么定义合理? 有一个重要定理保证:同一个向量空间的所有基,向量个数相同。

维度的直觉

维度可以理解为: - 描述空间中一个点需要多少个独立参数 - 空间中有多少个独立的移动方向 - 能容纳的最大线性独立向量数

常见空间的维度

空间 维度 说明
一个点(零向量空间) 0 没有移动自由度
一条直线 1 只能前后移动
一个平面 2 前后+左右
三维空间 3 前后+左右+上下
个独立方向

维度与线性独立的关系

关键定理:在 维空间中: - 超过 个向量一定线性相关 - 恰好 个线性独立的向量构成基 - 少于 个向量不可能张成整个空间

例子:在 中: - 4 个向量一定线性相关(可能张成,但有冗余) - 3 个线性独立的向量是基 - 2 个向量最多张成一个平面

子空间:空间中的空间

子空间的定义

定义(子空间):向量空间子空间 的子集,满足: 1. 包含零向量:> 2. 对加法封闭:若,则> 3. 对标量乘法封闭:若,则 对任意标量 直觉:子空间是"空间中的空间"——它本身也是一个向量空间,但"住在"更大的空间里。

子空间的例子

的子空间

  1. 零空间:只包含零向量,维度为 0
  2. 过原点的直线:如,维度为 1
  3. 过原点的平面:如 平面),维度为 2
  4. 本身:维度为 3

注意:不过原点的直线或平面不是子空间!

例如, 的平面)不是子空间,因为: - 不包含零向量 -,不封闭

Span 作为子空间

重要事实:任何向量组的 span 都是子空间。 满足: 1. 2. 两个线性组合相加还是线性组合 3. 线性组合乘以标量还是线性组合

这给了我们构造子空间的简单方法:取一些向量,求它们的 span 。

子空间的交与和

:两个子空间的交集还是子空间。

例如: 平面和 平面的交是 轴。

例如: 轴和 轴的和是 平面。

维度公式

实战案例: RGB 颜色空间

让我们深入探讨 RGB 颜色空间这个实际应用。

RGB 模型的数学结构

在 RGB 颜色模型中: - 每种颜色表示为三维向量 -( 8 位颜色深度)或(归一化)

三个基础颜色向量:

任何颜色都是它们的线性组合:

颜色混合的线性性

加法混合(光的混合,如显示器):

例如:红 加绿 等于黄 亮度调节 变暗, 变亮。

颜色空间作为向量空间

严格来说,如果限制, RGB 颜色空间不是向量空间(对加法和标量乘法不封闭)。

但如果我们允许(理论上的扩展),就得到了 向量空间。

实际应用:图像处理中,中间计算常用浮点数(不限制范围),最后才裁剪到

颜色空间的子空间

灰度图像 的颜色

这是 的 1 维子空间(过原点的直线)。

红绿色盲看到的颜色

某些类型的色盲只能区分蓝色和黄色(红+绿),相当于把 投影到 2 维子空间。

颜色空间变换

不同的颜色空间( RGB 、 HSV 、 LAB 等)对应不同的基选择。

从 RGB 到另一个颜色空间,就是基变换——这涉及到矩阵乘法(下一章内容)。

Python 实现

让我们用代码验证本章的概念。

判断线性独立

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import numpy as np

def is_linearly_independent(vectors):
"""
判断向量组是否线性独立
vectors: 向量列表,每个向量是 numpy 数组
"""
if len(vectors) == 0:
return True

# 将向量作为列组成矩阵
matrix = np.column_stack(vectors)

# 计算秩
rank = np.linalg.matrix_rank(matrix)

# 如果秩等于向量个数,则线性独立
return rank == len(vectors)

# 测试
v1 = np.array([1, 2, 3])
v2 = np.array([4, 5, 6])
v3 = np.array([7, 8, 9])

print(f"v1, v2, v3 线性独立: {is_linearly_independent([v1, v2, v3])}") # False

v4 = np.array([1, 0, 0])
v5 = np.array([0, 1, 0])
v6 = np.array([0, 0, 1])

print(f"v4, v5, v6 线性独立: {is_linearly_independent([v4, v5, v6])}") # True

检查向量是否在 Span 内

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
def is_in_span(target, basis_vectors):
"""
检查 target 向量是否在 basis_vectors 的 span 内
"""
matrix = np.column_stack(basis_vectors)

# 尝试解线性方程组 matrix @ coeffs = target
try:
coeffs, residuals, rank, s = np.linalg.lstsq(matrix, target, rcond=None)
# 检查是否真的是解(残差足够小)
reconstructed = matrix @ coeffs
return np.allclose(reconstructed, target)
except:
return False

# 测试
v1 = np.array([1, 0])
v2 = np.array([0, 1])
target = np.array([3, 5])

print(f"(3,5) 在 span(v1, v2) 内: {is_in_span(target, [v1, v2])}") # True

v3 = np.array([1, 1])
v4 = np.array([2, 2]) # 与 v3 共线
target2 = np.array([1, 0])

print(f"(1,0) 在 span(v3, v4) 内: {is_in_span(target2, [v3, v4])}") # False

可视化 Span

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

def visualize_span_2d(v1, v2, num_points=50):
"""可视化两个二维向量的 span"""
fig, ax = plt.subplots(figsize=(8, 8))

# 生成网格
c1 = np.linspace(-2, 2, num_points)
c2 = np.linspace(-2, 2, num_points)

for i in c1:
for j in c2:
point = i * v1 + j * v2
ax.plot(point[0], point[1], 'b.', alpha=0.3, markersize=2)

# 画原始向量
ax.quiver(0, 0, v1[0], v1[1], angles='xy', scale_units='xy', scale=1, color='r', width=0.02)
ax.quiver(0, 0, v2[0], v2[1], angles='xy', scale_units='xy', scale=1, color='g', width=0.02)

ax.set_xlim(-5, 5)
ax.set_ylim(-5, 5)
ax.set_aspect('equal')
ax.grid(True)
ax.set_title('Span of two vectors')
plt.show()

# 示例
v1 = np.array([1, 0.5])
v2 = np.array([0.3, 1])
visualize_span_2d(v1, v2)

常见误区与澄清

误区 1:"向量 可以张成二维平面"

错误!,它们共线,只能张成一条直线。

误区 2:"三个向量一定比两个向量张成更大的空间"

不一定!如果第三个向量在前两个的 span 内,空间不会变大。

误区 3:"线性独立的向量一定正交(垂直)"

错误! 线性独立但不正交。正交是比线性独立更强的条件。

误区 4:"基是唯一的"

错误!同一个空间可以有无穷多组基。但维度(基的大小)是唯一的。

误区 5:"子空间可以是任意子集"

错误!子空间必须满足三个条件(含零、加法封闭、标量乘法封闭)。

练习题

基础题

1. 判断以下向量组是否线性独立:

(a) (b) (c) (d) 2. 求以下向量组的 span 的维度:

(a)

(b)

(c)

3. 能否表示为 的线性组合?如果能,求系数。

4. 的维度是多少?需要多少个向量构成基?

5. 判断以下集合是否是 的子空间:

(a) (b) (c) (d)

进阶题

6. 证明:如果线性独立,则也线性独立。

7. 给定,找一个 使得 的基。

8. 证明: 中任意 个向量一定线性相关。

9.。求

10. 证明:如果线性独立,,则 可以唯一地表示为 的线性组合。

思考题

11. RGB 颜色空间是 3 维的。能否只用 2 种颜色的混合表示所有颜色?为什么?

12. 考虑所有 实矩阵的集合,定义矩阵的加法和标量乘法。这是向量空间吗?如果是,维度是多少?给出一组基。

13. 为什么过原点的直线是子空间,而不过原点的直线不是?从定义的三个条件分析。

14. 中, 100 个随机向量几乎总是线性独立的,而 101 个向量一定线性相关。为什么?

编程题

15. 用 Python 实现一个函数,判断给定向量组是否线性独立。用行列式或秩的方法。

16. 实现一个函数,给定一组向量,找出其中一个极大线性独立子集(即构成 span 的基)。

17. 写一个程序,可视化 3D 空间中两个向量张成的平面。

18. 实现一个交互程序:用户输入两个 2D 向量和系数,程序显示线性组合的结果,并可视化"在 span 中移动"的效果。

本章小结

本章我们建立了线性代数的核心概念框架:

概念 定义 直觉
线性组合 向量的加权求和
Span 所有可能的线性组合 能到达的所有位置
线性独立 唯一零解 没有冗余
独立+张成 最小完整集合
维度 基的大小 自由度
子空间 含零+封闭 空间中的空间

这些概念将贯穿整个线性代数:

  • 第 3 章:矩阵的列向量的 span 是列空间
  • 第 4 章:行列式判断是否线性独立
  • 第 5 章:线性方程组的解空间是子空间
  • 第 6 章:特征向量形成特殊的基
  • 第 7 章:正交基简化计算
  • 第 9 章: SVD 给出"最优"的基

预告:下一章

《矩阵作为线性变换》

矩阵不只是数字表格——它是变换的代理人!

我们将探索: - 矩阵乘以向量的几何意义 - 旋转、缩放、剪切、投影的矩阵表示 - 矩阵乘法 = 变换的复合 - 行列式 = 变换对面积/体积的影响

准备好改变你对矩阵的看法!

练习题详细答案

基础题答案

题 1:判断线性独立性

(a)

检查:,所以

答案线性相关(第二个是第一个的倍数)

(b)

只有 时成立

答案线性独立(标准基)

(c)

矩阵法:

答案线性独立

(d)

观察:

答案线性相关


题 2:求 span 的维度

(a) 中:维度 = 1 轴)

(b) 中:维度 = 1(共线)

(c) 中:维度 = 2(平面)


题 3 表示为 的线性组合

答案表示,


题 4 的维度和基

答案:维度 = 5,需要 5 个线性独立向量


题 5:判断子空间

(a)(直线(b)(不含零向量) (c)(坐标轴并集,不满足加法封闭) (d) 轴)


进阶题答案

题 6:证明子集的线性独立性

证明:采用反证法。

假设 线性相关,则存在不全为零的 使得:

,得:

这里 不全为零(因为 不全为零),这与 线性独立矛盾。

因此 必定线性独立。证毕

几何直觉:如果三个向量独立(不共面),其中任意两个必定不共线。


题 7:补全为 R³ 的基

给定:

方法一:行列式法

使得:

展开行列式:

只要,即 即可。

一个简单选择

验证:

答案(或任何满足 的向量)

其他可能的答案: -:验证 -:验证 -:验证 (不可以)


题 8:证明 n+1 个向量必线性相关

证明

中,考虑任意 个向量

构造矩阵,这是 矩阵

关键观察 是欠定方程组 - 未知数个数: - 方程个数: - 未知数 > 方程数

由秩-零化度定理(Rank-Nullity Theorem):

由于,因此:

结论:零空间至少是 1 维的,存在非零解 使得:

因此向量组线性相关。证毕

直觉 维空间最多只有 个独立方向,第 个必然是前面的线性组合,成为"多余"的。


题 9:求子空间的交与和

给定: - 平面, -:由 轴张成

(a) 交集 中的向量: 中的向量:

交集条件:

答案

这是一条过原点、方向为 的直线,维度为 1

(b) 和 由所有形如 的向量组成:

可以选择任意,因此能得到任意: - 令 - 令 - 令

答案(整个 3D 空间),维度为 3

验证维度公式


题 10:证明表示的唯一性

证明

已知: 线性独立,

假设 有两种表示方式:

两式相减:

由于 线性独立,只有零线性组合才能等于零向量:

因此: 对所有 成立

结论:表示方式唯一。证毕

几何意义:基就像坐标系,每个点只有唯一的坐标。这保证了向量空间中的"定位"是明确的。


思考题答案

题 11:能否只用 2 种颜色表示所有颜色?

答案不能

理由

  1. 维度论证
    • RGB 颜色空间是 3 维
    • 两种颜色最多张成 2 维子空间(平面)
    • 2 维 < 3 维,无法覆盖整个空间
  2. 具体例子
    • 只用红和绿:
    • 这只能产生黄-橙-红-绿系列的颜色
    • 无法产生蓝色及其相关颜色(如青色、紫色、蓝色)
  3. 数学证明
    • 纯蓝色 不在
    • 因为 对任意

实际应用:这就是为什么: - 显示器需要 RGB 三种光源 - 打印机需要 CMYK 四种墨水(青、品红、黄、黑) - 人眼有 三种视锥细胞(感知 RGB)


题 12 矩阵作为向量空间

答案是向量空间

验证向量空间公理

  1. 加法封闭:两个 矩阵相加仍是 矩阵
  2. 标量乘法封闭:标量乘矩阵仍是 矩阵
  3. 零元素存在:零矩阵
  4. 加法交换律、结合律等:继承自实数的性质

维度4

一组基(标准基):

验证:任何 矩阵可唯一表示为:

推广: - 矩阵空间的维度是 - 矩阵空间的维度是 9 - 对称 矩阵空间的维度是


题 13:为何不过原点的直线不是子空间?

分析:设直线(不过原点)

验证子空间三条件

条件 1:包含零向量 是否在 上?代入:矛盾!

已经不满足第一条,但我们继续验证其他条件:

条件 2:加法封闭

(验证: ) 取(验证:

相加:

检查 是否在 上:矛盾!

条件 3:数乘封闭

数乘:

检查 是否在 上:矛盾!

结论:不过原点的直线三个条件全都不满足

几何直觉: - 过原点的直线:沿着直线拉伸缩放,仍在直线上 - 不过原点的直线:缩放后会"平移",离开原直线

子空间必须"以原点为中心",像射线一样从原点辐射。平移后的集合失去了这种对称性。

对比: - 过原点的直线: → 是子空间 - 不过原点的直线: → 不是子空间


题 14:100 维空间中的线性独立性

答案与分析

命题 1:100 个随机向量几乎总是线性独立

数学原因

中,100 个向量 线性相关 ⟺

其中 矩阵

概率论视角: - 行列式 是矩阵元素的多项式函数 - 在连续概率分布(如正态分布、均匀分布)下 - 多项式恰好为 0 的概率是 0(测度论意义)

因此随机向量几乎必然线性独立(概率为 1)。

命题 2:101 个向量必然线性相关

数学原因:由题 8 的证明, 中任意 101 个向量一定线性相关。

直觉理解

类比: - 100 维空间像一个有 100 个自由度的房间 - 最多容纳 100 把"独立"的钥匙 - 第 101 把必然是某种组合锁(前 100 把的线性组合)

实际意义

在机器学习中: - 特征空间:如果有 100 维特征 - 训练样本:100 个样本"通常"是独立的(信息不重复) - 过采样:101 个样本必然存在线性依赖(存在冗余信息)

数值实验(Python):

1
2
3
4
5
6
7
8
9
import numpy as np

# 100 个随机向量(几乎必然独立)
A = np.random.randn(100, 100)
print(f"100 个向量的秩: {np.linalg.matrix_rank(A)}") # 几乎总是 100

# 101 个随机向量(必然相关)
B = np.random.randn(100, 101)
print(f"101 个向量的秩: {np.linalg.matrix_rank(B)}") # 最多是 100

理论深化

这个现象体现了维度的本质: - 维度 = 空间的"容量" - 超过维度的向量集合必然有冗余 - 这是线性代数最基本的定理之一


编程题答案

题 15:判断线性独立

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
import numpy as np

def is_linearly_independent(vectors):
"""
判断向量组是否线性独立

参数:
vectors: list of arrays or 2D array (每列是一个向量)

返回:
bool: True 表示线性独立,False 表示线性相关
"""
# 转换为矩阵(列向量)
if isinstance(vectors, list):
A = np.column_stack(vectors)
else:
A = vectors

n_vectors = A.shape[1]
rank = np.linalg.matrix_rank(A)

return rank == n_vectors

# ===== 测试用例 =====

# 测试 1:线性独立
v1 = np.array([1, 2, 3])
v2 = np.array([4, 5, 6])
print(f"测试 1: {is_linearly_independent([v1, v2])}") # True

# 测试 2:线性相关
v1 = np.array([1, 2, 3])
v2 = np.array([4, 5, 6])
v3 = np.array([5, 7, 9]) # v3 = v1 + v2
print(f"测试 2: {is_linearly_independent([v1, v2, v3])}") # False

# 测试 3:标准基
e1 = np.array([1, 0, 0])
e2 = np.array([0, 1, 0])
e3 = np.array([0, 0, 1])
print(f"测试 3: {is_linearly_independent([e1, e2, e3])}") # True

# 测试 4:共线向量
v1 = np.array([1, 2])
v2 = np.array([2, 4])
print(f"测试 4: {is_linearly_independent([v1, v2])}") # False


# ===== 方法 2:用行列式(仅适用于方阵)=====

def is_independent_det(vectors):
"""
用行列式判断线性独立性(仅适用于方阵)

参数:
vectors: list of arrays (n 个 n 维向量)

返回:
bool: True 表示线性独立
"""
A = np.column_stack(vectors)

if A.shape[0] != A.shape[1]:
raise ValueError("行列式法仅适用于方阵(n 个 n 维向量)")

det = np.linalg.det(A)
return np.abs(det) > 1e-10 # 数值容差

# 测试
print(f"\n行列式法测试:")
print(f"标准基: {is_independent_det([e1, e2, e3])}") # True
v1 = np.array([1, 2, 3])
v2 = np.array([2, 1, 0])
v3 = np.array([0, 0, 1])
print(f"自定义基: {is_independent_det([v1, v2, v3])}") # True


# ===== 方法 3:详细输出版 =====

def analyze_independence(vectors, names=None):
"""
详细分析向量组的线性独立性

参数:
vectors: list of arrays
names: list of str (向量名称)
"""
A = np.column_stack(vectors)
m, n = A.shape
rank = np.linalg.matrix_rank(A)

if names is None:
names = [f"v{i+1}" for i in range(n)]

print(f"\n向量组: {{{', '.join(names)}}}")
print(f"向量维度: {m}")
print(f"向量个数: {n}")
print(f"矩阵秩: {rank}")

if rank == n:
print("✓ 线性独立")
else:
print(f"✗ 线性相关(有 {n - rank} 个冗余向量)")

# 如果是方阵,计算行列式
if m == n:
det = np.linalg.det(A)
print(f"行列式: {det:.6f}")

return rank == n

# 测试
v1 = np.array([1, 2, 3])
v2 = np.array([4, 5, 6])
v3 = np.array([5, 7, 9])
analyze_independence([v1, v2, v3], ["v1", "v2", "v3"])

输出示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
测试 1: True
测试 2: False
测试 3: True
测试 4: False

行列式法测试:
标准基: True
自定义基: True

向量组: {v1, v2, v3}
向量维度: 3
向量个数: 3
矩阵秩: 2
✗ 线性相关(有 1 个冗余向量)
行列式: 0.000000

题 16:找极大线性独立子集

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
import numpy as np

def maximal_independent_subset(vectors):
"""
找出向量组的极大线性独立子集(构成 span 的基)

参数:
vectors: list of numpy arrays

返回:
indices: list of int (独立向量的索引)
basis: list of arrays (基向量)
"""
A = np.column_stack(vectors)
m, n = A.shape

independent_indices = []
current_matrix = np.zeros((m, 0))

for i in range(n):
# 尝试添加第 i 个向量
test_matrix = np.column_stack([current_matrix, A[:, i]])

# 检查是否仍然独立
new_rank = np.linalg.matrix_rank(test_matrix)
old_rank = len(independent_indices)

if new_rank > old_rank:
# 添加这个向量增加了秩,保留它
independent_indices.append(i)
current_matrix = test_matrix

basis = [vectors[i] for i in independent_indices]
return independent_indices, basis


def analyze_span(vectors, names=None):
"""
分析向量组的 span,找出基和维度
"""
if names is None:
names = [f"v{i+1}" for i in range(len(vectors))]

indices, basis = maximal_independent_subset(vectors)

print(f"\n原始向量组: {{{', '.join(names)}}}")
print(f"向量个数: {len(vectors)}")
print(f"\nSpan 的维度: {len(basis)}")
print(f"基的索引: {indices}")
print(f"基向量: {{{', '.join([names[i] for i in indices])}}}")

if len(basis) < len(vectors):
redundant = [i for i in range(len(vectors)) if i not in indices]
print(f"\n冗余向量索引: {redundant}")
print(f"冗余向量: {{{', '.join([names[i] for i in redundant])}}}")

return indices, basis


# ===== 测试用例 =====

# 测试 1:部分相关的向量组
vectors1 = [
np.array([1, 0, 0]),
np.array([0, 1, 0]),
np.array([1, 1, 0]), # 依赖于前两个
np.array([0, 0, 1])
]
print("=" * 50)
print("测试 1: 3D 空间中的 4 个向量")
indices1, basis1 = analyze_span(vectors1, ["v1", "v2", "v3", "v4"])

# 测试 2:全部独立
vectors2 = [
np.array([1, 0, 0]),
np.array([0, 1, 0]),
np.array([0, 0, 1])
]
print("\n" + "=" * 50)
print("测试 2: 标准基")
indices2, basis2 = analyze_span(vectors2, ["e1", "e2", "e3"])

# 测试 3:全部相关
vectors3 = [
np.array([1, 2]),
np.array([2, 4]),
np.array([3, 6])
]
print("\n" + "=" * 50)
print("测试 3: 共线向量")
indices3, basis3 = analyze_span(vectors3, ["v1", "v2", "v3"])

# 测试 4:实际应用 - 数据矩阵
print("\n" + "=" * 50)
print("测试 4: 数据矩阵的列向量")
data = np.array([
[1, 2, 3, 4],
[2, 4, 6, 5],
[3, 6, 9, 6]
]).T # 4 个样本,3 维特征

vectors4 = [data[i] for i in range(data.shape[0])]
indices4, basis4 = analyze_span(vectors4, ["sample1", "sample2", "sample3", "sample4"])

输出示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
==================================================
测试 1: 3D 空间中的 4 个向量

原始向量组: {v1, v2, v3, v4}
向量个数: 4

Span 的维度: 3
基的索引: [0, 1, 3]
基向量: {v1, v2, v4}

冗余向量索引: [2]
冗余向量: {v3}

==================================================
测试 2: 标准基

原始向量组: {e1, e2, e3}
向量个数: 3

Span 的维度: 3
基的索引: [0, 1, 2]
基向量: {e1, e2, e3}

==================================================
测试 3: 共线向量

原始向量组: {v1, v2, v3}
向量个数: 3

Span 的维度: 1
基的索引: [0]
基向量: {v1}

冗余向量索引: [1, 2]
冗余向量: {v2, v3}

题 17:可视化 3D 平面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

# 设置中文字体
plt.rcParams['font.sans-serif'] = ['Arial Unicode MS', 'SimHei']
plt.rcParams['axes.unicode_minus'] = False

def visualize_span_plane(v1, v2, title="两个向量张成的平面"):
"""
可视化两个 3D 向量张成的平面

参数:
v1, v2: numpy arrays (3D 向量)
title: str (图形标题)
"""
fig = plt.figure(figsize=(12, 9))
ax = fig.add_subplot(111, projection='3d')

# 绘制向量
ax.quiver(0, 0, 0, v1[0], v1[1], v1[2],
color='blue', arrow_length_ratio=0.15, linewidth=3,
label=f'$\\vec{{v}}_1 = ({v1[0]:.1f}, {v1[1]:.1f}, {v1[2]:.1f})$')
ax.quiver(0, 0, 0, v2[0], v2[1], v2[2],
color='red', arrow_length_ratio=0.15, linewidth=3,
label=f'$\\vec{{v}}_2 = ({v2[0]:.1f}, {v2[1]:.1f}, {v2[2]:.1f})$')

# 绘制平面(span)
s = np.linspace(-1.5, 1.5, 20)
t = np.linspace(-1.5, 1.5, 20)
S, T = np.meshgrid(s, t)

X = S * v1[0] + T * v2[0]
Y = S * v1[1] + T * v2[1]
Z = S * v1[2] + T * v2[2]

ax.plot_surface(X, Y, Z, alpha=0.3, color='purple', label='span')

# 绘制网格线
for s_val in np.linspace(-1, 1, 5):
line_x = np.linspace(-1, 1, 50) * v1[0] + s_val * v2[0]
line_y = np.linspace(-1, 1, 50) * v1[1] + s_val * v2[1]
line_z = np.linspace(-1, 1, 50) * v1[2] + s_val * v2[2]
ax.plot(line_x, line_y, line_z, 'gray', alpha=0.3, linewidth=0.5)

for t_val in np.linspace(-1, 1, 5):
line_x = t_val * v1[0] + np.linspace(-1, 1, 50) * v2[0]
line_y = t_val * v1[1] + np.linspace(-1, 1, 50) * v2[1]
line_z = t_val * v1[2] + np.linspace(-1, 1, 50) * v2[2]
ax.plot(line_x, line_y, line_z, 'gray', alpha=0.3, linewidth=0.5)

# 标注一些点
points = [
(0, 0, '原点'),
(v1, 'v1'),
(v2, 'v2'),
(v1 + v2, 'v1+v2'),
]

for point, label in points:
if isinstance(point, tuple):
continue
ax.scatter(*point, color='black', s=50, zorder=5)
ax.text(point[0], point[1], point[2], f' {label}', fontsize=10)

# 坐标轴
max_range = np.max([np.abs(v1).max(), np.abs(v2).max()]) * 1.5
ax.set_xlabel('$x$', fontsize=12)
ax.set_ylabel('$y$', fontsize=12)
ax.set_zlabel('$z$', fontsize=12)
ax.set_xlim(-max_range, max_range)
ax.set_ylim(-max_range, max_range)
ax.set_zlim(-max_range, max_range)

ax.set_title(title, fontsize=14, fontweight='bold', pad=20)
ax.legend(fontsize=11, loc='upper left')

# 添加说明文本
info_text = f'span{{$\\vec{{v}}_1, \\vec{{v}}_2$}} = ' \
f'{{$s\\vec{{v}}_1 + t\\vec{{v}}_2$ |$s, t \\in \\mathbb{{R}}$}}'
ax.text2D(0.5, 0.02, info_text, transform=ax.transAxes,
ha='center', fontsize=11,
bbox=dict(boxstyle='round', facecolor='wheat', alpha=0.5))

plt.tight_layout()
return fig, ax


# ===== 测试用例 =====

# 例子 1:xy 平面
v1 = np.array([1, 0, 0])
v2 = np.array([0, 1, 0])
fig1, ax1 = visualize_span_plane(v1, v2, "xy 平面")
plt.savefig('span_xy_plane.png', dpi=150, bbox_inches='tight')

# 例子 2:倾斜平面
v1 = np.array([1, 0, 1])
v2 = np.array([0, 1, 1])
fig2, ax2 = visualize_span_plane(v1, v2, "倾斜平面")
plt.savefig('span_tilted_plane.png', dpi=150, bbox_inches='tight')

# 例子 3:自定义平面
v1 = np.array([2, 1, 0])
v2 = np.array([1, 2, 1])
fig3, ax3 = visualize_span_plane(v1, v2, "自定义平面")
plt.savefig('span_custom_plane.png', dpi=150, bbox_inches='tight')

plt.show()

print("✓ 图片已保存")

题 18:交互式线性组合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider, Button

def interactive_linear_combination():
"""
交互式线性组合可视化
用户可以调整系数,实时看到结果变化
"""
# 初始向量
v1 = np.array([2, 1])
v2 = np.array([1, 2])

# 初始系数
c1_init, c2_init = 1.0, 1.0

# 创建图形
fig, ax = plt.subplots(figsize=(10, 10))
plt.subplots_adjust(bottom=0.25)

# 绘制设置
ax.set_xlim(-6, 6)
ax.set_ylim(-6, 6)
ax.set_aspect('equal')
ax.grid(True, alpha=0.3, linestyle='--')
ax.axhline(y=0, color='k', linewidth=1)
ax.axvline(x=0, color='k', linewidth=1)

# 绘制基向量
quiver1 = ax.quiver(0, 0, v1[0], v1[1], angles='xy', scale_units='xy',
scale=1, color='blue', width=0.01, alpha=0.8,
label='$\\vec{v}_1 = (2, 1)$')
quiver2 = ax.quiver(0, 0, v2[0], v2[1], angles='xy', scale_units='xy',
scale=1, color='red', width=0.01, alpha=0.8,
label='$\\vec{v}_2 = (1, 2)$')

# 初始结果
result = c1_init * v1 + c2_init * v2

# 绘制组合过程(虚线)
quiver_c1 = ax.quiver(0, 0, c1_init*v1[0], c1_init*v1[1],
angles='xy', scale_units='xy', scale=1,
color='blue', width=0.006, alpha=0.4, linestyle='--')
quiver_c2 = ax.quiver(c1_init*v1[0], c1_init*v1[1], c2_init*v2[0], c2_init*v2[1],
angles='xy', scale_units='xy', scale=1,
color='red', width=0.006, alpha=0.4, linestyle='--')

# 结果向量
quiver_result = ax.quiver(0, 0, result[0], result[1], angles='xy',
scale_units='xy', scale=1, color='green',
width=0.015, label='$c_1\\vec{v}_1 + c_2\\vec{v}_2$')

# 结果点
result_point, = ax.plot([result[0]], [result[1]], 'go', markersize=10, zorder=5)

# 标题
title = ax.set_title(
f'$c_1={c1_init:.2f}, c_2={c2_init:.2f}$ → 结果 = ({result[0]:.2f}, {result[1]:.2f})',
fontsize=13, fontweight='bold', pad=15
)

ax.legend(fontsize=11, loc='upper left')

# 创建滑块
ax_c1 = plt.axes([0.2, 0.15, 0.6, 0.03])
ax_c2 = plt.axes([0.2, 0.10, 0.6, 0.03])

slider_c1 = Slider(ax_c1, '$c_1$', -3.0, 3.0, valinit=c1_init, valstep=0.1)
slider_c2 = Slider(ax_c2, '$c_2$', -3.0, 3.0, valinit=c2_init, valstep=0.1)

# 存储历史轨迹
trajectory_x = [result[0]]
trajectory_y = [result[1]]
trajectory_line, = ax.plot(trajectory_x, trajectory_y, 'g-',
linewidth=1, alpha=0.5, label='轨迹')

def update(val):
"""更新函数"""
c1 = slider_c1.val
c2 = slider_c2.val
result = c1 * v1 + c2 * v2

# 更新组合过程向量
quiver_c1.set_UVC(c1*v1[0], c1*v1[1])
quiver_c2.set_offsets([[c1*v1[0], c1*v1[1]]])
quiver_c2.set_UVC(c2*v2[0], c2*v2[1])

# 更新结果向量
quiver_result.set_UVC(result[0], result[1])
result_point.set_data([result[0]], [result[1]])

# 更新标题
title.set_text(
f'$c_1={c1:.2f}, c_2={c2:.2f}$ → 结果 = ({result[0]:.2f}, {result[1]:.2f})'
)

# 更新轨迹
trajectory_x.append(result[0])
trajectory_y.append(result[1])
if len(trajectory_x) > 50: # 限制轨迹长度
trajectory_x.pop(0)
trajectory_y.pop(0)
trajectory_line.set_data(trajectory_x, trajectory_y)

fig.canvas.draw_idle()

slider_c1.on_changed(update)
slider_c2.on_changed(update)

# 重置按钮
reset_ax = plt.axes([0.8, 0.025, 0.1, 0.04])
reset_button = Button(reset_ax, '重置', hovercolor='0.975')

def reset(event):
slider_c1.reset()
slider_c2.reset()
trajectory_x.clear()
trajectory_y.clear()
trajectory_x.append(c1_init * v1[0] + c2_init * v2[0])
trajectory_y.append(c1_init * v1[1] + c2_init * v2[1])
trajectory_line.set_data(trajectory_x, trajectory_y)

reset_button.on_clicked(reset)

# 添加说明
info_text = '拖动滑块调整系数$c_1$ 和$c_2$\n观察线性组合的结果变化'
ax.text(0.02, 0.98, info_text, transform=ax.transAxes,
fontsize=10, verticalalignment='top',
bbox=dict(boxstyle='round', facecolor='wheat', alpha=0.5))

plt.show()

# 运行交互程序
interactive_linear_combination()

使用说明: 1. 运行程序后会弹出交互窗口 2. 拖动下方的滑块调整 3. 绿色向量实时显示线性组合结果 4. 绿色轨迹线显示结果点的移动路径 5. 点击"重置"按钮恢复初始状态


参考资料

  1. Strang, G. (2019). Linear Algebra and Learning from Data. Chapter 1.
  2. 3Blue1Brown. Essence of Linear Algebra, Chapters 2-3. [YouTube]
  3. Axler, S. (2015). Linear Algebra Done Right. Chapter 1-2.
  4. Boyd, S., & Vandenberghe, L. (2018). Introduction to Applied Linear Algebra. Chapter 2-5.
  5. Horn, R. A., & Johnson, C. R. (2012). Matrix Analysis. Chapter 0.

本文是《线性代数的本质与应用》系列的第 2 章。

  • 本文标题:线性代数(二)线性组合与向量空间
  • 本文作者:Chen Kai
  • 创建时间:2019-01-09 14:15:00
  • 本文链接:https://www.chenk.top/%E7%BA%BF%E6%80%A7%E4%BB%A3%E6%95%B0%EF%BC%88%E4%BA%8C%EF%BC%89%E7%BA%BF%E6%80%A7%E7%BB%84%E5%90%88%E4%B8%8E%E5%90%91%E9%87%8F%E7%A9%BA%E9%97%B4/
  • 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
 评论