我有MATLAB代码将n维点(n> 1)插入矩阵(myPointMatrix
)并且想到如何插入第一个点。
现在,程序在插入点之前检查myPointMatrix
的大小。如果是1x1,则myPointMatrix
设置为等于当前点。否则,附加当前点。这个if
- 语句只有一次,但每次插入一个点时都要进行评估,这是非常频繁的。
删除if
并尝试追加到myPointMatrix
会让MATLAB抱怨矩阵维度不一致。同时删除if
- 语句和myPointMatrix = 0
的inialization会导致MATLAB查找myPointMatrix
未定义。也可以理解。
如何初始化myPointMatrix
以便我可以删除if
- 语句?还是有其他智能解决方案吗?
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
答案 0 :(得分: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.
看到所需的时间相当高,远远超过我直接调用rand的时间。
tic,rand(n,3);toc
Elapsed time is 0.008036 seconds.
其他追加方式在时间上类似。例如,您也可以通过索引进行追加。
A = [];
A(end+1,:) = rand(1,3);
A
A =
0.91338 0.63236 0.09754
在通过串联追加的时间方面类似。一个有趣的事实是,将新行附加到数组与添加新列略有不同。追加行而不是列需要稍多的时间。这是因为元素存储在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.
任何追加操作的问题都在于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.
虽然这比动态增长的数组好得多,但它仍然比使用rand的矢量化更差。因此,只要有可能,请使用这样的矢量化函数。
问题是,有时你根本不知道你最终会得到多少元素。还有一些技巧可以用来避免令人讨厌的二次增长。
一个技巧是猜测A的最终大小。现在,使用索引将新值插入A中,但要仔细观察新条目何时会溢出A的边界。当这就是发生,D大小为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.
这比成长的阵列花费的时间更少。为什么?我们只构建了一个指向单元格的指针数组。关于这一点的一个好处是,如果每个追加步骤具有可变数量的行,它仍然可以很好地工作。
单元格数组的一个问题是,当要追加数百万个元素时,它的效率并不高。毕竟它仍然是二次运算,因为我们在每一步都在增加指针数组。
该问题的解决方案是使用上面显示的两种样式的汞合金。因此,将单元阵列的每个单元定义为中等大小。现在使用索引将新的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.
当时,使用持久变量的方法更快一些。
tic
growdata
for i = 1:n
growdata(rand(1,3))
end
A = growdata;
toc
Elapsed time is 2.048584 seconds.
从那时起,函数句柄的实现在MATLAB中得到了明显的改进,因此函数句柄现在更快了。
这些方案的优点是它们不会产生二次性能损失,同时允许数百万个附加步骤。
哦,当提问时,这肯定比最初要求的信息多。也许有人会从中得到一些东西。
答案 1 :(得分:14)
使用myPointMatrix = [];
初始化矩阵。
myPointMatrix
越大,附加速度越慢。它变得越来越慢,因为每次你附加一个点时,matlab会分配一个新大小的新矩阵,并将旧矩阵+新点中的信息复制到新矩阵中。
最好用最终大小初始化MyPointMatrix
,然后将点插入矩阵中的给定位置。
答案 2 :(得分:3)
您最好的选择是预先分配矩阵并使用循环变量。这应该快得多。
limit = 9;
myPointMatrix = nan((limit+1)^3,3);
loopVar = 1;
for x=0:limit
for y=0:limit
for z=0:limit
myPointMatrix(loopVar,:) = [x y z];
loopVar = loopVar + 1;
end
end
end
答案 3 :(得分:0)
我相信您正在寻找的解决方案是将myPointMatrix初始化为一个包含0行和3列的矩阵,即
myPointMatrix = zeros(0, 3);
然后是第一个作业
myPointMatrix = [myPointMatrix; tempPoint];
将正常工作,以及随后的工作。编写赋值的等效方法是
myPointMatrix(end+1,:) = tempPoint;
但是请记住,生长像这样的矩阵效率不高,正如AnnaR所说,用ifs最终大小初始化myPointMatrix
(如果已知)是更好的解决方案。
答案 4 :(得分:0)
这就是你需要的
myPointMatrix=[];
for x=0:limit
for y=0:limit
for x=0:limit
myPointMatrix(:,end+1)=[x y z];
end
end
end
但仅在您使用[x y z]进行非线性操作的情况下,才能进行分配。如果没有,那么您可以按如下方式编写上述行:
myPointMatrix=[];
myPointMatrix(1,:)=kron([1:limit],ones(1,limit^2));
myPointMatrix(2,:)=kron([1:limit^2],ones(1,limit));
myPointMatrix(3,:)=kron(ones(1,limit^2),[1:limit]);
以上是完全矢量化的,虽然有人可能想要edit kron.m
并将某些find
替换为logical
......但你可以自己这样做我想...:D < / p>
答案 5 :(得分:0)
%appending to matlab array "f":
lfg=[697 770 852 941];
hfg=[1209 1336 1477];
f=[];
for i=1:4,
for j=1:3,
%f = [ f [lfg(i);hfg(j)] ];
append( f , [lfg(i);hfg(j)] );
end
end
f