在MATLAB

时间:2016-09-02 19:00:15

标签: matlab vectorization

我有两个大矩阵,我想在这些矩阵的每一行之间执行交叉操作。我会得到类似的东西:

a =

     8     1     6
     3     5     7
     4     9     2

b =

     8     3     4
     1     5     9
     6     7     2

intersect(a,b) =

             8
             5
             2

对于不使用for循环的所有n行A和B的交叉。有可能吗?

2 个答案:

答案 0 :(得分:3)

编辑:这是一个kludgy解决方案,只有明显的矢量化(但实际上,arrayfun是循环的包装器)。请参阅Divakar的另一个答案,以获得正确的矢量化答案。

如果您的交叉路口保证每行的大小相等,您可以像这样使用arrayfun

intersect_array_fun = @(A,B) arrayfun(@(k) intersect(A(k,:),B(k,:)), ...
                                     (1:size(A,1)).');
row_intersects = intersect_array_fun(a,b);

这会给你答案:

row_intersects =

     8
     5
     2

请注意,如果arrayfun的输出不均匀,即intersect为每一行返回多个尺寸,则会失败。错误如下所示:

Error using arrayfun
Non-scalar in Uniform output, at index 1, output 1.
Set 'UniformOutput' to false.

Error in @(A,B)arrayfun(@(k)intersect(A(k,:),B(k,:)),1:size(A,1))

我们可以期待这一点而不是

intersect_array_fun = @(A,B) arrayfun(@(k) intersect(A(k,:),B(k,:)), ...
                                      (1:size(A,1)).');
intersect_cell_fun = @(A,B) arrayfun(@(k) intersect(A(k,:),B(k,:)), ...
                                     (1:size(A,1)).', ...
                                     'uniformoutput',false);

try
    row_intersects = intersect_array_fun(a,b);
catch
    row_intersects = intersect_cell_fun(a,b);
end

这将捕获错误,并告诉arrayfun返回cell输出,以防相交大小不匹配。对于上面的例子,结果是

row_intersects =

     8
     5
     2

但如果我们将a(1,3)更改为4,我们就会

row_intersects = 

    [1x2 double]
    [         5]
    [         2]

代替:列单元格数组。

这将我们带到最后一点:你的问题是矢量化。你希望在没有for循环的情况下实现所有这一切,同时在每一行上工作。好吧,我的第一个例子可能会欺骗你,但arrayfun只是一个循环的包装器,并不比等效循环更有效。但是,后一个行大小不匹配的示例表明,您所做的事情本质上是不可矢量化的。由于输出可能包含每行的变长向量,因此您可能无法避免使用循环。

答案 1 :(得分:3)

这是使用broadcasting/bsxfun -

的矢量化方法
b_mask = squeeze(any(bsxfun(@eq,a,permute(b,[1,3,2])),2));
bt = b.';
out = mat2cell(bt(b_mask'),sum(b_mask,2));

示例运行 -

a =
     8     1     6
     3     5     7
     4     9     2
b =
     3     8     2     6     1
     5     6     9     0     3
     6     7     1     5     3
out{1} =
     8
     6
     1
out{2} =
     5
     3
out{3} =
     []

请注意,对于b中连续数字重复的案例,这不会工作,因为在这种情况下,它会在b中列出重复的数字输出。这可能不是预期的,因为我们正在执行提供唯一数字的intersect操作。

为了使它适用于重复元素,我们需要修改b_mask的创建,保持原样。因此,修改后的版本看起来像这样 -

matches0 = bsxfun(@eq,a,permute(b,[1,3,2]));
[~,maxidx] = max(matches0,[],3);
matches1 = bsxfun(@eq,permute(1:size(b,2),[1,3,2]),maxidx) & matches0;
b_mask = squeeze(any(matches1,2));

示例运行 -

a =
     8     8     1
     3     5     7
     4     9     2
b =
     3     8     1     8     5
     5     6     9     0     3
     6     7     1     5     3
out{1} =
     8
     1
out{2} =
     5
     3
out{3} =
     []