2.1 多項式運算#
本節目標#
- 理解 SymPy 中符號多項式與 Python 數值運算的差異
- 熟練使用
expand()、factor()進行展開與因式分解 - 學會
collect()依變數整理多項式 - 認識
Poly類,掌握degree()、coeffs()等多項式檢視工具 - 透過 \((x+1)^{10}\) 的展開觀察二項式係數
從數學開始:什麼是多項式運算#
多項式是形如
的表達式。多項式的加減乘都封閉於多項式集合內,但除法會產生有理式(下一節的主題)。
兩個重要的方向性操作:
- 展開: \((x+1)^2 \to x^2 + 2x + 1\) ,把乘積攤開成標準型(升冪或降冪排列)
- 因式分解: \(x^2 + 2x + 1 \to (x+1)^2\) ,把標準型還原為乘積
這兩個操作互為逆操作(在可分解的範圍內),是代數運算的基本功。
基本運算:加、減、乘#
SymPy 中用 Symbol 宣告符號後,多項式就像普通的變數一樣做運算:
from sympy import Symbol, expand
x = Symbol('x')
p = x**2 + 3*x + 2
q = x - 5
print(p + q) # x**2 + 4*x - 3
print(p - q) # x**2 + 2*x + 7
print(p * q) # x**3 - 2*x**2 - 13*x - 10
注意:乘出來的結果 SymPy 未必自動展開。上面 p * q 直接印出已展開的樣子,是因為這個例子足夠簡單;遇到 \((x+1)^{10}\) 這類表達式,就需要明確呼叫 expand()。
expr = (x + 1) * (x + 2) * (x + 3)
print(expr) # (x + 3)*(x + 1)*(x + 2) —— 保持未展開
print(expand(expr)) # x**3 + 6*x**2 + 11*x + 6
expand():展開#
expand() 會把所有乘積與冪次攤開:
from sympy import symbols, expand
x, y = symbols('x y')
print(expand((x + y)**3))
# x**3 + 3*x**2*y + 3*x*y**2 + y**3
print(expand((x - 2)*(x**2 + 2*x + 4)))
# x**3 - 8
一個經典的驗證:平方差公式與和的平方公式。
from sympy import simplify
lhs1 = expand((x + y)**2) - (x**2 + 2*x*y + y**2)
lhs2 = expand((x + y)*(x - y)) - (x**2 - y**2)
print(lhs1) # 0
print(lhs2) # 0
結果為 0 就代表兩邊相等——這是後面章節「驗證恆等式」的基本手法。
factor():因式分解#
factor() 是 expand() 的反向操作:
from sympy import factor
print(factor(x**3 - x)) # x*(x - 1)*(x + 1)
print(factor(x**2 + 2*x + 1)) # (x + 1)**2
print(factor(x**4 - 16)) # (x - 2)*(x + 2)*(x**2 + 4)
整數係數 vs 有理係數#
factor() 預設在有理數域 \(\mathbb{Q}\) 上分解。有些多項式在整數係數下無法分解,但允許有理係數後就可以:
from sympy import Rational
# 整數係數下:x**2 + x + 1 在有理數域不可約
print(factor(x**2 + x + 1)) # x**2 + x + 1(不動)
# 但配合擴充域可以分解(此處僅示意)
from sympy import sqrt
print(factor(x**2 - 2)) # x**2 - 2(根是 ±√2,非有理數)
print(factor(x**2 - 2, extension=sqrt(2)))
# (x - sqrt(2))*(x + sqrt(2))
有理係數的多項式也能正常處理:
p = Rational(1, 2)*x**2 - Rational(3, 2)*x + 1
print(factor(p)) # (x - 2)*(x - 1)/2
collect():依變數整理#
當表達式含多個符號,想把它看成「對 \(x\) 的多項式」整理時,用 collect():
from sympy import collect
a, b = symbols('a b')
expr = a*x**2 + b*x**2 + a*x - b*x + a + b
print(collect(expr, x))
# x**2*(a + b) + x*(a - b) + a + b
collect() 常用在推導過程中,把某個變數的係數結構看清楚。搭配 .coeff() 可以取出特定次方的係數:
collected = collect(expr, x)
print(collected.coeff(x, 2)) # a + b
print(collected.coeff(x, 0)) # a + b
Poly 類:把多項式當物件#
Poly 類把表達式正式轉成「多項式物件」,提供一系列查詢方法:
from sympy import Poly
p = Poly(x**3 - 2*x + 1, x)
print(p.degree()) # 3(最高次方)
print(p.coeffs()) # [1, -2, 1](所有非零係數,由高到低)
print(p.monoms()) # [(3,), (1,), (0,)](對應的次方)
print(p.all_coeffs()) # [1, 0, -2, 1](含零係數的完整係數列)
Poly 很適合用來寫「多項式演算法」,例如判斷是否為多項式、取首項係數:
print(p.is_multivariate()) # False
print(p.LC()) # 1(領導係數)
範例: \((x+1)^{10}\) 與二項式係數#
\((x+1)^n\) 展開後的係數就是二項式係數 \(\binom{n}{k}\) 。用 SymPy 驗證:
from sympy import symbols, expand
x = symbols('x')
p = expand((x + 1)**10)
print(p)
# x**10 + 10*x**9 + 45*x**8 + 120*x**7 + 210*x**6 + 252*x**5
# + 210*x**4 + 120*x**3 + 45*x**2 + 10*x + 1
係數列 \(1, 10, 45, 120, 210, 252, \ldots\) 正是巴斯卡三角形第 10 列。用 Poly 抽出係數與 binomial 對照:
from sympy import Poly, binomial
coeffs = Poly(p, x).all_coeffs() # 由高次到低次
n = 10
check = [binomial(n, k) for k in range(n, -1, -1)]
print(coeffs == check) # True
也可以直接驗證一般公式:
k = symbols('k', integer=True)
from sympy import summation
total = summation(binomial(10, k), (k, 0, 10))
print(total) # 1024 = 2**10,即 (1+1)**10
動手實驗:探討 \((x-1)(x-2)\cdots(x-n)\)#
連乘 \((x-1)(x-2)\cdots(x-n)\) 稱為「降階階乘多項式」。展開後的係數有什麼規律?
from sympy import symbols, expand, Poly, prod
x = symbols('x')
def falling_poly(n):
return expand(prod(x - k for k in range(1, n + 1)))
for n in range(1, 7):
p = Poly(falling_poly(n), x)
print(n, p.all_coeffs())
觀察輸出:
1 [1, -1]
2 [1, -3, 2]
3 [1, -6, 11, -6]
4 [1, -10, 35, -50, 24]
5 [1, -15, 85, -225, 274, -120]
6 [1, -21, 175, -735, 1624, -1764, 720]
可以發現:
- 常數項是 \((-1)^n n!\) (因為把 \(x=0\) 代入, \((-1)(-2)\cdots(-n) = (-1)^n n!\) )
- 係數正負交錯
- 這些係數正是第一類 Stirling 數(帶符號版本)
進一步驗證常數項規律:
from sympy import factorial
n = symbols('n', integer=True, positive=True)
for m in range(1, 8):
p = Poly(falling_poly(m), x)
assert p.all_coeffs()[-1] == (-1)**m * factorial(m)
print("常數項規律驗證成功")
實驗延伸:
- 把
x - k換成x + k,係數規律有何改變? - 比較 \((x-1)(x-2)\cdots(x-n)\) 與 \(\prod (x-k)\) 在 \(x = n+1\) 的值,你有什麼發現?(提示:階乘)
小結與練習#
本節學會了:
expand()展開、factor()因式分解(含整數與有理係數的差異)collect()依變數整理、.coeff()取係數Poly類的degree()、coeffs()、all_coeffs()等方法- 用係數對照驗證數學猜想(二項式係數、Stirling 數)
練習:
- 展開 \((x+y+z)^3\) ,並用
collect()分別對 \(x\) 、 \(y\) 整理,觀察結果差異。 - 因式分解 \(x^4 - 5x^2 + 4\) ,並驗證
expand(factor(expr)) == expr。 - 對 \((x+2)^{15}\) 展開,驗證所有係數之和等於 \(3^{15}\) 。(提示:係數和 = 代入 \(x=1\) )
- 用
Poly寫一個函數,判斷兩個多項式是否恆等(不靠simplify,只靠係數比較)。