Як здійснити ітерацію кожного елемента в n-мірній матриці в MATLAB?


87

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

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

і так далі, але чи є спосіб зробити це для довільної кількості розмірів?


13
Примітка щодо термінології Matlab: Matlab має невелику кількість основних типів даних. Найважливішими є: структура, матриця та масив комірок. Посилаючись на частини матриці, зазвичай використовується термін "елемент", а термін "комірка" резервується для посилання на частини масиву комірок. Клітинні масиви та матриці мають численні синтаксичні та семантичні відмінності, незважаючи на те, що обидва вони є N-мірними структурами даних.
Містер Фооз,

3
Чи можу я запитати, для чого вам потрібна ітерація? Можливо, існує "векторизований" спосіб зробити це замість цього ...
Хосам Алі

Відповіді:


92

Ви можете використовувати лінійну індексацію для доступу до кожного елемента.

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

Це корисно, якщо вам не потрібно знати, на чому ви перебуваєте i, j, k. Однак, якщо вам не потрібно знати, в якому індексі ви знаходитесь, вам, мабуть, краще скористатися arrayfun ()


1
Крім того , якщо ви хочете , щоб відновити показники за якою - то причини, ви могли б з допомогою цих двох простих команд: I = cell(1, ndims(array)); [I{:}] = ind2sub(size(array),idx);.
knedlsepp

34

Ідея лінійного індексу для масивів у 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, я вважаю, що проблема вирішена для тих щасливих людей, які це роблять.)


Індексація в 64-розрядному MATLAB дійсно правильно дозволяє 64-розрядні індекси. Наприклад: x = ones(1,2^33,'uint8'); x(2^33)працює, як очікувалося.
Едрік,

@Edric - Звичайно, це поведінка, яка, безсумнівно, змінилася б протягом багатьох років (і багатьох випусків) з мого виступу. Дякую за перевірку.

:) Я не усвідомлював, скільки років було відповіді, аж після того, як прокоментував - запитання просто з’явилось у моєму RSS-каналі, і я навіть не помітив, що теж відповів на нього!
Едрік,

15

Як зазначалося в декількох інших відповідях, ви можете перебирати всі елементи в матриці 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);

13

Ще одна хитрість полягає у використанні 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

+1 за показ гарного прикладу того, як MATLAB ламає качиний набір.
Phillip Cloud

1

Ви можете змусити рекурсивну функцію виконувати роботу

  • Дозволяє L = size(M)
  • Дозволяє idx = zeros(L,1)
  • Взяти length(L)за максимальну глибину
  • Петля for idx(depth) = 1:L(depth)
  • Якщо ваша глибина length(L), виконайте операцію з елементом, інакше знову викликайте функцію за допомогоюdepth+1

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


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 голос. вибачте.
rayryeng

@rayryeng ти не маєш рації. array (:) не еквівалентно 1: array (1). Це подобається reshape(...).
mathcow

@rayryeng matlab r2013a + linux - це працює! ;) Я теж щойно запустив цей код
mathcow

Введіть 1 : array(:)у своєму командному рядку після створення array . Ви отримуєте порожню матрицю? якщо так, то ваш код не працює. Я залишаю свій голос, бо ви даєте неправдиву інформацію.
rayryeng,

@rayryeng я розумію! так, ви маєте рацію, вибачте за нерозумну суперечку
mathcow

-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, і я думаю, що він зробить те, що ви хочете.

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


Я не зовсім можу зрозуміти, як це впорядкування циклів буде перебирати всі елементи матриці. Наприклад, якщо у вас є матриця 3 на 4 (з 12 елементами), ваш внутрішній цикл повториться лише 7 разів.
gnovice

він повинен перебирати кожен вимір матриці. Зовнішній цикл перебирає розмірність, внутрішній цикл - розмір цього розміру. Принаймні, така ідея. Як зазначають усі інші, якщо все, що він хоче, це кожна клітинка, найкраще індексувати вкладиші. Якщо він хоче повторити кожен вимір, йому доведеться зробити щось подібне до цього.
Еріх Мірабаль,

також, дякую за редагування. моє посилання було заплутаним і просто не працювало правильно, використовуючи звичайний спосіб посилання. Також, щоб розширити моє твердження: йому все одно довелося б зробити багато іншого відстеження індексу (використовуючи як лічильник або щось подібне). Я думаю, що підхід вам чи Ендрю був би простішим для того, що, на мою думку, він намагається зробити.
Еріх Мірабаль,

-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, на жаль.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.