所以在我目前的MATLAB脚本中,我有一个非常大的不确定大小的增长数组。目前我无能为力,因为如果我实际预先分配,它将需要比它应该需要的内存多很多倍的内存(最大可能的值是每像素640,但通常它是2的线上的东西5)。
通常在这种情况下,我会使用C ++中的向量或其他东西,它在给定容量方面呈指数级增长。但我认为Matlab中的矩阵开始分片的速度比目的驱动的C ++向量要快得多。
你们认为这是什么样的最佳选择?或者我应该坚持使用普通数组,并希望顺序添加大约100k元素会起作用吗?
提前致谢。
答案 0 :(得分:10)
增加数组通常是件坏事,至少如果你多次这样做的话。可以使用一些技巧。 (我已经尝试过所有这些,并且如果你愿意,可以自动为你编写工具。)
问题是与阵列增长相关的时间是O(N ^ 2)操作。如果您需要稍后创建一个大型数组,它也会使您的内存碎片化,从而使您遇到潜在问题。
一种方法是将数组预分配到某个固定大小,然后在超出数组大小时附加大块零。现在使用矩阵索引将新值插入现有数组中。我们的想法是在需要重新分配数组时将其大小加倍。这只会导致一些重新分配,但最终会附加很多你不需要填充的零。最后,您将转储无关和未使用的数组元素。
第二个技巧是使用单元阵列。每次要添加新元素或其块时,只需添加包含这些元素的新单元格。然后在最后,你做一个cell2mat操作。单元技巧的一个问题是它仍然是一个O(N ^ 2)操作,因为单元数组本身本质上是一个指针数组,并且当你追加新元素时必须重新分配指针列表。
你可以做我喜欢的组合技巧,但它需要一个工具来实现操作。我们的想法是创建包含10000个元素块的单元格。用零填充第一个块。然后使用矩阵索引在生成元素时插入新元素。矩阵索引当然很快。当您在该单元格中用完房间时,会附加一个新的大单元格。然后在最后,您将所有单元格连接在一起,小心地丢弃未使用的元素。
最后一个方案涉及相对较少的新细胞元素,如果可能有数百万个追加步骤,那么整体效率最高。
您可以找到我多年来发布的多个文件交换提交中体现的每种方法。第一个可以找到grow_array,它在内部管理附加步骤,担心矩阵索引。
后来,我使用函数句柄或持久变量构建了最后一个方案来维护存储的信息。包含这些实现的工具位于growdata and growdata2。
答案 1 :(得分:6)
您可以尝试重新分配元素时std::vector
所做的事情 - 每次填充时其容量加倍,其累计成本为 O(1)。
function test_MAT_vector
LIM = 5e4;
%%# Normal
tic;
A = zeros(1,1);
for i = 1:LIM
A(end+1) = i;
end
toc
%%# std::vector clone
tic;
B = zeros(1,1);
for i = 1:LIM
if(i > numel(B))
B = [B;zeros(numel(B),1)];
end
B(i) = i;
end
toc
end
经过的时间是3.489820秒。
经过的时间是0.006710秒。
%%# cell
tic;
A = cell(1,1);
for i = 1:LIM
A(end+1) = {i};
end
toc
经过的时间是2.740792秒。
答案 2 :(得分:2)
在上一个问题中,我已将a similar solution发布到@Jacob提议的内容中。
我后来比较了大多数可用选项的性能(上面的@woodchips非常好地总结了)。以下是我在机器上得到的结果:
NUM = 50000;
%%# ========== MINE: ~0.07sec ==========
tic
BLOCK_SIZE = 2000; %# initial & increment size
listSize = BLOCK_SIZE; %# current list capacity
list = zeros(listSize, 2); %# actual list
listPtr = 1; %# pointer to last free position
for i=1:NUM
%# push items on list
list(listPtr,:) = [rand rand]; %# store new item
listPtr = listPtr + 1; %# increment position pointer
%# add new block of memory if needed
if( listPtr+(BLOCK_SIZE/10) > listSize ) %# less than 10%*BLOCK_SIZE free
listSize = listSize + BLOCK_SIZE; %# add new BLOCK_SIZE slots
list(listPtr+1:listSize,:) = 0;
end
end
list(listPtr:end,:) = []; %# remove unused slots
toc
%%# ========== PREALLOCATION (matrix): ~0.05sec ==========
tic
list = zeros(NUM,2);
for i=1:NUM
list(i,:) = [rand rand];
end
toc
%%# ========== PREALLOCATION (cell): ~1.1sec ==========
tic
list = cell(NUM,1);
for i=1:NUM
list{i} = [rand rand];
end
list = vertcat( list{:} );
toc
%%# ============ NO-PREALLOCATION (grow cell): ~5sec ========
tic
list = {};
for i=1:NUM
list{i} = [rand rand];
end
list = vertcat( list{:} );
toc
%%# ============ NO-PREALLOCATION (grow matrix): ~24sec ========
tic
list = [];
for i=1:NUM
list(i,:) = [rand rand];
end
toc
%%# ========== GROWDATA (by John D'Errico): ~3.3sec =========
tic
growdata %# The initialization call
for i = 1:NUM
growdata( [rand rand] ) %# push items
end
list = growdata; %# unpacking step
toc
答案 3 :(得分:0)
你可以增长一个比数组便宜得多的单元格数组,然后使用cell2mat
转换为数组(需要两倍的内存,但如果你拥有它可能是最简单的方法)。
如果您知道可以预先分配正确大小的单元格数组的像素数,请使用实际值数组填充每个单元格(最多640个但通常为2-5个),然后使用cell2mat
进行转换它是一个连续的阵列。
如果你担心碎片,你可以在加载应该对你的内存进行碎片整理的单元格之前pack
进行碎片整理(将所有内容写入磁盘并将其连续重新加载),然后再进行cell2mat
转换。