第 3 章 一元方程與聯立方程¶

本章重點:

  • 了解「方程」的觀念與解的意義。
  • 使用 SymPy 解一元一次、二次以及一般非線性方程。
  • 使用 SymPy 解二元、三元聯立一次方程組。
  • 認識參數方程:解的個數如何隨參數改變。
  • 理解「代數解」與「數值近似解」的差別。

我們會一邊用 SymPy 計算,一邊連結到高中與大一程度的數學觀念, 並透過幾個啟發性的例子,體會方程與函數圖形的關係。

3.1 匯入 SymPy 與符號設定¶

先匯入 SymPy,並建立需要的符號變數:

In [1]:
import sympy as sp
sp.init_printing()

x, y, z = sp.symbols('x y z')
a, b, c = sp.symbols('a b c')
x, y, z, a, b, c
Out[1]:
$\displaystyle \left( x, \ y, \ z, \ a, \ b, \ c\right)$

3.2 一元一次方程¶

最基本的方程是一元一次方程,例如:

$2x + 3 = 7$。

在 SymPy 中,我們用 sp.Eq(左邊, 右邊) 建立方程,用 sp.solve 求解:

In [2]:
eq1 = sp.Eq(2*x + 3, 7)
solution1 = sp.solve(eq1, x)
eq1, solution1
Out[2]:
$\displaystyle \left( 2 x + 3 = 7, \ \left[ 2\right]\right)$

如果方程是已經寫成「= 0」的形式,也可以直接把表達式丟進 solve, SymPy 會理解成要解這個表達式等於 0 的根:

In [3]:
eq2_expr = 3*x - 9  # 代表 3x - 9 = 0
sp.solve(eq2_expr, x)
Out[3]:
$\displaystyle \left[ 3\right]$

3.3 一元二次方程¶

考慮一般二次方程:

$x^2 - 5x + 6 = 0$。

解這個方程的方法很多:配方法、公式法、因式分解等等。SymPy 則可以直接幫我們求解:

In [4]:
eq_quad = sp.Eq(x**2 - 5*x + 6, 0)
sp.solve(eq_quad, x)
Out[4]:
$\displaystyle \left[ 2, \ 3\right]$

也可以不顯示 Eq,直接解多項式的根:

In [5]:
sp.solve(x**2 - 5*x + 6, x)
Out[5]:
$\displaystyle \left[ 2, \ 3\right]$

如果系數帶有參數(例如 $ax^2 + bx + c = 0$), SymPy 會給出包含參數的解:

In [6]:
a, b, c = sp.symbols('a b c')
eq_general = a*x**2 + b*x + c
sp.solve(eq_general, x)
Out[6]:
$\displaystyle \left[ \frac{- b - \sqrt{- 4 a c + b^{2}}}{2 a}, \ \frac{- b + \sqrt{- 4 a c + b^{2}}}{2 a}\right]$

你可以看到熟悉的求根公式:

$x = \dfrac{-b \pm \sqrt{b^2 - 4ac}}{2a}$。

這讓我們理解:教科書上的公式其實可以被電腦符號運算系統「重新推導」出來。

3.4 一般非線性方程與 solveset¶

除了多項式方程之外,我們也會遇到包含三角函數、指數、對數等的方程。 例如:

$\sin x = \dfrac{1}{2}$。

這種方程通常有無限多解,使用 solve 可能只得到部分解, 這時適合改用 solveset,它會以集合形式給出一般解:

In [7]:
eq_trig = sp.Eq(sp.sin(x), sp.Rational(1, 2))
sp.solveset(eq_trig, x)
Out[7]:
$\displaystyle \left\{2 n \pi + \frac{\pi}{6}\; \middle|\; n \in \mathbb{Z}\right\} \cup \left\{2 n \pi + \frac{5 \pi}{6}\; \middle|\; n \in \mathbb{Z}\right\}$

solveset 對於簡單的代數方程也適用:

In [8]:
sp.solveset(x**2 - 5*x + 6, x)
Out[8]:
$\displaystyle \left\{2, 3\right\}$

3.5 聯立一次方程組:solve¶

考慮二元一次聯立方程:

\begin{cases} x + y = 3 \\ x - y = 1 \end{cases}

在 SymPy 中,我們可以把每一條方程寫成表達式(默認等於 0)或 Eq, 再用 sp.solve([方程列表], [未知數列表]):

In [9]:
x, y = sp.symbols('x y')
solutions_2 = sp.solve([x + y - 3, x - y - 1], [x, y])
solutions_2
Out[9]:
$\displaystyle \left\{ x : 2, \ y : 1\right\}$

再看一個 3 元聯立系統:

\begin{cases} x + y + z = 6 \\ 2x - y + z = 3 \\ x + 2y - z = 4 \end{cases}

In [10]:
x, y, z = sp.symbols('x y z')
solutions_3 = sp.solve([
    x + y + z - 6,
    2*x - y + z - 3,
    x + 2*y - z - 4
], [x, y, z])
solutions_3
Out[10]:
$\displaystyle \left\{ x : \frac{11}{7}, \ y : \frac{16}{7}, \ z : \frac{15}{7}\right\}$

3.6 使用矩陣觀點解線性方程¶

線性代數中,我們把線性方程組寫成矩陣形式 $A\vec{x} = \vec{b}$。 SymPy 的 Matrix 提供了 rref()(列簡化階梯形)、解線性系統等工具。

以剛才的三元方程為例:

In [11]:
A = sp.Matrix([
    [1, 1, 1],
    [2, -1, 1],
    [1, 2, -1]
])
b_vec = sp.Matrix([6, 3, 4])

print("係數矩陣 A:")
display(A)
print("常數向量 b:")
display(b_vec)

print("增廣矩陣 [A|b] 的列簡化:")
Aug = A.row_join(b_vec)
Aug_rref, pivots = Aug.rref()
display(Aug_rref)
pivots
係數矩陣 A:
$\displaystyle \left[\begin{matrix}1 & 1 & 1\\2 & -1 & 1\\1 & 2 & -1\end{matrix}\right]$
常數向量 b:
$\displaystyle \left[\begin{matrix}6\\3\\4\end{matrix}\right]$
增廣矩陣 [A|b] 的列簡化:
$\displaystyle \left[\begin{matrix}1 & 0 & 0 & \frac{11}{7}\\0 & 1 & 0 & \frac{16}{7}\\0 & 0 & 1 & \frac{15}{7}\end{matrix}\right]$
Out[11]:
$\displaystyle \left( 0, \ 1, \ 2\right)$

從列簡化的結果中,我們可以看出:

  • 如果主對角線上每一列的主元都存在,則有唯一解。
  • 若出現全 0 = 非 0 的行,則無解。
  • 若有自由變數,則有無限多解(解集合是一個線性子空間或平移的子空間)。

在後續的線性代數課程中,你會更深入地使用這種矩陣觀點。

3.7 代數解 vs 數值近似解:nsolve¶

有些方程難以用「封閉公式」表示解(特別是高次方程或含有多種特殊函數), 這時我們會尋求數值近似解。

SymPy 提供 nsolve 來做數值求解,需要給定一個「初始猜測值」:

In [12]:
from sympy import nsolve

eq_nonlin = sp.Eq(sp.exp(x) - x**2, 0)
eq_nonlin
Out[12]:
$\displaystyle - x^{2} + e^{x} = 0$

我們可以嘗試不同的初始值,找到不同的解:

In [13]:
root1 = nsolve(sp.exp(x) - x**2, 0)   # 以 0 附近為起點
root2 = nsolve(sp.exp(x) - x**2, 2)   # 以 2 附近為起點
root1, root2
Out[13]:
$\displaystyle \left( -0.703467422498392, \ -0.703467422498392\right)$

這裡得到的是小數近似值。若需要更高精度,可以指定 nsolve 的 prec 或再用 sp.N 增加位數。

3.8 啟發性例子一:兩個函數的交點 = 方程解¶

考慮函數:

$y_1 = x^2$ 與 $y_2 = 2x + 3$。

它們的交點滿足:

$x^2 = 2x + 3$。

也就是:

$x^2 - 2x - 3 = 0$。

先用 SymPy 找出交點的 $x$ 值,再思考對應的圖形關係。

In [14]:
x = sp.symbols('x')
eq_cross = sp.Eq(x**2, 2*x + 3)
xs = sp.solve(eq_cross, x)
xs
Out[14]:
$\displaystyle \left[ -1, \ 3\right]$

若你的環境支援 sp.plot(如 Jupyter Notebook),可以畫圖輔助觀察:

In [15]:
# 此繪圖在某些線上環境可能無法顯示,但在本機 Jupyter 中通常可正常使用
sp.plot(x**2, 2*x + 3, (x, -5, 5), legend=True)
No description has been provided for this image
Out[15]:
<sympy.plotting.backends.matplotlibbackend.matplotlib.MatplotlibBackend at 0x7f35cecb5400>

思考:

  • 方程的「解」與圖形上的「交點」有什麼關係?
  • 如果把右邊改成 $y_2 = 2x - 3$,交點個數會如何改變? (試著自己改程式,重新求解並繪圖。)

3.9 啟發性例子二:非線性聯立方程¶

考慮平面上的兩條曲線:

$\begin{cases} x^2 + y^2 = 5 \\ y = x + 1 \end{cases}$

幾何意義:一個圓與一條直線的交點。使用 solve 來求解:

In [16]:
x, y = sp.symbols('x y')
solutions_circle = sp.solve([
    x**2 + y**2 - 5,
    y - (x + 1)
], [x, y])
solutions_circle
Out[16]:
$\displaystyle \left[ \left( -2, \ -1\right), \ \left( 1, \ 2\right)\right]$

這個例子展示了:聯立方程不一定要「一次」,也可以是非線性的曲線與曲線相交問題。 你可以試著修改常數 5 或右邊的線性關係,觀察交點數量如何改變。

3.10 啟發性例子三:參數方程與解的個數¶

考慮方程:

$x^2 - a x + 1 = 0$。

這是一個帶參數 $a$ 的二次方程。解為:

In [17]:
a = sp.symbols('a')
expr_param = x**2 - a*x + 1
sp.solve(expr_param, x)
Out[17]:
$\displaystyle \left[ \frac{a}{2} - \frac{\sqrt{a^{2} - 4}}{2}, \ \frac{a}{2} + \frac{\sqrt{a^{2} - 4}}{2}\right]$

判別式為 $D = a^2 - 4$,解的型態隨 $a$ 不同而變:

  • 若 $|a| > 2$,則兩個不等的實根。
  • 若 $|a| = 2$,則一個重根。
  • 若 $|a| < 2$,則兩個共軛複數根(無實根)。

我們可以用 SymPy 來驗證判別式:

In [18]:
D = sp.discriminant(expr_param, x)
D
Out[18]:
$\displaystyle a^{2} - 4$

你可以試著代入不同的 $a$ 值,例如 a=0, 1, 2, 3,觀察實根的個數:

In [19]:
for val in [0, 1, 2, 3, -3]:
    sol = sp.solve(expr_param.subs(a, val), x)
    print(f"a = {val} 時,解 = {sol}")
a = 0 時,解 = [-I, I]
a = 1 時,解 = [1/2 - sqrt(3)*I/2, 1/2 + sqrt(3)*I/2]
a = 2 時,解 = [1]
a = 3 時,解 = [3/2 - sqrt(5)/2, sqrt(5)/2 + 3/2]
a = -3 時,解 = [-3/2 - sqrt(5)/2, -3/2 + sqrt(5)/2]

這個例子讓我們看到「參數」對方程解的影響, 在許多應用問題(如物理、工程、經濟模型)中,這種思維非常重要。

3.11 本章小結¶

本章你學到了:

  • 如何用 Eq 與 solve 解一元一次、二次及一般代數方程。
  • 使用 solveset 處理具有無限多解的方程,尤其是三角方程。
  • 使用 solve 解二元、三元聯立方程,並搭配 Matrix 理解其線性代數結構。
  • 使用 nsolve 對難以求得封閉解的方程做數值近似。
  • 從幾何觀點理解方程解(曲線交點)、以及參數如何影響解的個數。

請記得:

  • 方程的「解」不只是一個數字,更對應到幾何圖形、物理或現實情境中的某種狀態。
  • SymPy 幫助我們快速求解與實驗,但理解條件、參數與解的意義仍需你的數學直覺與判斷。

3.12 練習題¶

請在本 Notebook 中新增儲存格,試著完成以下練習:

  1. 一元一次方程
    (a) 使用 Eq 解方程 $3x - 7 = 2x + 5$。
    (b) 將方程改寫為「= 0」的形式,再直接用 solve(表達式, x) 解一次,確認結果相同。

  2. 一元二次方程
    使用 solve 解下列方程:
    (a) $x^2 + 4x + 4 = 0$
    (b) $2x^2 - 3x - 2 = 0$
    並觀察解的型態(重根、兩實根、無實根)。

  3. 三角方程與 solveset
    (a) 使用 solveset 解 $\sin x = 0$。
    (b) 使用 solveset 解 $\cos x = \dfrac{1}{2}$。
    思考:這些解如何在單位圓上解釋?

  4. 二元聯立方程
    解聯立方程:
    $\begin{cases} 2x + 3y = 7 \\ x - y = 1 \end{cases}$
    並檢查解是否滿足兩條方程(可用 subs 代入檢查)。

  5. 矩陣方法
    令 $\begin{cases} x + 2y + 3z = 1 \\ 2x - y + z = 0 \\ 3x + y - z = 4 \end{cases}$
    (a) 寫出係數矩陣 $A$ 與常數向量 $b$。
    (b) 使用 Matrix 形成增廣矩陣,計算 rref()。
    (c) 比較 rref() 的結果與 solve 的解是否一致。

  6. 數值解 nsolve
    (a) 使用 nsolve 解方程 $\cos x = x$,試著從不同初始值出發(例如 0、1、2),觀察找到的解。
    (b) 試著解 $e^{-x} = x$,同樣比較不同初始值(例如 0、1、2)。

  7. 參數與解的個數(加分題)
    (a) 考慮方程 $x^2 + 2ax + 1 = 0$,計算其判別式並分別討論 $a > 0$、$a = 0$、$a < 0$ 的解的型態。
    (b) 寫一小段文字說明:為什麼「判別式」能決定二次方程的解的個數與型態?

完成這些練習後,你應該對『方程』與『解』有更深入的直觀,也更熟悉如何利用 SymPy 作為解題與探索的工具。

回首頁