关于c#:使用倒数平方和计算PI

Calculate PI using sum of inverse squares

我需要使用以下公式以预先定义的精度计算π:

enter image description here

所以我得到了这个解决方案。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private static double CalculatePIWithPrecision(int presicion)
{
    if (presicion == 0)
    {
        return PI_ZERO_PRECISION;
    }

    double sum = 0;

    double numberOfSumElements = Math.Pow(10, presicion + 2);

    for (double i = 1; i < numberOfSumElements; i++)
    {
        sum += 1 / (i * i);
    }

    double pi = Math.Sqrt(sum * 6);
    return pi;
}

所以这是正确的,但我面临的问题是效率。它非常慢,精度值为8或更高。

有更好(更快)的吗?用这个公式计算π的方法?


1
   double numberOfSumElements = Math.Pow(10, presicion + 2);

我将严格用实际软件工程术语来讨论这个问题,避免在形式数学中迷失方向。只是任何软件工程师都应该知道的实用技巧。

首先观察代码的复杂性。执行所需的时间由该表达式严格确定。你已经写了一个指数算法,你计算的值会随着预测值的增加而迅速增加。你引用不舒服的数字,8产生10^10或者一个循环,可以计算100亿。是的,你注意到了这一点,那就是计算机开始花费几秒钟来产生结果,不管它们有多快。

指数算法不好,性能很差。只有一个具有阶乘复杂度的函数才能做得更糟,o(n!),上升得更快。否则,许多现实问题的复杂性。

现在,这个表达真的准确吗?您可以通过"肘部测试"来实现这一点,使用一个实际的封底示例。让我们选取5位数的精度作为目标,并写出:

1
 1.0000 + 0.2500 + 0.1111 + 0.0625 + 0.0400 + 0.0278 + ...  = 1.6433

可以看出,添加的内容会很快变小,并且会很快收敛。你可以推断出,一旦你添加的下一个数字变得足够小,那么它几乎不会使结果更准确。假设下一个数字小于0.00001,那么是时候停止尝试改进结果了。

所以你会停在1/(n*n)=0.00001=>n*n=100000=>n=sqrt(100000)=>n~=316

你的表达方式是10^(5+2)=10000000

您可以看出,您还差得很远,循环的频率太高,并且在最近的9999万次迭代中无法提高结果的准确性。

是时候讨论真正的问题了,太糟糕了,以至于你没有解释你是如何得出如此严重的错误算法的。但在测试代码时,您肯定发现它并不擅长计算更精确的圆周率值。因此,您认为通过更频繁地迭代,可以得到更好的结果。

请注意,在这个肘部测试中,能够以足够的精度计算加法也是非常重要的。我故意把数字四舍五入,就好像它是在一台能精确地加5位数的机器上计算出来的一样。不管你做什么,结果永远不会比5位数更精确。

您正在代码中使用双类型。直接由处理器支持,它没有无限的精度。唯一需要记住的一条规则是,使用双精度的计算永远不会比15位数更精确。还要记住浮动规则,它永远不会比7位数更精确。

因此,无论您传递给Presicion的值是多少,结果都不会比15位更精确。这根本没用,你已经有了精确到15位的π值。这是数学。

要解决这个问题,您需要做的一件事是使用比double精度更高的类型。事实上,它需要是一个具有任意精度的类型,它至少需要和您传递的Presicion值一样精确。这种类型在.NET框架中不存在。找到一个可以为您提供一个的库是SO中的一个常见问题。