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)
使用第二个输出max
,它作为一种"矢量化find
":
[~, Y] = max(bsxfun(@lt, X(:).', Q(:)), [], 1);
Run Code Online (Sandbox Code Playgroud)
这是如何工作的:
X
,测试它是否小于每个元素Q
.这是完成的bsxfun(@lt, X(:).', Q(:))
.注意结果中的每一列对应一个元素X
,并且每一行对应一个元素Q
.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)
使用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)
如果您拥有的向量沿着不同的维度,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 中尝试此示例。