4

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

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;
}
4

2 回答 2

1

如果AND并且OR是您使用的唯一运算符,那么将您的树转换为 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;
}

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

TreeNode root = ....;
while (true) {
  TreeNode transformedRoot = reconstruct(root);
  if (root.equals(transformedRoot)) {
    break;
  }
  root = transformedRoot;
}
// root is now in CNF

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

于 2013-06-21T17:45:42.320 回答
0

我建议您查看树的导航方式,在您的代码中看起来像是深度优先搜索,因此您将从最深的分支(Deepest 运算符)开始,您必须设计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
     */
}

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

于 2013-06-21T17:18:13.913 回答