18

我有将 n 维点 (n >1) 插入矩阵 ( myPointMatrix) 的 MATLAB 代码,并且正在考虑如何插入第一个点。

现在程序myPointMatrix在插入点之前检查大小。如果是1x1,myPointMatrix则设置为等于当前点。否则附加当前点。这个if-statement 只成立一次,但每次我插入一个点时都会被评估,这很常见。

删除if并尝试附加到myPointMatrix使 MATLAB 抱怨矩阵尺寸不一致是可以理解的。删除if- 语句和初始化myPointMatrix = 0会导致 MATLAB 发现myPointMatrix未定义。也可以理解。

如何初始化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
4

6 回答 6

29

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

简单的方案将使用串联。例如,我将创建一个随机数组。虽然我知道在这里调用 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 的边界。当这即将发生时,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.

与增长的阵列相比,这需要更少的时间来完成。为什么?我们只是构建了一个指向单元格的指针数组。这样做的好处是,如果每个附加步骤都有可变数量的行,它仍然可以很好地工作。

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

解决该问题的方法是使用上面显示的两种样式的混合物。因此,将元胞数组的每个元胞定义为大小适中。现在使用索引将 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 中得到了明显改进,因此函数句柄现在更快了。

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

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

于 2009-04-23T19:12:24.497 回答
14

用于myPointMatrix = [];初始化矩阵。

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

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

于 2009-04-23T12:49:45.490 回答
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
于 2009-04-23T12:49:38.767 回答
0

我相信您正在寻找的解决方案是将 myPointMatrix 初始化为具有 0 行和 3 列的矩阵,即

myPointMatrix = zeros(0, 3);

然后第一个任务

myPointMatrix = [myPointMatrix; tempPoint];

将正常工作,以及后续工作。编写作业的等效方法是

myPointMatrix(end+1,:) = tempPoint;

但是请记住,比增长这样的矩阵效率不高,正如 AnnaR 所说,myPointMatrix使用 ifs 最终大小(如果已知)进行初始化是一个更好的解决方案。

于 2009-04-23T13:04:02.850 回答
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
于 2013-02-17T18:12:14.343 回答
0

这就是你需要的

myPointMatrix=[];
for x=0:limit
for y=0:limit
for x=0:limit
  myPointMatrix(:,end+1)=[x y z];
end
end
end

但仅在您使用 [ xyz ] 进行一些非线性操作的情况下,在分配它之前。如果没有,那么您可以按如下方式编写上述行:

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替换一些,但我想你可以自己做......:Dfindlogical

于 2012-04-02T16:44:13.000 回答