我有三个列表:old,new和ignore。旧的和新的是字符串列表。 ignore是一个索引列表,如果它们不匹配则应该忽略。目标是创建一个不同且不被忽略的指数列表。
旧的和新的可能包含不同数量的元素。如果旧旧之间的大小存在差异,则应将差异标记为不匹配(除非忽略)。
我目前的职能如下:
def CompareFields( old, new, ignore ):
if ( old == None ):
if ( new == None ):
return [];
else:
return xrange( len(new) )
elif ( new == None ):
return xrange( len(old) )
oldPadded = itertools.chain( old, itertools.repeat(None) )
newPadded = itertools.chain( new, itertools.repeat(None) )
comparisonIterator = itertools.izip( xrange( max( len(old ) , len( new ) ) ), oldPadded, newPadded )
changedItems = [ i for i,lhs,rhs in comparisonIterator if lhs != rhs and i not in ignore ]
return changedItems
我尝试的各种选项的时间为100,000次运行提供以下时间:
[4, 9]
CompareFields: 6.083546
set([9, 4])
Set based: 12.594869
[4, 9]
Function using yield: 13.063725
[4, 9]
Use a (precomputed) ignore bitmap: 7.009405
[4, 9]
Use a precomputed ignore bitmap and give a limit to itertools.repeat(): 8.297951
[4, 9]
Use precomputed ignore bitmap, limit padding and itertools.starmap()/operator.ne(): 11.868687
[4, 9]
Naive implementation: 7.438201
我的python的最新版本是2.6(它是RHEL5.5)。我目前正在编写Pypy以试一试。
那么有没有人有任何想法如何让这个功能更快地运行?是否值得使用Cython?
如果我无法让它运行得更快,我会考虑用C ++或Java重写整个工具。
修改
好的我定时了各种答案:
[4, 9]
CompareFields: 5.808944
[4, 9]
agf's itertools answer: 4.550836
set([9, 4])
agf's set based answer, but replaced list expression with a set to avoid duplicates: 9.149389
agf's set based answer, as described in answer: about 8 seconds
lucho's set based answer: 10.682579
因此,itertools似乎是现在的方法。令人惊讶的是,基于集合的解决方案执行得如此糟糕。虽然使用lambda的速度比较慢我并不感到惊讶。
编辑: Java基准测试 天真的实现,if语句太多了:128ms
答案 0 :(得分:4)
对于这两种解决方案,您应该这样做:
ignore = set(ignore)
将为您提供恒定(平均)时间in
测试。
我认为这是您正在寻找的基于itertools
/ zip
的方法:
[i for i, (o, n) in enumerate(izip_longest(old, new))
if o != n and i not in ignore]
无需chain
/ repeat
填充 - 这就是izip_longest
的用途。 enumerate
也比xrange
更合适。
在Lucho的答案中,filter
/集差异方法的更多Pythonic(可能更快)版本:
[i for i, v in set(enumerate(new)).symmetric_difference(enumerate(old))
if i not in ignore]
filter
上的列表推导优先于map
或lambda
,如果您使用{{1},则无需将这两个列表转换为set
s }}方法而不是symmetric_difference
/ xor运算符。
答案 1 :(得分:1)
同时设置ignore
。
filter(lambda x: x[0] not in ignore, set(enumerate(new)) ^ set(enumerate(old)))
我打赌它会比你的复杂的非pythonic试验更快(如果你能测量它会很酷 - 我很好奇)。
答案 2 :(得分:1)
def findDiff(old, new, ignore):
ignore = set(ignore)
diff = []
(small, big) = (old, new) if len(old) < len(new) else (new, old)
diff.extend([i for i in xrange(0,len(small)) if i not in ignore and old[i] != new[i]])
diff.extend([i for i in xrange(len(small), len(big)) if i not in ignore])
return diff
对于快速函数,这假设所有高于最小列表长度的索引将被计为不同,并且仍然通过忽略进行检查。