预分配的重要性

MATLAB 中的数组作为连续块保存在内存中,由 MATLAB 自动分配和释放。MATLAB 隐藏了内存管理操作,例如在易于使用的语法背后调整数组大小:

a = 1:4

a =

     1     2     3     4

a(5) = 10  % or alternatively a = [a, 10]

a =

     1     2     3     4    10

重要的是要理解上面不是一个简单的操作,a(5) = 10 将导致 MATLAB 分配一个大小为 5 的新内存块,复制前 4 个数字,并将第 5 个数字设置为 10.这是一个 O(numel(a)) 操作,而不是 O(1)

考虑以下:

clear all
n=12345678;
a=0;
tic
for i = 2:n
    a(i) = sqrt(a(i-1)) + i;
end
toc

Elapsed time is 3.004213 seconds.

a 在此循环中重新分配了 5 次(不包括 MATLAB 进行的一些优化)! 请注意,MATLAB 给出了一个警告:

“变量’a’似乎在每次循环迭代时改变大小。考虑预先分配速度。”

我们预先分配时会发生什么?

a=zeros(1,n);
tic
for i = 2:n
    a(i) = sqrt(a(i-1)) + i;
end
toc

Elapsed time is 0.410531 seconds.

我们可以看到运行时间减少了一个数量级。

预分配方法:

MATLAB 根据用户的具体要求提供各种矢量和矩阵分配功能。这些包括: zerosonesnaneyetrue 等。

a = zeros(3)       % Allocates a 3-by-3 matrix initialized to 0
a =

     0     0     0
     0     0     0
     0     0     0

a = zeros(3, 2)     % Allocates a 3-by-2 matrix initialized to 0
a =

     0     0
     0     0
     0     0

a = ones(2, 3, 2)      % Allocates a 3 dimensional array (2-by-3-by-2) initialized to 1
a(:,:,1) =

     1     1     1
     1     1     1

a(:,:,2) =

     1     1     1
     1     1     1

a = ones(1, 3) * 7  % Allocates a row vector of length 3 initialized to 7
a =

     7     7     7

还可以指定数据类型:

a = zeros(2, 1, 'uint8');  % allocates an array of type uint8

克隆现有数组的大小也很容易:

a = ones(3, 4);       % a is a 3-by-4 matrix of 1's
b = zeros(size(a));  % b is a 3-by-4 matrix of 0's

并克隆类型:

a = ones(3, 4, 'single');       % a is a 3-by-4 matrix of type single
b = zeros(2, 'like', a);        % b is a 2-by-2 matrix of type single

注意’喜欢’也克隆了复杂性稀疏性

使用任何返回最终所需大小数组的函数隐式实现预分配,例如 randgallerykronbsxfuncolon 等等。例如,分配具有线性变化元素的向量的常用方法是使用冒号运算符(使用 2 或 3 操作数变量 1 ):

a = 1:3 
a =

     1     2     3

a = 2:-3:-4
a =

     2    -1    -4

可以使用 cell() 函数分配单元阵列,其方式与 zeros() 非常相似。

a = cell(2,3)
a = 

    []    []    []
    []    []    []

请注意,单元格数组的工作方式是将指针指向单元格内容的内存中的位置。因此,所有预分配技巧也适用于单个单元阵列元素。

进一步阅读: