Как и другие ответы отметили, вы действительно не нужно беспокоиться, если ваш oil_changes
список не является очень долго.Однако, как поклонник «потоковых» вычислений, я думаю, интересно отметить, что itertools
предлагает все инструменты, необходимые для вычисления значения next_oil
в O (1) пространстве (и времени O (N), конечно же! -) независимо от того, насколько большой N, то есть len(next_oil)
, получает.
izip
сам по себе недостаточен, поскольку он лишь немного уменьшает мультипликативную константу, но оставляет пространство в пространстве как O (N). Ключевая идея довести эти требования до O (1) - это соединить izip
с tee
- и избежать понимания списка, которое было бы O (N) в пространстве, в любом случае, в пользу простой простой старомодной петли! -). Вот приходит:
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)
Вместо того, чтобы кусочки из списка, мы берем итератор на него, тройник его (что делает их два независимо выдвигаться клоны), и вперед, один раз, один из клонов, b
. tee
занимает пространство O (x), где x - максимальная абсолютная разница между продвижением различных клонов; здесь продвижение двух клонов отличается только не более чем на 1, поэтому требование пространства очевидно O (1).
izip
делает один-на-время «проносясь» из двух слегка косо клонов итераторов, и мы одеваем его в enumerate
, чтобы мы могли отслеживать, сколько раз мы проходим через петлю, то есть длина итеративный, который мы итерируем (нам нужен +1 в конечном выражении, потому что enumerate
начинается с 0! -). Мы вычисляем сумму простым +=
, что отлично подходит для чисел (sum
еще лучше, но он не отслеживал длину! -).
Заманчив после того, как петля использовать last_one = a.next()
, но это не будет работать, потому что a
фактически исчерпало - izip
продвигает свой аргумент итерируемых слева направо, так что он выдвинул a
один последний раз, прежде чем он понимает b
закончились! -). Все в порядке, потому что переменные цикла Python НЕ ограничены в области самого цикла. После цикла j
по-прежнему имеет значение, которое было в последний раз извлечено путем продвижения b
до того, как izip
сдался (как и thelen
, все еще имеет последнее значение счета, возвращаемое enumerate
). Я все еще назову значение last_one
, а не используя j
непосредственно в конечном выражении, потому что я думаю, что это более понятно и читаемо.
Так вот, это - я надеюсь, что это было поучительно!) - хотя для решения конкретной проблемы, которую вы поставили на этот раз, почти наверняка будет излишним. У итальянцев есть древняя пословица - «Impara l'Arte, e mettila da parte!» ... «Изучите искусство, а затем отложите его» - что, я думаю, здесь вполне применимо: хорошо учиться передовые и сложные способы решения очень сложных проблем, если вы когда-либо встречаете их, но для всего, что вам нужно для простоты и прямоты в гораздо более распространенном случае простых обычных проблем - не применять передовые решения, которые, скорее всего, выиграли Не нужно! -)
Всегда есть компромисс. Ваш код является самым медленным среди ответов здесь в соответствии с timeit. –
Для коротких списков это может быть; попробуйте различные подходы с несколькими миллионами элементов в oil_changes, скажем ... ;-) –
Возможно, стоит отметить, что конструкция tee + next + izip обычно абстрагируется как попарно(), подробности см. в документации itertools. С другой стороны, использование переменной for вне цикла (хотя принято как интерпретатором, так и сообществом Python) является IMHO довольно уродливым. Тем не менее, более функциональный (в смысле FP) решение будет труднее для новичков понять и, вероятно, не рассматривается как «Pythonic» (так как он будет использовать так ненавистный сокращение/foldl). – tokland