跳轉到

1.4 第一次會話#

本節目標#

  • 理解 symbols() 的真正意義:宣告「符號」,不是指定變數值
  • 完成第一次展開、因式分解與 pretty printing
  • 學會精確的 Rational 運算
  • 認識兩個最常見的新手錯誤

symbols():符號不是變數#

在一般 Python 中,x 一定先被賦值才能使用:

x = 5
x + 1   # 6

但數學裡的 \(x\) 不是「某個值」,而是一個尚未指定的代數符號。SymPy 用 Symbol 物件來表達這個概念:

from sympy import symbols

x = symbols('x')
print(x + 1)        # x + 1   ← 不是 6,是保留原樣的表達式!

這行 x = symbols('x') 的意思是:「建立一個名叫 x 的符號物件,並用 Python 變數 x 指向它」。之後所有 SymPy 運算都會把 x 當成未知數處理。可以一次宣告多個:

x, y, z = symbols('x y z')

注意:Python 變數名與符號名不一定要相同,但初學時保持一致最不容易搞混。

第一次展開與因式分解#

學校數學最熟悉的操作來了——展開與因式分解,互為逆運算:

from sympy import symbols, expand, factor

x, y = symbols('x y')

expr = (x + y)**2
print(expand(expr))   # x**2 + 2*x*y + y**2

print(factor(x**2 + 2*x*y + y**2))   # (x + y)**2

也可以直接操作多項式:

print(expand((x + y)**3))          # x**3 + 3*x**2*y + 3*x*y**2 + y**3
print(factor(x**3 - 1))            # (x - 1)*(x**2 + x + 1)
print(factor(x**2 - 5*x + 6))      # (x - 2)*(x - 3)  ← 十字交乘一次搞定

SymPy 的輸出是標準化後的表達式,你可以把它存進變數、繼續運算:

e = expand((x + 1)*(x + 2))
print(e.subs(x, 3))    # 20  ← 代入 x = 3

Pretty printing 與 LaTeX 輸出#

在 Jupyter 中,SymPy 預設就會用排版好的數學式顯示結果。若在純終端機環境,可用 init_printing() 開啟文字模式的漂亮排版:

from sympy import init_printing, symbols, sqrt
init_printing()

x = symbols('x')
sqrt(2) / 3     # 顯示 √2 ─── 3(分數形式,而不是 2**(1/2)/3)

想要把結果放進報告或 LaTeX 文件,用 latex() 直接轉換:

from sympy import latex

print(latex((x + y)**2))   # (x + y)^{2}

輸出的 LaTeX 碼貼到任何支援數學排版的文件中,就會變成 \((x + y)^2\) 。

精確算術:Rational#

SymPy 中整數相除是精確的:

from sympy import Rational, sqrt

print(1/3)               # 0.3333333333333333  ← 這是 Python float!
print(Rational(1, 3))    # 1/3                 ← 這才是 SymPy 分數
print(Rational(1, 3) + Rational(1, 6))   # 1/2

常數也都是精確符號:

from sympy import pi, E

print(pi.evalf(20))     # 3.1415926535897932385(想要幾位就幾位)
print(E**2)             # exp(2),保持符號形式

第一個小實驗:展開並因式分解#

把本節所學串成完整流程——展開、代值、再分解:

from sympy import symbols, expand, factor

x = symbols('x')

# 步驟 1:展開
e = expand((x - 2)*(x - 3))
print(e)          # x**2 - 5*x + 6

# 步驟 2:代入檢驗(x = 10 時兩邊應相等)
print(e.subs(x, 10))              # 56
print((10 - 2)*(10 - 3))          # 56 ✓

# 步驟 3:因式分解還原
print(factor(e))                  # (x - 2)*(x - 3)

# 步驟 4:挑戰三次式
print(factor(x**3 - 8))           # (x - 2)*(x**2 + 2*x + 4)

常見新手錯誤#

錯誤 1:忘記宣告 symbols

from sympy import expand
print(expand((x + y)**2))   # NameError: name 'x' is not defined

修正:先 x, y = symbols('x y')。這是最常見的錯,因為 from sympy import * 加 init_session() 的環境中 x 已存在,讓人誤以為永遠存在。

錯誤 2:把 = 當成等式

from sympy import symbols, solve
x = symbols('x')
solve(x**2 - 4 = 0, x)   # SyntaxError!

= 在 Python 是賦值,不能出現在表達式裡。解方程時把等號移項寫成多項式:

print(solve(x**2 - 4, x))    # [-2, 2]

若真的需要表示「等式」這個數學物件,用 Eq:

from sympy import Eq

print(Eq(x**2, 4))           # Eq(x**2, 4),代表方程式 x² = 4
print(solve(Eq(x**2, 4), x)) # [-2, 2]

錯誤 3(順帶一提):混用 float

from sympy import sqrt, simplify

print(simplify(sqrt(2.0)**2))   # 2.0000000000000004 的化簡結果,仍是浮點數
print(simplify(sqrt(2)**2))     # 2 ✓ 精確

原則:在 SymPy 裡,整數與分數都用原樣寫,別手癢加上小數點。

動手實驗#

實驗 1:驗證乘法公式。用 expand 驗證 \((a+b)^2 = a^2 + 2ab + b^2\) 與 \((a-b)(a+b) = a^2 - b^2\) :

from sympy import symbols, expand

a, b = symbols('a b')
print(expand((a + b)**2))
print(expand((a - b)*(a + b)))

實驗 2:因式分解大挑戰。分解下列各式,並用 subs 代數值檢驗:

from sympy import symbols, factor

x = symbols('x')
print(factor(x**2 - 9))
print(factor(x**2 + 7*x + 12))
print(factor(x**4 - 16))

實驗 3:故意犯錯一次——把本節「常見新手錯誤」的錯誤碼原樣執行,讀懂錯誤訊息後修正。能看懂錯誤訊息是重要的程式能力。

實驗 4:用 latex() 把 factor(x**3 - 8) 的結果轉成 LaTeX,貼到 [markdown 編輯器] 中欣賞 \((x - 2)(x^2 + 2x + 4)\) 的排版。

小結與練習#

  • symbols() 宣告的是「代數符號」,Python 變數只是指向它的名字。
  • expand() 展開、factor() 因式分解、subs() 代值,三者構成多項式的基本操作循環。
  • init_printing() 開啟漂亮排版,latex() 輸出 LaTeX 碼;Rational 保證精確分數運算。
  • 新手三大錯:忘記宣告 symbols、= 與 Eq 混淆、混入 float 破壞精確性。

練習

  1. 宣告符號 \(a, b, c\) ,展開 \((a + b + c)^2\) ,數一數結果有幾項。
  2. 因式分解 \(x^3 + x^2 + x + 1\) (提示:分組後可分解,答案含 \((x+1)\) )。
  3. 用 Rational 計算 \(\frac{1}{2} + \frac{1}{3} + \frac{1}{6}\) ,確認結果為精確值 \(1\) 。
  4. 寫一段程式:展開 \((x+1)^5\) ,然後代 \(x=2\) 驗證等於 \(3^5 = 243\) 。