我想创建一个健壮且快速的平方和。一个简单的解决方案是(在C ++中):
double sumOfSquares(const double *v, size_t n) {
double r = 0;
for (size_t i=0; i<n; i++) {
r += v[i]*v[i];
}
return r;
}
此例程并不健壮,因为当v[i]
较小或较大(计算不足/溢出)时,它无法给出良好的结果。这也不是最准确的例程,因为我们可以通过更好的方式对数字求和(例如在求和之前进行排序)。
我已经检查了LAPACK的DLASSQ
以获取启发(我在问题的末尾包含了源代码)。
基本上,DLASSQ
维护一个scale变量(此变量是即时计算的,它是已经看到的最大元素),并且数组元素通过该变量进行除法缩放。这样,例程可以避免下溢/上溢。
但是,我不喜欢这种解决方案,例如:
n
个分区,这意味着例程很慢我正在考虑可能的改进:比例尺始终是2的幂(已经看到的最大元素四舍五入为2的幂)。这样:
我的问题是:这是个好主意吗?我看不到陷阱吗?
这是DLASSQ
(fortran代码)的源代码:
SUBROUTINE DLASSQ( N, X, INCX, SCALE, SUMSQ )
*
* -- LAPACK auxiliary routine (version 3.7.0) --
* -- LAPACK is a software package provided by Univ. of Tennessee, --
* -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--
* December 2016
*
* .. Scalar Arguments ..
INTEGER INCX, N
DOUBLE PRECISION SCALE, SUMSQ
* ..
* .. Array Arguments ..
DOUBLE PRECISION X( * )
* ..
*
* =====================================================================
*
* .. Parameters ..
DOUBLE PRECISION ZERO
PARAMETER ( ZERO = 0.0D+0 )
* ..
* .. Local Scalars ..
INTEGER IX
DOUBLE PRECISION ABSXI
* ..
* .. External Functions ..
LOGICAL DISNAN
EXTERNAL DISNAN
* ..
* .. Intrinsic Functions ..
INTRINSIC ABS
* ..
* .. Executable Statements ..
*
IF( N.GT.0 ) THEN
DO 10 IX = 1, 1 + ( N-1 )*INCX, INCX
ABSXI = ABS( X( IX ) )
IF( ABSXI.GT.ZERO.OR.DISNAN( ABSXI ) ) THEN
IF( SCALE.LT.ABSXI ) THEN
SUMSQ = 1 + SUMSQ*( SCALE / ABSXI )**2
SCALE = ABSXI
ELSE
SUMSQ = SUMSQ + ( ABSXI / SCALE )**2
END IF
END IF
10 CONTINUE
END IF
RETURN
*
* End of DLASSQ
*
END