2014-09-28 121 views
6

我需要使用该公式来计算PI与预定义的精度:计算PI使用逆平方和

enter image description here

所以我结束了这个解决方案。

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或更高。

是否有更好(更快!)的方式来计算PI使用该公式?

+0

如果只执行一次,是否真的如此慢以至于有问题? – 2014-09-28 12:34:50

+0

你是否想要计算一下你正在做的一些运动? 'Math.PI'返回3.14159265358979323846,然后您可以截取或舍入您想要的小数位数...... – 2014-09-28 12:37:39

+5

尝试在'for'语句中使用'int'。我怀疑在那里使用双重减慢你的速度。 – Sopuli 2014-09-28 12:37:52

回答

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

我将在实际的软件工程术语中严格谈论这一点,避免在正式的数学中迷失方向。只是任何软件工程师应该知道的实用技巧。

首先观察代码的复杂性。执行所花费的时间严格取决于此表达式。您已经编写了指数算法,您计算的值非常迅速地上升,因为认为增加。你引用不舒服的数字,8产生10^10或一个循环,使得计算结果为百万。是的,你注意到了这一点,那就是当计算机开始花费数秒来产生结果时,无论它们有多快。

指数算法不好,它们表现很差。 O(n!)的复杂度为,因为这个复杂度的上升速度更快,你只能做得更差。否则许多现实世界问题的复杂性。

现在,该表达式实际上是否准确?你可以用一个“肘测试”来做到这一点,它使用了一个实用的信封示例。让我们挑选的5位精度为目标,并把它写出来:

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 =开方(100000)=> N〜= 316

你表达说,停止在10 ^(5 + 2)= 10,000,000

你可以告诉你是方式关闭,循环过于频繁,并且没有提高最后999.9万次迭代结果的准确性。


时间谈论真正的问题,太糟糕了,你没有解释你如何得到这样一个非常错误的算法。但是当你测试你的代码时,你肯定发现它并不是很擅长为pi计算更精确的值。所以你认为通过迭代更频繁,你会得到更好的结果。

请注意,在这个弯头测试中,能够以足够的精度计算加法也很重要。我故意将这些数字四舍五入,就好像它是在一台能够执行5位精度加法的机器上计算的一样。无论你做什么,其结果都不可能是更多精确到5位数。

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

所以不管你通过什么价值presicion,结果可能永远比15位更精确。这根本没用,你已经有了pi值精确到15位数的功能。它是Math.Pi

你需要做的一件事是解决这个问题,它使用的类型比double更精确。实际上,它必须是一种具有任意精度的类型,它至少需要与您通过的认定值一样精确。 .NET框架中不存在这种类型。找到一个可以提供给你的库是SO的common question