在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])
我想知道为什么会这样 - 有什么区别?
答案 0 :(得分:7)
区别在于:
那就是说,我认为区别主要在于文化:
ndarray
代替,ndarray
将提供与MATLAB矩阵完全相同的权衡。答案 1 :(得分:4)
在Matlab中追加数组显然非常低效(它以二次方运行),而在python中,相应的列表操作更加高度优化。追加到O(1)直到列表变满 - 此时列表的大小加倍以产生更多空间(这是O(n)操作)。这意味着随着列表变长(整体效率为O(1)摊销),追加变得越来越有效。这些优化也可能在Matlab中实现,但似乎它们不是自动完成的。
为了获得更好的性能,python还有collections.deque容器类,支持从任一端有效追加和删除(在两个方向都是O(1))。
答案 2 :(得分:2)
你的两个例子并不完全相同。 Matlab示例将两个列表连接到一个新列表中,每次都创建一个副本,而Python将项目附加到列表中,而不是每次都复制它。实际上,您可以编写与Matlab代码完全相同的Python,例如:
mine = mine + [newitem]
但是你不应该这样做,因为你每次都会复制一份不断增长的名单。这就是为什么列表有.append()
方法(也是.extend()
)。
出于类似的原因,Pythonistas建议您将单个字符串附加到列表中,然后在其上使用``.join()
,而不是通过串联构建字符串。
顺便说一句,Python列表总是为额外的项目分配空间,因此当附加新项目时,它们并不总是需要增长。
答案 3 :(得分:2)
您的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上测试的,它引入了对增长矩阵的改进(无需预先分配)。
您还应该检查此previous answer以获得一个结合了预分配但仍允许动态增长的解决方案(想法是以大块大小分配/增长)
另一方面,您应该注意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