Al final de esta respuesta hay un código de evaluación comparativa, ya que usted aclaró que está interesado en el rendimiento en lugar de evitar arbitrariamente los for
bucles.
De hecho, creo que los for
bucles son probablemente la opción más eficaz aquí. Desde que se introdujo el "nuevo" (2015b) motor JIT ( fuente ), los for
bucles no son inherentemente lentos, de hecho, están optimizados internamente.
Puede ver desde el punto de referencia que la mat2cell
opción ofrecida por ThomasIsCoding aquí es muy lenta ...
Si nos deshacemos de esa línea para splitapply
aclarar la escala, entonces mi método es bastante lento, la opción accumarray de obchardon es un poco mejor, pero las opciones más rápidas (y comparables) están usando arrayfun
(como también lo sugiere Thomas) o un for
bucle. Tenga en cuenta que arrayfun
es básicamente un for
bucle disfrazado para la mayoría de los casos de uso, ¡así que no es un lazo sorprendente!
Le recomendaría que use un for
bucle para aumentar la legibilidad del código y el mejor rendimiento.
Editar :
Si suponemos que el bucle es el enfoque más rápido, podemos hacer algunas optimizaciones en torno al find
comando.
Específicamente
Hacer M
lógico Como se muestra en el gráfico a continuación, esto puede ser más rápido para relativamente pequeño M
, pero más lento con el intercambio de conversión de tipo para grande M
.
Use un lógico M
para indexar una matriz en 1:size(M,2)
lugar de usar find
. Esto evita la parte más lenta del bucle (el find
comando) y supera la sobrecarga de conversión de tipo, por lo que es la opción más rápida.
Aquí está mi recomendación para el mejor rendimiento:
function A = f_forlooplogicalindexing( M )
M = logical(M);
k = 1:size(M,2);
N = size(M,1);
A = cell(N,1);
for r = 1:N
A{r} = k(M(r,:));
end
end
He agregado esto al punto de referencia a continuación, aquí está la comparación de los enfoques de estilo de bucle:
Código de evaluación comparativa:
rng(904); % Gives OP example for randi([0,1],3)
p = 2:12;
T = NaN( numel(p), 7 );
for ii = p
N = 2^ii;
M = randi([0,1],N);
fprintf( 'N = 2^%.0f = %.0f\n', log2(N), N );
f1 = @()f_arrayfun( M );
f2 = @()f_mat2cell( M );
f3 = @()f_accumarray( M );
f4 = @()f_splitapply( M );
f5 = @()f_forloop( M );
f6 = @()f_forlooplogical( M );
f7 = @()f_forlooplogicalindexing( M );
T(ii, 1) = timeit( f1 );
T(ii, 2) = timeit( f2 );
T(ii, 3) = timeit( f3 );
T(ii, 4) = timeit( f4 );
T(ii, 5) = timeit( f5 );
T(ii, 6) = timeit( f6 );
T(ii, 7) = timeit( f7 );
end
plot( (2.^p).', T(2:end,:) );
legend( {'arrayfun','mat2cell','accumarray','splitapply','for loop',...
'for loop logical', 'for loop logical + indexing'} );
grid on;
xlabel( 'N, where M = random N*N matrix of 1 or 0' );
ylabel( 'Execution time (s)' );
disp( 'Done' );
function A = f_arrayfun( M )
A = arrayfun(@(r) find(M(r,:)),1:size(M,1),'UniformOutput',false);
end
function A = f_mat2cell( M )
[i,j] = find(M.');
A = mat2cell(i,arrayfun(@(r) sum(j==r),min(j):max(j)));
end
function A = f_accumarray( M )
[val,ind] = ind2sub(size(M),find(M.'));
A = accumarray(ind,val,[],@(x) {x});
end
function A = f_splitapply( M )
[r,c] = find(M);
A = splitapply( @(x) {x}, c, r );
end
function A = f_forloop( M )
N = size(M,1);
A = cell(N,1);
for r = 1:N
A{r} = find(M(r,:));
end
end
function A = f_forlooplogical( M )
M = logical(M);
N = size(M,1);
A = cell(N,1);
for r = 1:N
A{r} = find(M(r,:));
end
end
function A = f_forlooplogicalindexing( M )
M = logical(M);
k = 1:size(M,2);
N = size(M,1);
A = cell(N,1);
for r = 1:N
A{r} = k(M(r,:));
end
end
for
bucles? Para este problema, con las versiones modernas de MATLAB, sospecho que unfor
bucle es la solución más rápida. Si tiene un problema de rendimiento, sospecho que está buscando la solución en el lugar equivocado en base a consejos desactualizados.