简介

什么是符号计算?

符号计算处理数学对象的符号计算。这意味着数学对象是精确表示的,而不是近似表示的,并且包含未求解变量的数学表达式将以符号形式保留。

让我们举个例子。假设我们要使用内置的 Python 函数来计算平方根。我们可以这样做

>>> import math
>>> math.sqrt(9)
3.0

9 是一个完全平方数,因此我们得到了精确答案 3。但假设我们计算了一个不是完全平方数的数字的平方根

>>> math.sqrt(8)
2.82842712475

这里我们得到了一个近似结果。2.82842712475 不是 8 的精确平方根(实际上,8 的实际平方根不能用有限小数表示,因为它是一个无理数)。如果我们只关心 8 的平方根的小数形式,我们就可以完成了。

但假设我们想要更进一步。回想一下 \(\sqrt{8} = \sqrt{4\cdot 2} = 2\sqrt{2}\)。从上面的结果中很难推导出这一点。这就是符号计算发挥作用的地方。使用像 SymPy 这样的符号计算系统,默认情况下不会对非完全平方数的平方根进行求值。

>>> import sympy
>>> sympy.sqrt(3)
sqrt(3)

此外——而这正是我们开始看到符号计算的真正力量的地方——符号结果可以被符号简化。

>>> sympy.sqrt(8)
2*sqrt(2)

更有趣的例子

上面的例子开始展示了我们如何使用 SymPy 精确地操作无理数。但它远不止于此。符号计算系统(顺便说一下,也经常被称为计算机代数系统,或简称为 CAS)如 SymPy 能够计算包含变量的符号表达式。

正如我们将在后面看到的,在 SymPy 中,变量使用 symbols 定义。与许多符号操作系统不同,SymPy 中的变量必须在使用之前定义(原因将在下一节中讨论)。

让我们定义一个符号表达式,代表数学表达式 \(x + 2y\)

>>> from sympy import symbols
>>> x, y = symbols('x y')
>>> expr = x + 2*y
>>> expr
x + 2*y

请注意,我们写了 x + 2*y,就像我们如果 xy 是普通的 Python 变量一样。但在这种情况下,表达式不会求值为某个值,而是保留为 x + 2*y。现在让我们玩一玩它

>>> expr + 1
x + 2*y + 1
>>> expr - x
2*y

在上面的例子中注意一些事情。当我们输入 expr - x 时,我们没有得到 x + 2*y - x,而是只得到了 2*yx-x 自动相互抵消。这类似于 sqrt(8) 在上面自动变为 2*sqrt(2)。然而,这并不总是发生在 SymPy 中。

>>> x*expr
x*(x + 2*y)

在这里,我们可能期望 \(x(x + 2y)\) 变为 \(x^2 + 2xy\),但我们看到表达式被保留了下来。这是 SymPy 中的一个常见主题。除了 \(x - x = 0\)\(\sqrt{8} = 2\sqrt{2}\) 之类的明显简化之外,大多数简化不会自动执行。这是因为我们可能更喜欢因式分解形式 \(x(x + 2y)\),或者我们可能更喜欢展开形式 \(x^2 + 2xy\)。两种形式在不同的情况下都有用。在 SymPy 中,有函数可以将一种形式转换为另一种形式。

>>> from sympy import expand, factor
>>> expanded_expr = expand(x*expr)
>>> expanded_expr
x**2 + 2*x*y
>>> factor(expanded_expr)
x*(x + 2*y)

符号计算的力量

像 SymPy 这样的符号计算系统的真正力量在于能够以符号方式进行各种计算。SymPy 可以简化表达式,计算导数、积分和极限,求解方程,处理矩阵,以及更多,并且所有这些都以符号方式进行。它包含用于绘图、打印(例如数学公式的二维漂亮打印输出,或 \(\mathrm{\LaTeX}\))、代码生成、物理学、统计学、组合学、数论、几何学、逻辑学等的模块。以下是一些 SymPy 能够实现的符号能力的示例,以激发您的兴趣。

>>> from sympy import *
>>> x, t, z, nu = symbols('x t z nu')

这将使所有后续示例以 Unicode 字符进行漂亮打印。

>>> init_printing(use_unicode=True)

\(\sin{(x)}e^x\) 的导数。

>>> diff(sin(x)*exp(x), x)
 x           x
ℯ ⋅sin(x) + ℯ ⋅cos(x)

计算 \(\int(e^x\sin{(x)} + e^x\cos{(x)})\,dx\)

>>> integrate(exp(x)*sin(x) + exp(x)*cos(x), x)
 x
ℯ ⋅sin(x)

计算 \(\int_{-\infty}^\infty \sin{(x^2)}\,dx\)

>>> integrate(sin(x**2), (x, -oo, oo))
√2⋅√π
─────
  2

\(\lim_{x\to 0}\frac{\sin{(x)}}{x}\)

>>> limit(sin(x)/x, x, 0)
1

求解 \(x^2 - 2 = 0\)

>>> solve(x**2 - 2, x)
[-√2, √2]

求解微分方程 \(y'' - y = e^t\)

>>> y = Function('y')
>>> dsolve(Eq(y(t).diff(t, t) - y(t), exp(t)), y(t))
           -t   ⎛     t⎞  t
y(t) = C₂⋅ℯ   + ⎜C₁ + ─⎟⋅ℯ
                ⎝     2⎠

\(\left[\begin{smallmatrix}1 & 2\\2 & 2\end{smallmatrix}\right]\) 的特征值。

>>> Matrix([[1, 2], [2, 2]]).eigenvals()
⎧3   √17     3   √17   ⎫
⎨─ - ───: 1, ─ + ───: 1⎬
⎩2    2      2    2    ⎭

将贝塞尔函数 \(J_{\nu}\left(z\right)\) 改写为球贝塞尔函数 \(j_\nu(z)\)

>>> besselj(nu, z).rewrite(jn)
√2⋅√z⋅jn(ν - 1/2, z)
────────────────────
         √π

使用 \(\mathrm{\LaTeX}\) 打印 \(\int_{0}^{\pi} \cos^{2}{\left (x \right )}\, dx\)

>>> latex(Integral(cos(x)**2, (x, 0, pi)))
\int\limits_{0}^{\pi} \cos^{2}{\left(x \right)}\, dx

为什么选择 SymPy?

市面上有很多计算机代数系统。 这里 是维基百科上列出的一些系统。是什么让 SymPy 比其他系统更好呢?

首先,SymPy 完全免费。它是开源的,并根据宽松的 BSD 许可证授权,因此您可以修改源代码,甚至可以出售它(如果您愿意)。这与 Maple 或 Mathematica 等流行的商业系统形成对比,后者需要数百美元的许可证费用。

其次,SymPy 使用 Python。大多数计算机代数系统发明了自己的语言。SymPy 则没有。SymPy 完全用 Python 编写,并在 Python 中执行。这意味着如果您已经了解 Python,那么使用 SymPy 会容易得多,因为您已经知道语法(如果您不了解 Python,那么学习它也非常容易)。我们已经知道 Python 是一种设计良好、经受考验的语言。SymPy 开发人员对他们在编写数学软件方面的能力充满信心,但编程语言设计完全是另一回事。通过重用现有的语言,我们能够专注于重要的事情:数学。

另一个计算机代数系统 Sage 也使用 Python 作为其语言。但 Sage 很大,下载量超过 1 GB。SymPy 的一个优势是它很轻量级。除了相对较小之外,它也没有除 Python 之外的其他依赖项,因此它几乎可以在任何地方轻松使用。此外,Sage 和 SymPy 的目标不同。Sage 的目标是成为一个功能齐全的数学系统,并通过将所有主要的开源数学系统编译到一个系统中来实现这一点。当您在 Sage 中调用某个函数时,例如 integrate,它会调用它包含的其中一个开源包。实际上,SymPy 包含在 Sage 中。另一方面,SymPy 的目标是成为一个独立的系统,所有功能都在 SymPy 本身中实现。

SymPy 的另一个重要特性是它可以作为库使用。许多计算机代数系统专注于在交互式环境中可用,但如果您希望自动化或扩展它们,则很难做到。使用 SymPy,您可以在交互式 Python 环境中轻松使用它,也可以在您自己的 Python 应用程序中导入它。SymPy 还提供 API,使其易于用您自己的自定义函数扩展它。