1.1.4. 计算

参考 Calculus

本节介绍如何在 SymPy 中执行基本的微积分任务,例如导数、积分、极限和级数展开。

import warnings
from sympy import *

warnings.filterwarnings('ignore')
x, y, z = symbols('x y z')

微分

微分(derivatives)使用 diff 函数:

diff(cos(x), x)
sin(x)
diff(exp(x**2), x)
2xex2

diff 可以一次取多个导数。要获取多个导数,请根据您希望微分的次数传递变量,或者在变量后传递一个数字。例如,下面两个都找到了 x4 的三阶导数。

diff(x**4, x, x, x)
24x
diff(x**4, x, 3)
24x

您还可以一次对许多变量进行导数。只需按顺序传递每个导数,使用与单变量导数相同的语法。

例如:计算 7xy2z4exyz

expr = exp(x*y*z)
diff(expr, x, y, y, z, z, z, z)
x3y2(x3y3z3+14x2y2z2+52xyz+48)exyz
diff(expr, x, y, 2, z, 4)
x3y2(x3y3z3+14x2y2z2+52xyz+48)exyz
diff(expr, x, y, y, z, 4)
x3y2(x3y3z3+14x2y2z2+52xyz+48)exyz

diff 也可以称为方法。这两种调用 diff 的方式完全一样,提供只是为了方便。

expr.diff(x, y, y, z, 4)
x3y2(x3y3z3+14x2y2z2+52xyz+48)exyz

要创建未计算的导数,请使用 Derivative 类。它的语法与 diff 相同。

deriv = Derivative(expr, x, y, y, z, 4)
deriv
7z4y2xexyz

要计算未评估的导数,请使用 doit 方法。

deriv.doit()
x3y2(x3y3z3+14x2y2z2+52xyz+48)exyz

这些未求值的对象对于延迟导数的求值或用于打印目的很有用。当 SymPy 不知道如何计算表达式的导数时(例如,如果它包含未定义的函数,这在求解微分方程部分中进行了描述),它们也会被使用。

可以使用元组 (x, n) 创建未指定阶的导数,其中 n 是导数相对于 x 的阶数。

m, n, a, b = symbols('m n a b')
expr = (a*x + b)**m
expr.diff((x, n))
nxn(ax+b)m

积分

要计算积分,请使用 integrate 函数。积分有定积分和不定积分两种。要计算不定积分,即反导数(antiderivative)或原语(primitive),只需在表达式后传递变量即可。

integrate(cos(x), x)
sin(x)

要计算定积分,请传递参数 (integration_variable, lower_limit, upper_limit)

例如,计算:0exdx,

integrate(exp(-x), (x, 0, oo))
1

与不定积分一样,您可以传递多个极限元组来执行多重积分。例如,要计算

ex2y2dxdy,
integrate(exp(-x**2 - y**2), (x, -oo, oo), (y, -oo, oo))
π

如果 integrate 无法计算积分,则返回未计算的 Integral 对象。

expr = integrate(x**x, x)
expr
xxdx
print(expr)
Integral(x**x, x)

Derivative 一样,您可以使用 Integral 创建未计算的积分。稍后要评估这个积分,请调用 doit

expr = Integral(log(x)**2, x)
expr
log(x)2dx
expr.doit()
xlog(x)22xlog(x)+2x

集成使用强大的算法来计算定积分和不定积分,包括启发式模式匹配类型算法、Risch 算法的部分实现以及使用 Meijer G 函数的算法,该算法对于计算特殊函数的积分很有用 ,尤其是定积分。以下是一些集成函数的示例。

integ = Integral((x**4 + x**2*exp(x) - x**2 - 2*x*exp(x) - 2*x - \
                  exp(x))*exp(x)/((x - 1)**2*(x + 1)**2*(exp(x) + 1)), x)
integ
(x4+x2exx22xex2xex)ex(x1)2(x+1)2(ex+1)dx
integ.doit()
log(ex+1)+exx21
integ = Integral(sin(x**2), x)
integ
sin(x2)dx
integ.doit()
32πS(2xπ)Γ(34)8Γ(74)
integ = Integral(x**y*exp(-x), (x, 0, oo))
integ
0xyexdx
integ.doit()
{Γ(y+1)forre(y)>10xyexdxotherwise

极限

计算:

limxx0f(x)

使用 limit(f(x), x, x0)

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

当评估点是奇点(singularity)时,应使用 limit 而不是 subs。尽管 SymPy 有表示 的对象,但使用它们进行评估并不可靠,因为它们不会跟踪诸如增长率之类的事情。此外,诸如 之类的东西返回 nan (非数字)。

expr = x**2/exp(x)
expr.subs(x, oo)
NaN
limit(expr, x, oo)
0

DerivativeIntegral 一样,limit 也有一个未计算的对应物 Limit。要评估它,请使用 doit

expr = Limit((cos(x) - 1)/x, x, 0)
expr
limx0+(cos(x)1x)
expr.doit()
0

要仅评估单侧极限,请将“+”或“-”作为 limit 的第四个参数传递。例如,要计算

limx0+1x,
limit(1/x, x, 0, '+')
limit(1/x, x, 0, '-')

级数展开

SymPy 可以计算函数围绕一个点的渐近级数展开。要计算 f(x) 围绕 xn 阶项 x=x0 项的展开,请使用 f(x).series(x, x0, n). x0n 可以省略,在这种情况下,将使用默认值 x0=0n=6

expr = exp(sin(x))
expr.series(x, 0, 4)
1+x+x22+O(x4)

最后的 O(x4) 项表示 x=0 处的 Landau 阶项。

x + x**3 + x**6 + O(x**4)
x+x3+O(x4)
x*O(1)
O(x)

如果您不想要 O 项,请使用 removeO 方法。

expr.series(x, 0, 4).removeO()
x22+x+1

O 表示法支持任意限制点(0 除外):

exp(x - 6).series(x, x0=6)
5+(x6)22+(x6)36+(x6)424+(x6)5120+x+O((x6)6;x6)

有限差分

到目前为止,我们已经分别研究了具有解析导数和原始函数的表达式。但是,如果我们想要一个表达式来估计一条曲线的导数,而该曲线缺少封闭形式表示,或者我们还不知道其函数值,该怎么办?一种方法是使用有限差分(Finite differences)方法。

使用有限差分进行微分的最简单方法是使用 distinct_finite 函数:

f, g = symbols('f g', cls=Function)

differentiate_finite(f(x)*g(x))
f(x12)g(x12)+f(x+12)g(x+12)

如果您已经有一个 Derivative 实例,您可以使用 as_finite_difference 方法生成任意阶导数的近似值:

f = Function('f')

dfdx = f(x).diff(x)
dfdx
ddxf(x)
dfdx.as_finite_difference()
f(x12)+f(x+12)

在这里,使用最小数量的点(一阶导数为 2)使用步长为 1 等距计算一阶导数围绕 x 近似。我们可以使用任意步长(可能包含符号表达式):

f = Function('f')
d2fdx2 = f(x).diff(x, 2)
h = Symbol('h')
d2fdx2.as_finite_difference([-3*h,-h,2*h])
f(3h)5h2f(h)3h2+2f(2h)15h2

如果您只是对评估权重感兴趣,您可以手动进行:

finite_diff_weights(2, [-3, -1, 2], 0)[-1][-1]
[1/5, -1/3, 2/15]

请注意,我们只需要从 finite_diff_weights 返回的最后一个子列表中的最后一个元素。这样做的原因是该函数还为较低的导数生成权重并使用较少的点(有关更多详细信息,请参阅 finite_diff_weights 的文档)。

如果直接使用 finite_diff_weights 看起来复杂,并且 Derivative 实例的 as_finite_difference 方法不够灵活,可以使用以 orderx_listy_listx0 为参数的 apply_finite_diff

x_list = [-3, 1, 2]
y_list = symbols('a b c')
apply_finite_diff(1, x_list, y_list, 0)
3a20b4+2c5