3.1 方程組即變換: \(A\mathbf{x}=\mathbf{b}\) 的三種命運#
本節目標#
- 把「解聯立方程式」重新理解成「找一個向量,讓變換後落在 \(\mathbf{b}\) 上」
- 認識 \(A\mathbf{x}=\mathbf{b}\) 的三種命運:唯一解、無解、無窮多解
- 學會 行圖(row picture,直線交點)與 列圖(column picture,向量組合)兩種觀點,並都畫出來
- 會用
np.linalg.solve解方程組,並在電腦說「解不存在」時自己判斷原因
一、先換一副眼鏡:方程組是「找回原向量」#
高中我們這樣寫方程組:
現在換一種寫法 —— 把係數收進矩陣、未知數收進向量:
也就是 \(A\mathbf{x}=\mathbf{b}\) 。第 2 章說過:矩陣是把平面壓扁、拉長、旋轉的變換機。於是解方程組的幾何意義是:
已知一個向量 \(\mathbf{x}\) 被機器 \(A\) 加工後變成 \(\mathbf{b}\) ,請問原本的 \(\mathbf{x}\) 是什麼?
這是「逆向工程」:變換是已知的,結果是已知的,要猜的是輸入。
二、三種命運#
一個變換 \(A\) 把整個平面(或空間)搬到 像空間(column space,第 4 章正式介紹)。 \(\mathbf{b}\) 的下場只有三種:
| 命運 | 幾何意義 | 代數條件 |
|---|---|---|
| 唯一解 | \(\mathbf{b}\) 在像空間裡,且 \(A\) 沒有把兩個不同輸入壓成同一輸出(可逆) | \(\det(A)\neq 0\) |
| 無解 | \(\mathbf{b}\) 根本不在 \(A\) 能到達的範圍裡 | $\operatorname{rank}(A) < \operatorname{rank}([A\, |
| 無窮多解 | \(\mathbf{b}\) 在像空間裡,但 \(A\) 把某個方向壓扁了,整條線的輸入都映射到 \(\mathbf{b}\) | $\operatorname{rank}(A) = \operatorname{rank}([A\, |
直覺記法:
- 唯一解:機器是「一對一」的,還原只有一個答案。
- 無解: \(\mathbf{b}\) 是機器「製造不出來」的產品。
- 無窮多解:機器把一整條線壓成一個點,所以一大票輸入都對。
三、用 NumPy 看三種命運#
import numpy as np
# 命運 1:唯一解
A1 = np.array([[2.0, 1.0],
[1.0, -1.0]])
b1 = np.array([3.0, 0.0])
x1 = np.linalg.solve(A1, b1)
print(x1) # [1. 1.]
print(np.linalg.det(A1)) # -3.0,非奇異 → 可逆 → 唯一解
# 命運 2:無解(兩方程的係數成比例,但右邊不成比例)
A2 = np.array([[1.0, 2.0],
[2.0, 4.0]]) # rank = 1,把平面壓成一條線
b2 = np.array([1.0, 3.0]) # 不在 A2 的像(直線 y=2x)上
# np.linalg.solve(A2, b2) # LinAlgError: Singular matrix
# 命運 3:無窮多解
b3 = np.array([1.0, 2.0]) # 剛好落在像空間上
# solve 會報錯,但解其實存在一整條:x + 2y = 1
注意:NumPy 的 solve 只對「唯一解」給答案;遇到奇異矩陣它直接丟 LinAlgError,不會告訴你是無解還是無窮多解 —— 這正是我們需要幾何直覺的原因:電腦只會報錯,判斷命運要靠你。
四、兩種觀點:行圖與列圖#
同一個方程組有兩種畫法,兩種都要會。
行圖(row picture):每個方程是一條直線,解 = 交點。這是高中的畫法。
列圖(column picture):把方程組讀成
問題變成:「 \(\mathbf{b}\) 要怎麼用兩個行向量 線性組合 出來?」解 \((x,y)\) 就是組合的配方份量。
import matplotlib.pyplot as plt
from linalg_viz import plot_vectors, setup_axes
A = np.array([[2.0, 1.0],
[1.0, -1.0]])
b = np.array([3.0, 0.0])
x = np.linalg.solve(A, b)
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(11, 5))
# ---- 行圖:兩條直線的交點 ----
t = np.linspace(-1, 3, 100)
ax1.plot(t, 3 - 2*t, label=r"$2x+y=3$") # 第一條直線
ax1.plot(t, t - 1, label=r"$x-y=0$") # 第二條直線
ax1.plot(*x, "ko", ms=8, label=f"交點 = {x}")
ax1.set_title("行圖:解 = 直線交點")
ax1.legend(); ax1.grid(True); ax1.set_aspect("equal")
# ---- 列圖:向量的線性組合 ----
setup_axes(ax2, -1, 4, -2, 4)
plot_vectors(ax2, [A[:, 0], A[:, 1]], ["$a_1$", "$a_2$"])
plot_vectors(ax2, [b], ["$\\mathbf{b}$"], colors=["crimson"])
# 畫出組合路徑:先走 x 倍的 a1,再走 y 倍的 a2
ax2.annotate("", xy=x[0]*A[:, 0], xytext=(0, 0),
arrowprops=dict(arrowstyle="->", color="gray"))
ax2.annotate("", xy=b, xytext=x[0]*A[:, 0],
arrowprops=dict(arrowstyle="->", color="gray"))
ax2.set_title("列圖:解 = 組合配方 $(x, y)$")
plt.tight_layout()
plt.show()
行圖裡,若兩條直線 平行 → 無解;若兩條直線 重合 → 無窮多解。列圖裡,若 \(\mathbf{b}\) 不在兩個行向量張成的直線上 → 無解;若行向量共線且 \(\mathbf{b}\) 就在這條線上 → 無窮多解。兩種畫法說的是同一件事,只是切的角度不同。
五、把命運 2、3 也畫出來#
A2 = np.array([[1.0, 2.0],
[2.0, 4.0]]) # 兩行向量共線
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(11, 5))
# 無解:兩直線平行
t = np.linspace(-2, 2, 100)
ax1.plot(t, (1 - t)/2, label=r"$x+2y=1$")
ax1.plot(t, (3 - 2*t)/4, "--", label=r"$2x+4y=3$") # 平行,永不相交
ax1.set_title("命運 2:平行線 → 無解")
ax1.legend(); ax1.grid(True); ax1.set_aspect("equal")
# 無窮多解:兩直線重合
ax2.plot(t, (1 - t)/2, lw=3, alpha=0.6, label=r"$x+2y=1$")
ax2.plot(t, (2 - 2*t)/4, "--", label=r"$2x+4y=2$")
ax2.set_title("命運 3:重合線 → 無窮多解")
ax2.legend(); ax2.grid(True); ax2.set_aspect("equal")
plt.tight_layout(); plt.show()
對照列圖: \(A_2\) 的兩個行向量都指著同一方向,所以 \(A_2\) 的「可達範圍」只有一條直線 \(y=2x\) 。 \(\mathbf{b}_2=(1,3)\) 離這條線很遠 → 無解; \(\mathbf{b}_3=(1,2)\) 剛好在線上 → 無窮多解(任何滿足 \(x+2y=1\) 的 \((x,y)\) 都是配方)。
六、solve vs 人工判斷:實務守則#
def diagnose(A, b, tol=1e-10):
"""教學版:判斷 Ax=b 的命運(用秩,3.3 節正式介紹)"""
A, b = np.asarray(A, float), np.asarray(b, float)
rank_A = np.linalg.matrix_rank(A)
rank_Ab = np.linalg.matrix_rank(np.column_stack([A, b]))
n = A.shape[1]
if rank_A < rank_Ab:
return "無解:b 不在 A 的像空間"
if rank_A == n:
return "唯一解"
return f"無窮多解:自由變數有 {n - rank_A} 個"
print(diagnose([[2,1],[1,-1]], [3,0])) # 唯一解
print(diagnose([[1,2],[2,4]], [1,3])) # 無解
print(diagnose([[1,2],[2,4]], [1,2])) # 無窮多解
守則:
- 先
np.linalg.solve,能解就解(數值上也最穩,4.4 之後你會更體會)。 - 若丟出
LinAlgError: Singular matrix,不要急著改用inv,先用matrix_rank或畫圖判斷是無解還是無窮多解。 - 接近奇異(
det很小、條件數很大)時答案也不可靠,這留給第 9 章。
小結與練習#
- \(A\mathbf{x}=\mathbf{b}\) 的幾何意義:找出被 \(A\) 變換後會落在 \(\mathbf{b}\) 的那個輸入向量。
- 三種命運:唯一解( \(A\) 可逆且 \(\mathbf{b}\) 在像空間)、無解( \(\mathbf{b}\) 不可達)、無窮多解( \(\mathbf{b}\) 可達但 \(A\) 壓扁了某方向)。
- 行圖看直線交點,列圖看向量線性組合;奇異時「平行 vs 重合」「偏離 vs 落在線上」一一對應。
np.linalg.solve只處理唯一解;報錯時要用秩與圖形判斷真正的命運。
練習:
- 用行圖與列圖各畫一次 \(\begin{cases}x+y=2\\ x-y=0\end{cases}\) ,並用
solve驗證交點。 - 構造一個 \(2\times 2\) 的 \(A\) 與 \(\mathbf{b}\) 使方程組無解;再只改 \(\mathbf{b}\) 讓它變成無窮多解。畫圖確認。
- 命運 3 中, \(x+2y=1\) 的所有解構成一條直線。取兩個不同的解 \(\mathbf{x}_1,\mathbf{x}_2\) ,驗證 \(A(\mathbf{x}_1-\mathbf{x}_2)=\mathbf{0}\) —— 這條「解之差的方向」就是下一節的零空間伏筆。
- 寫一個迴圈隨機產生 100 組 \(2\times 2\) 的 \(A,\mathbf{b}\) ,統計
solve成功的比例,並用np.linalg.matrix_rank解釋為什麼幾乎所有隨機方程組都有唯一解。