Am Ende dieser Antwort steht ein Benchmarking-Code, da Sie klargestellt haben, dass Sie an Leistung interessiert sind, anstatt for
Schleifen willkürlich zu vermeiden .
Tatsächlich denke ich, dass for
Loops hier wahrscheinlich die performanteste Option sind. Seit Einführung der "neuen" (2015b) JIT-Engine sind ( Quell- ) for
Schleifen nicht von Natur aus langsam - tatsächlich werden sie intern optimiert.
Sie können dem Benchmark mat2cell
entnehmen, dass die von ThomasIsCoding hier angebotene Option sehr langsam ist ...
Wenn wir diese Linie splitapply
entfernen , um die Skala klarer zu machen, ist meine Methode ziemlich langsam. Obchardons Accumarray-Option ist etwas besser, aber die schnellsten (und vergleichbaren) Optionen verwenden entweder arrayfun
(wie auch von Thomas vorgeschlagen) oder eine for
Schleife. Beachten Sie, dass dies für die meisten Anwendungsfälle im arrayfun
Grunde genommen eine for
verschleierte Schleife ist. Dies ist also keine überraschende Verbindung!
Ich würde empfehlen, eine for
Schleife zu verwenden, um die Lesbarkeit des Codes und die beste Leistung zu verbessern.
Bearbeiten :
Wenn wir davon ausgehen, dass das Schleifen der schnellste Ansatz ist, können wir einige Optimierungen am find
Befehl vornehmen .
Speziell
Machen Sie M
logisch. Wie das folgende Diagramm zeigt, kann dies für relativ kleine Personen schneller sein M
, jedoch langsamer, wenn die Typkonvertierung für große Geräte abgewogen wird M
.
Verwenden Sie eine Logik, M
um ein Array zu indizieren, 1:size(M,2)
anstatt es zu verwenden find
. Dies vermeidet den langsamsten Teil der Schleife (den find
Befehl) und überwiegt den Typkonvertierungsaufwand, was ihn zur schnellsten Option macht.
Hier ist meine Empfehlung für die beste Leistung:
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
Ich habe dies dem unten stehenden Benchmark hinzugefügt. Hier ist der Vergleich von Loop-Ansätzen:
Benchmarking-Code:
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
Schleifen vermeidet ? Für dieses Problem vermute ich bei modernen Versionen von MATLAB stark, dass einefor
Schleife die schnellste Lösung ist. Wenn Sie ein Leistungsproblem haben, suchen Sie vermutlich am falschen Ort nach einer Lösung, die auf veralteten Ratschlägen basiert.