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루프는 본질적으로 느리지 않습니다. 실제로 내부적으로 최적화되어 있습니다.
것을 당신은 벤치 마크에서 볼 수 mat2cellThomasIsCoding에 의해 제공되는 옵션은 여기에 매우 느립니다 …
우리가 스케일을 더 명확하게하기 위해 그 라인을 제거하면 내 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});



