Matlab 如何避免循环以减少此代码的计算时间?

Matlab 如何避免循环以减少此代码的计算时间?,matlab,matrix,runtime,vectorization,Matlab,Matrix,Runtime,Vectorization,如何避免循环以减少此代码()的计算时间: 我希望找到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

如何避免循环以减少此代码()的计算时间:

我希望找到
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 = [1007  4044  5002  622;
      552   300   431  124;
     2010  1113  1100   88];
A
包含唯一的
M(1:3,:)

显示我们想要的列

out = A(:,idxC)
结果:

>> out

out =

    1007        4044
     552         300
    2010        1113
列向量
[5002;431;1100]
被删除,因为
[2;10;55]
包含在
X{2}=[2 10 55 9 17]

由于
[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答案)

我根据第一行得到if
A
排序:(正确结果)

如果我没有对矩阵
A
排序,我会得到:(假结果)

应该消除列向量
A(:,4)=[622;124;88]
,因为
[32 12]=X{4}


列向量
[5002;431;1100]
应该被删除,因为
[2;10;55]
包含在
X{2}=[2 10 55 9 17]
也许您可以使用两次
cellfun

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

在这种情况下,您不应该试图消除循环。矢量化实际上对你有很大的伤害

特别是(给你的匿名lambda起个名字)

效率低得可笑,因为它不会短路。换成一个环

同样适用于

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
语句,尤其是第二个语句,触发提前退出,这可能会节省大量计算量。

Ben Voigt的答案很好,但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
进行某种排序,SUB就不会按a中的相遇顺序编号,而是按C中的相遇顺序编号(已排序):

因此,您应该通过
unique
给出的矩阵而不是A来获得最终输出

进入

然后,要获得最终输出,请输入

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

        1007        4044
         552         300
        2010        1113
Shot#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

Shot#2

前面提到的方法可能在以下方面存在瓶颈:

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

因此,为了保持绩效作为良好动机,可以将整个过程分为两个阶段。第一阶段可以处理
X
的单元,这些单元在
M
的第四行中不重复,这可以通过矢量化方法实现,而另一阶段可以使用我们较慢的
cellfun
方法解决
X的其余
单元

因此,代码会膨胀一点,但希望性能会更好。最终的实现看起来像这样-

%// 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

验证

本节列出了验证输出的代码。下面是验证shot#1代码的代码-


谢谢你的回答。我认为xj=X{jj};而不是xj=X{j};我得到了错误信息:???将单元格内容分配给非单元格数组对象。错误在==>idxC{ii}=false;是的,那些应该是括号而不是大括号。我想这是个问题!例如,out=A(:,idxC)out=Empty matrix:3-by-0您的答案很快,但给出的结果是错误的,我认为您的code@bzak:有两条捷径。第一,如果在X{jj}中找不到A4{ii}的任何元素,不要测试A4{ii}的其余部分,从下一个jj开始。其次,如果一个A4{ii}的所有元素都在任何一个X{jj}中找到,不要测试jj的剩余值,已经删除了那个A4{ii}。是的,首先你应该避免使用
cellfun
而像另一个答案一样使用for循环,直到一切都是正确的,只有这样你才能尝试一部分一部分地垂直化。我只是想指出,你可以一起使用cellfun两次,但正确性取决于你的实际问题,你必须自己调整。你能解释一下你如何获得输出的逻辑吗?这将节省我们从你的数据中推断出来的时间code@LuisMendo当前位置我的问题得到了两个答案。scmg响应提供rignt输出,如示例中所示,但如果X非常大,则需要大量计算时间。Ben Voigt开发的逻辑很有趣,但输出结果是错误的,我无法找出原因!我问题中的输入是M,A和X,输出是out=A(:,idxC)@LuisMendo:我希望找到A(1:3,:)的列向量,其M(4,:)中的对应值不是单元格X的向量之一(显然不等于这些向量之一)。如果X非常大,我会寻找一个快速的解决方案。只是想澄清一下:你的意思是“M(4,:)中的对应值不是X单元格的同一向量的一部分”,对吗?@LuisMendo:是的,是X单元格的同一向量。谢谢你的回答,在你的回答之后
subs =

 2
 2
 3
 2
 3
 2
 4
 4
 4
 1
 1
[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
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
%// 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
%// Setup inputs and output
load('matrice_data.mat');   %// Load input data
X = cellfun(@(x) unique(x).',X,'Uni',0); %// Consider X's 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!')