如何在不使用 MATLAB 循环的情况下查找向量中元素子集的出现次数?

How to find number of occurrences of a subset of elements in a vector without using loops in MATLAB?

假设 X 是给定的向量:

   X=[1
      2
      4
      2
      3
      1
      4
      5
      2
      4
      5];

而 Y 是 X 的给定元素子集:

Y=[3
   4
   5];

要求输出Y中的元素在X中出现的次数:

out=[1
     3
     2];

我的解决方案是使用 for 循环:

for i=1:size(X,1)
    temp = X(X(:,1)==Y(i,1),:);
    out(i,1) = size(temp,1);
end

但是当X和Y很大时,这是低效的。那么,如何利用矢量化更快地做到这一点呢?我知道 histhistc,但我想不出在这种情况下如何使用它们来获得所需的输出。

快速选择

您可以使用 bsxfun 结合 sum 来计算这个

sum(bsxfun(@eq, Y, X.'), 2)

说明

在此示例中,bsxfunXY 中的每个元素组合执行给定操作。我们要使用的操作是 eq(检查是否相等)。结果是一个矩阵,其中 Y 中的每个元素对应一行,X 中的每个元素对应一列。如果 X 中的元素等于 Y 中对应于给定行的元素,它将具有 1 值。

bsxfun(@eq, Y, X.')

%    0     0     0     0     1     0     0     0     0     0     0
%    0     0     1     0     0     0     1     0     0     1     0
%    0     0     0     0     0     0     0     1     0     0     1

然后我们可以对各列求和以计算 X 中等于 Y 中给定值的元素数。

sum(bsxfun(@eq, Y, X.'), 2)

%   1
%   3
%   2

在较新版本的 MATLAB 中(自 R2016b 起),您可以省略 bsxfun,因为相等运算将自动广播。

sum(Y - X.', 2)

内存高效的选择

第一个选项不是最有效的,因为它需要创建一个 [numel(Y), numel(X)] 个元素大的矩阵。另一种内存效率更高的方法可能是使用 ismember 的第二个输出与 accumarray

[tf, ind] = ismember(X, Y);
counts = accumarray(ind(tf), ones(sum(tf), 1), [numel(Y), 1], @numel);

说明

ismember 用于判断一个数组中的值是否在另一个数组中。第一个输入告诉我们 if 第一个输入的每个元素都在第二个输入中,第二个输出告诉你 where 在第二个输入中的每个元素找到了第一个输入。

[tf, ind] = ismember(X, Y);

%   0 0 1 0 1 0 1 1 0 1 1
%   0 0 2 0 1 0 2 3 0 2 3

我们可以使用第二个输入来 "group" 将相同的值放在一起。 accumarray 函数正是这样做的,它使用上面的 ind 变量来确定组,然后将给定的操作应用于每个组。在我们的例子中,我们只想确定每个组中元素的数量。所以要做到这一点,我们可以传递第二个输入,大小为 ind 输入(减去不匹配的),然后使用 numel 作为操作(计算每个中的数字)组)

counts = accumarray(ind(tf), ones(sum(tf), 1), [numel(Y), 1], @numel);

%   1
%   3
%   2