解决指南¶
这些指南适用于多种类型的解决方法。
数值解法¶
无闭式解的方程¶
绝大多数任意非线性方程没有封闭形式的解。可解方程的类别基本上是:
线性方程
多项式,除非受到 阿贝尔-鲁菲尼定理 的限制(了解更多关于使用
GroebnerBasis
解决多项式的信息)可以通过反转一些超越函数来求解的方程
可以转化为上述情况的问题(例如,通过将三角函数转化为多项式)
还有一些其他特殊情况可以通过类似
Lambert W 函数
的方法解决。你可以通过上述任何一种方法对
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 邮件列表 上发布问题。