3. 表达式化简的常用策略与函数
在符号计算中,“化简”并不是一个把表达式变得“更短”的单一操作,而是一组目标不同、方向各异的变换。同一个表达式,用 simplify 可能得到紧凑的分式,用 expand 会展开成多项式,用 factor 又会回到因式乘积。因此,本节的核心不是记住函数名,而是建立“看形态、选策略、验结果”的判断力:先判断表达式属于哪一类结构,再选择能暴露该结构的变换,最后用数值代入或结构比对确认变换是否达到目的。
五个核心函数各自在做什么
以 SymPy 为例,常用化简函数的分工可以这样理解。simplify 是一个“综合尝试器”,它会调用多种规则并挑选它认为最简单的形式,适合作为兜底手段,但代价是速度较慢、结果不完全可预测。expand 专门做展开:把乘积、幂、部分分式等拆成和式,适合观察各项系数、做逐项比较或准备后续合并同类项。factor 是 expand 的反方向,把多项式还原为因式乘积,适合求根、约分和判断零点。trigsimp 面向三角恒等式,把正余弦、正切、双曲函数之间的冗余组合压缩。powsimp 处理幂的合并与拆分,例如把 x**a*x**b 合并为 x**(a+b),或在需要时反向拆开。
需要强调的是,这些函数并非互斥的“正确答案”,而是不同的观察视角。选择哪一个,取决于你接下来要做什么:要解方程就倾向 factor,要比较系数就倾向 expand,要验证恒等式就倾向 trigsimp 或 simplify。
同一表达式的三条化简路径
设表达式为 E = (x + 1)*(x - 2) + (x - 1)**2。先看三条路径的差异。
路径一,直接 expand:
import sympy as sp
x = sp.symbols('x')
E = (x + 1)*(x - 2) + (x - 1)**2
print(sp.expand(E)) # 2*x**2 - 2*x - 1
print(sp.factor(sp.expand(E))) # 无法在有理数域进一步分解
展开后得到 2*x**2 - 2*x - 1,各项系数一目了然,但判别式 Δ = 4 + 8 = 12 不是完全平方数,因此在有理数域上不可约,factor 不会给出更紧凑的乘积形式。这说明“展开后再因式分解”并不总能得到更简形式。
路径二,先 simplify 再观察:
print(sp.simplify(E)) # 2*x**2 - 2*x - 1
print(sp.expand(E) == sp.simplify(E)) # True
本例中 simplify 与 expand 结果一致,因为表达式本身就是多项式,没有分式或三角结构可供压缩。simplify 的价值在更复杂的混合表达式上才会显现。
路径三,构造一个能体现差异的对照表达式 F = (x**2 - 1)/(x - 1):
F = (x**2 - 1)/(x - 1)
print(sp.simplify(F)) # x + 1
print(sp.factor(x**2 - 1)) # (x - 1)*(x + 1)
print(sp.cancel(F)) # x + 1
这里 simplify 和 cancel 都约去了公因子 (x - 1),得到 x + 1;而 factor 只作用于分子,得到 (x - 1)*(x + 1),并未完成约分。可见“因式分解”和“约分”是两个动作,前者是后者的前置条件,但不能替代后者。实际使用中,分式化简应优先考虑 cancel 或 simplify,而不是单独用 factor。
三角与幂:专用函数的不可替代性
对 T = sp.sin(x)**2 + sp.cos(x)**2 + sp.sin(x)*sp.cos(x),trigsimp 会先把平方和压成 1,得到 1 + sin(x)*cos(x);若再配合 expand_trig 或 rewrite,还能进一步写成 1 + sin(2x)/2。而 simplify 对同一表达式通常也能得到相近结果,但耗时更长,且在不同版本中可能给出不同形式。因此在三角场景中,明确知道目标恒等式时,直接用 trigsimp 更可控。
对幂表达式 P = x**a * x**b / x**(a + b - 1),powsimp(P) 会合并同底幂,得到 x(在符号假设允许合并的前提下)。若符号未声明为正,SymPy 可能保留原式或给出带条件的合并结果,这时需要用 symbols('x', positive=True) 明确假设。这一点是初学者最容易忽略的:化简结果依赖符号假设,同一表达式在不同假设下可能得到不同形式,甚至“化简失败”。
evalf 比对,若差值在数值误差范围内一致,说明变换在代数上等价;再结合结构目标(是否出现因式、是否约去公因子、是否合并幂)判断是否“更简”。建立选择策略的初步判断力
综合以上示例,可以形成一张简明的判断顺序。第一步看结构:是多项式、分式、三角还是幂表达式。第二步看目的:要系数就 expand,要零点就 factor,要约分就 cancel 或 simplify,要恒等式就 trigsimp,要合并幂就 powsimp。第三步看假设:符号的正负、是否整数,会直接影响合并与开方是否合法。第四步做验证:数值代入比对,避免“看起来更短但不等价”的陷阱。
| 表达式形态 | 首选函数 | 典型目标 | 注意事项 |
|---|---|---|---|
| 多项式乘积 | expand | 逐项系数、后续合并 | 展开后项数可能暴增 |
| 和式多项式 | factor | 求根、约分前置 | 有理数域上未必可约 |
| 分式 | cancel / simplify | 约去公因子 | 单用 factor 不完成约分 |
| 三角组合 | trigsimp | 压缩为最简恒等式 | 可配合 rewrite 换形式 |
| 同底幂 | powsimp | 合并或拆分指数 | 依赖符号正性假设 |
| 混合复杂式 | simplify | 兜底尝试 | 较慢,结果不完全可预测 |
最后要提醒的是,化简没有全局最优解。“更简”是相对于目标而言的:对求解方程,因式形式更简;对数值求值,展开形式可能更稳定;对展示恒等式,三角压缩形式更直观。掌握这些函数的差异,本质上是在掌握“用哪种视角看表达式”的能力,而这正是后续自动求导结果整理与人工复核的基础。