Python 如何重新排列布尔逻辑以更快地短路?
我有一个问题,我有一堆需要很长时间才能执行的函数,每个函数都返回一个布尔真/假。我将一个巨大的布尔表达式应用于所有函数,以获得总体的真/假分数。目前我的代码不是基于函数的,所以所有函数都会执行,然后应用大型布尔表达式。我已经发现,让它们成为函数将允许使用子表达式,这些子表达式会缩短圈以防止某些函数调用。我现在需要的是一种对表达式重新排序的方法,这样我就有了最少的调用次数 考虑到下面的代码示例,您应该明白:Python 如何重新排列布尔逻辑以更快地短路?,python,boolean-logic,Python,Boolean Logic,我有一个问题,我有一堆需要很长时间才能执行的函数,每个函数都返回一个布尔真/假。我将一个巨大的布尔表达式应用于所有函数,以获得总体的真/假分数。目前我的代码不是基于函数的,所以所有函数都会执行,然后应用大型布尔表达式。我已经发现,让它们成为函数将允许使用子表达式,这些子表达式会缩短圈以防止某些函数调用。我现在需要的是一种对表达式重新排序的方法,这样我就有了最少的调用次数 考虑到下面的代码示例,您应该明白: def q(): print "q" return False def
def q():
print "q"
return False
def r():
print "r"
return False
def s():
print "s"
return False
def a():
print "a"
return False
def b():
print "b"
return False
def c():
print "c"
return False
def d():
print "d"
return False
def i():
print "i"
return False
def j():
print "j"
return False
(q() or r() or s()) and (a() and b() and c() and (i() or j()))
在这种情况下,您会看到打印的q r s。都是假的,所以它短路了。但是在这种情况下,应该首先计算b或c,因为如果其中任何一个为False,则整个表达式为False。假设最后的表达式是由用户生成的,这样我就无法硬编码最佳顺序。我想有一个非常简单的算法我没有
还有两件事:
一,。如果我允许其他逻辑,例如不允许,该怎么办?
2.我可以根据运行所需的时间为每个函数分配一个分数,然后在公式中进行计算吗?你的公式是这样的,顺便说一句,顶级OR不需要括号,这对于计算复杂度来说很好,这是一个非常简单的公式。因为你现在还没有,我真的不知道是否有必要寻找一些复杂的算法,你的公式已经足够简单了。但是你肯定可以尝试一些启发式方法,比如从计算尽可能少的文本的子句开始,这样你就可以尽可能快地失败。。。问题是,即使您从只有一个文本的子句开始,计算函数可能比计算更大的子句更昂贵,因此,是的,不根据大小而是根据预期的计算复杂性对它们进行排序是有意义的
目前你还没有合并,你可以找到一些额外的有用的东西。特别是如何再次将它们转换为CNF,以及来自CNF的想法可能对您很有用。您的公式,因为它在btw中,您不需要在顶级ors周围使用括号,这对于计算复杂性来说非常好,这是一个非常简单的公式。因为你现在还没有,我真的不知道是否有必要寻找一些复杂的算法,你的公式已经足够简单了。但是你肯定可以尝试一些启发式方法,比如从计算尽可能少的文本的子句开始,这样你就可以尽可能快地失败。。。问题是,即使您从只有一个文本的子句开始,计算函数可能比计算更大的子句更昂贵,因此,是的,不根据大小而是根据预期的计算复杂性对它们进行排序是有意义的
目前你还没有合并,你可以找到一些额外的有用的东西。尤其是如何将这些函数再次转换为CNF以及来自CNF的想法可能会对您有所帮助。要优化表达式,您需要知道两件事:每个函数的成本以及它短路的概率。一旦你有了它,你可以计算每个子表达式来产生相同的术语;尝试参数顺序的每个排列将显示哪种排列成本最低
def evaluate_or(argument_evaluation_list):
total_cost = 0.0
probability_of_reaching = 1.0
for cost, probability_of_true in argument_evaluation_list:
total_cost += probability_of_reaching * cost
probability_of_reaching *= 1.0 - probability_of_true
return total_cost, 1.0 - probability_of_reaching
def evaluate_and(argument_evaluation_list):
total_cost = 0.0
probability_of_reaching = 1.0
for cost, probability_of_true in argument_evaluation_list:
total_cost += probability_of_reaching * cost
probability_of_reaching *= probability_of_true
return total_cost, probability_of_reaching
def evaluate_not(argument_evaluation)
cost, probability_of_true = argument_evaluation
return cost, 1.0 - probability_of_true
要优化表达式,需要知道两件事:每个函数的成本和它短路的概率。一旦你有了它,你可以计算每个子表达式来产生相同的术语;尝试参数顺序的每个排列将显示哪种排列成本最低
def evaluate_or(argument_evaluation_list):
total_cost = 0.0
probability_of_reaching = 1.0
for cost, probability_of_true in argument_evaluation_list:
total_cost += probability_of_reaching * cost
probability_of_reaching *= 1.0 - probability_of_true
return total_cost, 1.0 - probability_of_reaching
def evaluate_and(argument_evaluation_list):
total_cost = 0.0
probability_of_reaching = 1.0
for cost, probability_of_true in argument_evaluation_list:
total_cost += probability_of_reaching * cost
probability_of_reaching *= probability_of_true
return total_cost, probability_of_reaching
def evaluate_not(argument_evaluation)
cost, probability_of_true = argument_evaluation
return cost, 1.0 - probability_of_true
可以将表达式转换为树结构,其中每个分支表示有序子级列表上的任意或所有进程。第一个为假时所有分支短路,第一个为真时任何分支短路 如果所有条件都具有相同的权重,则可以将简单条件的叶子放在第一位,然后再将分支按其单个复杂性度量的数量递归地增加其子级复杂性的总和排序 因此,在这种树结构中表示的条件如下所示:
ALL:
*--- ANY: q(), r(), q()
a()
b()
c()
*--- ANY: i(), j()
计算每个孩子的体重表示相对体重:
ALL:
3 *--- ANY: q(), r(), q()
1 a()
1 b()
1 c()
2 *--- ANY: i(), j()
---
8
这将允许您对条件进行重新排序:
ALL:
1 a()
1 b()
1 c()
2 *--- ANY: i(), j()
3 *--- ANY: q(), r(), q()
---
8
至少还有两个因素需要考虑,但您需要更深入地了解这样做的条件:
评估每个条件需要多少时间或资源
条件返回TRUE或FALSE的可能性有多大
需要更多时间/资源进行评估的条件应放在评估顺序的较低位置
更可能产生真/假结果的情况,即短路任何/所有组
重新输入的顺序应放在更高的位置。任何/所有组的可能性可以通过组合每个子组的概率来计算,例如ALL:1-1-P11-P2…1-Pn。资源成本也可以根据概率进行调整。这就是添加NOT:grouping的地方
在这些额外的因素之间找到正确的平衡可能是一门艺术,并且将非常依赖于解决方案的域。您可以将表达式转换为树结构,其中每个分支表示有序子级列表上的任意或所有进程。第一个为假时所有分支短路,第一个为真时任何分支短路 如果所有条件都具有相同的权重,则可以将简单条件的叶子放在第一位,然后再将分支按其单个复杂性度量的数量递归地增加其子级复杂性的总和排序 因此,在这种树结构中表示的条件如下所示:
ALL:
*--- ANY: q(), r(), q()
a()
b()
c()
*--- ANY: i(), j()
计算每个孩子的体重表示相对体重:
ALL:
3 *--- ANY: q(), r(), q()
1 a()
1 b()
1 c()
2 *--- ANY: i(), j()
---
8
这将允许您对条件进行重新排序:
ALL:
1 a()
1 b()
1 c()
2 *--- ANY: i(), j()
3 *--- ANY: q(), r(), q()
---
8
至少还有两个因素需要考虑,但您需要更深入地了解这样做的条件:
评估每个条件需要多少时间或资源
条件返回TRUE或FALSE的可能性有多大
需要更多时间/资源进行评估的条件应放在评估顺序的较低位置
更有可能产生短路所属任意/所有组的真/假结果的条件应按更高的顺序排列。任何/所有组的可能性可以通过组合每个子组的概率来计算,例如ALL:1-1-P11-P2…1-Pn。资源成本也可以根据概率进行调整。这就是添加NOT:grouping的地方
在这些额外因素之间找到正确的平衡可能是一门艺术,并且将非常依赖于解决方案的领域