如何重新排序布尔逻辑以更快地短路?

时间:2012-03-27 22:23:50

标签: python boolean-logic

我有一个问题,我有一堆需要很长时间才能执行的函数,每个函数返回一个布尔值True / False。我将一个巨大的布尔表达式应用于所有函数,以获得整体的真/假分数。目前我的代码不是基于函数的,因此执行所有函数,然后应用大布尔表达式。我已经发现,使它们成为函数会允许使用短循环的子表达式来阻止某些函数调用。我现在需要的是一种重新排序表达式的方法,以便我有最少的调用次数。

考虑以下代码(可怕的代码示例,但您应该明白这一点):

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。假设最后的表达式是由用户生成的,这样我就不能硬编码最好的订单。我想我有一个非常简单的算法。

另外两件事:

1。)如果我允许其他逻辑如“不”怎么办? 2.)我可以根据运行所需的时间为每个功能分配一个分数,然后计算出来吗?

3 个答案:

答案 0 :(得分:2)

要优化表达式,您需要了解两件事:每个函数的成本以及它将短路的可能性。完成后,您可以评估每个子表达式以生成相同的术语;尝试参数顺序的每个排列将显示哪种排列具有最低成本。

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

答案 1 :(得分:1)

你的公式在CNF中(顺便说一句,你不需要在顶级or附近使用这些括号),这对于计算复杂性来说非常好,它是非常简单的公式。既然你目前还没有not,我真的不知道是否有必要寻找某种复杂的算法,你的公式已经很简单了。但是你肯定可以尝试某种启发式方法(比如开始评估尽可能少的文字的子句,以便尽快失败...问题是即使你从一个只有一个字面的子句开始,到计算函数可能比计算更大的子句更昂贵,所以是的,根据大小而不是根据预期的计算复杂度对它们进行排序是有意义的。

在您合并not的那一刻,您可以找到一些有用的其他内容。特别是如何将这些转换为CNF,以及来自resolution的想法对您有用。

答案 2 :(得分:0)

您可以将表达式转换为树结构,其中每个分支代表有序子级列表上的ANY或ALL进程。在第一个FALSE上ALL分支短路,在第一个TRUE上ANY分支短路。

如果所有条件的权重均相同,则应先放置简单条件(叶),然后再按其各自的复杂性数量递增(以其子代的复杂性总和递归度量)来对子分支进行排序。

因此,以这种树形结构表示的条件将如下所示:

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的条件是,短路所处的ANY / ALL组的顺序应更高。 ANY / ALL组的可能性可以通过组合每个孩子的概率来计算(例如,ALL: 1-(1-P1)(1-P2)...(1-Pn)) 。资源成本也可以根据概率进行调整。在此处添加NOT:分组会有所不同

在这些其他因素之间找到合适的平衡可能是一门艺术,并且将非常取决于解决方案的领域。