Au bas de cette réponse se trouve un code de référence, car vous avez précisé que vous êtes intéressé par les performances plutôt que d'éviter arbitrairement les for
boucles.
En fait, je pense que les for
boucles sont probablement l'option la plus performante ici. Depuis que le "nouveau" moteur JIT (2015b) a été introduit ( source ), les for
boucles ne sont pas intrinsèquement lentes - en fait, elles sont optimisées en interne.
Vous pouvez voir dans le benchmark que l' mat2cell
option offerte par ThomasIsCoding ici est très lente ...
Si nous nous débarrassons de cette ligne pour rendre l'échelle plus claire, alors ma splitapply
méthode est assez lente, l' option accumarray d' Obchardon est un peu meilleure, mais les options les plus rapides (et comparables) utilisent soit arrayfun
(comme l'a également suggéré Thomas) soit une for
boucle. Notez que arrayfun
c'est essentiellement une for
boucle déguisée pour la plupart des cas d'utilisation, donc ce n'est pas une cravate surprenante!
Je vous recommande d'utiliser une for
boucle pour une meilleure lisibilité du code et les meilleures performances.
Modifier :
Si nous supposons que le bouclage est l'approche la plus rapide, nous pouvons faire quelques optimisations autour de la find
commande.
Plus précisément
Rendez M
logique. Comme le montre le graphique ci-dessous, cela peut être plus rapide pour les relativement petits M
, mais plus lent avec le compromis de conversion de type pour les grands M
.
Utilisez une logique M
pour indexer un tableau 1:size(M,2)
au lieu d'utiliser find
. Cela évite la partie la plus lente de la boucle (la find
commande) et l'emporte sur la surcharge de conversion de type, ce qui en fait l'option la plus rapide.
Voici ma recommandation pour de meilleures performances:
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
Je l'ai ajouté au benchmark ci-dessous, voici la comparaison des approches de style boucle:
Code de référence:
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
boucles? Pour ce problème, avec les versions modernes de MATLAB, je soupçonne fortement qu'unefor
boucle est la solution la plus rapide. Si vous avez un problème de performances, je soupçonne que vous cherchez au mauvais endroit la solution basée sur des conseils obsolètes.