Как мне перебрать каждый элемент в n-мерной матрице в MATLAB? - PullRequest
76 голосов
/ 17 апреля 2009

У меня проблема. Мне нужно перебрать каждый элемент в n-мерной матрице в MATLAB. Проблема в том, что я не знаю, как это сделать для произвольного числа измерений. Я знаю, что могу сказать

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

и так далее, но есть ли способ сделать это для произвольного числа измерений?

Ответы [ 8 ]

89 голосов
/ 17 апреля 2009

Вы можете использовать линейное индексирование для доступа к каждому элементу.

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

Это полезно, если вам не нужно знать, что вы, j, k. Однако, если вам не нужно знать, по какому индексу вы находитесь, вам, вероятно, лучше использовать arrayfun ()

33 голосов
/ 17 апреля 2009

Идея линейного индекса для массивов в 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

Как видите, восьмой элемент - это число 7. На самом деле функция find возвращает свои результаты в виде линейного индекса.

find(A>6)
ans =
     1
     6
     8

В результате мы можем получить доступ к каждому элементу по очереди из общего массива n-d, используя один цикл. Например, если мы хотим возвести в квадрат элементы 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, я считаю, что проблема была решена для тех счастливчиков, которые используют.)

15 голосов
/ 17 апреля 2009

Как указано в нескольких других ответах, вы можете перебирать все элементы в матрице A (любого измерения), используя линейный индекс от 1 до цифра (A) в одном цикле for. Есть несколько других приемов, которые вы можете использовать: ARRAYFUN и CELLFUN .

Давайте сначала предположим, что у вас есть функция, которую вы хотите применить к каждому элементу 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_func», они помещаются в outArgs , который будет иметь тот же размер / размер, что и A .

Одно предупреждение на выходах ... если "my_func" возвращает выходы разных размеров и типов, когда он работает с различными элементами A , то outArgs необходимо будет преобразовать в массив ячеек. Это делается путем вызова ARRAYFUN или CELLFUN с дополнительной парой параметр / значение:

outArgs = arrayfun(fcn,A,'UniformOutput',false);
outArgs = cellfun(fcn,A,'UniformOutput',false);
13 голосов
/ 17 апреля 2009

Еще один прием - использовать ind2sub и sub2ind. В сочетании с 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 голос
/ 09 апреля 2015

эти решения быстрее (около 11%), чем при использовании numel;)

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

или

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

UPD. tnx @rayryeng за обнаруженную ошибку в последнем ответе


Отказ от ответственности

Информация о времени, на которую ссылалась эта запись, неверна и неточна из-за фундаментальной опечатки (см. Поток комментариев ниже, а также история редактирования - специально посмотрите на первую версию этого ответа ). Предостережение Emptor .

1 голос
/ 16 ноября 2012

Вы можете заставить рекурсивную функцию выполнять работу

  • Пусть L = size(M)
  • Пусть idx = zeros(L,1)
  • Взять length(L) за максимальную глубину
  • Петля for idx(depth) = 1:L(depth)
  • Если ваша глубина length(L), выполните операцию элемента, иначе вызовите функцию еще раз с depth+1

Не так быстро, как векторизованные методы, если вы хотите проверить все точки, но если вам не нужно оценивать большинство из них, это может значительно сэкономить время.

0 голосов
/ 10 марта 2010

Вы хотите смоделировать n-вложенные циклы.

Итерация по n-мерному массиву может рассматриваться как увеличение числа из n цифр.

В каждом измерении у нас столько цифр, сколько и длина измерения.

Пример:

Предположим, у нас был массив (матрица)

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 для второй и пять для третьей цифры

Нам нужно увеличить число, чтобы мы получили последовательность

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-значного числа. Вы можете сделать это таким образом, чтобы начать с любого значения числа и увеличивать / уменьшать цифры на любые числа. Таким образом, вы можете моделировать вложенные циклы, которые начинаются где-то в таблице и заканчиваются не в конце.

Это непростая задача. К сожалению, я не могу помочь с обозначением Matlab.

0 голосов
/ 17 апреля 2009

Если вы посмотрите глубже на другие варианты использования size, вы увидите, что вы можете получить вектор размера каждого измерения. Эта ссылка показывает вам документацию:

www.mathworks.com / доступ / Служба поддержки / помощь / Techdoc / исх / size.html

Получив вектор размера, выполните итерацию по этому вектору. Примерно так (простите мой синтаксис, так как я не использовал Matlab с колледжа):

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

Превратите это в настоящий синтаксис Matlab-Legal, и я думаю, что он сделает то, что вы хотите.

Кроме того, вы сможете выполнять линейное индексирование, как описано здесь .

...