向序列对象添加元素
在Matlab中,这种算法(“动态数组”)是不推荐使用的。
mine = []
for i=1:100,
mine = [mine,randn(1)]
end
而在Python中,很多例子却展示了这种算法(不过这个例子真的很糟糕):
import numpy.random as rand
mine = []
for i in range(100):
mine.append(rand.random(1)[0])
我在想这是为什么——它们之间有什么区别呢?
4 个回答
3
你的MATLAB代码可以写得更好。看看下面的实现方式:
%# preallocation
tic
x = zeros(1,100000); for i=1:100000, x(i) = 99; end
toc
%# appending
tic
x = []; for i=1:100000, x(end+1) = 99; end
toc
%# concatenation
tic
x = []; for i=1:100000, x = [x, 99]; end
toc
我得到了以下结果:
Elapsed time is 0.001844 seconds. %# preallocated vector/matrix
Elapsed time is 0.109597 seconds. %# appending using "end+1" syntax
Elapsed time is 35.226361 seconds. %# appending using concatenation
请注意,上面的测试是在R2011b版本上进行的,这个版本在动态扩展矩阵方面做了改进(不需要提前分配空间)。
你还应该查看这个之前的回答,它提供了一种结合了预分配和动态扩展的解决方案(这个思路是以较大的块大小进行分配和扩展)。
另一方面,你需要注意的是,Python的列表在向末尾添加项目时是经过优化的。如果你在开头插入项目,时间会有很大的不同。举个例子:
>>> from timeit import timeit
>>> timeit('x.insert(0,99)', 'x=[]', number=100000)
5.3840245059078597
>>> timeit('x.append(99)', 'x=[]', number=100000) # x.insert(len(x),99)
0.039047700196533697
4
在Matlab中,给数组添加元素的效率似乎很低(运行时间是平方级别),而在Python中,相应的列表操作则优化得好多了。Python中的添加操作在列表没有满之前是O(1)的,也就是说,添加一个元素的时间是固定的。但是当列表满了的时候,它会把列表的大小翻倍,以腾出更多空间(这个操作是O(n)的)。这意味着,随着列表越来越长,添加元素的效率会越来越高(整体效率是O(1)的,算上平均值)。这种优化在Matlab中也许可以实现,但看起来并不是自动完成的。
为了获得更好的性能,Python还有一个叫做collections.deque的容器类,它支持从两端高效地添加和移除元素(在两个方向上都是O(1)的)。
7
它们之间的区别在于:
- 在MATLAB中,每次循环都会重新分配矩阵的空间,增加一个元素,并把原来的所有内容复制到新分配的空间里。
- 而Python的列表就不是这样。它会一次性分配比当前需要的更多空间,这样在添加新元素时,可以更快地完成这个操作。
不过,我觉得这种差异主要是文化上的:
- 在MATLAB中,处理大型数字矩阵是很常见的,如果每次只增加一个元素(或者一行/一列),那会非常耗费资源。
- 而在Python中,没有人会用列表(或者列表的列表)来表示一个大型矩阵:这样做会非常慢,并且会浪费很多内存。通常会使用NumPy的
ndarray
,它的性能和MATLAB的矩阵是一样的。