我有两个大矩阵,我想在这些矩阵的每一行之间执行交叉操作。我会得到类似的东西:
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的交叉。有可能吗?
答案 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} =
[]