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)?
Looking for the fastest way of generating a tuple with the pattern mentioned in the title, i.e:
(1.0, 0.0, 0.0, 2.0, 0.0, 0.0, ..., N, 0.0, 0.0)
for any positive N that respects:
round(N) == N.
Fastest I can come up with off-hand is using
itertools functions to push all the work to the C layer:
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 makes the zeroes,
map(float, range(1, round(N)+1)) makes the non-zero values,
ziping them together makes three-
chain.from_iterable flattens so
tuple constructs the final result directly.
While it does involve temporary three-
tuples (unlike Patrick's answer), on the CPython reference interpreter it doesn't actually create new
tuples at all;
zip is optimized to reuse the
tuple from the last result for the new result if no other references to the
tuple exist when the next value is requested (and
chain.from_iterable is releasing its reference each time).
For comparison with other answers,
ipython microbenchmarks for
N of 150:
>>> %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)
I tried a few other approaches similar to my own approach above with listcomps and genexprs myself, but none of them got below 40 µs, so I didn't bother to post them.
Tim Peter's solution is definitely the fastest posted so far, and unlikely to be bettered. As he notes, it's a little more memory hungry, since at peak memory usage it needs storage for the whole result
tuple and the temporary
list (though each should be exactly sized, with no overallocation), meaning the peak memory for the containers is roughly twice what is "needed". Mine does require
tuple to overallocate as it goes (since it doesn't know how large the result will be), which in current CPython, as an implementation detail, means overallocation of around 25%. A savings, but not a significant one; if performance mattered, I'd almost always go with Tim's solution.
Later update: I did eventually manage to find something that beats Tim's answer, but only by resorting to
numpy, and the incremental improvement is pretty trivial:
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())
It's basically the same as Tim's answer, it just uses
numpy to do the work with raw C primitive types in bulk (e.g.
np.arange directly produces a range in floating point form without creating a bunch of Python
ints only to convert them to
floats), uses the
tolist method to have
numpy perform the conversion to
list without a Python iterator getting involved, then wraps in the
tuple constructor (which special cases
list, so again no iterator involvement). Even with all that, the advantage is pretty trivial:
>>> %timeit -r5 make_tuple_numpy(150) 13.8 µs ± 158 ns per loop (mean ± std. dev. of 5 runs, 100000 loops each)
It's a further reduction in runtime of ~20% vs. Tim's solution, but unless you're doing this a lot, the cost of importing
numpy probably eliminates the savings.
Python Operator - Types of Operators in Python, Python divides the operators in the following groups: Arithmetic operators; Assignment operators; Comparison operators; Logical operators; Identity operators� The Python web site provides a Python Package Index (also known as the Cheese Shop, a reference to the Monty Python script of that name). There is also a search page for a number of sources of Python-related information. Failing that, just Google for a phrase including
Who knows? ;-) In CPython, "the trick" usually involves avoiding explicit Python-level loops, and in avoiding quadratic-time catenations. Here's one way:
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)
All the real work runs "at C speed" then, and even
float is looked up only once (despite being invoked
Python Operators, Many of the examples in this manual, even those entered at the interactive prompt, include comments. Comments in Python start with the hash character, # , and� Python is an interpreted, high-level, general-purpose programming language.Created by Guido van Rossum and first released in 1991, Python's design philosophy emphasizes code readability with its notable use of significant whitespace.
Here's a way that doesn't generate any temporary tuples.
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)
3. An Informal Introduction to Python — Python 3.8.5 documentation, For constructing a list, a set or a dictionary Python provides special syntax called “displays”, each of them in two flavors: either the container contents are listed� Note that Python 3.5.6 cannot be used on Windows XP or earlier. No files for this release. Python 3.4.9 - Aug. 2, 2018. No files for this release. Python 3.7.0 - June 27, 2018. Note that Python 3.7.0 cannot be used on Windows XP or earlier. Download Windows help file; Download Windows x86-64 embeddable zip file; Download Windows x86-64
6. Expressions — Python 3.8.5 documentation, Many of the examples in this manual, even those entered at the interactive prompt, include comments. Comments in Python start with the hash� Python is a programming language. Python can be used on a server to create web applications. Start learning Python now »
3. An Informal Introduction to Python — Python 2.7.18 documentation, This was added to Python at the request of the developers of Numerical Python, which uses the third argument extensively. However, Python's built-in list, tuple,� Python 3.7.0. Release Date: June 27, 2018 Python 3.7.0 was the initial feature release of Python 3.7.
15 Extended Slices, Comments in Python start with the hash character, "#", and extend to the end of the physical line. A comment may appear at the start of a line or following� Python Bitwise Operators. Bitwise operator works on bits and performs bit by bit operation. Assume if a = 60; and b = 13; Now in the binary format their values will be 0011 1100 and 0000 1101 respectively.
- Unless, of course, numpy is required by the enclosing code. :-)
- Ooh, nice. Beats my answer, which was already pretty good (yours runs in about 60% of the time of mine).
- Ya, "the problem" with itertools-heavy approaches to things like this is that - alas - it takes actual time to resume an iterator over and over. It adds up. The thing I posted gets all the zeroes essentially for free, and they make up 2/3rds of the result. Turns out it's cheaper to mutate a list to replace the other 1/3rd, then copy the whole thing into a distinct tuple. Yours is more frugal with memory, though, which can matter if
Nis on the order of billions ;-)
- Yeah, I didn't doubt it. I just sometimes forget a one-liner isn't always the best solution. :-) As soon as I saw your code I suspected it would beat mine, precisely because of the overhead (however tiny) of advancing the
zipthe results. I added precise numbers to my answer just for comparison.
- Minor tweak required to match OP: Change
nums = range(1, N+1)to
nums = map(float, range(1, round(N)+1))so it rounds the input and converts the values to
float, and change each
yield 0.0to make the zeroes