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

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

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

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

Ответ 1

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

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

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

Ответ 2

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

Ответ 3

Как указывалось в нескольких других ответах, вы можете перебирать все элементы матрицы A (любого измерения), используя линейный индекс от 1 до numel(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);

Ответ 4

Другим трюком является использование ind2sub и sub2ind. В сочетании с numel и size это может позволить вам сделать что-то вроде следующего, что создает N-мерный массив, а затем устанавливает все элементы в диагонали "1".

d = zeros( 3, 4, 5, 6 ); % Let 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

Ответ 5

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

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

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

Ответ 6

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

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

или

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

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


Отказ

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

Ответ 7

Если вы посмотрите глубже на другие применения 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, и я думаю, что он сделает то, что вы хотите.

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

Ответ 8

Вы хотите имитировать 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 к несчастью.