Matlab:需要一些帮助来实现看似简单的操作矢量化

Matlab: need some help for a seemingly simple vectorization of an operation

我想优化这段Matlab代码,但到目前为止我没有成功。我尝试了 repmat 和 sums 以及 cumsum 的不同组合,但我所有的尝试似乎都没有给出正确的结果。对于这个棘手的问题,我将不胜感激专家指导。

S=1000; T=10;
X=rand(T,S),
X=sort(X,1,'ascend');
Result=zeros(S,1);
for c=1:T-1
    for cc=c+1:T
        d=(X(cc,:)-X(c,:))-(cc-c)/T;
        Result=Result+abs(d');
    end
end

基本上我创建了 1000 个包含 10 个随机数的向量,并且对于每个向量我计算了每对值(比如第 mth 和 nth)它们之间的差值,减去差值(n-m)。我对可能的对求和,然后 return 每个向量的结果。

我希望这个解释是清楚的,

非常感谢。

nchoosek(v,k)函数一次生成v中元素的所有组合k。我们可以使用它来生成所有可能的索引对,然后使用它来向量化循环。在这种情况下,矢量化似乎并没有真正提高性能(至少在我的 2017a 机器上是这样)。也许有人会想出更有效的方法。

idx = nchoosek(1:T,2);
d = bsxfun(@minus,(X(idx(:,2),:) - X(idx(:,1),:)), (idx(:,2)-idx(:,1))/T);
Result = sum(abs(d),1)';

至少可以很容易地向量化你的内部循环:

Result=zeros(S,1);
for c=1:T-1
   d=(X(c+1:T,:)-X(c,:))-((c+1:T)'-c)./T;
   Result=Result+sum(abs(d),1)';
end

在这里,我使用了新的自动单例扩展。如果您使用的是旧版本的 MATLAB,则需要对两个减法运算使用 bsxfun。例如,X(c+1:T,:)-X(c,:)bsxfun(@minus,X(c+1:T,:),X(c,:)) 相同。

这段代码中发生的事情是,我们不是循环 cc=c+1:T,而是一次获取所有这些索引。所以我简单地将cc替换为c+1:Td 是一个有多行的矩阵(第一次迭代时有 9 行,随后的每次迭代中都会减少 1 行)。

令人惊讶的是,这比双循环慢,并且速度与 Jodag 的答案相似。

接下来,我们可以尝试改进索引。请注意,上面的代码从矩阵中逐行提取数据。 MATLAB 按列存储数据。所以从矩阵中提取列比提取行更有效。让我们转置 X:

X=X';
Result=zeros(S,1);
for c=1:T-1
   d=(X(:,c+1:T)-X(:,c))-((c+1:T)-c)./T;
   Result=Result+sum(abs(d),2);
end

这比按行编制索引的代码快两倍多。

但是当然同样的技巧可以应用到问题中的代码,将它的速度提高大约 50%:

X=X';
Result=zeros(S,1);
for c=1:T-1
   for cc=c+1:T
      d=(X(:,cc)-X(:,c))-(cc-c)/T;
      Result=Result+abs(d);
   end
end

我从这个练习中得到的信息是 MATLAB 的 JIT 编译器已经有了很大的改进。在过去,任何类型的循环都会使代码停止工作。今天它不一定是最糟糕的方法,特别是如果你所做的只是使用内置函数。

更新:这里是不同提案(10^5 次试验)运行 次的结果:

所以看起来矩阵变换是最有效的干预,令人惊讶的是,与矢量化版本相比,我原来的双循环实现是最好的。然而,在我手中(2017a),与使用平均值的原始版本(使用中位数的 18.2%)相比,改进仅为 16.6%。

也许还有改进的余地?