我试图使用这两种算法对数组求和:
代码如下:
# Summation divide and conquer
def summation(array,left,right):
if left == right:
return array[left]
else:
if left == right-1:
return array[left] + array[right]
else:
mid = left + (right-left)//2
left_hand = summation(array,left,mid)
right_hand = summation(array,mid+1,right)
return left_hand + right_hand
还有..
# Loop summation
def summation_normal(array):
sums = 0
for i in range(len(array)):
sums += array[i]
return sums
以上两种算法均能正常工作,并且渐近都为O(n)。
但是我无法确定其中哪一个更快。 请帮助
答案 0 :(得分:1)
在两种算法中,对数组值执行加法的次数几乎相同!但是,递归算法的开销更大,因此运行速度会稍慢。
您可以可视化遍历二叉树的递归算法,获取两个子节点的值并将它们求和成其父节点。因此,(数组值的)加法数对应于该树的内部节点数。
现在看一看由2个元素组成的简短数组(索引为0和1):
+
/ \
0 1
加号表示递归算法中发生的唯一加法。现在,将一个元素添加到数组中。这意味着其中一个叶节点成为两个叶节点的父节点(添加了一个叶节点)。例如:
+
/ \
+ 2
/ \
0 1
因此,现在需要执行另外一个加法:还有一个内部节点。您可以轻松地看到,在此结构中添加另一个数组元素会使内部节点的数量增加1。因此,还会有一个加法。
同样,在树表示中,叶节点表示数组值,内部节点表示所得到的中间和。
二叉树中的叶子数量总是比内部节点的数量多一。因此涉及数组值的加法数为n-1。这比迭代解决方案少一个,因为那里的第一个(也是额外的)加法是0 + array[0]
。您可以通过从array[0]
开始并从索引1开始循环来改善这种情况。如果这样做,两种算法都会执行n-1个涉及数组值的加法运算。
很明显,递归算法还有更多的“内务处理”:计算中间索引,使用堆栈传递参数,等等,因此它会慢一些,但是时间复杂度不同。
答案 1 :(得分:0)
我会做这样的事情:
a = np.random.randint(0, 1000, 10000)
%timeit summation(a, 0, len(a)-1)
3.59 ms ± 81 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
输出:
%timeit summation_normal(a)
1.29 ms ± 7.45 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
请记住,这些时间很大程度上取决于处理器中正在进行的活动以及其他一些因素。
很明显sumsum_normal是这里的赢家。