此特定代码中二次复杂性背后的推理

Reasoning behind Quadratic Complexity in this particular code

我是参考这段代码提问的:

array = [-37,-36,-19,-99,29,20,3,-7,-64,84,36,62,26,-76,55,-24,84,49,-65,41]
print sum(i for i in array if array.index(i) % 2 == 0)*array[-1] if array != [] else 0

你可以在这里看到:Python code for sum with condition
这是 Quadratic 是因为括号内的 for 循环后跟 if 语句吗?

此代码是否由同一页面上的另一个人提出 - array[-1] * sum(array[::2]) 没有二次行为?
我认为,它又是一个二次方,因为它必须进行遍历,而且这也是一个交替的遍历。

提前致谢。

是的,正是 array.index 使它成为二次方。

让我们先把无关紧要的东西删掉。条件与复杂性推理无关(我们将有 array != [] 并且该检查需要 O(1) 时间)。 array[-1] 的乘法也是如此。所以你剩下:

sum(i for i in array if array.index(i) % 2 == 0)

现在内部是一个生成器,它将扩展为一个匿名函数循环遍历 array 并产生一堆值,每次迭代最多一个。 sum 函数接收这些值并将它们相加。

令人困惑的是发电机的实际工作方式。它实际上是由 运行 生成器与来自消费者的代码混合而成的。这导致复杂度是生成器和消费者的复杂度之和(即sum)。现在 sum 具有线性复杂度(它应该有,如果我写它就会)。

因此对于生成器,它循环遍历数组,但对于数组中的每个元素,它调用 array.index,这具有 O(N) 的复杂性。

要解决此问题,您可以使用 enumerate 来避免必须调用 array.index(i),这可能是也可能不是您想要做的,因为 array.index(i) returns元素为 i 的第一个索引可能不是您实际找到 i:

的索引
sum(i for idx, i in enumerate(array) if idx % 2 == 0)

要查看差异,请考虑列表 array = [0, 1, 2, 2],第一个解决方案应将此总结为 4,因为 array.index(2) == 2 因此它还会添加第二个 2。然而,后来的解决方案将加起来为 2,因为 enumerate 将枚举 array 中的元素,产生对 (0,0)(1,1)(2,2)(3,2) - 其中第一个组件是实际索引,而第二个组件是实际元素。这里省略了第二个 2 因为它实际上是从索引 3.

得到的

第一个解决方案确实是二次的:当您调用 array.index 时,您基本上每次都会在 array 上再次迭代,因此它的行为就像一个嵌入式循环。

第二种解决方案只遍历列表一次,跳过每个奇数索引。