2009-04-17 14 views
67

Ho un problema. Ho bisogno di scorrere tutti gli elementi in una matrice n-dimensionale in MATLAB. Il problema è che non so come farlo per un numero arbitrario di dimensioni. So che posso direCome faccio a scorrere ogni elemento in una matrice n-dimensionale in MATLAB?

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

e così via, ma c'è un modo per farlo per un numero arbitrario di dimensioni?

+12

Nota terminologica Matlab: Matlab ha un numero ridotto di tipi di dati principali. I più importanti sono: struct, matrix e cell array. Quando ci si riferisce a parti di una matrice, è comune usare il termine "elemento" e riservare il termine "cella" per riferirsi a parti di un array di celle. Le matrici e le matrici di celle presentano numerose differenze sintattiche e semantiche, anche se entrambe sono strutture di dati N-dimensionali. –

+1

Notato e modificato. Grazie! – rlbond

+2

Posso chiedere a cosa serve l'iterazione? Forse c'è un modo "vettorializzato" per farlo invece ... –

risposta

78

È possibile utilizzare l'indicizzazione lineare per accedere a ciascun elemento.

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

Questo è utile se non è necessario sapere cosa io, j, k, ci si trova. Tuttavia, se non hai bisogno di sapere quale indice sei, probabilmente stai meglio usando arrayfun()

+1

Inoltre, se si desidera recuperare gli indici per qualche motivo, è comunque possibile utilizzare questi due semplici comandi: 'I = cell (1, ndims (array)); [I {:}] = ind2sub (size (array), idx); '. – knedlsepp

-1

Se si guarda più in profondità agli altri usi di size, è possibile notare che è possibile ottenere un vettore delle dimensioni di ciascuna dimensione. Questo link mostra la documentazione:

www.mathworks.com/access/helpdesk/help/techdoc/ref/size.html

Dopo aver ottenuto il vettore dimensioni, un'iterazione su quel vettore. Qualcosa di simile a questo (scusa la mia sintassi visto che non ho usato Matlab dai tempi del college):

d = size(m); 
dims = ndims(m); 
for dimNumber = 1:dims 
    for i = 1:d[dimNumber] 
     ... 

fare questo in reale sintassi Matlab-legale, e penso che sarebbe fare quello che vuoi.

Inoltre, si dovrebbe essere in grado di eseguire l'indicizzazione lineare come descritto here.

+0

Non riesco a vedere bene come quell'ordinamento di cicli itererà su tutti gli elementi di una matrice. Ad esempio, se si dispone di una matrice 3 per 4 (con 12 elementi), il ciclo interno eseguirà solo iterazioni 7 volte. – gnovice

+0

dovrebbe iterare su ogni dimensione della matrice. Il ciclo esterno esegue l'iterazione sulla dimensione, il ciclo interno sopra la dimensione di quella dimensione. Almeno, questa è l'idea. Come tutti gli altri stanno affermando, se tutto ciò che vuole è ogni cella, l'indicizzazione del liner è la migliore. Se vuole scorrere su ogni dimensione, dovrà fare qualcosa di simile a questo. –

+0

anche, grazie per la modifica. il mio collegamento era piuttosto complicato e non funzionava correttamente usando il solito modo di collegamento. Inoltre, per espandere la mia affermazione: avrebbe comunque dovuto eseguire molti altri tracciamenti dell'indice (usando come un contatore o qualcosa del genere). Penso che tu o l'approccio di Andrew sarebbe più facile per quello che penso stia cercando di fare. –

32

L'idea di un indice lineare per gli array in MATLAB è importante. Un array in MATLAB è in realtà solo un vettore di elementi, in memoria. MATLAB consente di utilizzare un indice di riga e colonna o un singolo indice lineare. Ad esempio,

A = magic(3) 
A = 
    8  1  6 
    3  5  7 
    4  9  2 

A(2,3) 
ans = 
    7 

A(8) 
ans = 
    7 

Possiamo vedere l'ordine in cui gli elementi sono memorizzati nella memoria srotolando l'array in un vettore.

A(:) 
ans = 
    8 
    3 
    4 
    1 
    5 
    9 
    6 
    7 
    2 

Come si può vedere, l'ottavo elemento è il numero 7. In effetti, la funzione find restituisce i risultati come un indice lineare.

find(A>6) 
ans = 
    1 
    6 
    8 

Il risultato è, possiamo accedere a ciascun elemento a sua volta di un generale matrice n-d utilizzando un unico ciclo. Per esempio, se volessimo far quadrare gli elementi di A (sì, lo so che ci sono modi migliori per fare questo), si potrebbe fare questo:

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 

Ci sono molte circostanze in cui l'indice lineare è più utile. La conversione tra l'indice lineare e due (o più alti) indici dimensionali viene eseguita con le funzioni sub2ind e ind2sub.

L'indice lineare si applica in generale a qualsiasi matrice in MATLAB. Quindi puoi usarlo su strutture, array di celle, ecc. L'unico problema con l'indice lineare è quando diventano troppo grandi. MATLAB usa un intero a 32 bit per memorizzare questi indici. Quindi se l'array ha più di 2^32 elementi in totale, l'indice lineare fallirà. È davvero solo un problema se usi spesso matrici sparse, quando occasionalmente questo causerà un problema. (Anche se non uso una versione MATLAB a 64 bit, credo che il problema sia stato risolto per quelle persone fortunate che lo fanno.)

+0

L'indicizzazione in MATLAB a 64 bit consente infatti correttamente gli indici a 64 bit. Ad esempio: 'x = ones (1,2^33, 'uint8'); x (2^33) 'funziona come previsto. – Edric

+0

@Edric - Ovviamente, questo è un comportamento che sicuramente sarebbe cambiato negli anni (e in molte versioni) da quando ho fatto quella dichiarazione. Grazie per il controllo però. –

+0

:) Non mi sono reso conto di quanti anni avesse la risposta fino a dopo aver commentato: la domanda è appena stata visualizzata nel mio feed RSS, e non ho nemmeno notato che avevo risposto anche io! – Edric

15

Come sottolineato in poche altre risposte, è possibile iterare su tutti gli elementi in una matrice Una (di qualsiasi dimensione) utilizzando un indice lineare da a Numel (A) in un singolo per ciclo continuo. Ci sono un paio di altri trucchi che puoi usare: ARRAYFUN e CELLFUN.

Supponiamo innanzitutto di avere una funzione che si desidera applicare a ciascun elemento di A (chiamato "my_func"). Per prima cosa creare un function handle a questa funzione:

fcn = @my_func; 

Se Un è una matrice (di tipo double, singolo, etc.) di dimensione arbitraria, è possibile utilizzare ARRAYFUN per applicare "my_func" per ogni elemento:

outArgs = arrayfun(fcn,A); 

Se un è un array di celle di dimensione arbitraria, è possibile utilizzare CELLFUN per applicare "my_func" per ogni cella:

outArgs = cellfun(fcn,A); 

La funzione "my_func" deve accettare A come input. Se ci sono uscite da "my_func", queste vengono posizionate in outArgs, che avrà la stessa dimensione/dimensione di A.

Un avvertimento sulle uscite ... se "my_func" restituisce uscite di diverse dimensioni e tipologie quando opera su diversi elementi di A, quindi outArgs dovrà essere realizzato in una matrice di celle. Questo viene fatto chiamando sia ARRAYFUN o CELLFUN con una coppia parametro/valore aggiunto:

outArgs = arrayfun(fcn,A,'UniformOutput',false); 
outArgs = cellfun(fcn,A,'UniformOutput',false); 
13

Un altro trucco è quello di utilizzare ind2sub e sub2ind. In concomitanza con numel e size, questo può farti fare cose come la seguente, che crea un array N-dimensionale, e quindi imposta tutti gli elementi sulla "diagonale" di essere 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 
+0

+1 per ind2sub e sub2ind – catchmeifyoutry

+0

+1 per mostrare un buon esempio di come MATLAB interrompe la digitazione. –

-1

Volete simulare n-nested per i loop.

L'iterazione attraverso la matrice n-dimensionale può essere vista come un aumento del numero di n cifre.

Ad ogni dimmensione abbiamo tante cifre quante la lunghezza della dimmensione.

Esempio:

Supponiamo di avere array (matrice)

int[][][] T=new int[3][4][5]; 

in "per la notazione" abbiamo:

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]=... 

per simulare questo si dovrà utilizzare il "n -digit number notation "

Abbiamo il numero a 3 cifre, con 3 cifre per il primo, 4 per il secondo e cinque per il thi rd cifre

Dobbiamo aumentare il numero, così avremmo ottenere la sequenza

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 

Così si può scrivere il codice per aumentare tale numero n cifre. Puoi farlo in modo tale da poter iniziare con qualsiasi valore del numero e aumentare/diminuire le cifre di qualsiasi numero. In questo modo puoi simulare i cicli nidificati che iniziano da qualche parte nel tavolo e terminano non alla fine.

Questo non è un compito facile però. Non posso aiutare sfortunatamente con la notazione MATLAB.

1

Si potrebbe fare una funzione ricorsiva fare il lavoro

  • Let L = size(M)
  • Let idx = zeros(L,1)
  • Prendere length(L) come la profondità massima
  • Loop for idx(depth) = 1:L(depth)
  • Se la profondità è length(L), fare l'operazione elemento, altrimenti chiamare di nuovo la funzione con depth+1

Non veloce come i metodi vettorizzati se si desidera controllare tutti i punti, ma se non è necessario valutare la maggior parte di essi può essere un bel risparmio di tempo.

1

queste soluzioni sono più veloci (circa 11%) rispetto all'utilizzo numel;)

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

o

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

UPD. tnx @rayryeng per errore rilevato in ultima risposta


responsabilità

Le informazioni di temporizzazione che questo post ha fatto riferimento non è corretto e imprecisa a causa di un errore di battitura fondamentale che è stato fatto (vedi commenti torrente sottostante, nonché la edit history - in particolare, guarda la prima versione di questa risposta). Caveat Emptor.

+1

'1: array (:)' è equivalente a '1: array (1)'. Questo non fa scorrere tutti gli elementi ed è per questo che i tuoi tempi di esecuzione sono veloci. Inoltre, 'rand' genera ** numeri in virgola mobile **, e così facendo' 1: array (:) 'produrrebbe un array vuoto mentre la tua istruzione sta cercando di trovare un vettore in aumento con il suo valore iniziale come 1 con un finale valore come numero a virgola mobile con un intervallo di '[0,1)' esclusivo di 1 in incrementi di 1. Non esiste un tale vettore possibile, che si traduce in un vettore vuoto. Il tuo ciclo 'for' non viene eseguito, quindi la tua richiesta è falsa. Voto -1. scusa. – rayryeng

+0

@rayryeng non hai ragione. array (:) non è equivalente a 1: array (1). A lui piace 'rimodellare (...)'. – mathcow

+0

Ho appena eseguito quel codice in MATLAB. Non funziona. – rayryeng