为了账号安全,请及时绑定邮箱和手机立即绑定

在python中生成元组的最快方法是什么:

在python中生成元组的最快方法是什么:

喵喔喔 2021-11-16 10:48:47
寻找使用标题中提到的模式生成元组的最快方法,即:(1.0, 0.0, 0.0, 2.0, 0.0, 0.0, ..., N, 0.0, 0.0)对于任意的正n表示方面:round(N) == N。
查看完整描述

3 回答

?
神不在的星期二

TA贡献1963条经验 获得超6个赞

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


查看完整回答
反对 回复 2021-11-16
?
德玛西亚99

TA贡献1770条经验 获得超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扁平化从而tuple直接构建最终结果。


虽然它确实涉及临时三tuple(与帕特里克的回答不同),但在 CPython 参考解释器上,它实际上根本不创建新tuple的;如果在请求下一个值时不存在对 的其他引用(并且每次都释放其引用),zip则优化为将tuple来自上一个结果的 重用于新结果。tuplechain.from_iterable


为了与其他答案进行比较,微ipython基准测试为N150:


>>> %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 的解决方案。


后来的更新:我最终设法找到了打败 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 ints 仅将它们转换为floats),使用tolist方法在不涉及 Python 迭代器的情况下numpy执行转换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)

这是在约20%,而蒂姆的解决方案运行时间进一步减少,但除非你做这样的很多,进口的成本numpy可能消除了节约。


查看完整回答
反对 回复 2021-11-16
?
摇曳的蔷薇

TA贡献1793条经验 获得超6个赞

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


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)


查看完整回答
反对 回复 2021-11-16
  • 3 回答
  • 0 关注
  • 175 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信