Как избежать цикла, чтобы сократить время вычисления этого кода?
как избежать цикла, чтобы сократить время вычисления этого кода (одно из решений моего последнего вопроса):
Надеюсь найти векторы столбцов 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)