前几天,我发表了一篇随笔:“BigArithmetic - 提供任意精度的算术运算的静态类”。现在,让我们使用 BigArithmetic 类来计算圆周率。
我们需要一个计算 π 的分析算法。有用的算法是二次收敛的,即每一次迭代使有效位数增加一倍。计算 π 的二次收敛算法是基于 ACM 法(算术几何平均法)。首先设置初始值为:
然后,当 i = 0, 1, ... 重复迭代:
直到足够的精度。
下面就是源程序代码:
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 }
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 }
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#程序”这篇随笔中的结果相对照。
分类:
算法
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· [AI/GPT/综述] AI Agent的设计模式综述