编程语言
首页 > 编程语言> > python-Sympy:如何查找所有N个表达式共有的最长表达式

python-Sympy:如何查找所有N个表达式共有的最长表达式

作者:互联网

我在Sympy中有N个表达式,我需要找到所有N个表达式共有的最长表达式(例如最长的表达式在所有N个表达式中/包含在其中)

from sympy import Symbol
from sympy.logic.boolalg import And, Not, Or
a = Symbol("a")
b = Symbol("b")

expr0 = And(And(a, b), c)
expr1 = Not(And(a, b))
expr2 = Or(Not(a), Not(b))
#?? now how to find that all expressions contains And(a,b) ?  

我为什么需要它?我有一些表达式,我需要为它们构建If-then-else块:

expr0 causes operation Op0
expr1 causes operation Op1
expr2 causes operation Op2

and the result should be:
And(a,b) is longest expression which all of expr have in common
so I will be able to build if then else block like this

If (And(a, b)) {
    if(c)
        Op0;
}else{
    op1;
    op2;
}

所以这就是为什么我需要找到所有N个表达式共有的最长表达式.
为了优化此If-then-else块.

解决方法:

SymPy对象具有.has(…)方法:

>>> expr1.has(expr0)
True

>>> expr2.has(expr0)
False

关于Python的其余部分(即不是SymPy),您最好定义共享表达式的含义.逻辑运算通常应该返回一个值,而不是构建一个表达式.

编辑

好的,从讨论中我了解到您想在两个表达式之间寻找一个公共子表达式(因此,不测试一个子表达式是否是一个公共子表达式).

此外,您想在逻辑上匹配等价的表达式,也就是说,看起来不同但逻辑上等价的表达式应匹配为等价的.这使一切变得更加复杂,但是我为您提出了一个简单的解决方案.

SymPy具有satisfiable()函数,该函数找到逻辑表达式的解.如果您传递参数all_models = True,则此函数将返回满足该逻辑条件的所有解决方案:

In [5]: list(satisfiable(expr2, all_models=True))
Out[5]: [{b: False, a: False}, {b: False, a: True}, {b: True, a: False}]

函数preorder_traversal允许我们访问整个表达式树(即,轻松创建一个for循环来访问所有子表达式).因此,我们可以构造一个双for循环以对expr1的子表达式执行搜索,检查它们的可满足性,对另一个表达式(在本示例中为expr2)执行第二个for循环,并比较两个子表达式的可满足性:

for e1 in preorder_traversal(expr1):
    s1 = list(satisfiable(e1 ,all_models =True ))
    for e2 in preorder_traversal (expr2):
        s2 = list (satisfiable (e2 ,all_models =True ))
        if s1 == s2:
            print("Logically equiv subexpr found: ", e1, " and ", e2)

如果将expr1和expr2放在此处,则会得到:

('Logically equiv subexpr found: ', Not(And(a, b)), ' and ', Or(Not(a), Not(b)))
('Logically equiv subexpr found: ', b, ' and ', b)
('Logically equiv subexpr found: ', a, ' and ', a)

我认为您可以轻松地将此代码适应您的需求.

编辑2

要处理将树分为两个以上子表达式的情况(即,像And(a,b,c,d)或Xor(a,b,c,d,e)之类的情况),可以生成这样的子集(这次代码未经过测试):

# get a function to generate all subsets of a tuple,
# let's say `subsets( )`

for e1 in preorder_traversal(expr1):
    for es1 in subsets(e1.args):
        s1 = list(satisfiable(e1.func(*es1), all_models=True))
        # NOW do the same with the `e2` for-loop,
        # then compare `s1` and `s2` as usual.

该代码看起来像一系列4个嵌套的for循环,但可以重新排列以定义一些辅助函数,以使其看起来更好.

可以进一步推广比较两个以上的表达式,再次更好地重新组织代码.

编辑3

我想了一夜,我认为您可以通过仅使用satisfiable()筛选第一个表达式来进一步改进该算法,一旦获得了符号的布尔值解决方案,只需将它们替换为其他表达式以查看哪些部分兼容.

标签:sympy,boolean-logic,python
来源: https://codeday.me/bug/20191119/2038302.html