用代数或数值方法求多项式的根¶
使用 SymPy 代数求解一元多项式的根。例如,求解 \(ax^2 + bx + c\) 关于 \(x\) 的根将得到 \(x = \frac{-b\pm\sqrt{b^2 - 4ac}}{2a}\)。
其他选择¶
代数求解多项式根的示例¶
以下是如何代数求解多项式根的示例:
>>> from sympy import roots
>>> from sympy.abc import x, a, b, c
>>> roots(a*x**2 + b*x + c, x)
{-b/(2*a) - sqrt(-4*a*c + b**2)/(2*a): 1,
-b/(2*a) + sqrt(-4*a*c + b**2)/(2*a): 1}
此示例重现了 求根公式。
查找多项式根的函数¶
有多个函数可用于查找多项式的根。
solve()
是一个通用的求解函数,可以找到根,但效率低于all_roots()
,并且是此列表中唯一不能传达根的重数的函数;solve()
也适用于 非多项式方程 和 非多项式方程组roots()
计算单变量多项式的符号根;对于大多数高次多项式(五次或更高次)将失败。nroots()
计算任何多项式的根的数值近似值,这些多项式的系数可以进行数值计算,无论系数是 有理数还是无理数。RootOf()
可以精确地表示任意次数多项式的所有根,只要系数是有理数。RootOf()
可以避免病态条件和返回虚假复数部分,因为它使用基于隔离区间的更精确但更慢的数值算法。以下两个函数使用RootOf()
,因此具有相同的属性。real_roots()
可以精确地找到任意次数多项式的所有实根;因为它只找到实根,所以它可能比找到所有根的函数效率更高。all_roots()
可以精确地找到任意次数多项式的所有根。
factor()
将多项式分解成不可约式,并可以揭示根位于系数环中。
每个函数都将在本页使用。
指南¶
参考 在函数调用中包含要求解的变量 和 使用精确值。
查找多项式的根¶
您可以通过多种方式代数地查找多项式的根。使用哪种方法取决于您是否
想要代数答案还是数值答案
想要每个根的重数(每个根作为解的次数)。在以下
expression
中表示 \((x+2)^2(x-3)\),根 -2 的重数为 2,因为 \(x+2\) 是平方,而 3 的重数为 1,因为 \(x-3\) 没有指数。类似地,对于symbolic
表达式,根 \(-a\) 的重数为 2,而根 \(b\) 的重数为 1。
>>> from sympy import solve, roots, real_roots, factor, nroots, RootOf, expand
>>> from sympy import Poly
>>> expression = (x+2)**2 * (x-3)
>>> symbolic = (x+a)**2 * (x-b)
无根重数的代数解¶
您可以使用 SymPy 的标准 solve()
函数,尽管它不会返回根的重数。
>>> solve(expression, x, dict=True)
[{x: -2}, {x: 3}]
>>> solve(symbolic, x, dict=True)
[{x: -a}, {x: b}]
solve()
将首先尝试使用 roots()
;如果不起作用,它将尝试使用 all_roots()
。对于三次方程(三次多项式)和四次方程(四次多项式),这意味着 solve()
将使用根的根式公式,而不是 RootOf()
,即使 RootOf 是可能的。三次和四次的公式通常会给出非常复杂的表达式,这些表达式在实践中没有用。因此,您可能希望将 solve()
参数 cubics
或 quartics
设置为 False
以返回 RootOf()
结果。
>>> from sympy import solve
>>> from sympy.abc import x
>>> # By default, solve() uses the radical formula, yielding very complex terms
>>> solve(x**4 - x + 1, x)
[-sqrt(2/(3*(1/16 + sqrt(687)*I/144)**(1/3)) + 2*(1/16 + sqrt(687)*I/144)**(1/3))/2 - sqrt(-2*(1/16 + sqrt(687)*I/144)**(1/3) - 2/sqrt(2/(3*(1/16 + sqrt(687)*I/144)**(1/3)) + 2*(1/16 + sqrt(687)*I/144)**(1/3)) - 2/(3*(1/16 + sqrt(687)*I/144)**(1/3)))/2,
sqrt(2/(3*(1/16 + sqrt(687)*I/144)**(1/3)) + 2*(1/16 + sqrt(687)*I/144)**(1/3))/2 - sqrt(-2*(1/16 + sqrt(687)*I/144)**(1/3) + 2/sqrt(2/(3*(1/16 + sqrt(687)*I/144)**(1/3)) + 2*(1/16 + sqrt(687)*I/144)**(1/3)) - 2/(3*(1/16 + sqrt(687)*I/144)**(1/3)))/2,
sqrt(-2*(1/16 + sqrt(687)*I/144)**(1/3) - 2/sqrt(2/(3*(1/16 + sqrt(687)*I/144)**(1/3)) + 2*(1/16 + sqrt(687)*I/144)**(1/3)) - 2/(3*(1/16 + sqrt(687)*I/144)**(1/3)))/2 - sqrt(2/(3*(1/16 + sqrt(687)*I/144)**(1/3)) + 2*(1/16 + sqrt(687)*I/144)**(1/3))/2,
sqrt(-2*(1/16 + sqrt(687)*I/144)**(1/3) + 2/sqrt(2/(3*(1/16 + sqrt(687)*I/144)**(1/3)) + 2*(1/16 + sqrt(687)*I/144)**(1/3)) - 2/(3*(1/16 + sqrt(687)*I/144)**(1/3)))/2 + sqrt(2/(3*(1/16 + sqrt(687)*I/144)**(1/3)) + 2*(1/16 + sqrt(687)*I/144)**(1/3))/2]
>>> # If you set quartics=False, solve() uses RootOf()
>>> solve(x**4 - x + 1, x, quartics=False)
[CRootOf(x**4 - x + 1, 0),
CRootOf(x**4 - x + 1, 1),
CRootOf(x**4 - x + 1, 2),
CRootOf(x**4 - x + 1, 3)]
将 solve()
中的第一个根写成标准数学符号可以突出显示其复杂性。
此外,对于五次方程(五次方程)或更高次方程,没有通用的根式公式,因此它们的 RootOf()
表示可能 是最好的选择。
带根重数的代数解¶
roots
¶
roots()
可以给出具有符号系数(即系数中包含符号)的多项式的根的显式表达式,如果 factor()
没有揭示它们。但是,它可能会针对某些多项式失败。以下是一些 roots()
的示例。
>>> roots(expression, x)
{-2: 2, 3: 1}
>>> roots(symbolic, x)
{-a: 2, b: 1}
它将结果作为字典返回,其中键是根(例如,-2),值是该根的重数(例如,2)。
roots()
函数使用多种技术(分解、分解、根式公式)来尽可能找到根的根式表达式。当它能找到一些根的根式表达式时,它会将它们及其重数一起返回。此函数将针对大多数高次多项式(五次或更高次)失败,因为它们没有根式解,并且不能保证它们有任何闭式解,正如 阿贝尔-鲁菲尼定理 所解释的那样。
分解方程¶
另一种方法是使用 factor()
分解多项式,它不会直接给出根,但可以给出更简单的表达式。
>>> expression_expanded = expand(expression)
>>> expression_expanded
x**3 + x**2 - 8*x - 12
>>> factor(expression_expanded)
(x - 3)*(x + 2)**2
>>> symbolic_expanded = expand(symbolic)
>>> symbolic_expanded
-a**2*b + a**2*x - 2*a*b*x + 2*a*x**2 - b*x**2 + x**3
>>> factor(symbolic_expanded)
(a + x)**2*(-b + x)
factor()
还可以将多项式分解到给定的 多项式环 中,这可以揭示根位于系数环中。例如,如果多项式具有有理系数,那么 factor()
将揭示任何有理根。如果系数是涉及例如符号 \(a\) 的多项式,这些多项式具有有理系数,那么任何作为 \(a\) 的多项式函数的根(具有有理系数)将被揭示。在此示例中,factor()
揭示了 \(x = a^2\) 和 \(x = -a^3 - a\) 是根。
>>> from sympy import expand, factor
>>> from sympy.abc import x, a
>>> p = expand((x - a**2)*(x + a + a**3))
>>> p
-a**5 + a**3*x - a**3 - a**2*x + a*x + x**2
>>> factor(p)
(-a**2 + x)*(a**3 + a + x)
带根重数的精确数值解¶
real_roots
¶
如果多项式的根是实数,使用 real_roots()
可确保仅返回实数根(而不是复数根或虚数根)。
>>> from sympy import real_roots
>>> from sympy.abc import x
>>> cubed = x**3 - 1
>>> # roots() returns real and complex roots
>>> roots(cubed)
{1: 1, -1/2 - sqrt(3)*I/2: 1, -1/2 + sqrt(3)*I/2: 1}
>>> # real_roots() returns only real roots
>>> real_roots(cubed)
[1]
real_roots()
调用 RootOf()
,因此对于所有根均为实数的方程,可以通过迭代方程的根数来获得相同的结果。
>>> [RootOf(expression, n) for n in range(3)]
[-2, -2, 3]
带根重数的近似数值解¶
nroots
¶
nroots()
给出了多项式根的近似数值解。这个例子表明它可能包含数值噪声,例如,应该为实根的根可能包含(微不足道的)虚部。
>>> nroots(expression)
[3.0, -2.0 - 4.18482169793536e-14*I, -2.0 + 4.55872552179222e-14*I]
如果想要实根的数值近似值,但又想确切知道哪些根是实根,那么最好的方法是使用 real_roots()
和 evalf()
>>> [r.n(2) for r in real_roots(expression)]
[-2.0, -2.0, 3.0]
>>> [r.is_real for r in real_roots(expression)]
[True, True, True]
nroots()
与 NumPy 的 roots()
函数类似。通常,这两个函数之间的区别在于 nroots()
更精确,但速度更慢。
nroots()
的一个主要优点是可以计算任何多项式的数值近似解,这些多项式的系数可以使用 evalf()
进行数值计算(即,它们没有自由符号)。相反,根据 Abel-Ruffini 定理,对于更高阶(五阶或更高阶)多项式,符号解可能无法实现。即使存在闭式解,它们也可能包含太多项,在实践中没有用。因此,即使存在闭式符号解,你可能也希望使用 nroots()
来查找近似数值解。例如,四阶(四次)多项式的闭式解可能相当复杂。
>>> rq0, rq1, rq2, rq3 = roots(x**4 + 3*x**2 + 2*x + 1)
>>> rq0
sqrt(-4 - 2*(-1/8 + sqrt(237)*I/36)**(1/3) + 4/sqrt(-2 + 7/(6*(-1/8 + sqrt(237)*I/36)**(1/3)) + 2*(-1/8 + sqrt(237)*I/36)**(1/3)) - 7/(6*(-1/8 + sqrt(237)*I/36)**(1/3)))/2 - sqrt(-2 + 7/(6*(-1/8 + sqrt(237)*I/36)**(1/3)) + 2*(-1/8 + sqrt(237)*I/36)**(1/3))/2
因此,你可能更喜欢近似数值解。
>>> rq0.n()
-0.349745826211722 - 0.438990337475312*I
nroots()
有时会对数值病态的多项式失败,例如 Wilkinson 多项式。使用 RootOf()
和 evalf()
(如 Numerically Evaluate CRootOf Roots 中所述)可以避免病态条件和返回虚假复数部分,因为它使用了一种更精确但速度更慢的基于隔离区间的数值算法。
复根¶
对于复根,可以使用类似的函数,例如 solve()
>>> from sympy import solve, roots, nroots, real_roots, expand, RootOf, CRootOf, Symbol
>>> from sympy import Poly
>>> from sympy.abc import x
>>> expression_complex = (x**2+4)**2 * (x-3)
>>> solve(expression_complex, x, dict=True)
[{x: 3}, {x: -2*I}, {x: 2*I}]
如果常数是符号的,你可能需要指定它们的域,以便 SymPy 识别出解不是实数。例如,指定 \(a\) 为正会导致虚根。
>>> a = Symbol("a", positive=True)
>>> symbolic_complex = (x**2+a)**2 * (x-3)
>>> solve(symbolic_complex, x, dict=True)
[{x: 3}, {x: -I*sqrt(a)}, {x: I*sqrt(a)}]
roots()
也会找到虚根或复根。
>>> roots(expression_complex, x)
{3: 1, -2*I: 2, 2*I: 2}
RootOf()
也会返回复根。
>>> [RootOf(expression_complex, n) for n in range(0,3)]
[3, -2*I, -2*I]
real_roots()
仅返回实根。
>>> real_roots(expression_complex)
[3]
real_roots()
的一个优点是,它比生成所有根更有效:RootOf()
对于复根可能很慢。
如果将表达式转换为多项式类 Poly
,可以使用它的 all_roots()
方法来查找根。
>>> expression_complex_poly = Poly(expression_complex)
>>> expression_complex_poly.all_roots()
[3, -2*I, -2*I, 2*I, 2*I]
使用解结果¶
从结果中提取解的方法取决于结果的形式。
列表 (all_roots
, real_roots
, nroots
)¶
可以使用标准的 Python 列表遍历技术,例如循环。在这里,我们将每个根代入表达式,以验证结果是否为 \(0\)
>>> expression = (x+2)**2 * (x-3)
>>> my_real_roots = real_roots(expression)
>>> my_real_roots
[-2, -2, 3]
>>> for root in my_real_roots:
... print(f"expression({root}) = {expression.subs(x, root)}")
expression(-2) = 0
expression(-2) = 0
expression(3) = 0
字典列表 (solve
)¶
字典 (roots
)¶
可以使用标准的 Python 列表遍历技术,例如循环遍历字典中的键和值。在这里,我们打印每个根的值和重数。
>>> my_roots = roots(expression)
>>> my_roots
{-2: 2, 3: 1}
>>> for root, multiplicity in my_roots.items():
... print(f"Root {root} has multiplicity of {multiplicity}")
Root 3 has multiplicity of 1
Root -2 has multiplicity of 2
表达式 (factor
)¶
可以使用各种 SymPy 技术来操作代数表达式,例如将符号或数值代入 \(x\)
>>> from sympy.abc import y
>>> factored = factor(expression_expanded)
>>> factored
(x - 3)*(x + 2)**2
>>> factored.subs(x, 2*y)
(2*y - 3)*(2*y + 2)**2
>>> factored.subs(x, 7)
324
权衡¶
数学精确度、根列表的完整性和速度¶
考虑高阶多项式 \(x^5 - x + 1 = 0\)。 nroots()
返回所有五个根的数值近似值。
>>> from sympy import roots, solve, real_roots, nroots
>>> from sympy.abc import x
>>> fifth_order = x**5 - x + 1
>>> nroots(fifth_order)
[-1.16730397826142,
-0.181232444469875 - 1.08395410131771*I,
-0.181232444469875 + 1.08395410131771*I,
0.764884433600585 - 0.352471546031726*I,
0.764884433600585 + 0.352471546031726*I]
roots()
有时可能只返回根的子集,或者如果它不能用根式表示任何根,则可能不返回任何根。在这种情况下,它不返回任何根(空集)。
>>> roots(fifth_order, x)
{}
但是,如果设置标志 strict=True
,roots()
会告知你无法返回所有根。
>>> roots(x**5 - x + 1, x, strict=True)
Traceback (most recent call last):
...
sympy.polys.polyerrors.UnsolvableFactorError: Strict mode: some factors cannot be solved in radicals, so a complete
list of solutions cannot be returned. Call roots with strict=False to
get solutions expressible in radicals (if there are any).
获取所有根,可能为隐式¶
solve()
将返回所有五个根作为 CRootOf
(ComplexRootOf()
) 类成员。
>>> fifth_order_solved = solve(fifth_order, x, dict=True)
>>> fifth_order_solved
[{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)}]
其中每个 CRootOf
中的第二个参数是根的索引。
数值计算 CRootOf
根¶
然后可以使用 evalf()
中的 n
来对这些 CRootOf
根进行数值计算。
>>> for root in fifth_order_solved:
... print(root[x].n(10))
-1.167303978
-0.1812324445 - 1.083954101*I
-0.1812324445 + 1.083954101*I
0.7648844336 - 0.352471546*I
0.7648844336 + 0.352471546*I
如果只对唯一的实根感兴趣,则使用 real_roots()
速度更快,因为它不会尝试查找复根。
>>> real_root = real_roots(fifth_order, x)
>>> real_root
[CRootOf(x**5 - x + 1, 0)]
>>> real_root[0].n(10)
-1.167303978
表示根¶
RootOf()
、real_roots()
和 all_roots()
可以精确地找到任意大次数多项式的所有根,尽管有 Abel-Ruffini 定理。这些函数允许精确地对根进行分类,并以符号方式进行操作。
>>> from sympy import init_printing
>>> init_printing()
>>> real_roots(fifth_order)
/ 5 \
[CRootOf\x - x + 1, 0/]
>>> r = r0, r1, r2, r3, r4 = Poly(fifth_order, x).all_roots(); r
/ 5 \ / 5 \ / 5 \ / 5 \ / 5 \
[CRootOf\x - x + 1, 0/, CRootOf\x - x + 1, 1/, CRootOf\x - x + 1, 2/, CRootOf\x - x + 1, 3/, CRootOf\x - x + 1, 4/]
>>> r0
/ 5 \
CRootOf\x - x + 1, 0/
现在,根已经精确找到,可以确定它们的属性,不受数值噪声的影响。例如,我们可以判断根是否是实数。如果请求根的 conjugate()
(实部相同,虚部符号相反),例如 r1
,并且它与另一个根 r2
完全相等,则将返回该根 r2
>>> r0.n()
-1.16730397826142
>>> r0.is_real
True
>>> r1.n()
-0.181232444469875 - 1.08395410131771*I
>>> r2.n()
-0.181232444469875 + 1.08395410131771*I
>>> r1
/ 5 \
CRootOf\x - x + 1, 1/
>>> r1.conjugate()
/ 5 \
CRootOf\x - x + 1, 2/
>>> r1.is_real
False
solve()
也能尽可能给出复根,但效率不如直接使用 all_roots()
。
RootOf()
以一种可以符号操作并计算到任意精度的方式精确地表示根。 RootOf()
表示使之成为可能,可以精确地
计算具有精确有理系数的多项式的所有根。
精确地确定每个根的重数。
精确地确定根是否为实数。
精确地对实根和复根进行排序。
精确地知道哪些根是彼此的共轭复数对。
精确地确定哪些根是有理数与无理数。
精确地表示每个可能的代数数。
其他数值方法,例如 NumPy 的 roots()
、nroots()
和 nsolve()
无法可靠地完成任何这些事情,如果可以的话。类似地,当使用 evalf()
进行数值计算时,solve()
或 roots()
返回的根式表达式也无法可靠地完成这些事情。
并非所有方程都可以求解¶
没有闭式解的方程¶
如上所述,高阶多项式(五次或更高次)不太可能有闭式解,因此您可能必须使用例如 RootOf
如上所述 来表示它们,或使用数值方法,例如 nroots
如上所述。
报告错误¶
如果您在使用这些命令时遇到错误,请在 SymPy 邮件列表 上发布问题。在问题解决之前,您可以使用其他 用于查找多项式根的函数 或尝试 可考虑的替代方案 之一。