SymPy-1-13-中文文档-八-

SymPy 1.13 中文文档(八)

原文:docs.sympy.org/latest/index.html

形式幂级数

原文:docs.sympy.org/latest/modules/series/formal.html

计算和操作形式幂级数的方法。

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 : 数字,可选

指定应截断多项式的项的顺序。

解释

如果 fg 是两个不同函数的形式幂级数,则组合形式幂级数 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 : 数字,可选

指定应截断多项式的项的顺序。

解释

如果 fg 是两个不同函数的形式幂级数,则组合形式幂级数 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 = x0f 的形式级数展开,关于 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_algorithmsympy.series.formal.hyper_algorithm

class sympy.series.formal.FormalPowerSeriesCompose(*args)

表示两个函数的组合形式幂级数。

解释

不执行计算。项按逐项逻辑计算,而不是点对点逻辑。

FormalPowerSeries对象与FormalPowerSeriesCompose对象之间有两个区别。第一个参数包含组合中涉及的外部函数和内部函数。此外,系数序列包含将与自定义bell_seq有限序列相乘的通用序列。然后将有限项相加以获得最终项。

参见

sympy.series.formal.FormalPowerSeriessympy.series.formal.FiniteFormalPowerSeries

property function

组合形式幂级数的函数。

class sympy.series.formal.FormalPowerSeriesInverse(*args)

表示形式幂级数的逆。

解释

不执行计算。项按逐项逻辑计算,而不是点对点逻辑。

FormalPowerSeries 对象与 FormalPowerSeriesInverse 对象之间有一个区别。系数序列包含一个通用序列,将乘以一个自定义的 bell_seq 有限序列。然后将添加有限项以得到最终项。

另见

sympy.series.formal.FormalPowerSeriessympy.series.formal.FiniteFormalPowerSeries

property function

形式幂级数的反函数。

class sympy.series.formal.FormalPowerSeriesProduct(*args)

表示两个函数的形式幂级数的乘积。

解释

不进行计算。使用逐项逻辑计算项,而不是点对点逻辑。

FormalPowerSeries 对象与 FormalPowerSeriesProduct 对象之间有两个区别。第一个参数包含参与乘积的两个函数。此外,系数序列包含涉及函数的形式幂级数的系数序列。

另见

sympy.series.formal.FormalPowerSeriessympy.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)

保持超几何类型的变换:

  1. x**nf(x): b(k + m) = R(k - n)b(k)
  2. f(Ax): b(k + m) = A**mR(k)*b(k)
  3. f(x**n): b(k + nm) = R(k/n)b(k)
  4. f(x**(1/m)): b(k + 1) = R(km)b(k)
  5. 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

序列的极限

原文:docs.sympy.org/latest/modules/series/limitseq.html

提供计算具有无穷大序列的项的极限的方法。

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

简化

原文:docs.sympy.org/latest/modules/simplify/index.html

  • 简化

  • 超几何展开

  • 傅宏光的三角简化

简化

原文:docs.sympy.org/latest/modules/simplify/simplify.html

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=1simplify的输出不能比输入更长。

>>> 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度量标准时,hg更简单。然而,我们可能不喜欢简化(在这种情况下,使用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()),简化将返回 TrueFalse,如果可以确定其真值。如果表达式默认不被评估(例如 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 特殊函数等效表示的函数和整数序列组成。

解释

该算法执行三个基本步骤:

  1. 尽可能用 gamma 重写所有函数。

  2. 用整数、绝对常数指数的 gamma 的乘积重写所有 gamma 的出现。

  3. 对于嵌套分数、幂的简化操作,如果结果表达式是多项式的商,则减少它们的总次数。

如果 f(k) 是超几何的,则结果是最小次数的多项式商。否则返回 None。

有关实现算法的更多信息,请参考:

  1. W. Koepf,《m 倍超几何求和的算法》,符号计算杂志(1995)20,399-417
sympy.simplify.simplify.hypersimilar(f, g, k)

如果 fg 是超相似的,则返回 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)

如果 forceTrue,则假设以上假设将被认为在数量上不存在任何假设的情况下成立。例如,如果 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有理化;numden是其平方是正有理数的项的和。

示例

>>> 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生成的素理想归一化有理表达式exprG应为理想的 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) 

如果polynomialFalse,算法计算一个有理化简,该简化最小化了分子和分母的总次数之和。

如果polynomialTrue,该函数只将分子和分母置于标准形式中。这样做速度更快,但结果可能更差。

引用

[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

如果 forceTrue,则会在不检查假设的情况下合并基数,例如 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]

web.archive.org/web/20210806201615/https://researcher.watson.ibm.com/researcher/files/us-fagin/symb85.pdf

[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 设置为 0zoo 表示首选选择。

示例

>>> 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)。

如果 funcNoneepath() 检索由 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) 

超几何展开

原文:docs.sympy.org/latest/modules/simplify/hyperexpand.html

本页面描述了函数 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),以及虚拟变量abcz

添加新公式的第一步是使用add(ap, bq, res)。这声明了hyper(ap, bq, z) == res。在这里,apbq可以使用虚拟变量abc作为自由符号。例如,众所周知的公式(\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。

《洪光·傅的三角简化》

原文链接:docs.sympy.org/latest/modules/simplify/fu.html

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 文档字符串以了解maxpow的高级用法。

例子

>>> 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 文档字符串以了解maxpow的高级用法。

例子

>>> 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)

cossin项的和作为cossin的乘积。

例子

>>> 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)

cossin中分离求和。

例子

>>> 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)

更改tancot的产品。

例子

>>> 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 以了解 maxpow 的高级用法。

示例

>>> 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 以了解 maxpow 的高级用法。

示例

>>> 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 以了解 maxpow 的高级用法。

示例

>>> 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 中扩展的。

参考文献

求解器

原文:docs.sympy.org/latest/modules/solvers/index.html

本模块文档详细介绍了 sympy.solvers 模块的函数。

内容

  • 丢番图方程

  • 不等式求解器

  • 常微分方程

  • 偏微分方程

  • 求解器

  • 解集

丢番图

原文链接:docs.sympy.org/latest/modules/solvers/diophantine.html

注意

对于初学者的指南,重点在解决丢番图方程上,请参阅 代数解丢番图方程。

丢番图方程

“丢番图”一词源自于数学家丢番奴,他大约生活在公元 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.

thilinaatsympy.wordpress.com/

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): 解决丢番图方程 eqt 是可选的 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是解的参数。

详情

DN对应于方程中的 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]

  1. 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): DN是方程(x² - Dy² = N)中的系数,t是解的参数。

详情

DN对应于方程中的 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 表示为 kp 次幂的和。如果 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 表示为 kp 次幂的和。如果 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_0D 是整数,分别对应于连分数 (\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

考虑前两个参数解中的 pq,观察到给定一对参数可以表示多个解。如果 pq 不是互质的,这是显而易见的,因为共同因子也将是解值的公因子。但是即使 pq 是互质的,这也可能是真的:

>>> 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)} 
posted @ 2024-06-27 17:13  绝不原创的飞龙  阅读(196)  评论(0)    收藏  举报