В нижней части этого ответа приведен код тестирования, поскольку вы пояснили, что заинтересованы в производительности, а не в том, чтобы произвольно избегать циклов for
.
На самом деле, я думаю, что циклы for
, вероятно, самый производительный вариант здесь. Поскольку был введен «новый» (2015b) механизм JIT ( source ), циклы for
не являются медленными по своей природе - фактически они оптимизируются внутри.
Из теста можно видеть, что опция mat2cell
, предлагаемая ThomasIsCoding здесь , очень медленная ...
Если мы избавимся от этой строки, сделайте шкалу более понятной, тогда мой splitapply
метод будет довольно медленным, опция accmarray от obchardon немного лучше, но самые быстрые (и сопоставимые) варианты используют либо arrayfun
(что также предложено Томасом) или for
l oop. Обратите внимание, что arrayfun
в основном скрыт для for
l oop для большинства случаев использования, так что это не удивительно t ie!
Я бы порекомендовал вам использовать for
l oop для повышения читабельности кода и лучшей производительности.
Редактировать :
Если мы предположим, что цикл является самым быстрым подходом, мы можем сделать некоторые оптимизации вокруг команды find
.
В частности
Сделайте M
логичным. Как показано на графике ниже, это может быть быстрее для относительно небольших M
, но медленнее с компромиссом преобразования типов для больших M
.
Используйте логический M
индексировать массив 1:size(M,2)
вместо использования find
. Это позволяет избежать самой медленной части l oop (команда find
) и перевешивает накладные расходы на преобразование типов, что делает его самым быстрым вариантом.
Вот моя рекомендация для лучшей производительности:
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
Я добавил это в тест ниже, вот сравнение подходов в стиле l oop:
Код бенчмаркинга:
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