跳轉到

3.1 方程組即變換: \(A\mathbf{x}=\mathbf{b}\) 的三種命運#

本節目標#

  • 把「解聯立方程式」重新理解成「找一個向量,讓變換後落在 \(\mathbf{b}\) 上」
  • 認識 \(A\mathbf{x}=\mathbf{b}\) 的三種命運:唯一解、無解、無窮多解
  • 學會 行圖(row picture,直線交點)與 列圖(column picture,向量組合)兩種觀點,並都畫出來
  • 會用 np.linalg.solve 解方程組,並在電腦說「解不存在」時自己判斷原因

一、先換一副眼鏡:方程組是「找回原向量」#

高中我們這樣寫方程組:

\[ \begin{cases} 2x + y = 3 \\ x - y = 0 \end{cases} \]

現在換一種寫法 —— 把係數收進矩陣、未知數收進向量:

\[ \begin{bmatrix} 2 & 1 \\ 1 & -1 \end{bmatrix} \begin{bmatrix} x \\ y \end{bmatrix} = \begin{bmatrix} 3 \\ 0 \end{bmatrix} \]

也就是 \(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):把方程組讀成

\[ x \begin{bmatrix} 2 \\ 1 \end{bmatrix} + y \begin{bmatrix} 1 \\ -1 \end{bmatrix} = \begin{bmatrix} 3 \\ 0 \end{bmatrix} \]

問題變成:「 \(\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]))   # 無窮多解

守則:

  1. 先 np.linalg.solve,能解就解(數值上也最穩,4.4 之後你會更體會)。
  2. 若丟出 LinAlgError: Singular matrix,不要急著改用 inv,先用 matrix_rank 或畫圖判斷是無解還是無窮多解。
  3. 接近奇異(det 很小、條件數很大)時答案也不可靠,這留給第 9 章。

小結與練習#

  • \(A\mathbf{x}=\mathbf{b}\) 的幾何意義:找出被 \(A\) 變換後會落在 \(\mathbf{b}\) 的那個輸入向量。
  • 三種命運:唯一解( \(A\) 可逆且 \(\mathbf{b}\) 在像空間)、無解( \(\mathbf{b}\) 不可達)、無窮多解( \(\mathbf{b}\) 可達但 \(A\) 壓扁了某方向)。
  • 行圖看直線交點,列圖看向量線性組合;奇異時「平行 vs 重合」「偏離 vs 落在線上」一一對應。
  • np.linalg.solve 只處理唯一解;報錯時要用秩與圖形判斷真正的命運。

練習:

  1. 用行圖與列圖各畫一次 \(\begin{cases}x+y=2\\ x-y=0\end{cases}\) ,並用 solve 驗證交點。
  2. 構造一個 \(2\times 2\) 的 \(A\) 與 \(\mathbf{b}\) 使方程組無解;再只改 \(\mathbf{b}\) 讓它變成無窮多解。畫圖確認。
  3. 命運 3 中, \(x+2y=1\) 的所有解構成一條直線。取兩個不同的解 \(\mathbf{x}_1,\mathbf{x}_2\) ,驗證 \(A(\mathbf{x}_1-\mathbf{x}_2)=\mathbf{0}\) —— 這條「解之差的方向」就是下一節的零空間伏筆。
  4. 寫一個迴圈隨機產生 100 組 \(2\times 2\) 的 \(A,\mathbf{b}\) ,統計 solve 成功的比例,並用 np.linalg.matrix_rank 解釋為什麼幾乎所有隨機方程組都有唯一解。