2.1 矩陣即變換: \(A\mathbf{v}\) 的幾何意義#
本節目標#
- 把「矩陣」從一格格的數字,重新看成 空間的變換(function:吃進向量、吐出向量)
- 理解線性變換的兩條規則:保持加法與純量乘
- 掌握關鍵事實:矩陣的每一行,就是基底向量 \(\mathbf{e_1}, \mathbf{e_2}\) 變換後的像
- 用第 1 章的
linalg_viz工具箱,親眼看見單位方格網被矩陣「剪」成斜的 - 會用 NumPy 的
A @ v對向量做變換
2.1.1 從「數字格子」到「空間機器」#
高中教你的矩陣長這樣:
一堆數字,乘法規則靠背。但換個角度:
矩陣是一部機器:餵它一個向量 \(\mathbf{v}\) ,它吐出另一個向量 \(A\mathbf{v}\) 。
輸入 \(\mathbf{v} = \begin{bmatrix} 2 \\ 1 \end{bmatrix}\) ,輸出:
向量被「推」了。整個平面上所有的點同時被推 —— 這就是 變換。
2.1.2 什麼是「線性」變換?#
不是所有變換都配得上矩陣。矩陣只能表達 線性 變換,它必須遵守兩條規則:
- 保持加法: \(T(\mathbf{u} + \mathbf{v}) = T(\mathbf{u}) + T(\mathbf{v})\)
- 保持純量乘: \(T(c\,\mathbf{v}) = c\,T(\mathbf{v})\)
幾何上這意味著:
- 原點不動( \(T(\mathbf{0}) = \mathbf{0}\) )
- 直線仍是直線(不會彎)
- 等距的點變換後仍等距(格子不會被不均勻地拉扯)
旋轉、鏡射、剪切、縮放都遵守這兩條;平移 不遵守(平移會動到原點),所以純矩陣表達不了平移 —— 這要到第 8 章用「齊次座標」偷偷解決。
用 NumPy 直接驗證兩條規則:
import numpy as np
A = np.array([[1., 1.],
[0., 1.]])
u = np.array([2., 1.])
v = np.array([-1., 3.])
lhs = A @ (u + v) # T(u + v)
rhs = A @ u + A @ v # T(u) + T(v)
print(np.allclose(lhs, rhs)) # True:加法保持
lhs = A @ (2.5 * v) # T(c v)
rhs = 2.5 * (A @ v) # c T(v)
print(np.allclose(lhs, rhs)) # True:純量乘保持
2.1.3 最關鍵的一句話:矩陣的行就是 \(\mathbf{e_1}, \mathbf{e_2}\) 的像#
把 \(\mathbf{e_1} = \begin{bmatrix} 1 \\ 0 \end{bmatrix}\) 、 \(\mathbf{e_2} = \begin{bmatrix} 0 \\ 1 \end{bmatrix}\) 餵進矩陣:
看到了嗎?** \(A\mathbf{e_1}\) 就是 \(A\) 的第一行, \(A\mathbf{e_2}\) 就是 \(A\) 的第二行**。一般地:
矩陣不需要背,它是「基底向量被丟去哪裡」的完整紀錄。 知道 \(\mathbf{e_1}, \mathbf{e_2}\) 的下場,就知道整個平面的下場 —— 因為任何向量都是它們的線性組合(1.3 節)。
2.1.4 雙重觀點: \(A\mathbf{v}\) 是「行的線性組合」#
同一個計算 \(A\mathbf{v}\) ,有兩種讀法:
觀點一(逐列):每一列和 \(\mathbf{v}\) 做內積,得到輸出的每個分量。
觀點二(逐行):
** \(A\mathbf{v}\) 就是「用 \(\mathbf{v}\) 的分量當係數,對 \(A\) 的行做線性組合」。**
觀點二是理解一切的鑰匙:變換後的向量落在「行的張成空間」裡。這句話會在第 4 章(張成、基底)、第 5 章(特徵向量)反覆出現。
2.1.5 動手畫:剪切變換把方格網變成什麼?#
用第 1.4 節建立的 linalg_viz 工具箱,畫出 \(A = \begin{bmatrix} 1 & 1 \\ 0 & 1 \end{bmatrix}\) 作用前後的單位方格網:
import numpy as np
import matplotlib.pyplot as plt
from linalg_viz import setup_axes, draw_grid_transform, draw_vectors
A = np.array([[1., 1.],
[0., 1.]])
fig, ax = plt.subplots(figsize=(7, 7))
setup_axes(ax, xlim=(-1, 5), ylim=(-1, 4))
draw_grid_transform(ax, A, xlim=(-1, 5), ylim=(-1, 4))
draw_vectors(ax, {
r"$\mathbf{e_1}$": np.array([1., 0.]), # 變換前(虛線)
r"$T(\mathbf{e_1})$": A @ np.array([1., 0.]),
r"$\mathbf{e_2}$": np.array([0., 1.]),
r"$T(\mathbf{e_2})$": A @ np.array([0., 1.]),
})
ax.set_title(r"Shear: $A = \begin{bmatrix}1&1\\0&1\end{bmatrix}$")
plt.show()
你會看到:
- 淡色方格網是變換前的平面
- 深色網格是變換後的平面:每一條水平線向右滑,滑的距離等於它的高度( \(y\) 越大滑越多)
- \(\mathbf{e_1}\) 沒動( \(A\mathbf{e_1} = \mathbf{e_1}\) ,第一行是 \((1,0)\) )
- \(\mathbf{e_2}\) 被推成 \((1, 1)\) (第二行)—— 這就是「剪」的來源:像推倒一疊撲克牌
試著把 \(A\) 換成 \(\begin{bmatrix} 1 & 0 \\ 1 & 1 \end{bmatrix}\) (垂直剪切)、 \(\begin{bmatrix} 2 & 0 \\ 0 & 1 \end{bmatrix}\) (水平拉伸),重跑一次,觀察行向量如何「指揮」整個平面。
2.1.6 NumPy 實戰:A @ v#
import numpy as np
A = np.array([[1., 1.],
[0., 1.]])
v = np.array([2., 1.])
print(A @ v) # [3. 1.] 矩陣乘向量
print(A @ np.eye(2)) # 驗證:乘單位矩陣不變
# 同時變換一堆向量(每行一個向量)——一次算整個平面!
V = np.array([[2., -1., 0.],
[1., 3., 2.]])
print(A @ V)
# [[3. 2. 2.]
# [1. 3. 2.]]
注意 A @ V 一次變換三個向量:矩陣乘矩陣,其實就是「對每個行向量各做一次 \(A\mathbf{v}\) 」。這為下一節的矩陣乘法鋪路。
flowchart LR
v["向量 v"] --> M["機器 A"] --> w["新向量 Av"]
e1["e1"] --> M
e2["e2"] --> M
subgraph 紀錄
col1["第一行 = A e1"]
col2["第二行 = A e2"]
end
M -.記錄下場.-> 紀錄
小結與練習#
本節重點
- 矩陣是 線性變換:保持加法與純量乘,原點不動、直線仍直
- \(A\) 的第 \(j\) 行就是 \(T(\mathbf{e_j})\) :矩陣是基底向量的「下場紀錄表」
- \(A\mathbf{v} = v_1(\text{第1行}) + v_2(\text{第2行})\) :矩陣乘向量 = 行的線性組合
A @ v一行程式,A @ V一次變換整批向量
練習
- 寫下矩陣 \(B\) ,使 \(B\mathbf{e_1} = \begin{bmatrix} 0 \\ 2 \end{bmatrix}\) 、 \(B\mathbf{e_2} = \begin{bmatrix} -1 \\ 0 \end{bmatrix}\) ,用
linalg_viz畫出 \(B\) 的網格圖。這個變換做了什麼(旋轉?鏡射?)? - 計算 \(A\mathbf{v}\) ( \(A\) 為剪切矩陣, \(\mathbf{v} = (3, -2)\) ),先用「行的線性組合」心算,再用 NumPy 驗證。
- 證明(用 NumPy 隨機向量測試即可):若 \(A\) 是任意 \(2\times2\) 矩陣,則 \(A(\mathbf{0}) = \mathbf{0}\) 。
- 挑戰:矩陣 \(C = \begin{bmatrix} 1 & 2 \\ 2 & 4 \end{bmatrix}\) 把整個平面壓成一條直線。畫出 \(C\) 的網格圖,並觀察它的兩行有什麼關係(提示:第 3 章的「線性相依」)。