У меня проблема. Мне нужно перебрать каждый элемент 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
Это полезно, если вам не нужно знать, на каком i, 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
Как видите, 8-й элемент - это номер 7. Фактически, функция find возвращает свои результаты в виде линейного индекса.
find(A>6) ans = 1 6 8
В результате мы можем получить доступ к каждому элементу по очереди общего массива nd с помощью одного цикла. Например, если мы хотим возвести элементы 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)
в одном цикле for. Вы также можете использовать несколько функций:arrayfun
иcellfun
.Давайте сначала предположим, что у вас есть функция, которую вы хотите применить к каждому элементу
A
(вызываемогоmy_func
). Сначала вы создаете дескриптор функции для этой функции:Если
A
это матрица (типа double, single и т. Д.) Произвольной размерности, вы можете использоватьarrayfun
для примененияmy_func
к каждому элементу:Если
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'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 .
источник
1 : array(:)
эквивалентно1 : array(1)
. Это не повторяет все элементы, поэтому время выполнения у вас быстрое. Более того,rand
генерирует числа с плавающей запятой , и это1 : array(:)
приведет к созданию пустого массива, поскольку ваш оператор пытается найти увеличивающийся вектор с его начальным значением, равным 1, с конечным значением в виде числа с плавающей запятой с диапазоном,[0,1)
исключающим 1 при увеличении шаги 1. Нет такого возможного вектора, что приводит к пустому вектору. Вашfor
цикл не запускается, поэтому ваше утверждение неверно. -1 голос. извиняюсь.reshape(...)
.1 : array(:)
в командной строке после созданияarray
. Получается пустая матрица? если да, то ваш код не работает. Я оставляю свой голос, потому что вы даете ложную информацию.Если вы посмотрите глубже на другие варианты использования,
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-вложенные циклы for.
Итерацию по 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-значного числа. Вы можете сделать это таким образом, что можете начать с любого значения числа и увеличивать / уменьшать цифры на любые числа. Таким образом вы можете моделировать вложенные циклы for, которые начинаются где-то в таблице и заканчиваются не в конце.
Однако это непростая задача. К сожалению, я не могу помочь с обозначением Matlab.
источник