求解指南¶
这些指南适用于多种求解类型。
数值解¶
没有闭式解的方程¶
绝大多数任意非线性方程没有闭式解。可解的方程类别基本上是
线性方程
多项式,除了受Abel-Ruffini 定理限制的情况(使用
GroebnerBasis
了解更多关于求解多项式的信息)可以通过反转一些超越函数来求解的方程
可以转换为上述情况的问题(例如,将三角函数转换为多项式)
其他一些可以使用
Lambert W 函数
等方法求解的特殊情况您可以使用
decompose()
通过上述任何方法分解的方程式
SymPy 可能会反映您的方程式没有可以用代数 (符号) 表达的解,或者 SymPy 缺少算法来找到存在的封闭形式解,从而返回错误,例如 NotImplementedError
>>> 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 个项(如果要查看所有项,请在下面的代码块中水平滚动),而以数值形式表达时包含 8 个项
>>> 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 数字与 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
格式),然后求解解析后的表达式。从字符串解析方程式需要您使用 transformations
以便 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 邮件列表 上发布问题。