Na dole tej odpowiedzi znajduje się kod porównawczy, ponieważ wyjaśniłeś, że interesuje Cię wydajność, a nie arbitralne unikanie for
pętli.
W rzeczywistości myślę, że for
pętle są prawdopodobnie najbardziej wydajną opcją tutaj. Od czasu wprowadzenia „nowego” (2015b) silnika JIT pętle ( źródłowe ) for
nie są z natury wolne - w rzeczywistości są zoptymalizowane wewnętrznie.
Można zobaczyć od benchmarku, że mat2cell
opcja oferowana przez ThomasIsCoding tutaj jest bardzo powolny ...
Jeśli pozbywamy się tej linii, aby skala była wyraźniejsza, wówczas moja splitapply
metoda jest dość powolna, opcja akumulacji tablicy obchardon jest nieco lepsza, ale najszybsze (i porównywalne) opcje albo używają arrayfun
(jak sugeruje Thomas), albo for
pętli. Pamiętaj, że w większości przypadków arrayfun
jest to for
ukryta pętla, więc nie jest to zaskakujący remis!
Polecam użycie for
pętli dla zwiększenia czytelności kodu i najlepszej wydajności.
Edytuj :
Jeśli założymy, że zapętlenie jest najszybszym podejściem, możemy dokonać optymalizacji wokół find
polecenia.
konkretnie
Zrób M
logiczne. Jak pokazuje poniższy wykres, może to być szybsze dla stosunkowo małych M
, ale wolniejsze z kompromisem konwersji typu dla dużych M
.
Użyj logiki, M
aby zindeksować tablicę 1:size(M,2)
zamiast używać find
. Pozwala to uniknąć najwolniejszej części pętli ( find
polecenia) i przewyższa narzut związany z konwersją typów, co czyni ją najszybszą opcją.
Oto moja rekomendacja dla najlepszej wydajności:
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
Dodałem to do poniższego testu porównawczego, oto porównanie podejść w stylu pętli:
Kod porównawczy:
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
pętli? W przypadku tego problemu, w przypadku nowoczesnych wersji MATLAB, podejrzewam, żefor
pętla będzie najszybszym rozwiązaniem. Jeśli masz problem z wydajnością, podejrzewam, że szukasz niewłaściwego miejsca na rozwiązanie w oparciu o nieaktualne porady.