使用 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)')

\[\begin{align*} &\frac{\sin{\left(x \right)} \tan{\left(x \right)}}{\sec{\left(x \right)}}\\ &\quad\ \ \sin^{2}{\left(x \right)}\\ &\quad\sin(x+y) \end{align*} \]

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

\[\begin{align*} &a b + 2 \Gamma\left(a_{b}^{2}\right)\\ &\quad ab+12 \end{align*} \]

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)

\[\begin{align*} 0.001024 z^{6} + 1.73472347597681 \cdot 10^{-18} z^{5} - 0.0384 z^{4} - 0.0640000000000001 z^{3} + 0.36 z^{2} + 1.2 z + 1.0 \end{align*} \]

式中 \(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)

\[\begin{align*} \frac{16 z^{6}}{15625} - \frac{24 z^{4}}{625} - \frac{8 z^{3}}{125} + \frac{9 z^{2}}{25} + \frac{6 z}{5} + 1 \end{align*} \]

结合前面使用字符串来书写公式的思想

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

\[\begin{align*} 0.001024 z^{6} - 0.0384 z^{4} - 0.064 z^{3} + 0.36 z^{2} + 1.2 z + 1.0 \end{align*} \]

将 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}
posted @ 2022-11-11 01:03  Violeshnv  阅读(138)  评论(0编辑  收藏  举报