我想证明两个数据处理函数在功能上是等价的。为此,我为每个函数构建了一个抽象语法树,然后运行控制流的模拟,以构建最终输出的数据流图。
数据流图由(操作,操作数,操作数)的3元组表示,使得(a + b)* 2表示为:
('*',('+','a','b'),'2')
我的一些操作是可交换的,因此来自等效图的数据流可能是:
('*','2',('+','b','a'))
如何检查我的2个数据流图是否是同构的(即执行完全相同的操作)?
我的想法是尝试通过检测交换运算符并将其操作数按排序顺序(例如字典顺序)将每个数据流图转换为规范形式,尽管只要我保持一致,我就不认为顺序很重要)。然后我可以比较重新排序的元组以获得严格的相等。
对于我的图表,我认为这个算法应该足够,即使它不会发现像(a + b)+ c和a +(b + c)这样的东西之间的等价。
然而,即使是小图,我也遇到了效率问题。
例如,这个Python代码构建了一个简单的数据流图,只有28个操作,但是需要花费8秒来比较元组(而较大的图形会呈指数级变差):
from time import time
def make_dataflow_graph(n):
A='y'
for i in range(n):
A=('+',A,A)
return A
G1 = make_dataflow_graph(28)
G2 = make_dataflow_graph(28)
t0 = time()
print G1<G2
print time()-t0
我认为问题在于Python以递归方式进行比较,因此浪费了大量时间一次又一次地比较相同的节点。
是否有一些Pythonic方法可以使元组比较更有效,或者是否有更好的算法来比较我的数据流图?
答案 0 :(得分:2)
我不能说它是否是惯用的Python,但你可以从Lisp中借用一个名为hash consing的想法。在一个句子中,我们的想法是使用哈希表将子对象共享增加到最大值,允许通过身份而不是深度来比较元组。
这样的事情:
canonical_ids = set()
canonical_objs = {}
canonical_tuples = {}
def canonical_object(obj):
if id(obj) in canonical_ids:
return obj
if isinstance(obj, tuple):
operator, left_operand, right_operand = map(canonical_object, obj)
if operator in {'+', '*'} and id(left_operand) > id(right_operand):
left_operand, right_operand = right_operand, left_operand
obj = operator, left_operand, right_operand
canon_obj = canonical_tuples.setdefault(tuple(map(id, obj)), obj)
else:
canon_obj = canonical_objs.setdefault(obj, obj)
canonical_ids.add(id(canon_obj))
return canon_obj
然后你可以做像
这样的事情canonical_object(obj1) is canonical_object(obj2)