寻找用标题中提到的模式生成元组的最快方法,即:
(1.0, 0.0, 0.0, 2.0, 0.0, 0.0, ..., N, 0.0, 0.0)
对于任何尊重的积极N:round(N) == N
。
答案 0 :(得分:5)
谁知道? ;-)在CPython中,“诀窍”通常涉及避免显式的Python级别的循环,以及避免二次时间的链接。这是一种方法:
def gentup(N):
NI = round(N)
assert N == NI
result = [0.] * (3 * NI)
result[::3] = map(float, range(1, NI + 1))
return tuple(result)
然后,例如
>>> gentup(4)
(1.0, 0.0, 0.0, 2.0, 0.0, 0.0, 3.0, 0.0, 0.0, 4.0, 0.0, 0.0)
然后,所有实际工作都以“ C速度”运行,甚至float
也只被查找一次(尽管被调用 round(N)
次)。
答案 1 :(得分:3)
最快可以立即使用的方法是使用itertools
函数将所有工作推送到C层:
from itertools import chain, repeat
def make_tuple(N):
return return tuple(chain.from_iterable(zip(map(float, range(1, round(N)+1)), repeat(0.0), repeat(0.0))))
repeat
设置为零,map(float, range(1, round(N)+1))
设置为非零值,zip
将它们组合在一起将生成三个tuple
,它们被chain.from_iterable
展平,因此{ {1}}直接构造最终结果。
尽管它确实涉及临时的三个tuple
(与Patrick's answer不同),但在CPython参考解释器上,它实际上根本没有创建新的tuple
。如果在请求下一个值时(并且释放tuple
时不存在对zip
的其他引用,则tuple
已被优化为从新结果的最新结果中重用tuple
每次参考)。
要与其他答案进行比较,请使用chain.from_iterable
中的ipython
的微基准测试:
N
我自己尝试了一些其他方法,类似于上面使用listcomps和genexprs的方法,但没有一个方法低于40 µs,因此我不必费心张贴。
Tim Peter's solution无疑是迄今为止发布最快的,而且不太可能得到改善。与he notes一样,它会占用更多的内存,因为在峰值内存使用情况下,它需要存储整个结果>>> %timeit -r5 make_tuple(150)
28.1 µs ± 1.67 µs per loop (mean ± std. dev. of 5 runs, 10000 loops each)
>>> %timeit -r5 make_tuple_tim_peters(150)
17.1 µs ± 52 ns per loop (mean ± std. dev. of 5 runs, 100000 loops each)
>>> %timeit -r5 make_tuple_julien(150)
154 µs ± 1.85 µs per loop (mean ± std. dev. of 5 runs, 10000 loops each)
>>> %timeit -r5 tuple(values_patrick_haugh(150)) # Modified to convert to float properly
40.7 µs ± 1.29 µs per loop (mean ± std. dev. of 5 runs, 10000 loops each)
和临时tuple
(尽管每个大小都应精确确定,并且不存在过度分配) ),这意味着容器的峰值内存大约是“所需”内存的两倍。我的确需要list
来进行总体分配(因为它不知道结果的大小),这在当前的CPython中,作为实现细节,意味着过度分配约25%。一笔节省,但不是一笔很大的钱;如果性能很重要,我几乎总是选择蒂姆的解决方案。
后来更新:我确实设法找到了一些超出Tim答案的方法,但是只能依靠tuple
,而增量的改进是微不足道的:
numpy
基本上与Tim的回答相同,只是使用from numpy import arange, zeros
def make_tuple_numpy(N):
ret = zeros(3*round(N))
ret[::3] = arange(1., N+1.)
return tuple(ret.tolist())
来处理原始C基本类型(例如numpy
直接以浮点形式生成范围,而不会创建一堆Python np.arange
仅将它们转换为int
s),使用float
方法使tolist
执行到numpy
的转换,而无需使用Python迭代器,然后包装list
构造函数(特殊情况为tuple
,因此也不再涉及迭代器)。即使有所有这些,优点也很微不足道:
list
与Tim的解决方案相比,它的运行时间进一步减少了约20%,但除非您这样做很多,否则导入>>> %timeit -r5 make_tuple_numpy(150)
13.8 µs ± 158 ns per loop (mean ± std. dev. of 5 runs, 100000 loops each)
的成本可能会节省下来。< / p>
答案 2 :(得分:2)
这是一种不会生成任何临时元组的方法。
def values(N):
nums = range(1, N+1)
for n in nums:
yield n
yield 0
yield 0
print(tuple(values(5)))
# (1, 0, 0, 2, 0, 0, 3, 0, 0, 4, 0, 0, 5, 0, 0)