使用快速傅里叶变换计算圆周率

前几天,我发表了一篇随笔:“BigArithmetic - 提供任意精度的算术运算的静态类”。现在,让我们使用 BigArithmetic 类来计算圆周率。

我们需要一个计算 π 的分析算法。有用的算法是二次收敛的,即每一次迭代使有效位数增加一倍。计算 π 的二次收敛算法是基于 ACM 法(算术几何平均法)。首先设置初始值为:

pi_x0

pi_pi0

pi_y0

然后,当 i = 0, 1, ... 重复迭代:

pi_x

pi_pi

pi_y

直到足够的精度。

下面就是源程序代码:

 1  using  System;
 2 
 3  namespace  Skyiv.Numeric
 4  {
 5     ///   <summary>
 6     ///  圆周率
 7     ///   </summary>
 8     static   class  Pi
 9    {
10       // = pp.780-781, mppi, 20.6 任意精度的运算
11       ///   <summary>
12       ///  计算圆周率到小数点后 digits 位数字。
13       ///  计算结果保存在返回的字节数组中,每个字节存放两个十进制数字,字节数组的第一个元素是“03”。
14       ///  字节数组的长度可能大于 (digits + 1) / 2 + 1,只保证小数点后前 digits 个十进制数字是准确的。
15       ///   </summary>
16       ///   <param name="digits"> 小数点后的十进制数字个数 </param>
17       ///   <returns> 存放圆周率的字节数组 </returns>
18       public   static   byte [] Compute( int  digits)
19      {
20         if  (digits  <   0 throw   new  ArgumentOutOfRangeException( " digits " " can't less than zero " );
21         int  n  =  Math.Max( 5 , (digits  +   1 /   2   +   2 );
22         byte [] pi  =   new   byte [n  +   1 ];
23         byte [] x  =   new   byte [n  +   1 ], y  =   new   byte [n  <<   1 ];
24         byte [] sx  =   new   byte [n], sxi  =   new   byte [n];
25         byte [] t  =   new   byte [n  <<   1 ], s  =   new   byte [ 3   *  n];
26        t[ 0 =   2 ;                              //  t = 2
27        BigArithmetic.Sqrt(x, x, n, t, n);     //  x = sqrt(2)
28        BigArithmetic.Add(pi, t, x, n);        //  pi = 2 + sqrt(2)
29        Array.Copy(pi,  1 , pi,  0 , n);
30        BigArithmetic.Sqrt(sx, sxi, n, x, n);  //  sx = sqrt(x)
31        Array.Copy(sx, y, n);                  //  y = sqrt(x)
32         for  (; ; )
33        {
34          BigArithmetic.Add(x, sx, sxi, n);        //  x = sqrt(x) + 1/sqrt(x)
35          Array.Copy(x,  1 , x,  0 , n);
36          BigArithmetic.Divide(x, x, n,  2 );        //  x = x / 2
37          BigArithmetic.Sqrt(sx, sxi, n, x, n);    //  sx = sqrt(x), sxi = 1/sqrt(x)
38          BigArithmetic.Multiply(t, y, n, sx, n);  //  t = y * sx
39          Array.Copy(t,  1 , t,  0 , n);
40          BigArithmetic.Add(t, t, sxi, n);         //  t = y * sx + sxi
41          x[ 0 ] ++ ;
42          y[ 0 ] ++ ;
43          BigArithmetic.Inverse(s, n, y, n);       //  s = 1 / (y + 1)
44          Array.Copy(t,  1 , t,  0 , n);
45          BigArithmetic.Multiply(y, t, n, s, n);   //  y = t / (y + 1)
46          Array.Copy(y,  1 , y,  0 , n);
47          BigArithmetic.Multiply(t, x, n, s, n);   //  t = (x + 1) / (y + 1)
48           int  mm  =  t[ 1 -   1 ;                       //  若 t == 1 则收敛
49           int  j  =  t[n]  -  mm;
50           if  (j  >   1   ||  j  <   - 1 )
51          {
52             for  (j  =   2 ; j  <  n; j ++ )
53            {
54               if  (t[j]  !=  mm)
55              {
56                Array.Copy(t,  1 , t,  0 , n);
57                BigArithmetic.Multiply(s, pi, n, t, n);  //  s = t * pi
58                Array.Copy(s,  1 , pi,  0 , n);              //  pi = t * pi
59                 break ;
60              }
61            }
62             if  (j  <  n)  continue ;
63          }
64           break ;
65        }
66         return  pi;
67      }
68    }
69  }

这个程序很简单,就是按照前面给出的公式进行计算而已。

下面是测试程序:

 1  using  System;
 2  using  System.Text;
 3  using  System.Diagnostics;
 4  using  Skyiv.Numeric;
 5 
 6  namespace  Skyiv
 7  {
 8     sealed   class  Test
 9    {
10       static   void  Main( string [] args)
11      {
12        var stopWatch  =  Stopwatch.StartNew();
13         int  digits  =  (args.Length  >   0 ?   int .Parse(args[ 0 ]) :  10000 ;
14        var bs  =  Pi.Compute(digits);
15        var sb  =   new  StringBuilder();
16        sb.AppendFormat( " Pi = {0}.{1} " , bs[ 0 ], Environment.NewLine);
17         for  ( int  i  =   1 ; i  <=  (digits  +   1 /   2 ; i ++ )
18        {
19          sb.Append(bs[i].ToString( " D2 " ));
20           if  (i  %   25   ==   0 ) sb.AppendLine();
21           else   if  (i  %   5   ==   0 ) sb.Append( '   ' );
22        }
23        stopWatch.Stop();
24         if  ((digits  +   1 /   2   %   25   !=   0 ) sb.AppendLine();
25        sb.AppendFormat( " DIGITS:{0:N0} ELAPSED:{1} " , digits, stopWatch.Elapsed);
26        Console.Write(sb);
27      }
28    }
29  }

运行结果如下:

Pi = 3.
1415926535 8979323846 2643383279 5028841971 6939937510
5820974944 5923078164 0628620899 8628034825 3421170679
...
4610126483 6999892256 9596881592 0560010165 5256375678
DIGITS:10,000 ELAPSED:00:00:04.0998524

这个结果可以和我在2005年9月30日写的“计算圆周率的C#程序”这篇随笔中的结果相对照。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值