繁体   English   中英

在python中,生成元组的最快方法是什么:(1.0,0.0,0.0,2.0,0.0,0.0,…,N,0.0,0.0)?

[英]In python what's the fastest way to generate the tuple: (1.0, 0.0, 0.0, 2.0, 0.0, 0.0, …, N, 0.0, 0.0)?

寻找用标题中提到的模式生成元组的最快方法,即:

(1.0, 0.0, 0.0, 2.0, 0.0, 0.0, ..., N, 0.0, 0.0)

对于任何正数N而言: round(N) == N

谁知道? ;-)在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)调用 )。

itertools就可以想到的是使用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其中s chain.from_iterable变平,以便tuple直接构建的最终结果。

尽管它确实涉及临时的三元tuple (与Patrick的答案不同),但是在CPython参考解释器上,它实际上根本没有创建新的tuple 如果在请求下一个值时不存在对tuple其他引用(并且chain.from_iterable都释放其引用),则zip进行了优化,以将来自最后一个结果的tuple用于新结果。

为了与其他答案进行比较, ipythonN为150的ipython

>>> %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)

我自己尝试了一些其他方法,类似于上面使用listcomps和genexprs的方法,但没有一个低于40 µs,因此我不必费心发布它们。

蒂姆·彼得(Tim Peter)的解决方案无疑是迄今为止最快的解决方案 ,而且不太可能得到改善。 正如他指出的那样 ,这会占用更多的内存,因为在峰值内存使用情况下,它需要存储整个结果tuple和临时list (尽管每个都应精确调整大小,并且没有过度分配),这意味着容器的峰值内存大约是“需要”的两倍。 我的确需要tuple来进行整体分配(因为它不知道结果会有多大),这在当前的CPython中,作为实现细节,意味着过度分配约25%。 一笔节省,但不是一笔很大的钱; 如果性能很重要,我几乎总是选择蒂姆的解决方案。

后来的更新:我最终设法找到了一些超出Tim答案的方法,但是只能依靠numpy ,而增量的改进是微不足道的:

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())

这基本上与Tim的答案相同,只是使用numpy来批量处理原始C基本类型(例如, np.arange直接生成浮点形式的范围,而无需创建一堆Python int只是将它们转换为float s),使用tolist方法让numpy在不涉及Python迭代器的情况下执行到list的转换,然后包装在tuple构造函数中(特殊情况下为list ,因此也不再涉及迭代器)。 即使有所有这些,优点也很微不足道:

>>> %timeit -r5 make_tuple_numpy(150)
13.8 µs ± 158 ns per loop (mean ± std. dev. of 5 runs, 100000 loops each)

与Tim的解决方案相比,它的运行时间进一步减少了约20%,但是除非您执行很多操作 ,否则导入numpy的成本可能会消除节省的空间。

这是一种不会生成任何临时元组的方法。

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)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM