【问题标题】:How to avoid the loop to reduce the computation time of this code?如何避免循环以减少此代码的计算时间?
【发布时间】:2015-08-04 23:50:54
【问题描述】:

如何避免循环以减少这段代码的计算时间(one solution of my last question):

我希望找到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 进行排序,我会得到:(错误结果)

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]

【问题讨论】:

  • 你能解释一下你如何获得输出的逻辑吗?这将节省我们尝试从您的代码中推断出来的时间
  • @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 的相同向量。

标签: matlab matrix runtime vectorization


【解决方案1】:

也许你可以使用2次cellfun

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

【讨论】:

  • 是的,起初你应该避免使用cellfun,而是像其他答案一样使用for-loop,直到一切都正确,然后你才能尝试逐部分验证。我只是想指出你可以同时使用cellfun 2次,但正确性取决于你的实际问题,你必须自己适应它。
【解决方案2】:

在这种情况下,您不应该尝试消除循环。矢量化实际上对您造成了严重伤害。

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

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

【讨论】:

  • 感谢您的回答。我认为 xj = X{jj};而不是 xj = X{j};我收到错误消息:???将单元格内容分配给非单元格数组对象。 ==> idxC{ii} 中的错误 = false;
  • 是的,那些应该是括号而不是大括号
  • 我觉得有问题!例如,out = A(:,idxC) 给出 out = Empty matrix: 3-by-0
  • 你的答案很快,但它给出了错误的结果,我认为你的代码有错误
  • @bzak:有两个快捷方式。一,如果在 X{jj} 中找不到 A4{ii} 的任何元素,则不要测试 A4{ii} 的其余部分,从下一个 jj 重新开始。其次,如果 A4{ii} 的所有元素都在任何 X{jj} 中找到,则不要测试 jj 的剩余值,已删除该 A4{ii}。
【解决方案3】:

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做一种排序,subs就不是按照A中遇到的顺序编号,而是按照C中遇到的顺序(排序):

subs =

 2
 2
 3
 2
 3
 2
 4
 4
 4
 1
 1

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

输入

[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

【讨论】:

  • 感谢您的回答,根据您的建议,我发现结果:out = [A(:,2) A(:,3)] 而不是正确的结果:out = [A( :,1) A(:,2)]
  • 我同意这个澄清的答案。如何使循环同时适用于行向量和列向量?也许像A4{ii}(:)' 之类的?
  • 我编辑了我的答案以解释为什么你没有得到正确的结果@Ben Voigt:是的,只要 (:) 将所有内容都放在列中,这实际上就可以工作
  • 转置操作是.'而不是'!仅使用' 会进行复共轭转置,这是一种不同的操作,会导致错误的结果。
  • @HamtaroWarrior:非常感谢您为帮助我所做的努力。现在,我得到了示例的结果,但是我的真实数据得到了错误的结果,而使用 scmg 答案,我得到了正确的结果!!!我对 scmg 答案的问题是 X 非常大时的计算时间。
【解决方案4】:

第一枪

本节列出的方法应该是一种快速且直接的方法来解决我们的问题。请注意,由于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 的第四行中没有重复,这可以通过矢量化方法来实现,另一个阶段使用我们较慢的@987654330 解决X's 的其余单元格基于@的方法。

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

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

验证

本节列出了验证输出的代码。这是验证镜头#1代码的代码-

%// 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!')

【讨论】:

  • 我将真实的 M 和 X 数据保存在一个文件中,并将您的答案直接应用于此数据(输入:M 和 X),我得到的输出与矩阵 A 完全相同。但是使用示例中的数据我得到了正确的结果,我觉得这完全奇怪!!!
  • 我添加了另一个示例,对 M 稍作修改。使用您的代码而不是获取 [A(:,5) A(:,1) A(:,2)],我得到 [A(:,4) A(:,5) A(:,1) A( :,2)]。列向量 A(:,4) = [622;124;88] 应该被消除,因为 [32 12] = X{4}。
  • @bzak 检查编辑?看看它是否适用于您的实际情况?
  • @bzak 如果在函数first_passsecond_pass 中输入X 是一个空元胞数组,则可能会引发错误。如果是这样,请在这两个函数的顶部添加此代码 sn-p:pastebin.com/t5uSTWPU
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-05-03
  • 1970-01-01
  • 1970-01-01
  • 2017-06-17
  • 2014-11-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多