Как перебрать каждый элемент в n-мерной матрице в MATLAB?

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

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

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


person rlbond    schedule 17.04.2009    source источник
comment
Примечание по терминологии Matlab: Matlab имеет небольшое количество основных типов данных. Наиболее важными являются: структура, матрица и массив ячеек. При обращении к частям матрицы обычно используется термин «элемент», а термин «ячейка» резервируется для обозначения частей массива ячеек. Массивы ячеек и матрицы имеют многочисленные синтаксические и семантические различия, несмотря на то, что оба являются N-мерными структурами данных.   -  person Mr Fooz    schedule 17.04.2009
comment
Могу я спросить, для чего вам нужна итерация? Может быть, есть векторный способ сделать это вместо этого...   -  person Hosam Aly    schedule 10.11.2009


Ответы (8)


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

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

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

person Andrew    schedule 17.04.2009
comment
Кроме того, если вы хотите восстановить индексы по какой-либо причине, вы все равно можете использовать эти две простые команды: I = cell(1, ndims(array)); [I{:}] = ind2sub(size(array),idx);. - person knedlsepp; 04.01.2015

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

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

person Community    schedule 17.04.2009
comment
Индексирование в 64-битном MATLAB действительно корректно допускает 64-битные индексы. Например: x = ones(1,2^33,'uint8'); x(2^33) работает как положено. - person Edric; 19.11.2012
comment
@Edric - Конечно, это поведение наверняка изменилось бы с годами (и многими выпусками) с тех пор, как я сделал это заявление. Спасибо за проверку. - person ; 19.11.2012
comment
:) Я не осознавал, сколько лет ответу, пока не прокомментировал - вопрос только что появился в моей RSS-ленте, и я даже не заметил, что тоже ответил на него! - person Edric; 19.11.2012

Как указано в нескольких других ответах, вы можете перебирать все элементы в матрице A (любого измерения), используя линейный индекс от 1 до numel(A) в одном цикле for. Вы также можете использовать несколько функций: arrayfun и < a href="https://www.mathworks.com/help/matlab/ref/cellfun.html" rel="nofollow noreferrer">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);
person gnovice    schedule 17.04.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
person Edric    schedule 17.04.2009
comment
+1 за показ хорошего примера того, как MATLAB ломает утиную печать. - person Phillip Cloud; 03.07.2012

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

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

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

person Dennis Jaheruddin    schedule 15.11.2012

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

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

or

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

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


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

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

person mathcow    schedule 08.04.2015
comment
1 : array(:) эквивалентно 1 : array(1). Это не повторяет все элементы, поэтому ваше время выполнения быстрое. Кроме того, rand генерирует числа с плавающей запятой, поэтому выполнение 1 : array(:) приведет к созданию пустого массива, поскольку ваш оператор пытается найти возрастающий вектор с начальным значением 1 и конечным значением в виде числа с плавающей запятой. с диапазоном [0,1) без учета 1 с возрастающим шагом 1. Такого возможного вектора не существует, что приводит к пустому вектору. Ваш цикл for не работает, поэтому ваше утверждение ложно. -1 голос. Извините. - person rayryeng; 09.04.2015
comment
@rayryeng ты не прав. массив(:) не эквивалентен 1 : массив(1). Ему нравится reshape(...). - person mathcow; 09.04.2015
comment
@rayryeng matlab r2013a + linux - работает! ;) Я тоже только что запустил этот код - person mathcow; 09.04.2015
comment
Введите 1 : array(:) в командной строке после создания array . Вы получаете пустую матрицу? если да, то ваш код не работает. Я отказываюсь от своего голоса, потому что вы даете ложную информацию. - person rayryeng; 09.04.2015
comment
@rayryeng я понимаю! да, вы правы, извините за глупый спор - person mathcow; 09.04.2015
comment
Это нормально. Теперь ваш ответ правильный... Хотя и не сильно отличается от других ответов. Я удаляю свой отрицательный голос. - person rayryeng; 09.04.2015

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

www.mathworks.com/access/helpdesk/help/techdoc/ ссылка/размер.html

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

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

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

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

person Erich Mirabal    schedule 17.04.2009
comment
Я не совсем понимаю, как этот порядок циклов будет повторяться по всем элементам матрицы. Например, если у вас есть матрица 3 на 4 (с 12 элементами), ваш внутренний цикл будет повторяться только 7 раз. - person gnovice; 17.04.2009
comment
он должен перебирать каждое измерение матрицы. Внешний цикл повторяется по измерению, внутренний цикл по размеру этого измерения. По крайней мере, это идея. Как заявляют все остальные, если все, что ему нужно, это каждая ячейка, лучше всего использовать линейную индексацию. Если он хочет выполнить итерацию по каждому измерению, ему придется сделать что-то подобное. - person Erich Mirabal; 17.04.2009
comment
также спасибо за редактирование. моя ссылка была довольно запутанной и просто не работала бы правильно, используя обычный способ ссылки. Кроме того, чтобы расширить мое утверждение: ему все равно придется выполнять много другого отслеживания индекса (используя счетчик или что-то в этом роде). Я думаю, что вам или подходу Эндрю было бы проще сделать то, что я думаю, что он пытается сделать. - person Erich Mirabal; 18.04.2009

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

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

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

person bmegli    schedule 10.03.2010