在二叉树中将 AND 分布在 OR 上(合取范式)

Posted

技术标签:

【中文标题】在二叉树中将 AND 分布在 OR 上(合取范式)【英文标题】:Distributing AND over OR in a binary tree (Conjunctive Normal Form) 【发布时间】:2013-06-18 19:02:05 【问题描述】:

我正在尝试转换二叉树,例如

OR (Implementation of Operator - a specialisation of TreeNode... see below)
|-A (Implementation of TreeNode... see below)
|-OR
  |-B
  |-AND (Implementation of Operator - a specialisation of TreeNode... see below)
    |-C
    |-OR
      |-D
      |-E

转换为等效的连接范式 (CND) 表示。我相信因为我只使用逻辑 OR + AND 运算符,所以我必须执行的唯一步骤是 AND 在 OR 上的分布。这将在 CNF 中生成以下树(出于我的目的仍然是二进制):

AND
|-OR
| |-A
| |-OR
|   |-B
|   |-OR
|     |-E
|     |-D
|-OR
  |-A
  |-OR
    |-B
    |-OR
      |-E
      |-C

我在创建算法来执行此操作时遇到问题...到目前为止,我有以下骨架,它将自下而上重新编写树(注意对重构的递归调用):

public TreeNode reconstruct(TreeNode treeNode) 
  if(treeNode instanceof Operator) 
    TreeNode left = reconstruct(((Operator)treeNode).getLeft());
    TreeNode right = reconstruct(((Operator)treeNode).getRight());

    return distribute(treeNode, left, right);
  
  else
    return node;

使用类:

 -----------
|  TreeNode | // Interface
 -----------
      ^
      |
 -----------
| Operator  | // Interface
 -----------
| getLeft() |
| getRight()|
| setLeft() |
| setRight()|
 -----------

有人可以建议一个可以转换为 CNF 的分发实现吗?

// EDIT 1(在 nif 回答之后)

private Node distribute(TreeNode node, TreeNode left, TreeNode right) 
  if (node instanceof Or) 
    if (left instanceof And) 
      // distribute right over left AND
      return 
        new And(
          new Or(((Operator)left).getLeft(), right),
          new Or(((Operator)left).getRight(), right)
        );
     
    else if (right instanceof And) 
      // distribute left over right AND
      return 
        new And(
          new Or(((Operator)right).getLeft(), left),
          new Or(((Operator)right).getRight(), left)
        );
    
  
  if(node instanceof Operator) 
    ((Operator)node).setLeft(left);
    ((Operator)node).setRight(right);
  
  // default
  return node;

【问题讨论】:

【参考方案1】:

如果 ANDOR 是您正在使用的唯一运算符,那么将您的树转换为 CNF 应该不难。您所要做的就是找到OR(AND(X,Y), Z)OR(Z, AND(X,Y)) 形式的结构并使用分布规律。

private static TreeNode distribute(TreeNode n, TreeNode left, TreeNode right) 
  if (n instanceof Or) 
    if (left instanceof And) 
      // distribute right over left AND
      return new And(new Or(left.getLeft(), right), 
                     new Or(left.getRight(), right));
     else if (right instanceof And) 
      // distribute left over right AND
      return new And(new Or(right.getLeft(), left), 
                     new Or(right.getRight(), left));
    
  

  // no change
  return treeNode;

此算法必须应用于树的所有节点,直到树不再更改。将算法应用于节点的顺序无关紧要。直观地说,算法的重复应用将拉起所有AND 节点超过OR 节点,直到树在CNF 中。

TreeNode root = ....;
while (true) 
  TreeNode transformedRoot = reconstruct(root);
  if (root.equals(transformedRoot)) 
    break;
  
  root = transformedRoot;

// root is now in CNF

注意:请注意,CNF 转换可能会使您的树呈指数级增长。所示的实现非常原始,没有使用任何增强功能来减少计算时间。

【讨论】:

谢谢...我没有考虑过迭代方法,因此我的分发方法的实现更加复杂和复杂,这要简单得多!我必须做的一件事是将一小段代码引入到默认返回的分发方法中,以便在返回之前设置 TreeNode 的左侧和右侧,但仍然是一个很好的答案!我已将更改后的代码包含在原始问题的编辑中【参考方案2】:

我建议您查看树的导航方式,在您的代码中看起来像是深度优先搜索,因此您将从最深的分支(Deepest 运算符)开始您必须设计您的方法distribute 期望该顺序并应用以回溯方式对子节点的分配法则。

distribute 方法应该做的一个非常笼统的描述是:

必须应用哪种分配律的流程取决于 关于父节点的操作类型和子节点。 每个子节点可以是一个算子或一个值,根据这个组合做规律所要求的分布。

我想告诉你的伪代码是:

if parent node is OR type
    if child nodes are OPERATOR-VALUE combination
        if OPERATION is AND type
            apply correspondig distribution 
            return the new parent
        else
            apply correspondig distribution 
            return the new parent
    if child node are VALUE-VALUE combination
        return parent
if parent node is AND type
    if child nodes are OPERATOR-VALUE combination
        if OPERATION is AND type
            apply correspondig distribution 
            return the new parent
        else
            apply correspondig distribution 
            return the new parent
    if child nodes are VALUE-VALUE combination
        return parent;

一个实现示例:

public TreeNode distribute(TreeNode parent,TreeNode leftChild, TreeNode rightChild) 
    if( !(leftChild instanceof Operator) && !(rightChild instanceof Operator) )
        /*There is nothing to do */
        return parent;
    
    if( parent.getType() == 'OR')
        /*
            Apply distributive laws and return the new branch
            for example:        
        */
        if ( (leftChild instanceof operator) &&  !(rightChild instanceof Operator) )
            TreeNode operatorLeftChild  = leftChild.getLeftChild();
            TreeNode operatorRightChild = leftChild.getRightChild();
            if(leftChild.getType() == 'AND' )
            
                /*
                Applying distributive laws:
                    rightChild OR (operatorLeftChild AND operatorRightChild) 
                        -> (rightChild OR operatorLeftChild) AND (rightChild OR operatorRightChild)
                */
                TreeNode newBranch = new Operator("AND");
                /*new Left child*/
                TreeNode newLeftChild= new Operator("OR");
                newLeftChild.setLeftChild(rightChild);
                newLeftChild.setRightChild(operatorLeftChild);
                /*new Richt Child */
                TreeNode newRightChild= new Operator("OR");
                newRightChild.setLeftChild(rightChild);
                newRightChild.setRightChild(operatorRightChild);
                /*Setting the new Branch*/
                newBranch.setLeftChild(newLeftChild);
                newBranch.setRightChild(newRightChild);
                return newBranch;
            

        
    
    if( parent.getType() == 'AND')
        /*
            Else-If and distributive laws stuff
        */
    
    /*
        You can also implement this part wihtout the else-if code by implementing a true table
        but is more abstract and less human redeable
     */

注意 之前的代码没有经过测试,我假设了很多我不知道的事情 您的树的实现方式可能是您需要更新子节点中的父引用。

【讨论】:

以上是关于在二叉树中将 AND 分布在 OR 上(合取范式)的主要内容,如果未能解决你的问题,请参考以下文章

6.2.2-1 指针与引用在二叉树创建的应用

python3实现在二叉树中找出和为某一值的所有路径

在二叉树中成为主人

在二叉树中插入元素

线索二叉树

线索二叉树