如何在C#中计算PI?

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

我认为它将通过递归函数,如果是这样,它会是什么样子,是否有任何数学方程式支持它?

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

如果你想要递归:

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

Isaac Newton(你之前可能听说过他;))想出了这个伎俩。 请注意,我省略了结束条件,以保持简单。 在现实生活中,你需要一个。

如何使用:

 double pi = Math.PI; 

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

有几个非常非常古老的技巧,我很惊讶在这里看不到。

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 as:3.14159265358979323846这可能是最好的。

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

355/113是一个古老的中国估计,我相信它在多年前的22/7之前。 当我还是一名本科生时,它是由一位物理教授教给我的。

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

并行编程模式:使用.NET Framework理解和应用并行模式4

你可以在这个可爱的实现中找到(我身边的一些细微变化):

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

不同算法的良好概述:

  • 计算pi ;
  • 高斯 – 勒让德 – 萨拉明 。

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

我鼓励你尝试一下,但收敛速度非常快。

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

请注意,如果您对性能感兴趣,那么以有限精度(通常需要’double’,’float’,…输出)工作并不真正有意义,因为在这种情况下明显的答案就是硬编码值。

这是一篇关于在C#中计算PI的文章:

http://www.boyet.com/Articles/PiCalculator.html

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

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

计算如下:

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

你有Pi !!!

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

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

这是一个很好的方法(来自维基百科的主要条目 ); 它比上面讨论的简单公式收敛得快得多,并且如果你的目的是将递归作为一种学习练习,它非常适合递归解决方案。 (假设你是在学习经验之后,我没有提供任何实际的代码。)

基本公式与上述相同,但这种方法平均部分和以加速收敛。

定义一个两个参数函数,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大于零的值。

这个算法的优点在于,当您浏览逐渐变大的参数所产生的结果时,您可以使用电子表格轻松模拟它以检查代码。 当您计算饼图(10,10)时,您将获得pi的近似值,这对于大多数工程目的而言足够好。

 Enumerable.Range(0, 100000000).Aggregate(0d, (tot, next) => tot += Math.Pow(-1d, next)/(2*next + 1)*4) 
 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(); } } } 
  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; } 

在任何生产场景中,我都会强迫您查找该值,达到所需的小数点数,并将其存储为您的类可以到达的某个“const”。

(除非你正在编写科学’Pi’特定软件……)

关于…

……如何从学习的角度来看待它。

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

在任何一种情况下,我认为编写自己的Pi是一个已解决的问题。 德米特里已经展示了’Math.PI’常数。 在同一个空间攻击另一个问题! 去寻找通用的Newton近似值或者一些光滑的东西。

@Thomas Kammeyer:

请注意,Atan(1.0)经常是硬编码的,所以如果你正在调用一个库Atan函数,4 * Atan(1.0)并不是真正的’算法’(已经有很多建议确实通过替换Atan(x)来实现它的一系列(或无限产品),然后在x = 1时评估它。

此外, 极少数情况下,您需要pi的精度高于几十位 (可以很容易地进行硬编码!)。 我一直致力于数学应用,计算一些(相当复杂的)数学对象(具有整数系数的多项式),我必须对实数和复数(包括计算pi)进行算术运算,精度高达a几百万位…但这在现实生活中不是很常见’:)

您可以查找以下示例代码 。

我喜欢这篇论文 ,它解释了如何根据Arctangent的泰勒级数展开计算π。

本文从简单的假设开始

Atan(1)=π/ 4弧度

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

atan(x)= x – x ^ 3/3 + x ^ 5/5 – x ^ 7/7 + x ^ 9/9 ……

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

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

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

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

这里的一个很好的function是它是一个全精度双,你可以使用,或与计算结果进行比较。 该URL的选项卡具有与C ++,F#和Visual Basic类似的常量。

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

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

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

Wolfram Pi Formulas

PI(π)可以通过使用无穷级数来计算。 以下是两个例子:

Gregory-Leibniz系列:

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

Nilakantha系列:

π= 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 Series返回六个正确的PI小数,迭代次数为100次,而Gregory-Leibniz Series返回五个正确的PI小数,迭代次数为100次:

在此处输入图像描述

我的代码可以在这里测试>>

这是一个很好的方法:计算一系列1 / x ^ 2的x从1到你想要的 – 更大的数字 – 更好的馅饼结果。 将结果乘以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); } 
 public double PI = 22.0 / 7.0;