Как избежать цикла, чтобы сократить время вычисления этого кода?

как избежать цикла, чтобы сократить время вычисления этого кода (одно из решений моего последнего вопроса):

Надеюсь найти векторы столбцов A(1:3,:), соответствующие значения которых в M(4,:) не являются частью одного из векторов ячейки X (и, очевидно, не равны одному из этих векторов). Я ищу быстрое решение, если X очень велико.

M = [1007  1007  4044  1007  4044  1007  5002 5002 5002 622 622;
      552   552   300   552   300   552   431  431  431 124 124; 
     2010  2010  1113  2010  1113  2010  1100 1100 1100  88  88;
        7    12    25    15    12    30     2   10   55  32  12];

Здесь я беру непосредственно A:

A = [1007  4044  5002  622;
      552   300   431  124;
     2010  1113  1100   88];

A содержит уникальные столбчатые векторы M(1:3,:)

X = {[2 5 68 44],[2 10 55 9 17],[1 55 6 7 8 9],[32 12]};

[~, ~, subs] = unique(M(1:3,:)','rows');

A4 = accumarray(subs(:),M(4,:).',[],@(x) {x});

%// getting a mask of which columns we want
idxC(length(A4)) = false;
for ii = 1:length(A4)
    idxC(ii) = ~any(cellfun(@(x) all(ismember(A4{ii},x)), X));
end

Отображение нужных столбцов

out = A(:,idxC)

Результаты:

>> out

out =

    1007        4044
     552         300
    2010        1113

вектор столбца [5002;431;1100] был исключен, поскольку [2;10;55] содержится в X{2} = [2 10 55 9 17]

вектор столбца [622;124;88] был исключен, потому что [32 12] = X{4}

Другой пример: с тем же X

    M = [1007  4044  1007  4044  1007  5002 5002 5002 622 622  1007  1007  1007;
          552   300   552   300   552   431  431  431 124 124   552    11    11; 
         2010  1113  2010  1113  2010  1100 1100 1100  88  88  2010    20    20;
           12    25    15    12    30     2   10   55  32  12     7    12     7];

X = {[2 5 68 44],[2 10 55 9 17],[1 55 6 7 8 9],[32 12]};

A = [1007  4044  5002  622  1077;
      552   300   431  124    11;
     2010  1113  1100   88    20];

Результаты: (с ответом scmg)

Я получаю, если A сортируется в соответствии с первой строкой: (правильный результат)

out =

         1007        1007        4044
           11         552         300
           20        2010        1113

Если я не сортирую матрицу A, я получаю: (false result)

out =

        4044        5002         622
         300         431         124
        1113        1100          88

вектор столбца A(:,4) = [622;124;88] должен быть удален, потому что [32 12] = X{4}.

вектор-столбец [5002;431;1100] должен быть исключен, поскольку [2;10;55] содержится в X{2} = [2 10 55 9 17]

Ответы

Ответ 1

Ответ Ben Voigt велик, но строка for A4i = A4{ii} является той, которая вызывает проблемы: цикл for не работает таким образом с векторами столбцов:

%row vector
for i = 1:3
    disp('foo');
end

    foo
    foo
    foo

%column vector
for i = (1:3).'
    disp('foo');
end

    foo

Просто попробуйте вместо A4i = A4{ii}.', и он должен выполнить вашу работу!

Теперь, если мы посмотрим на результат:

A(:,idxC) =

    4044        5002
     300         431
    1113        1100

Как вы можете видеть, конечный результат - это не то, что мы ожидали.

Пока unique выполняет своего рода сортировку, подпрограммы не нумеруются порядком встречи в A, а по порядку встречи в C (который сортируется):

subs =

 2
 2
 3
 2
 3
 2
 4
 4
 4
 1
 1

Поэтому вы должны передать матрицу, заданную unique, а не A, чтобы получить окончательный вывод

Enter

[C, ~, subs] = unique(M(1:3,:)','rows'); 
%% rather than [~, ~, subs] = unique(M(1:3,:)','rows');

Затем, чтобы получить окончательный вывод, введите

>> out = C(idxC,:).'
out =

        1007        4044
         552         300
        2010        1113

Ответ 2

В этом случае вы не должны пытаться исключить циклы. Векторизация на самом деле сильно вредит вам.

В частности (давая имя вашей анонимной лямбда)

issubset = @(x) all(ismember(A4{ii},x))

является смехотворно неэффективным, потому что он не является короткозамкнутым. Замените это петлей.

То же самое для

any(cellfun(issubset, X))

Используйте подход, подобный этому:

idxC = true(size(A4));
NX = numel(X);
for ii = 1:length(A4)
    for jj = 1:NX
        xj = X{jj};
        issubset = true;
        for A4i=A4{ii}
            if ~ismember(A4i, xj)
                issubset = false;
                break;
            end;
        end;
        if issubset
            idxC(ii) = false;
            break;
        end;
    end;
end;

Два оператора break, и особенно второй, запускают ранний выход, что потенциально экономит вам огромное количество вычислений.

Ответ 3

Выстрел # 1

В этом разделе приведен подход, который должен быть быстрым и прямым подходом к решению нашего дела. Обратите внимание, что поскольку A - это матрица уникальных столбцов из M, относящаяся к третьей строке, она пропускается здесь как вход, потому что мы ее внутренне генерируем с помощью кода решения. Это поддерживается в следующем подходе/выстреле. Здесь реализация -

function out = shot1_func(M,X)

%// Get unique columns and corresponding subscripts
[unqrows, ~, subs_idx] = unique(M(1:3,:)','rows');
unqcols = unqrows.'; %//'

counts = accumarray(subs_idx(:),1); %// Counts of each unique subs_idx

%// Modify each cell of X based on their relevance with the fourth row of M
X1 = cellfun(@(x) subs_idx(ismember(M(4,:),x)),X,'Uni',0);

lensX = cellfun('length',X1); %// Cell element count of X1

Xn = vertcat(X1{:}); %// Numeric array version of X
N = max(subs_idx);   %// Number of unique subs_idx

%// Finally, get decision mask to select the correst columns from unqcols
sums = cumsum(bsxfun(@eq,Xn,1:N),1);
cumsums_at_shifts = sums(cumsum(lensX),:);

mask1 = any(bsxfun(@eq,diff(cumsums_at_shifts,[],1),counts(:).'),1); %//'
decision_mask = mask1 | cumsums_at_shifts(1,:) == counts(:).';    %//'
out = unqcols(:,~decision_mask);

return

Выстрел # 2

Ранее упомянутый подход может иметь узкое место при:

cellfun(@(x) subs_idx(ismember(M4,x)),X,'Uni',0)

Таким образом, чтобы сохранить производительность как хорошую мотивацию, можно разделить весь процесс на два этапа. На первом этапе можно было бы позаботиться о клетках X, которые не повторяются в четвертой строке M, которые могут быть реализованы с помощью векторизованного подхода и другого решения этапа для остальных клеток X's с нашим более медленным cellfun > .

Таким образом, код немного раздувается, но, надеюсь, будет лучше с производительностью. Окончательная реализация будет выглядеть примерно так:

%// Get unique columns and corresponding subscripts
[unqrows, ~, subs_idx] = unique(M(1:3,:)','rows')
unqcols = unqrows.' %//'
counts = accumarray(subs_idx,1);

%// Form ID array for X
lX = cellfun('length',X)
X_id = zeros(1,sum(lX))
X_id([1 cumsum(lX(1:end-1)) + 1]) = 1
X_id = cumsum(X_id)

Xr = cellfun(@(x) x(:).',X,'Uni',0); %//'# Convert to cells of row vectors
X1 = [Xr{:}]                         %// Get numeric array version

%// Detect cells that are to be processed by part1 (vectorized code)
[valid,idx1] = ismember(M(4,:),X1)
p1v = ~ismember(1:max(X_id),unique(X_id(accumarray(idx1(valid).',1)>1))) %//'

X_part1 = Xr(p1v)
X_part2 = Xr(~p1v)

%// Get decision masks from first and second passes and thus the final output
N = size(unqcols,2);
dm1 = first_pass(X_part1,M(4,:),subs_idx,counts,N)
dm2 = second_pass(X_part2,M(4,:),subs_idx,counts)
out = unqcols(:,~dm1 & ~dm2)

Связанные функции -

function decision_mask = first_pass(X,M4,subs_idx,counts,N)

lensX = cellfun('length',X)'; %//'# Get X cells lengths
X1 = [X{:}];                  %// Extract cell data from X

%// Finally, get the decision mask
vals = changem(X1,subs_idx,M4) .* ismember(X1,M4);

sums = cumsum(bsxfun(@eq,vals(:),1:N),1);
cumsums_at_shifts = sums(cumsum(lensX),:);
mask1 = any(bsxfun(@eq,diff(cumsums_at_shifts,[],1),counts(:).'),1); %//'
decision_mask = mask1 | cumsums_at_shifts(1,:) == counts(:).';    %//'
return


function decision_mask = second_pass(X,M4,subs_idx,counts)

%// Modify each cell of X based on their relevance with the fourth row of M
X1 = cellfun(@(x) subs_idx(ismember(M4,x)),X,'Uni',0);

lensX = cellfun('length',X1); %// Cell element count of X1

Xn = vertcat(X1{:}); %// Numeric array version of X
N = max(subs_idx);   %// Number of unique subs_idx

%// Finally, get decision mask to select the correst columns from unqcols
sums = cumsum(bsxfun(@eq,Xn,1:N),1);
cumsums_at_shifts = sums(cumsum(lensX),:);

mask1 = any(bsxfun(@eq,diff(cumsums_at_shifts,[],1),counts(:).'),1); %//'
decision_mask = mask1 | cumsums_at_shifts(1,:) == counts(:).';       %//'

return

Verficication

В этом разделе приведен список кода для проверки вывода. Вот код для этого, чтобы проверить код выстрела # 1 -

%// Setup inputs and output
load('matrice_data.mat');   %// Load input data
X = cellfun(@(x) unique(x).',X,'Uni',0); %// Consider X unique elements
out = shot1_func(M,X); %// output with Shot#1 function

%// Accumulate fourth row data from M based on the uniqueness from first 3 rows
[unqrows, ~, subs] = unique(M(1:3,:)','rows');    %//'
unqcols = unqrows.';                              %//'
M4 = accumarray(subs(:),M(4,:).',[],@(x) {x});    %//'
M4 = cellfun(@(x) unique(x),M4,'Uni',0);

%// Find out cells in M4 that correspond to unique columns unqcols
[unqcols_idx,~] = find(pdist2(unqcols.',out.')==0);

%// Finally, verify output
for ii = 1:numel(unqcols_idx)
    for jj = 1:numel(X)
        if all(ismember(M4{unqcols_idx(ii)},X{jj}))
            error('Error: Wrong output!')
        end
    end
end
disp('Success!')

Ответ 4

Возможно, вы можете использовать 2 раза cellfun:

idxC = cellfun(@(a) ~any(cellfun(@(x) all(ismember(a,x)), X)), A4, 'un', 0);
idxC = cell2mat(idxC);
out = A(:,idxC)