解决指南

这些指南适用于多种类型的解决方法。

数值解法

无闭式解的方程

绝大多数任意非线性方程没有封闭形式的解。可解方程的类别基本上是:

  1. 线性方程

  2. 多项式,除非受到 阿贝尔-鲁菲尼定理 的限制(了解更多关于使用 GroebnerBasis 解决多项式的信息)

  3. 可以通过反转一些超越函数来求解的方程

  4. 可以转化为上述情况的问题(例如,通过将三角函数转化为多项式)

  5. 还有一些其他特殊情况可以通过类似 Lambert W 函数 的方法解决。

  6. 你可以通过上述任何一种方法对 decompose() 进行分解的方程式。

SymPy 可能会通过返回诸如 NotImplementedError 的错误,反映出您的方程没有可以代数表达(符号化)的解,或者 SymPy 缺乏找到已存在的封闭形式解的算法。

>>> from sympy import solve, cos
>>> 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)

因此,你可能需要通过数值方法来求解你的方程,例如使用 nsolve()

>>> from sympy import nsolve, cos
>>> from sympy.abc import x
>>> nsolve(cos(x) - x, x, 2)
0.739085133215161

如果你收到非封闭形式的解,例如 CRootOf()(表示多项式的索引复根),你可以使用 evalf() 对它们进行数值评估。

>>> from sympy import solve
>>> from sympy.abc import x
>>> solutions = solve(x**5 - x - 1, x, dict=True)
>>> solutions
[{x: CRootOf(x**5 - x - 1, 0)}, {x: CRootOf(x**5 - x - 1, 1)}, {x: CRootOf(x**5 - x - 1, 2)}, {x: CRootOf(x**5 - x - 1, 3)}, {x: CRootOf(x**5 - x - 1, 4)}]
>>> [solution[x].evalf(3) for solution in solutions]
[1.17, -0.765 - 0.352*I, -0.765 + 0.352*I, 0.181 - 1.08*I, 0.181 + 1.08*I]

当你可能更倾向于数值解时

即使你的问题有封闭形式的解,你也可能更喜欢数值解。

solve()solveset() 这样的求解函数不会尝试找到数值解,只会寻找数学上精确的符号解。因此,如果您需要数值解,请考虑使用 nsolve()

在某些情况下,尽管存在闭式解,但它可能过于繁琐而不切实际。在这种情况下,如果数值解是可以接受的,你可以使用 evalf() 来替代。例如,以下解集在精确表达时总共包含超过40项(如果你想查看所有项,可以在下面的代码块中水平滚动),而数值表达时只有八项:

>>> from sympy import symbols, solve
>>> x = symbols('x')
>>> solutions = solve(x**4 + 10*x**2 + x + 1, x, dict=True)
>>> solutions
[{x: -sqrt(-20/3 + 56/(9*(1307/432 + sqrt(434607)*I/144)**(1/3)) + 2*(1307/432 + sqrt(434607)*I/144)**(1/3))/2 - sqrt(-40/3 - 2*(1307/432 + sqrt(434607)*I/144)**(1/3) + 2/sqrt(-20/3 + 56/(9*(1307/432 + sqrt(434607)*I/144)**(1/3)) + 2*(1307/432 + sqrt(434607)*I/144)**(1/3)) - 56/(9*(1307/432 + sqrt(434607)*I/144)**(1/3)))/2}, {x: sqrt(-20/3 + 56/(9*(1307/432 + sqrt(434607)*I/144)**(1/3)) + 2*(1307/432 + sqrt(434607)*I/144)**(1/3))/2 - sqrt(-40/3 - 2*(1307/432 + sqrt(434607)*I/144)**(1/3) - 2/sqrt(-20/3 + 56/(9*(1307/432 + sqrt(434607)*I/144)**(1/3)) + 2*(1307/432 + sqrt(434607)*I/144)**(1/3)) - 56/(9*(1307/432 + sqrt(434607)*I/144)**(1/3)))/2}, {x: sqrt(-40/3 - 2*(1307/432 + sqrt(434607)*I/144)**(1/3) - 2/sqrt(-20/3 + 56/(9*(1307/432 + sqrt(434607)*I/144)**(1/3)) + 2*(1307/432 + sqrt(434607)*I/144)**(1/3)) - 56/(9*(1307/432 + sqrt(434607)*I/144)**(1/3)))/2 + sqrt(-20/3 + 56/(9*(1307/432 + sqrt(434607)*I/144)**(1/3)) + 2*(1307/432 + sqrt(434607)*I/144)**(1/3))/2}, {x: sqrt(-40/3 - 2*(1307/432 + sqrt(434607)*I/144)**(1/3) + 2/sqrt(-20/3 + 56/(9*(1307/432 + sqrt(434607)*I/144)**(1/3)) + 2*(1307/432 + sqrt(434607)*I/144)**(1/3)) - 56/(9*(1307/432 + sqrt(434607)*I/144)**(1/3)))/2 - sqrt(-20/3 + 56/(9*(1307/432 + sqrt(434607)*I/144)**(1/3)) + 2*(1307/432 + sqrt(434607)*I/144)**(1/3))/2}]
>>> for solution in solutions:
...     solution[x].evalf()
-0.0509758447494279 + 0.313552108895239*I
0.0509758447494279 + 3.14751999969868*I
0.0509758447494279 - 3.14751999969868*I
-0.0509758447494279 - 0.313552108895239*I

在其他情况下,即使精确解的项数很少,您可能仍希望得到一个数值解,以便了解其近似数值。例如,可能难以估计 \(\sqrt{2} e^{\pi}/2\) 大约为 \(16\)

>>> from sympy import pi, sqrt, exp, solve, evalf
>>> shorter = solve(sqrt(2)*x - exp(pi), x, dict=True)
>>> shorter
[{x: sqrt(2)*exp(pi)/2}]
>>> [solution[x].evalf(3) for solution in shorter]
[16.4]

使用精确值

如果你想保留诸如超越数和平方根等符号的精确数学值,请将它们定义为SymPy可以符号化解释的形式,例如使用SymPy的Pi

>>> from sympy import symbols, solve, pi
>>> x = symbols('x')
>>> solve(x**2 - pi, x, dict=True)
[{x: -sqrt(pi)}, {x: sqrt(pi)}]

如果你使用标准Python数学库中的\(\pi\),Python会将这个不精确的值传递给SymPy,从而导致一个不精确的数值解:

>>> from sympy import symbols, solve
>>> from math import pi
>>> x = symbols('x')
>>> solve(x**2 - pi, x, dict=True)
[{x: -1.77245385090552}, {x: 1.77245385090552}]

要使用如 \(6.2\)\(1/2\) 这样的精确数值,请参考 Python 数字 vs. SymPy 数字

在某些情况下,使用不精确的值会阻止 SymPy 找到结果。例如,这个精确的方程可以被求解:

>>> from sympy import symbols, solve, sqrt
>>> x = symbols('x')
>>> eq = x**sqrt(2) - 2
>>> solve(eq, x, dict=True)
[{x: 2**(sqrt(2)/2)}]

但如果你使用不精确的方程 eq = x**1.4142135623730951 - 2,SymPy 尽管尝试了很长时间,也不会返回结果。

在函数调用中包含要解决的变量

我们建议您将待求解的变量作为第二个参数传递给包括 solve()solveset() 在内的求解函数。虽然这对于单变量方程是可选的,但这是一个好的做法,因为它确保了 SymPy 会求解所需的符号。例如,您可能对 \(x\) 的解感兴趣,但 SymPy 却求解了 \(y\)

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

指定要解的变量可以确保 SymPy 对其进行求解:

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

确保从 solve() 的格式一致

solve() 生成多种输出,如 按类型解决输出 中所述。使用 dict=True 将提供一致的输出格式,这在以编程方式提取有关解决方案的信息时尤为重要。

要提取解决方案,您可以遍历字典列表:

>>> from sympy import parse_expr, solve, solveset
>>> from sympy.abc import x
>>> expr = "x^2 = y"
>>> parsed = parse_expr(expr, transformations="all")
>>> parsed
Eq(x**2, y)
>>> solutions = solve(parsed, x, dict=True)
>>> [solution[x] for solution in solutions]
[-sqrt(y), sqrt(y)]
>>> solveset(parsed, x)
{-sqrt(y), sqrt(y)}

可以加速 solve() 的选项

包含解决方案 使任何分母为零

通常,solve() 会检查是否有任何解使得分母为零,并自动排除这些解。如果你想包含这些解,并且加快 solve() 的速度(以获得无效解为风险),请设置 check=False

>>> from sympy import Symbol, sin, solve
>>> x = Symbol("x")
>>> solve(sin(x)/x, x, dict=True) # 0 is excluded
[{x: pi}]
>>> solve(sin(x)/x, x, dict=True, check=False) # 0 is not excluded
[{x: 0}, {x: pi}]

不要简化解决方案

通常,solve() 在返回结果之前会简化许多结果,并且(如果 check 不是 False)会在解和代入函数后得到的表达式上使用通用的 simplify() 函数,该表达式应为零。如果你不希望简化解,并且想要加快 solve() 的速度,请使用 simplify=False

>>> from sympy import solve
>>> from sympy.abc import x, y
>>> expr = x**2 - (y**5 - 3*y**3 + y**2 - 3)
>>> solve(expr, x, dict=True)
[{x: -sqrt(y**5 - 3*y**3 + y**2 - 3)}, {x: sqrt(y**5 - 3*y**3 + y**2 - 3)}]
>>> solve(expr, x, dict=True, simplify=False)
[{x: -sqrt((y + 1)*(y**2 - 3)*(y**2 - y + 1))}, {x: sqrt((y + 1)*(y**2 - 3)*(y**2 - y + 1))}]

解析表示方程的字符串

如果你自己创建表达式,我们建议 不要使用字符串解析来创建表达式。但如果你是程序化地读取字符串,这种方法很方便。

你可以将表示方程的字符串解析成 SymPy 可以理解的形式(例如,Eq 形式),然后求解解析后的表达式。从字符串解析方程需要你使用 转换 以便 SymPy 能够

  • 解释等号

  • 从您的变量创建符号

  • 使用更多的数学符号(而不是标准的Python符号),例如,指数运算符可以从 ^ 解析,而不必使用Python的 **

如果你已经有了 Eq (方程) 形式,你可以解析那个字符串:

>>> from sympy import parse_expr, solve, solveset
>>> from sympy.abc import x
>>> expr = "Eq(x^2, y)"
>>> parsed = parse_expr(expr, transformations="all")
>>> parsed
Eq(x**2, y)

SymPy 还可以使用 parse_latex()LaTeX 解析为表达式。

报告一个错误

如果你在使用这些命令时发现了一个错误,请在 SymPy 邮件列表 上发布问题。