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

时间:2017-12-15 05:38:25

标签: tree dynamic-programming np circuit

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

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

我知道如果我们删除了树结构的限制,这将减少到NP-hard的Circuit-SAT。非常感谢任何帮助!

1 个答案:

答案 0 :(得分:0)

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

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

这是带有这个想法的伪代码:

(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)中运行。