oil_changes
正如其他答案所指出的那样,除非您的清单非常长,否则您不必担心。但是,作为“基于流”计算的粉丝,我认为有趣的是指出它提供了在 O(1) 空间(当然还有 O(N) 时间!-)itertools
中计算价值所需的所有工具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
以便我们可以跟踪我们通过循环的次数,即我们正在迭代的可迭代对象的长度on(我们需要在最终表达式中加上 +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!”……“学习艺术,然后把它放在一边”——我认为这句话在这里很适用:学习是件好事解决非常困难的问题的高级和复杂的方法,以防万一你遇到它们,但是在更常见的简单、普通问题的情况下,你需要采取简单和直接的方式——不要应用最有可能获胜的高级解决方案不需要!-)