我在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块。
答案 0 :(得分:3)
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 的子表达式的搜索,检查它们的可满足性,对另一个表达式执行第二个for循环( expr2 在此例子),并比较两个子表达式的可满足性:
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
处理树分裂成两个以上子表达式的情况(即和(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()来筛选第一个表达式来进一步改进这个算法,一旦得到符号的布尔解,只需将它们替换为其他表达式,以查看哪个部分是兼容的。