将向量附加到空MATLAB矩阵

Ann*_*naR 17 matlab matrix

我有MATLAB代码将n维点(n> 1)插入到矩阵(myPointMatrix)中,并且想到如何插入第一个点.

现在程序检查myPointMatrix插入点之前的大小.如果是1x1,myPointMatrix则设置为等于当前点.否则,附加当前点.这个if-statement只有一次,但每次插入一个点时都要进行评估,这是非常频繁的.

删除if和尝试追加myPointMatrix使得MATLAB可以理解地抱怨矩阵维度不一致.删除if-statement和inialization myPointMatrix = 0导致MATLAB找到myPointMatrixundefined.也可以理解.

如何初始化myPointMatrix以便我可以删除if-statement?还是有其他智能解决方案吗?

myPointMatrix = 0;
for x=0:limit
    for y=0:limit
        for z=0:limit
            tempPoint = [x y z];
            if (length(myPointMatrix) == 1)
                myPointMatrix = tempPoint;
            else
                myPointMatrix = [myPointMatrix; tempPoint];
            end
        end
    end
end
Run Code Online (Sandbox Code Playgroud)

小智 28

有几种方法可以将矩阵或向量附加到任何矩阵,无论是否为空.很大程度上取决于矩阵的大小,以及你追求追加的频率.(请注意,稀疏矩阵是完全不同的动物.它们需要单独处理.)

简单的方案将使用连接.例如,我将创建一个随机数组.虽然我知道对rand的一次调用在这里是正确的解决方案,但我只是为了比较而这样做.

n = 10000;
tic
A = [];
for i = 1:n
  Ai = rand(1,3);
  A = [A;Ai];
end
toc

Elapsed time is 9.537194 seconds.
Run Code Online (Sandbox Code Playgroud)

看到所需的时间相当高,远远超过我直接调用rand的时间.

tic,rand(n,3);toc
Elapsed time is 0.008036 seconds.
Run Code Online (Sandbox Code Playgroud)

其他追加方式在时间上是相似的.例如,您也可以通过索引进行追加.

A = [];
A(end+1,:) = rand(1,3);
A
A =
      0.91338      0.63236      0.09754
Run Code Online (Sandbox Code Playgroud)

在通过串联追加的时间方面类似.一个有趣的事实是,将新行附加到数组与添加新列略有不同.追加行而不是列需要稍多的时间.这是因为元素存储在MATLAB中的方式.追加新行意味着元素实际上必须在内存中混洗.

A = zeros(10000,3);
B = zeros(3,10000);

tic,for i = 1:100,A(end+1,:) = rand(1,3);end,toc
Elapsed time is 0.124814 seconds.

tic,for i = 1:100,B(:,end+1) = rand(3,1);end,toc
Elapsed time is 0.116209 seconds.
Run Code Online (Sandbox Code Playgroud)

任何追加操作的问题都在于MATLAB必须重新分配A所需的内存,并且每当矩阵的大小增加时都要这样做.由于A的大小呈线性增长,因此所需的总时间与n呈二次方增长.那么我们要将n的大小加倍,动态增长的A将需要四倍的构建时间.这种二次行为是人们告诉您在动态增长时预先分配MATLAB数组的原因.事实上,如果你在编辑器中查看mlint标志,MATLAB会在发现这种情况时发出警告.

如果您知道A的最终大小,更好的解决方案是将A预先分配到其最终大小.然后只需索引.

tic
A = zeros(n,3);
for i = 1:n
  A(i,:) = rand(1,3);
end
toc

Elapsed time is 0.156826 seconds.
Run Code Online (Sandbox Code Playgroud)

虽然这比动态增长的数组要好得多,但它仍然比使用rand的矢量化更差.因此,只要有可能,请使用这样的矢量化形式的函数.

问题是,有时你根本不知道你最终会得到多少元素.仍然有一些技巧可以用来避免令人讨厌的二次增长.

一个技巧是猜测A的最终大小.现在,使用索引将新值插入A中,但要仔细观察新条目何时溢出A的边界.当这种情况即将发生时,DOUBLE A的大小,最后添加一大块零.现在返回将新元素索引到A中.单独计算已"附加"了多少元素.在此过程的最后,删除未使用的元素.这避免了许多令人讨厌的二次行为,因为只会做几个附加步骤.(记住,当你必须做追加时,你的A的大小加倍.)

第二个技巧是使用指针.虽然MATLAB在指针方面并没有提供太多功能,但是单元阵列是朝着这个方向迈出的一步.

tic
C = {};
for i = 1:n
  C{end+1} = rand(1,3);
end
A = cat(1,C{:});
toc

Elapsed time is 3.042742 seconds.
Run Code Online (Sandbox Code Playgroud)

这比完成的阵列花费的时间更少.为什么?我们只构建了一个指向单元格的指针数组.关于这一点的一个好处是,如果每个追加步骤具有可变数量的行,它仍然可以很好地工作.

单元阵列的一个问题是,当有数百万个要追加的元素时,它的效率并不高.毕竟它仍然是二次运算,因为我们在每一步都在增加指针数组.

该问题的解决方案是使用上面显示的两种样式的混合.因此,将单元阵列的每个单元定义为中等大小.现在使用索引将新的A行填充到单元格中.当当前单元格必须在下一个附加步骤中变大时,只需向单元格数组添加一个新单元格.

几年前,这个讨论出现在MATLAB新闻组中,并提出了几条解决方案.我将解决方案growdata和growdata2作为文件发布在MATLAB Central File Exchange上.Growdata2使用函数句柄来解决问题:

tic
Ahandle = growdata2;
for i = 1:n
  Ahandle(rand(1,3))
end
% unpack the object into a normal array
A = Ahandle();
toc

Elapsed time is 1.572798 seconds.
Run Code Online (Sandbox Code Playgroud)

当时,使用持久变量是一种更快的方法.

tic
growdata
for i = 1:n
  growdata(rand(1,3))
end
A = growdata;
toc

Elapsed time is 2.048584 seconds.
Run Code Online (Sandbox Code Playgroud)

从那时起,函数句柄的实现在MATLAB中得到了明显的改进,因此函数句柄现在更快了.

这些方案的优点是它们不会产生二次性能损失,同时允许数百万个附加步骤.

哦,当提问时,这肯定比最初要求的信息更多.也许有人会从中得到一些东西.


Ann*_*naR 14

使用myPointMatrix = [];初始化矩阵.

越大myPointMatrix,附加速度越慢.它变得越来越慢,因为每次你附加一个点时,matlab会分配一个新大小的新矩阵,并将旧矩阵+新点中的信息复制到新矩阵中.

然后最好MyPointMatrix用其最终大小进行初始化,并将这些点插入矩阵中的给定位置.

  • 你的最后一句话+1.这是在MATLAB中初始化矩阵的最有效方法. (2认同)