Na parte inferior desta resposta está um código de benchmarking, pois você esclareceu que está interessado em desempenho, em vez de evitar arbitrariamente for
loops.
Na verdade, acho que os for
loops são provavelmente a opção de melhor desempenho aqui. Desde que o "novo" (2015b) mecanismo JIT foi introduzido ( fonte ), os for
loops não são inerentemente lentos - na verdade, eles são otimizados internamente.
Você pode ver no benchmark que a mat2cell
opção oferecida por ThomasIsCoding aqui é muito lenta ...
Se nos livrarmos dessa linha para tornar a escala mais clara, meu splitapply
método é bastante lento, a opção accumarray do obchardon é um pouco melhor, mas as opções mais rápidas (e comparáveis) estão usando arrayfun
(como também sugerido por Thomas) ou um for
loop. Observe que arrayfun
é basicamente um for
loop disfarçado para a maioria dos casos de uso, portanto, este não é um empate surpreendente!
Eu recomendo que você use um for
loop para aumentar a legibilidade do código e o melhor desempenho.
Editar :
Se assumirmos que o loop é a abordagem mais rápida, podemos fazer algumas otimizações em torno do find
comando.
Especificamente
Faça M
lógica. Como mostra o gráfico abaixo, isso pode ser mais rápido para relativamente pequeno M
, mas mais lento com a troca de conversão de tipo para grande M
.
Use um lógico M
para indexar uma matriz em 1:size(M,2)
vez de usar find
. Isso evita a parte mais lenta do loop (o find
comando) e supera a sobrecarga de conversão de tipo, tornando-a a opção mais rápida.
Aqui está a minha recomendação para o melhor desempenho:
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
Adicionei isso à referência abaixo, eis a comparação de abordagens no estilo de loop:
Código de 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
loops? Para esse problema, nas versões modernas do MATLAB, suspeito fortemente que umfor
loop seja a solução mais rápida. Se você tiver um problema de desempenho, suspeito que esteja procurando o local errado, com base em conselhos desatualizados.