将矩阵列转换为单位向量的快速方法
Fast way to turn matrix columns into unit vectors
我有一个矩阵:
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 ];
我想将列转换为单位向量,所以我使用 for
循环
for i=1:size(S,2)
S(:,i) = S(:,i) / norm( S(:,i) );
end
有没有办法在 MATLAB 中更有效地做到这一点?
TLDR
如果您有 MATLAB 2016b 或更新版本,并且没有兼容性问题,我会使用
S = S ./ sqrt(sum(S.^2,1));
编辑:请参阅底部的基准以了解备选方案的性能基准。
上下文
我们可以手动计算范数并按列划分。
根据定义,norm(x) = sqrt( sum( x(:).^2 ) )
。我在这里使用 (:)
来表明 norm
是在整个矩阵上计算的。对我们有用的是 sum
默认情况下按列工作,因此按列范数定义如下:
nrm = sqrt( sum( x.^2 ) );
请注意,如果您的矩阵 S
有可能只有 1 行,您应该使用 nrm = sqrt(sum(x.^2,1))
.
明确地强制执行按列求和
现在我们有几个除法选项:
隐式扩展(MATLAB R2016b 或更新版本)
S = S ./ nrm;
使用 bsxfun
的隐式扩展(所有 MATLAB 版本)
S = bsxfun( @mrdivide, S, nrm );
使用repmat
手动扩展(所有MATLAB版本)
S = S ./ repmat(nrm, size(S,1), 1);
如果您有 MATLAB R2017b 或更新版本,并且没有兼容性问题,您可以使用 vecnorm
,它可以代替手动范数计算
S = S ./ vecnorm(S, 2, 1);
基准:
既然你问的是性能,这里有一个简单的基准来测试这些不同方法的速度。特别是问题中的原始循环与使用 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
结论
- 对于相对较小的数组,所有方法都非常快,我会选择代码清晰度而不是性能。
- 如果您只想使用支持它的 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
我有一个矩阵:
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 ];
我想将列转换为单位向量,所以我使用 for
循环
for i=1:size(S,2)
S(:,i) = S(:,i) / norm( S(:,i) );
end
有没有办法在 MATLAB 中更有效地做到这一点?
TLDR
如果您有 MATLAB 2016b 或更新版本,并且没有兼容性问题,我会使用
S = S ./ sqrt(sum(S.^2,1));
编辑:请参阅底部的基准以了解备选方案的性能基准。
上下文
我们可以手动计算范数并按列划分。
根据定义,norm(x) = sqrt( sum( x(:).^2 ) )
。我在这里使用 (:)
来表明 norm
是在整个矩阵上计算的。对我们有用的是 sum
默认情况下按列工作,因此按列范数定义如下:
nrm = sqrt( sum( x.^2 ) );
请注意,如果您的矩阵 S
有可能只有 1 行,您应该使用 nrm = sqrt(sum(x.^2,1))
.
现在我们有几个除法选项:
隐式扩展(MATLAB R2016b 或更新版本)
S = S ./ nrm;
使用
bsxfun
的隐式扩展(所有 MATLAB 版本)S = bsxfun( @mrdivide, S, nrm );
使用
repmat
手动扩展(所有MATLAB版本)S = S ./ repmat(nrm, size(S,1), 1);
如果您有 MATLAB R2017b 或更新版本,并且没有兼容性问题,您可以使用 vecnorm
,它可以代替手动范数计算
S = S ./ vecnorm(S, 2, 1);
基准:
既然你问的是性能,这里有一个简单的基准来测试这些不同方法的速度。特别是问题中的原始循环与使用 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
结论
- 对于相对较小的数组,所有方法都非常快,我会选择代码清晰度而不是性能。
- 如果您只想使用支持它的 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