介绍¶
什么是符号计算?¶
符号计算处理的是以符号形式进行的数学对象的计算。这意味着数学对象被精确地表示,而不是近似地表示,并且带有未求值变量的数学表达式保持符号形式。
让我们举个例子。假设我们想使用内置的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
就像 x
和 y
是普通的 Python 变量一样。但在这种情况下,表达式不会计算出结果,而是保持为 x + 2*y
。现在让我们来玩玩它:
>>> expr + 1
x + 2*y + 1
>>> expr - x
2*y
注意上面的例子中的一些东西。当我们输入 expr - x
时,我们没有得到 x + 2*y - x
,而是只得到了 2*y
。x
和 -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 体积庞大,下载量超过一吉字节。SymPy 的一个优势是它轻量级。除了相对较小之外,它除了 Python 之外没有其他依赖项,因此几乎可以在任何地方轻松使用。此外,Sage 和 SymPy 的目标是不同的。Sage 旨在成为一个功能齐全的数学系统,并通过将所有主要的开源数学系统编译在一起实现这一目标。当你在 Sage 中调用某些函数,例如 integrate
,它会调用它包含的某个开源包。事实上,SymPy 包含在 Sage 中。而 SymPy 则旨在成为一个独立的系统,所有功能都在 SymPy 本身中实现。
SymPy 的一个重要特性是它可以作为库使用。许多计算机代数系统专注于在交互环境中使用,但如果您希望自动化或扩展它们,则很难做到。使用 SymPy,您可以同样轻松地在交互式 Python 环境中使用它,或者将其导入到您自己的 Python 应用程序中。SymPy 还提供了 API,以便轻松地用您自己的自定义函数扩展它。