正如其他答案所指出的,除非您的 oil_changes 列表非常长,否则您不必担心。但是,作为“基于流”计算的粉丝,我认为有趣的是,itertools 提供了在 O(1) 空间(和 O(N) 时间)计算 next_oil 值所需的所有工具当然!-) 无论 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)
我们不是从列表中获取切片,而是在其上获取一个迭代器,对其进行 tee(制作两个可独立推进的克隆),然后推进一次其中一个克隆,b。 tee 占用空间 O(x),其中 x 是各个克隆的进步之间的最大绝对差;在这里,两个克隆的进度最多相差1,所以空间需求显然是O(1)。
izip 对两个稍微歪斜的克隆迭代器进行一次一个“压缩”,我们将其修饰为enumerate,以便我们可以跟踪我们通过循环的次数,即我们正在迭代的可迭代对象的长度(我们需要在最终表达式中加上 +1,因为enumerate 从 0 开始!-)。我们使用简单的+= 计算总和,这对于数字来说很好(sum 更好,但它不会跟踪长度!-)。
在循环之后使用last_one = a.next() 很诱人,但这不会起作用,因为a 实际上已经用尽了——izip 从左到右推进它的参数迭代,所以它之前最后一次推进了a它意识到b 结束了!-)。没关系,因为 Python 循环变量的范围不限于循环本身——在循环之后,j 仍然具有在izip 放弃之前通过推进b 最后提取的值(就像thelen仍然有enumerate 返回的最后一个计数值)。我仍然将值命名为last_one,而不是在最终表达式中直接使用j,因为我认为它更清晰易读。
就是这样--我希望它具有指导意义!-)--尽管对于您这次提出的具体问题的解决方案,它几乎可以肯定是矫枉过正。我们意大利人有一句古老的谚语——“Impara l'Arte, e mettila da parte!”……“学习艺术,然后把它放在一边”——我认为这句话在这里很适用:学习是件好事解决非常困难的问题的高级和复杂的方法,以防万一你遇到它们,但是在更常见的简单、普通问题的情况下,你需要采取简单和直接的方式——不要应用最有可能获胜的高级解决方案'不需要!-)