Sympy - 比较表达式

36

有没有一种方法可以检查两个表达式是否在数学上相等?我期望tg(x)cos(x) == sin(x)会输出True,但实际上它输出的是False。是否有一种使用sympy进行这样比较的方法?另一个例子是(a+b)**2 == a**2 + 2*a*b + b**2,出乎意料地也输出了False

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

5个回答

32

来自SymPy文档

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

事实证明,在使用SymPy作为库时,让 == 测试精确的符号相等性比让它代表符号相等性或测试数学相等性更有用。然而,作为新用户,您可能更关心后两者。我们已经看到了代表符号相等性的替代方法Eq。要测试两个东西是否相等,最好记住以下基本事实:如果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

请注意,如果您有布尔表达式(其中变量a,b,... = sympy.symbols('a b ...',boolean = True)并且使用sympy.Orsympy.Xor等),那么您可以执行isEquivalent = lambda expr1,expr2:sympy.simplify((expr1&(〜expr2))|((〜expr1)&expr2))== False - Phylliida
2
它适用于多项式和有理函数,但不适用于其他很多情况...例如,f1 = log(x)+log(y)f2 = log(x*y) ... (f1+f2).simplify() 得到的结果是 log(x)+log(y) - log(x*y) 而不是 0 - Prokop Hapala
3
如果假设xy为正数,那么这个式子的结果实际上是0,否则当xy都是负数时,恒等式不成立。@Prokop - Marco Repetto

12

或者您可以使用.equals方法来比较表达式:

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

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

expr1.equals(expr2)

True

4
简化操作对我来说太慢了(必须交叉检查多个变量),因此我编写了以下函数,在最后一步才使用简化操作,首先进行了一些简单的检查,以减少计算时间。
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(Expr2_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
很棒的解决方案。for循环可以简化为Expr1_num=Expr1_num.subs(zip(vars, your_values)) Expr2_num=Expr2_num.subs(zip(vars, your_values))。Sympy可以一次替换多个元组。 - Prox
1
有一个打字错误,Expr1_num=float(Expr2_num) 应该是 Expr1_num=float(Expr1_num),否则这将始终返回 true。 - undefined
修正了代码以纠正拼写错误 - undefined

1
如前所述,(expr1 - expr2).simplify()expr1.equals(expr2)有时无法识别复杂表达式的相等性。为了解决这个问题,使用随机数对表达式进行数值评估可能构成一种相对安全的“暴力”测试。我改编了 @Okapi575 的优秀解决方案来执行以下操作:
  1. 使用不同的随机数N次测试数值相等性,以获得更加可靠的诊断结果
  2. 当一对表达式只通过数值测试而未经过符号相等性测试时,向用户发出警告。
例如:

Example of test for equality

希望它能够证明有用。
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)允许指定正数或严格正数范围。


我的测试中无法工作,因为两个表达式不同,但返回的结果是数值上相同的,这是错误的:exp1=0.5x**4 - 1.0x3 + 2.5*x2 - 2.0x + 1.0,exp2=1.5x4 - 3.0*x3 + 3.5x**2 - 2.0x + 1.0。 - sol

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接