# 自动化简的边界 · 为什么 sin²+cos² 不自动等于 1

> x+x 自动变成 2x，sin²+cos² 却不自动变成 1——这不是 SymPy 偷懒，而是一个深刻的事实：判断一个表达式是否等于零，在一般情况下是「不可判定」的。这一章讲清自动化简的边界、「最简」的模糊，以及为什么 simplify 只能是一个尽力而为的启发式操作。

- 作者：David（道雾轩）
- 专栏：符号计算的艺术（https://daiw.org/manual/symbolic-computing.md）
- 最后更新：2026-07-14
- 原文：https://daiw.org/manual/symbolic-computing/auto-simplification
- 转载与引用：请注明出处并附原文链接（https://daiw.org/about/copyright）

# 自动化简的边界 · 为什么 sin²+cos² 不自动等于 1

新手常有个期待：输入一个式子，CAS 就该「自动化到最简」。现实却微妙得多——**有些化简自动发生，有些死活不发生**。理解这条边界在哪、为什么在那，是用好符号计算的关键，也是我们第一次触到「计算的本质」。

## 有些化简，自动发生

SymPy 在你构造表达式的**同时**，就会做一批**廉价、且永远正确**的化简：

```python
from sympy import *
x = symbols('x')

print(x + x)                  # 2*x
print(2*x + 3*x)              # 5*x
print(x / x)                  # 1
print(sqrt(8) - 2*sqrt(2))    # 0     —— √8 规范成 2√2,于是相减为 0
print(exp(x) * exp(-x))       # 1     —— 同底幂相乘,指数相加为 0
```

这些叫**自动化简（automatic simplification）**。它们有个共同点：**又快又无脑地对**——合并同类项、约分、同底幂相加，这些规则**任何时候都成立**，而且**代价极低**。SymPy 用它们把表达式维持在一个**规范形式（canonical form）**，好让 $x+x$ 和 $2x$ 被认成同一棵[树](https://daiw.org/manual/symbolic-computing/expression-tree)。

## 有些化简，死活不自动发生

但下面这些，SymPy **不会**主动帮你做：

```python
print((x + 1)**2)               # (x + 1)**2     —— 不自动展开
print(sin(x)**2 + cos(x)**2)    # sin(x)**2 + cos(x)**2   —— 不自动等于 1
print(log(x**2))                # log(x**2)      —— 不自动变 2*log(x)
```

$\sin^2x+\cos^2x$ 明明等于 $1$，为什么不自动化简？两个原因，一浅一深。

### 浅层原因：「最简」根本没有定义

先看 $(x+1)^2$。它**不自动展开**成 $x^2+2x+1$——可反过来，$x^2+2x+1$ 也**不自动因式分解**成 $(x+1)^2$。哪个更简单？

<Callout type="warn">
  **「最简形式」是个模糊、甚至自相矛盾的概念。** $(x+1)^2$ 和 $x^2+2x+1$ 哪个简单？**看你要干什么**：想求根，因式分解的 $(x+1)^2$ 更好；想求导或看各项系数，展开的 $x^2+2x+1$ 更方便。没有放之四海皆「最简」的形式。所以 CAS **不敢替你擅自决定**朝哪个方向化——它把选择权交给你，用**有明确方向的操作**：`expand`（展开）、`factor`（因式分解）、`collect`（合并）、`trigsimp`（三角化简）……

  ```python
  print(expand((x + 1)**2))        # x**2 + 2*x + 1
  print(factor(x**2 + 2*x + 1))    # (x + 1)**2
  ```

  你要展开还是因式分解？**你说了算**，而不是系统猜。
</Callout>

### 深层原因：判断「是否等于零」是不可判定的

$\sin^2x+\cos^2x=1$ 需要一条**三角恒等式**——这不像「合并同类项」那样无脑，得**搜索**该用哪条恒等式。而搜索是**昂贵**的：如果每构造一个式子都自动跑一遍所有可能的恒等式，符号计算会慢到无法使用。所以这类化简被挪出「自动」范畴，交给一个**你显式调用**的操作：

```python
print(simplify(sin(x)**2 + cos(x)**2))   # 1   —— 你得开口问
```

但代价高只是表面。真正深刻的原因是：

<Callout type="info">
  **一个「完美的化简算法」根本不存在——这是数学上被证明的不可能。** 化简的核心是判断「两个式子是否相等」，等价于判断「它们的差**是否恒等于零**」。而 **Richardson 定理（1968）** 证明：对于包含 $\pi$、指数、对数、$\sin$、绝对值等的一大类表达式，**判断一个表达式是否恒等于零，是不可判定的**——不存在任何算法，能对所有这类式子给出「是零 / 非零」的正确回答。

  这意味着：**没有、也不可能有**一个 `simplify` 能把任何式子都化到真正的最简。它只能是一个**启发式**：试一堆常见变换，挑个看起来最简的返回——**大多数时候好用，但有时化不动、甚至越化越复杂**。这不是 SymPy 或 Mathematica 不够聪明，而是**问题本身就没有完美解**。我们在[上一章](https://daiw.org/manual/symbolic-computing/exact-numbers)说精确数消灭了误差；这里则看到，精确的世界自有它无法逾越的边界——而这条边界，直指「计算」的极限。
</Callout>

## 所以：用有方向的工具，别指望「一键最简」

认清边界后，实践策略很清楚：**别指望一个万能的 `simplify`，而是根据目的选对准的工具。** 常用的一批：

| 你想要 | 用 | 例子 |
| --- | --- | --- |
| 乘开括号 | `expand` | $(x+1)^2 \to x^2+2x+1$ |
| 因式分解 | `factor` | $x^2-1 \to (x-1)(x+1)$ |
| 按某变量合并 | `collect` | 整理成 $x$ 的多项式 |
| 三角化简 | `trigsimp` | $\sin^2+\cos^2 \to 1$ |
| 拆对数 | `expand_log` | $\log(x^2)\to 2\log x$（需 $x>0$） |
| 通用启发式 | `simplify` | 不确定时的兜底，可能慢 |

`simplify` 是「不知道用啥就先试它」的兜底，但**生产代码里更推荐用有明确方向的操作**——它们快、可预期，不会给你一个「我也不知道为啥长这样」的结果。

顺带一提 `log(x**2)` 那个：它**不自动**变 $2\log x$，是因为**当 $x<0$ 时这一步并不成立**（$\log$ 的定义域）！CAS 不敢在不知道 $x$ 正负时乱化——这也是它「谨慎」的一面。你可以用**假设**告诉它 `x` 为正，它就放心化了（假设系统是符号计算的重要一环）。

## 小结与下一站

自动化简只做**廉价且永远正确**的规范化（$x+x\to2x$、约分、同底幂）；**不做**有方向的变换（$(x+1)^2$ 不自动展开）和需要搜索恒等式的化简（$\sin^2+\cos^2$ 不自动等于 1）。深层原因是 **Richardson 定理**：判断表达式是否恒为零**不可判定**，所以**完美的 `simplify` 不存在**，它只能是启发式。实践中用 `expand`/`factor`/`trigsimp` 等**有方向**的工具。

地基就位：表达式是树、数是精确的、化简有其边界。接下来进入**内核**——符号计算到底怎么「算」？下一章你会发现，无论求导、化简还是解方程，底层都是同一个动作：**在树里找到一个模式，把它替换掉**。👉 [替换与模式匹配](https://daiw.org/manual/symbolic-computing/substitution-matching)。

## 参考文献

- [1] Daniel Richardson.「Some Undecidable Problems Involving Elementary Functions of a Real Variable.」*J. Symbolic Logic*, 1968. —— 零判定不可判定性。
- [2] Joel S. Cohen.《Computer Algebra and Symbolic Computation》—— 自动化简与规范形式。
- [3] SymPy 文档.「Simplify.」https://docs.sympy.org/latest/tutorials/intro-tutorial/simplification.html
