Sympy - 比较表达式

问题描述 投票:0回答:5

有没有办法检查两个表达式在数学上是否相等?我期望

tg(x)cos(x) == sin(x)
输出
True
,但它输出
False
。有没有办法与 sympy 进行这样的比较?另一个例子是
(a+b)**2 == a**2 + 2*a*b + b**2
令人惊讶的是还输出
False

我发现了一些类似的问题,但没有一个涵盖这个确切的问题。

python sympy comparison-operators
5个回答
31
投票

来自 SymPy 文档

==
代表精确的结构相等性测试。这里的“精确”意味着两个表达式只有在结构上完全相等时才会用 == 进行比较。这里,(x+1)^2 和 x^2+2x+1 在符号上并不相同。一项是两项相加的幂,一项是三项相加的幂。

事实证明,当使用 SymPy 作为库时,使用

==
测试精确的符号相等性比让它表示符号相等性或测试数学相等性要有用得多。然而,作为一个新用户,您可能会更关心后两者。我们已经看到了用符号表示平等的另一种选择,方程。要测试两个事物是否相等,最好回顾一下基本事实:如果 a=b,则 a−b=0。因此,检查是否 a=b 的最佳方法是取 a−b 并简化它,看看它是否变为 0。稍后我们将了解到执行此操作的函数称为
simplify
。这种方法并非万无一失——事实上,理论上可以证明,一般情况下不可能确定两个符号表达式是否相同——但对于大多数常见表达式来说,它的效果相当好。

作为您特定问题的演示,我们可以使用等价表达式的减法并与 0 进行比较,如下所示

>>> from sympy import simplify
>>> from sympy.abc import x,y
>>> vers1 = (x+y)**2
>>> vers2 = x**2 + 2*x*y + y**2
>>> simplify(vers1-vers2) == 0
True
>>> simplify(vers1+vers2) == 0
False

12
投票

您也可以使用

.equals
方法来比较表达式:

from sympy import *
x = symbols('x')

expr1 = tan(x) * cos(x)
expr2 = sin(x)

expr1.equals(expr2)

True

4
投票

使用 simple 的解决方案对我来说太慢了(必须交叉检查多个变量),所以我编写了以下函数,它预先做了一些简单的检查,以减少计算时间,仅在最后一步使用 simple。

import numpy as np
import sympy as sp

def check_equal(Expr1,Expr2):
    if Expr1==None or Expr2==None:
        return(False)
    if Expr1.free_symbols!=Expr2.free_symbols:
        return(False)
    vars = Expr1.free_symbols
    your_values=np.random.random(len(vars))
    Expr1_num=Expr1
    Expr2_num=Expr2
    for symbol,number in zip(vars, your_values):
        Expr1_num=Expr1_num.subs(symbol, sp.Float(number))
        Expr2_num=Expr2_num.subs(symbol, sp.Float(number))
    Expr1_num=float(Expr1_num)
    Expr2_num=float(Expr2_num)
    if not np.allclose(Expr1_num,Expr2_num):
        return(False)
    if (Expr1.equals(Expr2)):
        return(True)
    else:
        return(False)

1
投票

如前所述,

(expr1 - expr2).simplify()
expr1.equals(expr2)
有时无法识别难以简化的复杂表达式的相等性。为了解决这个问题,用随机数对表达式进行数值评估可以构成相对安全的“强力”测试。我已将@Okapi575 的优秀解决方案改编为:

  1. 每次使用不同的随机数测试数值相等性 N 次,以获得更可信的诊断
  2. 当一对表达式仅通过数值测试但未通过符号相等测试时警告用户。

例如:

希望它能有用:

import sympy as sp
import numpy as np

def check_equal(Expr1, Expr2, n=10, positive=False, strictly_positive=False):
    
    # Determine over what range to generate random numbers
    sample_min = -1
    sample_max = 1
    if positive:
        sample_min = 0
        sample_max = 1
    if strictly_positive:
        sample_min = 1
        sample_max = 2
    
    # Regroup all free symbols from both expressions
    free_symbols = set(Expr1.free_symbols) | set(Expr2.free_symbols)
    
    # Numeric (brute force) equality testing n-times
    for i in range(n):
        your_values=np.random.uniform(sample_min, sample_max, len(free_symbols))
        Expr1_num=Expr1
        Expr2_num=Expr2
        for symbol,number in zip(free_symbols, your_values):
            Expr1_num=Expr1_num.subs(symbol, sp.Float(number))
            Expr2_num=Expr2_num.subs(symbol, sp.Float(number))
        Expr1_num=float(Expr2_num)
        Expr2_num=float(Expr2_num)
        if not np.allclose(Expr1_num, Expr2_num):
            print("Fails numerical test")
            return(False)
        
    # If all goes well so far, check symbolic equality
    if (Expr1.equals(Expr2)):
        return(True)

    else:
        print("Passes the numerical test but not the symbolic test")
        # Still returns true though
        return(True)

编辑:更新代码(1)以将表达式与不同数量的自由符号进行比较(例如,在简化过程中符号被取消后),以及(2)以允许指定正或严格正随机数范围。


0
投票

从原始的 sympy 本身中检查这一点。 https://github.com/sympy/sympy/wiki/Faq

对我有用的例子

© www.soinside.com 2019 - 2024. All rights reserved.