[英]Python - How to convert a binary tree to N-ary tree keeping the same information
I have a binary tree that represents a parsed logical formula.我有一个二叉树,它代表一个解析的逻辑公式。 For example, f = a & b & -c |
例如, f = a & b & -c | d is represented by a list of lists in prefix notation, where the first element is the operator (unary or binary) and the next elements their arguments:
d由前缀表示法的列表列表表示,其中第一个元素是运算符(一元或二元),接下来的元素是 arguments:
f = [ |, [&, a, [&, b, [-, c]]], d] f = [ |, [&, a, [&, b, [-, c]]], d]
But if you translate (by recursion) to the classical infix notation the result is the same.但是如果你(通过递归)翻译成经典的中缀符号,结果是一样的。
f = (((-c & b) & a) | d) = a & b & -c | f = (((-c & b) & a) | d) = a & b & -c | d
d
What I'm trying to do is to convert it into an N-ary tree that retains the same information, that is, if you translate it into formula again, the result must be the same.我要做的是将其转换为保留相同信息的N叉树,即如果将其再次转换为公式,则结果必须相同。 Something like this:
像这样的东西:
f = {l: [{&: [a,b,{-:[c]}]}, d]} f = {l: [{&: [a,b,{-:[c]}]}, d]}
Which in infixed notation is the following.以下是中缀符号。
f = ((a & b & -c) | d) = a & b & -c | f = ((a & b & -c) | d) = a & b & -c | d
d
I haven't found any library so I tried to do it by myself recursively.我还没有找到任何库,所以我尝试自己递归地做。 However, I have only achieved this code that fails in some cases and it's not very elegant...
但是,我只实现了在某些情况下失败的代码,而且它不是很优雅......
def explore_tree(self,tree, last_symbol, new_tree):
if type(tree) != list: # This true means that root is an atom
new_tree[last_symbol].append(tree)
return
root = tree[0]
if is_operator(root):
if root != last_symbol:
branch = {root: []}
new_tree[last_symbol].append(branch)
#This line is to search the index of branch object and expand by them
self.explore_branches(tree, root, new_tree[last_symbol]
[new_tree[last_symbol].index(branch)])
else:
self.explore_branches(tree,root,new_tree)
The functions explore_branches()
call recursively to explore tree from left and right (if exist), and is_operator()
return true if the given string is one logic operator, for example & or |.函数
explore_branches()
递归调用以从左到右探索树(如果存在),如果给定字符串是一个逻辑运算符,例如 & 或 |,则is_operator()
返回 true。
Any other idea of how can I do this?关于我该怎么做的任何其他想法?
Thanks you in advance.提前谢谢你。
The only touchy case is the negation.唯一敏感的情况是否定。 Apart from it, one can simply write your algo or similar such as
除此之外,您可以简单地编写您的算法或类似的东西,例如
from functools import reduce
def op(tree):
return 0 if type(tree)!=list else tree[0]
def bin_to_n(tree):
if op(tree)==0:
return tree
op_tree = tree[0]
out = [op_tree]
for node in tree[1:]:
flat_node = bin_to_n(node)
if op(node) != op_tree:
out.append(flat_node)
else:
out += flat_node[1:]
return out
Now regarding the negation.现在关于否定。 The failing case of above algorithm is when flattening
-(-(1))
which gives -1
instead of 1
上述算法的失败情况是在展平
-(-(1))
时给出-1
而不是1
< if op(node) != op_tree
---
> if op(node) != op_tree or op(node)=="-"
meaning that if a "minus" is found, you never "concatenate" it.这意味着如果找到“减号”,您永远不会“连接”它。 Thus this lets
-(-(1))
as is.因此,这让
-(-(1))
保持原样。
Now we can simplify more but those simplifications could have been done beforehand on the input list.现在我们可以进行更多的简化,但这些简化可以事先在输入列表中完成。 So it "semantically" changes the tree (even though evaluation stays identical).
所以它“语义上”改变了树(即使评估保持不变)。
op_tree = tree[0]
> if op_tree == '-' and op(tree[1]) == '-':
> return bin_to_n2(tree[1][1])
out = [op_tree]
#really invert according to demorgan's law
def bin_to_n3(tree, negate=False):
if op(tree)==0:
return tree
op_tree = tree[0]
if negate:
if op_tree == '-':
#double neg, skip the node
return bin_to_n3(tree[1])
#demorgan
out = [ '+' if op_tree == '*' else '*' ]
for node in tree[1:]:
flat_node = bin_to_n3(node, True)
#notice that since we modify the operators we have
#to take the operator of the resulting tree
if op(flat_node) != op_tree:
out.append(flat_node)
else:
out += flat_node[1:]
return out
if op_tree == '-' and op(op_tree)==0:
#do not touch the leaf
return tree
#same code as above, not pun to factorize it
out = [op_tree]
for node in tree[1:]:
flat_node = bin_to_n3(node)
if op(flat_node) != op_tree:
out.append(flat_node)
else:
out += flat_node[1:]
return out
Below some random checks to ensure transformation keeps the tree's value intact下面进行一些随机检查以确保转换保持树的值不变
from functools import reduce
def op(tree):
return 0 if type(tree)!=list else tree[0]
def bin_to_n(tree):
if op(tree)==0:
return tree
op_tree = tree[0]
out = [op_tree]
for node in tree[1:]:
flat_node = bin_to_n(node)
if op(node) != op_tree or op(node)=='-':
out.append(flat_node)
else:
out += flat_node[1:]
return out
def bin_to_n2(tree):
if op(tree)==0:
return tree
op_tree = tree[0]
if op_tree == '-' and op(tree[1]) == '-':
return bin_to_n2(tree[1][1])
out = [op_tree]
for node in tree[1:]:
flat_node = bin_to_n2(node)
if op(node) != op_tree:
out.append(flat_node)
else:
out += flat_node[1:]
return out
#really invert according to demorgan's law
def bin_to_n3(tree, negate=False):
if op(tree)==0:
return tree
op_tree = tree[0]
if negate:
if op_tree == '-':
#double neg, skip the node
return bin_to_n3(tree[1])
#demorgan
out = [ '+' if op_tree == '*' else '*' ]
for node in tree[1:]:
flat_node = bin_to_n3(node, True)
#notice that since we modify the operators we have
#to take the operator of the resulting tree
if op(flat_node) != op_tree:
out.append(flat_node)
else:
out += flat_node[1:]
return out
if op_tree == '-' and op(op_tree)==0:
#do not touch the leaf
return tree
#same code as above, not pun to factorize it
out = [op_tree]
for node in tree[1:]:
flat_node = bin_to_n3(node)
if op(flat_node) != op_tree:
out.append(flat_node)
else:
out += flat_node[1:]
return out
def calc(tree):
if op(tree) == 0:
return tree
s = 0
subtree = tree[1:]
if op(tree)=='+':
s = reduce(lambda x,y: x or calc(y), subtree, False)
elif op(tree) == '-':
s = not calc(subtree[0])
else:
s = reduce(lambda x,y: x and calc(y), subtree, True)
return s
#adaptated from https://stackoverflow.com/questions/6881170/is-there-a-way-to-autogenerate-valid-arithmetic-expressions
def brute_check():
import random
random.seed(3)
def make_L(n=3):
def expr(depth):
if depth==1 or random.random()<1.0/(2**depth-1):
return random.choice([0,1])
if random.random()<0.25:
return ['-', expr(depth-1)]
return [random.choice(['+','*']), expr(depth-1), expr(depth-1)]
return expr(n)
for i in range(100):
L = make_L(n=10)
a = calc(L)
b = calc(bin_to_n(L))
c = calc(bin_to_n2(L))
d = calc(bin_to_n3(L))
if a != b:
print('discrepancy', L,bin_to_n(L), a, b)
if a != c:
print('discrepancy', L,bin_to_n2(L), a, c)
if a != d:
print('discrepancy', L,bin_to_n3(L), a, d)
brute_check()
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.