2009-04-17 37 views
67

Tengo un problema. Necesito iterar a través de cada elemento en una matriz n-dimensional en MATLAB. El problema es que no sé cómo hacer esto para un número arbitrario de dimensiones. Sé que puedo decir¿Cómo puedo iterar a través de cada elemento en una matriz n-dimensional en MATLAB?

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

y así sucesivamente, pero ¿hay alguna manera de hacerlo por un número arbitrario de dimensiones?

+12

Nota terminológica de Matlab: Matlab tiene una pequeña cantidad de tipos de datos básicos. Los más importantes son: struct, matrix y cell array. Cuando se hace referencia a partes de una matriz, es común usar el término "elemento" y reservar el término "celda" para referirse a partes de una matriz de células. Las matrices y matrices de celdas tienen numerosas diferencias sintácticas y semánticas, aunque ambas son estructuras de datos en N dimensiones. –

+1

Observado y cambiado. ¡Gracias! – rlbond

+2

¿Puedo preguntar para qué necesita la iteración? Tal vez hay una forma "vectorizada" de hacerlo en su lugar ... –

Respuesta

78

Puede usar la indexación lineal para acceder a cada elemento.

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

Esto es útil si no necesita saber en qué i, j, k se encuentra. Sin embargo, si no necesita saber en qué índice se encuentra, probablemente sea mejor utilizar arrayfun()

+1

Además, si desea recuperar los índices por algún motivo, aún podría utilizar estos dos comandos simples: 'I = celda (1, ndims (matriz)); [I {:}] = ind2sub (tamaño (matriz), idx); '. – knedlsepp

-1

Si profundiza en los otros usos de size, verá que en realidad puede obtener un vector del tamaño de cada dimensión. Este enlace se muestra la documentación:

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

Después de conseguir el vector de tamaño, iterar sobre ese vector. Algo como esto (perdón por mi sintaxis ya que no he utilizado Matlab desde la universidad):

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

convertir esto en sintaxis de Matlab-legal real, y creo que sería hacer lo que quiera.

Además, debe poder hacer la indexación lineal como se describe here.

+0

No puedo ver cómo el orden de los bucles iterará sobre todos los elementos de una matriz. Por ejemplo, si tiene una matriz de 3 por 4 (con 12 elementos), su ciclo interno solo se repetirá 7 veces. – gnovice

+0

debe iterar sobre cada dimensión de la matriz. El ciclo externo itera sobre la dimensión, el ciclo interno sobre el tamaño de esa dimensión. Al menos, esa es la idea. Como todo el mundo dice, si todo lo que quiere es cada celda, la indexación del trazador de líneas es la mejor. Si quiere iterar sobre cada dimensión, tendrá que hacer algo similar a esto. –

+0

también, gracias por la edición. mi enlace era complicado y simplemente no funcionaría correctamente usando la forma de enlace habitual. Además, para expandir mi afirmación: todavía tendría que hacer muchos otros rastreos del índice (usar como un contador o algo así). Creo que tu o el enfoque de Andrew sería más fácil para lo que creo que está tratando de hacer. –

32

La idea de un índice lineal para matrices en matlab es importante. Una matriz en MATLAB es realmente solo un vector de elementos, colgado en la memoria. MATLAB le permite usar un índice de fila y columna, o un índice lineal único. Por ejemplo,

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

A(2,3) 
ans = 
    7 

A(8) 
ans = 
    7 

Podemos ver el orden en que se almacenan los elementos en la memoria desenrollando la matriz en un vector.

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

Como se puede ver, el elemento octavo es el número 7. De hecho, el hallazgo función devuelve sus resultados como un índice lineal.

find(A>6) 
ans = 
    1 
    6 
    8 

El resultado es, podemos acceder a cada elemento a su vez de una matriz general de n-d usando un solo bucle. Por ejemplo, si quisiéramos cuadrar los elementos de A (sí, ya sé que hay mejores maneras de hacer esto), se podría hacer esto:

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 

Hay muchas circunstancias en las que el índice lineal es más útil. La conversión entre el índice lineal y dos subíndices dimensionales (o superiores) se logra con las funciones sub2ind e ind2sub.

El índice lineal se aplica en general a cualquier matriz en matlab. Entonces puede usarlo en estructuras, arreglos de celdas, etc. El único problema con el índice lineal es cuando se hacen demasiado grandes. MATLAB usa un entero de 32 bits para almacenar estos índices. Entonces, si su matriz tiene más de un total de 2^32 elementos, el índice lineal fallará. En realidad, solo es un problema si usa matrices dispersas a menudo, cuando ocasionalmente esto puede causar un problema. (Aunque no uso una versión de MATLAB de 64 bits, creo que el problema se ha resuelto para las personas afortunadas que sí lo hacen).)

+0

La indexación en MATLAB de 64 bits permite correctamente los subíndices de 64 bits. Por ejemplo: 'x = unos (1,2^33, 'uint8'); x (2^33) 'funciona como se esperaba. – Edric

+0

@Edric - Por supuesto, este es un comportamiento que seguramente habría cambiado en los años (y muchos lanzamientos) desde que hice esa declaración. Gracias por comprobarlo. –

+0

:) No me di cuenta de la edad de la respuesta hasta después de que comencé - ¡la pregunta solo apareció en mi feed RSS, y ni siquiera noté que también la había respondido! – Edric

15

Como se señaló en algunas otras respuestas, se puede iterar sobre todos los elementos en una matriz A (de cualquier dimensión) utilizando un índice lineal de a Numel (A) en una sola para lazo. Hay un par de otros trucos que puede usar: ARRAYFUN y CELLFUN.

Supongamos primero que tiene una función que desea aplicar a cada elemento de A (llamado "my_func"). Primero se crea un function handle a esta función:

fcn = @my_func; 

Si Un es una matriz (de tipo doble, individual, etc.) de dimensión arbitraria, se puede utilizar para aplicar ARRAYFUN "mi_func" para cada elemento:

outArgs = arrayfun(fcn,A); 

Si un es una matriz de celdas de dimensión arbitraria, se puede utilizar para aplicar CELLFUN "mi_func" a cada celda:

outArgs = cellfun(fcn,A); 

La función "my_func" tiene que aceptar A como entrada. Si hay salidas de "my_func", estas se colocan en outArgs, que tendrá el mismo tamaño/dimensión que A.

Una advertencia en las salidas ... si "mi_func" devuelve salidas de diferentes tamaños y tipos cuando se opera sobre diferentes elementos de Un, luego outArgs tendrá que ser hecho en una serie de células. Esto se hace llamando ya sea ARRAYFUN o CELLFUN con un par parámetro/valor adicional:

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

Un otro truco es usar ind2sub y sub2ind. En conjunción con numel y size, esto puede permitirle hacer cosas como las siguientes, que crea una matriz de N dimensiones, y luego establece todos los elementos en la "diagonal" para ser 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 para ind2sub y sub2ind – catchmeifyoutry

+0

+1 para mostrar un buen ejemplo de cómo MATLAB rompe la tipificación de pato. –

-1

Quiere simular n-anidados para bucles.

La iteración a través de la matriz n-dimmensional puede verse como un aumento del número de n dígitos.

En cada dimmension tenemos tantos dígitos como la longitud de la dimmension.

Ejemplo:

supongamos que tenemos array (matriz)

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

en "para la notación" tenemos:

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

para simular este tendría que utilizar el "n -notación de números de dígitos "

Tenemos un número de 3 dígitos, con 3 dígitos para el primero, 4 para el segundo y cinco para thi rd dígitos

Tenemos que aumentar el número, por lo que se pueden conseguir la secuencia

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 

para que pueda escribir el código para aumentar dicho número de n dígitos. Puede hacerlo de tal manera que pueda comenzar con cualquier valor del número y aumentar/disminuir los dígitos por cualquier número. De esta forma, puede simular bucles anidados que comienzan en algún lugar de la tabla y terminan al final.

Sin embargo, esta no es una tarea fácil. No puedo ayudar con la notación matlab desafortunadamente.

1

Se podría hacer una función recursiva haga el trabajo

  • Vamos L = size(M)
  • Vamos idx = zeros(L,1)
  • Take length(L) como la profundidad máxima
  • Loop for idx(depth) = 1:L(depth)
  • Si su profundidad es length(L), hacer la operación del elemento; de lo contrario, vuelva a llamar a la función con depth+1

No es tan rápido como los métodos vectorizados si desea comprobar todos los puntos, pero si no necesita evaluar la mayoría de ellos, puede ser bastante un ahorro de tiempo.

1

estas soluciones son más rápido (aproximadamente 11%) que el uso de numel;)

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

o

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

UPD. @rayryeng TNX para el error detectado en última respuesta


Negación

La información de temporización que este post ha hecho referencia es incorrecta e inexacta debido a un error tipográfico fundamental que se hizo (ver comentarios corriente abajo, así como la edit history - mira específicamente la primera versión de esta respuesta). Caveat Emptor.

+1

'1: array (:)' es equivalente a '1: array (1)'. Esto no itera a través de todos los elementos, por lo que los tiempos de ejecución son rápidos. Además, 'rand' genera ** números de punto flotante **, por lo que' 1: array (:) 'produciría una matriz vacía ya que su declaración está tratando de encontrar un vector creciente con su valor inicial como 1 con una terminación valor como un número de punto flotante con un rango de '[0,1)' exclusivo de 1 en pasos crecientes de 1. No existe tal vector posible, lo que da como resultado un vector vacío. Su ciclo 'for' no se ejecuta, por lo que su reclamo es falso. -1 voto. lo siento. – rayryeng

+0

@rayryeng no tienes razón. array (:) no es equivalente a 1: array (1). Le gusta 'remodelar (...)'. – mathcow

+0

Acabo de ejecutar ese código en MATLAB. No funciona – rayryeng

Cuestiones relacionadas