DP 查找二进制布尔表达式树可以评估为真的方式数

Posted

技术标签:

【中文标题】DP 查找二进制布尔表达式树可以评估为真的方式数【英文标题】:DP to find number of ways a binary boolean expression tree can evaluate to true 【发布时间】:2018-05-29 07:30:58 【问题描述】:

我们有一个树形的电路。输入是最底部的叶节点,叶节点可以通过与门连接或附加到非门。有一些根节点输出一个最终值。

我一直在尝试提出一个多项式时间算法来计算我们可以使该电路评估为真的方法的数量。我认为我们可以使用动态编程并自上而下,从根节点处的 True 开始并向下传播(即,如果门是 NOT 并且进入它的值是 True,那么 NOT 门下的任何东西都必须有是假的,等等)。但是,我不确定我们应该如何存储先前子问题的结果——我们应该也使用树结构还是可以使用某种二维数组(尽管看起来很多单元格可能未被使用——这可能是浪费)。

我知道,如果我们移除树结构的限制,这将简化为 NP-hard 的 Circuit-SAT。非常感谢任何帮助!

【问题讨论】:

对于您的问题设置,“输入”是否只允许在单个叶节点上使用?即,您不能在布尔表达式中多次使用给定的输入? 【参考方案1】:

我假设给定的输入只能出现在一个叶子中(否则问题再次变成boolean satisfiability--尽管树结构和受限的运算符--根据德摩根定律)。

我相信答案可以在没有数据结构的情况下计算出来。毕竟,您不需要列举解决方案。我认为递归步骤有两个参数:要处理的节点,以及该节点要产生的目标输出,它返回两个值:实现节点目标的方法数,以及下面的叶子总数节点。这两个都可以通过对树的单个深度优先遍历来获得。

下面是这个想法的伪代码:

(int ways, int leaves) = recurse(Node n, bool target) 
  //Base case of recursion:
  if(n.type == LEAF) 
    return (1, 1);
  
  else if(n.type == NOT) 
    //for a NOT, we just invert the target.
    return recurse(n.child[0], !target)
  
  else if (n.type == AND) 
    //AND is more complicated.  First, count the ways that we can make each
    //sub-expression evaluate to true, and the total number of leaves under
    //both the left and right child:
    (int left_true, int left_leaves) = recurse(n.child[0], true);
    (int right_true, int right_leaves) = recurse(n.child[1], true);

    //the total number of ways to make a true AND result is the product of
    //the number of ways to make the left true, and the number of ways to 
    //make the right true.
    int total_true_ways = left_true * right_true;

    //the total number of leaves under this node is the sum of leaves under
    //the left and right subtrees.
    int total_leaves = left_leaves + right_leaves;

    if(target == true) 
      //if this node's target is 'true' we've already computed the number of
      //ways to satisfy that.
      return (total_true_ways, total_leaves);
    
    else 
      //The number of ways to make a 'false' is the total number of possible
      //input combinations, less the number of input combinations that make 
      //a 'true'.

      //The total number of possible input combinations is given by 2 to the 
      //power of the number of boolean inputs, which is given by the 
      //number of leaves below the node:
      int num_possible_inputs = pow(2, total_leaves);

      return ( num_possible_inputs - total_true_ways, total_leaves);
    
  
  else 
    throw internal error
  

不幸的是,运行时间不能严格地用叶子的数量表示,因为您的树不排除任意长的 NOT 操作链。如果我们假设没有背靠背的 NOT 分支,那么最大深度的树是通过交替的 NOT 和 AND 层来实现的,从而得到具有2*ciel(log_2(n)+1) 层和2n + 2(n-1) 总节点的树。 (其中n 是叶节点的数量。)因此,在假设没有背靠背NOT 运算符的情况下,一次触及每个节点的深度优先遍历在O(n) 中运行。

【讨论】:

以上是关于DP 查找二进制布尔表达式树可以评估为真的方式数的主要内容,如果未能解决你的问题,请参考以下文章

是否存在程序员可能希望避免对布尔表达式进行短路评估的合理场景? [关闭]

在 Python 中动态评估简单的布尔逻辑

SSIS 条件拆分错误 - 表达式评估为 NULL,但“条件拆分”需要布尔结果

查找值为真的布尔数组的索引

将对象评估为布尔值

用布尔值(PHP)评估数组的最短方法?