|
返回主页<- |
| 二分快速方法
----翻译,添改自
Mathematical
Constants and computation 的
Binary
splitting method |
|
Binary splitting method |
| 许多公式, 常数的计算,
用经典方法都要 O(n2) 的时间去计算 n
位有效数字 ( 例如 arctan的级数,e的级数 见{无穷级数}
). 使用{FFT乘法}和以下介绍的二分法,
可以令计算在 O(n log(n)3) (或 O(n log(n)2)
计算 e) 的时间里完成. |
|
为什么
二分法 可以 加快 计算速度?
看一个简单的例子:
计算 A的8次方. 即 X=AAAAAAAA
用最易理解的方法,即反复计算
X=XA 七次(X初值是A):
设
A 是一个2位十进制.即 3>lg(A)>=1,其中lg(z)=log(z)/log(10)
1.
X=A 这一步消耗的时间可忽略
2.
X=XA 需要
2*2 的时间, X 现在有 4 位数, 总消耗时间
4
3.
X=XA 需要
4*2 的时间, X 现在有 6 位数, 总消耗时间 12
4.
X=XA 需要
6*2 的时间, X 现在有 8 位数, 总消耗时间 24
5.
X=XA 需要
8*2 的时间, X 现在有 10 位数, 总消耗时间 40
6.
X=XA 需要
10*2 的时间, X 现在有 12 位数, 总消耗时间 60
7.
X=XA 需要
12*2 的时间, X 现在有 14 位数, 总消耗时间 84
8.
X=XA 需要
14*2 的时间, X 现在有 16 位数, 总消耗时间 112
再用二分法试一下:
假设 A 还是2位.
1.
X=AAAA*AAAA=X1*X1 均分成两部分.
2.
X1=AA*AA =X2*X2 其中一部分再均分.
3.
X2=A*A
可以直接计算了, 耗时 2*2 , X2 现有 4
位数, 总耗时 4
4.
X1=X2*X2
耗时 4*4 , X1 现有 8 位数, 总耗时
20
5.
X =X1*X1
耗时 8*8 , X 现有 16 位数, 总耗时
84
计算时间之比:
112:84=1.33…, 或者说后者是前者的 84/112=75%.
更一般的,
设 A 有 L 位数(或者 L=lgA), 求An,注意到用一般方法每步需要的时间是等差数列,公差为
L2,有n-1项, 所以耗时 n(n-1)L2/2.而二分法每步耗时
L2, 4L2, 16L2 ... 是等比数列.所以耗时
后者是前者耗时的
→2/3. 约为 66.7%.
设 A 有 L 位数, 用O(nlogn)的乘法,
求Ann(n-1)L2/2
可见不论是n增长还是L增长,最终
T=O(nlogn). 非一般法的 T=O(n2) 可比。
|
|
假如你要计算很多位数的
n!. 一个基本的方法是计算下列值.
x1 = 1,
x2 = 2x1, x3 = 3x2, ¼, xn
= nxn-1.
值 xk 有 O(klog(k)) 位数(斯特林公式:
), 因此计算
xk+1 = kxk 有 O(klog(k)).
最后它的总时间复杂度是:
O(2log(2)+¼+n log(n))
= O(n2log(n)).
改用二分法(binary
splitting) 递归地对半分割 n 个数的乘积. 设
| p(a,b) º
(a+1)(a+2) ¼(b-1) b = |
b!
a!
|
, |
|
明显有递推关系:
| p(a,b) = p |
æ
ç
è |
a, |
a+b
2
|
ö
÷
ø |
p |
æ
ç
è |
a+b
2
|
,b |
ö
÷
ø |
, |
|
这样处理后,
当中涉及到的乘法乘数和被乘数的位数都相差不太大,
可以利用快速乘法提高效率.
当然最好是能做到两乘数的大小几乎一样,
因为这样通常可以减少计算步骤, 又充分利用了快速乘法.
最后n!=p(0,n).
上例给了我们一个很好的启示.
事实上, 许多级数都可以用类似的办法来处理, 但是要注意必须使用
FFT乘法 才有较好效果.
|
| 2 二分法在指数级数 |
|
我们要计算 e 的前 n 位小数, 用级数:
设有:
Q(a,b) = (a+1)(a+2)¼b
P(a,b) = b(b-1)¼(a+2)
+ b(b-1)¼(a+3) +¼+
(b-1)b + b + 1
整数 P(a,b) & Q(a,b) 满足:
P(a,b)
Q(a,b) |
= |
1
a+1 |
+ |
1
(a+1)(a+2) |
+ ¼+ |
1
(a+1)(a+2)¼b |
|
特别地, 1+P(0,K)/Q(0,K) 是e的级数前 K 项和.二分地计算 P(a,b) & Q(a,b)
, m 是a,b的均值:
| m = |
é
ê
ë |
a+b
2 |
ù
ú
û |
(整数部分). |
|
有递推关系:
P(a,b) = P(a,m)Q(m,b)+P(m,b),
Q(a,b) = Q(a,m)Q(m,b).
也可以用其它方法计算m,
原则是计算简单和使得P(a,m), Q(m,b)或Q(a,m), Q(m,b)的大小相近.
最后的除法计算方法见 {高精度求倒数}
{}
{程序}
|
| 3 二分法用在
arctan 级数. |
| |
我们再来看 arctan 级数的计算
| arctan |
æ
ç
è |
1
q |
ö
÷
ø |
= |
1
q |
- |
1
3q3 |
+ |
1
5q5 |
+¼
= |
å
k ³
0 |
(-1)k
(2k+1)q2k+1 |
|
比指数级数稍有复杂, 设:
R(a,b) = (2a+3)(2a+5)¼(2b+1),
Q(a,b) = (2a+3)(2a+5)¼(2b+1)
q2(b-a),
| P(a,b) = (-1)a+1 |
R(a,b)
2a+3 |
q2(b-a-1)
+ (-1)a+2 |
R(a,b)
2a+5 |
q2(b-a-2)
+¼ |
|
| + (-1)b-1 |
R(a,b)
2b-1 |
q2 +
(-1)b |
R(a,b)
2b+1 |
有:
P(a,b)
Q(a,b) |
= |
b
å
k=a+1
|
(-1)k
(2k+1)q2(k-a) |
|
令 a = 0, b = K, 表达式
|
1
q
|
|
æ
ç
è |
1 + |
P(0,K)
Q(0,K)
|
ö
÷
ø |
|
|
等于计算前 n 项 arctan 级数.
递归处理(m 是 a+b 均值):
P(a,b) = P(a,m)Q(m,b)+R(a,m)P(m,b),
Q(a,b) = Q(a,m)Q(m,b), R(a,b) = R(a,m)R(m,b).
|
| 4 自己动手来二分 |
|
初看上面的二分计算式会感到很奇怪:
它们是怎样得到的?
暂且不直接入主题, 而去验算上面的式子,
也许会有一点收获:
对于阶乘, 我们要验证:
P(a,b) = P(a,m)P(m,b) 和P(0,n)=n!
右边 P(a,m) * P(m,b)=(a+1)(a+2)...(m-1)m *
(m+1)(m+2)...(b-1)b = P(a,b)
当 a=0, b=n 时 P(a,b)=1*2*3*...*n=n!
第二例的验证就有点复杂了. 但你若亲自验证了它,
就表明你已正式踏入了二分法世界的大门.
P(a,m)Q(m,b)+P(m,b)
=[(a+2)(a+3)...m + (a+3)(a+4)...m + ... + 1 ] *
(m+1)(m+2)...b + [(m+2)(m+3)...b + (m+3)(m+4)...b + ... + 1 ]
=(a+2)(a+3)...m * (m+1)(m+2)...b + (a+3)(a+4)...m *
(m+1)(m+2)...b + ... + 1
=P(a,b)
剩下的步骤就很容易了.
至于 第三例 atan(1/p) 的验算是同样的方法.
但你若想象不出中间过程, 最好还是纸上写一遍,
加深理解和记忆.
回想后两例, 发现 P(a,m)Q(m,b)+P(m,b) 和 P(a,b) =
P(a,m)Q(m,b)+R(a,m)P(m,b) 实际上只是提出了公因子而得到的,
这就正构造二分计算式的关键.
下面以构造函数 e^(1/x)=sum_(0->∞)
1/x^()
------未完待续
|
Last update 22/1/2005
|