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


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

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

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

8 71

8 ответов:

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

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

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

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

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

предположим, у вас есть функция, которую вы хотите применить к каждому элементу A (называется "my_func"). Сначала вы создаете

еще один трюк, чтобы использовать 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

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

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

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

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

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

или

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

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


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

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

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

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

вы хотите имитировать N-вложенные циклы.

итерация через N-dimmensional массив можно рассматривать как увеличение n-значного числа.

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

пример:

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

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, к сожалению.