SymPy-1-13-中文文档-八-
SymPy 1.13 中文文档(八)
形式幂级数
计算和操作形式幂级数的方法。
class sympy.series.formal.FormalPowerSeries(*args)
表示函数的形式幂级数。
解释
不执行计算。此类仅用于表示级数。不执行检查。
用于计算系列的 fps().
另请参见
sympy.series.formal.fps
coeff_bell(n)
self.coeff_bell(n) 返回第二类贝尔多项式的序列。请注意 n 应为整数。
第二类贝尔多项式(有时称为“部分”贝尔多项式或不完全贝尔多项式)定义为
[B_{n,k}(x_1, x_2,\dotsc x_{n-k+1}) = \sum_{j_1+j_2+j_2+\dotsb=k \atop j_1+2j_2+3j_2+\dotsb=n} \frac{n!}{j_1!j_2!\dotsb j_{n-k+1}!} \left(\frac{x_1}{1!} \right)^{j_1} \left(\frac{x_2}{2!} \right)^{j_2} \dotsb \left(\frac{x_{n-k+1}}{(n-k+1)!} \right) ^{j_{n-k+1}}.]
bell(n, k, (x1, x2, ...))给出第二类贝尔多项式,(B_{n,k}(x_1, x_2, \dotsc, x_{n-k+1})).
另请参见
sympy.functions.combinatorial.numbers.bell
compose(other, x=None, n=6)
返回组合函数的形式幂级数的截断项,最多到指定的 n。
参数:
n : 数字,可选
指定应截断多项式的项的顺序。
解释
如果 f 和 g 是两个不同函数的形式幂级数,则组合形式幂级数 fp 的系数序列 ak 如下。
[\sum\limits_{k=0}^{n} b_k B_{n,k}(x_1, x_2, \dotsc, x_{n-k+1})]
示例
>>> from sympy import fps, sin, exp
>>> from sympy.abc import x
>>> f1 = fps(exp(x))
>>> f2 = fps(sin(x))
>>> f1.compose(f2, x).truncate()
1 + x + x**2/2 - x**4/8 - x**5/15 + O(x**6)
>>> f1.compose(f2, x).truncate(8)
1 + x + x**2/2 - x**4/8 - x**5/15 - x**6/240 + x**7/90 + O(x**8)
另请参见
sympy.functions.combinatorial.numbers.bell, sympy.series.formal.FormalPowerSeriesCompose
参考文献
[R824]
Comtet, Louis: Advanced combinatorics; the art of finite and infinite expansions. Reidel, 1974.
property infinite
返回系列的无限表示
integrate(x=None, **kwargs)
积分形式幂级数。
示例
>>> from sympy import fps, sin, integrate
>>> from sympy.abc import x
>>> f = fps(sin(x))
>>> f.integrate(x).truncate()
-1 + x**2/2 - x**4/24 + O(x**6)
>>> integrate(f, (x, 0, 1))
1 - cos(1)
inverse(x=None, n=6)
返回形式幂级数的截断项的逆,最多到指定的 n。
参数:
n : 数字,可选
指定应截断多项式的项的顺序。
解释
如果 f 和 g 是两个不同函数的形式幂级数,则组合形式幂级数 fp 的系数序列 ak 如下。
[\sum\limits_{k=0}^{n} (-1)^{k} x_0^{-k-1} B_{n,k}(x_1, x_2, \dotsc, x_{n-k+1})]
示例
>>> from sympy import fps, exp, cos
>>> from sympy.abc import x
>>> f1 = fps(exp(x))
>>> f2 = fps(cos(x))
>>> f1.inverse(x).truncate()
1 - x + x**2/2 - x**3/6 + x**4/24 - x**5/120 + O(x**6)
>>> f2.inverse(x).truncate(8)
1 + x**2/2 + 5*x**4/24 + 61*x**6/720 + O(x**8)
另请参见
sympy.functions.combinatorial.numbers.bell, sympy.series.formal.FormalPowerSeriesInverse
参考文献
[R825]
Comtet, Louis: 高级组合数学;有限和无限展开的艺术。Reidel, 1974.
polynomial(n=6)
截断的级数作为多项式。
解释
返回 f 的级数展开,直到阶数为 O(x**n) 作为多项式(不包括 O 项)。
product(other, x=None, n=6)
两个形式幂级数相乘,使用离散卷积并返回截断的指定阶数的项。
参数:
n : 数字, 可选
指定应截断多项式的项的顺序。
示例
>>> from sympy import fps, sin, exp
>>> from sympy.abc import x
>>> f1 = fps(sin(x))
>>> f2 = fps(exp(x))
>>> f1.product(f2, x).truncate(4)
x + x**2 + x**3/3 + O(x**4)
另请参见
sympy.discrete.convolutions, sympy.series.formal.FormalPowerSeriesProduct
truncate(n=6)
截断级数。
解释
返回 f 的截断级数展开,直到阶数为 O(x**n)。
如果 n 为 None,则返回一个无限迭代器。
sympy.series.formal.fps(f, x=None, x0=0, dir=1, hyper=True, order=4, rational=True, full=False)
生成 f 的形式幂级数。
参数:
x : 符号, 可选
如果 x 为 None 并且
f是单变量的,则将提供单变量符号,否则将引发错误。
x0 : 数字, 可选
执行级数展开的点。默认为 0。
dir : {1, -1, ‘+’, ‘-‘}, 可选
如果 dir 为 1 或 ‘+’,则从右边计算级数;如果为 -1 或 ‘-’,则从左边计算级数。对于光滑函数,此标志不会改变结果。默认为 1。
hyper : {True, False}, 可选
将 hyper 设置为 False 以跳过超几何算法。默认设置为 False。
order : 整数, 可选
f的导数的顺序,默认为 4。
rational : {True, False}, 可选
将 rational 设置为 False 以跳过有理算法。默认设置为 True。
full : {True, False}, 可选
将 full 设置为 True 以增加有理算法的范围。有关详细信息,请参阅
rational_algorithm()。默认设置为 False。
解释
返回关于 x = x0 的 f 的形式级数展开,关于 x 的形式为 FormalPowerSeries 对象。
形式幂级数使用使用不同算法计算的显式公式表示。
有关计算公式的详细信息,请参阅 compute_fps()。
示例
>>> from sympy import fps, ln, atan, sin
>>> from sympy.abc import x, n
有理函数
>>> fps(ln(1 + x)).truncate()
x - x**2/2 + x**3/3 - x**4/4 + x**5/5 + O(x**6)
>>> fps(atan(x), full=True).truncate()
x - x**3/3 + x**5/5 + O(x**6)
符号函数
>>> fps(x**n*sin(x**2), x).truncate(8)
-x**(n + 6)/6 + x**(n + 2) + O(x**(n + 8))
另请参见
sympy.series.formal.FormalPowerSeries, sympy.series.formal.compute_fps
sympy.series.formal.compute_fps(f, x, x0=0, dir=1, hyper=True, order=4, rational=True, full=False)
计算函数的形式幂级数的公式。
参数:
x:符号
x0:数值,可选
执行级数展开的点。默认为 0。
dir:{1, -1, ‘+’, ‘-‘},可选
如果 dir 为 1 或‘+’,则从右侧计算级数;如果为-1 或‘-’,则从左侧计算级数。对于平滑函数,此标志不会改变结果。默认为 1。
hyper:{True, False},可选
将 hyper 设置为 False 以跳过超几何算法。默认为 False。
order:整数,可选
f的导数阶数,默认为 4。
rational:{True, False},可选
将 rational 设置为 False 以跳过有理算法。默认为 True。
full:{True, False},可选
将 full 设置为 True 以增加有理算法的范围。详见
rational_algorithm()。默认为 False。
返回:
ak:序列
系数序列。
xk:序列
x 的幂序列。
ind:表达式
独立项。
mul:Pow
常见术语。
解释
尝试应用以下技术计算公式(按顺序):
-
rational_algorithm
-
超几何算法
参见
sympy.series.formal.rational_algorithm,sympy.series.formal.hyper_algorithm
class sympy.series.formal.FormalPowerSeriesCompose(*args)
表示两个函数的组合形式幂级数。
解释
不执行计算。项按逐项逻辑计算,而不是点对点逻辑。
FormalPowerSeries对象与FormalPowerSeriesCompose对象之间有两个区别。第一个参数包含组合中涉及的外部函数和内部函数。此外,系数序列包含将与自定义bell_seq有限序列相乘的通用序列。然后将有限项相加以获得最终项。
参见
sympy.series.formal.FormalPowerSeries,sympy.series.formal.FiniteFormalPowerSeries
property function
组合形式幂级数的函数。
class sympy.series.formal.FormalPowerSeriesInverse(*args)
表示形式幂级数的逆。
解释
不执行计算。项按逐项逻辑计算,而不是点对点逻辑。
FormalPowerSeries 对象与 FormalPowerSeriesInverse 对象之间有一个区别。系数序列包含一个通用序列,将乘以一个自定义的 bell_seq 有限序列。然后将添加有限项以得到最终项。
另见
sympy.series.formal.FormalPowerSeries,sympy.series.formal.FiniteFormalPowerSeries
property function
形式幂级数的反函数。
class sympy.series.formal.FormalPowerSeriesProduct(*args)
表示两个函数的形式幂级数的乘积。
解释
不进行计算。使用逐项逻辑计算项,而不是点对点逻辑。
FormalPowerSeries 对象与 FormalPowerSeriesProduct 对象之间有两个区别。第一个参数包含参与乘积的两个函数。此外,系数序列包含涉及函数的形式幂级数的系数序列。
另见
sympy.series.formal.FormalPowerSeries,sympy.series.formal.FiniteFormalPowerSeries
property function
两个形式幂级数的乘积函数。
class sympy.series.formal.FiniteFormalPowerSeries(*args)
产品、组合和反函数的基类
有理算法
sympy.series.formal.rational_independent(terms, x)
返回所有有理独立项的列表。
示例
>>> from sympy import sin, cos
>>> from sympy.series.formal import rational_independent
>>> from sympy.abc import x
>>> rational_independent([cos(x), sin(x)], x)
[cos(x), sin(x)]
>>> rational_independent([x**2, sin(x), x*sin(x), x**3], x)
[x**3 + x**2, x*sin(x) + sin(x)]
sympy.series.formal.rational_algorithm(f, x, k, order=4, full=False)
计算函数形式幂级数系数的有理算法。
参数:
x:符号
order:整数,可选
函数
f的导数阶数,默认为 4。
full:布尔值
返回:
formula:表达式
ind:表达
独立项。
order:整数
full:布尔值
解释
当 f(x) 或 f(x) 的某个导数是 x 的有理函数时适用。
rational_algorithm() 使用 apart() 函数进行部分分解。apart() 默认使用‘未定系数法’。通过设置 full=True,可以改用‘Bronstein 算法’。
寻找函数的 4 阶导数(默认)。可以使用 order 选项覆盖此行为。
示例
>>> from sympy import log, atan
>>> from sympy.series.formal import rational_algorithm as ra
>>> from sympy.abc import x, k
>>> ra(1 / (1 - x), x, k)
(1, 0, 0)
>>> ra(log(1 + x), x, k)
(-1/((-1)**k*k), 0, 1)
>>> ra(atan(x), x, k, full=True)
((-I/(2*(-I)**k) + I/(2*I**k))/k, 0, 1)
注
通过设置 full=True,可以增加使用 rational_algorithm 解决的可接受函数范围。应谨慎使用此选项,因为它会显著减慢由 apart() 函数返回的 RootSum 对象上的 doit 计算。尽可能使用 full=False。
参见
sympy.polys.partfrac.apart
参考文献
[R826]
形式幂级数 - Dominik Gruntz, Wolfram Koepf
[R827]
计算代数中的幂级数 - Wolfram Koepf
超几何算法
sympy.series.formal.simpleDE(f, x, g, order=4)
生成简单的 DE。
解释
DE 的形式为
[f^k(x) + \sum\limits_{j=0}^{k-1} A_j f^j(x) = 0]
其中 (A_j) 应该是 x 的有理函数。
生成至多 4 阶(默认)的 DE。DE 也可以具有自由参数。
通过增加阶数,可以找到更高阶的 DE。
返回一个元组 (DE, order)。
sympy.series.formal.exp_re(DE, r, k)
将具有常数系数的 DE(如指数式)转换为 RE。
解释
执行替换:
[f^j(x) \to r(k + j)]
规范化术语,使得术语的最低阶始终为 r(k)。
例子
>>> from sympy import Function, Derivative
>>> from sympy.series.formal import exp_re
>>> from sympy.abc import x, k
>>> f, r = Function('f'), Function('r')
>>> exp_re(-f(x) + Derivative(f(x)), r, k)
-r(k) + r(k + 1)
>>> exp_re(Derivative(f(x), x) + Derivative(f(x), (x, 2)), r, k)
r(k) + r(k + 1)
参见
sympy.series.formal.hyper_re
sympy.series.formal.hyper_re(DE, r, k)
将 DE 转换为 RE。
解释
执行替换:
[x^l f^j(x) \to (k + 1 - l)j . a]
规范化术语,使得术语的最低阶始终为 r(k)。
例子
>>> from sympy import Function, Derivative
>>> from sympy.series.formal import hyper_re
>>> from sympy.abc import x, k
>>> f, r = Function('f'), Function('r')
>>> hyper_re(-f(x) + Derivative(f(x)), r, k)
(k + 1)*r(k + 1) - r(k)
>>> hyper_re(-x*f(x) + Derivative(f(x), (x, 2)), r, k)
(k + 2)*(k + 3)*r(k + 3) - r(k)
参见
sympy.series.formal.exp_re
sympy.series.formal.rsolve_hypergeometric(f, x, P, Q, k, m)
解决超几何类型的 RE。
返回:
formula : 表达式
ind : 表达式
独立项。
order : 整数
解释
尝试解决形式为 RE 的方程
Q(k)a(k + m) - P(k)a(k)
保持超几何类型的变换:
- x**nf(x): b(k + m) = R(k - n)b(k)
- f(Ax): b(k + m) = A**mR(k)*b(k)
- f(x**n): b(k + nm) = R(k/n)b(k)
- f(x**(1/m)): b(k + 1) = R(km)b(k)
- f’(x): b(k + m) = ((k + m + 1)/(k + 1))R(k + 1)b(k)
一些这些变换已被用来解决 RE。
例子
>>> from sympy import exp, ln, S
>>> from sympy.series.formal import rsolve_hypergeometric as rh
>>> from sympy.abc import x, k
>>> rh(exp(x), x, -S.One, (k + 1), k, 1)
(Piecewise((1/factorial(k), Eq(Mod(k, 1), 0)), (0, True)), 1, 1)
>>> rh(ln(1 + x), x, k**2, k*(k + 1), k, 1)
(Piecewise(((-1)**(k - 1)*factorial(k - 1)/RisingFactorial(2, k - 1),
Eq(Mod(k, 1), 0)), (0, True)), x, 2)
参考文献
[R828]
形式幂级数 - Dominik Gruntz, Wolfram Koepf
[R829]
计算代数中的幂级数 - Wolfram Koepf
sympy.series.formal.solve_de(f, x, DE, order, g, k)
解决 DE。
返回:
formula : 表达式
ind : 表达式
独立项。
order : 整数
解释
尝试通过将其转换为包含两项的 RE 或将其转换为具有常数系数的 DE 来解决 DE。
例子
>>> from sympy import Derivative as D, Function
>>> from sympy import exp, ln
>>> from sympy.series.formal import solve_de
>>> from sympy.abc import x, k
>>> f = Function('f')
>>> solve_de(exp(x), x, D(f(x), x) - f(x), 1, f, k)
(Piecewise((1/factorial(k), Eq(Mod(k, 1), 0)), (0, True)), 1, 1)
>>> solve_de(ln(1 + x), x, (x + 1)*D(f(x), x, 2) + D(f(x)), 2, f, k)
(Piecewise(((-1)**(k - 1)*factorial(k - 1)/RisingFactorial(2, k - 1),
Eq(Mod(k, 1), 0)), (0, True)), x, 2)
sympy.series.formal.hyper_algorithm(f, x, k, order=4)
用于计算形式幂级数的超几何算法。
解释
步骤:
-
生成 DE
-
将 DE 转换为 RE
-
解决 RE
例子
>>> from sympy import exp, ln
>>> from sympy.series.formal import hyper_algorithm
>>> from sympy.abc import x, k
>>> hyper_algorithm(exp(x), x, k)
(Piecewise((1/factorial(k), Eq(Mod(k, 1), 0)), (0, True)), 1, 1)
>>> hyper_algorithm(ln(1 + x), x, k)
(Piecewise(((-1)**(k - 1)*factorial(k - 1)/RisingFactorial(2, k - 1),
Eq(Mod(k, 1), 0)), (0, True)), x, 2)
参见
sympy.series.formal.simpleDE, sympy.series.formal.solve_de
序列的极限
提供计算具有无穷大序列的项的极限的方法。
sympy.series.limitseq.difference_delta(expr, n=None, step=1)
差分操作符。
解释
差分操作符的离散模拟。给定一个序列 x[n],返回序列 x[n + step] - x[n]。
例子
>>> from sympy import difference_delta as dd
>>> from sympy.abc import n
>>> dd(n*(n + 1), n)
2*n + 2
>>> dd(n*(n + 1), n, 2)
4*n + 6
参考文献
[R833]
reference.wolfram.com/language/ref/DifferenceDelta.html
sympy.series.limitseq.dominant(expr, n)
找到和中的支配项,即支配每个其他项的项。
解释
如果 limit(a/b, n, oo) 是 oo,则 a 支配 b。如果 limit(a/b, n, oo) 是 0,则 b 支配 a。否则,a 和 b 是可比较的。
如果没有唯一的支配项,则返回 None。
例子
>>> from sympy import Sum
>>> from sympy.series.limitseq import dominant
>>> from sympy.abc import n, k
>>> dominant(5*n**3 + 4*n**2 + n + 1, n)
5*n**3
>>> dominant(2**n + Sum(k, (k, 0, n)), n)
2**n
另请参阅
sympy.series.limitseq.dominant
sympy.series.limitseq.limit_seq(expr, n=None, trials=5)
找到序列随着索引 n 趋向于正无穷的极限。
参数:
expr : 表达式
SymPy 表达式用于序列的第
n-th项
n : 符号,可选
序列的索引,一个趋向于正无穷的整数。如果为 None,则从表达式推断,除非表达式具有多个符号。
trials: int, optional :试验次数
算法高度递归。如果算法返回
None,则trials是防止无限递归的保护措施,请尝试增加trials。
可接受的项
该算法设计用于由有理函数、不定和、不定乘积构建的序列,该序列依赖于一个不定的 n。允许交替符号的项,但不支持更复杂的振荡行为。
例子
>>> from sympy import limit_seq, Sum, binomial
>>> from sympy.abc import n, k, m
>>> limit_seq((5*n**3 + 3*n**2 + 4) / (3*n**3 + 4*n - 5), n)
5/3
>>> limit_seq(binomial(2*n, n) / Sum(binomial(2*k, k), (k, 1, n)), n)
3/4
>>> limit_seq(Sum(k**2 * Sum(2**m/m, (m, 1, k)), (k, 1, n)) / (2**n*n), n)
4
另请参阅
sympy.series.limitseq.dominant
参考文献
[R834]
计算序列的极限 - Manuel Kauers
简化
-
简化
-
超几何展开
-
傅宏光的三角简化
简化
sympy.simplify.simplify.simplify(expr, ratio=1.7, measure=<function count_ops>, rational=False, inverse=False, doit=True, **kwargs)
简化给定的表达式。
解释
简化并不是一个明确定义的术语,而且此函数尝试的确切策略可能会在 SymPy 的未来版本中改变。如果你的算法依赖于“简化”(无论是什么),请尝试确定你确切需要什么 - 是powsimp()?radsimp()?together()?logcombine()?还是其他什么?然后直接使用这个特定的函数,因为这些都是明确定义的,因此你的算法将更加健壮。
尽管如此,特别是对于交互式使用或者当你对表达式的结构一无所知时,simplify()会尝试应用智能启发式方法使输入表达式“更简单”。例如:
>>> from sympy import simplify, cos, sin
>>> from sympy.abc import x, y
>>> a = (x + x**2)/(x*sin(y)**2 + x*cos(y)**2)
>>> a
(x**2 + x)/(x*sin(y)**2 + x*cos(y)**2)
>>> simplify(a)
x + 1
请注意,我们可以通过使用特定的简化函数获得相同的结果:
>>> from sympy import trigsimp, cancel
>>> trigsimp(a)
(x**2 + x)/x
>>> cancel(_)
x + 1
在某些情况下,应用simplify()实际上可能会导致一些更复杂的表达式。默认情况下,ratio=1.7防止更极端的情况:如果(结果长度)/(输入长度)> ratio,则返回未修改的输入。measure参数允许您指定用于确定表达式复杂性的函数。该函数应接受一个表达式作为唯一参数,并返回一个数字,以便如果表达式a比表达式b更复杂,则measure(a) > measure(b)。默认的度量函数是count_ops(),它返回表达式中操作的总数。
例如,如果ratio=1,simplify的输出不能比输入更长。
>>> from sympy import sqrt, simplify, count_ops, oo
>>> root = 1/(sqrt(2)+3)
由于simplify(root)会导致略长一些的表达式,所以 root 将原样返回:
>>> simplify(root, ratio=1) == root
True
如果ratio=oo,简化将无论如何被应用:
>>> count_ops(simplify(root, ratio=oo)) > count_ops(root)
True
请注意,最短的表达式未必是最简单的,因此将ratio设置为 1 可能并不是一个好主意。从启发式的角度来看,默认值ratio=1.7似乎是一个合理的选择。
您可以根据您认为应该表示输入表达式的“大小”或“复杂性”的内容轻松定义自己的度量函数。请注意,某些选择,例如lambda expr: len(str(expr))可能看起来是良好的度量标准,但可能存在其他问题(在这种情况下,度量函数可能会因非常大的表达式而使简化变慢)。如果您不知道什么是一个好的度量标准,那么默认的count_ops是一个不错的选择。
例如:
>>> from sympy import symbols, log
>>> a, b = symbols('a b', positive=True)
>>> g = log(a) + log(b) + log(a)*log(1/b)
>>> h = simplify(g)
>>> h
log(a*b**(1 - log(a)))
>>> count_ops(g)
8
>>> count_ops(h)
5
因此,您可以看到,使用count_ops度量标准时,h比g更简单。然而,我们可能不喜欢简化(在这种情况下,使用logcombine)如何创建了b**(log(1/a) + 1)项。减少这种情况的简单方法是在count_ops中给予幂运算更多的权重。我们可以通过使用visual=True选项来实现这一点:
>>> print(count_ops(g, visual=True))
2*ADD + DIV + 4*LOG + MUL
>>> print(count_ops(h, visual=True))
2*LOG + MUL + POW + SUB
>>> from sympy import Symbol, S
>>> def my_measure(expr):
... POW = Symbol('POW')
... # Discourage powers by giving POW a weight of 10
... count = count_ops(expr, visual=True).subs(POW, 10)
... # Every other operation gets a weight of 1 (the default)
... count = count.replace(Symbol, type(S.One))
... return count
>>> my_measure(g)
8
>>> my_measure(h)
14
>>> 15./8 > 1.7 # 1.7 is the default ratio
True
>>> simplify(g, measure=my_measure)
-log(a)*log(b) + log(a) + log(b)
注意,因为simplify()内部尝试许多不同的简化策略,然后使用度量函数进行比较,所以通过这种方式得到一个完全不同的结果,仍然与输入表达式不同。
如果 rational=True,则在简化之前,浮点数将重新转换为有理数。如果 rational=None,则浮点数将转换为有理数,但结果将重新转换为浮点数。如果 rational=False(默认值),则浮点数将不做任何处理。
如果 inverse=True,将假定可以按任意顺序取消反函数的组合,例如,asin(sin(x))将返回 x,而不检查 x 是否属于此关系为真的集合。默认值为 False。
注意,simplify()会自动在最终表达式上调用 doit()。可以通过传递 doit=False 参数来避免这种行为。
此外,应注意简化布尔表达式并不是完全定义良好的。如果表达式偏向于自动评估(例如 Eq() 或 Or()),简化将返回 True 或 False,如果可以确定其真值。如果表达式默认不被评估(例如 Predicate()),简化将不会减少它,并且您应该使用 refine() 或 ask() 函数。此不一致性将在将来版本中解决。
见也
sympy.assumptions.refine.refine
使用假设进行简化。
sympy.assumptions.ask.ask
使用假设查询布尔表达式。
sympy.simplify.simplify.separatevars(expr, symbols=[], dict=False, force=False)
如果可能,分离表达式中的变量。默认情况下,它会根据表达式中的所有符号进行分离,并收集与符号无关的常数系数。
解释
如果 dict=True,则分离的项将以符号为键返回为字典。默认情况下,表达式中的所有符号都会出现为键;如果提供了符号,则所有这些符号将被用作键,表达式中包含其他符号或非符号的任何项将以字符串 'coeff' 为键返回。(对于符号为 None 的情况,将返回以 'coeff' 为键的表达式字典。)
如果 force=True,则会分离幂的基数,而不考虑所涉及符号的假设。
注意
因此,因为乘法的顺序由 Mul 决定,分离的表达式可能不一定被分组在一起。
尽管在某些表达式中分解是必要的以分离变量,但并非所有情况都需要,因此不应指望返回的因子是分解的。
示例
>>> from sympy.abc import x, y, z, alpha
>>> from sympy import separatevars, sin
>>> separatevars((x*y)**y)
(x*y)**y
>>> separatevars((x*y)**y, force=True)
x**y*y**y
>>> e = 2*x**2*z*sin(y)+2*z*x**2
>>> separatevars(e)
2*x**2*z*(sin(y) + 1)
>>> separatevars(e, symbols=(x, y), dict=True)
{'coeff': 2*z, x: x**2, y: sin(y) + 1}
>>> separatevars(e, [x, y, alpha], dict=True)
{'coeff': 2*z, alpha: 1, x: x**2, y: sin(y) + 1}
如果表达式实际上不可分离,或者仅部分可分离,则 separatevars 将尽力使用分解进行分离。
>>> separatevars(x + x*y - 3*x**2)
-x*(3*x - y - 1)
如果表达式不可分离,则返回未更改的 expr 或(如果 dict=True)则返回 None。
>>> eq = 2*x + y*sin(x)
>>> separatevars(eq) == eq
True
>>> separatevars(2*x + y*sin(x), symbols=(x, y), dict=True) is None
True
sympy.simplify.simplify.nthroot(expr, n, max_len=4, prec=15)
计算和的根号的实数第 n 次根。
参数:
expr:根号和的和
n:整数
max_len:作为常数传递给 nsimplify 的根号的最大数目
算法
首先使用 nsimplify 获取一个候选根;如果它不是根,则计算最小多项式;答案是其根之一。
示例
>>> from sympy.simplify.simplify import nthroot
>>> from sympy import sqrt
>>> nthroot(90 + 34*sqrt(7), 3)
sqrt(7) + 3
sympy.simplify.simplify.kroneckersimp(expr)
用 KroneckerDelta 简化表达式。
当前尝试的唯一简化是识别乘法取消:
示例
>>> from sympy import KroneckerDelta, kroneckersimp
>>> from sympy.abc import i
>>> kroneckersimp(1 + KroneckerDelta(0, i) * KroneckerDelta(1, i))
1
sympy.simplify.simplify.besselsimp(expr)
简化贝塞尔类型函数。
解释
此例程试图简化贝塞尔类型函数。目前仅适用于 Bessel J 和 I 函数,然而。它通过依次查看所有这样的函数,并消除参数前的 “I” 和 “-1” 的因子(实际上是它们的极坐标等效物)。然后,使用三角函数重写半整数阶的函数,并使用低阶函数重写整数阶(> 1)的函数。最后,如果表达式已更改,则使用 factor() 计算结果的因式分解。
>>> from sympy import besselj, besseli, besselsimp, polar_lift, I, S
>>> from sympy.abc import z, nu
>>> besselsimp(besselj(nu, z*polar_lift(-1)))
exp(I*pi*nu)*besselj(nu, z)
>>> besselsimp(besseli(nu, z*polar_lift(-I)))
exp(-I*pi*nu/2)*besselj(nu, z)
>>> besselsimp(besseli(S(-1)/2, z))
sqrt(2)*cosh(z)/(sqrt(pi)*sqrt(z))
>>> besselsimp(z*besseli(0, z) + z*(besseli(2, z))/2 + besseli(1, z))
3*z*besseli(0, z)/2
sympy.simplify.simplify.hypersimp(f, k)
给定组合项 f(k),简化其连续项比率即 f(k+1)/f(k)。输入项可以由具有 gamma 特殊函数等效表示的函数和整数序列组成。
解释
该算法执行三个基本步骤:
-
尽可能用 gamma 重写所有函数。
-
用整数、绝对常数指数的 gamma 的乘积重写所有 gamma 的出现。
-
对于嵌套分数、幂的简化操作,如果结果表达式是多项式的商,则减少它们的总次数。
如果 f(k) 是超几何的,则结果是最小次数的多项式商。否则返回 None。
有关实现算法的更多信息,请参考:
- W. Koepf,《m 倍超几何求和的算法》,符号计算杂志(1995)20,399-417
sympy.simplify.simplify.hypersimilar(f, g, k)
如果 f 和 g 是超相似的,则返回 True。
解释
超几何意义上的相似性意味着 f(k) 和 g(k) 的商是 k 的有理函数。这个过程在解决递推关系中很有用。
有关详细信息,请参见 hypersimp()。
sympy.simplify.simplify.nsimplify(expr, constants=(), tolerance=None, full=False, rational=None, rational_conversion='base10')
为一个数字找到简单的表示,或者如果存在自由符号或者 rational=True,则用它们的有理数等价物替换 Floats。如果没有更改且 rational 不为 False,则 Floats 将至少被转换为 Rationals。
解释
对于数值表达式,寻找一个能够数值匹配给定数值表达式的简单公式(输入应至少能够 evalf 到 30 位精度)。
可选地,可以给出包含在公式中的(有理数独立的)常数列表。
可以设置更低的容差来寻找不那么精确的匹配。如果未给出容差,则最不精确的值将设置容差(例如,浮点数默认精度为 15 位数字,因此容差为 10**-15)。
使用 full=True 进行更广泛的搜索(当设置容差较低时寻找更简单的数字非常有用)。
在转换为有理数时,如果 rational_conversion='base10'(默认),则使用其基于十进制的浮点数表示来转换浮点数为有理数。当 rational_conversion='exact' 时,则使用精确的基于二进制的表示来转换。
示例
>>> from sympy import nsimplify, sqrt, GoldenRatio, exp, I, pi
>>> nsimplify(4/(1+sqrt(5)), [GoldenRatio])
-2 + 2*GoldenRatio
>>> nsimplify((1/(exp(3*pi*I/5)+1)))
1/2 - I*sqrt(sqrt(5)/10 + 1/4)
>>> nsimplify(I**I, [pi])
exp(-pi/2)
>>> nsimplify(pi, tolerance=0.01)
22/7
>>> nsimplify(0.333333333333333, rational=True, rational_conversion='exact')
6004799503160655/18014398509481984
>>> nsimplify(0.333333333333333, rational=True)
1/3
另见
sympy.core.function.nfloat
sympy.simplify.simplify.posify(eq)
返回 eq(使通用符号变为正的)和包含旧符号与新符号映射的字典。
解释
任何具有 positive=None 的符号将被替换为具有相同名称的正虚拟符号。这种替换将允许更多的符号处理表达式,特别是涉及幂次和对数的表达式。
还返回一个可以发送到 subs 以将 eq 恢复为其原始符号的字典。
>>> from sympy import posify, Symbol, log, solve
>>> from sympy.abc import x
>>> posify(x + Symbol('p', positive=True) + Symbol('n', negative=True))
(_x + n + p, {_x: x})
>>> eq = 1/x
>>> log(eq).expand()
log(1/x)
>>> log(posify(eq)[0]).expand()
-log(_x)
>>> p, rep = posify(eq)
>>> log(p).expand().subs(rep)
-log(x)
可以对表达式的可迭代项应用相同的转换方法:
>>> eq = x**2 - 4
>>> solve(eq, x)
[-2, 2]
>>> eq_x, reps = posify([eq, x]); eq_x
[_x**2 - 4, _x]
>>> solve(*eq_x)
[2]
sympy.simplify.simplify.logcombine(expr, force=False)
使用以下规则取对数并将它们结合起来:
-
如果两者均为正,则
log(x) + log(y) == log(x*y)。 -
如果
x是正数且a是实数,则a*log(x) == log(x**a)。
如果 force 是 True,则假设以上假设将被认为在数量上不存在任何假设的情况下成立。例如,如果 a 是虚数或参数为负数,则 force 不会执行组合;但如果 a 是一个没有假设的符号,则变化将发生。
示例
>>> from sympy import Symbol, symbols, log, logcombine, I
>>> from sympy.abc import a, x, y, z
>>> logcombine(a*log(x) + log(y) - log(z))
a*log(x) + log(y) - log(z)
>>> logcombine(a*log(x) + log(y) - log(z), force=True)
log(x**a*y/z)
>>> x,y,z = symbols('x,y,z', positive=True)
>>> a = Symbol('a', real=True)
>>> logcombine(a*log(x) + log(y) - log(z))
log(x**a*y/z)
转换仅限于包含对数的因子和/或项,因此结果取决于展开的初始状态:
>>> eq = (2 + 3*I)*log(x)
>>> logcombine(eq, force=True) == eq
True
>>> logcombine(eq.expand(), force=True)
log(x**2) + I*log(x**3)
另见
posify
用具有正假设的符号替换所有符号。
sympy.core.function.expand_log
展开产品和幂的对数;与 logcombine 的相反操作。
sympy.simplify.radsimp.radsimp(expr, symbolic=True, max_terms=4)
通过去除平方根来有理化分母。
解释
注意,从radsimp返回的表达式必须谨慎使用,因为如果分母包含符号,可能会进行违反简化过程假设的替换:对于分母匹配a + b*sqrt(c)的情况,要求a != +/-b*sqrt(c)。(如果没有符号,通过收集sqrt(c)项来使得变量a不包含sqrt(c)来使该假设成立。)如果不希望对符号分母进行简化,请将symbolic设为False。
如果超过max_terms个根式项,则返回原始表达式。
示例
>>> from sympy import radsimp, sqrt, Symbol, pprint
>>> from sympy import factor_terms, fraction, signsimp
>>> from sympy.simplify.radsimp import collect_sqrt
>>> from sympy.abc import a, b, c
>>> radsimp(1/(2 + sqrt(2)))
(2 - sqrt(2))/2
>>> x,y = map(Symbol, 'xy')
>>> e = ((2 + 2*sqrt(2))*x + (2 + sqrt(8))*y)/(2 + sqrt(2))
>>> radsimp(e)
sqrt(2)*(x + y)
除去 gcd 外不进行任何简化。但是,可以通过收集平方根项稍微优化结果:
>>> r2 = sqrt(2)
>>> r5 = sqrt(5)
>>> ans = radsimp(1/(y*r2 + x*r2 + a*r5 + b*r5)); pprint(ans)
___ ___ ___ ___
\/ 5 *a + \/ 5 *b - \/ 2 *x - \/ 2 *y
------------------------------------------
2 2 2 2
5*a + 10*a*b + 5*b - 2*x - 4*x*y - 2*y
>>> n, d = fraction(ans)
>>> pprint(factor_terms(signsimp(collect_sqrt(n))/d, radical=True))
___ ___
\/ 5 *(a + b) - \/ 2 *(x + y)
------------------------------------------
2 2 2 2
5*a + 10*a*b + 5*b - 2*x - 4*x*y - 2*y
如果分母中的根式无法移除或没有分母,则将返回原始表达式。
>>> radsimp(sqrt(2)*x + sqrt(2))
sqrt(2)*x + sqrt(2)
带有符号的结果并不总是对所有替换有效:
>>> eq = 1/(a + b*sqrt(c))
>>> eq.subs(a, b*sqrt(c))
1/(2*b*sqrt(c))
>>> radsimp(eq).subs(a, b*sqrt(c))
nan
如果symbolic=False,则符号分母将不会转换(但数字分母仍将被处理):
>>> radsimp(eq, symbolic=False)
1/(a + b*sqrt(c))
sympy.simplify.radsimp.rad_rationalize(num, den)
通过移除分母中的平方根来使num/den有理化;num和den是其平方是正有理数的项的和。
示例
>>> from sympy import sqrt
>>> from sympy.simplify.radsimp import rad_rationalize
>>> rad_rationalize(sqrt(3), 1 + sqrt(2)/3)
(-sqrt(3) + sqrt(6)/3, -7/9)
sympy.simplify.radsimp.collect(expr, syms, func=None, evaluate=None, exact=False, distribute_order_term=True)
收集表达式的加法项。
解释
此函数根据表达式中的符号(术语)的列表收集表达式的加法项,直到有理指数幂为止。这里的术语符号指的是任意表达式,可以包含幂、乘积、和等。换句话说,符号是一个模式,将在表达式的术语中搜索。
输入表达式不会被collect()扩展,因此用户应提供一个适当形式的表达式。这使得collect()更加可预测,因为没有任何神奇的背后操作。然而,需要注意的是,通过expand_power_base()函数将乘积的幂转换为幂的乘积。
有两种可能的输出类型。首先,如果设置了evaluate标志,则该函数将返回带有收集项的表达式,否则将返回带有直到有理幂为键的字典和收集系数为值。
示例
>>> from sympy import S, collect, expand, factor, Wild
>>> from sympy.abc import a, b, c, x, y
此函数可以收集多项式或有理表达式中的符号系数。它将能够找到所有整数或有理的收集变量的幂:
>>> collect(a*x**2 + b*x**2 + a*x - b*x + c, x)
c + x**2*(a + b) + x*(a - b)
结果可以以字典形式达到相同效果:
>>> d = collect(a*x**2 + b*x**2 + a*x - b*x + c, x, evaluate=False)
>>> d[x**2]
a + b
>>> d[x]
a - b
>>> d[S.One]
c
您还可以处理多变量多项式。但请记住,此函数是贪婪的,因此它一次只关心一个符号,按照规定的顺序:
>>> collect(x**2 + y*x**2 + x*y + y + a*y, [x, y])
x**2*(y + 1) + x*y + y*(a + 1)
还可以使用更复杂的表达式作为模式:
>>> from sympy import sin, log
>>> collect(a*sin(2*x) + b*sin(2*x), sin(2*x))
(a + b)*sin(2*x)
>>> collect(a*x*log(x) + b*(x*log(x)), x*log(x))
x*(a + b)*log(x)
您可以在模式中使用通配符:
>>> w = Wild('w1')
>>> collect(a*x**y - b*x**y, w**y)
x**y*(a - b)
还可以处理符号幂,尽管它具有更复杂的行为,因为在这种情况下,幂的基数和指数的符号部分被视为单个符号:
>>> collect(a*x**c + b*x**c, x)
a*x**c + b*x**c
>>> collect(a*x**c + b*x**c, x**c)
x**c*(a + b)
但是,如果您将有理数合并到指数中,则会得到众所周知的行为:
>>> collect(a*x**(2*c) + b*x**(2*c), x**c)
x**(2*c)*(a + b)
还请注意,关于 collect() 函数的所有先前陈述事实也适用于指数函数,因此您可以获得:
>>> from sympy import exp
>>> collect(a*exp(2*x) + b*exp(2*x), exp(x))
(a + b)*exp(2*x)
如果您只想收集某些符号的特定幂次,则将 exact 标志设置为 True:
>>> collect(a*x**7 + b*x**7, x, exact=True)
a*x**7 + b*x**7
>>> collect(a*x**7 + b*x**7, x**7, exact=True)
x**7*(a + b)
如果您想要对包含符号的任何对象进行收集,请将 exact 设置为 None:
>>> collect(x*exp(x) + sin(x)*y + sin(x)*2 + 3*x, x, exact=None)
x*exp(x) + 3*x + (y + 2)*sin(x)
>>> collect(a*x*y + x*y + b*x + x, [x, y], exact=None)
x*y*(a + 1) + x*(b + 1)
您还可以将此函数应用于微分方程,其中可以收集任意阶导数。请注意,如果针对函数或函数的导数进行收集,则该函数的所有导数也将被收集。使用 exact=True 可以防止此情况发生:
>>> from sympy import Derivative as D, collect, Function
>>> f = Function('f') (x)
>>> collect(a*D(f,x) + b*D(f,x), D(f,x))
(a + b)*Derivative(f(x), x)
>>> collect(a*D(D(f,x),x) + b*D(D(f,x),x), f)
(a + b)*Derivative(f(x), (x, 2))
>>> collect(a*D(D(f,x),x) + b*D(D(f,x),x), D(f,x), exact=True)
a*Derivative(f(x), (x, 2)) + b*Derivative(f(x), (x, 2))
>>> collect(a*D(f,x) + b*D(f,x) + a*f + b*f, f)
(a + b)*f(x) + (a + b)*Derivative(f(x), x)
或者您甚至可以同时匹配导数顺序和指数:
>>> collect(a*D(D(f,x),x)**2 + b*D(D(f,x),x)**2, D(f,x))
(a + b)*Derivative(f(x), (x, 2))**2
最后,您可以对每个收集的系数应用一个函数。例如,您可以因式分解多项式的符号系数:
>>> f = expand((x + a + 1)**3)
>>> collect(f, x, factor)
x**3 + 3*x**2*(a + 1) + 3*x*(a + 1)**2 + (a + 1)**3
注意
参数应当以展开形式给出,因此在调用该函数之前,可能需要调用 expand() 函数。
另见
collect_const, collect_sqrt, rcollect
sympy.simplify.radsimp.rcollect(expr, *vars)
递归地收集表达式中的和项。
示例
>>> from sympy.simplify import rcollect
>>> from sympy.abc import x, y
>>> expr = (x**2*y + x*y + x + y)/(x + y)
>>> rcollect(expr, y)
(x + y*(x**2 + x + 1))/(x + y)
另见
collect, collect_const, collect_sqrt
sympy.simplify.radsimp.collect_sqrt(expr, evaluate=None)
将具有公共平方根的项合并在一起的表达式返回。如果 evaluate 参数为 False,则将返回包含有平方根项的 Add 的项数,如果非零,则将返回 Add 的项,否则将返回表达式本身作为单个项。如果 evaluate 参数为 True,则将返回包含任何已收集项的表达式。
注意:因为 I = sqrt(-1),它也被收集了。
示例
>>> from sympy import sqrt
>>> from sympy.simplify.radsimp import collect_sqrt
>>> from sympy.abc import a, b
>>> r2, r3, r5 = [sqrt(i) for i in [2, 3, 5]]
>>> collect_sqrt(a*r2 + b*r2)
sqrt(2)*(a + b)
>>> collect_sqrt(a*r2 + b*r2 + a*r3 + b*r3)
sqrt(2)*(a + b) + sqrt(3)*(a + b)
>>> collect_sqrt(a*r2 + b*r2 + a*r3 + b*r5)
sqrt(3)*a + sqrt(5)*b + sqrt(2)*(a + b)
如果 evaluate 参数为 False,则参数将被排序并作为列表返回,并返回包含平方根项的数量:
>>> collect_sqrt(a*r2 + b*r2 + a*r3 + b*r5, evaluate=False)
((sqrt(3)*a, sqrt(5)*b, sqrt(2)*(a + b)), 3)
>>> collect_sqrt(a*sqrt(2) + b, evaluate=False)
((b, sqrt(2)*a), 1)
>>> collect_sqrt(a + b, evaluate=False)
((a + b,), 0)
另见
collect, collect_const, rcollect
sympy.simplify.radsimp.collect_const(expr, *vars, Numbers=True)
在 Add 表达式中,以非贪婪方式收集具有相似数值系数的项。如果提供了vars,则只会针对这些常数进行收集。尽管任何数值也可以被收集,如果不希望这样,请设置Numbers=False,则不会收集任何浮点数或有理数。
参数:
expr : SymPy 表达式
此参数定义了要从中收集具有相似系数的项的表达式。非 Add 表达式原样返回。
vars : 变量长度的数字集合,可选
指定要收集的常数。可以是多个。
Numbers : bool
指定是否针对所有
sympy.core.numbers.Number类的实例。如果Numbers=False,则不会收集任何浮点数或有理数。
返回:
expr : 表达式
返回一个具有相似系数项的表达式。
示例
>>> from sympy import sqrt
>>> from sympy.abc import s, x, y, z
>>> from sympy.simplify.radsimp import collect_const
>>> collect_const(sqrt(3) + sqrt(3)*(1 + sqrt(2)))
sqrt(3)*(sqrt(2) + 2)
>>> collect_const(sqrt(3)*s + sqrt(7)*s + sqrt(3) + sqrt(7))
(sqrt(3) + sqrt(7))*(s + 1)
>>> s = sqrt(2) + 2
>>> collect_const(sqrt(3)*s + sqrt(3) + sqrt(7)*s + sqrt(7))
(sqrt(2) + 3)*(sqrt(3) + sqrt(7))
>>> collect_const(sqrt(3)*s + sqrt(3) + sqrt(7)*s + sqrt(7), sqrt(3))
sqrt(7) + sqrt(3)*(sqrt(2) + 3) + sqrt(7)*(sqrt(2) + 2)
收集时保持符号敏感,给无符号值更高的优先级:
>>> collect_const(x - y - z)
x - (y + z)
>>> collect_const(-y - z)
-(y + z)
>>> collect_const(2*x - 2*y - 2*z, 2)
2*(x - y - z)
>>> collect_const(2*x - 2*y - 2*z, -2)
2*x - 2*(y + z)
参见
collect, collect_sqrt, rcollect
sympy.simplify.radsimp.fraction(expr, exact=False)
返回一个表达式的分子和分母。如果给定的表达式不是分数,则该函数将返回元组(expr, 1)。
此函数不会尝试简化嵌套分数或进行任何项重写。
如果只需要分子/分母中的一个,则分别使用numer(expr)或denom(expr)函数。
>>> from sympy import fraction, Rational, Symbol
>>> from sympy.abc import x, y
>>> fraction(x/y)
(x, y)
>>> fraction(x)
(x, 1)
>>> fraction(1/y**2)
(1, y**2)
>>> fraction(x*y/2)
(x*y, 2)
>>> fraction(Rational(1, 2))
(1, 2)
此函数也可以与假设一起使用良好:
>>> k = Symbol('k', negative=True)
>>> fraction(x * y**k)
(x, y**(-k))
如果我们对某些指数的符号一无所知,并且未设置exact标志,则将分析指数的结构,并返回漂亮的分数:
>>> from sympy import exp, Mul
>>> fraction(2*x**(-y))
(2, x**y)
>>> fraction(exp(-x))
(1, exp(x))
>>> fraction(exp(-x), exact=True)
(exp(-x), 1)
exact标志还将保留任何未评估的乘积:
>>> u = Mul(2, x + 1, evaluate=False)
>>> fraction(u)
(2*x + 2, 1)
>>> fraction(u, exact=True)
(2*(x + 1), 1)
sympy.simplify.ratsimp.ratsimp(expr)
将表达式放置在公共分母上,取消并化简。
示例
>>> from sympy import ratsimp
>>> from sympy.abc import x, y
>>> ratsimp(1/x + 1/y)
(x + y)/(x*y)
sympy.simplify.ratsimp.ratsimpmodprime(expr, G, *gens, quick=True, polynomial=False, **args)
通过使用由G生成的素理想归一化有理表达式expr。G应为理想的 Groebner 基础。
示例
>>> from sympy.simplify.ratsimp import ratsimpmodprime
>>> from sympy.abc import x, y
>>> eq = (x + y**5 + y)/(x - y)
>>> ratsimpmodprime(eq, [x*y**5 - x - y], x, y, order='lex')
(-x**2 - x*y - x - y)/(-x**2 + x*y)
如果polynomial是False,算法计算一个有理化简,该简化最小化了分子和分母的总次数之和。
如果polynomial是True,该函数只将分子和分母置于标准形式中。这样做速度更快,但结果可能更差。
引用
[R855]
M. Monagan, R. Pearce, 有理化简模多项式理想,dl.acm.org/doi/pdf/10.1145/1145768.1145809(特别是第二个算法)
sympy.simplify.trigsimp.trigsimp(expr, inverse=False, **opts)
通过已知的三角恒等式返回简化的表达式。
参数:
inverse : bool, 可选
如果
inverse=True,则将假定可以以任意顺序取消反函数的组合,例如asin(sin(x))将在不检查 x 是否属于此关系为真的集合的情况下产生x。默认为 False。默认值:True
method:字符串,可选
指定要使用的方法。有效选择为:
'matching',默认'groebner''combined''fu''old'如果选择
'matching',则通过针对常见模式进行递归简化表达式。如果选择'groebner',则应用试验性的 Groebner 基算法。在这种情况下,进一步的选项将被传递给trigsimp_groebner,请参阅其文档字符串。如果选择'combined',它首先使用默认较小的参数运行 Groebner 基础算法,然后运行'matching'算法。如果选择'fu',则运行 Fu 等人描述的三角变换集合(参见fu()的文档字符串)。如果选择'old',则运行原始的 SymPy 三角简化函数。
opts:
可选的关键字参数传递给该方法。请查看每个方法的函数文档字符串以获取详细信息。
示例
>>> from sympy import trigsimp, sin, cos, log
>>> from sympy.abc import x
>>> e = 2*sin(x)**2 + 2*cos(x)**2
>>> trigsimp(e)
2
简化发生在三角函数所在的任何地方。
>>> trigsimp(log(e))
log(2)
使用 method='groebner'(或 method='combined')可能会导致更大的简化。
可以通过 method='old' 访问旧的 trigsimp 例程。
>>> from sympy import coth, tanh
>>> t = 3*tanh(x)**7 - 2/coth(x)**7
>>> trigsimp(t, method='old') == t
True
>>> trigsimp(t)
tanh(x)**7
sympy.simplify.powsimp.powsimp(expr, deep=False, combine='all', force=False, measure=<function count_ops>)
通过合并具有相似基数和指数的幂来减少表达式。
解释
如果 deep=True,则 powsimp() 还将简化函数参数。默认情况下,deep 设置为 False。
如果 force 为 True,则会在不检查假设的情况下合并基数,例如 sqrt(x)*sqrt(y) -> sqrt(x*y),如果 x 和 y 都为负数则不成立。
您可以通过更改 combine='base' 或 combine='exp' 使 powsimp() 仅合并基数或仅合并指数。默认情况下,combine='all' 即同时进行两者合并。combine='base' 只会合并:
a a a 2x x
x * y => (x*y) as well as things like 2 => 4
而 combine='exp' 只会合并
a b (a + b)
x * x => x
combine='exp' 严格只会按照以前的自动方式合并指数。如果需要旧的行为,请同时使用 deep=True。
当 combine='all' 时,首先评估 exp。考虑下面的第一个示例,以了解可能与此相关的歧义。这样做是为了能够完全合并第二个示例。如果希望首先合并 base,请执行像 powsimp(powsimp(expr, combine='base'), combine='exp') 这样的操作。
示例
>>> from sympy import powsimp, exp, log, symbols
>>> from sympy.abc import x, y, z, n
>>> powsimp(x**y*x**z*y**z, combine='all')
x**(y + z)*y**z
>>> powsimp(x**y*x**z*y**z, combine='exp')
x**(y + z)*y**z
>>> powsimp(x**y*x**z*y**z, combine='base', force=True)
x**y*(x*y)**z
>>> powsimp(x**z*x**y*n**z*n**y, combine='all', force=True)
(n*x)**(y + z)
>>> powsimp(x**z*x**y*n**z*n**y, combine='exp')
n**(y + z)*x**(y + z)
>>> powsimp(x**z*x**y*n**z*n**y, combine='base', force=True)
(n*x)**y*(n*x)**z
>>> x, y = symbols('x y', positive=True)
>>> powsimp(log(exp(x)*exp(y)))
log(exp(x)*exp(y))
>>> powsimp(log(exp(x)*exp(y)), deep=True)
x + y
如果 combine='exp',则基数为 Mul 的根式将被合并
>>> from sympy import sqrt
>>> x, y = symbols('x y')
两个根式将通过 Mul 自动连接:
>>> a=sqrt(x*sqrt(y))
>>> a*a**3 == a**4
True
但是,如果该根式的整数幂已经自动展开,则 Mul 不会连接生成的因子:
>>> a**4 # auto expands to a Mul, no longer a Pow
x**2*y
>>> _*a # so Mul doesn't combine them
x**2*y*sqrt(x*sqrt(y))
>>> powsimp(_) # but powsimp will
(x*sqrt(y))**(5/2)
>>> powsimp(x*y*a) # but won't when doing so would violate assumptions
x*y*sqrt(x*sqrt(y))
sympy.simplify.powsimp.powdenest(eq, force=False, polar=False)
根据假设允许的幂来收集幂。
解释
给定 (bb**be)**e,可以按以下方式简化:
-
如果
bb为正数,或 -
e是一个整数,或 -
如果
|be| < 1,则简化为bb**(be*e)
给定幂的乘积 (bb1**be1 * bb2**be2...)**e,可以按以下方式简化:
-
如果 e 为正,则所有 bei 的 gcd 可以与 e 结合;
-
所有非负的 bb 可以从那些是负的分开,并且它们的 gcd 可以与 e 结合;autosimplification 已处理此分离。
-
具有分母指数中的整数的整数因子可以从任何项中移除,并且这些整数的 gcd 可以与 e 结合
将force设置为True将使不显式为负的符号表现得像它们是正的,从而导致更多去嵌套。
将polar设置为True将在对数的黎曼曲面上进行简化,从而导致更多去嵌套。
当 exp()中存在对数和的和时,可能会得到幂的乘积,例如exp(3*(log(a) + 2*log(b))) -> a**3*b**6。
示例
>>> from sympy.abc import a, b, x, y, z
>>> from sympy import Symbol, exp, log, sqrt, symbols, powdenest
>>> powdenest((x**(2*a/3))**(3*x))
(x**(2*a/3))**(3*x)
>>> powdenest(exp(3*x*log(2)))
2**(3*x)
假设可能阻止扩展:
>>> powdenest(sqrt(x**2))
sqrt(x**2)
>>> p = symbols('p', positive=True)
>>> powdenest(sqrt(p**2))
p
不进行其他扩展。
>>> i, j = symbols('i,j', integer=True)
>>> powdenest((x**x)**(i + j)) # -X-> (x**x)**i*(x**x)**j
x**(x*(i + j))
但通过将所有非对数项移到函数外部来去嵌套 exp();这可能导致 exp 折叠成具有不同底数的幂:
>>> powdenest(exp(3*y*log(x)))
x**(3*y)
>>> powdenest(exp(y*(log(a) + log(b))))
(a*b)**y
>>> powdenest(exp(3*(log(a) + log(b))))
a**3*b**3
如果假设允许,符号也可以移动到最外层的指数:
>>> i = Symbol('i', integer=True)
>>> powdenest(((x**(2*i))**(3*y))**x)
((x**(2*i))**(3*y))**x
>>> powdenest(((x**(2*i))**(3*y))**x, force=True)
x**(6*i*x*y)
>>> powdenest(((x**(2*a/3))**(3*y/i))**x)
((x**(2*a/3))**(3*y/i))**x
>>> powdenest((x**(2*i)*y**(4*i))**z, force=True)
(x*y**2)**(2*i*z)
>>> n = Symbol('n', negative=True)
>>> powdenest((x**i)**y, force=True)
x**(i*y)
>>> powdenest((n**i)**x, force=True)
(n**i)**x
sympy.simplify.combsimp.combsimp(expr)
简化组合表达式。
解释
此函数以包含阶乘、二项式系数、Pochhammer 符号和其他“组合”函数的表达式作为输入,并尝试最小化这些函数的数量并减少其参数的大小。
该算法通过将所有组合函数重写为伽玛函数并应用 gammasimp()来工作,除了可能使整数参数非整数的简化步骤。有关更多信息,请参阅 gammasimp 的文档字符串。
然后通过将伽玛函数重写为阶乘并将(a+b)!/a!b!转换为二项式来以阶乘和二项式的术语重写表达式。
如果表达式具有伽玛函数或具有非整数参数的组合函数,则将其自动传递给 gammasimp。
示例
>>> from sympy.simplify import combsimp
>>> from sympy import factorial, binomial, symbols
>>> n, k = symbols('n k', integer = True)
>>> combsimp(factorial(n)/factorial(n - 3))
n*(n - 2)*(n - 1)
>>> combsimp(binomial(n+1, k+1)/binomial(n, k))
(n + 1)/(k + 1)
sympy.simplify.sqrtdenest.sqrtdenest(expr, max_iter=3)
如果可能,对表达式中包含其他平方根的表达式进行去嵌套,否则返回未更改的表达式。这基于[1]的算法。
示例
>>> from sympy.simplify.sqrtdenest import sqrtdenest
>>> from sympy import sqrt
>>> sqrtdenest(sqrt(5 + 2 * sqrt(6)))
sqrt(2) + sqrt(3)
请参见
sympy.solvers.solvers.unrad
参考
[R856]
[R857]
D. J. Jeffrey 和 A. D. Rich,《通过去嵌套简化平方根的平方根》(可在www.cybertester.com/data/denest.pdf找到)
sympy.simplify.cse_main.cse(exprs, symbols=None, optimizations=None, postprocess=None, order='canonical', ignore=(), list=True)
对表达式执行常见子表达式消除。
参数:
exprs:SymPy 表达式的列表或单个 SymPy 表达式
要简化的表达式。
symbols:无限迭代器,生成唯一的 Symbols
用于标记提取出的公共子表达式的符号。
numbered_symbols生成器很有用。默认情况下是形如“x0”、“x1”等的符号流。这必须是一个无限迭代器。
optimizations:(callable,callable)对列表。
外部优化函数的预处理器、后处理器对。可选地,可以传递‘basic’以获取一组预定义的基本优化。这些‘basic’优化在旧实现中默认使用,但在处理较大表达式时可能非常慢。现在,默认情况下不进行任何预处理或后处理优化。
后处理:接受 CSE 的两个返回值的函数。
返回所需的 cse 输出形式,例如如果想要反转替换,则函数可能是以下 lambda:lambda r,e:return reversed(r),e
顺序:字符串,‘none’或‘canonical’。
处理 Mul 和 Add 参数的顺序。如果设置为‘canonical’,参数将按照规范顺序排列。如果设置为‘none’,排序将更快,但依赖于表达式哈希,因此是机器相关和可变的。对于大表达式而言,如果关注速度,使用 order=‘none’设置。
ignore:Symbol 的可迭代对象。
包含任何
ignore中符号的替换将被忽略。
列表:布尔值,(默认为真)。
返回以列表形式或与输入相同类型的表达式(当为 False 时)。
返回:
替换:(Symbol,表达式)对列表。
所有被替换的公共子表达式。此列表中较早的子表达式可能会出现在较晚的子表达式中。
reduced_exprs:SymPy 表达式列表。
所有上述替换后的简化表达式。
示例。
>>> from sympy import cse, SparseMatrix
>>> from sympy.abc import x, y, z, w
>>> cse(((w + x + y + z)*(w + y + z))/(w + x)**3)
([(x0, y + z), (x1, w + x)], [(w + x0)*(x0 + x1)/x1**3])
具有递归替换的表达式列表。
>>> m = SparseMatrix([x + y, x + y + z])
>>> cse([(x+y)**2, x + y + z, y + z, x + z + y, m])
([(x0, x + y), (x1, x0 + z)], [x0**2, x1, y + z, x1, Matrix([
[x0],
[x1]])])
注意:输入矩阵的类型和可变性保持不变。
>>> isinstance(_[1][-1], SparseMatrix)
True
用户可以禁止包含特定符号的替换。
>>> cse([y**2*(x + 1), 3*y**2*(x + 1)], ignore=(y,))
([(x0, x + 1)], [x0*y**2, 3*x0*y**2])
缩减表达式的默认返回值是一个列表,即使只有一个表达式。list标志保留了输出中输入的类型:
>>> cse(x)
([], [x])
>>> cse(x, list=False)
([], x)
sympy.simplify.cse_main.opt_cse(exprs, order='canonical')
查找 Adds、Muls、Pows 和负系数 Muls 中的优化机会。
参数:
exprs:SymPy 表达式列表。
要优化的表达式。
顺序:字符串,‘none’或‘canonical’。
处理 Mul 和 Add 参数的顺序。对于大表达式而言,如果关注速度,使用 order=‘none’设置。
返回:
opt_subs:表达式替换的字典。
用于优化公共子表达式消除的表达式替换。
示例。
>>> from sympy.simplify.cse_main import opt_cse
>>> from sympy.abc import x
>>> opt_subs = opt_cse([x**-2])
>>> k, v = list(opt_subs.keys())[0], list(opt_subs.values())[0]
>>> print((k, v.as_unevaluated_basic()))
(x**(-2), 1/(x**2))
sympy.simplify.cse_main.tree_cse(exprs, symbols, opt_subs=None, order='canonical', ignore=())
在考虑 opt_subs 的情况下在表达式树上执行原始 CSE。
参数:
exprs:SymPy 表达式列表。
要减少的表达式。
symbols:生成唯一 Symbol 的无限迭代器。
用于标记提取出的公共子表达式的符号。
opt_subs:表达式替换的字典。
在执行任何 CSE 操作之前要替换的表达式。
顺序:字符串,‘none’或‘canonical’。
处理 Mul 和 Add 参数的顺序。对于速度是关键的大型表达式,请使用 order='none' 设置。
ignore:Symbol 的可迭代对象
包含来自
ignore的任何符号的替换将被忽略。
sympy.simplify.hyperexpand.hyperexpand(f, allow_hyper=False, rewrite='default', place=None)
展开超几何函数。如果 allow_hyper 为 True,则允许部分简化(即与输入不同但仍包含超几何函数)。
如果 G 函数在零点和无穷远处均有展开,可以将 place 设置为 0 或 zoo 表示首选选择。
示例
>>> from sympy.simplify.hyperexpand import hyperexpand
>>> from sympy.functions import hyper
>>> from sympy.abc import z
>>> hyperexpand(hyper([], [], z))
exp(z)
表达式的非超几何部分和未被识别的超几何表达式保持不变:
>>> hyperexpand(1 + hyper([1, 1, 1], [], z))
hyper((1, 1, 1), (), z) + 1
class sympy.simplify.epathtools.EPath(path)
使用路径操作表达式。
EPath 的 EBNF 表示法语法:
literal ::= /[A-Za-z_][A-Za-z_0-9]*/
number ::= /-?\d+/
type ::= literal
attribute ::= literal "?"
all ::= "*"
slice ::= "[" number? (":" number? (":" number?)?)? "]"
range ::= all | slice
query ::= (type | attribute) ("|" (type | attribute))*
selector ::= range | query range?
path ::= "/" selector ("/" selector)*
参见 epath() 函数的文档字符串。
apply(expr, func, args=None, kwargs=None)
修改由路径选择的表达式的部分。
示例
>>> from sympy.simplify.epathtools import EPath
>>> from sympy import sin, cos, E
>>> from sympy.abc import x, y, z, t
>>> path = EPath("/*/[0]/Symbol")
>>> expr = [((x, 1), 2), ((3, y), z)]
>>> path.apply(expr, lambda expr: expr**2)
[((x**2, 1), 2), ((3, y**2), z)]
>>> path = EPath("/*/*/Symbol")
>>> expr = t + sin(x + 1) + cos(x + y + E)
>>> path.apply(expr, lambda expr: 2*expr)
t + sin(2*x + 1) + cos(2*x + 2*y + E)
select(expr)
检索由路径选择的表达式的部分。
示例
>>> from sympy.simplify.epathtools import EPath
>>> from sympy import sin, cos, E
>>> from sympy.abc import x, y, z, t
>>> path = EPath("/*/[0]/Symbol")
>>> expr = [((x, 1), 2), ((3, y), z)]
>>> path.select(expr)
[x, y]
>>> path = EPath("/*/*/Symbol")
>>> expr = t + sin(x + 1) + cos(x + y + E)
>>> path.select(expr)
[x, x, y]
sympy.simplify.epathtools.epath(path, expr=None, func=None, args=None, kwargs=None)
操作表达式中路径选择的部分。
参数:
path:字符串 | EPath
作为字符串或编译的 EPath 的路径。
expr:基本类型 | 可迭代对象
表达式或表达式容器。
func:可调用对象(可选)
将应用于匹配部分的可调用对象。
args:元组(可选)
func的额外位置参数。
kwargs:字典(可选)
func的额外关键字参数。
解释
此函数允许在单行代码中操作大型嵌套表达式,利用在 XML 处理标准中应用的技术(例如 XPath)。
如果 func 为 None,epath() 检索由 path 选择的元素。否则,将 func 应用于每个匹配的元素。
请注意,创建一个 EPath 对象并使用该对象的 select 和 apply 方法更有效,因为这样只会编译一次路径字符串。这个函数应该只用作交互式使用的便捷快捷方式。
这是支持的语法:
-
选择全部:
/*等同于
for arg in args:。 -
选择切片:
/[0]或/[1:5]或/[1:5:2]支持标准 Python 的切片语法。
-
按类型选择:
/list或/list|tuple模拟
isinstance()。 -
按属性选择:
/__iter__?模拟
hasattr()。
示例
>>> from sympy.simplify.epathtools import epath
>>> from sympy import sin, cos, E
>>> from sympy.abc import x, y, z, t
>>> path = "/*/[0]/Symbol"
>>> expr = [((x, 1), 2), ((3, y), z)]
>>> epath(path, expr)
[x, y]
>>> epath(path, expr, lambda expr: expr**2)
[((x**2, 1), 2), ((3, y**2), z)]
>>> path = "/*/*/Symbol"
>>> expr = t + sin(x + 1) + cos(x + y + E)
>>> epath(path, expr)
[x, x, y]
>>> epath(path, expr, lambda expr: 2*expr)
t + sin(2*x + 1) + cos(2*x + 2*y + E)
超几何展开
本页面描述了函数 hyperexpand() 及其相关代码的工作原理。有关使用方法,请参阅 symplify 模块的文档。
超几何函数展开算法
本节描述了扩展超几何函数所使用的算法。其中大部分基于文献 [Roach1996] 和 [Roach1997]。
回顾超几何函数(最初)定义为
[\begin{split}{}pF_q\left(\begin{matrix} a_1, \cdots, a_p \ b_1, \cdots, b_q \end{matrix} \middle| z \right) = \sum^\infty \frac{(a_1)_n \cdots (a_p)_n}{(b_1)_n \cdots (b_q)_n} \frac{z^n}{n!}.\end{split}]
结果表明,有些微分算子可以将 (a_p) 和 (b_q) 参数按整数改变。如果已知这样一系列算子,可以将索引集 (a_r⁰) 和 (b_s⁰) 转换为 (a_p) 和 (b_q),则我们将说从 (a_r⁰, b_s⁰) 到 (a_p, b_q) 的对是可达的。因此,我们的一般策略如下:给定一组参数 (a_p, b_q),尝试查找一个起点 (a_r⁰, b_s⁰),我们知道一个表达式,然后应用一系列微分算子到已知表达式,以找到我们感兴趣的超几何函数的表达式。
符号
在以下内容中,符号 (a) 总是表示分子参数,符号 (b) 总是表示分母参数。下标 (p, q, r, s) 表示具有该长度的向量,例如 (a_p) 表示具有 (p) 个分子参数的向量。下标 (i) 和 (j) 表示“运行索引”,因此它们通常应与“对所有 (i)”一起使用。例如,对于所有 (i),(a_i < 4)。大写下标 (I) 和 (J) 表示选择的固定索引。因此,例如,如果不等式对我们当前感兴趣的一个索引 (I) 成立,则 (a_I > 0) 为真。
增加和减少指数
假设 (a_i \ne 0). 设 (A(a_i) = \frac{z}{a_i}\frac{\mathrm{d}}{dz}+1)。可以轻松地证明 (A(a_i) {}_p F_q\left({a_p \atop b_q} \middle| z \right) = {}_p F_q\left({a_p + e_i \atop b_q} \middle| z \right)),其中 (e_i) 是第 (i) 个单位向量。类似地,对于 (b_j \ne 1),我们设 (B(b_j) = \frac{z}{b_j-1} \frac{\mathrm{d}}{dz}+1) 并找到 (B(b_j) {}_p F_q\left({a_p \atop b_q} \middle| z \right) = {}_p F_q\left({a_p \atop b_q - e_i} \middle| z \right))。因此,我们可以随意增加上标和减少下标,只要我们不经过零。(A(a_i)) 和 (B(b_j)) 被称为移位算子。
还可以轻松地证明 (\frac{\mathrm{d}}{dz} {}p F_q\left({a_p \atop b_q} \middle| z \right) = \frac{a_1 \cdots a_p}{b_1 \cdots b_q} {}p F_q\left({a_p + 1 \atop b_q + 1} \middle| z \right)),其中 (a_p + 1) 是向量 (a_1 + 1, a_2 + 1, \ldots),对 (b_q + 1) 同样适用。将此与移位算子结合起来,我们得到超几何微分方程的一种形式:(\left[ \frac{\mathrm{d}}{dz} \prod^q B(b_j) - \frac{a_1 \cdots a_p}{(b_1-1) \cdots (b_q-1)} \prod^p A(a_i) \right] {}p F_q\left({a_p \atop b_q} \middle| z \right) = 0)。如果所有移位算子都被定义,即没有 (a_i = 0) 且没有 (b_j = 1),则此等式成立。清除分母并通过 (z) 乘以我们得到以下方程:(\left[ z\frac{\mathrm{d}}{dz} \prod^q \left(z\frac{\mathrm{d}}{dz} + b_j-1 \right) - z \prod_{i=1}^p \left( z\frac{\mathrm{d}}{\mathrm{d}z} + a_i \right) \right] {}_p F_q\left({a_p \atop b_q} \middle| z\right) = 0)。尽管我们的推导没有显示出它,但可以检查到只要 ({}_p F_q) 被定义,此方程成立。
注意,在 (a_I, b_J) 的适当条件下,每个算子 (A(a_i)),(B(b_j)) 和 (z\frac{\mathrm{d}}{\mathrm{d}z}) 可以用 (A(a_I)) 或 (B(b_J)) 的术语表达。我们的下一个目标是将超几何微分方程写为如下形式:([X A(a_I) - r] {}_p F_q\left({a_p \atop b_q} \middle| z\right) = 0),其中 (X) 是某个算子,(r) 是待定常数。如果 (r \ne 0),则我们可以写成 (\frac{-1}{r} X {}_p F_q\left({a_p + e_I \atop b_q} \middle| z\right) = {}_p F_q\left({a_p \atop b_q} \middle| z\right)),因此 (\frac{-1}{r}X) 消除了 (A(a_I)) 的移位,因此它被称为逆移位算子。
现在,如果 (a_I \ne 0),则 (A(a_I)) 存在,并且 (z\frac{\mathrm{d}}{\mathrm{d}z} = a_I A(a_I) - a_I)。还要注意所有算子 (A(a_i)),(B(b_j)) 和 (z\frac{\mathrm{d}}{\mathrm{d}z}) 是可交换的。我们有 (\prod_{i=1}^p \left( z\frac{\mathrm{d}}{\mathrm{d}z} + a_i \right) = \left(\prod_{i=1, i \ne I}^p \left( z\frac{\mathrm{d}}{\mathrm{d}z} + a_i \right)\right) a_I A(a_I)),因此这给了我们 (X) 的前半部分。另一半没有这样漂亮的表达式。我们找到 (z\frac{\mathrm{d}}{dz} \prod_{j=1}^q \left(z\frac{\mathrm{d}}{dz} + b_j-1 \right) = \left(a_I A(a_I) - a_I\right) \prod_{j=1}^q \left(a_I A(a_I) - a_I + b_j - 1\right))。由于前半部分没有常数项,我们推断 (r = -a_I\prod_{j=1}^q(b_j - 1 -a_I))。
这告诉我们可以“取消移位” (A(a_I)) 的条件,即当 (a_I \ne 0) 且 (r \ne 0) 时。将 (a_I - 1) 替换为 (a_I) 然后告诉我们可以减少索引 (a_I) 的条件。对 (B(a_J)) 进行类似分析,我们得到以下规则:
-
可以减小索引 (a_I),如果 (a_I \ne 1) 且对所有 (b_j) 都有 (a_I \ne b_j)。
-
如果(b_J \ne -1)且对所有(a_i)都有(b_J \ne a_i),则可以增加索引(b_J)。
结合存在移位算子的条件(如上所述),我们已经建立了游戏规则!
降阶
注意,如果(a_I = b_J),我们有({}p F_q\left({a_p \atop b_q} \middle| z \right) = {} F_{q-1}\left({a_p^* \atop b_q^} \middle| z \right)),其中(a_p*)表示省略(a_I)后的(a_p),(b_q)类似。我们称之为降阶。
实际上,我们可以做得更好。如果(a_I - b_J \in \mathbb{Z}_{>0}),那么很容易看出(\frac{(a_I)_n}{(b_J)_n})实际上是(n)的多项式。同时,很容易看出((z\frac{\mathrm{d}}{\mathrm{d}z})^k z^n = n^k z^n)。结合这两点,我们得出:
如果(a_I - b_J \in \mathbb{Z}_{>0}),则存在一个多项式(p(n) = p_0 + p_1 n + \cdots)(次数为(a_I - b_J))使得(\frac{(a_I)_n}{(b_J)n} = p(n)),并且({}p F_q\left({a_p \atop b_q} \middle| z \right) = \left(p_0 + p_1 z\frac{\mathrm{d}}{\mathrm{d}z} + p_2 \left(z\frac{\mathrm{d}}{\mathrm{d}z}\right)² + \cdots \right) {} F\left({a_p^* \atop b_q^*} \middle| z \right))。
因此,任意一组参数(a_p, b_q)都可以从一组参数(c_r, d_s)到达,其中(c_i - d_j \in \mathbb{Z})意味着(c_i < d_j)。这样的一组参数(c_r, d_s)称为合适。我们已知的公式数据库应仅包含合适的起源。原因有二:首先,从合适的起源工作更容易;其次,可以从低阶公式推导出非合适起源的公式,我们应该将后者放入数据库中。
在参数空间中移动
现在需要研究以下问题:假设(a_p, b_q)和(a_p⁰, b_q⁰)都合适,并且(a_i - a_i⁰ \in \mathbb{Z}),(b_j - b_j⁰ \in \mathbb{Z})。那么(a_p, b_q)从(a_p⁰, b_q⁰)可达的条件是什么?显然,我们可以独立处理所有模 1 不同余的参数。因此假设对于所有(i)和(j),(a_i)和(b_j)对模 1 同余于(r)。对于(a_i⁰)和(b_j⁰)也是如此。
如果(r \ne 0),则任何这样的(a_p, b_q)都可以从任何(a_p⁰, b_q⁰)到达。要看到这一点,请注意存在同余于 1 的常数(c, c⁰),使得对于所有(i)和(j),(a_i < c < b_j),类似地(a_i⁰ < c⁰ < b_j⁰)。如果(n = c - c⁰ > 0),那么我们首先向上反向移位所有(b_j⁰) (n)次,然后类似地向上移位所有(a_i⁰) (n)次。如果(n < 0),那么我们首先向下反向移位(a_i⁰),然后向下移位(b_j⁰)。这归结为情况(c = c⁰)。但显然,我们现在可以任意移位或反向移位(a_i⁰),只要保持它们小于(c),并对(b_j⁰)也是如此。因此,(a_p, b_q)可以从(a_p⁰, b_q⁰)到达。
如果 (r = 0),那么问题会稍微复杂一些。不失一般性地,没有参数为零。现在我们有一个额外的复杂性:没有参数可以穿过零点。因此 (a_p, b_q) 从 (a_p⁰, b_q⁰) 可达,当且仅当 (a_i < 0) 的个数等于 (a_i⁰ < 0) 的个数,并且类似地对于 (b_i) 和 (b_i⁰)。但在适当的参数集中,所有 (b_j > 0)!这是因为如果其中一个 (b_j) 是非正整数且所有 (a_i) 都小于 (b_j),那么超几何函数是未定义的。因此 (b_j \le 0) 的个数总是零。
因此,我们可以将每个适当的参数集 (a_p, b_q) 关联到以下不变量:
- 对于每个 (r \in [0, 1)),参数 (a_i \equiv r \pmod{1}) 的数量 (\alpha_r),以及类似地参数 (b_i \equiv r \pmod{1}) 的数量 (\beta_r)。
- 整数 (\gamma) 满足 (a_i < 0) 的个数。
上述推理表明,(a_p, b_q) 从 (a_p⁰, b_q⁰) 可达,当且仅当不变量 (\alpha_r, \beta_r, \gamma) 全部一致。因此特别是“可达性”在适当的参数上是一个对称关系,没有零点。
应用操作符
如果一切顺利,那么对于给定的参数集,我们可以在我们的数据库中找到一个良好的公式的起源。现在我们必须将(可能)许多微分操作符应用于它。如果我们这样做得盲目,结果将会非常凌乱。这是因为在超几何类型函数中,导数通常被表达为两个连续函数的和。因此,如果我们计算 (N) 个导数,那么答案将涉及 (2N) 个连续函数!显然这是不可取的。事实上,从超几何微分方程我们知道,我们最多需要 (\max(p, q+1)) 个连续函数来表达所有的导数。
因此,与其盲目进行不同 iating,我们将与一个 (\mathbb{C}(z))-模块基础一起工作:对于原点 (a_r⁰, b_s⁰),我们要么存储(对于特别漂亮的答案),要么计算一组 (N) 函数(通常 (N = \max(r, s+1))),具有以下属性:任何其中之一的导数都是它们的 (\mathbb{C}(z))-线性组合。在公式中,我们存储了一个 (N) 函数的向量 (B),一个矩阵 (M) 和一个向量 (C)(后两者的条目在 (\mathbb{C}(z)) 中),具有以下属性:
-
({}_r F_s\left({a_r⁰ \atop b_s⁰} \middle| z \right) = C B)
-
(z\frac{\mathrm{d}}{\mathrm{d}z} B = M B)。
然后我们可以计算任意多的导数,我们将总是得到至多 (N) 个特殊函数的 (\mathbb{C}(z))-线性组合。
如上所示,(B),(M) 和 (C) 可以全部存储(用于特别漂亮的答案)或者从单个 ({}_p F_q) 公式计算得到。
收尾工作
这描述了超几何函数算法的大部分。在 hyperexpand.py 源文件中还有一些进一步的技巧。Meijer G-函数的扩展也在那里描述。
有限合流的 Meijer G-Functions
Slater 定理本质上将 (G)-函数评估为残余和。如果所有极点都是简单的,则得到的级数可以识别为超几何级数。因此,(G)-函数可以评估为一些超几何函数的和。
如果极点不是简单的,得到的级数就不是超几何级数。这被称为“合流”或“对数”情况(后者因其结果级数中包含对数而得名)。答案以复杂的方式取决于各种极点的重数,并且没有公认的符号表示方法(据我所知)。然而,如果仅有有限多个多重极点,我们可以将 (G) 函数评估为一些超几何函数的和,再加上有限多项。我找不到关于此的好参考资料,这就是我在这里工作的原因。
回顾一般的设置。我们定义
[G(z) = \frac{1}{2\pi i} \int_L \frac{\prod_{j=1}^m \Gamma(b_j - s) \prod_{j=1}^n \Gamma(1 - a_j + s)}{\prod_{j=m+1}^q \Gamma(1 - b_j + s) \prod_{j=n+1}^p \Gamma(a_j - s)} z^s \mathrm{d}s,]
其中 (L) 是一个从 (+\infty) 开始并结束的轮廓,负方向上环绕所有 (\Gamma(b_j - s)) 的极点,(j = 1, \ldots, n),并且没有其他极点。还假定积分是绝对收敛的。
在接下来的讨论中,对于任意复数 (a, b),我们写成 (a \equiv b \pmod{1}),当且仅当存在整数 (k) 使得 (a - b = k)。因此,当且仅当存在某些 (i \ne j \le n) 使得 (a_i \equiv a_j \pmod{1}) 时,存在双极点。
现在我们假设每当对于 (i \le m) 有 (b_j \equiv a_i \pmod{1}),对于 (j > n) 则 (b_j < a_i)。这意味着相关伽玛函数的任何商都不是多项式,并且可以通过“阶降”来实现。固定复数 (c),使得 ({b_i | b_i \equiv c \pmod{1}, i \le m}) 不为空。将这个集合枚举为 (b, b+k_1, \ldots, b+k_u),其中 (k_i) 是非负整数。类似地将 ({a_j | a_j \equiv c \pmod{1}, j > n}) 枚举为 (b + l_1, \ldots, b + l_v)。然后对于所有这样的 (c),都需要假设 (v \ge u) 以实现有限合流。
让 (c_1, \ldots, c_w) 是不同的 (\pmod{1}),并穷尽了 (b_i) 的同余类。我声明
[G(z) = -\sum_{j=1}^w (F_j(z) + R_j(z)),]
其中 (F_j(z)) 是一个超几何函数,(R_j(z)) 是一个有限和,两者稍后会具体说明。事实上,对应于每个 (c_j),大多数情况下存在一系列极点,其中大多数是多重极点。这就是第 (j) 项的来源。
因此再次修复(c),将相关的(b_i)枚举为(b, b + k_1, \ldots, b + k_u)。我们将看看与(a + l_1, \ldots, a + l_u)对应的(a_j)。其他的(a_i)不作特殊处理。相应的伽马函数在(s = b + r)(可能)处有极点。对于(r \ge l_u),被积函数的极点是简单的。因此,我们设定
[R(z) = \sum_{r=0}^{l_u - 1} res_{s = r + b}.]
我们最终需要调查其他的极点。设定(r = l_u + t),(t \ge 0)。计算显示
[\frac{\Gamma(k_i - l_u - t)}{\Gamma(l_i - l_u - t)} = \frac{1}{(k_i - l_u - t){l_i - k_i}} = \frac{(-1)^{\delta_i}}{(l_u - l_i + 1){\delta_i}} \frac{(l_u - l_i + 1)_t}{(l_u - k_i + 1)_t},]
其中(\delta_i = l_i - k_i).
又
[ \begin{align}\begin{aligned}\begin{split}\Gamma(b_j - l_u - b - t) = \frac{\Gamma(b_j - l_u - b)}{(-1)^t(l_u + b + 1 - b_j)_t}, \\end{split}\\Gamma(1 - a_j + l_u + b + t) = \Gamma(1 - a_j + l_u + b) (1 - a_j + l_u + b)_t\end{aligned}\end{align} ]
和
[res_{s = b + l_u + t} \Gamma(b - s) = -\frac{(-1)^{l_u + t}}{(l_u + t)!} = -\frac{(-1)^{l_u}}{l_u!} \frac{(-1)^t}{(l_u+1)_t}.]
因此
[\begin{split}res_{s = b + l_u + t} =& -z^{b + l_u} \frac{(-1)^{l_u}}{l_u!} \prod_{i=1}^{u} \frac{(-1)^{\delta_i}}{(l_u - k_i + 1){\delta_i}} \frac{\prod^n \Gamma(1 - a_j + l_u + b) \prod_{j=1}^m \Gamma(b_j - l_u - b)^} {\prod_{j=n+1}^p \Gamma(a_j - l_u - b)^ \prod_{j=m+1}^q \Gamma(1 - b_j + l_u + b)} \ &\times z^t \frac{(-1)^t}{(l_u+1)t} \prod^{u} \frac{(l_u - l_i + 1)_t}{(l_u - k_i + 1)t} \frac{\prod^n (1 - a_j + l_u + b)t \prod^p (-1)^t (l_u + b + 1 - a_j)t^*} {\prod^m (-1)^t (l_u + b + 1 - b_j)t^* \prod^q (1 - b_j + l_u + b)_t},\end{split}]
其中(*)表示我们特别处理的项。
我们因此得到
[\begin{split}F(z) = C \times {}{p+1}F\left( \begin{matrix} 1, (1 + l_u - l_i), (1 + l_u + b - a_i)^* \ 1 + l_u, (1 + l_u - k_i), (1 + l_u + b - b_i)^* \end{matrix} \middle| (-1)^{p-m-n} z\right),\end{split}]
其中(C)指定了与(t)无关的残留因子。(这个结果也可以稍微简化,将所有的(l_u)等转换回(a_* - b_*),但这样做将需要更多的符号仍然不利于计算。)
扩展超几何表
向表格添加新的公式非常简单。在文件sympy/simplify/hyperexpand.py的顶部,有一个名为add_formulae()的函数。在其中嵌套定义了两个辅助函数add(ap, bq, res)和addb(ap, bq, B, C, M),以及虚拟变量a、b、c和z。
添加新公式的第一步是使用add(ap, bq, res)。这声明了hyper(ap, bq, z) == res。在这里,ap和bq可以使用虚拟变量a、b和c作为自由符号。例如,众所周知的公式(\sum_0^\infty \frac{(-a)_n z^n}{n!} = (1-z)^a)由以下行声明:add((-a, ), (), (1-z)**a).
根据提供的信息,矩阵(B)、(C)和(M)将会被计算,并且在展开超几何函数时,公式现在是可用的。接下来应该运行测试文件sympy/simplify/tests/test_hyperexpand.py,特别是测试test_formulae()。这将对新添加的公式进行数值测试。如果测试失败,则可能是输入中有拼写错误。
由于所有新增的公式可能相对复杂,自动计算的基可能不够优化(没有很好的测试方法,除非观察非常混乱的输出)。在这种情况下,矩阵(B)、(C)和(M)应该手动计算。然后可以使用助手addb来声明一个使用手动计算基础的超几何公式。
例如
因为到目前为止这个解释可能非常理论化且难以理解,我们现在通过一个明确的例子来详细说明。我们取弗雷内尔函数(C(z)),它遵循以下超几何表示:
[\begin{split}C(z) = z \cdot {}{1}F\left.\left( \begin{matrix} \frac{1}{4} \ \frac{1}{2}, \frac{5}{4} \end{matrix} \right| -\frac{\pi² z⁴}{16}\right) ,.\end{split}]
首先,我们尝试通过使用(更简单的)函数add(ap, bq, res)将此公式添加到查找表中。前两个参数只是包含({}{1}F)的参数集合的列表。参数res稍微复杂一些。我们只知道(C(z))关于({}{1}F(\ldots | f(z)))的表达式,其中(f)是(z)的函数,即我们的情况下
[f(z) = -\frac{\pi² z⁴}{16} ,.]
我们需要的是一个公式,其中超几何函数只有(z)作为参数({}{1}F(\ldots | z))。我们引入新的复数符号(w)并寻找一个函数(g(w)),使得
[f(g(w)) = w]
成立。然后我们可以用(g(w))替换(C(z))中的每一个(z)。在我们的例子中,函数(g)可能看起来像
[g(w) = \frac{2}{\sqrt{\pi}} \exp\left(\frac{i \pi}{4}\right) w^{\frac{1}{4}} ,.]
我们主要通过猜测和测试结果来获取这些函数。因此,我们继续计算(f(g(w)))(并且朴素化简)
[\begin{split}f(g(w)) &= -\frac{\pi² g(w)⁴}{16} \ &= -\frac{\pi² g\left(\frac{2}{\sqrt{\pi}} \exp\left(\frac{i \pi}{4}\right) w^{\frac{1}{4}}\right)⁴}{16} \ &= -\frac{\pi² \frac{2⁴}{\sqrt{\pi}⁴} \exp\left(\frac{i \pi}{4}\right)⁴ {w^{\frac{1}{4}}}⁴}{16} \ &= -\exp\left(i \pi\right) w \ &= w\end{split}]
并且确实得到(w)。对于分支函数的情况,我们必须注意分支切割。在这种情况下,我们将(w)取为正实数并检查公式。如果我们找到的适用于正(w),那么只需在任何分支函数内用exp替换为exp_polar,我们得到的就是所有(w)都正确的。因此,我们可以将公式写成
[\begin{split}C(g(w)) = g(w) \cdot {}{1}F\left.\left( \begin{matrix} \frac{1}{4} \ \frac{1}{2}, \frac{5}{4} \end{matrix} \right| w\right) ,.\end{split}]
并且显然
[\begin{split}{}{1}F\left.\left( \begin{matrix} \frac{1}{4} \ \frac{1}{2}, \frac{5}{4} \end{matrix} \right| w\right) = \frac{C(g(w))}{g(w)} = \frac{C\left(\frac{2}{\sqrt{\pi}} \exp\left(\frac{i \pi}{4}\right) w^{\frac{1}{4}}\right)} {\frac{2}{\sqrt{\pi}} \exp\left(\frac{i \pi}{4}\right) w^{\frac{1}{4}}}\end{split}]
这正是add函数中第三个参数res所需要的。最后,将此规则添加到表中的整个函数调用看起来像:
add([S(1)/4],
[S(1)/2, S(5)/4],
fresnelc(exp(pi*I/4)*root(z,4)*2/sqrt(pi)) / (exp(pi*I/4)*root(z,4)*2/sqrt(pi))
)
使用这个规则,我们将发现它确实有效,但结果在简洁性和包含特殊函数实例数量方面并不理想。通过另一种方法将公式添加到查找表中,我们可以获得更好的结果。为此,我们使用(更复杂的)函数addb(ap, bq, B, C, M)。前两个参数再次是包含({}{1}F)参数集的列表。剩余的三个是本页早些时候提到的矩阵。
我们知道(n = \max{\left(p, q+1\right)})次导数可以表示为低阶导数的线性组合。矩阵(B)包含基础({B_0, B_1, \ldots}),形状为(n \times 1)。获取(B_i)的最佳方式是对({}_p F_q)的表达式进行前(n = \max(p, q+1))次导数,提取有用的部分。在我们的情况下,我们发现(n = \max{\left(1, 2+1\right)} = 3)。为了计算导数,我们必须使用操作符(z\frac{\mathrm{d}}{\mathrm{d}z})。第一个基础元素(B_0)设定为从上述({}_1 F_2)的表达式:
[B_0 = \frac{ \sqrt{\pi} \exp\left(-\frac{\mathbf{\imath}\pi}{4}\right) C\left( \frac{2}{\sqrt{\pi}} \exp\left(\frac{\mathbf{\imath}\pi}{4}\right) z^{\frac{1}{4}}\right)} {2 z^{\frac{1}{4}}}]
接下来计算(z\frac{\mathrm{d}}{\mathrm{d}z} B_0)。对此,我们可以直接使用 SymPy!
>>> from sympy import Symbol, sqrt, exp, I, pi, fresnelc, root, diff, expand
>>> z = Symbol("z")
>>> B0 = sqrt(pi)*exp(-I*pi/4)*fresnelc(2*root(z,4)*exp(I*pi/4)/sqrt(pi))/\
... (2*root(z,4))
>>> z * diff(B0, z)
z*(cosh(2*sqrt(z))/(4*z) - sqrt(pi)*exp(-I*pi/4)*fresnelc(2*z**(1/4)*exp(I*pi/4)/sqrt(pi))/(8*z**(5/4)))
>>> expand(_)
cosh(2*sqrt(z))/4 - sqrt(pi)*exp(-I*pi/4)*fresnelc(2*z**(1/4)*exp(I*pi/4)/sqrt(pi))/(8*z**(1/4))
格式化这个结果得到
[B_1^\prime = - \frac{1}{4} \frac{ \sqrt{\pi} \exp\left(-\frac{\mathbf{\imath}\pi}{4}\right) C\left( \frac{2}{\sqrt{\pi}} \exp\left(\frac{\mathbf{\imath}\pi}{4}\right) z^{\frac{1}{4}}\right) } {2 z^{\frac{1}{4}}} + \frac{1}{4} \cosh{\left( 2 \sqrt{z} \right )}]
计算第二阶导数我们得到
>>> from sympy import (Symbol, cosh, sqrt, pi, exp, I, fresnelc, root,
... diff, expand)
>>> z = Symbol("z")
>>> B1prime = cosh(2*sqrt(z))/4 - sqrt(pi)*exp(-I*pi/4)*\
... fresnelc(2*root(z,4)*exp(I*pi/4)/sqrt(pi))/(8*root(z,4))
>>> z * diff(B1prime, z)
z*(-cosh(2*sqrt(z))/(16*z) + sinh(2*sqrt(z))/(4*sqrt(z)) + sqrt(pi)*exp(-I*pi/4)*fresnelc(2*z**(1/4)*exp(I*pi/4)/sqrt(pi))/(32*z**(5/4)))
>>> expand(_)
sqrt(z)*sinh(2*sqrt(z))/4 - cosh(2*sqrt(z))/16 + sqrt(pi)*exp(-I*pi/4)*fresnelc(2*z**(1/4)*exp(I*pi/4)/sqrt(pi))/(32*z**(1/4))
可以打印为
[B_2^\prime = \frac{1}{16} \frac{ \sqrt{\pi} \exp\left(-\frac{\mathbf{\imath}\pi}{4}\right) C\left( \frac{2}{\sqrt{\pi}} \exp\left(\frac{\mathbf{\imath}\pi}{4}\right) z^{\frac{1}{4}}\right) } {2 z^{\frac{1}{4}}} - \frac{1}{16} \cosh{\left(2\sqrt{z}\right)} + \frac{1}{4} \sinh{\left(2\sqrt{z}\right)} \sqrt{z}]
我们看到了共同的模式,并且可以收集这些部分。因此,选择(B_1)和(B_2)如下所示是有意义的
[\begin{split}B = \left( \begin{matrix} B_0 \ B_1 \ B_2 \end{matrix} \right) = \left( \begin{matrix} \frac{ \sqrt{\pi} \exp\left(-\frac{\mathbf{\imath}\pi}{4}\right) C\left( \frac{2}{\sqrt{\pi}} \exp\left(\frac{\mathbf{\imath}\pi}{4}\right) z^{\frac{1}{4}}\right) }{2 z^{\frac{1}{4}}} \ \cosh\left(2\sqrt{z}\right) \ \sinh\left(2\sqrt{z}\right) \sqrt{z} \end{matrix} \right)\end{split}]
(这与基础(B = \left(B_0, B_1^\prime, B_2^\prime\right))的计算方法形成对比,如果我们只是使用add(ap, bq, res),它会自动计算。)
因为必须满足({}_p F_q\left(\cdots \middle| z \right) = C B),所以(C)的条目显然是
[\begin{split}C = \left( \begin{matrix} 1 \ 0 \ 0 \end{matrix} \right)\end{split}]
最后,我们必须计算(3 \times 3)矩阵(M)的条目,使得(z\frac{\mathrm{d}}{\mathrm{d}z} B = M B)成立。这很容易。我们已经计算了第一部分(z\frac{\mathrm{d}}{\mathrm{d}z} B_0)。这给我们了(M)的第一行。对于第二行,我们有:
>>> from sympy import Symbol, cosh, sqrt, diff
>>> z = Symbol("z")
>>> B1 = cosh(2*sqrt(z))
>>> z * diff(B1, z)
sqrt(z)*sinh(2*sqrt(z))
而对于第三个
>>> from sympy import Symbol, sinh, sqrt, expand, diff
>>> z = Symbol("z")
>>> B2 = sinh(2*sqrt(z))*sqrt(z)
>>> expand(z * diff(B2, z))
sqrt(z)*sinh(2*sqrt(z))/2 + z*cosh(2*sqrt(z))
现在我们已经计算出了该矩阵的条目为
[\begin{split}M = \left( \begin{matrix} -\frac{1}{4} & \frac{1}{4} & 0 \ 0 & 0 & 1 \ 0 & z & \frac{1}{2} \ \end{matrix} \right)\end{split}]
注意(C)和(M)的条目通常应该是(z)的有理函数,带有有理系数。这是我们为了将新的公式添加到hyperexpand查找表中所需做的一切。
实现了超几何公式
算法的一个重要部分是超几何函数表示的一个相对较大的表。以下是 SymPy 中实现的所有自动生成的表示(当然,还有更多来自它们的推导)。这些公式主要来自于[Luke1969]和[Prudnikov1990]。它们都经过了数值测试。
[\begin{split}{{}{0}F\left(\begin{matrix} \ \end{matrix}\middle| {z} \right)} = e^{z}\end{split}][\begin{split}{{}{1}F\left(\begin{matrix} a \ \end{matrix}\middle| {z} \right)} = \left(1 - z\right)^{- a}\end{split}][\begin{split}{{}{2}F\left(\begin{matrix} a, a - \frac{1}{2} \ 2 a \end{matrix}\middle| {z} \right)} = 2^{2 a - 1} \left(\sqrt{1 - z} + 1\right)^{1 - 2 a}\end{split}][\begin{split}{{}{2}F\left(\begin{matrix} 1, 1 \ 2 \end{matrix}\middle| {z} \right)} = - \frac{\log{\left(1 - z \right)}}{z}\end{split}][\begin{split}{{}{2}F\left(\begin{matrix} \frac{1}{2}, 1 \ \frac{3}{2} \end{matrix}\middle| {z} \right)} = \frac{\operatorname{atanh}{\left(\sqrt{z} \right)}}{\sqrt{z}}\end{split}][\begin{split}{{}{2}F\left(\begin{matrix} \frac{1}{2}, \frac{1}{2} \ \frac{3}{2} \end{matrix}\middle| {z} \right)} = \frac{\operatorname{asin}{\left(\sqrt{z} \right)}}{\sqrt{z}}\end{split}][\begin{split}{{}{2}F\left(\begin{matrix} a, a + \frac{1}{2} \ \frac{1}{2} \end{matrix}\middle| {z} \right)} = \frac{\left(\sqrt{z} + 1\right)^{- 2 a}}{2} + \frac{\left(1 - \sqrt{z}\right)^{- 2 a}}{2}\end{split}][\begin{split}{{}{2}F\left(\begin{matrix} a, - a \ \frac{1}{2} \end{matrix}\middle| {z} \right)} = \cos{\left(2 a \operatorname{asin}{\left(\sqrt{z} \right)} \right)}\end{split}][\begin{split}{{}{2}F\left(\begin{matrix} 1, 1 \ \frac{3}{2} \end{matrix}\middle| {z} \right)} = \frac{\operatorname{asin}{\left(\sqrt{z} \right)}}{\sqrt{z} \sqrt{1 - z}}\end{split}][\begin{split}{{}{2}F\left(\begin{matrix} \frac{1}{2}, \frac{1}{2} \ 1 \end{matrix}\middle| {z} \right)} = \frac{2 K\left(z\right)}{\pi}\end{split}][\begin{split}{{}{2}F\left(\begin{matrix} - \frac{1}{2}, \frac{1}{2} \ 1 \end{matrix}\middle| {z} \right)} = \frac{2 E\left(z\right)}{\pi}\end{split}][\begin{split}{{}{3}F\left(\begin{matrix} - \frac{1}{2}, 1, 1 \ \frac{1}{2}, 2 \end{matrix}\middle| {z} \right)} = - \frac{2 \sqrt{z} \operatorname{atanh}{\left(\sqrt{z} \right)}}{3} + \frac{2}{3} - \frac{\log{\left(1 - z \right)}}{3 z}\end{split}][\begin{split}{{}{3}F\left(\begin{matrix} - \frac{1}{2}, 1, 1 \ 2, 2 \end{matrix}\middle| {z} \right)} = \left(\frac{4}{9} - \frac{16}{9 z}\right) \sqrt{1 - z} + \frac{4 \log{\left(\frac{\sqrt{1 - z}}{2} + \frac{1}{2} \right)}}{3 z} + \frac{16}{9 z}\end{split}][\begin{split}{{}{1}F\left(\begin{matrix} 1 \ b \end{matrix}\middle| {z} \right)} = z^{1 - b} \left(b - 1\right) e^{z} \gamma\left(b - 1, z\right)\end{split}][\begin{split}{{}{1}F\left(\begin{matrix} a \ 2 a \end{matrix}\middle| {z} \right)} = 4^{a - \frac{1}{2}} z^{\frac{1}{2} - a} e^{\frac{z}{2}} I_{a - \frac{1}{2}}\left(\frac{z}{2}\right) \Gamma\left(a + \frac{1}{2}\right)\end{split}][\begin{split}{{}{1}F\left(\begin{matrix} a \ a + 1 \end{matrix}\middle| {z} \right)} = a \left(z e^{i \pi}\right)^{- a} \gamma\left(a, z e^{i \pi}\right)\end{split}][\begin{split}{{}{1}F\left(\begin{matrix} - \frac{1}{2} \ \frac{1}{2} \end{matrix}\middle| {z} \right)} = \sqrt{z} i \sqrt{\pi} \operatorname{erf}{\left(\sqrt{z} i \right)} + e^{z}\end{split}][\begin{split}{{}{1}F\left(\begin{matrix} 1 \ \frac{3}{4}, \frac{5}{4} \end{matrix}\middle| {z} \right)} = \frac{\sqrt{\pi} \left(i \sinh{\left(2 \sqrt{z} \right)} S\left(\frac{2 \sqrt[4]{z} e^{\frac{i \pi}{4}}}{\sqrt{\pi}}\right) + \cosh{\left(2 \sqrt{z} \right)} C\left(\frac{2 \sqrt[4]{z} e^{\frac{i \pi}{4}}}{\sqrt{\pi}}\right)\right) e^{- \frac{i \pi}{4}}}{2 \sqrt[4]{z}}\end{split}][\begin{split}{{}{2}F\left(\begin{matrix} \frac{1}{2}, a \ \frac{3}{2}, a + 1 \end{matrix}\middle| {z} \right)} = - \frac{a i \sqrt{\pi} \sqrt{\frac{1}{z}} \operatorname{erf}{\left(\sqrt{z} i \right)}}{2 a - 1} - \frac{a \left(z e^{i \pi}\right)^{- a} \gamma\left(a, z e^{i \pi}\right)}{2 a - 1}\end{split}][\begin{split}{{}{2}F\left(\begin{matrix} 1, 1 \ 2, 2 \end{matrix}\middle| {z} \right)} = \frac{- \log{\left(z \right)} + \operatorname{Ei}{\left(z \right)}}{z} - \frac{\gamma}{z}\end{split}][\begin{split}{{}{0}F\left(\begin{matrix} \ \frac{1}{2} \end{matrix}\middle| {z} \right)} = \cosh{\left(2 \sqrt{z} \right)}\end{split}][\begin{split}{{}{0}F\left(\begin{matrix} \ b \end{matrix}\middle| {z} \right)} = z^{\frac{1}{2} - \frac{b}{2}} I_{b - 1}\left(2 \sqrt{z}\right) \Gamma\left(b\right)\end{split}][\begin{split}{{}{0}F\left(\begin{matrix} \ \frac{1}{2}, a, a + \frac{1}{2} \end{matrix}\middle| {z} \right)} = 2^{- 2 a} z^{\frac{1}{4} - \frac{a}{2}} \left(I_{2 a - 1}\left(4 \sqrt[4]{z}\right) + J_{2 a - 1}\left(4 \sqrt[4]{z}\right)\right) \Gamma\left(2 a\right)\end{split}][\begin{split}{{}{0}F\left(\begin{matrix} \ a, 2 a, a + \frac{1}{2} \end{matrix}\middle| {z} \right)} = \left(2 \sqrt{z} e^{\frac{i \pi}{2}}\right)^{1 - 2 a} I_{2 a - 1}\left(2 \sqrt{2} \sqrt[4]{z} e^{\frac{i \pi}{4}}\right) J_{2 a - 1}\left(2 \sqrt{2} \sqrt[4]{z} e^{\frac{i \pi}{4}}\right) \Gamma^{2}\left(2 a\right)\end{split}][\begin{split}{{}{1}F\left(\begin{matrix} a \ 2 a, a - \frac{1}{2} \end{matrix}\middle| {z} \right)} = 2 \cdot 4^{a - 1} z^{1 - a} I_{a - \frac{3}{2}}\left(\sqrt{z}\right) I_{a - \frac{1}{2}}\left(\sqrt{z}\right) \Gamma\left(a - \frac{1}{2}\right) \Gamma\left(a + \frac{1}{2}\right) - 4^{a - \frac{1}{2}} z^{\frac{1}{2} - a} I^{2}{a - \frac{1}{2}}\left(\sqrt{z}\right) \Gamma^{2}\left(a + \frac{1}{2}\right)\end{split}][\begin{split}{{}F_{2}\left(\begin{matrix} \frac{1}{2} \ b, 2 - b \end{matrix}\middle| {z} \right)} = \frac{\pi \left(1 - b\right) I_{1 - b}\left(\sqrt{z}\right) I_{b - 1}\left(\sqrt{z}\right)}{\sin{\left(b \pi \right)}}\end{split}][\begin{split}{{}{1}F\left(\begin{matrix} \frac{1}{2} \ \frac{3}{2}, \frac{3}{2} \end{matrix}\middle| {z} \right)} = \frac{\operatorname{Shi}{\left(2 \sqrt{z} \right)}}{2 \sqrt{z}}\end{split}][\begin{split}{{}{1}F\left(\begin{matrix} \frac{3}{4} \ \frac{3}{2}, \frac{7}{4} \end{matrix}\middle| {z} \right)} = \frac{3 \sqrt{\pi} e^{- \frac{3 i \pi}{4}} S\left(\frac{2 \sqrt[4]{z} e^{\frac{i \pi}{4}}}{\sqrt{\pi}}\right)}{4 z^{\frac{3}{4}}}\end{split}][\begin{split}{{}{1}F\left(\begin{matrix} \frac{1}{4} \ \frac{1}{2}, \frac{5}{4} \end{matrix}\middle| {z} \right)} = \frac{\sqrt{\pi} e^{- \frac{i \pi}{4}} C\left(\frac{2 \sqrt[4]{z} e^{\frac{i \pi}{4}}}{\sqrt{\pi}}\right)}{2 \sqrt[4]{z}}\end{split}][\begin{split}{{}{2}F\left(\begin{matrix} a, a + \frac{1}{2} \ b, 2 a, 2 a - b + 1 \end{matrix}\middle| {z} \right)} = \left(\frac{\sqrt{z}}{2}\right)^{1 - 2 a} I_{2 a - b}\left(\sqrt{z}\right) I_{b - 1}\left(\sqrt{z}\right) \Gamma\left(b\right) \Gamma\left(2 a - b + 1\right)\end{split}][\begin{split}{{}{2}F\left(\begin{matrix} 1, 1 \ \frac{3}{2}, 2, 2 \end{matrix}\middle| {z} \right)} = \frac{- \log{\left(2 \sqrt{z} \right)} + \operatorname{Chi}\left(2 \sqrt{z}\right)}{z} - \frac{\gamma}{z}\end{split}][\begin{split}{{}{3}F\left(\begin{matrix} 1, 1, a \ 2, 2, a + 1 \end{matrix}\middle| {z} \right)} = \frac{a \left(- z\right)^{- a} \left(\Gamma\left(a\right) - \Gamma\left(a, - z\right)\right)}{\left(a - 1\right)^{2}} + \frac{a \left(1 - a\right) \left(\log{\left(- z \right)} + \operatorname{E}_{1}\left(- z\right) + \gamma\right)}{z \left(a^{2} - 2 a + 1\right)} - \frac{a e^{z}}{z \left(a^{2} - 2 a + 1\right)} + \frac{a}{z \left(a^{2} - 2 a + 1\right)}\end{split}]
参考文献
[Roach1996]
Kelly B. Roach. 超几何函数表示。在:1996 年国际符号和代数计算研讨会论文集,页码 301-308,纽约,1996 年。ACM。
[Roach1997]
Kelly B. Roach. Meijer G 函数表示。在:1997 年国际符号和代数计算研讨会论文集,页码 205-211,纽约,1997 年。ACM。
[Luke1969]
Luke, Y. L. (1969),特殊函数及其近似,第 1 卷。
[Prudnikov1990]
A. P. Prudnikov, Yu. A. Brychkov 和 O. I. Marichev (1990)。积分和级数:更多特殊函数,第 3 卷,Gordon and Breach Science Publisher。
《洪光·傅的三角简化》
Fu 等人的三角简化算法实现
Fu 算法的核心思想是利用学生在预微积分课程中学到的一系列规则。这些规则是启发式应用的,它使用贪婪算法同时应用多个规则,并选择具有最少叶子节点的结果。
存在一些转换规则,其中单个规则应用于表达树。以下仅仅是助记性质的;详细示例请参见文档字符串。
-
TR0()- 简化表达式 -
TR1()- sec-csc 到 cos-sin -
TR2()- tan-cot 到 sin-cos 的比率 -
TR2i()- sin-cos 的比率到 tan -
TR3()- 角度规范化 -
TR4()- 特定角度上的函数 -
TR5()- sin 的幂到 cos 的幂 -
TR6()- cos 的幂到 sin 的幂 -
TR7()- 减少 cos 的幂(增加角度) -
TR8()- 展开 sin-cos 的乘积为和 -
TR9()- 将 sin-cos 的和约简为乘积 -
TR10()- 分离 sin-cos 的参数 -
TR10i()- 收集 sin-cos 的参数 -
TR11()- 减少双角度 -
TR12()- 分离 tan 的参数 -
TR12i()- 收集 tan 的参数 -
TR13()- 展开 tan-cot 的乘积 -
TRmorrie()- prod(cos(x2i), (i, 0, k - 1)) -> sin(2kx)/(2**k*sin(x)) -
TR14()- sin 或 cos 的幂的因式分解到 cos 或 sin 的幂 -
TR15()- sin 的负幂到 cot 的幂 -
TR16()- cos 的负幂到 tan 的幂 -
TR22()- tan-cot 的幂到 sec-csc 函数的负幂 -
TR111()- sin-cos-tan 的负幂到 csc-sec-cot
存在 4 种组合转换(CTR1 - CTR4),其中应用一系列转换,并从几个选项中选择最简表达式。
最后,有两个规则列表(RL1 和 RL2),它们应用一系列转换和组合转换,以及fu算法本身,它应用规则和规则列表并选择最佳表达式。还有一个函数L,它计算表达式中出现的三角函数的数量。
除了 TR0 外,转换不会重新编写表达式。例如,TR10i 找到了一个形式为cos(x)*cos(y) + sin(x)*sin(y)的和的项对。这类表达式在对表达式进行自下而上的遍历时被针对,但不会尝试操纵它们使其出现。例如,
为下面的示例做准备:
>>> from sympy.simplify.fu import fu, L, TR9, TR10i, TR11
>>> from sympy import factor, sin, cos, powsimp
>>> from sympy.abc import x, y, z, a
>>> from time import time
>>> eq = cos(x + y)/cos(x)
>>> TR10i(eq.expand(trig=True))
-sin(x)*sin(y)/cos(x) + cos(y)
如果将表达式放在“正常”形式(具有共同的分母)中,则转换是成功的:
>>> TR10i(_.normal())
cos(x + y)/cos(x)
TR11 的行为类似。它将双角重新写成较小的角度,但不对结果进行任何简化。
>>> TR11(sin(2)**a*cos(1)**(-a), 1)
(2*sin(1)*cos(1))**a/cos(1)**a
>>> powsimp(_)
(2*sin(1))**a
诱惑是尝试使这些 TR 规则“更智能化”,但实际上应该在更高的层次上完成;TR 规则应尝试保持“专注于一件事”的原则。然而,有一个例外。在 TR10i 和 TR9 中,即使它们各自乘以一个公因子,也会识别到术语:
>>> fu(a*cos(x)*cos(y) + a*sin(x)*sin(y))
a*cos(x - y)
使用factor_terms进行因式分解,但它类似于“即时”执行,直到被认为有必要才执行。此外,如果因式分解对简化没有帮助,则不会保留它,因此a*cos(x)*cos(y) + a*sin(x)*sin(z)不会变成一个因式分解(但在三角函数意义上未简化)的表达式:
>>> fu(a*cos(x)*cos(y) + a*sin(x)*sin(z))
a*sin(x)*sin(z) + a*cos(x)*cos(y)
在某些情况下,因式分解可能是一个好主意,但用户需要自行决定。例如:
>>> expr=((15*sin(2*x) + 19*sin(x + y) + 17*sin(x + z) + 19*cos(x - z) +
... 25)*(20*sin(2*x) + 15*sin(x + y) + sin(y + z) + 14*cos(x - z) +
... 14*cos(y - z))*(9*sin(2*y) + 12*sin(y + z) + 10*cos(x - y) + 2*cos(y -
... z) + 18)).expand(trig=True).expand()
在展开状态下,有近 1000 个三角函数:
>>> L(expr)
932
如果首先对表达式进行因式分解,这将花费时间,但生成的表达式将非常快速地被转换:
>>> def clock(f, n=2):
... t=time(); f(); return round(time()-t, n)
...
>>> clock(lambda: factor(expr))
0.86
>>> clock(lambda: TR10i(expr), 3)
0.016
如果使用未展开的表达式,则转换时间较长,但不如因式分解和转换所需的时间长:
>>> clock(lambda: TR10i(expr), 2)
0.28
因此,在TR10i中既不使用展开也不使用因式分解:如果表达式已经被因式分解(或部分因式分解),那么带有trig=True的展开会破坏已知内容并且需要更长时间;如果表达式已经展开,进行因式分解可能比简单应用转换本身还要花费更长时间。
尽管算法应该是规范的,总是给出相同的结果,但它们可能不会产生最佳结果。这一般是简化的本质,因为搜索所有可能的转换路径非常昂贵。这里有一个简单的例子。以下和有 6 项的和:
>>> expr = (sin(x)**2*cos(y)*cos(z) + sin(x)*sin(y)*cos(x)*cos(z) +
... sin(x)*sin(z)*cos(x)*cos(y) + sin(y)*sin(z)*cos(x)**2 + sin(y)*sin(z) +
... cos(y)*cos(z))
>>> args = expr.args
出乎意料地,fu提供了最佳结果:
>>> fu(expr)
3*cos(y - z)/2 - cos(2*x + y + z)/2
但是,如果合并不同的项,则可能得到一个次优结果,需要额外的工作来获得更好的简化,但仍然不是最优的。以下显示了一种expr的另一种形式,一旦采取某个步骤,则阻碍最优简化,因为它导致了死胡同:
>>> TR9(-cos(x)**2*cos(y + z) + 3*cos(y - z)/2 +
... cos(y + z)/2 + cos(-2*x + y + z)/4 - cos(2*x + y + z)/4)
sin(2*x)*sin(y + z)/2 - cos(x)**2*cos(y + z) + 3*cos(y - z)/2 + cos(y + z)/2
这里有一个展示相同行为的较小表达式:
>>> a = sin(x)*sin(z)*cos(x)*cos(y) + sin(x)*sin(y)*cos(x)*cos(z)
>>> TR10i(a)
sin(x)*sin(y + z)*cos(x)
>>> newa = _
>>> TR10i(expr - a) # this combines two more of the remaining terms
sin(x)**2*cos(y)*cos(z) + sin(y)*sin(z)*cos(x)**2 + cos(y - z)
>>> TR10i(_ + newa) == _ + newa # but now there is no more simplification
True
在没有侥幸或尝试所有可能的参数对的情况下,最终结果可能不太理想,并且没有更好的启发法或所有可能性的暴力试验,无法找到。
规则
sympy.simplify.fu.TR0(rv)
简化有理多项式,尝试简化表达式,例如组合像 3x + 2x 这样的东西等……
sympy.simplify.fu.TR1(rv)
用 1/cos 和 1/sin 替换 sec 和 csc
例子
>>> from sympy.simplify.fu import TR1, sec, csc
>>> from sympy.abc import x
>>> TR1(2*csc(x) + sec(x))
1/cos(x) + 2/sin(x)
sympy.simplify.fu.TR2(rv)
用 sin/cos 和 cos/sin 替换 tan 和 cot
例子
>>> from sympy.simplify.fu import TR2
>>> from sympy.abc import x
>>> from sympy import tan, cot, sin, cos
>>> TR2(tan(x))
sin(x)/cos(x)
>>> TR2(cot(x))
cos(x)/sin(x)
>>> TR2(tan(tan(x) - sin(x)/cos(x)))
0
sympy.simplify.fu.TR2i(rv, half=False)
将涉及 sin 和 cos 的比率转换为:
sin(x)/cos(x) -> tan(x) sin(x)/(cos(x) + 1) -> tan(x/2),如果 half=True
例子
>>> from sympy.simplify.fu import TR2i
>>> from sympy.abc import x, a
>>> from sympy import sin, cos
>>> TR2i(sin(x)/cos(x))
tan(x)
分子和分母的幂也会被识别
>>> TR2i(sin(x)**2/(cos(x) + 1)**2, half=True)
tan(x/2)**2
除非假设允许(即基数必须为正或指数必须为分子和分母的整数),否则转换不会发生。
>>> TR2i(sin(x)**a/(cos(x) + 1)**a)
sin(x)**a/(cos(x) + 1)**a
sympy.simplify.fu.TR3(rv)
引出的公式:例子 sin(-a) = -sin(a)
例子
>>> from sympy.simplify.fu import TR3
>>> from sympy.abc import x, y
>>> from sympy import pi
>>> from sympy import cos
>>> TR3(cos(y - x*(y - x)))
cos(x*(x - y) + y)
>>> cos(pi/2 + x)
-sin(x)
>>> cos(30*pi/2 + x)
-cos(x)
sympy.simplify.fu.TR4(rv)
识别特殊角度的值。
A= 0 Pi/6 Pi/4 Pi/3 Pi/2
sin(a) 0 1/2 sqrt(2)/2 sqrt(3)/2 1 cos(a) 1 sqrt(3)/2 sqrt(2)/2 1/2 0 tan(a) 0 sqt(3)/3 1 sqrt(3) –
例子
>>> from sympy import pi
>>> from sympy import cos, sin, tan, cot
>>> for s in (0, pi/6, pi/4, pi/3, pi/2):
... print('%s %s %s %s' % (cos(s), sin(s), tan(s), cot(s)))
...
1 0 0 zoo
sqrt(3)/2 1/2 sqrt(3)/3 sqrt(3)
sqrt(2)/2 sqrt(2)/2 1 1
1/2 sqrt(3)/2 sqrt(3) sqrt(3)/3
0 1 zoo 0
sympy.simplify.fu.TR5(rv, max=4, pow=False)
用 1 - cos(x)2 替换 sin2。
查看 _TR56 文档字符串以了解max和pow的高级用法。
例子
>>> from sympy.simplify.fu import TR5
>>> from sympy.abc import x
>>> from sympy import sin
>>> TR5(sin(x)**2)
1 - cos(x)**2
>>> TR5(sin(x)**-2) # unchanged
sin(x)**(-2)
>>> TR5(sin(x)**4)
(1 - cos(x)**2)**2
sympy.simplify.fu.TR6(rv, max=4, pow=False)
用 1 - sin(x)2 替换 cos2。
查看 _TR56 文档字符串以了解max和pow的高级用法。
例子
>>> from sympy.simplify.fu import TR6
>>> from sympy.abc import x
>>> from sympy import cos
>>> TR6(cos(x)**2)
1 - sin(x)**2
>>> TR6(cos(x)**-2) #unchanged
cos(x)**(-2)
>>> TR6(cos(x)**4)
(1 - sin(x)**2)**2
sympy.simplify.fu.TR7(rv)
降低 cos(x)**2 的度数。
例子
>>> from sympy.simplify.fu import TR7
>>> from sympy.abc import x
>>> from sympy import cos
>>> TR7(cos(x)**2)
cos(2*x)/2 + 1/2
>>> TR7(cos(x)**2 + 1)
cos(2*x)/2 + 3/2
sympy.simplify.fu.TR8(rv, first=True)
将cos和/或sin的乘积转换为cos和/或sin项的和或差。
例子
>>> from sympy.simplify.fu import TR8
>>> from sympy import cos, sin
>>> TR8(cos(2)*cos(3))
cos(5)/2 + cos(1)/2
>>> TR8(cos(2)*sin(3))
sin(5)/2 + sin(1)/2
>>> TR8(sin(2)*sin(3))
-cos(5)/2 + cos(1)/2
sympy.simplify.fu.TR9(rv)
cos或sin项的和作为cos或sin的乘积。
例子
>>> from sympy.simplify.fu import TR9
>>> from sympy import cos, sin
>>> TR9(cos(1) + cos(2))
2*cos(1/2)*cos(3/2)
>>> TR9(cos(1) + 2*sin(1) + 2*sin(2))
cos(1) + 4*sin(3/2)*cos(1/2)
如果 TR9 没有进行任何更改,则不会重新排列表达式。例如,尽管尝试因式分解公共项,但如果因式分解的表达式没有改变,将返回原始表达式:
>>> TR9(cos(3) + cos(3)*cos(2))
cos(3) + cos(2)*cos(3)
sympy.simplify.fu.TR10(rv, first=True)
在cos和sin中分离求和。
例子
>>> from sympy.simplify.fu import TR10
>>> from sympy.abc import a, b, c
>>> from sympy import cos, sin
>>> TR10(cos(a + b))
-sin(a)*sin(b) + cos(a)*cos(b)
>>> TR10(sin(a + b))
sin(a)*cos(b) + sin(b)*cos(a)
>>> TR10(sin(a + b + c))
(-sin(a)*sin(b) + cos(a)*cos(b))*sin(c) + (sin(a)*cos(b) + sin(b)*cos(a))*cos(c)
sympy.simplify.fu.TR10i(rv)
产品的和到函数的简化。
例子
>>> from sympy.simplify.fu import TR10i
>>> from sympy import cos, sin, sqrt
>>> from sympy.abc import x
>>> TR10i(cos(1)*cos(3) + sin(1)*sin(3))
cos(2)
>>> TR10i(cos(1)*sin(3) + sin(1)*cos(3) + cos(3))
cos(3) + sin(4)
>>> TR10i(sqrt(2)*cos(x)*x + sqrt(6)*sin(x)*x)
2*sqrt(2)*x*sin(x + pi/6)
sympy.simplify.fu.TR11(rv, base=None)
双角函数到乘积的函数。base参数可用于指示未加倍的参数,例如,如果 3pi/7 是基础,则参数为 6pi/7 的 cosine 和 sine 函数将被替换。
例子
>>> from sympy.simplify.fu import TR11
>>> from sympy import cos, sin, pi
>>> from sympy.abc import x
>>> TR11(sin(2*x))
2*sin(x)*cos(x)
>>> TR11(cos(2*x))
-sin(x)**2 + cos(x)**2
>>> TR11(sin(4*x))
4*(-sin(x)**2 + cos(x)**2)*sin(x)*cos(x)
>>> TR11(sin(4*x/3))
4*(-sin(x/3)**2 + cos(x/3)**2)*sin(x/3)*cos(x/3)
如果参数只是整数,则不会进行任何更改,除非提供基数:
>>> TR11(cos(2))
cos(2)
>>> TR11(cos(4), 2)
-sin(2)**2 + cos(2)**2
这里有一个微妙的问题,即自动简化将一些更高的角度转换为较低的角度。
>>> cos(6*pi/7) + cos(3*pi/7)
-cos(pi/7) + cos(3*pi/7)
6pi/7 角现在是 pi/7,但可以通过提供 3pi/7 的基数目标到 TR11:
>>> TR11(_, 3*pi/7)
-sin(3*pi/7)**2 + cos(3*pi/7)**2 + cos(3*pi/7)
sympy.simplify.fu.TR12(rv, first=True)
在tan中分离求和。
例子
>>> from sympy.abc import x, y
>>> from sympy import tan
>>> from sympy.simplify.fu import TR12
>>> TR12(tan(x + y))
(tan(x) + tan(y))/(-tan(x)*tan(y) + 1)
sympy.simplify.fu.TR12i(rv)
将 tan(y) + tan(x))/(tan(x)*tan(y) - 1)组合为-tan(x + y)的参数。
例子
>>> from sympy.simplify.fu import TR12i
>>> from sympy import tan
>>> from sympy.abc import a, b, c
>>> ta, tb, tc = [tan(i) for i in (a, b, c)]
>>> TR12i((ta + tb)/(-ta*tb + 1))
tan(a + b)
>>> TR12i((ta + tb)/(ta*tb - 1))
-tan(a + b)
>>> TR12i((-ta - tb)/(ta*tb - 1))
tan(a + b)
>>> eq = (ta + tb)/(-ta*tb + 1)**2*(-3*ta - 3*tc)/(2*(ta*tc - 1))
>>> TR12i(eq.expand())
-3*tan(a + b)*tan(a + c)/(2*(tan(a) + tan(b) - 1))
sympy.simplify.fu.TR13(rv)
更改tan或cot的产品。
例子
>>> from sympy.simplify.fu import TR13
>>> from sympy import tan, cot
>>> TR13(tan(3)*tan(2))
-tan(2)/tan(5) - tan(3)/tan(5) + 1
>>> TR13(cot(3)*cot(2))
cot(2)*cot(5) + 1 + cot(3)*cot(5)
sympy.simplify.fu.TRmorrie(rv)
返回 cos(x)cos(2x)…cos(2(k-1)*x) -> sin(2kx)/(2**ksin(x))
例子
>>> from sympy.simplify.fu import TRmorrie, TR8, TR3
>>> from sympy.abc import x
>>> from sympy import Mul, cos, pi
>>> TRmorrie(cos(x)*cos(2*x))
sin(4*x)/(4*sin(x))
>>> TRmorrie(7*Mul(*[cos(x) for x in range(10)]))
7*sin(12)*sin(16)*cos(5)*cos(7)*cos(9)/(64*sin(1)*sin(3))
有时,自动简化会导致某个幂不被识别。例如,在以下情况中,cos(4pi/7)会自动简化为-cos(3pi/7),因此只有 3 个术语中的 2 个会被识别:
>>> TRmorrie(cos(pi/7)*cos(2*pi/7)*cos(4*pi/7))
-sin(3*pi/7)*cos(3*pi/7)/(4*sin(pi/7))
TR8 轻触将表达式解决为有理数
>>> TR8(_)
-1/8
在这种情况下,如果未简化等式,将直接获得答案:
>>> eq = cos(pi/9)*cos(2*pi/9)*cos(3*pi/9)*cos(4*pi/9)
>>> TRmorrie(eq)
1/16
但是如果角度通过 TR3 变为规范化,则未简化的答案将直接获得:
>>> TR3(eq)
sin(pi/18)*cos(pi/9)*cos(2*pi/9)/2
>>> TRmorrie(_)
sin(pi/18)*sin(4*pi/9)/(8*sin(pi/9))
>>> TR8(_)
cos(7*pi/18)/(16*sin(pi/9))
>>> TR3(_)
1/16
原始表达式将直接解决为 1/16,但是:
>>> TR8(eq)
1/16
参考
[R852]
en.wikipedia.org/wiki/Morrie%27s_law 的内容。
sympy.simplify.fu.TR14(rv, first=True)
将 sin 和 cos 的因式分解幂转换为更简单的表达式。
示例
>>> from sympy.simplify.fu import TR14
>>> from sympy.abc import x, y
>>> from sympy import cos, sin
>>> TR14((cos(x) - 1)*(cos(x) + 1))
-sin(x)**2
>>> TR14((sin(x) - 1)*(sin(x) + 1))
-cos(x)**2
>>> p1 = (cos(x) + 1)*(cos(x) - 1)
>>> p2 = (cos(y) - 1)*2*(cos(y) + 1)
>>> p3 = (3*(cos(y) - 1))*(3*(cos(y) + 1))
>>> TR14(p1*p2*p3*(x - 1))
-18*(x - 1)*sin(x)**2*sin(y)**4
sympy.simplify.fu.TR15(rv, max=4, pow=False)
将 sin(x)-2 转换为 1 + cot(x)2。
查看 _TR56 的 docstring 以了解 max 和 pow 的高级用法。
示例
>>> from sympy.simplify.fu import TR15
>>> from sympy.abc import x
>>> from sympy import sin
>>> TR15(1 - 1/sin(x)**2)
-cot(x)**2
sympy.simplify.fu.TR16(rv, max=4, pow=False)
将 cos(x)-2 转换为 1 + tan(x)2。
查看 _TR56 的 docstring 以了解 max 和 pow 的高级用法。
示例
>>> from sympy.simplify.fu import TR16
>>> from sympy.abc import x
>>> from sympy import cos
>>> TR16(1 - 1/cos(x)**2)
-tan(x)**2
sympy.simplify.fu.TR111(rv)
将 f(x)-i 转换为 g(x)i,其中 i 是整数或基数为正且 f、g 是:tan、cot;sin、csc;或 cos、sec。
示例
>>> from sympy.simplify.fu import TR111
>>> from sympy.abc import x
>>> from sympy import tan
>>> TR111(1 - 1/tan(x)**2)
1 - cot(x)**2
sympy.simplify.fu.TR22(rv, max=4, pow=False)
将 tan(x)2 转换为 sec(x)2 - 1,cot(x)2 转换为 csc(x)2 - 1。
查看 _TR56 的 docstring 以了解 max 和 pow 的高级用法。
示例
>>> from sympy.simplify.fu import TR22
>>> from sympy.abc import x
>>> from sympy import tan, cot
>>> TR22(1 + tan(x)**2)
sec(x)**2
>>> TR22(1 + cot(x)**2)
csc(x)**2
sympy.simplify.fu.TRpower(rv)
将 sin(x)n 和 cos(x)n(其中 n 为正数)转换为和的形式。
示例
>>> from sympy.simplify.fu import TRpower
>>> from sympy.abc import x
>>> from sympy import cos, sin
>>> TRpower(sin(x)**6)
-15*cos(2*x)/32 + 3*cos(4*x)/16 - cos(6*x)/32 + 5/16
>>> TRpower(sin(x)**3*cos(2*x)**4)
(3*sin(x)/4 - sin(3*x)/4)*(cos(4*x)/2 + cos(8*x)/8 + 3/8)
参考文献
[R853]
en.wikipedia.org/wiki/List_of_trigonometric_identities#Power-reduction_formulae 的内容。
sympy.simplify.fu.fu(rv, measure=<function <lambda>>)
尝试使用 Fu 等人算法中给出的转换规则简化表达式。
fu() 将尝试通过最小化目标函数 measure 来简化表达式。默认情况下,首先最小化三角函数的数量,然后最小化总操作数。
示例
>>> from sympy.simplify.fu import fu
>>> from sympy import cos, sin, tan, pi, S, sqrt
>>> from sympy.abc import x, y, a, b
>>> fu(sin(50)**2 + cos(50)**2 + sin(pi/6))
3/2
>>> fu(sqrt(6)*cos(x) + sqrt(2)*sin(x))
2*sqrt(2)*sin(x + pi/3)
CTR1 示例
>>> eq = sin(x)**4 - cos(y)**2 + sin(y)**2 + 2*cos(x)**2
>>> fu(eq)
cos(x)**4 - 2*cos(y)**2 + 2
CTR2 示例
>>> fu(S.Half - cos(2*x)/2)
sin(x)**2
CTR3 示例
>>> fu(sin(a)*(cos(b) - sin(b)) + cos(a)*(sin(b) + cos(b)))
sqrt(2)*sin(a + b + pi/4)
CTR4 示例
>>> fu(sqrt(3)*cos(x)/2 + sin(x)/2)
sin(x + pi/3)
示例 1
>>> fu(1-sin(2*x)**2/4-sin(y)**2-cos(x)**4)
-cos(x)**2 + cos(y)**2
示例 2
>>> fu(cos(4*pi/9))
sin(pi/18)
>>> fu(cos(pi/9)*cos(2*pi/9)*cos(3*pi/9)*cos(4*pi/9))
1/16
示例 3
>>> fu(tan(7*pi/18)+tan(5*pi/18)-sqrt(3)*tan(5*pi/18)*tan(7*pi/18))
-sqrt(3)
目标函数示例
>>> fu(sin(x)/cos(x)) # default objective function
tan(x)
>>> fu(sin(x)/cos(x), measure=lambda x: -x.count_ops()) # maximize op count
sin(x)/cos(x)
参考文献
[R854]
www.sciencedirect.com/science/article/pii/S0895717706001609 的内容。
注意
这项工作由 Dimitar Vlahovski 在 “Electronic systems” 技术学校(2011 年 11 月 30 日)开始。
超出 TR13,其他规则不是来自原始论文,而是在 SymPy 中扩展的。
参考文献
求解器
本模块文档详细介绍了 sympy.solvers 模块的函数。
内容
-
丢番图方程
-
不等式求解器
-
常微分方程
-
偏微分方程
-
求解器
-
解集
丢番图
注意
对于初学者的指南,重点在解决丢番图方程上,请参阅 代数解丢番图方程。
丢番图方程
“丢番图”一词源自于数学家丢番奴,他大约生活在公元 250 年左右的亚历山大大城市。他在其著作《算术》中提出了 150 个问题,标志着数论的早期发展,即关于整数及其性质的研究领域。丢番图方程在数论中起着核心和重要的作用。
我们称“丢番图方程”为形如 (f(x_1, x_2, \ldots x_n) = 0) 的方程,其中 (n \geq 2) 且 (x_1, x_2, \ldots x_n) 是整数变量。如果我们能找到 (n) 个整数 (a_1, a_2, \ldots a_n) 使得 (x_1 = a_1, x_2 = a_2, \ldots x_n = a_n) 满足上述方程,则称该方程可解。您可以在 [1] 和 [2] 中了解更多关于丢番图方程的信息。
目前,diophantine() 及其它丢番图模块的辅助函数可以解决以下五种类型的丢番图方程。
-
线性丢番图方程:(a_1x_1 + a_2x_2 + \ldots + a_nx_n = b).
-
一般二元二次方程:(ax² + bxy + cy² + dx + ey + f = 0)
-
齐次三元二次方程:(ax² + by² + cz² + dxy + eyz + fzx = 0)
-
扩展勾股定理方程:(a_{1}x_{1}² + a_{2}x_{2}² + \ldots + a_{n}x_{n}² = a_{n+1}x_{n+1}²)
-
一般平方和:(x_{1}² + x_{2}² + \ldots + x_{n}² = k)
模块结构
这个模块包含 diophantine() 函数及其它辅助函数,用于解决特定的丢番图方程。其结构如下所示。
-
diophantine()-
diop_solve()-
classify_diop() -
diop_linear() -
diop_quadratic() -
diop_ternary_quadratic() -
diop_ternary_quadratic_normal() -
diop_general_pythagorean() -
diop_general_sum_of_squares() -
diop_general_sum_of_even_powers()
-
-
merge_solution()
-
当方程被传递给diophantine()时,它会因式分解该方程(如果可能),并通过分别调用diop_solve()解决每个因子给出的方程。然后,所有结果都使用merge_solution()组合起来。
diop_solve()在内部使用classify_diop()来找到给定给它的方程的类型(以及其他一些细节),然后根据返回的类型调用适当的求解器函数。例如,如果classify_diop()返回方程的类型为“线性”,那么diop_solve()会调用diop_linear()来解决该方程。
每个函数,diop_linear(), diop_quadratic(), diop_ternary_quadratic(), diop_general_pythagorean() 和 diop_general_sum_of_squares() 都解决特定类型的方程,根据名称可以轻易猜出类型。
除了这些函数外,“Diophantine Module”中还有大量其他函数,所有这些函数都列在用户函数和内部函数下。
教程
首先,让我们导入丢番图模块的最高级 API。
>>> from sympy.solvers.diophantine import diophantine
在我们开始解决方程之前,我们需要定义变量。
>>> from sympy import symbols
>>> x, y, z = symbols("x, y, z", integer=True)
让我们从解决最简单类型的丢番图方程开始,即线性丢番图方程。让我们解决 (2x + 3y = 5)。请注意,尽管我们以上述形式编写方程,但当我们将方程输入到丢番图模块中的任何函数时,它需要是 (eq = 0) 的形式。
>>> diophantine(2*x + 3*y - 5)
{(3*t_0 - 5, 5 - 2*t_0)}
请注意,再往下一级,我们可以通过调用 diop_solve() 解决完全相同的方程。
>>> from sympy.solvers.diophantine.diophantine import diop_solve
>>> diop_solve(2*x + 3*y - 5)
(3*t_0 - 5, 5 - 2*t_0)
请注意,它返回的是元组而不是集合。diophantine() 总是返回一组元组。但是,diop_solve() 可能根据给定方程的类型返回单个元组或一组元组。
我们还可以通过调用 diop_linear() 来解决这个方程,这是 diop_solve() 内部调用的函数。
>>> from sympy.solvers.diophantine.diophantine import diop_linear
>>> diop_linear(2*x + 3*y - 5)
(3*t_0 - 5, 5 - 2*t_0)
如果给定方程没有解,则输出如下所示。
>>> diophantine(2*x + 4*y - 3)
set()
>>> diop_solve(2*x + 4*y - 3)
(None, None)
>>> diop_linear(2*x + 4*y - 3)
(None, None)
请注意,除了最高级 API 外,如果没有解决方案,则返回一个 None 元组。元组的大小与变量数相同。此外,可以通过传递定制参数来设置解决方案中要使用的参数。考虑以下示例:
>>> m = symbols("m", integer=True)
>>> diop_solve(2*x + 3*y - 5, m)
(3*m_0 - 5, 5 - 2*m_0)
对于线性丢番图方程,解决方案中每个自由变量的前缀都是定制参数。考虑以下示例:
>>> diop_solve(2*x + 3*y - 5*z + 7, m)
(m_0, m_0 + 5*m_1 - 14, m_0 + 3*m_1 - 7)
在上述解中,(m_0) 和 (m_1) 是独立的自由变量。
请注意,目前用户只能为线性丢番图方程和二元二次方程设置参数。
让我们尝试解决一个二元二次方程,这是一个具有两个变量且二次度的方程。在尝试解决这些方程之前,了解与方程相关的各种情况会很有帮助。请参考[3]和[4]以获取关于不同情况和解的详细分析。让我们定义 (\Delta = b² - 4ac) 关于二元二次方程 (ax² + bxy + cy² + dx + ey + f = 0)。
当 (\Delta < 0) 时,要么没有解,要么只有有限个解。
>>> diophantine(x**2 - 4*x*y + 8*y**2 - 3*x + 7*y - 5)
{(2, 1), (5, 1)}
在上述方程中 (\Delta = (-4)² - 418 = -16),因此只有有限个解存在。
当 (\Delta = 0) 时,我们可能没有解,或者有参数化的解。
>>> diophantine(3*x**2 - 6*x*y + 3*y**2 - 3*x + 7*y - 5)
set()
>>> diophantine(x**2 - 4*x*y + 4*y**2 - 3*x + 7*y - 5)
{(-2*t**2 - 7*t + 10, -t**2 - 3*t + 5)}
>>> diophantine(x**2 + 2*x*y + y**2 - 3*x - 3*y)
{(t_0, -t_0), (t_0, 3 - t_0)}
最有趣的情况是当 (\Delta > 0) 且不是完全平方时。在这种情况下,方程要么没有解,要么有无限多个解。考虑下面 (\Delta = 8) 的情况。
>>> diophantine(x**2 - 4*x*y + 2*y**2 - 3*x + 7*y - 5)
set()
>>> from sympy import sqrt
>>> n = symbols("n", integer=True)
>>> s = diophantine(x**2 - 2*y**2 - 2*x - 4*y, n)
>>> x_1, y_1 = s.pop()
>>> x_2, y_2 = s.pop()
>>> x_n = -(-2*sqrt(2) + 3)**n/2 + sqrt(2)*(-2*sqrt(2) + 3)**n/2 - sqrt(2)*(2*sqrt(2) + 3)**n/2 - (2*sqrt(2) + 3)**n/2 + 1
>>> x_1 == x_n or x_2 == x_n
True
>>> y_n = -sqrt(2)*(-2*sqrt(2) + 3)**n/4 + (-2*sqrt(2) + 3)**n/2 + sqrt(2)*(2*sqrt(2) + 3)**n/4 + (2*sqrt(2) + 3)**n/2 - 1
>>> y_1 == y_n or y_2 == y_n
True
这里 (n) 是整数。虽然 (x_n) 和 (y_n) 看起来可能不像整数,但是通过设置特定的 (n) 值(并简化)可以证明它们是整数。例如,考虑下面的例子,我们将 (n) 设为 9。
>>> from sympy import simplify
>>> simplify(x_n.subs({n: 9}))
-9369318
任何形如 (ax² + bxy + cy² + dx + ey + f = 0) 的二元二次方程都可以转换成等价形式 (X² - DY² = N)。
>>> from sympy.solvers.diophantine.diophantine import find_DN, diop_DN, transformation_to_DN
>>> find_DN(x**2 - 3*x*y + y**2 - 7*x + 5*y - 3)
(5, 920)
因此,上述方程在线性变换后等价于方程 (X² - 5Y² = 920)。如果我们想找到线性变换,我们可以使用 transformation_to_DN()。
>>> A, B = transformation_to_DN(x**2 - 3*x*y + y**2 - 7*x + 5*y - 3)
这里的 (A) 是一个 2 X 2 矩阵,(B) 是一个 2 X 1 矩阵,这样变换
[\begin{split}\begin{bmatrix} X\Y \end{bmatrix} = A \begin{bmatrix} x\y \end{bmatrix} + B\end{split}]
给出方程 (X² -5Y² = 920)。(A) 和 (B) 的值如下。
>>> A
Matrix([
[1/10, 3/10],
[ 0, 1/5]])
>>> B
Matrix([
[ 1/5],
[-11/5]])
我们可以通过将 (D) 和 (N) 传递给 diop_DN() 来解决形如 (X² - DY² = N) 的方程。
>>> diop_DN(5, 920)
[]
不幸的是,我们的方程没有解。
现在让我们转向齐次三元二次方程。这些方程的形式为 (ax² + by² + cz² + dxy + eyz + fzx = 0)。这类方程要么有无限多个解,要么没有解(除了显然的解(0, 0, 0))。
>>> diophantine(3*x**2 + 4*y**2 - 5*z**2 + 4*x*y + 6*y*z + 7*z*x)
{(0, 0, 0)}
>>> diophantine(3*x**2 + 4*y**2 - 5*z**2 + 4*x*y - 7*y*z + 7*z*x)
{(-16*p**2 + 28*p*q + 20*q**2, 3*p**2 + 38*p*q - 25*q**2, 4*p**2 - 24*p*q + 68*q**2)}
如果您只对基本解感兴趣,而不是参数化的一般解(更准确地说是一般解之一),您可以使用 diop_ternary_quadratic()。
>>> from sympy.solvers.diophantine.diophantine import diop_ternary_quadratic
>>> diop_ternary_quadratic(3*x**2 + 4*y**2 - 5*z**2 + 4*x*y - 7*y*z + 7*z*x)
(-4, 5, 1)
diop_ternary_quadratic() first converts the given equation to an equivalent equation of the form (w² = AX² + BY²) and then it uses descent() to solve the latter equation. You can refer to the docs of transformation_to_normal() to find more on this. The equation (w² = AX² + BY²) can be solved more easily by using the Aforementioned descent().
>>> from sympy.solvers.diophantine.diophantine import descent
>>> descent(3, 1) # solves the equation w**2 = 3*Y**2 + Z**2
(1, 0, 1)
Here the solution tuple is in the order (w, Y, Z)
The extended Pythagorean equation, (a_{1}x_{1}² + a_{2}x_{2}² + \ldots + a_{n}x_{n}² = a_{n+1}x_{n+1}²) and the general sum of squares equation, (x_{1}² + x_{2}² + \ldots + x_{n}² = k) can also be solved using the Diophantine module.
>>> from sympy.abc import a, b, c, d, e, f
>>> diophantine(9*a**2 + 16*b**2 + c**2 + 49*d**2 + 4*e**2 - 25*f**2)
{(70*t1**2 + 70*t2**2 + 70*t3**2 + 70*t4**2 - 70*t5**2, 105*t1*t5, 420*t2*t5, 60*t3*t5, 210*t4*t5, 42*t1**2 + 42*t2**2 + 42*t3**2 + 42*t4**2 + 42*t5**2)}
function diop_general_pythagorean() can also be called directly to solve the same equation. Either you can call diop_general_pythagorean() or use the high level API. For the general sum of squares, this is also true, but one advantage of calling diop_general_sum_of_squares() is that you can control how many solutions are returned.
>>> from sympy.solvers.diophantine.diophantine import diop_general_sum_of_squares
>>> eq = a**2 + b**2 + c**2 + d**2 - 18
>>> diophantine(eq)
{(0, 0, 3, 3), (0, 1, 1, 4), (1, 2, 2, 3)}
>>> diop_general_sum_of_squares(eq, 2)
{(0, 0, 3, 3), (1, 2, 2, 3)}
The sum_of_squares() routine will providean iterator that returns solutions and one may control whether the solutions contain zeros or not (and the solutions not containing zeros are returned first):
>>> from sympy.solvers.diophantine.diophantine import sum_of_squares
>>> sos = sum_of_squares(18, 4, zeros=True)
>>> next(sos)
(1, 2, 2, 3)
>>> next(sos)
(0, 0, 3, 3)
Simple Eqyptian fractions can be found with the Diophantine module, too. For example, here are the ways that one might represent 1/2 as a sum of two unit fractions:
>>> from sympy import Eq, S
>>> diophantine(Eq(1/x + 1/y, S(1)/2))
{(-2, 1), (1, -2), (3, 6), (4, 4), (6, 3)}
To get a more thorough understanding of the Diophantine module, please refer to the following blog.
References
User Functions
This functions is imported into the global namespace with from sympy import *:
sympy.solvers.diophantine.diophantine.diophantine(eq, param=t, syms=None, permute=False)
Simplify the solution procedure of diophantine equation eq by converting it into a product of terms which should equal zero.
Explanation
例如,当解决 (x² - y² = 0) 时,这被视为 ((x + y)(x - y) = 0),分别解决 (x + y = 0) 和 (x - y = 0),然后合并。每个术语通过调用 diop_solve() 来解决。(虽然可以直接调用 diop_solve(),但必须小心传递正确形式的方程并正确解释输出;diophantine() 是一般情况下使用的公共函数。)
diophantine() 的输出是一组元组。元组的元素是方程中每个变量的解决方案,并按照变量的字母顺序排列。例如,对于两个变量 (a) 和 (b) 的方程,元组的第一个元素是 (a) 的解,第二个是 (b) 的解。
用法
diophantine(eq, t, syms): 解决丢番图方程 eq。t 是可选的 diop_solve() 使用的参数。syms 是一个可选的符号列表,确定返回元组中元素的顺序。
默认情况下,只返回基本解决方案。如果设置 permute 为 True,则在适用时将返回基本解决方案的排列组合和/或值符号的排列组合。
细节
eq 应为假设为零的表达式。t 是解决方案中使用的参数。
示例
>>> from sympy import diophantine
>>> from sympy.abc import a, b
>>> eq = a**4 + b**4 - (2**4 + 3**4)
>>> diophantine(eq)
{(2, 3)}
>>> diophantine(eq, permute=True)
{(-3, -2), (-3, 2), (-2, -3), (-2, 3), (2, -3), (2, 3), (3, -2), (3, 2)}
>>> from sympy.abc import x, y, z
>>> diophantine(x**2 - y**2)
{(t_0, -t_0), (t_0, t_0)}
>>> diophantine(x*(2*x + 3*y - z))
{(0, n1, n2), (t_0, t_1, 2*t_0 + 3*t_1)}
>>> diophantine(x**2 + 3*x*y + 4*x)
{(0, n1), (-3*t_0 - 4, t_0)}
另请参阅
解丢番图方程, 排列符号, 符号排列
并且此函数通过 from sympy.solvers.diophantine import * 导入:
sympy.solvers.diophantine.diophantine.classify_diop(eq, _dict=True)
内部函数
这些函数旨在用于丢番图模块的内部使用。
sympy.solvers.diophantine.diophantine.diop_solve(eq, param=t)
解决丢番图方程 eq。
解释
与 diophantine() 不同,不尝试对 eq 进行因式分解。使用 classify_diop() 确定方程的类型并调用适当的解算函数。
推荐使用 diophantine() 而不是其他辅助函数。diop_solve() 可以根据方程的性质返回集合或元组。
用法
diop_solve(eq, t): 使用 t 作为参数解决丢番图方程 eq。
细节
eq 应为假设为零的表达式。t 是解决方案中使用的参数。
示例
>>> from sympy.solvers.diophantine import diop_solve
>>> from sympy.abc import x, y, z, w
>>> diop_solve(2*x + 3*y - 5)
(3*t_0 - 5, 5 - 2*t_0)
>>> diop_solve(4*x + 3*y - 4*z + 5)
(t_0, 8*t_0 + 4*t_1 + 5, 7*t_0 + 3*t_1 + 5)
>>> diop_solve(x + 3*y - 4*z + w - 6)
(t_0, t_0 + t_1, 6*t_0 + 5*t_1 + 4*t_2 - 6, 5*t_0 + 4*t_1 + 3*t_2 - 6)
>>> diop_solve(x**2 + y**2 - 5)
{(-2, -1), (-2, 1), (-1, -2), (-1, 2), (1, -2), (1, 2), (2, -1), (2, 1)}
另请参阅
丢番图方程
sympy.solvers.diophantine.diophantine.diop_linear(eq, param=t)
解决线性丢番图方程。
线性丢番图方程是形如 (a_{1}x_{1} + a_{2}x_{2} + .. + a_{n}x_{n} = 0) 的方程,其中 (a_{1}, a_{2}, ..a_{n}) 是整数常数,(x_{1}, x_{2}, ..x_{n}) 是整数变量。
用法
diop_linear(eq): 返回一个包含丢番图方程 eq 的解的元组。元组中的值按排序后的变量顺序排列。
详情
eq 是假设为零的线性丢番图方程。param 是解中要使用的参数。
示例
>>> from sympy.solvers.diophantine.diophantine import diop_linear
>>> from sympy.abc import x, y, z
>>> diop_linear(2*x - 3*y - 5) # solves equation 2*x - 3*y - 5 == 0
(3*t_0 - 5, 2*t_0 - 5)
这里 x = -3t_0 - 5,y = -2t_0 - 5
>>> diop_linear(2*x - 3*y - 4*z -3)
(t_0, 2*t_0 + 4*t_1 + 3, -t_0 - 3*t_1 - 3)
另请参见
diop_quadratic, diop_ternary_quadratic, diop_general_pythagorean, diop_general_sum_of_squares
sympy.solvers.diophantine.diophantine.base_solution_linear(c, a, b, t=None)
返回线性方程 (ax + by = c) 的基础解。
解释
由 diop_linear() 使用以找到线性丢番图方程的基础解。如果给定 t,则返回参数化解。
用法
base_solution_linear(c, a, b, t): a, b, c 是 (ax + by = c) 中的系数,t 是解中要使用的参数。
示例
>>> from sympy.solvers.diophantine.diophantine import base_solution_linear
>>> from sympy.abc import t
>>> base_solution_linear(5, 2, 3) # equation 2*x + 3*y = 5
(-5, 5)
>>> base_solution_linear(0, 5, 7) # equation 5*x + 7*y = 0
(0, 0)
>>> base_solution_linear(5, 2, 3, t) # equation 2*x + 3*y = 5
(3*t - 5, 5 - 2*t)
>>> base_solution_linear(0, 5, 7, t) # equation 5*x + 7*y = 0
(7*t, -5*t)
sympy.solvers.diophantine.diophantine.diop_quadratic(eq, param=t)
解决二次丢番图方程。
即形式为 (Ax² + Bxy + Cy² + Dx + Ey + F = 0) 的方程。返回包含元组 ((x, y)) 的集合,包含解。如果没有解,则返回 ((None, None))。
用法
diop_quadratic(eq, param): eq 是二次二元丢番图方程。param 用于指示解中要使用的参数。
详情
eq 应为假设为零的表达式。param 是解中要使用的参数。
示例
>>> from sympy.abc import x, y, t
>>> from sympy.solvers.diophantine.diophantine import diop_quadratic
>>> diop_quadratic(x**2 + y**2 + 2*x + 2*y + 2, t)
{(-1, -1)}
另请参见
diop_linear, diop_ternary_quadratic, diop_general_sum_of_squares, diop_general_pythagorean
参考
[R858]
解决方程 Ax² + Bxy + Cy² + Dx + Ey + F = 0 的方法,[在线],可访问:www.alpertron.com.ar/METHODS.HTM
[R859]
解决方程 ax²+ bxy + cy² + dx + ey + f= 0,[在线],可访问:web.archive.org/web/20160323033111/http://www.jpr2718.org/ax2p.pdf
sympy.solvers.diophantine.diophantine.diop_DN(D, N, t=t)
解决方程 (x² - Dy² = N)。
解释
主要关注(D > 0)且(D)不是完全平方数的情况,这与广义 Pell 方程相同。LMM 算法[R860]用于解决此方程。
对每个解类返回一个解元组((x, y))。可以根据 D 和 N 的值构造该类的其他解。
用法
diop_DN(D, N, t): D 和 N 是方程(x² - Dy² = N)中的整数,t是解的参数。
详情
D和N对应于方程中的 D 和 N。t是解的参数。
示例
>>> from sympy.solvers.diophantine.diophantine import diop_DN
>>> diop_DN(13, -4) # Solves equation x**2 - 13*y**2 = -4
[(3, 1), (393, 109), (36, 10)]
输出可以解释如下:方程(x² - 13y² = -4)有三个基本解,分别为(3, 1),(393, 109)和(36, 10)。每个元组都是形如(x, y)的形式,即解(3, 1)意味着(x = 3)且(y = 1)。
>>> diop_DN(986, 1) # Solves equation x**2 - 986*y**2 = 1
[(49299, 1570)]
参见
find_DN, diop_bf_DN
参考
[R860] (1,2)
解决广义 Pell 方程(x² - D*y² = N),John P. Robertson,2004 年 7 月 31 日,第 16 - 17 页。[在线],可用:web.archive.org/web/20160323033128/http://www.jpr2718.org/pell.pdf
sympy.solvers.diophantine.diophantine.cornacchia(a: int, b: int, m: int) → set[tuple[int, int]]
解决方程(ax² + by² = m),其中(\gcd(a, b) = 1 = gcd(a, m)),且(a, b > 0)。
说明
使用 Cornacchia 算法。该方法仅找到原始解,即满足(\gcd(x, y) = 1)的解。因此,该方法不能用于寻找方程(x² + y² = 20)的解,因为前者的唯一解为((x, y) = (4, 2)),且不是原始解。当(a = b)时,仅找到满足(x \leq y)的解。更多详细信息,请参见参考资料。
示例
>>> from sympy.solvers.diophantine.diophantine import cornacchia
>>> cornacchia(2, 3, 35) # equation 2x**2 + 3y**2 = 35
{(2, 3), (4, 1)}
>>> cornacchia(1, 1, 25) # equation x**2 + y**2 = 25
{(4, 3)}
参见
sympy.utilities.iterables.signed_permutations
参考
[R861]
- Nitaj,“L’algorithme de Cornacchia”
[R862]
通过 Cornacchia 方法解决二次丢番图方程 ax2 + by2 = m,[在线],可用:www.numbertheory.org/php/cornacchia.html
sympy.solvers.diophantine.diophantine.diop_bf_DN(D, N, t=t)
用暴力方法解决方程(x² - Dy² = N)。
说明
主要关注广义 Pell 方程,即当(D > 0)且(D)不是完全平方数的情况。有关此情况的更多信息,请参考[R863]。设((t, u))是方程(x² - Dy² = 1)的最小正解。然后,此方法要求(\sqrt{\frac{\mid N \mid (t \pm 1)}{2D}})要很小。
用法
diop_bf_DN(D, N, t): D和N是方程(x² - Dy² = N)中的系数,t是解的参数。
详情
D和N对应于方程中的 D 和 N。t是解的参数。
示例
>>> from sympy.solvers.diophantine.diophantine import diop_bf_DN
>>> diop_bf_DN(13, -4)
[(3, 1), (-3, 1), (36, 10)]
>>> diop_bf_DN(986, 1)
[(49299, 1570)]
参见
diop_DN
参考文献
[R863] (1,2)
解决广义 Pell 方程 (x² - D*y² = N),John P. Robertson,2004 年 7 月 31 日,第 15 页。web.archive.org/web/20160323033128/http://www.jpr2718.org/pell.pdf
sympy.solvers.diophantine.diophantine.transformation_to_DN(eq)
此函数将一般二次方程 (ax² + bxy + cy² + dx + ey + f = 0) 转换为更易处理的形式 (X² - DY² = N)。
解释
这用于通过转换将一般二次方程解决为后一种形式。更详细的转换信息请参考[R864]。此函数返回一个元组 (A, B),其中 A 是一个 2 X 2 矩阵,B 是一个 2 X 1 矩阵,使得,
Transpose([x y]) = A * Transpose([X Y]) + B
用法
transformation_to_DN(eq): 其中 eq 是要转换的二次方程。
示例
>>> from sympy.abc import x, y
>>> from sympy.solvers.diophantine.diophantine import transformation_to_DN
>>> A, B = transformation_to_DN(x**2 - 3*x*y - y**2 - 2*y + 1)
>>> A
Matrix([
[1/26, 3/26],
[ 0, 1/13]])
>>> B
Matrix([
[-6/13],
[-4/13]])
返回的 A, B 满足 Transpose((x y)) = A * Transpose((X Y)) + B。将这些值代入 (x) 和 (y) 并进行简化,得到形如 (x² - Dy² = N) 的方程。
>>> from sympy.abc import X, Y
>>> from sympy import Matrix, simplify
>>> u = (A*Matrix([X, Y]) + B)[0] # Transformation for x
>>> u
X/26 + 3*Y/26 - 6/13
>>> v = (A*Matrix([X, Y]) + B)[1] # Transformation for y
>>> v
Y/13 - 4/13
接下来,我们将这些公式代入 (x) 和 (y) 并执行 simplify()。
>>> eq = simplify((x**2 - 3*x*y - y**2 - 2*y + 1).subs(zip((x, y), (u, v))))
>>> eq
X**2/676 - Y**2/52 + 17/13
通过适当地乘以分母,我们可以得到标准形式的 Pell 方程。
>>> eq * 676
X**2 - 13*Y**2 + 884
如果只需要最终方程,则可以使用 find_DN()。
另请参阅
find_DN
参考文献
[R864] (1,2)
解方程 (ax² + bxy + cy² + dx + ey + f = 0),John P.Robertson,2003 年 5 月 8 日,第 7 - 11 页。web.archive.org/web/20160323033111/http://www.jpr2718.org/ax2p.pdf
sympy.solvers.diophantine.diophantine.transformation_to_normal(eq)
返回转换矩阵,将一般三元二次方程 eq ((ax² + by² + cz² + dxy + eyz + fxz)) 转换为没有交叉项的形式:(ax² + by² + cz² = 0)。这不用于解决三元二次方程,仅仅是为了完整性而实现的。
sympy.solvers.diophantine.diophantine.find_DN(eq)
此函数返回简化形式的元组 ((D, N)),即 (x² - Dy² = N),对应于一般二次方程 (ax² + bxy + cy² + dx + ey + f = 0)。
解决一般二次方程等效于解决方程 (X² - DY² = N) 并通过使用 transformation_to_DN() 返回的转换矩阵转换解。
用法
find_DN(eq): 其中 eq 是要转换的二次方程。
示例
>>> from sympy.abc import x, y
>>> from sympy.solvers.diophantine.diophantine import find_DN
>>> find_DN(x**2 - 3*x*y - y**2 - 2*y + 1)
(13, -884)
输出的解释是,我们通过使用 transformation_to_DN() 返回的转换来转换 (x² - 3xy - y² - 2y + 1) ,得到 (X² -13Y² = -884)。
另请参阅
transformation_to_DN
参考文献
[R865]
解决方程 (ax² + bxy + cy² + dx + ey + f = 0),John P.Robertson,2003 年 5 月 8 日,第 7 - 11 页。web.archive.org/web/20160323033111/http://www.jpr2718.org/ax2p.pdf
sympy.solvers.diophantine.diophantine.diop_ternary_quadratic(eq, parameterize=False)
解决一般的二次三元形式,(ax² + by² + cz² + fxy + gyz + hxz = 0)。
返回一个元组 ((x, y, z)),它是上述方程的一个基本解。如果没有解,则返回 ((None, None, None))。
用法
diop_ternary_quadratic(eq): 返回一个包含 eq 的基本解的元组。
详情
eq 应该是三个变量中二次齐次表达式,假定其为零。
示例
>>> from sympy.abc import x, y, z
>>> from sympy.solvers.diophantine.diophantine import diop_ternary_quadratic
>>> diop_ternary_quadratic(x**2 + 3*y**2 - z**2)
(1, 0, 1)
>>> diop_ternary_quadratic(4*x**2 + 5*y**2 - z**2)
(1, 0, 2)
>>> diop_ternary_quadratic(45*x**2 - 7*y**2 - 8*x*y - z**2)
(28, 45, 105)
>>> diop_ternary_quadratic(x**2 - 49*y**2 - z**2 + 13*z*y -8*x*y)
(9, 1, 5)
sympy.solvers.diophantine.diophantine.square_factor(a)
返回整数 (c),使得 (a = c²k, \ c,k \in Z)。这里 (k) 是无平方因子。(a) 可以作为整数或因子字典给出。
示例
>>> from sympy.solvers.diophantine.diophantine import square_factor
>>> square_factor(24)
2
>>> square_factor(-36*3)
6
>>> square_factor(1)
1
>>> square_factor({3: 2, 2: 1, -1: 1}) # -18
3
另见
sympy.ntheory.factor_.core
sympy.solvers.diophantine.diophantine.descent(A, B)
使用拉格朗日下降法和格点约简,返回一个非平凡解((x, y, z)),使得 (x² = Ay² + Bz²)。这里假定 (A) 和 (B) 是使这样的解存在的有效值。
该算法比普通的拉格朗日下降算法更快,因为使用了高斯约简。
示例
>>> from sympy.solvers.diophantine.diophantine import descent
>>> descent(3, 1) # x**2 = 3*y**2 + z**2
(1, 0, 1)
((x, y, z) = (1, 0, 1)) 是上述方程的一个解。
>>> descent(41, -113)
(-16, -3, 1)
参考文献
[R866]
Cremona, J. E., Rusin, D. (2003). Rational Conics 的有效解法。《计算数学》,72(243),1417-1441。doi.org/10.1090/S0025-5718-02-01480-1
sympy.solvers.diophantine.diophantine.diop_general_pythagorean(eq, param=m)
解决一般的勾股方程,(a_{1}²x_{1}² + a_{2}²x_{2}² + . . . + a_{n}²x_{n}² - a_{n + 1}²x_{n + 1}² = 0)。
返回一个包含按输入变量相同顺序排序的参数化解的元组。
用法
diop_general_pythagorean(eq, param):这里 eq 是一个被假定为零的一般勾股方程,param 是用于构造其他参数的基础参数。
示例
>>> from sympy.solvers.diophantine.diophantine import diop_general_pythagorean
>>> from sympy.abc import a, b, c, d, e
>>> diop_general_pythagorean(a**2 + b**2 + c**2 - d**2)
(m1**2 + m2**2 - m3**2, 2*m1*m3, 2*m2*m3, m1**2 + m2**2 + m3**2)
>>> diop_general_pythagorean(9*a**2 - 4*b**2 + 16*c**2 + 25*d**2 + e**2)
(10*m1**2 + 10*m2**2 + 10*m3**2 - 10*m4**2, 15*m1**2 + 15*m2**2 + 15*m3**2 + 15*m4**2, 15*m1*m4, 12*m2*m4, 60*m3*m4)
sympy.solvers.diophantine.diophantine.diop_general_sum_of_squares(eq, limit=1)
解决方程 (x_{1}² + x_{2}² + . . . + x_{n}² - k = 0)。
返回最多limit个解。
用法
general_sum_of_squares(eq, limit):这里eq是一个被假定为零的表达式。同时,eq应该是这种形式,(x_{1}² + x_{2}² + . . . + x_{n}² - k = 0)。
详情
当 (n = 3) 时,如果 (k = 4^a(8m + 7)) 对某些 (a, m \in Z) 成立,则没有解。详见[R867]了解更多细节。
示例
>>> from sympy.solvers.diophantine.diophantine import diop_general_sum_of_squares
>>> from sympy.abc import a, b, c, d, e
>>> diop_general_sum_of_squares(a**2 + b**2 + c**2 + d**2 + e**2 - 2345)
{(15, 22, 22, 24, 24)}
参考文献
[R867]
代表一个整数作为三个平方和的和,[在线],可参考:www.proofwiki.org/wiki/Integer_as_Sum_of_Three_Squares
sympy.solvers.diophantine.diophantine.diop_general_sum_of_even_powers(eq, limit=1)
解决形如 (x_{1}^e + x_{2}^e + . . . + x_{n}^e - k = 0) 的方程,其中 (e) 是偶数整数幂。
返回最多limit个解。
用法
general_sum_of_even_powers(eq, limit):这里 eq 是一个假设为零的表达式。此外,eq 应该是形如 (x_{1}^e + x_{2}^e + . . . + x_{n}^e - k = 0) 的形式。
示例
>>> from sympy.solvers.diophantine.diophantine import diop_general_sum_of_even_powers
>>> from sympy.abc import a, b
>>> diop_general_sum_of_even_powers(a**4 + b**4 - (2**4 + 3**4))
{(2, 3)}
另请参阅
power_representation
sympy.solvers.diophantine.diophantine.power_representation(n, p, k, zeros=False)
返回一个生成器,用于找到整数 k 元组 ((n_{1}, n_{2}, . . . n_{k})),使得 (n = n_{1}^p + n_{2}^p + . . . n_{k}^p)。
用法
power_representation(n, p, k, zeros):将非负数 n 表示为 k 个 p 次幂的和。如果 zeros 为 true,则允许解包含零。
示例
>>> from sympy.solvers.diophantine.diophantine import power_representation
将 1729 表示为两个立方体的和:
>>> f = power_representation(1729, 3, 2)
>>> next(f)
(9, 10)
>>> next(f)
(1, 12)
如果标志 zeros 为 True,则解可能包含具有零的元组;在不含零的解之后生成这些解:
>>> list(power_representation(125, 2, 3, zeros=True))
[(5, 6, 8), (3, 4, 10), (0, 5, 10), (0, 2, 11)]
对于偶数 (p),可以使用 (permute_sign) 函数来获取所有带符号的值:
>>> from sympy.utilities.iterables import permute_signs
>>> list(permute_signs((1, 12)))
[(1, 12), (-1, 12), (1, -12), (-1, -12)]
还可以获取所有可能的带符号排列:
>>> from sympy.utilities.iterables import signed_permutations
>>> list(signed_permutations((1, 12)))
[(1, 12), (-1, 12), (1, -12), (-1, -12), (12, 1), (-12, 1), (12, -1), (-12, -1)]
sympy.solvers.diophantine.diophantine.partition(n, k=None, zeros=False)
返回一个生成器,用于生成整数 (n) 的分区。
解释
(n) 的一个分区是一组加起来等于 (n) 的正整数。例如,3 的分区有 3,1 + 2,1 + 1 + 1。分区以元组的形式返回。如果 k 等于 None,则返回所有可能的分区,而不考虑其大小;否则只返回大小为 k 的分区。如果将 zero 参数设置为 True,则在每个小于 k 大小的分区末尾添加适当数量的零。
zero 参数仅在 k 不为 None 时考虑。当分区结束时,最后的 (next()) 调用将引发 StopIteration 异常,因此应始终在 try-except 块中使用此函数。
细节
partition(n, k):这里 n 是一个正整数,k 是分区的大小,也是正整数。
示例
>>> from sympy.solvers.diophantine.diophantine import partition
>>> f = partition(5)
>>> next(f)
(1, 1, 1, 1, 1)
>>> next(f)
(1, 1, 1, 2)
>>> g = partition(5, 3)
>>> next(g)
(1, 1, 3)
>>> next(g)
(1, 2, 2)
>>> g = partition(5, 3, zeros=True)
>>> next(g)
(0, 0, 5)
sympy.solvers.diophantine.diophantine.sum_of_three_squares(n)
返回一个 3-元组 ((a, b, c)),使得 (a² + b² + c² = n) 并且 (a, b, c \geq 0)。
如果 (n = 4^a(8m + 7)) 对于某些 (a, m \in \mathbb{Z}),则返回 None。详细信息请参见 [R868]。
参数:
n :整数
非负整数
返回:
(int, int, int) | None :满足 a**2 + b**2 + c**2 = n 的三元组非负整数 (a, b, c)。
a、b、c 按升序排列。如果没有这样的
(a,b,c),则返回None。
抛出:
数值错误
如果
n是负整数
示例
>>> from sympy.solvers.diophantine.diophantine import sum_of_three_squares
>>> sum_of_three_squares(44542)
(18, 37, 207)
另请参阅
power_representation
sum_of_three_squares(n) 是 power_representation(n, 2, 3, zeros=True) 输出的解之一。
参考
[R868] (1,2)
将一个数表示为三个平方的和,[在线],可用:schorn.ch/lagrange.html
sympy.solvers.diophantine.diophantine.sum_of_four_squares(n)
返回一个 4-元组 ((a, b, c, d)),使得 (a² + b² + c² + d² = n)。这里 (a, b, c, d \geq 0)。
参数:
n :整数
非负整数
返回:
(int, int, int, int):满足 a**2 + b**2 + c**2 + d**2 = n 的 4-元组非负整数 (a, b, c, d)。
a、b、c、d 按升序排列。
引发:
数值错误
如果
n是负整数
示例
>>> from sympy.solvers.diophantine.diophantine import sum_of_four_squares
>>> sum_of_four_squares(3456)
(8, 8, 32, 48)
>>> sum_of_four_squares(1294585930293)
(0, 1234, 2161, 1137796)
参见
power_representation
sum_of_four_squares(n) 是由 power_representation(n, 2, 4, zeros=True) 输出的解之一
参考文献
[R869]
表示一个数作为四个平方的和,[在线],可访问:schorn.ch/lagrange.html
sympy.solvers.diophantine.diophantine.sum_of_powers(n, p, k, zeros=False)
返回一个生成器,用于查找 k-元组整数 ((n_{1}, n_{2}, . . . n_{k})),使得 (n = n_{1}^p + n_{2}^p + . . . n_{k}^p)。
用法
power_representation(n, p, k, zeros): 将非负数 n 表示为 k 个 p 次幂的和。如果 zeros 为真,则允许解包含零。
示例
>>> from sympy.solvers.diophantine.diophantine import power_representation
将 1729 表示为两个立方体的和:
>>> f = power_representation(1729, 3, 2)
>>> next(f)
(9, 10)
>>> next(f)
(1, 12)
如果标志 (zeros) 为 True,则解决方案可能包含带有零的元组;任何此类解决方案将在不带零的解决方案之后生成:
>>> list(power_representation(125, 2, 3, zeros=True))
[(5, 6, 8), (3, 4, 10), (0, 5, 10), (0, 2, 11)]
对于偶数 ( p ),可以使用 ( permute_sign ) 函数来获取所有符号化的值:
>>> from sympy.utilities.iterables import permute_signs
>>> list(permute_signs((1, 12)))
[(1, 12), (-1, 12), (1, -12), (-1, -12)]
也可以获得所有可能的符号化排列:
>>> from sympy.utilities.iterables import signed_permutations
>>> list(signed_permutations((1, 12)))
[(1, 12), (-1, 12), (1, -12), (-1, -12), (12, 1), (-12, 1), (12, -1), (-12, -1)]
sympy.solvers.diophantine.diophantine.sum_of_squares(n, k, zeros=False)
返回生成器,产生非负值的 k-元组,其平方和为 n。如果 zeros 为 False(默认值),则解决方案不包含零。元组的非负元素已排序。
-
如果 k == 1 并且 n 是平方数,则返回 (n,)。
-
如果 k == 2,则仅当 n 在其因数分解中的每个形式为 4k + 3 的素数具有偶重数时,n 才能被写成两个平方的和。如果 n 是素数,则仅当它为 4k + 1 形式时,才能将其写成两个平方的和。
-
如果 k == 3,则只要 n 不具有 4**m(8k + 7) 的形式,n 就可以被写成平方的和。
-
所有整数都可以写成 4 个平方的和。
-
如果 k > 4,则 n 可以被分区,并且每个分区可以被写成 4 个平方的和;如果 n 不是 4 的倍数,则仅当可以将附加分区写成平方的和时,n 才能被写成平方的和。例如,如果 k = 6,则 n 被分成两部分,第一部分被写成 4 个平方的和,第二部分被写成 2 个平方的和——只有当满足 k = 2 的条件时才能做到这一点,因此这将自动拒绝某些 n 的分区。
示例
>>> from sympy.solvers.diophantine.diophantine import sum_of_squares
>>> list(sum_of_squares(25, 2))
[(3, 4)]
>>> list(sum_of_squares(25, 2, True))
[(3, 4), (0, 5)]
>>> list(sum_of_squares(25, 4))
[(1, 2, 2, 4)]
参见
sympy.utilities.iterables.signed_permutations
sympy.solvers.diophantine.diophantine.merge_solution(var, var_t, solution)
这用于从子方程的解构建完整的解决方案。
解释
例如,当解方程 ((x - y)(x² + y² - z²) = 0) 时,可以分别独立找到方程 (x - y = 0) 和 (x² + y² - z²) 的解。 (x - y = 0) 的解为 ((x, y) = (t, t))。但是在输出原始方程的解时,应为 (x - y = 0) 的解引入一个值为 z 的参数。此函数将 ((t, t)) 转换为 ((t, t, n_{1})),其中 (n_{1}) 是整数参数。
sympy.solvers.diophantine.diophantine.divisible(a, b)
如果 a 能被 b 整除则返回 (True),否则返回 (False)。
sympy.solvers.diophantine.diophantine.PQa(P_0, Q_0, D)
返回解决 Pell 方程所需的有用信息。
Explanation
与 (\frac{P + \sqrt{D}}{Q}) 的连分数表示相关的六个整数序列被定义,即 {(P_{i})}, {(Q_{i})}, {(a_{i})},{(A_{i})}, {(B_{i})}, {(G_{i})}。PQa() 以与上述顺序相同的 6 元组返回这些值。更详细的信息请参见 [R870]。
使用
PQa(P_0, Q_0, D): P_0, Q_0 和 D 是整数,分别对应于连分数 (\frac{P_{0} + \sqrt{D}}{Q_{0}}) 中的 (P_{0}), (Q_{0}) 和 (D)。同时假设 (P_{0}² == D mod(|Q_{0}|)),且 (D) 是无平方因子。
Examples
>>> from sympy.solvers.diophantine.diophantine import PQa
>>> pqa = PQa(13, 4, 5) # (13 + sqrt(5))/4
>>> next(pqa) # (P_0, Q_0, a_0, A_0, B_0, G_0)
(13, 4, 3, 3, 1, -1)
>>> next(pqa) # (P_1, Q_1, a_1, A_1, B_1, G_1)
(-1, 1, 1, 4, 1, 3)
References
[R870] (1,2)
解决广义 Pell 方程 (x² - Dy² = N),John P. Robertson, 2004 年 7 月 31 日,第 4 - 8 页。web.archive.org/web/20160323033128/http://www.jpr2718.org/pell.pdf
sympy.solvers.diophantine.diophantine.equivalent(u, v, r, s, D, N)
如果两个解 ((u, v)) 和 ((r, s)) 满足 (x² - Dy² = N) 并属于同一等价类,则返回 True,否则返回 False。
Explanation
对于上述方程的两个解 ((u, v)) 和 ((r, s)),它们属于相同等价类当且仅当 ((ur - Dvs)) 和 ((us - vr)) 都能被 (N) 整除。参见引用 [R871]。不会对是否 ((u, v)) 和 ((r, s)) 实际上是方程的解进行测试。用户应当注意这一点。
Usage
equivalent(u, v, r, s, D, N): ((u, v)) 和 ((r, s)) 是方程 (x² - Dy² = N) 的两个解,涉及的所有参数都是整数。
Examples
>>> from sympy.solvers.diophantine.diophantine import equivalent
>>> equivalent(18, 5, -18, -5, 13, -1)
True
>>> equivalent(3, 1, -18, 393, 109, -4)
False
References
[R871] (1,2)
解决广义 Pell 方程 (x² - D*y² = N),John P. Robertson, 2004 年 7 月 31 日,第 12 页。web.archive.org/web/20160323033128/http://www.jpr2718.org/pell.pdf
sympy.solvers.diophantine.diophantine.parametrize_ternary_quadratic(eq)
返回三元二次方程 eq 的参数化一般解,其形式为 (ax² + by² + cz² + fxy + gyz + hxz = 0)。
Examples
>>> from sympy import Tuple, ordered
>>> from sympy.abc import x, y, z
>>> from sympy.solvers.diophantine.diophantine import parametrize_ternary_quadratic
可能会返回参数化解与三个参数:
>>> parametrize_ternary_quadratic(2*x**2 + y**2 - 2*z**2)
(p**2 - 2*q**2, -2*p**2 + 4*p*q - 4*p*r - 4*q**2, p**2 - 4*p*q + 2*q**2 - 4*q*r)
也可能只有两个参数:
>>> parametrize_ternary_quadratic(4*x**2 + 2*y**2 - 3*z**2)
(2*p**2 - 3*q**2, -4*p**2 + 12*p*q - 6*q**2, 4*p**2 - 8*p*q + 6*q**2)
Notes
考虑前两个参数解中的 p 和 q,观察到给定一对参数可以表示多个解。如果 p 和 q 不是互质的,这是显而易见的,因为共同因子也将是解值的公因子。但是即使 p 和 q 是互质的,这也可能是真的:
>>> sol = Tuple(*_)
>>> p, q = ordered(sol.free_symbols)
>>> sol.subs([(p, 3), (q, 2)])
(6, 12, 12)
>>> sol.subs([(q, 1), (p, 1)])
(-1, 2, 2)
>>> sol.subs([(q, 0), (p, 1)])
(2, -4, 4)
>>> sol.subs([(q, 1), (p, 0)])
(-3, -6, 6)
除了符号和一个公共因子外,这些等同于解(1, 2, 2)。
参考文献
[R872]
Diophantine 方程的算法解决,Nigel P. Smart,伦敦数学学会学生文集 41,剑桥大学出版社,剑桥,1998 年。
sympy.solvers.diophantine.diophantine.diop_ternary_quadratic_normal(eq, parameterize=False)
解二次三元丢番图方程 (ax² + by² + cz² = 0)。
解释
这里的系数 (a)、(b) 和 (c) 应为非零。否则方程将成为二次二进制或单变量方程。如果可解,返回满足给定方程的元组 ((x, y, z))。如果方程没有整数解,则返回 ((None, None, None))。
使用方法
diop_ternary_quadratic_normal(eq): 其中eq是形如 (ax² + by² + cz² = 0) 的方程。
示例
>>> from sympy.abc import x, y, z
>>> from sympy.solvers.diophantine.diophantine import diop_ternary_quadratic_normal
>>> diop_ternary_quadratic_normal(x**2 + 3*y**2 - z**2)
(1, 0, 1)
>>> diop_ternary_quadratic_normal(4*x**2 + 5*y**2 - z**2)
(1, 0, 2)
>>> diop_ternary_quadratic_normal(34*x**2 - 3*y**2 - 301*z**2)
(4, 9, 1)
sympy.solvers.diophantine.diophantine.ldescent(A, B)
使用拉格朗日方法返回 (w² = Ax² + By²) 的非平凡解;如果没有这样的解,则返回 None。
参数:
A : 整数
B : 整数
非零整数
返回:
(int, int, int) | None : 一个元组 ((w_0, x_0, y_0)),它是上述方程的解。
示例
>>> from sympy.solvers.diophantine.diophantine import ldescent
>>> ldescent(1, 1) # w² = x² + y²
(1, 1, 0)
>>> ldescent(4, -7) # w² = 4x² - 7y²
(2, -1, 0)
这意味着 (x = -1, y = 0) 和 (w = 2) 是方程 (w² = 4x² - 7y²) 的解。
>>> ldescent(5, -1) # w² = 5x² - y²
(2, 1, -1)
参考资料
[R873]
Diophantine 方程的算法解决,Nigel P. Smart,伦敦数学学会学生文集 41,剑桥大学出版社,剑桥,1998 年。
[R874]
Cremona, J. E., Rusin, D. (2003). Rational Conics 的高效解法。计算数学,72(243),1417-1441。doi.org/10.1090/S0025-5718-02-01480-1
sympy.solvers.diophantine.diophantine.gaussian_reduce(w: int, a: int, b: int) → tuple[int, int]
返回对应模 (b) 的同余方程 (X² - aZ² \equiv 0 \pmod{b}) 的简化解 ((x, z)),使得 (x² + |a|z²) 尽可能小。这里 w 是满足同余方程 (x² \equiv a \pmod{b}) 的解。
此函数仅用于 descent()。
参数:
w : 整数
w满足 (w² \equiv a \pmod{b})
a : 整数
无平方因子的非零整数
b : 整数
无平方因子的非零整数
解释
高斯约简可以找到任何范数的最短向量。因此,我们对向量 (u = (u_1, u_2)) 和 (v = (v_1, v_2)) 定义特殊的范数如下。
[u \cdot v := (wu_1 + bu_2)(wv_1 + bv_2) + |a|u_1v_1]
注意,给定映射 (f: (u_1, u_2) \to (wu_1 + bu_2, u_1)),(f((u_1,u_2))) 是方程 (X² - aZ² \equiv 0 \pmod{b}) 的解。换句话说,找到这个范数中的最短向量将给出一个 (X² + |a|Z²) 较小的解。该算法从基础向量 ((0, 1)) 和 ((1, 0)) 开始(分别对应解 ((b, 0)) 和 ((w, 1))),并找到最短向量。最短向量不一定对应最小的解,但由于 descent() 只需最小可能的解,这已经足够。
示例
>>> from sympy.solvers.diophantine.diophantine import gaussian_reduce
>>> from sympy.ntheory.residue_ntheory import sqrt_mod
>>> a, b = 19, 101
>>> gaussian_reduce(sqrt_mod(a, b), a, b) # 1**2 - 19*(-4)**2 = -303
(1, -4)
>>> a, b = 11, 14
>>> x, z = gaussian_reduce(sqrt_mod(a, b), a, b)
>>> (x**2 - a*z**2) % b == 0
True
它并不总是返回最小的解。
>>> a, b = 6, 95
>>> min_x, min_z = 1, 4
>>> x, z = gaussian_reduce(sqrt_mod(a, b), a, b)
>>> (x**2 - a*z**2) % b == 0 and (min_x**2 - a*min_z**2) % b == 0
True
>>> min_x**2 + abs(a)*min_z**2 < x**2 + abs(a)*z**2
True
参考
[R875]
高斯格点缩减 [在线]. 可访问: web.archive.org/web/20201021115213/http://home.ie.cuhk.edu.hk/~wkshum/wordpress/?p=404
[R876]
Cremona, J. E., Rusin, D. (2003). 有效解决有理圆锥曲线问题. 计算数学, 72(243), 1417-1441. doi.org/10.1090/S0025-5718-02-01480-1
sympy.solvers.diophantine.diophantine.holzer(x, y, z, a, b, c)
简化方程 (ax² + by² = cz²) 的解 ((x, y, z)),其中 (a, b, c > 0) 且 (z² \geq \mid ab \mid),至一个新的减少解 ((x', y', z')),使得 (z'² \leq \mid ab \mid)。
该算法是 Mordell 的缩减的一种解释,如 Cremona 和 Rusin 论文第 8 页所述 [R877],以及 Mordell 在参考文献 [R878] 中的工作。
参考文献
[R877] (1,2)
Cremona, J. E., Rusin, D. (2003). 有效解决有理圆锥曲线问题. 计算数学, 72(243), 1417-1441. doi.org/10.1090/S0025-5718-02-01480-1
[R878] (1,2)
丢番图方程, L. J. Mordell, 第 48 页.
sympy.solvers.diophantine.diophantine.prime_as_sum_of_two_squares(p)
将素数 (p) 表示为两个平方数之和;仅当素数满足 (p \equiv 1 \pmod{4}) 时可行。
参数:
p : 整数
一个满足 (p \equiv 1 \pmod{4}) 的素数
返回:
(int, int) | None : 正整数对 (x, y) 满足 x**2 + y**2 = p。
若
p不满足 (p \equiv 1 \pmod{4}),则返回 None。
抛出:
ValueError
若
p不是素数
示例
>>> from sympy.solvers.diophantine.diophantine import prime_as_sum_of_two_squares
>>> prime_as_sum_of_two_squares(7) # can't be done
>>> prime_as_sum_of_two_squares(5)
(1, 2)
参考
[R879]
将一个数表示为四个平方数之和 [在线]. 可访问: schorn.ch/lagrange.html
参见
sum_of_squares
sympy.solvers.diophantine.diophantine.sqf_normal(a, b, c, steps=False)
返回 (a', b', c'),即 (ax² + by² + cz² = 0) 的平方自由正则形式的系数,其中 (a', b', c') 是两两素数。如果 steps 为真,则还返回三个元组:(sq),(sqf) 和 ((a', b', c')),在去除 (gcd(a, b, c)) 后包含 (a),(b) 和 (c) 的平方因子。
对于 (ax² + by² + cz² = 0) 的解可以从 (a'x² + b'y² + c'z² = 0) 的解恢复。
示例
>>> from sympy.solvers.diophantine.diophantine import sqf_normal
>>> sqf_normal(2 * 3**2 * 5, 2 * 5 * 11, 2 * 7**2 * 11)
(11, 1, 5)
>>> sqf_normal(2 * 3**2 * 5, 2 * 5 * 11, 2 * 7**2 * 11, True)
((3, 1, 7), (5, 55, 11), (11, 1, 5))
参见
reconstruct
参考
[R880]
Legendre 定理,Lagrange 下降,public.csusm.edu/aitken_html/notes/legendre.pdf
sympy.solvers.diophantine.diophantine.reconstruct(A, B, z)
从等式的平方自由正常形式 (a'x² + b'y² + c'*z²) 的解的 (z) 值重建等价解的 (z) 值,其中 (a'), (b') 和 (c') 是平方自由的,且 (gcd(a', b', c') == 1)。
内部类
这些类旨在供丢番图模块内部使用。
class sympy.solvers.diophantine.diophantine.DiophantineSolutionSet(symbols_seq, parameters)
一特定丢番图方程的解集的容器。
基本表示是一组元组,代表每个解的解决方案。
参数:
symbols : 列表
原方程中的自由符号列表。
parameters: list
用于解决方案的参数列表。
示例
添加解决方案:
>>> from sympy.solvers.diophantine.diophantine import DiophantineSolutionSet
>>> from sympy.abc import x, y, t, u
>>> s1 = DiophantineSolutionSet([x, y], [t, u])
>>> s1
set()
>>> s1.add((2, 3))
>>> s1.add((-1, u))
>>> s1
{(-1, u), (2, 3)}
>>> s2 = DiophantineSolutionSet([x, y], [t, u])
>>> s2.add((3, 4))
>>> s1.update(*s2)
>>> s1
{(-1, u), (2, 3), (3, 4)}
将解转换为字典:
>>> list(s1.dict_iterator())
[{x: -1, y: u}, {x: 2, y: 3}, {x: 3, y: 4}]
替换数值:
>>> s3 = DiophantineSolutionSet([x, y], [t, u])
>>> s3.add((t**2, t + u))
>>> s3
{(t**2, t + u)}
>>> s3.subs({t: 2, u: 3})
{(4, 5)}
>>> s3.subs(t, -1)
{(1, u - 1)}
>>> s3.subs(t, 3)
{(9, u + 3)}
特定值的评估。位置参数与参数顺序相同:
>>> s3(-2, 3)
{(4, 1)}
>>> s3(5)
{(25, u + 5)}
>>> s3(None, 2)
{(t**2, t + 2)}
class sympy.solvers.diophantine.diophantine.DiophantineEquationType(equation, free_symbols=None)
特定丢番图方程类型的内部表示。
参数:
equation :
正在解决的丢番图方程。
free_symbols : 列表 (可选)
正在解决的符号。
属性
| total_degree : | 方程中所有项的最大度数 |
|---|---|
| homogeneous : | 方程是否包含度为 0 的项 |
| homogeneous_order : | 方程中是否包含任何符号的系数 |
| dimension : | 正在解决的符号数量 |
matches()
确定给定方程是否可以与特定方程类型匹配。
class sympy.solvers.diophantine.diophantine.Univariate(equation, free_symbols=None)
一元二次丢番图方程的表示。
一元二次丢番图方程是一个形如 (a_{0} + a_{1}x + a_{2}x² + .. + a_{n}x^n = 0) 的方程,其中 (a_{1}, a_{2}, ..a_{n}) 是整数常数,(x) 是整数变量。
示例
>>> from sympy.solvers.diophantine.diophantine import Univariate
>>> from sympy.abc import x
>>> Univariate((x - 2)*(x - 3)**2).solve() # solves equation (x - 2)*(x - 3)**2 == 0
{(2,), (3,)}
class sympy.solvers.diophantine.diophantine.Linear(equation, free_symbols=None)
一元二次丢番图方程的表示。
线性丢番图方程是一个形如 (a_{1}x_{1} + a_{2}x_{2} + .. + a_{n}x_{n} = 0) 的方程,其中 (a_{1}, a_{2}, ..a_{n}) 是整数常数,(x_{1}, x_{2}, ..x_{n}) 是整数变量。
示例
>>> from sympy.solvers.diophantine.diophantine import Linear
>>> from sympy.abc import x, y, z
>>> l1 = Linear(2*x - 3*y - 5)
>>> l1.matches() # is this equation linear
True
>>> l1.solve() # solves equation 2*x - 3*y - 5 == 0
{(3*t_0 - 5, 2*t_0 - 5)}
这里 x = -3t_0 - 5 和 y = -2t_0 - 5
>>> Linear(2*x - 3*y - 4*z -3).solve()
{(t_0, 2*t_0 + 4*t_1 + 3, -t_0 - 3*t_1 - 3)}
class sympy.solvers.diophantine.diophantine.BinaryQuadratic(equation, free_symbols=None)
二元二次丢番图方程的表示。
二元二次丢番图方程是一个形如 (Ax² + Bxy + Cy² + Dx + Ey + F = 0) 的方程,其中 (A, B, C, D, E, F) 是整数常数,(x) 和 (y) 是整数变量。
示例
>>> from sympy.abc import x, y
>>> from sympy.solvers.diophantine.diophantine import BinaryQuadratic
>>> b1 = BinaryQuadratic(x**3 + y**2 + 1)
>>> b1.matches()
False
>>> b2 = BinaryQuadratic(x**2 + y**2 + 2*x + 2*y + 2)
>>> b2.matches()
True
>>> b2.solve()
{(-1, -1)}
参考
[R881]
解决 Ax² + Bxy + Cy² + Dx + Ey + F = 0 的方法,[在线],可用:www.alpertron.com.ar/METHODS.HTM
[R882]
解方程(ax²+ bxy + cy² + dx + ey + f= 0),[在线],可参见:web.archive.org/web/20160323033111/http://www.jpr2718.org/ax2p.pdf
class sympy.solvers.diophantine.diophantine.InhomogeneousTernaryQuadratic(equation, free_symbols=None)
表示非均匀三元二次方程。
目前还没有为这种方程类型实现求解器。
class sympy.solvers.diophantine.diophantine.HomogeneousTernaryQuadraticNormal(equation, free_symbols=None)
表示均匀三元二次正规丢番图方程。
示例
>>> from sympy.abc import x, y, z
>>> from sympy.solvers.diophantine.diophantine import HomogeneousTernaryQuadraticNormal
>>> HomogeneousTernaryQuadraticNormal(4*x**2 - 5*y**2 + z**2).solve()
{(1, 2, 4)}
class sympy.solvers.diophantine.diophantine.HomogeneousTernaryQuadratic(equation, free_symbols=None)
表示均匀三元二次丢番图方程。
示例
>>> from sympy.abc import x, y, z
>>> from sympy.solvers.diophantine.diophantine import HomogeneousTernaryQuadratic
>>> HomogeneousTernaryQuadratic(x**2 + y**2 - 3*z**2 + x*y).solve()
{(-1, 2, 1)}
>>> HomogeneousTernaryQuadratic(3*x**2 + y**2 - 3*z**2 + 5*x*y + y*z).solve()
{(3, 12, 13)}
class sympy.solvers.diophantine.diophantine.InhomogeneousGeneralQuadratic(equation, free_symbols=None)
表示非均匀一般二次方程。
目前还没有为这种方程类型实现求解器。
class sympy.solvers.diophantine.diophantine.HomogeneousGeneralQuadratic(equation, free_symbols=None)
表示均匀一般二次方程。
目前还没有为这种方程类型实现求解器。
class sympy.solvers.diophantine.diophantine.GeneralSumOfSquares(equation, free_symbols=None)
表示丢番图方程
(x_{1}² + x_{2}² + . . . + x_{n}² - k = 0).
详情
当(n = 3)时,如果(k = 4^a(8m + 7)),其中(a, m \in Z),则没有解。参考[R883]获取更多详情。
示例
>>> from sympy.solvers.diophantine.diophantine import GeneralSumOfSquares
>>> from sympy.abc import a, b, c, d, e
>>> GeneralSumOfSquares(a**2 + b**2 + c**2 + d**2 + e**2 - 2345).solve()
{(15, 22, 22, 24, 24)}
默认情况下仅返回 1 个解。使用关键字(limit)可获取更多:
>>> sorted(GeneralSumOfSquares(a**2 + b**2 + c**2 + d**2 + e**2 - 2345).solve(limit=3))
[(15, 22, 22, 24, 24), (16, 19, 24, 24, 24), (16, 20, 22, 23, 26)]
参考文献
[R883] (1,2)
将整数表示为三个平方数的和,[在线],可参见:www.proofwiki.org/wiki/Integer_as_Sum_of_Three_Squares
class sympy.solvers.diophantine.diophantine.GeneralPythagorean(equation, free_symbols=None)
表示一般的毕达哥拉斯方程,(a_{1}²x_{1}² + a_{2}²x_{2}² + . . . + a_{n}²x_{n}² - a_{n + 1}²x_{n + 1}² = 0)。
示例
>>> from sympy.solvers.diophantine.diophantine import GeneralPythagorean
>>> from sympy.abc import a, b, c, d, e, x, y, z, t
>>> GeneralPythagorean(a**2 + b**2 + c**2 - d**2).solve()
{(t_0**2 + t_1**2 - t_2**2, 2*t_0*t_2, 2*t_1*t_2, t_0**2 + t_1**2 + t_2**2)}
>>> GeneralPythagorean(9*a**2 - 4*b**2 + 16*c**2 + 25*d**2 + e**2).solve(parameters=[x, y, z, t])
{(-10*t**2 + 10*x**2 + 10*y**2 + 10*z**2, 15*t**2 + 15*x**2 + 15*y**2 + 15*z**2, 15*t*x, 12*t*y, 60*t*z)}
class sympy.solvers.diophantine.diophantine.CubicThue(equation, free_symbols=None)
表示一个三次丢番图方程。
一个三次丢番图方程是一个形式为(f(x, y) = r)的三次多项式,其中(x)和(y)是整数,(r)是有理数。
目前还没有为这种方程类型实现求解器。
示例
>>> from sympy.abc import x, y
>>> from sympy.solvers.diophantine.diophantine import CubicThue
>>> c1 = CubicThue(x**3 + y**2 + 1)
>>> c1.matches()
True
class sympy.solvers.diophantine.diophantine.GeneralSumOfEvenPowers(equation, free_symbols=None)
表示丢番图方程
(x_{1}^e + x_{2}^e + . . . + x_{n}^e - k = 0)
其中(e)是一个偶数整数幂。
示例
>>> from sympy.solvers.diophantine.diophantine import GeneralSumOfEvenPowers
>>> from sympy.abc import a, b
>>> GeneralSumOfEvenPowers(a**4 + b**4 - (2**4 + 3**4)).solve()
{(2, 3)}


浙公网安备 33010602011771号