In fondo a questa risposta c'è un codice di benchmarking, poiché hai chiarito che sei interessato alle prestazioni piuttosto che evitare arbitrariamente i for
loop.
In effetti, penso che i for
loop siano probabilmente l'opzione più performante qui. Da quando è stato introdotto il "nuovo" motore (2015b) JIT, i loop (di origine ) for
non sono intrinsecamente lenti - in realtà sono ottimizzati internamente.
Dal benchmark puoi vedere che l' mat2cell
opzione offerta da ThomasIsCoding qui è molto lenta ...
Se ci liberiamo di quella linea per rendere più chiara la scala, allora il mio splitapply
metodo è abbastanza lento, l' opzione accumarray di obchardon è un po 'migliore, ma le opzioni più veloci (e comparabili) stanno usando arrayfun
(come suggerito anche da Thomas) o un for
loop. Si noti che arrayfun
è praticamente un for
circuito nascosto sotto mentite spoglie per la maggior parte dei casi d'uso, quindi non è un legame sorprendente!
Consiglierei di utilizzare un for
loop per una maggiore leggibilità del codice e le migliori prestazioni.
Modifica :
Se assumiamo che il looping sia l'approccio più veloce, possiamo fare alcune ottimizzazioni attorno al find
comando.
In particolare
Rendi M
logico. Come mostra la trama sotto, questo può essere più veloce per relativamente piccoli M
, ma più lento con il compromesso della conversione del tipo per grandi M
.
Utilizzare un logico M
per indicizzare un array 1:size(M,2)
invece di utilizzare find
. Ciò evita la parte più lenta del ciclo (il find
comando) e supera il sovraccarico di conversione del tipo, rendendolo l'opzione più rapida.
Ecco la mia raccomandazione per le migliori prestazioni:
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
Ho aggiunto questo al benchmark di seguito, ecco il confronto degli approcci in stile loop:
Codice di benchmarking:
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
loop? Per questo problema, con le versioni moderne di MATLAB, sospetto fortemente che unfor
loop sia la soluzione più veloce. Se hai un problema di prestazioni, sospetto che tu stia cercando la soluzione sbagliata in base a consigli obsoleti.