向量化区间限制表中的查找值

ftx*_*txx 7 matlab loops vectorization octave

这是一个关于我们是否可以在matlab中使用矢量化操作类型以避免写入循环的问题.

我有一个矢量

Q = [0.1,0.3,0.6,1.0]
Run Code Online (Sandbox Code Playgroud)

我生成一个均匀分布的随机向量 [0,1)

X = [0.11,0.72,0.32,0.94]
Run Code Online (Sandbox Code Playgroud)

我想知道的每个条目是否X是之间[0,0.1)[0.1,0.3)[0.3,0.6)[0.6,1.0)我想返回包含最大元素的索引向量Q那的每个条目X少于.

我可以写一个for循环

Y = zeros(length(X),1)
for i = 1:1:length(X)
    Y(i) = find(X(i)<Q, 1);
end
Run Code Online (Sandbox Code Playgroud)

此示例的预期结果:

Y = [2,4,3,4]
Run Code Online (Sandbox Code Playgroud)

但我想知道是否有办法避免写循环?(我看到我的问题有很多非常好的答案.非常感谢你!如果我们更进一步,如果我的Q是一个矩阵,那么我想检查是否)

Y = zeros(length(X),1)
for i = 1:1:length(X)
    Y(i) = find(X(i)<Q(i), 1);
end
Run Code Online (Sandbox Code Playgroud)

Lui*_*ndo 8

使用第二个输出max,它作为一种"矢量化find":

[~, Y] = max(bsxfun(@lt, X(:).', Q(:)), [], 1);
Run Code Online (Sandbox Code Playgroud)

这是如何工作的:

  1. 对于每个元素X,测试它是否小于每个元素Q.这是完成的bsxfun(@lt, X(:).', Q(:)).注意结果中的每一列对应一个元素X,并且每一行对应一个元素Q.
  2. 然后,对于每个元素X,获取该Q比较所在的第一个元素的索引true.这是完成的[~, Y] = max(..., [], 1).请注意,第二个输出max返回第一个最大化器的索引(沿指定的维度),因此在这种情况下,它给出true每列中第一个的索引.

对于您的示例值,

Q = [0.1, 0.3, 0.6, 1.0];
X = [0.11, 0.72, 0.32, 0.94];
[~, Y] = max(bsxfun(@lt, X(:).', Q(:)), [], 1);
Run Code Online (Sandbox Code Playgroud)

Y =
     2     4     3     4
Run Code Online (Sandbox Code Playgroud)


Tro*_*dor 6

使用bsxfun将有助于实现这一目标 你需要阅读它.我还在开头添加了一个Q = 0来处理小X的情况

X = [0.11,0.72,0.32,0.94 0.01];
Q = [0.1,0.3,0.6,1.0];
Q_extra = [0 Q];

Diff = bsxfun(@minus,X(:)',Q_extra (:)); %vectorized subtraction
logical_matrix = diff(Diff < 0); %find the transition from neg to positive
[X_categories,~] = find(logical_matrix == true); % get indices
Run Code Online (Sandbox Code Playgroud)

%输出为2 4 3 4 1

编辑:每种方法需要多长时间?

我对每个解决方案之间的区别感到好奇:

测试代码如下:

Q = [0,0.1,0.3,0.6,1.0];

X = rand(1,1e3);

tic
Y = zeros(length(X),1);
for i = 1:1:length(X)
    Y(i) = find(X(i)<Q, 1);
end
toc
tic
result = arrayfun(@(x)find(x < Q, 1), X);
toc

tic
Q = [0 Q];
Diff = bsxfun(@minus,X(:)',Q(:)); %vectorized subtraction
logical_matrix = diff(Diff < 0); %find the transition from neg to positive
[X_categories,~] = find(logical_matrix == true); % get indices
toc
Run Code Online (Sandbox Code Playgroud)

为自己运行,我发现当X的大小是1e6时,bsxfun要快得多,而对于较小的阵列,差异是变化的,可以忽略不计.

样品:当尺寸X为1e3时

Elapsed time is 0.001582 seconds. % for loop
Elapsed time is 0.007324 seconds. % anonymous function
Elapsed time is 0.000785 seconds. % bsxfun
Run Code Online (Sandbox Code Playgroud)


Mad*_*ist 2

如果您拥有的向量沿着不同的维度,Octave 会为您提供一个巧妙的自动向量化技巧。如果你制作Q一个列向量,你可以这样做:

X = [0.11, 0.72, 0.32, 0.94];
Q = [0.1; 0.3; 0.6; 1.0; 2.0; 3.0];
X <= Q
Run Code Online (Sandbox Code Playgroud)

结果是一个 6x4 矩阵,指示 的Q每个元素X小于哪些元素。我制作了Q不同的长度,而X不仅仅是为了说明这一点:

0   0   0   0
1   0   0   0
1   0   1   0
1   1   1   1
1   1   1   1
1   1   1   1
Run Code Online (Sandbox Code Playgroud)

回到原来的例子,你可以这样做

length(Q) - sum(X <= Q) + 1
Run Code Online (Sandbox Code Playgroud)

要得到

2   4   3   4
Run Code Online (Sandbox Code Playgroud)

请注意,我在 的定义中使用分号而不是逗号Q。如果您想在定义后将其设为列向量,请执行以下操作:

length(Q) - sum(X <= Q') + 1
Run Code Online (Sandbox Code Playgroud)

这样做的原因是 Octave 隐式应用于bsxfun行和列向量上的操作。根据 @excaza 的评论,MATLAB 在 R2016b 之前不会执行此操作,因此在 MATLAB 中您可以执行以下操作:

length(Q) - sum(bsxfun(@le, X, Q)) + 1
Run Code Online (Sandbox Code Playgroud)

您可以在此处的IDEOne 中尝试此示例。

  • 我无法让它工作,你确定吗? (2认同)
  • 这可能只是一个八度技巧,因为我现在正在运行 MATLAB,但它在 MATLAB 上不起作用 (2认同)
  • MATLAB *确实*具有此功能,Octave 只是隐式使用 [`bsxfun`](http://www.mathworks.com/help/matlab/ref/bsxfun.html)。MATLAB 的等效函数是“length(Q) - sum(bsxfun(@le, X, Q)) + 1”。R2016b 中向 MATLAB 引入了隐式“bsxfun” (2认同)