我正在尝试确定整数列表是否连贯或“一次性”,这意味着相邻两个元素之间的差必须恰好为1,并且数字必须单调递增。我发现了一种巧妙的方法,可以根据列表中的数字减去元素在列表中的位置进行分组 - 当数字不连贯时,此差异会改变。显然,当序列不包含间隙或重复时应该有一个组。
明显胜出。对于小型列表(10^3个元素),它的速度约为
>>> l1 = [1, 2, 3, 4, 5, 6]
>>> l2 = [1, 2, 3, 4, 5, 7]
>>> l3 = [1, 2, 3, 4, 5, 5]
>>> l4 = [1, 2, 3, 4, 5, 4]
>>> l5 = [6, 5, 4, 3, 2, 1]
>>> def is_coherent(seq):
... return len(list(g for _, g in itertools.groupby(enumerate(seq), lambda (i,e): i-e))) == 1
...
>>> is_coherent(l1)
True
>>> is_coherent(l2)
False
>>> is_coherent(l3)
False
>>> is_coherent(l4)
False
>>> is_coherent(l5)
False
这个解决方案运行良好,但我个人认为,在问题的简单性方面,这个解决方案有点过于复杂。你能想出一个更清晰的方法来实现相同的功能而不显著增加代码长度吗?
编辑:答案摘要
从下面给出的答案中,解决方案为
def is_coherent(seq):
return seq == range(seq[0], seq[-1]+1)
明显胜出。对于小型列表(10^3个元素),它的速度约为
groupby
方法的10倍,且(在我的机器上)仍比下一个最好的方法(使用izip_longest
)快四倍。它的缩放行为最差,但即使对于具有10^8个元素的大型列表,它仍然比下一个最好的方法快两倍,这也是基于izip_longest
的解决方案。通过timeit
获得相关的时间信息:Testing is_coherent_groupby...
small/large/larger/verylarge duration: 8.27 s, 20.23 s, 20.22 s, 20.76 s
largest/smallest = 2.51
Testing is_coherent_npdiff...
small/large/larger/verylarge duration: 7.05 s, 15.81 s, 16.16 s, 15.94 s
largest/smallest = 2.26
Testing is_coherent_zip...
small/large/larger/verylarge duration: 5.74 s, 20.54 s, 21.69 s, 24.62 s
largest/smallest = 4.28
Testing is_coherent_izip_longest...
small/large/larger/verylarge duration: 4.20 s, 10.81 s, 10.76 s, 10.81 s
largest/smallest = 2.58
Testing is_coherent_all_xrange...
small/large/larger/verylarge duration: 6.52 s, 17.06 s, 17.44 s, 17.30 s
largest/smallest = 2.65
Testing is_coherent_range...
small/large/larger/verylarge duration: 0.96 s, 4.14 s, 4.48 s, 4.48 s
largest/smallest = 4.66
测试代码:
import itertools
import numpy as np
import timeit
setup = """
import numpy as np
def is_coherent_groupby(seq):
return len(list(g for _, g in itertools.groupby(enumerate(seq), lambda (i,e): i-e))) == 1
def is_coherent_npdiff(x):
return all(np.diff(x) == 1)
def is_coherent_zip(seq):
return all(x==y+1 for x, y in zip(seq[1:], seq))
def is_coherent_izip_longest(l):
return all(a==b for a, b in itertools.izip_longest(l, xrange(l[0], l[-1]+1)))
def is_coherent_all_xrange(l):
return all(l[i] + 1 == l[i+1] for i in xrange(len(l)-1))
def is_coherent_range(seq):
return seq == range(seq[0], seq[-1]+1)
small_list = range(10**3)
large_list = range(10**6)
larger_list = range(10**7)
very_large_list = range(10**8)
"""
fs = [
'is_coherent_groupby',
'is_coherent_npdiff',
'is_coherent_zip',
'is_coherent_izip_longest',
'is_coherent_all_xrange',
'is_coherent_range'
]
for n in fs:
print "Testing %s..." % n
t1 = timeit.timeit(
'%s(small_list)' % n,
setup,
number=40000
)
t2 = timeit.timeit(
'%s(large_list)' % n,
setup,
number=100
)
t3 = timeit.timeit(
'%s(larger_list)' % n,
setup,
number=10
)
t4 = timeit.timeit(
'%s(very_large_list)' % n,
setup,
number=1
)
print " small/large/larger/verylarge duration: %.2f s, %.2f s, %.2f s, %.2f s" % (t1, t2, t3, t4)
print " largest/smallest = %.2f" % (t4/t1)
测试机器:
- Linux 3.2.0 (Ubuntu 12.04)
- Python 2.7.3 (gcc 4.1.2)
- 使用Intel编译器构建的numpy 1.6.2
- CPU:E5-2650 @ 2.00GHz
- 24 GB内存
list == range(list[0], list[-1]+1)
? :) - Dr. Jan-Philip Gehrckelist()
来包装range()
;也许值得一提。 - Zero Piraeuslist == range(list[0], list[-1]+1)
安全地添加到您的答案中!(编辑:现在 Carl 已经做了...) - Dr. Jan-Philip Gehrcke