У меня проблема. Мне нужно перебирать каждый элемент в n-мерной матрице в MATLAB. Проблема в том, что я не знаю, как это сделать для произвольного числа измерений. Я знаю, что могу сказать
for i = 1:size(m,1)
for j = 1:size(m,2)
for k = 1:size(m,3)
и т.д., но есть ли способ сделать это для произвольного числа измерений?
Вы можете использовать линейную индексацию для доступа к каждому элементу.
for idx = 1:numel(array)
element = array(idx)
....
end
Это полезно, если вам не нужно знать, что я, j, k, вы находитесь. Однако, если вам не нужно знать, в каком индексе вы находитесь, вам, вероятно, лучше использовать arrayfun()
I = cell(1, ndims(array)); [I{:}] = ind2sub(size(array),idx);
,
Идея линейного индекса для массивов в 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, я считаю, что проблема решена для тех счастливчиков, которые это делают.)
x = ones(1,2^33,'uint8'); x(2^33)
работает как ожидалось.
Как указано в нескольких других ответах, вы можете перебирать все элементы в матрице 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);
Другим трюком является использование 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
эти решения быстрее (около 11%), чем при использовании numel
;)
for idx = reshape(array,1,[]),
element = element + idx;
end
или
for idx = array(:)',
element = element + idx;
end
UPD. tnx @rayryeng для обнаруженной ошибки в последнем ответе
Информация о времени, на которую эта ссылка ссылалась, является неправильной и неточной из-за основной опечатки, которая была сделана (см. поток комментариев ниже, а также историю изменений - в частности посмотрите на первую версию этого ответа). Caveat Emptor.
1 : array(:)
эквивалентен 1 : array(1)
. Это не повторяет все элементы, поэтому время выполнения у вас быстрое. Более того, rand
генерирует числа с плавающей запятой , и, таким образом, выполнение 1 : array(:)
приведет к созданию пустого массива, поскольку ваш оператор пытается найти растущий вектор с его начальным значением равным 1 с конечным значением в виде числа с плавающей запятой с диапазон [0,1)
исключая 1 при увеличении с шагом 1. Нет такого возможного вектора, который приводит к пустому вектору. Ваш цикл for
не выполняется, и поэтому ваше утверждение неверно. -1 голос. извиняюсь.
reshape(...)
.
Вы можете сделать рекурсивную функцию для работы
L = size(M)
idx = zeros(L,1)
length(L)
как максимальную глубинуfor idx(depth) = 1:L(depth)
length(L)
, выполните операцию элемента, иначе вызовите функцию снова с помощью depth+1
Не так быстро, как векторизованные методы, если вы хотите проверить все точки, но если вам не нужно оценивать большинство из них, это может быть довольно экономичным временем.
Если вы посмотрите глубже на другие применения 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-мерный массив можно рассматривать как увеличение 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 к несчастью.