解释两个python代码的时间和空间复杂性。哪一个是最好的? (非主观的)

时间:2015-07-27 07:42:38

标签: python time complexity-theory space

这些代码在不使用循环语句的情况下给出列表中偶数整数的总和。我想知道两个代码的时间复杂性和空间复杂性。哪个最好?

代码1:

class EvenSum:
     #Initialize the class

    def __init__(self):
        self.res = 0

    def sumEvenIntegers(self, integerlist):
        if integerlist:
            if not integerlist[0] % 2: 
                self.res += integerlist[0]
                del integerlist[0]
                self.sumEvenIntegers(integerlist)
            else:
                del integerlist[0]
                self.sumEvenIntegers(integerlist)
        return self.res

#main method
if __name__ == "__main__":
l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
even = EvenSum()
print even.sumEvenIntegers(l)

代码2:

import numpy as np

def sum_of_all_even_integers(list):
    list_sum = sum(list)  
    bin_arr  = map(lambda x:x%2, list)
    return list_sum - sum(list*bin_arr)

if __name__ == "__main__":

    list = np.array([1,2,3,4,5,6,7,8,9,10])
    print sum_of_all_even_integers(list)

4 个答案:

答案 0 :(得分:2)

根据Python wiki,从列表中删除项目需要线性时间与列表中的元素数量成比例。由于您删除了列表中的每个项目,并且每次删除都需要线性时间,因此整个运行时间与列表中项目数的平方成正比。

在您的第二个代码段中,summap都需要线性时间。因此总体复杂度与列表中元素的数量成线性比例。有趣的是,sum_of_elements根本没有使用(但它并没有对所有偶数元素求和)。

答案 1 :(得分:1)

第一个代码使用列表中的项删除和递归,python不太好的两件事:时间删除花费O(n)时间,因为你重新创建整个列表,而python不优化递归调用(保持关于追溯的完整信息我认为。)

所以我会选择第二个代码(我认为实际使用#34; for循环"只有循环隐藏在reducemap中)。

如果您使用numpy,您实际上可以执行以下操作:

a = np.array([1,2,3,4,5,6,7,8,9,10])
np.sum(np.where((a+1)%2,a,0))

或者像anki提议的那样:

np.sum( a[a%2 == 0] )

我认为这是最好的,因为numpy针对数组操作进行了优化。

顺便说一句,永远不要命名对象list,因为它会覆盖列表构造函数。

编辑:

如果你只想要[0,n]中所有偶数的总和,你就不需要总和或任何东西。有一个数学公式:

 s=(n//2)*(n//2+1)

答案 2 :(得分:1)

以下情况怎么样?

import numpy as np    
a = np.arange(20)
print np.sum(a[a%2==0])

与两个代码段相比,它看起来要轻得多。

np.arange(998)的小时间:

Pure numpy:
248502
0.0
Class recursion:
248502
0.00399994850159
List/Numpy one: 
248502
0.00200009346008

而且,如果有一个999元素数组,那么你的类运行失败,因为达到了最大递归深度。

答案 3 :(得分:0)

首先具有O(N^2)时间复杂度和O(N)空间复杂度。第二个具有O(N)时间复杂度和空间复杂度。

第一个对阵列中的每个元素使用一个堆栈帧(一个常量但非常大的堆栈存储器)。另外,它为每个元素执行函数,但是每次都删除数组的第一个元素,这是一个O(N)操作。

第二种情况发生在幕后。 map函数生成一个与原始大小相同的新列表,此外它还为每个元素调用一个函数 - 直接给出复杂性。类似于reducesum函数 - 它们对列表中的每个元素执行相同的操作,尽管它们不使用比常量更多的内存。添加这些不会使复杂性变得更糟 - 两次或三次O(N)仍然是O(N)

最好的可能是后者,但又一次 - 这取决于你的偏好。也许你想要消耗大量的时间和堆栈空间?在这种情况下,第一个更适合您的喜好。

另请注意,第一个解决方案会修改输入数据 - 换句话说,它们不会执行相同的操作。在调用第一个之后,发送给函数的列表将为空(这可能是也可能不是坏事)。