Matlab과 함께 일하고 있습니다.
이진 정사각 행렬이 있습니다. 각 행마다 1 이상의 항목이 있습니다.이 행렬의 각 행을 통과하여 해당 1의 인덱스를 반환하여 셀 항목에 저장하려고합니다.
Matlab에서는 for 루프가 실제로 느리기 때문에이 행렬의 모든 행을 반복하지 않고이 작업을 수행 할 수있는 방법이 있는지 궁금합니다.
예를 들어, 내 매트릭스
M = 0 1 0
1 0 1
1 1 1
결국에는
A = [2]
[1,3]
[1,2,3]
A
세포도 마찬가지 입니다.
for 루프를 사용하지 않고 결과를 더 빨리 계산할 목적으로이 목표를 달성 할 수있는 방법이 있습니까?
답변
이 답변의 맨 아래에는 임의의 for
루프를 피하는 것이 아니라 성능에 관심이 있다는 것을 분명히 했으므로 벤치마킹 코드가 있습니다.
실제로 for
루프는 아마도 가장 성능이 좋은 옵션 이라고 생각 합니다. “새로운”(2015b) JIT 엔진이 도입 된 이후 ( 소스 ) for
루프는 본질적으로 느리지 않습니다. 실제로 내부적으로 최적화되어 있습니다.
것을 당신은 벤치 마크에서 볼 수 mat2cell
ThomasIsCoding에 의해 제공되는 옵션은 여기에 매우 느립니다 …
우리가 스케일을 더 명확하게하기 위해 그 라인을 제거하면 내 splitapply
방법이 상당히 느리고 obchardon의 accumarray 옵션 이 조금 더 좋지만 가장 빠른 (그리고 비교 가능한) 옵션은 arrayfun
(Thomas가 제안한대로) 또는 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
답변
arrayfun
아래처럼 시도해 볼 수 있습니다.M
A = arrayfun(@(r) find(M(r,:)),1:size(M,1),'UniformOutput',false)
A =
{
[1,1] = 2
[1,2] =
1 3
[1,3] =
1 2 3
}
또는 (에 의한 느린 접근 mat2cell
)
[i,j] = find(M.');
A = mat2cell(i,arrayfun(@(r) sum(j==r),min(j):max(j)))
A =
{
[1,1] = 2
[2,1] =
1
3
[3,1] =
1
2
3
}
답변
편집 : 벤치 마크를 추가하면 결과 가 for 루프가보다 효율적이라는 것을 알 수accumarray
있습니다.
당신은 사용할 수 있습니다 find
및 accumarray
:
[c, r] = find(A');
C = accumarray(r, c, [], @(v) {v'});
열별로 그룹화 A'
되므로 행렬이 바뀝니다 ( ) find
.
예:
A = [1 0 0 1 0
0 1 0 0 0
0 0 1 1 0
1 0 1 0 1];
% Find nonzero rows and colums
[c, r] = find(A');
% Group row indices for each columns
C = accumarray(r, c, [], @(v) {v'});
% Display cell array contents
celldisp(C)
산출:
C{1} =
1 4
C{2} =
2
C{3} =
3 4
C{4} =
1 3 5
기준:
m = 10000;
n = 10000;
A = randi([0 1], m,n);
disp('accumarray:')
tic
[c, r] = find(A');
C = accumarray(r, c, [], @(v) {v'});
toc
disp(' ')
disp('For loop:')
tic
C = cell([size(A,1) 1]);
for i = 1:size(A,1)
C{i} = find(A(i,:));
end
toc
결과:
accumarray:
Elapsed time is 2.407773 seconds.
For loop:
Elapsed time is 1.671387 seconds.
for 루프는 다음보다 효율적입니다 accumarray
.
답변
accumarray 사용 :
M = [0 1 0
1 0 1
1 1 1];
[val,ind] = find(M.');
A = accumarray(ind,val,[],@(x) {x});