
线性代数
向量究竟是什么?
前面我们在序言中提到,学习线性代数,需要把数值计算和几何直观联系起来。接下来,我们从最基础的组成部分开始,也就是向量。
大家可能已经接触过向量。学习物理时,我们用它描述速度、位移;编写程序时,我们会看到一组有顺序的数字;学习数学时,又会遇到向量空间这样的概念。
那么,向量到底是一根箭头,还是一组数字?这些看起来不同的说法,又是怎样联系在一起的?
这一篇我们重点讲清楚三个问题:怎样理解向量,怎样用坐标表示向量,以及怎样完成向量加法与数乘。 每一种运算,我们都会先观察几何过程,再看公式和 Python 代码。
三种看待向量的方式
从几何角度看,向量是空间中的箭头
我们先采用最直观的理解:把向量看成空间中的一根箭头。 对于一个非零向量,我们关心的是它的长度,以及它所指的方向。
比如一根箭头表示"向右移动 1 个单位,再向上移动 2 个单位"。只要这段位移没有变化,我们把箭头整体平移到其他位置,它仍然可以表示同一个向量。

图中三根箭头的起点不同,但它们都表示向右 、向上
的位移。我们可以选择其中任何一根,作为这个向量的几何表示。
这里讨论的是自由向量。在具体物理问题中,如果还需要考虑力的作用点等信息,就不能把那些额外信息一起忽略。当前我们先专注于向量本身的加法和数乘。
为了后面讲解方便,接下来我们通常把箭头的起点放在坐标原点。这样,比较两个向量时,我们就能直接观察它们的终点位置。
把起点画在原点,是一种便于观察的约定。 并不是说向量一旦离开原点,就不能再表示原来的位移。后面讲向量加法时,我们还会主动平移其中一根箭头。
从数据角度看,向量是一组有顺序的数字
接下来,我们换一个角度。
假设现在需要分析房屋信息,只关注两个特征:房屋面积 和房屋价格 。我们约定面积使用平方米,价格使用万元,那么一套面积为 平方米、价格为
万元的房屋,可以记录成:
$$
\mathbf h= \begin{bmatrix} 100\\300 \end{bmatrix}
$$
$$
\begin{aligned}
\text{第一项:面积,单位为平方米}\\
\text{第二项:价格,单位为万元}
\end{aligned}
$$
这里我公式插入不出来

这里的两个数字有固定的顺序,第一项记录面积,第二项记录价格。顺序一旦改变,数据含义就改变了。 在同一套约定下,[100, 300] 和 [300, 100] 不能混用。
这就是使用二维向量表示一条房屋记录:每条记录包含两个坐标分量。将来增加楼龄、房间数量等特征,就需要更多的分量。
大家需要区分两件事:房屋本身存在于三维物理空间,并不意味着描述房屋的数据向量只能有三个分量。数据向量的维数,取决于我们选取了多少个特征。
还有一个实际问题:面积和价格使用不同单位,因此不能把这个特征向量的长度直接解释成现实中的空间距离。改变价格单位,数字就会改变。这里先用这个例子说明"有序数字"的含义,暂时不讨论特征之间的距离度量。
从数学角度看,重点在于运算规则
前面两种理解,一种关注空间中的箭头,另一种关注计算机里的数字。数学会继续追问:能不能找出它们共同遵循的规则?
本章最重要的两种运算,就是向量与向量相加 ,以及一个数与向量相乘。空间中的箭头可以做这两种运算,有序的数值坐标也可以做这两种运算,而且两边的结果能够对应起来。
更一般的向量空间,还可以把满足相应运算规则的多项式、函数等对象当作向量。这里需要补充一点:并不是随便定义一个加号和乘号,就自动得到了向量空间;这些运算还需要满足结合律、分配律等规则。
这部分我们留到后面再展开。当前大家先把三种视角放在一起理解:
| 看待向量的方式 | 重点关注什么? | 当前可以怎样理解? |
|---|---|---|
| 几何视角 | 长度和方向 | 空间中的箭头 |
| 数据视角 | 分量的顺序和含义 | 按固定顺序排列的数值坐标 |
| 抽象数学视角 | 对象遵循的运算规则 | 满足向量空间规则的对象 |
接下来的主线,是先用箭头建立直观,再用数字描述它。有了坐标,我们就能把图上的变化,交给计算机准确地计算。
向量坐标:把箭头写成数字
二维向量怎样表示?
我们先回到二维平面,画出水平的 轴和竖直的
轴,把它们的交点作为原点
。
在这个固定的坐标系中,我们用一对数记录从原点到箭头终点的位移。第一个数对应水平方向,正数向右、负数向左;第二个数对应竖直方向,正数向上、负数向下。
比如下面这个向量:
它表示从原点出发,向右移动 个单位,再向上移动
个单位。另一个向量:
则表示向右移动 个单位,再向下移动
个单位。

图中的虚线把水平位移和竖直位移分别标记出来。这里"先水平、再竖直"是在说明坐标,走路的先后并不会改变最终位置。我们也可以先完成竖直位移,再完成水平位移。
固定坐标系以后,每一个坐标对,都对应一个唯一的向量;每一个向量,也对应一组唯一的坐标。 如果改变坐标系,同一个几何向量的坐标可能发生变化。这是后面学习基与基变换时需要继续讨论的问题。
大家还会看到两种相近的写法:点 ,以及向量
。前者强调一个位置,后者强调从起点到终点的位移。这里的上标
表示转置,用来把横着写的一行数字表达成列向量。
当起点选在原点时, 的分量正好等于点
的坐标。但如果箭头从
指向
,它表示的向量就是:
这也解释了前面为什么可以平移箭头:起点和终点一起移动,二者的坐标差保持不变。关于点、箭头和坐标之间的联系,也可以参考佐治亚理工学院教材 Interactive Linear Algebra:Vectors。
三维向量怎样表示?
理解二维向量之后,我们再增加一根与 轴、
轴都垂直的
轴,就得到了常用的三维直角坐标系。
这时候,需要三个数记录位移:
它表示从原点出发,先沿 轴正方向移动
,再沿平行于
轴的方向移动
,最后沿平行于
轴的方向移动
。

图中蓝色箭头表示最终向量,分段路径表示三个坐标分量。三维图画在二维屏幕上,会产生透视效果,但三个分量的含义没有改变。
同样,四维、五维或者更高维的数值向量,也可以按顺序记录四个、五个或更多分量。我们未必能把它们直接画成空间中的箭头,但仍然可以按照明确的规则计算。
使用 Python 创建向量
接下来,我们把前面的向量写到程序里。
这里继续使用 NumPy,新建 Python 文件,命名为 vectors_demo.py。如果当前环境没有安装依赖,可以先执行:
python
python3 -m pip install numpy
第一步,导入 numpy,创建二维向量和三维向量:
python
import numpy as np
vector = np.array([1.0, 2.0])
other = np.array([3.0, -1.0])
vector_3d = np.array([2.0, 1.0, 3.0])
print("二维向量:", vector)
print("三维向量:", vector_3d)
print("二维向量的分量个数:", vector.size)
print("NumPy 数组的维数:", vector.ndim)
print("NumPy 数组的形状:", vector.shape)
运行代码,输出结果如下:
python
二维向量: [1. 2.]
三维向量: [2. 1. 3.]
二维向量的分量个数: 2
NumPy 数组的维数: 1
NumPy 数组的形状: (2,)
这里有一个初学时很容易混淆的地方:数学中的二维向量,可以使用 NumPy 的一维数组存储。
数学里的"二维",说的是当前向量有两个坐标分量;vector.ndim 说的是数组有几根索引轴。现在只需要一个索引就能访问分量,例如 vector[0]、vector[1],所以这个数组的 ndim 是 1。
这里两者都没有写错,只是在回答不同的问题。NumPy 对数组形状、索引轴和元素数量的说明,可以查阅官方入门文档。
向量加法:把两段位移连接起来
先观察几何过程
前面我们已经定义了两个向量:
接下来,我们计算 。
先不要急着套公式。我们把 整体平移,让它的起点落在
的终点。然后,从第一个向量的起点出发,向第二个向量平移后的终点画一根箭头,这根箭头就是两个向量的和。

图中左边是首尾相接的画法。我们先按照 移动,再按照
移动,最终到达的位置,与直接按照
移动完全相同。
大家可以把它和数轴上的加法对照一下:先向右走 步,再向右走
步,总效果就是向右走
步。向量加法只是把这种"连续位移"的理解,扩展到了更多方向。
图中右边把两种先后顺序都画了出来,就得到了平行四边形。对角线表示同一个和向量,因此:
这里平移的是箭头的位置,箭头本身表示的位移没有改变。首尾相接之后,第二根箭头的新终点坐标,不能误当成第二个向量自身的分量。 它的分量仍然需要用终点减去起点。
再看坐标怎样计算
按照前面的例子,我们先向右 、向上
,接着向右
、向下
。把水平和竖直位移分别合在一起:
这样,图上最终到达的位置,与计算出的坐标就对应起来了。一般情况下,两个同维数的坐标向量相加,就是把对应分量分别相加:
这里不只要求分量个数一致,还需要使用相同的坐标约定。 如果两组数据的第一项分别表示面积和价格,即使都包含两个数,也不能不加区分地把对应位置相加。
编写向量加法代码
前面创建数组之后,我们继续添加:
python
result = vector + other
print("第一个向量:", vector)
print("第二个向量:", other)
print("两个向量的和:", result)
输出结果:
python
第一个向量: [1. 2.]
第二个向量: [ 3. -1.]
两个向量的和: [4. 1.]
我们可以看到,NumPy 把第一个位置的 1.0 与 3.0 相加,把第二个位置的 2.0 与 -1.0 相加,得到 [4. 1.]。
对于当前同形状的数组,vector + other 对应逐元素相加,也可以写成 np.add(vector, other)。这里的接口含义,可以查看 NumPy 官方 add 文档。
我们再用程序检查首尾相接时的终点:
python
start = np.array([0.0, 0.0])
first_end = start + vector
second_end = first_end + other
print("第一段位移的终点:", first_end)
print("第二段位移的终点:", second_end)
print("第二根箭头表示的向量:", second_end - first_end)
输出结果:
第一段位移的终点: [1. 2.]
第二段位移的终点: [4. 1.]
第二根箭头表示的向量: [ 3. -1.]
第二根箭头虽然从 画到了
,它表示的依然是向右
、向下
。这就把向量、起点和终点之间的关系说明白了。
向量数乘:把同一段位移放大或缩小
向量加法讲清楚之后,我们继续学习第二种基本运算:向量数乘,也就是一个数与向量相乘。
这里参与运算的数,我们称为标量 。当前讨论实数向量,所以这里的标量就是一个实数。比如 、
、
,都可以作为缩放向量的系数。
我们仍然使用 ,分别观察下面几种情况。
乘以正数:方向相同,长度按比例变化
乘以 ,就是把向量伸长到原来的两倍:
乘以 ,就是把长度缩短到原来的三分之一:
这两个例子中,水平和竖直分量按照相同的比例变化,所以得到的非零向量仍然指向原来的方向。
乘以负数:方向反转,长度看绝对值
如果乘以 :
这时候,箭头指向相反方向,长度变成原来的 倍。负号表示方向反转,并不表示向量的长度变成了负数。

图中各小图使用相同的坐标尺度,虚线表示原向量,实线表示数乘后的向量。大家可以同时对比长度和方向,不能只看箭头的终点处在第几个象限。
一般来说,如果 是非零向量,
是一个非零实数,那么
的长度是原长度的
倍。
时方向相同,
时方向相反。
乘以零:得到零向量
还有一种特殊情况:
这个结果称为零向量 。它的长度为 ,没有确定的方向,所以图中用原点处的圆点表示。
这里也需要区分:标量 是一个数,零向量
则是当前向量空间中的向量。它们在公式里承担的角色不同,不能因为都写成零,就忽略它们的类型。
把前面的规律写成坐标公式,就是:
向量数乘,需要把每一个分量都乘以同一个标量。 如果只改变某一个分量,一般就不会得到原向量的等比例缩放。
编写向量数乘代码
接下来,我们继续在前面的程序后面添加:
python
print("伸长到两倍:", 2 * vector)
print("缩短到三分之一:", np.round((1 / 3) * vector, 6))
print("反向并伸长:", -1.8 * vector)
print("乘以零:", 0 * vector)
输出结果:
python
伸长到两倍: [2. 4.]
缩短到三分之一: [0.333333 0.666667]
反向并伸长: [-1.8 -3.6]
乘以零: [0. 0.]
这里 np.round(..., 6) 只是把三分之一和三分之二的显示结果保留到小数点后六位。数学中的精确值仍然是 和
,不是有限位小数。
在这段代码里,2 * vector 也可以写成 np.multiply(2, vector)。multiply 执行逐元素乘法,标量 2 会作用到向量的每一个分量,具体可以查看 NumPy 官方文档。
这里讲的是标量与向量相乘 。两个向量的点积、叉积,以及两个数组使用 * 的含义,需要分别讨论,不能把它们直接当成同一种运算。
编写代码时,再注意两个问题
Python 列表相加,不是向量相加
前面我们把坐标向量理解成"有序的数字列表",但这个数学描述,不代表 Python 的 list 类型会自动执行向量运算。
我们先看下面的代码:
python
list_vector = [1, 2]
list_other = [3, -1]
print("Python 列表相加:", list_vector + list_other)
print("Python 列表乘以二:", 2 * list_vector)
输出结果:
python
Python 列表相加: [1, 2, 3, -1]
Python 列表乘以二: [1, 2, 1, 2]
可以看到,列表相加执行的是拼接,列表乘以整数执行的是重复。它们没有按照前面的坐标公式进行计算。
这就是我们在示例中使用 np.array() 的原因:明确使用数值数组,再利用它提供的逐元素运算,表达当前需要的向量加法与数乘。
同样的加号和乘号,对不同类型的对象,可能有不同的含义。 代码没有报错,不一定代表完成了我们想要的数学运算。运行之后,还需要检查输出的分量和形状。
对应分量相加,先检查形状与含义
前面的两个二维向量,都使用形状为 (1, 2) 的数组存储,所以对应分量能够直接相加。
如果一个数组使用 (1, 2),另一个却使用 (2, 1),NumPy 可能按照广播规则得到一个 (2, 2) 的数组。这个结果虽然符合程序库的运算规则,却不是我们当前想求的两个向量之和。
因此,当前练习统一使用前面的一维数组写法。做加法之前,先确认两者的分量个数、数组形状以及每一项的含义;二维位移与三维位移,也不能直接按本章的规则相加。
先明确数学对象,再选择代码表示。 这样,数值计算才能和我们脑海里的箭头保持一致。
为什么要从这两种运算开始?
到这里,向量加法和数乘都已经讲解完成。大家可能会觉得,这两种运算并不复杂,为什么需要花时间从图像和代码两个角度讲一遍?
因为后面很多操作,都需要把它们组合起来。
比如我们先把 放大到原来的两倍,再加上
:
从几何上看,就是先按照 移动,再按照
移动;从数值上看,就是先对每个分量乘以
,再完成对应分量相加。
我们在程序中继续添加:
python
combined = 2 * vector + other
print("先数乘,再相加:", combined)
输出结果:
python
先数乘,再相加: [5. 3.]
像这样,把向量分别乘以标量,再把结果相加,就是后面要学习的线性组合。这一篇先认识这个形式,下一篇再继续讨论:改变这些标量,最终能够到达哪些位置?
大家现在可以回头看,向量的两种具体理解已经联系起来了:箭头帮助我们判断一次运算在做什么,坐标帮助我们把运算准确写出来,程序则按照相应规则完成计算。
对于数据分析,我们能够通过向量运算理解一组数字怎样变化;对于图形程序,我们能够把希望发生的空间变化,转成计算机可以处理的数值。后面学习时,我们会不断在这两种视角之间切换。
先自己算一遍,再运行代码
正式进入下一篇之前,我们先做三个很小的练习。仍然使用 和
:
- 不运行程序,先画出
,再换一个先后顺序画出
,看看最终位置是否相同。
- 画出
,解释为什么它与
相加后会得到零向量。
- 先手算
,再把程序里的表达式改成
vector + 2 * other,检查输出。
第三题的计算结果是 ,对应向右移动
个单位,竖直方向的位移正好抵消。这里向量的长度并不为零,只是其中一个分量为零,需要和零向量区分开。
安装 NumPy 后,在本文所在目录执行:
python
python3 examples/vectors_demo.py
看输出时,可以逐项对照前面的图和公式。我们需要能够说明:这个结果表示哪一段位移,为什么对应这些数字,以及换一个起点之后,哪些内容会改变、哪些内容仍然相同。
到这里,向量的基本含义、坐标表示、向量加法和向量数乘就讲解完成了。下一篇,我们继续学习:线性组合、张成的空间与基。
参考资料与配套内容
- 上一篇:线性代数 序言 。
- 教材补充:Interactive Linear Algebra:Vectors。
- 代码接口:NumPy 数组入门、逐元素加法、逐元素乘法。