使用 sympy 写公式
sympy.abc
以前定义变量的常用方法
x, y = Symbol('x y')
实际上,在 sympy.abc 中已经定义好了
from sympy.abc import x, y
化简、分解、合成、展开
https://docs.sympy.org/latest/tutorials/intro-tutorial/simplification.html
以下函数,如果没有使用字符串输入公式,则说明此函数不可以使用字符串作参数。
-
simplify()或S()S()是simplify()内置的一种简单写法 -
expand()expand('(x + 2) * (x - 3)')\[\begin{align*} x^{2} - x - 6 \end{align*} \] -
factor()factor('x**3 - x**2 + x - 1')\[\begin{align*} \left(x - 1\right) \left(x^{2} + 1\right) \end{align*} \] -
collect()collect('x*y + x - 3 + 2*x**2 - z*x**2 + x**3', 'x') collect('x*y + x - 3 + 2*x**2 - z*x**2 + x**3', 'x').coeff('x', 2)\[\begin{align*} &x^{3} + x^{2} \cdot \left(2 - z\right) + x \left(y + 1\right) - 3\\ &\qquad\qquad\qquad2-z \end{align*} \] -
cancel()cancel('(x*y**2 - 2*x*y*z + x*z**2 + y**2 - 2*y*z + z**2)/(x**2 - 1)')\[\begin{align*} \frac{y^{2} - 2 y z + z^{2}}{x - 1} \end{align*} \] -
apart()apart('(4*x**3 + 21*x**2 + 10*x + 12)/(x**4 + 5*x**3 + 5*x**2 + 4*x)')\[\begin{align*} \frac{2 x - 1}{x^{2} + x + 1} - \frac{1}{x + 4} + \frac{3}{x} \end{align*} \] -
fraction()fraction('2*x**(-y)') # (2, x**y)
及其变种,这些变种可能改变变量的定义域,所以被单独作为一个函数,有些函数需要加上参数 force=True 才会工作。
-
radsimp()移除分母中的根号
radsimp(S('1/(y*r2 + x*r2 + a*r5 + b*r5)').subs({'r2':sqrt(2), 'r5':sqrt(5)}))\[\begin{align*} \frac{\sqrt{5} a + \sqrt{5} b - \sqrt{2} x - \sqrt{2} y}{5 a^{2} + 10 a b + 5 b^{2} - 2 x^{2} - 4 x y - 2 y^{2}} \end{align*} \] -
ratsimp()合成带分母的项
ratsimp('1/x + 1/y + y')\[\begin{align*} y + \frac{x + y}{x y} \end{align*} \] -
collect_sqrt()对根号起作用的
collect()collect_sqrt(sqrt(2) * x + sqrt(2) * y) -
collect_const()对常数起作用的
collect()collect_const(2*x - 2*y - 2*z, 2) collect_const(2*x - 2*y - 2*z, -2)
- `trigsimp()`
~~~python
S('sin(x)*tan(x)/sec(x)')
trigsimp('sin(x)*tan(x)/sec(x)')
trigsimp('sin(x)*cos(y) + sin(y)*cos(x)')
-
expand_trig() -
powsimp() -
expand_power_exp() -
powdenest() -
expand_log()expand_log(ln(x*y), force=True) -
logcombine()logcombine(n*log(z), force=True) -
expand_func() -
hyperexpand() -
combsimp() -
gammasimp()
使用字符串写公式
不使用字符串写公式的情况
from sympy import gamma, Symbol
from sympy.abc import a, b
a_b, a, b = symbols('a_b a b')
2 * gamma(a_b ** 2) + a * b
如果使用字符串书写公式,则可以像在 markdown 中一样,这样写不用导入(import)函数和变量,不过也要注意
- 环境中已经存在的变量不会被使用;
- 部分符号的含义发生了变化,比如
^代表异或或者指数; - 还是要显式地写出乘法
*; - 字母后面用或不用
_连接的字母、数字,会被视为一体,比如'r_23caax'、'r23caax'或者'r2_3ca_ax'都会被看作(不同的)符号,这些符号显示出来是一样的,但实际上是不同的符号。转化为 LaTeX 时_会作为空格处理。
from sympy import S
S('2*gamma(a_b^2) + a*b')
S('2*gamma(a_b^2) + a*b').subs('a_b', 2)
Sympy 中绝大部分函数都识别字符串,并且将其自发地转化为相应的函数或者变量,比如 'gamma' 就被转化为了函数 \(\Gamma(x) := \int^{\infty}_{0} t^{x-1} e^{-t} \mathrm{d}t\)。
小数和分数
写公式的时候经常遇到,小数精度问题。
a = (1 + 0.4 * z) ** 4 * (1 - 0.2 * z) ** 2
cancel(a)
式中 \(z^5\) 的系数实际上是 0,但由于精度原因变成了一个极小的数,\(z^3\) 的系数也被加上了一个极小的数。
解决的方法之一就是将公式中的所有小数转化为有理数的形式,可以使用类 Rational 来达成这一目的。
a = (1 + Rational(4, 10) * z) ** 4 * (1 - Rational(2, 10) * z) ** 2
cancel(a)
但这种方法非常麻烦,因为每个小数都要计算分子分母作为参数,当然也可以使用字符串做参数 Rational('0.4'),但麻烦程度相差不大。
实际上可以使用 nsimplify() 或者 simplify(rational=True) 来达成。比如
from sympy.abc import z
a = nsimplify((1 + 0.4 * z) ** 4 * (1 - 0.2 * z) ** 2)
# a = S((1 + 0.4 * z) ** 4 * (1 - 0.2 * z) ** 2, rational=True)
cancel(a)
结合前面使用字符串来书写公式的思想
a = nsimplify('(1 + 0.4 * z) ** 4 * (1 - 0.2 * z) ** 2')
cancel(a)
这个时候如果想要小数表示,就可以使用 evalf() 函数,可以加上参数 n 保留有效数字,比如 evalf(n=6) 表示保留 6 位有效数字。
a = nsimplify('(1 + 0.4 * z) ** 4 * (1 - 0.2 * z) ** 2')
cancel(a).evalf()
将 Sympy 中的公式用 LaTeX 表示
使用 latex() 即可
a = nsimplify('(1 + 0.4 * z) ** 4 * (1 - 0.2 * z) ** 2')
print(latex(a))
# \left(1 - \frac{z}{5}\right)^{2} \left(\frac{2 z}{5} + 1\right)^{4}
print(latex(S('r_23caax * r23_caax * r2_3ca_ax')))
# r_{23 caax} r_{2 3ca ax} r_{23caax}

浙公网安备 33010602011771号