如何在MATLAB中遍历n维矩阵中的每个元素?


87

我有个问题。我需要遍历MATLAB中n维矩阵中的每个元素。问题是,我不知道如何对任意数量的尺寸执行此操作。我知道我可以说

for i = 1:size(m,1)
    for j = 1:size(m,2)
        for k = 1:size(m,3)

等等,但是有没有办法针对任意数量的尺寸呢?


13
Matlab术语注释:Matlab具有少量核心数据类型。最重要的是:结构,矩阵和单元格数组。引用矩阵的各个部分时,通常使用术语“元素”,保留术语“单元”以引用单元阵列的各个部分。即使单元格数组和矩阵都是N维数据结构,它们也具有许多语法和语义上的差异。
福斯先生09年

3
请问您需要什么迭代?也许有一种“矢量化”的方式来代替它……
Hosam Aly

Answers:


92

您可以使用线性索引来访问每个元素。

for idx = 1:numel(array)
    element = array(idx)
    ....
end

如果您不需要知道i,j,k在什么位置,这很有用。但是,如果您不需要知道所处的索引,最好使用arrayfun()


1
另外,如果由于某种原因要恢复索引,仍然可以使用以下两个简单命令: I = cell(1, ndims(array)); [I{:}] = ind2sub(size(array),idx);
knedlsepp

34

matlab中数组的线性索引的想法很重要。MATLAB中的数组实际上只是元素的向量,存在于内存中。MATLAB允许您使用行索引和列索引,也可以使用单个线性索引。例如,

A = magic(3)
A =
     8     1     6
     3     5     7
     4     9     2

A(2,3)
ans =
     7

A(8)
ans =
     7

通过将数组展开为向量,可以看到元素在内存中存储的顺序。

A(:)
ans =
     8
     3
     4
     1
     5
     9
     6
     7
     2

如您所见,第8个元素是数字7。实际上,函数find将其结果作为线性索引返回。

find(A>6)
ans =
     1
     6
     8

结果是,我们可以使用单个循环依次访问通用nd数组中的每个元素。例如,如果我们想平方A的元素(是的,我知道有更好的方法可以做到这一点),则可以这样做:

B = zeros(size(A));
for i = 1:numel(A)
  B(i) = A(i).^2;
end

B
B =
    64     1    36
     9    25    49
    16    81     4

在许多情况下,线性索引更有用。线性索引和二维(或更高维)下标之间的转换是通过sub2ind和ind2sub函数完成的。

线性索引通常适用于matlab中的任何数组。因此,您可以在结构,单元格阵列等上使用它。线性索引的唯一问题是它们太大时。MATLAB使用32位整数存储这些索引。因此,如果数组中包含的元素总数超过2 ^ 32,则线性索引将失败。如果经常使用稀疏矩阵,这实际上只是一个问题,有时这会引起问题。(尽管我不使用64位MATLAB版本,但我相信对于那些幸运的人来说,问题已经解决了。)


在64位MATLAB中建立索引确实确实正确地允许64位下标。例如:x = ones(1,2^33,'uint8'); x(2^33)按预期工作。
Edric

@Edric-当然,自从我发表声明以来,这种行为肯定会在多年(以及许多版本)中发生改变。感谢您的检查。

:)直到我发表评论后,我才意识到答案有多大-这个问题刚刚出现在我的RSS feed中,我什至没有注意到我也回答了它!
Edric

15

正如在其他一些答案中指出的那样,您可以在单个for循环中A使用线性索引1to遍历矩阵(任意维度)中的所有元素numel(A)。您还可以使用几个功能:arrayfuncellfun

首先,假设您有一个要应用于A(称为my_func)每个元素的函数。您首先创建此函数的函数句柄

fcn = @my_func;

如果A是任意维度的矩阵(类型为double,single等),则可以将arrayfun其应用于my_func每个元素:

outArgs = arrayfun(fcn, A);

如果A是任意维度的单元格数组,则可以用于cellfun将其应用于my_func每个单元格:

outArgs = cellfun(fcn, A);

该功能my_func必须接受A作为输入。如果有任何输出my_funcoutArgs则将它们放置在中,其大小/尺寸与相同A

关于输出的一个警告...如果my_func在对的不同元素进行操作时返回不同大小和类型的输出A,则outArgs必须将其制成单元数组。这可以通过调用arrayfuncellfun使用附加的参数/值对来完成:

outArgs = arrayfun(fcn, A, 'UniformOutput', false);
outArgs = cellfun(fcn, A, 'UniformOutput', false);

13

另一种技巧是使用ind2subsub2ind。与numel和结合使用size,可以让您执行以下操作,创建一个N维数组,然后将“对角线”上的所有元素设置为1。

d = zeros( 3, 4, 5, 6 ); % Let's pretend this is a user input
nel = numel( d );
sz = size( d );
szargs = cell( 1, ndims( d ) ); % We'll use this with ind2sub in the loop
for ii=1:nel
    [ szargs{:} ] = ind2sub( sz, ii ); % Convert linear index back to subscripts
    if all( [szargs{2:end}] == szargs{1} ) % On the diagonal?
        d( ii ) = 1;
    end
end

+1是一个很好的例子,展示了MATLAB如何打破鸭子的打字方式。
菲利普·

1

你可以做一个递归函数

  • L = size(M)
  • idx = zeros(L,1)
  • length(L)最大深度为准
  • 循环 for idx(depth) = 1:L(depth)
  • 如果您的深度为length(L),请执行元素操作,否则使用再次调用该函数depth+1

如果要检查所有要点,则没有向量化方法快,但是如果不需要评估大多数要点,则可以节省大量时间。


1

这些解决方案比使用numel;)更快(约11%)。

for idx = reshape(array,1,[]),
     element = element + idx;
end

要么

for idx = array(:)',
    element = element + idx;
end

UPD。tnx @rayryeng在上一个答案中检测到错误


免责声明

由于做出了基本的错字,此帖子引用的时间信息不正确且不准确(请参阅下面的评论流以及编辑历史记录-特别是看此答案的第一个版本)。 警告购买者


1
1 : array(:)等价于1 : array(1)。这并不会遍历所有元素,这就是运行时间快的原因。此外,rand生成浮点数,这样做1 : array(:)会产生一个空数组,因为您的语句试图查找一个初始值为1且终点值为浮点数且不包含1的递增向量[0,1)。 1.的步骤。没有这样的可能向量,从而导致空向量。您的for循环无法运行,因此您的声明是错误的。-1票。抱歉。
rayryeng 2015年

@rayryeng你不对。array(:)不等于1:array(1)。喜欢reshape(...)
mathcow

@rayryeng Matlab R2013a + Linux-工作正常!;)我也只运行了该代码
mathcow 2015年

1 : array(:)创建完成后,在命令提示符下键入on array 。你得到一个空矩阵吗?如果是,则您的代码不起作用。我离开我的投票,因为您提供的是虚假信息。
rayryeng 2015年

@rayryeng我了解!是的,您是对的,对不起您的愚蠢之争
mathcow 2015年

-1

如果您进一步了解的其他用途,size您会发现实际上可以得到每个尺寸大小的向量。此链接向您显示文档:

www.mathworks.com/access/helpdesk/help/techdoc/ref/size.html

获得大小向量后,对该向量进行迭代。像这样的东西(请原谅我的语法,因为自从大学以来我没有使用过Matlab):

d = size(m);
dims = ndims(m);
for dimNumber = 1:dims
   for i = 1:d[dimNumber]
      ...

将其变成实际的Matlab-legal语法,我认为它将满足您的要求。

另外,您应该能够按照此处所述进行线性索引。


我不太清楚循环的顺序如何遍历矩阵的所有元素。例如,如果您有一个3×4矩阵(包含12个元素),则您的内部循环将仅迭代7次。
gnovice

它应该遍历矩阵的每个维度。外循环遍历维度,内循环遍历该维度的大小。至少,那是主意。正如其他所有人都说的那样,如果他只需要每个单元格,则衬线索引是最好的。如果他想遍历每个维度,则必须执行与此类似的操作。
Erich Mirabal

另外,感谢您的编辑。我的链接有点混乱,使用通常的链接方式无法正常工作。同样,为了扩展我的说法:他仍然必须对索引进行其他许多跟踪(例如使用计数器或类似方法)。我认为您或安德鲁的做法会更容易,因为我认为他想这样做。
Erich Mirabal

-1

您要模拟n嵌套for循环。

遍历n维数组可以看作是增加n位数字。

在每个维度上,我们拥有的数字与维度的长度一样多。

例:

假设我们有array(matrix)

int[][][] T=new int[3][4][5];

在“表示法”中,我们有:

for(int x=0;x<3;x++)
   for(int y=0;y<4;y++)
       for(int z=0;z<5;z++)
          T[x][y][z]=...

要对此进行模拟,您必须使用“ n位数字符号”

我们有3位数,第一位数为3位,第二位数为4位,第三位数为5位

我们必须增加数量,所以我们要得到序列

0 0 0
0 0 1
0 0 2    
0 0 3
0 0 4
0 1 0
0 1 1
0 1 2
0 1 3
0 1 4
0 2 0
0 2 1
0 2 2
0 2 3
0 2 4
0 3 0
0 3 1
0 3 2
0 3 3
0 3 4
and so on

因此,您可以编写用于增加此类n位数字的代码。您可以以任何数字值开头并以任意数字增加/减少数字的方式进行操作。这样,您可以模拟嵌套的for循环,这些循环从表中的某处开始,直到结束为止。

但是,这并不是一件容易的事。不幸的是,我不禁会用matlab表示法。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.