循环的运行时间分析

时间:2016-08-22 22:38:40

标签: python algorithm

考虑以下循环:

def func1(xs, ys, zs):
    for x in xs:
        for y in ys:
            pass

        for z in zs:
            pass

运行时间应为size of xs * (size of ys + size of zs),可以用O(X) * (O(Y) + O(Z))的大写符号编写。

def func2(xs, ys, zs):
    for y in ys:
        for x in xs:
            pass

    for z in zs:
        for x in xs:
            pass

此功能的运行时间应为size of ys * size of xs + size of zs * size of xs,这将在Big-O O(Y) * O(X) + O(Z) * O(X)中生成。

问题是,这些运行时间分析是否正确?如果是这样,这些功能的运行时间是否相等?因为来自算术x * (y + z) = x * y + x * z

ipython %timeit函数的结果显示我似乎错了。

In [8]: ys1 = range(1, 500)

In [9]: zs1 = range(1, 1000)

In [11]: xs1 = range(1, 1000000)

In [12]: %timeit func1(xs1, ys1, zs1)
1 loop, best of 3: 15.7 s per loop

In [13]: %timeit func2(xs1, ys1, zs1)
1 loop, best of 3: 19.1 s per loop

想了解我的分析有什么问题。感谢。

2 个答案:

答案 0 :(得分:2)

您正在测量for循环本身执行的时间,而不是循环中的虚构语句。

当你的循环结构如下:

for x in xs:
    for y in ys:
        pass

    for z in zs:
        pass

考虑每个列表迭代的次数:

  • xs从外部循环迭代一次
  • ys在内循环中迭代X
  • zs在内循环中迭代X

所以总迭代次数为X * (1 + Y + Z),扩展为X + XY + XZ

pass语句执行的次数为X * (Y+Z)XY + XZ,与总迭代次数不同。

当循环的结构改为如下:

for y in ys:
    for x in xs:
        pass

for z in zs:
    for x in xs:
        pass
  • ys从第一个外循环迭代一次
  • xs在第一个内循环中迭代Y
  • zs从第二个外循环迭代一次
  • xs在第二内循环中迭代Z

意味着迭代总数为Y*(1+X) + Z*(1 + X),扩展为XY + XZ + Y + Z这与第一个等式有很大不同。

但是执行的pass语句的数量是相同的:XY + XZ

基本上:执行的实际语句的数量是相同的,但是第二个例子的迭代次数(获取列表的下一个元素)通常更大,并且因为pass花费的时间少于对于循环开销,后者是你实际测量的。

答案 1 :(得分:0)

你想使用O(x * (y + x))而不是O(X) * (O(Y) + O(Z))(其中x是X的长度,y是Y的长度等)来描述第一个循环的复杂性(O应该“包裹”整个表达)

为什么?

假设f(x)是返回执行大小为x的任务所需的确切操作数的函数。

根据定义f(x)= O(x)如果存在a,b使得a * O(x)+ b = f(x)对于所有x(注意它也意味着对于所有O(x) ),所有全部b,所有c和所有d,a * O(x) + b = c * O(x) + d)。要理解为什么这意味着您需要包装表达式,请考虑以下循环:

for i in range(n):
    for j in range(n):
        pass #  This is assumed to be exactly one action

请注意,对于此循环,f(n)完全等于n^2

假设我们认为此循环的复杂性为O(n) * O(n)而不是O(n ^ 2)。然后,O(n)^2 = (a*O(n)+b)^2 = a^2 * O(n) + a * b * O(n) + b ^ 2。您可以注意到,在扩展中会出现a ^ 2 * O(n)。它使得不能添加或减去这样的常量,使得该表达式总是等于f(n),因为O(n)显然根据输入大小而改变。因此,这是错误的符号。

O(n ^ 2)虽然恰到好处,但因为a = 1且b = 1,所有n都为a*O(n)+b=f(n)

因此,我们需要重写所有循环的复杂性,并牢记这一点。对于第一个循环,正确的复杂度为O(x * (y + z)) = O(x*y + x*z)。对于第二个循环,正确的复杂度为O(y z + z x)。

所以,两个循环的总体复杂性是相同的。