21. 计算 PI 的小数后 10000 位

 

20. 用跨网游标处理多行大数据

根据下面的 J.Marchin 的圆周率计算公式:

imagepng

其第 n 项为:

imagepng

如果需要计算到 pi 的小数点后第 L 位,则至少需要计算至第 n 项,其中 n 为:

imagepng

以上的中括号 [] 均为高斯函数,即取整函数,等于不大于该数值的最大整数。

下面,请利用以上公式,将 pi 推算到小数点后 10000 位,并估算计算所用毫秒数。

提示:

由于小数点后 10000 位的精度是各种类型的数据都难以满足的,因此可以考虑使用大实数计算来处理,为此需要先准备大整数计算的脚本 BigIntegerCalc.splx:

A B C D
1 return case(calc,“+”:func(A4,[a,b]),“-”:func(A8,[a,b]),“*”:func(A12,[a,b]),“/”:func(A16,[a,b]))
2 10000000000L return A1
3 /add
4 func =A4(1).rvs() =A4(2).rvs() >c=0
5 =B4.((sum=~+C4(#)+c,if(sum>A2,(c=1,sum-A2),(c=0,sum))))
6 >if(c>0,B5=B5|long(c)) return B5.rvs()
7 /minus
8 func =A8(1).rvs() =A8(2).rvs() >c=0
9 =B8.((differ=~-C8(#)-c,if(differ<0,(c=1,differ+A2),(c=0,differ))))
10 return B9.rvs()
11 /multiply
12 func =A12(1).rvs() =A12(2) >c=0
13 =B4.((prodct=~+C12(#)+c,c=long(product/A2),long(product%A2)))
14 >if(c>0,B13=B13|long(c)) return B13.rvs()
15 /divide
16 func =A16(1) =A16(2) >r=0
17 =B16.((dividend=~+r,(r=(dividend%C16)*A2, dividend\C16)))
18 return B17

其中,calc 为参数输入运算符号 +-*/,a 和 b 为参与运算的大实数序列或普通整数:

为了易于理解,A3,A7 等格子中记录了注释信息,以 / 开头的字符不做任何处理,仅用于记录说明文字。A1 中根据不同的符号,调用不同的子程序执行计算,调用时用序列参数,这种使用方法会使得调用时,参数都填入子程序的起始格中。计算时,每 10 位为一个“数字”,而后将每 10 位视为一个新的位数值,形成一个 10 位的 1010进制的数字,而计算时的处理类似于普通四则运算。为了区分各个子程序的功能,在每个子程序之前添加了注释信息来说明。

以乘法为例,由最低位“数字”开始计算,为此在计算前在 B12 和 C12 中将各个“数字”位倒转。用变量 c 记录每位“数字”计算时的进位,计算出当前“数字”位的乘积 product 后,用整数除法计算出进位,余数作为本位结果。每一位循环计算完毕后,需判断是否仍然有进位,若有需要在结果中添加一个高位。

特别的,由于这里是在处理大整数计算,因此在计算除法时,要求除数不超过进制单位,而结果仅保存整数部分,除不尽的余数舍弃。

参考答案:

解答:

使用提示中的 BigIntegerCalc.splx 用 call 函数辅助计算。由于公式中的第 n 项和第 n+1 项中,前半部分相差 25 倍,后半部分相差 239*239 倍,因此计算首项时同样把除数调整为 25 和 239*239。

A B C
1 =now() 10000 =int(B1/10)+3
2 =C1*[0] =C1*[0] >B2(1)=16*5
3 BigIntegerCalc.splx =C1*[0] >B3(1)=4*239
4 for int(C1/(2*lg(5))+1)
5 >B2=call(A3,“/”,B2,25)
6 >B3=call(A3,“/”,B3,239*239)
7 =call(A3,“-”,B2,B3)
8 =call(A3,“/”,B7,2*#A4-1)
9 if #A4%2==1 >A2=call(A3,“+”,A2,B8)
10 else >A2=call(A3,“-”,A2,B8)
11 =string(A2(1))+“.”+A2.to(2,).(string(~,“0000000000”)).concat() =interval@ms(A1,now())

C1 中准备足够的“数字”位来存储结果,其中每位“数字”为 10 位整数,用指定位数的大整数计算来替代实数计算。B2 和 B3 中存储每次循环时,公式中所需的计算参数,在 A4 中执行所需的循环次数。执行后在 A2 中能够查看按“数字”位存储的结果序列,A11 中拼接出最终结果,需要注意的是,如果某个“数字”不满 10 位需要在前方补 0。

在本题中,需要频繁调用脚本 BigIntegerCalc.splx,为此也可以将它登记为常规函数,如将 A3 中代码修改为 >register(“bigintcalc”,“BigIntegerCalc.splx”),此后调用脚本时也需要改为直接使用登记的函数,以 B5 中代码为例,需要修改为 >B2=bigintcalc(B2,25),其执行效果是一致的。登记过后,这个函数将对所有脚本都有效。


22. 绘图的简单使用
目录和习题数据