As other answers pointed out, you don't really need to worry unless your oil_changes
list is extremely long. However, as a fan of "stream-based" computing, I think it's interesting to point out that itertools
offers all the tools you need to compute your next_oil
value in O(1) space (and O(N) time of course!-) no matter how big N, that is, len(next_oil)
, gets.
izip
per se is insufficient, because it only reduces a bit the multiplicative constant but leaves your space demands as O(N). The key idea to bring those demands down to O(1) is to pair izip
with tee
-- and avoiding the list comprehension, which would be O(N) in space anyway, in favor of a good simple old-fashioned loop!-). Here comes:
it = iter(oil_changes)
a, b = itertools.tee(it)
b.next()
thesum = 0
for thelen, (i, j) in enumerate(itertools.izip(a, b)):
thesum += j - i
last_one = j
next_oil = last_one + thesum / (thelen + 1)
Instead of taking slices from the list, we take an iterator on it, tee it (making two independently advanceable clones thereof), and advance, once, one of the clones, b
. tee
takes space O(x) where x is the maximum absolute difference between the advancement of the various clones; here, the two clones' advancement only differs by 1 at most, so the space requirement is clearly O(1).
izip
makes a one-at-a-time "zipping" of the two slightly-askew clone iterators, and we dress it up in enumerate
so we can track how many times we go through the loop, i.e. the length of the iterable we're iterating on (we need the +1 in the final expression, because enumerate
starts from 0!-). We compute the sum with a simple +=
, which is fine for numbers (sum
is even better, but it wouldn't track the length!-).
It's tempting after the loop to use last_one = a.next()
, but that would not work because a
is actually exhausted -- izip
advances its argument iterables left to right, so it has advanced a
one last time before it realizes b
is over!-). That's OK, because Python loop variables are NOT limited in scope to the loop itself -- after the loop, j
still has the value that was last extracted by advancing b
before izip
gave up (just like thelen
still has the last count value returned by enumerate
). I'm still naming the value last_one
rather than using j
directly in the final expression, because I think it's clearer and more readable.
So there it is -- I hope it was instructive!-) -- although for the solution of the specific problem that you posed this time, it's almost certain to be overkill. We Italians have an ancient proverb -- "Impara l'Arte, e mettila da parte!"... "Learn the Art, and then set it aside" -- which I think is quite applicable here: it's a good thing to learn advanced and sophisticated ways to solve very hard problems, in case you ever meet them, but for all that you need to go for simplicity and directness in the vastly more common case of simple, ordinary problems -- not apply advanced solutions that most likely won't be needed!-)