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

65

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

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

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

  • 12
    Примечание по терминологии Matlab: Matlab имеет небольшое количество основных типов данных. Наиболее важными являются: структура, матрица и массив ячеек. При ссылке на части матрицы обычно используют термин «элемент» и резервируют термин «ячейка» для ссылки на части массива ячеек. Массивы и матрицы ячеек имеют множество синтаксических и семантических различий, хотя обе являются N-мерными структурами данных.
  • 1
    Отметил и изменил. Спасибо!
Показать ещё 1 комментарий
Теги:
arrays
multidimensional-array
matrix
iteration

8 ответов

78
Лучший ответ

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

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

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

  • 1
    Также, если вы хотите восстановить индексы по какой-либо причине, вы все равно можете использовать эти две простые команды: I = cell(1, ndims(array)); [I{:}] = ind2sub(size(array),idx); ,
31

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

  • 0
    Индексирование в 64-битной MATLAB действительно правильно разрешает 64-битные подписки. Например: x = ones(1,2^33,'uint8'); x(2^33) работает как ожидалось.
  • 0
    @ Эдрик - Конечно, это поведение, которое наверняка изменилось бы за годы (и многие релизы) с тех пор, как я сделал это заявление. Спасибо за проверку, хотя.
Показать ещё 1 комментарий
14

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

Другим трюком является использование 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
  • 0
    +1 для ind2sub и sub2ind
  • 0
    +1 за хороший пример того, как MATLAB нарушает печать утки.
1

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

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

или

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

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


Отказ

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

  • 1
    1 : array(:) эквивалентен 1 : array(1) . Это не повторяет все элементы, поэтому время выполнения у вас быстрое. Более того, rand генерирует числа с плавающей запятой , и, таким образом, выполнение 1 : array(:) приведет к созданию пустого массива, поскольку ваш оператор пытается найти растущий вектор с его начальным значением равным 1 с конечным значением в виде числа с плавающей запятой с диапазон [0,1) исключая 1 при увеличении с шагом 1. Нет такого возможного вектора, который приводит к пустому вектору. Ваш цикл for не выполняется, и поэтому ваше утверждение неверно. -1 голос. извиняюсь.
  • 0
    @rayryeng вы не правы. массив (:) не эквивалентен 1: массив (1). Он любит reshape(...) .
Показать ещё 5 комментариев
1

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

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

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

0

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

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

  • 0
    Я не совсем понимаю, как этот порядок циклов будет повторяться по всем элементам матрицы. Например, если у вас матрица 3 на 4 (с 12 элементами), ваш внутренний цикл будет повторяться только 7 раз.
  • 0
    он должен повторяться по каждому измерению матрицы. Внешний цикл перебирает размерность, внутренний цикл - размер этого измерения. По крайней мере, это идея. Как и все остальные, если все, что он хочет, это каждая ячейка, то индексирование лайнера лучше. Если он хочет перебрать каждое измерение, ему придется сделать что-то похожее на это.
Показать ещё 1 комментарий
-1

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню