我有一个sumranges()函数,它对元组元组中找到的所有连续数字的范围求和。举例说明:
def sumranges(nums):
return sum([sum([1 for j in range(len(nums[i])) if
nums[i][j] == 0 or
nums[i][j - 1] + 1 != nums[i][j]]) for
i in range(len(nums))])
>>> nums = ((1, 2, 3, 4), (1, 5, 6), (19, 20, 24, 29, 400))
>>> print sumranges(nums)
7
如您所见,它返回元组内连续数字的范围数,即:len((1,2,3,4),(1),(5,6),(19,20) ),(24),(29),(400))= 7.元组总是有序的。
我的问题是我的sumranges()非常糟糕。我讨厌看着它。我现在只是迭代元组和每个子元素,如果数字不是(1 +前一个数字),则分配1,并总计总和。我觉得我错过了一个更简单的方法来实现我声明的目标。有没有人知道更多的pythonic方法呢?
编辑:我已经对迄今为止给出的所有答案进行了基准测试。感谢大家的回答。
基准测试代码如下,使用100K的样本大小:
from time import time
from random import randrange
nums = [sorted(list(set(randrange(1, 10) for i in range(10)))) for
j in range(100000)]
for func in sumranges, alex, matt, redglyph, ephemient, ferdinand:
start = time()
result = func(nums)
end = time()
print ', '.join([func.__name__, str(result), str(end - start) + ' s'])
结果如下。显示实际答案以验证所有函数是否返回正确答案:
sumranges, 250281, 0.54171204567 s
alex, 250281, 0.531121015549 s
matt, 250281, 0.843333005905 s
redglyph, 250281, 0.366822004318 s
ephemient, 250281, 0.805964946747 s
ferdinand, 250281, 0.405596971512 s
RedGlyph确实在速度方面有优势,但最简单的答案可能是费迪南德,而且可能是大多数蟒蛇的胜利。
答案 0 :(得分:14)
我的2美分:
>>> sum(len(set(x - i for i, x in enumerate(t))) for t in nums)
7
它与Alex' post中描述的基本相同,但使用set
代替itertools.groupby
,导致表达式缩短。由于set
是在C中实现的,并且一个集合的len()
在恒定时间内运行,因此这也应该非常快。
答案 1 :(得分:9)
考虑:
>>> nums = ((1, 2, 3, 4), (1, 5, 6), (19, 20, 24, 29, 400))
>>> flat = [[(x - i) for i, x in enumerate(tu)] for tu in nums]
>>> print flat
[[1, 1, 1, 1], [1, 4, 4], [19, 19, 22, 26, 396]]
>>> import itertools
>>> print sum(1 for tu in flat for _ in itertools.groupby(tu))
7
>>>
我们通过从值中减去索引来“平滑”所关注的“增加的斜坡”,将它们变成相同值的连续“运行”;然后我们确定并且可以与珍贵的itertools.groupby
“运行”。这似乎是一个非常优雅(快速)的解决方案。
答案 2 :(得分:7)
只是为了展示更接近原始代码的内容:
def sumranges(nums):
return sum( (1 for i in nums
for j, v in enumerate(i)
if j == 0 or v != i[j-1] + 1) )
这里的想法是:
但我的例子仍然需要剩下的sum()
。
答案 3 :(得分:1)
这是我的尝试:
def ranges(ls):
for l in ls:
consec = False
for (a,b) in zip(l, l[1:]+(None,)):
if b == a+1:
consec = True
if b is not None and b != a+1:
consec = False
if consec:
yield 1
'''
>>> nums = ((1, 2, 3, 4), (1, 5, 6), (19, 20, 24, 29, 400))
>>> print sum(ranges(nums))
7
'''
它成对地查看数字,检查它们是否是连续的对(除非它在列表的最后一个元素处)。每次有一对连续的数字,它就会产生1。
答案 4 :(得分:0)
这可能会以更紧凑的形式组合在一起,但我认为清晰度会受到影响:
def pairs(seq):
for i in range(1,len(seq)):
yield (seq[i-1], seq[i])
def isadjacent(pair):
return pair[0]+1 == pair[1]
def sumrange(seq):
return 1 + sum([1 for pair in pairs(seq) if not isadjacent(pair)])
def sumranges(nums):
return sum([sumrange(seq) for seq in nums])
nums = ((1, 2, 3, 4), (1, 5, 6), (19, 20, 24, 29, 400))
print sumranges(nums) # prints 7
答案 5 :(得分:0)
如果你有一个IntervalSet class,你可能会做得更好,因为那时你会扫描你的范围来构建你的IntervalSet,然后只使用集合成员的数量。
有些任务并不总是适合整洁的代码,特别是如果你需要编写性能代码。
答案 6 :(得分:0)
有一个公式,前n个数的和,1 + 2 + ... + n = n(n + 1)/ 2。那么如果你想得到i-j的总和那么它是(j(j + 1)/ 2) - (i(i + 1)/ 2)这个我肯定会简化,但你可以解决这个问题。它可能不是pythonic,但它是我会使用的。