C++ 如何将命题逻辑树转换为连接范式(CNF)树
我有一根像绳子一样的绳子C++ 如何将命题逻辑树转换为连接范式(CNF)树,c++,c,recursion,conjunctive-normal-form,prefix-tree,C++,C,Recursion,Conjunctive Normal Form,Prefix Tree,我有一根像绳子一样的绳子 s="(=> P (OR (AND A (NOT B)) (AND B (NOT A))))"; 并将其转换为输出此字符串的CNF,如 (或(非p)(或A B)) (或(非P)(或(非B)(非A))) 我是否需要创建一个结构树节点来保持该值 struct TreeNode { string val; // The data in this node. TreeNode *left
s="(=> P (OR (AND A (NOT B)) (AND B (NOT A))))";
并将其转换为输出此字符串的CNF,如
(或(非p)(或A B))
(或(非P)(或(非B)(非A)))
我是否需要创建一个结构树节点来保持该值
struct TreeNode {
string val; // The data in this node.
TreeNode *left; // Pointer to the left subtree.
TreeNode *right; // Pointer to the right subtree.
//TreeNode *farther;//should I use farther or not in convert to CNF?
};
如何将其转换为CNF,即连接范式?
请给出一些算法细节。
在我看来,也许使用递归函数更好地解决了这个问题,但我仍然想不出如何使用递归。或者您有其他解决此问题的建议吗?假设您将函数命名为
CNF
,获取一棵树并在CNF中返回该树
和(p=>q,q=>p)
替换等价性pq
,然后用或(q,非(p))
替换等价性p=>q
NOT
操作向下移动到树中,以便NOT
操作仅绑定到原子(A
,B
)CNF(和(x,y))
的结果很简单:和(CNF(x,CNF(y))
,因为CNF喜欢在树中有和的高度
CNF(或(和(x,y,z))
的结果稍微复杂一点。在这里,我们将使用析取在连接上的分布规则,即或(和(x,y),z)
相当于和(或(x,z),或(y,z))
。实际上,CNF(或(和(x,y),z))
将是和(或(CNF(x),CNF(z)),或(CNF(y),CNF(z))
完成了。简单递归下降解析器解决方案:
TreeNode*ParseExpression(const char*&p)
:如果p指向的字符串不是以“(”开头,则返回ParseAtom(p),否则将p移过“(”,调用ParseOperation(p),然后将p移过“)”,并返回ParseOperation返回的值
TreeNode*ParseAtom(const char*&p)
:跳过atom(连续的非空格序列)的p。返回一个TreeNode,其中atom为值,左右为空
TreeNode*ParseOperation(const char*&p)
:p指向的字符串应以运算符开头。将p移过运算符,然后确定运算符的操作数。如果是一个,则调用ParseExpression(p)一次;如果是两个,则调用ParseExpression(p)两次。然后返回一个TreeNode,其中运算符为值,一个或两个ParseExpression调用的结果为left和right(对于只有一个操作数的运算符,right应为NULL)
设置指向原始字符串的指针;对该指针调用ParseExpression;返回值是树,指针将指向字符串中的第一个表达式
这解决了您的一个问题:如何将字符串转换为树。Adrian Panasiuk解决了另一个问题,即如何将树转换为正常形式。由于您要进行额外的树转换,节点中的“值”应称为“op”或类似名称,以代表“operator”(在C++中是保留字),它应该是一个枚举,而不是一个字符串。请自己动手。遇到问题时,可以问一个特定的问题。但这里有一个提示:“递归下降解析器”.另一个提示:程序需要做的第一件事是什么?我只想知道一些算法并从中学习一些东西,这样我就可以做我自己的工作,我不想有人为我发布源代码。我确实很难找到有用的信息,但仍然不明白,这就是为什么我在这里问这个问题,我错了吗?在你写当然,你可以在谷歌上搜索“递归下降解析器”并在路上走很长时间。没问题,如果你觉得答案有用,请接受它!丽贝卡,我已经发布了一个答案,可以很容易地引导你找到一个完整的程序。那么你的算法是递归还是非递归?暗示(=>)呢在底部,X=>Y CNF:不是X和Y,如果字符串很长,事情会很复杂,而且会有很大的麻烦work@RebeccaAdrian解决了这个问题。是的,它是递归的……任何树遍历都是递归的。你看到“AND(CNF(x),CNF(y))”了吗例如?这是一个CNF的递归调用。对于一个有经验的程序员来说,这并不是那么多工作。如果你不是一个程序员,也许你应该从一个更简单的问题开始。@Rebecca在某些情况下,算法的输出将与输入的大小成指数关系,例如
或(和(x_1,y_1),和(x_2,y_2),…和(x_n,y_n))
结果将是或(z_1,z_2,…,z_n)形式的所有子句的连接
每个Z_i
都是X_i
或Y_i
@AdrianPanasiuk Im使用您提到的相同方法。但是可以在树的一次遍历中应用所有分布规律。Mi问题是类似于a&b&c|d的东西会转换为((a | d)和((b&c)| d)),因此我必须进行两次遍历以获得所需的:((a | d)和((b | d)和(c | d))。是我做错了什么事还是这是意料之中的事?@Wyvern666尝试将和和和或视为n元而不是二进制-a&b&c | d
变成或(和(a,b,c),d
而不是或(和(和(a,和(b,c)),d)
。实际上,没有必要在树中转换字符串,这会使事情变得复杂,对吗?我可以使用substr()和find()以及strcpy()之类的东西将其转换为CNF吗?@Rebecca当然需要将字符串转换为树,我刚刚告诉过你如何转换。