【问题标题】:How do I calculate PI in C#?如何在 C# 中计算 PI?
【发布时间】:2010-09-07 13:15:22
【问题描述】:

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

我在想它是通过递归函数实现的,如果是这样,它会是什么样子,是否有任何数学方程式可以支持它?

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

【问题讨论】:

  • This question 从算法的角度来看有很多很好的解决方案。我认为将其中之一改编为 c# 并不难。

标签: c# pi


【解决方案1】:

这样计算:

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

你有 Pi !!!

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

PI 的值慢慢收敛到 Pi 的实际值(3.141592165……)。迭代次数越多越好。

【讨论】:

    【解决方案2】:

    如果你想要递归:

    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);
    }
    

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

    【讨论】:

    • 另外,需要返回值吗?
    • @jack 它没有终止条件或返回值。如果您想创建一个完整的工作示例,我建议您发布一个新答案。见答案Note that I left out the end condition, to keep it simple. In real life, you kind of need one.中的评论
    • 这怎么会有这么多赞?这不会返回 任何东西
    • 这是理论上的,这就是它不返回任何内容的原因。您必须定义一个精度
    • 对于那些感兴趣的人,这被称为power series
    【解决方案3】:

    如何使用:

    double pi = Math.PI;
    

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

    【讨论】:

    • 我认为这是一种罕见的情况,您需要比从 Math.PI 获得的精度更高;
    • 在这些情况下,您不能使用 double 但需要一个用于任意精度数字的库。
    • 在 pi day 和 pi hackaton 中,我需要比这更高的精度。
    • 哈哈。并不是他们真正想要的答案,calculate 是最重要的词。 Math.PI 是一个常数。
    【解决方案4】:

    在任何生产场景中,我都会强制您查找该值,找到所需的小数位数,并将其作为“const”存储在您的类可以访问的地方。

    (除非您正在编写科学的“Pi”专用软件...)

    【讨论】:

    • 对反对票的评论有助于我理解我做错了什么。现在我不得不猜测。也许你是一个心怀不满的计算机科学家,不欣赏我的务实方法。你不认为它回答了“如何从学习的角度去做”这个问题的精神。尽管我对此进行了辩论,因为我的答案是最有效的解决方案。
    • 问题是求算法来查找 pi,而不是如何使用 pi 的值
    【解决方案5】:

    对不同算法的良好概述:

    我不确定第一个链接中 Gauss-Legendre-Salamin 算法所声称的复杂性(我会说 O(N log^2(N) log(log(N))))。

    我鼓励你尝试一下,不过,收敛速度真的很快。

    另外,我不太确定为什么要尝试将一个非常简单的过程算法转换为递归算法?

    请注意,如果您对性能感兴趣,那么以有限的精度工作(通常需要“double”、“float”、... 输出)实际上并没有什么意义,因为在这种情况下显而易见只是对值进行硬编码。

    【讨论】:

      【解决方案6】:
      public double PI = 22.0 / 7.0;
      

      【讨论】:

      • PI == 3.14159... 22.0 / 7.0 == 3.14285... 几乎没有准确的答案。
      • -1 问题是要求一种用于教育目的的方法,而不是一个值,无论如何 22/7 是一个很差的近似值。
      【解决方案7】:

      关于...

      ...从学习的角度来看如何去做。

      您是否正在尝试学习编程科学方法?还是生产生产软件?我希望社区认为这是一个有效的问题,而不是挑剔。

      无论哪种情况,我认为编写自己的 Pi 是一个已解决的问题。 Dmitry 已经显示了“Math.PI”常数。攻击同一空间的另一个问题!寻找通用的牛顿近似值或其他巧妙的方法。

      【讨论】:

        【解决方案8】:

        这是一个不错的方法(来自the main Wikipedia entry on 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 近似值。

        【讨论】:

          【解决方案9】:

          什么是 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

          递归到任何深度,对于商业程序通常是不切实际的,但尾递归允许算法以递归方式表示,同时实现为循环。递归搜索算法有时可以使用队列而不是进程的堆栈来实现,搜索必须从死端回溯并采取另一条路径 - 这些回溯点可以放入队列中,多个进程可以取消排队并尝试其他路径。

          【讨论】:

            【解决方案10】:

            这里有一些非常非常古老的技巧,我很惊讶在这里没有看到。

            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 是#define'd: 3.14159265358979323846 就目前而言,这可能是好的。

            您从估算 PI 中得到的教训是,有很多方法可以做到这一点, 没有一个是完美的,您必须按预期用途对它们进行分类。

            355/113 是一个古老的中国估计,我相信它比 22/7 早了很多年。这是我本科时一位物理学教授教给我的。

            【讨论】:

            • 我对 22/7 感到惊讶。除了使用整数之外,考虑到它的 2 位小数精度,它怎么可能有任何实际用途?也就是说,数字常量 3.14 不需要计算,字符数相同,并且每个人都记住了。它让人想起辛普森一家的笑话“pi 正好是 3!”有人大喊大叫,以引起吵架教授的恐惧和沉默的房间的注意。 :-)
            【解决方案11】:

            @Thomas Kammeyer:

            请注意,Atan(1.0) 经常是硬编码的,因此,如果您正在调用库 Atan 函数,则 4*Atan(1.0) 并不是真正的“算法”(很多人已经建议确实通过替换 Atan( x) 对其进行系列(或无限积),然后在 x=1 处对其进行评估。

            另外,在极少数情况下您需要比几十位更精确的 pi(可以轻松硬编码!)。我从事数学应用工作,为了计算一些(相当复杂的)数学对象(具有整数系数的多项式),我必须对实数和复数(包括计算 pi)进行算术运算,精度高达几百万比特......但这在“现实生活”中并不常见:)

            您可以查看以下示例code

            【讨论】:

              【解决方案12】:
              Enumerable.Range(0, 100000000).Aggregate(0d, (tot, next) => tot += Math.Pow(-1d, next)/(2*next + 1)*4)
              

              【讨论】:

              • 实际上,如果您尝试使用 100000000,它会返回 3,14159264358933,这是错误的。如果您删除一个 0 (10000000),它会返回 3,14159265358979,这没关系。再次攻击浮点运算综述?
              【解决方案13】:

              如果你仔细看看这个非常好的指南:

              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;
              }
              

              【讨论】:

              • +1 非常快的有趣方法。但除了它不能按原样编译(如果你愿意,我可以编辑它)之外,它也不起作用。无论如何,我的观点是,一旦你做对了并且值看起来“看起来像” PI,深入研究一下,你会发现它给出的最后一个小数是非常不准确的,并且......每次运行都会改变。文档中的 double 版本有同样的问题,但 decimal 版本并不准确。
              【解决方案14】:

              下面的链接展示了如何根据积分的定义来计算 pi 常数,可以写成求和的极限,非常有趣: https://sites.google.com/site/rcorcs/posts/calculatingthepiconstant 文件“Pi as an integral”解释了本文中使用的这种方法。

              【讨论】:

                【解决方案15】:

                我喜欢this paper,它解释了如何根据反正切的泰勒级数展开计算 π。

                本文从一个简单的假设开始

                Atan(1) = π/4 弧度

                Atan(x) 可以用泰勒级数迭代估计

                atan(x) = x - x^3/3 + x^5/5 - x^7/7 + x^9/9...

                该论文指出了为什么这不是特别有效,并继续对该技术进行了一些逻辑改进。他们还提供了一个将 π 计算到几千位的示例程序,并附有源代码,包括所需的无限精度数学例程。

                【讨论】:

                • 当你在那里的时候,看看 awesome 分形生成器 :)
                【解决方案16】:
                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();
                
                         }
                     }
                 }
                

                【讨论】:

                  【解决方案17】:
                      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;
                      }
                  

                  【讨论】:

                    【解决方案18】:

                    首先,注意C#可以使用.NET框架的Math.PI字段:

                    https://msdn.microsoft.com/en-us/library/system.math.pi(v=vs.110).aspx

                    这里的优点是它是一个全精度双精度,您可以使用它,也可以与计算结果进行比较。该 URL 上的选项卡在 C++、F# 和 Visual Basic 中具有类似的常量。

                    要计算更多位置,您可以编写自己的扩展精度代码。一种快速编码且相当快速且易于编程的方法是:

                    Pi = 4 * [4 * arctan (1/5) - arctan (1/239)]

                    这个公式和许多其他公式,包括一些以惊人的速度收敛的公式,例如每项 50 位,都在 Wolfram:

                    Wolfram Pi Formulas

                    【讨论】:

                      【解决方案19】:

                      PI (π)可以用无穷级数计算。这里有两个例子:

                      格雷戈里-莱布尼茨系列:

                      π/4 = 1 - 1/3 + 1/5 - 1/7 + 1/9 - ...

                      C#方法:

                      public static decimal GregoryLeibnizGetPI(int n)
                      {
                          decimal sum = 0;
                          decimal temp = 0;
                          for (int i = 0; i < n; i++)
                          {
                              temp = 4m / (1 + 2 * i);
                              sum += i % 2 == 0 ? temp : -temp;
                          }
                          return sum;
                      }
                      

                      尼拉坎塔系列:

                      π = 3 + 4 / (2x3x4) - 4 / (4x5x6) + 4 / (6x7x8) - 4 / (8x9x10) + ...

                      C#方法:

                      public static decimal NilakanthaGetPI(int n)
                      {
                          decimal sum = 0;
                          decimal temp = 0;
                          decimal a = 2, b = 3, c = 4;
                          for (int i = 0; i < n; i++)
                          {
                              temp = 4 / (a * b * c);
                              sum += i % 2 == 0 ? temp : -temp;
                              a += 2; b += 2; c += 2;
                          }
                          return 3 + sum;
                      }
                      

                      两个函数的输入参数n代表迭代次数。

                      与 Gregory-Leibniz 系列相比,Nilakantha 系列收敛得更快。可以使用以下代码测试方法:

                      static void Main(string[] args)
                      {
                          const decimal pi = 3.1415926535897932384626433832m;
                          Console.WriteLine($"PI = {pi}");
                      
                          //Nilakantha Series
                          int iterationsN = 100;
                          decimal nilakanthaPI = NilakanthaGetPI(iterationsN);
                          decimal CalcErrorNilakantha = pi - nilakanthaPI;
                          Console.WriteLine($"\nNilakantha Series -> PI = {nilakanthaPI}");
                          Console.WriteLine($"Calculation error = {CalcErrorNilakantha}");
                          int numDecNilakantha = pi.ToString().Zip(nilakanthaPI.ToString(), (x, y) => x == y).TakeWhile(x => x).Count() - 2;
                          Console.WriteLine($"Number of correct decimals = {numDecNilakantha}");
                          Console.WriteLine($"Number of iterations = {iterationsN}");
                      
                          //Gregory-Leibniz Series
                          int iterationsGL = 1000000;
                          decimal GregoryLeibnizPI = GregoryLeibnizGetPI(iterationsGL);
                          decimal CalcErrorGregoryLeibniz = pi - GregoryLeibnizPI;
                          Console.WriteLine($"\nGregory-Leibniz Series -> PI = {GregoryLeibnizPI}");
                          Console.WriteLine($"Calculation error = {CalcErrorGregoryLeibniz}");
                          int numDecGregoryLeibniz = pi.ToString().Zip(GregoryLeibnizPI.ToString(), (x, y) => x == y).TakeWhile(x => x).Count() - 2;
                          Console.WriteLine($"Number of correct decimals = {numDecGregoryLeibniz}");
                          Console.WriteLine($"Number of iterations = {iterationsGL}");
                      
                          Console.ReadKey();
                      }
                      

                      以下输出显示 Nilakantha 系列在 100 次迭代中返回 PI 的六个正确小数位,而 Gregory-Leibniz 系列在 100 万次迭代中返回 PI 的五个正确小数位:

                      我的代码可以测试>>here

                      【讨论】:

                        【解决方案20】:

                        这是一个不错的方法: 为 x 从 1 计算一系列 1/x^2 到您想要的任何值 - 数字越大 - 饼图结果越好。将结果乘以 6 并乘以 sqrt()。 这是 c# 中的代码(仅主要):

                        static void Main(string[] args)
                            {
                                double counter = 0;
                                for (double i = 1; i < 1000000; i++)
                                {
                        
                                    counter = counter + (1 / (Math.Pow(i, 2)));
                        
                                }
                                counter = counter * 6;
                                counter = Math.Sqrt(counter);
                                Console.WriteLine(counter);
                            }
                        

                        【讨论】:

                          猜你喜欢
                          • 1970-01-01
                          • 1970-01-01
                          • 1970-01-01
                          • 2021-12-20
                          • 1970-01-01
                          • 2019-12-11
                          • 1970-01-01
                          • 2017-09-27
                          • 1970-01-01
                          相关资源
                          最近更新 更多