Ở dưới cùng của câu trả lời này là một số mã điểm chuẩn, vì bạn đã làm rõ rằng bạn quan tâm đến hiệu suất thay vì tự ý tránh for
các vòng lặp.
Trong thực tế, tôi nghĩ rằng for
các vòng lặp có lẽ là lựa chọn hiệu quả nhất ở đây. Vì công cụ JIT "mới" (2015b) đã được giới thiệu (các nguồn ) for
không phải là chậm - thực tế chúng được tối ưu hóa trong nội bộ.
Bạn có thể thấy từ điểm chuẩn rằng mat2cell
tùy chọn do ThomasIsCoding cung cấp ở đây rất chậm ...
Nếu chúng ta thoát khỏi dòng đó để làm cho tỷ lệ rõ ràng hơn, thì splitapply
phương pháp của tôi khá chậm, tùy chọn tích lũy của obchardon tốt hơn một chút, nhưng các tùy chọn nhanh nhất (và có thể so sánh) đang sử dụng arrayfun
(cũng như Thomas đề xuất) hoặc một for
vòng lặp. Lưu ý rằng arrayfun
về cơ bản là một for
vòng lặp ngụy trang cho hầu hết các trường hợp sử dụng, vì vậy đây không phải là một mối quan hệ đáng ngạc nhiên!
Tôi khuyên bạn nên sử dụng một for
vòng lặp để tăng khả năng đọc mã và hiệu suất tốt nhất.
Chỉnh sửa :
Nếu chúng ta giả định rằng lặp là cách tiếp cận nhanh nhất, chúng ta có thể thực hiện một số tối ưu hóa xung quanh find
lệnh.
Đặc biệt
Làm cho M
hợp lý. Như sơ đồ dưới đây cho thấy, điều này có thể nhanh hơn đối với tương đối nhỏ M
, nhưng chậm hơn với sự đánh đổi của chuyển đổi loại lớn M
.
Sử dụng một logic M
để lập chỉ mục một mảng 1:size(M,2)
thay vì sử dụng find
. Điều này tránh phần chậm nhất của vòng lặp ( find
lệnh) và vượt xa chi phí chuyển đổi loại, làm cho nó trở thành tùy chọn nhanh nhất.
Dưới đây là khuyến nghị của tôi cho hiệu suất tốt nhất:
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
Tôi đã thêm điều này vào điểm chuẩn bên dưới, đây là so sánh các cách tiếp cận kiểu vòng lặp:
Mã điểm chuẩn:
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
vòng lặp? Đối với vấn đề này, với các phiên bản hiện đại của MATLAB, tôi hoàn toàn nghi ngờ mộtfor
vòng lặp là giải pháp nhanh nhất. Nếu bạn gặp vấn đề về hiệu suất, tôi nghi ngờ bạn đang tìm sai giải pháp dựa trên lời khuyên đã lỗi thời.