跳轉到

6.1 矩陣:把數字排成表格,讓計算變成一套規則#

本節目標#

  • 理解矩陣是什麼、為什麼要發明矩陣
  • 學會用 SymPy 的 Matrix() 建構矩陣,並做加法、純量乘法、矩陣乘法
  • 親手算一次 \(2 \times 3\) 乘 \(3 \times 2\) 的矩陣乘法,與電腦結果對照
  • 認識常用的特殊矩陣:eye()、zeros()、ones()
  • 用實驗驗證「矩陣乘法不滿足交換律」

矩陣是什麼?#

矩陣(matrix)就是把數字排成長方形的表格。一個 \(m \times n\) 矩陣有 \(m\) 列、 \(n\) 行:

\[ A = \begin{pmatrix} a_{11} & a_{12} \\ a_{21} & a_{22} \end{pmatrix} \]

但矩陣不只是表格——它是線性變換的編碼,也是聯立方程組的壓縮。例如方程組

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

可以壓縮成 \(A\mathbf{x} = \mathbf{b}\) ,其中 \(A = \begin{pmatrix} 2 & 3 \\ 1 & -1 \end{pmatrix}\) 、 \(\mathbf{x} = \begin{pmatrix} x \\ y \end{pmatrix}\) 、 \(\mathbf{b} = \begin{pmatrix} 8 \\ -1 \end{pmatrix}\) 。

一旦方程組變成 \(A\mathbf{x} = \mathbf{b}\) ,我們就能用同一套規則處理所有方程組,這正是矩陣的威力。

用 Matrix() 建構矩陣#

from sympy import Matrix

# 用「列的串列」建構:每個內層串列是一列
A = Matrix([[2, 3],
            [1, -1]])
print(A)

# 也可以只給一層串列,SymPy 會當成「行向量」(直的)
v = Matrix([1, 2])
print(v)

查詢矩陣的形狀與取元素:

print(A.shape)   # (2, 2):2 列 2 行
print(A[0, 0])   # 2:注意索引從 0 開始!
print(A[1, 0])   # 1:第 2 列、第 1 行
print(A[:, 0])   # 取出第 1 行:Matrix([[2], [1]])
print(A[0, :])   # 取出第 1 列:Matrix([[2, 3]])

A[i, j] 的順序是「列在前、行在後」,就像座標 \((y, x)\) 一樣,初學者最常在這裡搞混,請多寫幾次確認。

矩陣加法與純量乘法#

兩個同形狀的矩陣可以相加:對應元素相加。矩陣可以乘上一個數(純量):每個元素都乘上那個數。

\[ \begin{pmatrix} 1 & 2 \\ 3 & 4 \end{pmatrix} + \begin{pmatrix} 5 & 6 \\ 7 & 8 \end{pmatrix} = \begin{pmatrix} 6 & 8 \\ 10 & 12 \end{pmatrix}, \qquad 2 \cdot \begin{pmatrix} 1 & 2 \\ 3 & 4 \end{pmatrix} = \begin{pmatrix} 2 & 4 \\ 6 & 8 \end{pmatrix} \]
from sympy import Matrix, Rational

B = Matrix([[1, 2], [3, 4]])
C = Matrix([[5, 6], [7, 8]])

print(B + C)     # Matrix([[6, 8], [10, 12]])
print(2 * B)     # Matrix([[2, 4], [6, 8]])
print(B - C)     # Matrix([[-4, -4], [-4, -4]])

# 純量也可以是符號或精確分數
from sympy import Symbol
k = Symbol('k')
print(k * B)     # Matrix([[k, 2*k], [3*k, 4*k]])
print(Rational(1, 2) * B)  # Matrix([[1/2, 1], [3/2, 2]])

矩陣乘法:手算一次再對照電腦#

矩陣乘法不像加法那麼直覺。 \(m \times n\) 矩陣乘上 \(n \times p\) 矩陣,得到 \(m \times p\) 矩陣,其中結果的第 \(i\) 列第 \(j\) 行元素,是「左矩陣的第 \(i\) 列」與「右矩陣的第 \(j\) 行」的對應相乘再相加(內積)。

我們來手算一個 \(2 \times 3\) 乘 \(3 \times 2\) 的例子:

\[ A = \begin{pmatrix} 1 & 2 & 3 \\ 4 & 5 & 6 \end{pmatrix}, \quad B = \begin{pmatrix} 7 & 8 \\ 9 & 10 \\ 11 & 12 \end{pmatrix} \]

結果的每個元素:

  • \(c_{11} = 1 \cdot 7 + 2 \cdot 9 + 3 \cdot 11 = 7 + 18 + 33 = 58\)
  • \(c_{12} = 1 \cdot 8 + 2 \cdot 10 + 3 \cdot 12 = 8 + 20 + 36 = 64\)
  • \(c_{21} = 4 \cdot 7 + 5 \cdot 9 + 6 \cdot 11 = 28 + 45 + 66 = 139\)
  • \(c_{22} = 4 \cdot 8 + 5 \cdot 10 + 6 \cdot 12 = 32 + 50 + 72 = 154\)

用 SymPy 驗證:

from sympy import Matrix

A = Matrix([[1, 2, 3],
            [4, 5, 6]])
B = Matrix([[7, 8],
            [9, 10],
            [11, 12]])

print(A.shape, B.shape)  # (2, 3) (3, 2):內側維度相同,可以相乘
print(A * B)             # Matrix([[58, 64], [139, 154]])
print((A * B).shape)     # (2, 2)

一個容易忽略的細節:A * B 在 SymPy 中就是矩陣乘法(不是逐元素相乘)。若想做逐元素乘法(Hadamard 積),要用 A.multiply_elementwise(B)。

常用的特殊矩陣#

from sympy import eye, zeros, ones, Matrix

print(eye(3))       # 3x3 單位矩陣:對角線是 1,其他是 0
print(zeros(2, 3))  # 2x3 全零矩陣
print(ones(2, 2))   # 2x2 全 1 矩陣

輸出:

Matrix([[1, 0, 0], [0, 1, 0], [0, 0, 1]])
Matrix([[0, 0, 0], [0, 0, 0]])
Matrix([[1, 1], [1, 1]])

單位矩陣 \(I\) 在矩陣世界裡扮演「1」的角色:任何矩陣乘上大小相符的單位矩陣都不變。

I3 = eye(3)
A = Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 10]])
print(A * I3 == A)   # True
print(I3 * A == A)   # True

轉置#

轉置(transpose)把矩陣的「列變行、行變列」,記作 \(A^T\) :

\[ \begin{pmatrix} 1 & 2 & 3 \\ 4 & 5 & 6 \end{pmatrix}^T = \begin{pmatrix} 1 & 4 \\ 2 & 5 \\ 3 & 6 \end{pmatrix} \]
from sympy import Matrix

A = Matrix([[1, 2, 3], [4, 5, 6]])
print(A.T)           # Matrix([[1, 4], [2, 5], [3, 6]])
print(A.T.shape)     # (3, 2)

# 轉置的轉置回到原矩陣
print(A.T.T == A)    # True

轉置有兩個重要恆等式(可以自己用 SymPy 驗證):

\[ (A + B)^T = A^T + B^T, \qquad (AB)^T = B^T A^T \]

注意 \((AB)^T\) 是反過來的,這一點在 6.4 節推導最小平方法時會用到。

動手實驗:矩陣乘法滿足交換律嗎?#

實數乘法滿足交換律: \(3 \times 5 = 5 \times 3\) 。矩陣乘法呢?讓我們直接測試:

from sympy import Matrix

A = Matrix([[1, 2],
            [0, 1]])
B = Matrix([[1, 0],
            [3, 1]])

print(A * B)             # Matrix([[7, 2], [3, 1]])
print(B * A)             # Matrix([[1, 2], [3, 5]])
print(A * B == B * A)    # False ← 不交換!

更系統化地,用隨機矩陣大量測試:

import random
from sympy import Matrix

count = 0
for _ in range(100):
    A = Matrix(2, 2, lambda i, j: random.randint(-5, 5))
    B = Matrix(2, 2, lambda i, j: random.randint(-5, 5))
    if A * B != B * A:
        count += 1
print(f"100 組隨機矩陣中有 {count} 組 A*B != B*A")

執行後你會發現:幾乎每一組都不相等。能交換的矩陣是少數特例(例如單位矩陣、或 \(A\) 自己乘自己)。

再做一個實驗確認「形狀不合就不能交換」:

A = Matrix([[1, 2, 3], [4, 5, 6]])   # 2x3
B = Matrix([[1, 0], [0, 1], [1, 1]]) # 3x2
print((A * B).shape)  # (2, 2):可以乘
try:
    B * A             # 3x2 乘 2x3 其實也可以,得到 3x3!
    print((B * A).shape)  # (3, 3):形狀都不同了,怎麼可能相等
except Exception as e:
    print(e)

為什麼不交換? 直觀地說,矩陣代表「變換」: \(AB\) 是「先做 \(B\) 、再做 \(A\) 」的複合變換。「先旋轉再平移」和「先平移再旋轉」結果不同——順序當然重要。

小結與練習#

本節重點:

  • 矩陣是把數字排成表格的物件,能壓縮聯立方程組、編碼線性變換
  • Matrix([[...], [...]]) 逐列建構;A[i, j] 取元素,索引從 0 開始
  • 加法對應元素相加;矩陣乘法是「列乘行」的內積,形狀 \((m \times n)(n \times p) \to m \times p\)
  • eye() 單位矩陣、zeros() 零矩陣、ones() 全 1 矩陣
  • A.T 轉置,且 \((AB)^T = B^T A^T\)
  • 矩陣乘法不滿足交換律

練習:

  1. 用 SymPy 建構 \(A = \begin{pmatrix} 2 & 0 \\ 1 & 3 \end{pmatrix}\) 、 \(B = \begin{pmatrix} 1 & 1 \\ 0 & 2 \end{pmatrix}\) ,計算 \(A B\) 與 \(B A\) ,並驗證兩者不相等。
  2. 手算 \(\begin{pmatrix} 1 & 2 \\ 3 & 4 \end{pmatrix} \begin{pmatrix} 0 & 1 \\ 1 & 0 \end{pmatrix}\) ,再用 SymPy 對照。你觀察到這個右乘矩陣對 \(A\) 產生了什麼效果?(提示:看行的變化)
  3. 驗證轉置恆等式 \((AB)^T = B^T A^T\) :任取兩個 \(2 \times 2\) 矩陣,用 == 檢查兩邊是否相等。
  4. 建構 \(A = \begin{pmatrix} 1 & 2 \\ 2 & 4 \end{pmatrix}\) ,計算 \(A \cdot \begin{pmatrix} 2 \\ -1 \end{pmatrix}\) 與 \(A \cdot \begin{pmatrix} 1 \\ -2 \end{pmatrix}\) ,你發現了什麼?這和 6.3 節的特徵向量有關。
  5. 寫一個迴圈,找出所有滿足 \(A B = B A\) 的 \(2 \times 2\) 矩陣對(其中 \(A\) 固定為 \(\begin{pmatrix} 1 & 1 \\ 0 & 1 \end{pmatrix}\) , \(B\) 的元素取 \(0, 1, 2\) ),觀察這些 \(B\) 有什麼共同特徵。