Как выполнить итерацию каждого элемента в 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 к несчастью.