我知道树是一个研究得很好的结构。
我正在编写一个程序,随机生成许多表达式树,然后通过适应性属性进行排序和选择。
我有一个类MakeTreeInOrder(),它将树变成'eval'可以评估的字符串。
但它被多次调用,应该根据时间进行优化。
下面的是一个构建树的函数,该树添加连续的数字以用作测试。
我想知道是否有一种优化的方法来评估树结构中的表达式。我想那个
它使用了很多,有些人已经完成了这个。
import itertools
from collections import namedtuple
#Further developing Torsten Marek's second suggestion
KS = itertools.count()
Node = namedtuple("Node", ["cargo", "args"])
def build_nodes (depth = 5):
if (depth <= 0):
this_node = Node((str(KS.next())), [None, None])
return this_node
else:
this_node = Node('+', [])
this_node.args.extend(
build_nodes(depth = depth - (i + 1))
for i in range(2))
return this_node
以下是我认为可以快得多的代码。我希望有一些想法。
class MakeTreeInOrder(object):
def __init__(self, node):
object.__init__(self)
self.node = node
self.str = ''
def makeit(self, nnode = ''):
if nnode == '':
nnode = self.node
if nnode == None: return
self.str +='('
self.makeit(nnode.args[0])
self.str += nnode.cargo
self.makeit(nnode.args[1])
self.str+=')'
return self.str
def Main():
this_tree = build_nodes()
expression_generator = MakeTreeInOrder(this_tree)
this_expression = expression_generator.makeit()
print this_expression
print eval(this_expression)
if __name__ == '__main__':
rresult = Main()
答案 0 :(得分:3)
在这里添加一些面向对象会使事情变得更简单。为树中的每个东西都有Node的子类,并使用'eval'方法来评估它们。
import random
class ArithmeticOperatorNode(object):
def __init__(self, operator, *args):
self.operator = operator
self.children = args
def eval(self):
if self.operator == '+':
return sum(x.eval() for x in self.children)
assert False, 'Unknown arithmetic operator ' + self.operator
def __str__(self):
return '(%s)' % (' ' + self.operator + ' ').join(str(x) for x in self.children)
class ConstantNode(object):
def __init__(self, constant):
self.constant = constant
def eval(self):
return self.constant
def __str__(self):
return str(self.constant)
def build_tree(n):
if n == 0:
return ConstantNode(random.randrange(100))
else:
left = build_tree(n - 1)
right = build_tree(n - 1)
return ArithmeticOperatorNode('+', left, right)
node = build_tree(5)
print node
print node.eval()
要评估树,只需在顶级节点上调用.eval()。
node = build_tree(5)
print node.eval()
我还添加了一个__str__
方法将树转换为字符串,以便您可以看到它如何推广到其他树函数。它目前只做'+',但希望很清楚如何将其扩展到全范围的算术运算。
答案 1 :(得分:1)
您的示例导入numpy和random,但从不使用它们。它还有一个“for i in range(2)”,没有身体。这显然不是有效的Python代码。
您没有定义“货物”和节点应包含的内容。似乎'cargo'是一个数字,因为它来自itertools.count()。next()。但这没有任何意义,因为你希望结果是一个可评估的Python字符串。
如果您正在对树进行一次性评估,那么最快的解决方案是直接就地评估它,但如果没有您正在使用的数据的实际示例,我无法展示示例
如果你想让它更快,那就进一步向上游看。为什么要生成树然后对其进行评估?你不能直接在当前生成树结构的代码中评估组件吗?如果您有“+”和“*”之类的运算符,那么请考虑使用operator.add和operator.mul,它可以直接处理数据,而无需使用中间步骤。
==更新==
这建立在Paul Hankin的回答之上。我所做的就是取消中间树结构,直接评估表达式。
def build_tree2(n):
if n == 0:
return random.randrange(100)
else:
left = build_tree2(n-1)
right = build_tree2(n-1)
return left+right
这比Paul的解决方案快5倍左右。
可能需要知道最佳解的实际树结构,或N的前k,其中k <&lt;&lt; N.如果是这种情况,那么如果您还跟踪用于生成结果的RNG状态,则可以事后重新生成这些树。例如:
def build_tree3(n, rng=random._inst):
state = rng.getstate()
return _build_tree3(n, rng.randrange), state
def _build_tree3(n, randrange):
if n == 0:
return randrange(100)
else:
left = _build_tree3(n-1, randrange)
right = _build_tree3(n-1, randrange)
return left+right
找到最佳值后,使用键重新生成树
# Build Paul's tree data structure given a specific RNG
def build_tree4(n, rng):
if n == 0:
return ConstantNode(rng.randrange(100))
else:
left = build_tree4(n-1, rng)
right = build_tree4(n-1, rng)
return ArithmeticOperatorNode("+", left, right)
# This is a O(n log(n)) way to get the best k.
# An O(k log(k)) time solution is possible.
rng = random.Random()
best_5 = sorted(build_tree3(8, rng) for i in range(10000))[:5]
for value, state in best_5:
rng.setstate(state)
tree = build_tree4(8, rng)
print tree.eval(), "should be", value
print " ", str(tree)[:50] + " ..."
这是我运行时的样子
10793 should be 10793
((((((((92 + 75) + (35 + 69)) + ((39 + 79) + (6 + ...
10814 should be 10814
((((((((50 + 63) + (6 + 21)) + ((75 + 98) + (76 + ...
10892 should be 10892
((((((((51 + 25) + (5 + 32)) + ((40 + 71) + (17 + ...
11070 should be 11070
((((((((7 + 83) + (77 + 56)) + ((16 + 29) + (2 + 1 ...
11125 should be 11125
((((((((69 + 80) + (11 + 64)) + ((33 + 21) + (95 + ...