代数解方程

使用 SymPy 代数(符号)求解方程。例如,求解 \(x^2 = y\) 关于 \(x\) 的解得到 \(x \in \{-\sqrt{y},\sqrt{y}\}\)

其他方法

求解函数

有两个高级函数用于求解方程,solve()solveset()。以下分别给出每个函数的示例

solve()

>>> from sympy.abc import x, y
>>> from sympy import solve
>>> solve(x**2 - y, x, dict=True)
[{x: -sqrt(y)}, {x: sqrt(y)}]

solveset()

>>> from sympy import solveset
>>> from sympy.abc import x, y
>>> solveset(x**2 - y, x)
{-sqrt(y), sqrt(y)}

以下是关于何时使用这两种函数的建议

  • solve()

    • 您想要获得变量可能取值的显式符号表示,以满足方程。

    • 您想要使用 subs() 将这些显式解值代入包含相同变量的其他方程或表达式中。

  • solveset()

    • 您希望以数学精确的方式表示解,使用 数学集合

    • 您希望有一个表示所有解的表示,包括如果有无限多个解。

    • 您希望有一个一致的输入接口。

    • 您希望将解的域限制为任意集合。

    • 您不需要以编程方式从解集提取解:解集不能被以编程方式查询。

指导

参考 在函数调用中包含要求解的变量确保 solve() 的一致格式

代数求解方程

您可以通过几种方式求解方程。以下示例演示了使用 solve()solveset()(如果适用)。您可以选择最适合您方程的函数。

将您的方程转换为等于零的表达式

利用任何不在 Eq(方程)中的表达式都自动被求解函数假定为等于零 (0) 的事实。您可以将方程 \(x^2 = y\) 重排为 \(x^2 - y = 0\),并求解该表达式。如果您正在交互式地求解一个已经等于零的表达式,或者一个您不介意重排为 \(expression = 0\) 的方程,这种方法很方便。

>>> from sympy import solve, solveset
>>> from sympy.abc import x, y
>>> solve(x**2 - y, x, dict=True)
[{x: -sqrt(y)}, {x: sqrt(y)}]
>>> solveset(x**2 - y, x)
{-sqrt(y), sqrt(y)}

将您的方程转换为 Eq 格式

将您的方程转换为 Eq 格式,然后求解 Eq。如果您正在交互式地求解一个您已经以方程形式拥有的方程,或者您认为是等式,这种方法很方便。它还有助于在从一方减去另一方时防止符号错误。

>>> from sympy import Eq, solve, solveset
>>> from sympy.abc import x, y
>>> eqn = Eq(x**2, y)
>>> eqn
Eq(x**2, y)
>>> solutions = solve(eqn, x, dict=True)
>>> print(solutions)
[{x: -sqrt(y)}, {x: sqrt(y)}]
>>> solutions_set = solveset(eqn, x)
>>> print(solutions_set)
{-sqrt(y), sqrt(y)}
>>> for solution_set in solutions_set:
...     print(solution_set)
sqrt(y)
-sqrt(y)

限制解的域

默认情况下,SymPy 将在复数域返回解,这也包括纯实数和虚数。这里,前两个解是实数,后两个解是虚数

>>> from sympy import Symbol, solve, solveset
>>> x = Symbol('x')
>>> solve(x**4 - 256, x, dict=True)
[{x: -4}, {x: 4}, {x: -4*I}, {x: 4*I}]
>>> solveset(x**4 - 256, x)
{-4, 4, -4*I, 4*I}

要将返回的解限制为实数,或其他域或范围,不同的求解函数使用不同的方法。

对于 solve(),在要求解的符号 \(x\) 上放置一个假设

>>> from sympy import Symbol, solve
>>> x = Symbol('x', real=True)
>>> solve(x**4 - 256, x, dict=True)
[{x: -4}, {x: 4}]

或使用标准的 Python 技术来过滤列表(如列表推导)来限制解

>>> from sympy import Or, Symbol, solve
>>> x = Symbol('x', real=True)
>>> expr = (x-4)*(x-3)*(x-2)*(x-1)
>>> solution = solve(expr, x)
>>> print(solution)
[1, 2, 3, 4]
>>> solution_outside_2_3 = [v for v in solution if (v.is_real and Or(v<2,v>3))]
>>> print(solution_outside_2_3)
[1, 4]

对于 solveset(),通过设置域在函数调用中限制输出域

>>> from sympy import S, solveset
>>> from sympy.abc import x
>>> solveset(x**4 - 256, x, domain=S.Reals)
{-4, 4}

或通过将返回的解限制为任意集合,包括区间

>>> from sympy import Interval, pi, sin, solveset
>>> from sympy.abc import x
>>> solveset(sin(x), x, Interval(-pi, pi))
{0, -pi, pi}

如果您将解限制为一个没有解的域,solveset() 将返回空集,EmptySet

>>> from sympy import solveset, S
>>> from sympy.abc import x
>>> solveset(x**2 + 1, x, domain=S.Reals)
EmptySet

显式表示无限个可能的解集

solveset() 可以表示无限个可能的解集 并用标准的数学符号表达它们,例如 \(\sin(x) = 0\) 对于 \(x = n * \pi\) 对于每个整数的值 \(n\)

>>> from sympy import pprint, sin, solveset
>>> from sympy.abc import x
>>> solution = solveset(sin(x), x)
>>> pprint(solution)
{2*n*pi | n in Integers} U {2*n*pi + pi | n in Integers}

但是,solve() 只会返回有限个解

>>> from sympy import sin, solve
>>> from sympy.calculus.util import periodicity
>>> from sympy.abc import x
>>> f = sin(x)
>>> solve(f, x)
[0, pi]
>>> periodicity(f, x)
2*pi

solve() 试图仅返回足够多的解,以便通过添加方程的 periodicity()(这里为 \(2\pi\))的整数倍,可以从返回的解中生成所有(无限多个)解。

使用解结果

将从 solve() 中获得的解代入表达式

您可以将从 solve() 中获得的解代入表达式。

一个常见的用例是为函数 \(f\) 寻找临界点和值。在临界点,Derivative 等于零(或未定义)。然后,您可以使用 subs() 将临界点代回函数,从而获得这些临界点处的函数值。您还可以通过将值代入二阶导数表达式来判断临界点是最大值还是最小值:负值表示最大值,正值表示最小值。

>>> from sympy.abc import x
>>> from sympy import solve, diff
>>> f = x**3 + x**2 - x
>>> derivative = diff(f, x)
>>> critical_points = solve(derivative, x, dict=True)
>>> print(critical_points)
[{x: -1}, {x: 1/3}]
>>> point1, point2 = critical_points
>>> print(f.subs(point1))
1
>>> print(f.subs(point2))
-5/27
>>> curvature = diff(f, x, 2)
>>> print(curvature.subs(point1))
-4
>>> print(curvature.subs(point2))
4

solveset() 解集不能被以编程方式查询

如果 solveset() 返回一个有限集(类 FiniteSet),您可以遍历解

>>> from sympy import solveset
>>> from sympy.abc import x, y
>>> solution_set = solveset(x**2 - y, x)
>>> print(solution_set)
{-sqrt(y), sqrt(y)}
>>> solution_list = list(solution_set)
>>> print(solution_list)
[sqrt(y), -sqrt(y)]

但是,对于更复杂的结果,可能无法列出解

>>> from sympy import S, solveset, symbols
>>> x, y = symbols('x, y')
>>> solution_set = solveset(x**2 - y, x, domain=S.Reals)
>>> print(solution_set)
Intersection({-sqrt(y), sqrt(y)}, Reals)
>>> list(solution_set)
Traceback (most recent call last):
    ...
TypeError: The computation had not completed because of the undecidable set
membership is found in every candidates.

在这种情况下,这是因为,如果 \(y\) 为负,则它的平方根将是虚数而不是实数,因此在解集的声明域之外。通过声明 \(y\) 为实数且为正,SymPy 可以确定它的平方根是实数,从而解析解与实数集之间的交集

>>> from sympy import S, Symbol, solveset
>>> x = Symbol('x')
>>> y = Symbol('y', real=True, positive=True)
>>> solution_set = solveset(x**2 - y, x, domain=S.Reals)
>>> print(solution_set)
{-sqrt(y), sqrt(y)}
>>> list(solution_set)
[sqrt(y), -sqrt(y)]

或者,您可以使用 args 从解集提取集,然后从包含符号解的集合中创建列表

>>> from sympy import S, solveset, symbols
>>> x, y = symbols('x, y')
>>> solution_set = solveset(x**2 - y, x, domain=S.Reals)
>>> print(solution_set)
Intersection({-sqrt(y), sqrt(y)}, Reals)
>>> solution_set_args = solution_set.args
>>> print(solution_set.args)
(Reals, {-sqrt(y), sqrt(y)})
>>> list(solution_set_args[1])
[sqrt(y), -sqrt(y)]

可以加速 solve() 的选项

参考 求解指南

并非所有方程都可以求解

没有闭式解的方程

有些方程没有闭式解,在这种情况下,SymPy 可能会返回空集或给出错误。例如,以下 超越方程 没有闭式解

>>> from sympy import cos, solve
>>> from sympy.abc import x
>>> solve(cos(x) - x, x, dict=True)
Traceback (most recent call last):
    ...
NotImplementedError: multiple generators [x, cos(x)]
No algorithms are implemented to solve equation -x + cos(x)

有闭式解但 SymPy 无法求解的方程

您的方程也可能存在代数解,而 SymPy 尚未实现相应的算法。如果出现这种情况,或者 SymPy 在存在数学解时返回空集或列表(表明 SymPy 中存在错误),请在 邮件列表 上发布,或在 SymPy 的 GitHub 页面 上打开一个问题。在问题得到解决之前,您可以 以数值方式求解您的方程

报告错误

如果您发现求解函数存在错误,请在 SymPy 邮件列表 上发布问题。在问题得到解决之前,您可以使用 其他备选方案 中列出的其他方法。