Mathematics & Programme

返回主页<-

数值计算的优化
总的来说: 别做多余的事情. 此话说起来简单做起来难.

 

分割

即把复杂的问题适当地减小规模.如 {二分快速方法}

合并

即用适当的方法将分割的问题合并.

尽量把无序的化为有序的,如化为 整数指数乘幂的计算 , 乘法两因子尽量接近(对于FFT乘法,),乘法其中一因子尽量小(以便使复杂度降为O(n)).


例1:

原题: 使得 n2-19n+91 为完全平方数的自然数的个数是   , 即            .

数学解法是: 

    设 n2-19n+91=m2 , n2-19n+90=m2-1 , (n-10)(n-9)=(m+1)(m-1) , (n-10)(n-9)=0

    n=9,10

解法让人感到不太服气, 于是用程序来验证某小范围内的数.
最初的思路是这样的:

 {$N+}
const MaxN=30000000;

      ZERO=1e-8;
var f, q :extended;
    i :longint;
begin
  for i:=1 to MaxN do begin

    f:=i*(i-19)+91;  {完全按式子计算}

    if abs(f-int(f))<ZERO then writeln(i);

  end;

end.

因为 ((n+1)2-19(n+1)+91) - (n2-19n+91) = 2n-18 也就是说, 前一个f(n)加上2n-18就得到 f(n+1), 同样的 f(n+1) 加上 2(n+1)-18 得到 f(n+2). 更进一步, (2(n+1)-18) - (2n-18)= 2. 综合起来就是 f(n+1) = f(n) + t, t=t+2 . 其中 f(0)=91, t0=2*0-18=-18

这样就完全避免了乘法运算. 事实上, 若p(x)是多项式, 要计算连续的 p(x0+ka) (k是整数, a, x0 是常数), 总可以消去乘法运算.

 {$N+}
const n=60000000;
var f, q :extended;
    t :longint;
begin
  t:=-18;
  f:=91;
  while i<n do begin
    q:=sqrt(f);
    if f=int(f) then writeln(t shr 1);
    f:=f+t;
    inc(t,2);
  end;
end.

关于计算误差:

这里讲的是小数运算时产生误差的问题.

 

例如计算 1-cos0.1 ,假设用一个有6位数的计算器计算.

若先算得cos0.1=0.995004, 直接计算 1-0.995004=0.00499 只有3位有效数字(原来有6位).

若用幂级数计算cos时忽略首项, 同样的计算量(计算3项)可以获得多三位的有效数字(没有被截断).

若改成 1-cos0.1=2sin(0.05)^2, 同样计算3项幂级数, 也可获得多三位的有效数字(末位有-1的误差)

 

再看一个例子. 用狭义相对论计算物体的长度变化(与静止长度比较), 设速度是v=30m/s, 光速c=3e8m/s,物体沿速度方向长l0.

长度变化 Δl=l0-l0*√(1-v2/c2).

如果直接计算后一项, √(1-v2/c2)=0.99999999999999499 ,一直要计算到小数点后15位才看出变化.

而 Δl/l0 = (1-√(1-v2/c2)) = v2/c2/(1+√(1-v2/c2)) { 分子有理化 } =5.0000000000000125e-15 就有17位精度了.

 

从另一角度看, 有些函数本身的变化速度就很快,以至于即使自变量只有微小变化, 结果也会有较大不同.

为描述这种误差 设有 y=f(x), 考虑相对误差 Δy/y, 和自变量的误差 Δx/x,  用微分代替小量.

函数 误差放大量 函数 误差放大量
ax 1 sin(x) x cot(x)
x^2 2 cos(x) -x tan(x)
x^n n tan(x) x /sin(x) cos(x)
e^x x arcsin(x) x/√(1-x^2)/arcsin(x)
a^x x ln(a) arccos(x) -x/√(1-x^2)/arccos(x)
ln(x) 1/ln(x) arctan(x) x/(1+x^2)/arctan(x)
log(a,x) 1/ln(x) sinh(x) x cosh(x)
log(x,a) <*> -1/ln(x) cosh(x) x tanh(x)
    tanh(x) x/sinh(x)/cosh(x)
<*> 若c=log(a,b) 则 a^c=b. 即a是底数, b是真数, c是对数(指数).

TP7.0 的用户也可参照 TP各语句的运行速度比较 来修改程序使之最优.

Last update 09/02/2007 

交流:  留言本 or xyy82148@163.com