正如其他答案所指出的,除非您的
oil_changes
列表非常长,否则您不需要担心。然而,作为一名“基于流”的计算机爱好者,我认为值得一提的是,无论N有多大(即
len(next_oil)
),
itertools
都提供了计算您的
next_oil
值所需的所有工具,并且可以在O(1)空间(当然也是O(N)时间!)内完成。
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从左到右推进其参数可迭代对象,因此在意识到b已经结束之前,它已经将a向前推进了一次!这没关系,因为Python循环变量并不限于循环本身的范围——在循环之后,j仍然具有最后通过在izip放弃之前推进b而提取的值(就像thelen仍然具有最后一个计数值一样)。在最终表达式中,我仍然将该值命名为last_one,而不是直接使用j,因为我认为这样更清晰、更易读。
所以这就是它——我希望它是有益的!虽然对于你提出的这个特定问题的解决方案来说,它几乎肯定是过度设计的。我们意大利人有一句古老的谚语——“学艺术,把它放在一边!”——我认为它在这里非常适用:学习高级和复杂的方法来解决非常困难的问题是一件好事,以防万一你遇到了这些问题,但是对于所有这些,你需要在简单、直接的常见问题上去寻找简单的解决方案,而不是应用可能不需要的高级解决方案!