Skip to content

第 3 章 向量的加法、减法与数乘

学习目标

  • 掌握向量加法的两种几何法则:三角形法则、平行四边形法则
  • 掌握加法、减法、数乘的分量计算公式
  • 理解数乘如何改变向量的大小与方向
  • 会用 NumPy 与 PyTorch 做向量运算,并用 matplotlib 验证几何意义
  • 理解向量运算的运算律(交换律、结合律、分配律)

3.1 从位移到加法:先走一段,再走一段

小明的家在 点。他先向东走 3 千米到 点,再向北走 2 千米到 点。问:他最终的位置相对家来说,在哪里?

第一步的位移是向量 ,第二步的位移是向量 。两步合起来的效果,等于从 直接走到 的位移:

这就是向量加法:把两个位移「接起来」,得到总位移。

向量加法

几何上,向量加法有两种画法:

  1. 三角形法则:把 的起点放在 的终点上,「首尾相接」,从 的起点连到 的终点,得到
  2. 平行四边形法则:把 放在同一起点,以它们为邻边作平行四边形,从起点出发的那条对角线就是

两种画法得到同一个结果——这本身就是向量加法交换律的几何证明(见 3.4)。

3.2 分量加法:横加横,纵加纵

把向量写成坐标,加法就变成「对应分量相加」:

为什么可以这样加?因为横方向与纵方向互相独立:向东 3 千米的横位移是 3,向北 2 千米的横位移是 0,横方向总共移动 ;纵方向总共移动 。分量各自相加,再合成。

NumPy 与 PyTorch 里,加法就是数组相加:

python
import numpy as np

u = np.array([3.0, 0.0])   # 先向东 3
w = np.array([0.0, 2.0])   # 再向北 2
print("u + w =", u + w)

运行输出:

text
u + w = [3. 2.]
python
import torch

u = torch.tensor([3.0, 0.0])
w = torch.tensor([0.0, 2.0])
print("torch u + w =", u + w)

运行输出:

text
torch u + w = tensor([3., 2.])

3.3 减法:加上负向量

向量减法定义为「加上负向量」:

即对应分量相减。

几何上, 是从 的终点指向 的终点的那条有向线段(把两个向量起点放一起)。它的直观含义是「从 的位置,到 的位置,该怎么走」。

python
import numpy as np

u = np.array([3.0, 2.0])
w = np.array([1.0, 4.0])
print("u - w =", u - w)
print("-w     =", -w)
print("u + (-w) =", u + (-w))

运行输出:

text
u - w = [ 2. -2.]
-w     = [-1. -4.]
u + (-w) = [ 2. -2.]

3.4 数乘:把向量拉长、压短或反向

数乘:数 乘向量 ,得到

它的效果是:

  • :方向不变,长度变成原来的 倍;
  • :方向不变,长度变成原来的 倍(缩短);
  • :方向相反,长度变成原来的 倍;
  • :得到零向量。

数乘

python
import numpy as np

v = np.array([2.0, 1.0])
print("2v   =", 2 * v)    # 同向,2 倍长
print("0.5v =", 0.5 * v)  # 同向,一半长
print("-1v  =", -1 * v)   # 反向,一样长
print("0v   =", 0 * v)    # 零向量

运行输出:

text
2v   = [4. 2.]
0.5v = [1.  0.5]
-1v  = [-2. -1.]
0v   = [0. 0.]

「乘 」对应的实际场景:同样的方向、把速度调到 倍;或者「往反方向走」。数乘让我们能伸缩翻转向量,这是第 7 章矩阵变换的雏形。

3.5 运算律:为什么可以放心交换顺序

向量的加法与数乘满足下面五条基本运算律(标量 为任意数):

  1. 加法交换律:
  2. 加法结合律:
  3. 数乘分配律(对向量加法):
  4. 数乘分配律(对数的加法):
  5. 数乘结合律:

这些律不是「背下来就完了」,而是可以逐分量验证的。比如交换律:

因为数的加法满足 ,所以两个向量相等。其余各条同理——向量的运算律,最终都归结为数的运算律在每一个分量上成立

用代码验证三条关键律:

python
import numpy as np

u = np.array([1.0, 2.0])
w = np.array([3.0, -1.0])
z = np.array([-2.0, 4.0])
k, l = 2.0, 3.0

print("交换律:", np.allclose(u + w, w + u))
print("结合律:", np.allclose((u + w) + z, u + (w + z)))
print("分配律1:", np.allclose(k * (u + w), k * u + k * w))
print("分配律2:", np.allclose((k + l) * u, k * u + l * u))
print("结合律(数乘):", np.allclose(k * (l * u), (k * l) * u))

运行输出:

text
交换律: True
结合律: True
分配律1: True
分配律2: True
结合律(数乘): True

np.allclose 用来比较浮点数组是否「几乎相等」——因为计算机里小数有舍入误差,直接比较 == 可能因为 这类问题误报。

3.6 画图验证:平行四边形法则

把 3.1 的加法画出来,同时画出数乘:

python
import matplotlib.pyplot as plt
from matplotlib import font_manager
for f in font_manager.findSystemFonts():
    if any(k in f for k in ("NotoSansCJK", "NotoSansSC", "wqy", "SimHei", "msyh", "PingFang")):
        font_manager.fontManager.addfont(f)
plt.rcParams["font.sans-serif"] = ["Noto Sans CJK SC", "WenQuanYi Zen Hei", "SimHei", "Microsoft YaHei", "PingFang SC"]
plt.rcParams["axes.unicode_minus"] = False

import numpy as np

u = np.array([3.0, 0.0])
w = np.array([0.0, 2.0])

fig, ax = plt.subplots(figsize=(6.5, 5.5))
ax.axhline(0, color="black", linewidth=1)
ax.axvline(0, color="black", linewidth=1)
ax.grid(True, linestyle=":", alpha=0.5)
ax.set_xlim(-0.5, 4.5)
ax.set_ylim(-0.5, 3.5)
ax.set_aspect("equal")

def arrow(vec, start, color, label=None):
    ax.quiver(start[0], start[1], vec[0], vec[1],
              angles="xy", scale_units="xy", scale=1, color=color, width=0.015)
    if label:
        ax.annotate(label, start + vec / 2, textcoords="offset points",
                    xytext=(8, 6), color=color)

arrow(u, np.array([0.0, 0.0]), "tab:red", "u")
arrow(w, u, "tab:blue", "w")                       # 首尾相接:w 起点在 u 终点
arrow(u + w, np.array([0.0, 0.0]), "tab:purple", "u + w")  # 总位移

# 平行四边形补全(虚线):从 w 终点出发画 u
ax.plot([w[0], w[0] + u[0]], [w[1], w[1] + u[1]], color="tab:red",
        linestyle="--", linewidth=1)
ax.plot([u[0], u[0] + w[0]], [u[1], u[1] + w[1]], color="tab:blue",
        linestyle="--", linewidth=1)

ax.set_title("向量加法:三角形法则 = 平行四边形法则")
fig.savefig("book/public/figs/fig03-vector_add.png", dpi=120, bbox_inches="tight")
plt.close(fig)

# 数乘图
fig, ax = plt.subplots(figsize=(6, 6))
ax.axhline(0, color="black", linewidth=1)
ax.axvline(0, color="black", linewidth=1)
ax.grid(True, linestyle=":", alpha=0.5)
ax.set_xlim(-4.5, 5.5)
ax.set_ylim(-2.5, 3.5)
ax.set_aspect("equal")

v = np.array([2.0, 1.0])
arrow(v, np.array([0.0, 0.0]), "tab:red", "v")
arrow(2 * v, np.array([0.0, 0.0]), "tab:blue", "2v")
arrow(0.5 * v, np.array([0.0, 0.0]), "tab:green", "0.5v")
arrow(-v, np.array([0.0, 0.0]), "tab:orange", "-v")

ax.set_title("数乘:拉长、缩短、反向")
fig.savefig("book/public/figs/fig03-scalar_mult.png", dpi=120, bbox_inches="tight")
plt.close(fig)
print("已保存两张图")

动手实践

  1. 用三角形法则画出 的和,再用分量公式计算并验证。
  2. 在纸上画 ,画出 并判断后者的结果。
  3. 用 NumPy 计算 ,其中 ,再用 PyTorch 算一遍对比结果。
  4. 修改 3.6 的代码,把 改成 ,观察平行四边形变成什么形状,并解释为什么。

常见错误

错误写法/理解原因
也当分量加进去,如 数乘是每个分量都乘 :
减法方向搞反, 画成从 终点到 终点减法对应「从 的位置到 的位置」,箭头指向被减向量
认为 时长度变负长度永远是正的, 只是方向反转,长度是
直接用 == 比较浮点数组浮点有舍入误差,应使用 np.allclose

章末练习

基础

  1. 计算:
  2. 画出 ,标注每个向量。
  3. 用 NumPy 验证:(任选一组非零向量)。

提高

  1. 已知 ,求 ,并写出推导过程。
  2. 一个物体同时受到两个力:向右 4 牛、向上 3 牛。用向量加法求合力的大小与方向(方向用与水平方向的夹角描述,可先用勾股定理算大小,夹角留到第 4 章精确计算)。

挑战

  1. 对任意向量 , 对吗?用分量证明,再用代码验证。
  2. 用结合律解释:为什么先走 再走 再走 的总位移,与「三步合并成一步」的结果相同?写出对应的向量等式。

章末自测

    • A.
    • B.
    • C.
    • D.
    • A.
    • B.
    • C.
    • D.
    • A.
    • B.
    • C.
    • D.
    • A.
    • B.
    • C.
    • D.
  1. 向量加法满足交换律,意思是?
    • A.
    • B.
    • C.
    • D.
  2. 三角形法则中,两个向量如何摆放?
    • A. 同一个起点
    • B. 首尾相接
    • C. 相互垂直
    • D. 平行
  3. 等价于?
    • A.
    • B.
    • C.
    • D.
  4. 向量 ,则 是?
    • A.
    • B.
    • C.
    • D.
  5. 下列哪条是数乘分配律?
    • A.
    • B.
    • C.
    • D.
  6. 浮点数组比较应该用?
    • A. ==
    • B. np.allclose
    • C. is
    • D. >=