将矩阵列转换为单位向量的快速方法

Lui*_*uis 1 optimization performance matlab normalize matrix

我有一个矩阵:

S = [  -1.0400    4.9100    4.1000   -3.5450   -0.6600   -0.9300    4.3950   -1.0650    2.9850   -4.9800    0.2100;
   -0.5200   -4.3150   -3.0950    0.5700    4.4700    1.1500    3.1350    0.6450    0.3750   -4.9150   -2.1150; 
    5.0000    5.0000    5.0000    5.0000    5.0000    5.0000    5.0000    5.0000    5.0000    5.0000    5.0000 ];
Run Code Online (Sandbox Code Playgroud)

我想将列转换为单位向量,所以我使用for循环

for i=1:size(S,2)
    S(:,i) = S(:,i) / norm( S(:,i) );
end
Run Code Online (Sandbox Code Playgroud)

有没有办法在MATLAB中更有效地完成这项工作?

Wol*_*fie 5

TLDR

如果您有MATLAB 2016b或更新版本,并且没有兼容性问题,我会使用

S = S ./ sqrt(sum(S.^2,1));
Run Code Online (Sandbox Code Playgroud)

编辑:请参阅底部的基准测试,以获取替代品的性能基准.


上下文

我们可以手动计算范数并按列分割.

根据定义,norm(x) = sqrt( sum( x(:).^2 ) ).我在(:)这里用来表明norm是在整个矩阵上计算的.对我们有用的是sum默认情况下按列方式工作,因此列方式规范的定义如下:

nrm = sqrt( sum( x.^2 ) );
Run Code Online (Sandbox Code Playgroud)

请注意,如果您的矩阵可能S只有1行,则应使用明确强制执行逐列求和nrm = sqrt(sum(x.^2,1)).

现在我们有几种选择:


如果你有MATLAB R2017b或更新,并且再次没有兼容性问题,你可以使用vecnorm,它可以用来代替手动规范计算

S = S ./ vecnorm(S, 2, 1);
Run Code Online (Sandbox Code Playgroud)

基准测试:

既然你要求性能,这里有一个简单的基准来测试这些不同方法的速度.特别是你问题中的原始循环与使用vecnorm或手动计算的隐式扩展.

  • 结果(使用R2017b运行)

             size(S):  1e3*1e2  1e5*1e3  1e3*1e6    
             Looping:  0.0005   1.0186   12.7788
     Implicit manual:  0.0001   1.1236   10.4031
    Implicit vecnorm:  0.0002   0.5774    6.8058
    
    Run Code Online (Sandbox Code Playgroud)
  • 结论

    • 对于相对较小的阵列,所有方法都非常快,我会选择代码清晰度而不是性能.
    • 如果您只想使用支持它的MATLAB版本,vecnorm大约是其他大型矩阵方法的两倍.
    • 对于订单的矩阵1e5*1e3,循环与隐式扩展相当.
  • 码

    function benchie()
        S = rand( 1e3, 1e2 )*5;
    
        f1 = @() loopingNorm(S);
        f2 = @() implicitManual(S);
        f3 = @() implicitVecnorm(S);
    
        fprintf( 'Looping: %.4f\nImplicit manual: %.4f\nImplicit vecnorm: %.4f\n', ...
                 timeit(f1), timeit(f2), timeit(f3) );
    end
    function S = loopingNorm(S)
        for ii = 1:size(S,2)
            S(:,ii) = S(:,ii) / norm( S(:,ii) );
        end
    end
    function S = implicitManual(S)
        S = S ./ sqrt(sum(S.^2,1));
    end
    function S = implicitVecnorm(S)
        S = S ./ vecnorm( S, 2, 1 );
    end
    
    Run Code Online (Sandbox Code Playgroud)

  • 在2018a及更高版本中,还有一个`normalize`函数(选择'norm'方法)看起来就像整个工作一样.但这甚至不太向后兼容...... (2认同)