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

时间:2016-08-26 19:37:24

标签: matlab loops octave vectorization

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

我有一个载体

Q = [0.1,0.3,0.6,1.0]

我在[0,1)

上生成均匀分布的随机向量
X = [0.11,0.72,0.32,0.94]

我想知道X的每个条目是在[0,0.1)还是[0.1,0.3)还是[0.3,0.6)还是[0.6,1.0)之间,我想要返回一个向量包含QX的每个条目小于的最大元素的索引。

我可以写一个for循环

Y = zeros(length(X),1)
for i = 1:1:length(X)
    Y(i) = find(X(i)<Q, 1);
end

此示例的预期结果:

Y = [2,4,3,4]

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

Y = zeros(length(X),1)
for i = 1:1:length(X)
    Y(i) = find(X(i)<Q(i), 1);
end

6 个答案:

答案 0 :(得分:8)

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

[~, Y] = max(bsxfun(@lt, X(:).', Q(:)), [], 1);

如何运作

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

    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);
    

答案 1 :(得分: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

%输出为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

自己运行,我发现当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

答案 2 :(得分:4)

Octave有一个函数lookup来做到这一点。它需要一个有序值和数组的查找表,并返回一个数组,其中包含查找表中值的索引。

octave> Q = [0.1 0.3 0.6 1.0];
octave> x = [0.11 0.72 0.32 0.94];
octave> lookup (Q, X)
ans =

   1   3   2   3

唯一的问题是您的查找表有一个隐含的零,可以通过以下方式轻松修复:

octave> lookup ([0 Q], X) # alternatively, just add 1 at the results
ans =

   2   4   3   4

答案 3 :(得分:3)

您可以创建一个匿名函数来执行比较,然后使用arrayfun将其应用于X的每个成员:

compareFunc = @(x)find(x < Q, 1);
result = arrayfun(compareFunc, X, 'UniformOutput', 1);

创建匿名函数时,Q数组将存储在匿名函数(compareFunc)中。

或者,作为一行(Uniform Output是arrayfun的默认行为):

result = arrayfun(@(x)find(x < Q, 1), X);

答案 4 :(得分: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

结果是一个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

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

length(Q) - sum(X <= Q) + 1

获取

2   4   3   4

请注意,我在Q的定义中使用分号而不是逗号。如果你想在定义它之后使它成为一个列向量,那么就做这样的事情:

length(Q) - sum(X <= Q') + 1

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

length(Q) - sum(bsxfun(@le, X, Q)) + 1

您可以在IDEOne here中使用此示例。

答案 5 :(得分:0)

受@Mad Physicist发布的解决方案的启发,这是我的解决方案。

Q = [0.1,0.3,0.6,1.0]   
X = [0.11,0.72,0.32,0.94] 
Temp = repmat(X',1,4)<repmat(Q,4,1)
[~, ind]= max( Temp~=0, [], 2 );

这个想法是让X和Q成为“相同的形状”,然后使用元素比较,然后我们得到一个逻辑矩阵,其行告诉X中的给定元素是否小于Q中的每个元素,然后返回该逻辑矩阵的每一行的第一个非零索引。我没有测试过这种方法与其他方法的比较速度