У нижній частині цієї відповіді є деякий код бенчмаркінгу, оскільки ви уточнили, що вас цікавить ефективність, а не довільно уникати for
циклів.
Насправді, я думаю, for
петлі - це, мабуть, найефективніший варіант тут. З моменту впровадження «нового» (2015b) двигуна JIT ( джерело )for
петлі по суті не повільні - адже вони оптимізовані всередині країни.
З еталону видно, що mat2cell
опція, запропонована ThomasIsCoding тут , дуже повільна ...
Якщо ми позбудемося цього рядка, щоб зробити splitapply
ясність чіткішою, то мій метод досить повільний, варіант обхардона акумуляторного варіанту трохи кращий, але найшвидші (і порівнянні) варіанти або використовуються arrayfun
(як це запропонував Томас), або for
цикл. Зауважте, що arrayfun
в основному це for
петля, замаскована для більшості випадків використання, тому це не дивно, що це стосується!
Я рекомендую вам використовувати for
цикл для підвищення читабельності коду та найкращої продуктивності.
Редагувати :
Якщо припустити, що циклічне підключення - це найшвидший підхід, ми можемо зробити деякі оптимізації навколо find
команди.
Конкретно
Зробити M
логічним. Як показано на рисунку нижче, це може бути швидше відносно невеликим M
, але повільніше, коли компроміс конверсії типів для великихM
.
Використовуйте логічне, M
щоб індексувати масив, 1:size(M,2)
а не використовувати find
. Це дозволяє уникнути найповільнішої частини циклу ( 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
Я додав це до еталону нижче, ось порівняння підходів у стилі циклу:
Код бенчмаркінгу:
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
циклів? З цієї проблеми, в сучасних версіях MATLAB, я сильно підозрюю, щоfor
цикл є найшвидшим рішенням. Якщо у вас є проблеми з роботою, я підозрюю, що ви шукаєте рішення в неправильному місці на основі застарілих порад.