如何在C#中计算圆周率?

30

如何使用C#计算PI的值?

我认为可以通过递归函数实现,如果是这样,它会是什么样子,有哪些数学方程支持它?

我对性能并不太挑剔,主要是从学习的角度考虑如何去做。


这个问题从算法的角度提供了许多不错的解决方案。我认为将其中一个方案适配到C#上应该并不难。 - Mark Biek
20个回答

45

如果你想要递归:

PI = 2 * (1 + 1/3 * (1 + 2/5 * (1 + 3/7 * (...))))

在进行一些修改后,这将变成:

PI = 2 * F(1);

使用 F(i) 的语法:

double F (int i) {
    return 1 + i / (2.0 * i + 1) * F(i + 1);
}

艾萨克·牛顿(你可能以前听说过他;))想出了这个技巧。请注意,为了保持简单,我省略了结束条件。在现实生活中,你有点需要一个。


还有,你需要返回一个值吗? - Jacqlyn
3
@jack,它没有终止条件或返回值。如果你想创建一个完整的工作示例,我建议你发布一个新答案。请查看答案中的评论“注意我省略了结束条件,为了保持简单。在实际生活中,你需要一个。” - dcaswell
4
这怎么有这么多赞?它什么都没有返回。 - Guy
1
这是理论上的,所以它不会返回任何东西。你必须定义一个精度。 - Alvaro
我已经让F()的定义返回了,对于那些认为这种事情很重要的人来说。不过我认为主要思想在之前已经很清楚了。我不熟悉C#,而且我习惯于具有隐式返回的语言。 - wvdschel
2
对于那些感兴趣的人,这被称为幂级数 - Bennett Yeo

25

使用以下方式如何:

double pi = Math.PI;

如果您想要比这更好的精度,您需要使用算法系统和 Decimal 类型。


3
我认为有更高精度需求的情况非常罕见,一般使用 Math.PI 已经足够。 - Chris Ballance
1
在这些情况下,您不能使用double,而需要一个任意精度数字库。 - CodesInChaos
1
在派日和树莓派黑客松上,我需要比这更高的精度。 - wtsang02
哈,这并不是他们寻找的答案,因为“calculate”是关键词。Math.PI是一个常量。 - Willem van Ketwich

7
如果你仔细阅读这份非常好的指南:Patterns for Parallel Programming: Understanding and Applying Parallel Patterns with the .NET Framework 4,你会在第70页找到这个可爱的实现(经过我的微小修改):
static decimal ParallelPartitionerPi(int steps)
{
    decimal sum = 0.0;
    decimal step = 1.0 / (decimal)steps;
    object obj = new object();

    Parallel.ForEach(
        Partitioner.Create(0, steps),
        () => 0.0,
        (range, state, partial) =>
        {
            for (int i = range.Item1; i < range.Item2; i++)
            {
                decimal x = (i - 0.5) * step;
                partial += 4.0 / (1.0 + x * x);
            }

            return partial;
        },
        partial => { lock (obj) sum += partial; });

    return step * sum;
}

3
有趣的方法,速度非常快。但是除了无法直接编译(如果您想我可以编辑它),它也不能正常工作。无论如何,我的观点是,一旦您确保取得正确数值并且看起来像PI后,请仔细查看,您会发现它给出的最后几位小数相当不准确,并且每次运行结果都会有所改变。文档中的double版本存在相同的问题,而decimal版本也没有更高的精度。 - Simon Mourier

6
有一些非常古老的技巧,我很惊讶为什么没有在这里看到。
atan(1)== PI / 4,因此当可信赖的反正切函数存在时,一个古老的笑话是4 * atan(1)。
一个非常可爱的定比估计使得旧的西方22/7显得像垃圾,即355/113,在几个小数位上是准确的(至少三到四个小数位)。在某些情况下,这甚至足以进行整数算术:乘以355然后除以113。
355/113也容易记忆(对于某些人来说):数一,一,三,三,五,五,并记住您正在命名分母和分子中的数字(如果您忘记哪个三元组应放在顶部,则经过微秒的思考通常会解决它)。
请注意,22/7给出:3.14285714,千分之一错误。
355/113给出的是3.14159292,直到千万分之一才会出错。
根据我的桌面上的/usr/include/math.h,M_PI被定义为:3.14159265358979323846,这可能是正确的。
从估计PI中学到的教训是,有很多方法可以做到这一点,但没有一种方法是完美的,你必须通过预期的使用方式来排序它们。
355/113是一个古老的中国估计值,我相信它比22/7早很多年。这是我本科时一个物理教授教给我的。

我对22/7感到惊讶。除了使用整数,考虑到它只有2位小数的精度,它可能有什么实际用途呢?也就是说,数值常量3.14不需要计算,字符数量相同,并且被每个人记住。这让我想起辛普森笑话中的“圆周率恰好是3!”的喊声,引起了一群争吵的教授们的注意,他们现在都变得沉默了。 :-) - Beejor

4

不同算法的良好概述:

我对第一个链接中声称的高斯-勒让德-萨拉明算法复杂度并不确定(我认为是O(N log ^ 2(N)log(log(N))))。

但是,我鼓励您尝试一下,因为收敛速度非常快。

此外,我不确定为什么要将一个相当简单的过程性算法转换为递归算法?

请注意,如果您关心性能,则在有限精度下工作(通常需要“double”,“float”等输出)并没有太多意义,因为在这种情况下,显而易见的答案就是硬编码值。


2
什么是PI? 圆周与其直径的比值。
在计算机图形学中,可以从初始点x,y绘制具有中心0,0的圆,下一个点x',y'可以使用简单公式找到:x'=x+y/h: y'=y-x'/h
'h'通常是2的幂,以便可以轻松地通过移位(或从双精度数的指数中减去)进行除法。 h也希望是您圆的半径'r'。一个简单的起点是x=r,y=0,然后计算步数c,直到x<=0为止,可以绘制四分之一的圆。PI为4 * c / r或PI为4 * c / h。
对于商业程序来说,递归到任何深度通常是不切实际的,但是尾递归允许将算法递归地表达,同时作为循环实现。递归搜索算法有时可以使用队列而不是进程的堆栈来实现,搜索必须从死胡同回溯并采用另一条路径-这些回溯点可以放在队列中,并且多个进程可以取消排队点并尝试其他路径。

1
using System;

namespace Strings
{
    class Program
    {
        static void Main(string[] args)
        {

/*          decimal pie = 1; 
            decimal e = -1;
*/
            var stopwatch = new System.Diagnostics.Stopwatch();
            stopwatch.Start(); //added this nice stopwatch start routine 

  //leibniz formula in C# - code written completely by Todd Mandell 2014
/*
            for (decimal f = (e += 2); f < 1000001; f++)
            {
                 e += 2;
                 pie -= 1 / e;
                 e += 2;
                 pie += 1 / e;
                 Console.WriteLine(pie * 4);
            }

                 decimal finalDisplayString = (pie * 4);
                 Console.WriteLine("pie = {0}", finalDisplayString);
                 Console.WriteLine("Accuracy resulting from approximately {0} steps", e/4); 
*/

// Nilakantha formula - code written completely by Todd Mandell 2014
// π = 3 + 4/(2*3*4) - 4/(4*5*6) + 4/(6*7*8) - 4/(8*9*10) + 4/(10*11*12) - (4/(12*13*14) etc

            decimal pie = 0;
            decimal a = 2;
            decimal b = 3;
            decimal c = 4;
            decimal e = 1;

            for (decimal f = (e += 1); f < 100000; f++) 
            // Increase f where "f < 100000" to increase number of steps
            {

                pie += 4 / (a * b * c);

                a += 2;
                b += 2;
                c += 2;

                pie -= 4 / (a * b * c);

                a += 2;
                b += 2;
                c += 2;

                e += 1;
            }

            decimal finalDisplayString = (pie + 3);
            Console.WriteLine("pie = {0}", finalDisplayString);
            Console.WriteLine("Accuracy resulting from {0} steps", e); 

            stopwatch.Stop();
            TimeSpan ts = stopwatch.Elapsed;
            Console.WriteLine("Calc Time {0}", ts); 

            Console.ReadLine();

         }
     }
 }

1
    public static string PiNumberFinder(int digitNumber)
    {
        string piNumber = "3,";
        int dividedBy = 11080585;
        int divisor = 78256779;
        int result;

        for (int i = 0; i < digitNumber; i++)
        {
            if (dividedBy < divisor)
                dividedBy *= 10;

            result = dividedBy / divisor;

            string resultString = result.ToString();
            piNumber += resultString;

            dividedBy = dividedBy - divisor * result;
        }

        return piNumber;
    }

1

计算方式如下:

x = 1 - 1/3 + 1/5 - 1/7 + 1/9  (... etc as far as possible.)
PI = x * 4

你得到了派!!!

这是我知道的最简单的方法。

派的值会慢慢收敛于实际的派值(3.141592165......)。如果迭代次数越多,结果越精确。


1
这是一个不错的方法(取自pi的维基百科主题词条);它的收敛速度比上述简单公式快得多,并且对于递归解决方案而言非常适合作为学习练习。 (假设您想要获得学习经验,我不会提供任何实际代码。)
底层公式与上面相同,但此方法平均了部分总和以加速收敛。
定义一个两个参数的函数pie(h, w),如下:
pie(0,1) = 4/1
pie(0,2) = 4/1 - 4/3
pie(0,3) = 4/1 - 4/3 + 4/5
pie(0,4) = 4/1 - 4/3 + 4/5 - 4/7
... and so on

因此,您第一次探索递归的机会是将“水平”计算编码为“宽度”参数增加(对于零高度)。

然后使用以下公式添加第二个维度:

pie(h, w) = (pie(h-1,w) + pie(h-1,w+1)) / 2

当然,这个算法仅适用于大于零的h值。

这个算法的好处是你可以很容易地用电子表格模拟它,通过逐步增加参数来检查代码并探索结果。当你计算pie(10,10)时,你将得到一个足够好的近似pi值,适用于大多数工程目的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接