此特定代码中二次复杂性背后的推理
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
上再次迭代,因此它的行为就像一个嵌入式循环。
第二种解决方案只遍历列表一次,跳过每个奇数索引。
我是参考这段代码提问的:
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
上再次迭代,因此它的行为就像一个嵌入式循环。
第二种解决方案只遍历列表一次,跳过每个奇数索引。