如何在 C# 中计算 PI?

发布于 2024-07-04 05:40:36 字数 112 浏览 5 评论 0 原文

如何使用 C# 计算 PI 值?

我想这将是通过一个递归函数,如果是这样,它会是什么样子并且有任何数学方程来支持它吗?

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

How can I calculate the value of PI using C#?

I was thinking it would be through a recursive function, if so, what would it look like and are there any math equations to back it up?

I'm not too fussy about performance, mainly how to go about it from a learning point of view.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(20

万劫不复 2024-07-11 05:40:41
public double PI = 22.0 / 7.0;
public double PI = 22.0 / 7.0;
月亮邮递员 2024-07-11 05:40:41

这是一个很好的方法:
计算 x 的一系列 1/x^2,从 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);
    }

Here is a nice way:
Calculate a series of 1/x^2 for x from 1 to what ever you want- the bigger number- the better pie result. Multiply the result by 6 and to sqrt().
Here is the code in c# (main only):

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);
    }
听你说爱我 2024-07-11 05:40:40

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

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

In any production scenario, I would compel you to look up the value, to the desired number of decimal points, and store it as a 'const' somewhere your classes can get to it.

(unless you're writing scientific 'Pi' specific software...)

半衬遮猫 2024-07-11 05:40:40
    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;
    }
    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;
    }
独夜无伴 2024-07-11 05:40:40

我喜欢这篇论文,它解释了如何基于泰勒级数展开来计算 π为反正切。

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

Atan(1) = π/4 弧度

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

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

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

I like this paper, which explains how to calculate π based on a Taylor series expansion for Arctangent.

The paper starts with the simple assumption that

Atan(1) = π/4 radians

Atan(x) can be iteratively estimated with the Taylor series

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

The paper points out why this is not particularly efficient and goes on to make a number of logical refinements in the technique. They also provide a sample program that computes π to a few thousand digits, complete with source code, including the infinite-precision math routines required.

笑红尘 2024-07-11 05:40:40

@Thomas Kammeyer:

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

此外,很少有情况需要比几十位更精确的 pi(可以很容易地进行硬编码!)。我曾经研究过数学应用程序,为了计算一些(相当复杂的)数学对象(具有整数系数的多项式),我必须对实数和复数进行算术运算(包括计算 pi),精度高达几百万位...但这在现实生活中并不常见:)

您可以查找以下示例 代码

@Thomas Kammeyer:

Note that Atan(1.0) is quite often hardcoded, so 4*Atan(1.0) is not really an 'algorithm' if you're calling a library Atan function (an quite a few already suggested indeed proceed by replacing Atan(x) by a series (or infinite product) for it, then evaluating it at x=1.

Also, there are very few cases where you'd need pi at more precision than a few tens of bits (which can be easily hardcoded!). I've worked on applications in mathematics where, to compute some (quite complicated) mathematical objects (which were polynomial with integer coefficients), I had to do arithmetic on real and complex numbers (including computing pi) with a precision of up to a few million bits... but this is not very frequent 'in real life' :)

You can look up the following example code.

还如梦归 2024-07-11 05:40:40

关于...

...如何从学习的角度着手。

您是否正在尝试学习科学方法编程? 或者制作生产软件? 我希望社区认为这是一个有效的问题,而不是一个吹毛求疵的问题。

无论哪种情况,我认为编写自己的 Pi 都是一个已解决的问题。 Dmitry 已经显示了“Math.PI”常数。 在同一空间解决另一个问题! 寻求通用的牛顿近似或一些巧妙的东西。

Regarding...

... how to go about it from a learning point of view.

Are you trying to learning to program scientific methods? or to produce production software? I hope the community sees this as a valid question and not a nitpick.

In either case, I think writing your own Pi is a solved problem. Dmitry showed the 'Math.PI' constant already. Attack another problem in the same space! Go for generic Newton approximations or something slick.

┊风居住的梦幻卍 2024-07-11 05:40:40

首先,请注意,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 公式

First, note that C# can use the Math.PI field of the .NET framework:

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

The nice feature here is that it's a full-precision double that you can either use, or compare with computed results. The tabs at that URL have similar constants for C++, F# and Visual Basic.

To calculate more places, you can write your own extended-precision code. One that is quick to code and reasonably fast and easy to program is:

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

This formula and many others, including some that converge at amazingly fast rates, such as 50 digits per term, are at Wolfram:

Wolfram Pi Formulas

世俗缘 2024-07-11 05:40:40

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

The following link shows how to calculate the pi constant based on its definition as an integral, that can be written as a limit of a summation, it's very interesting:
https://sites.google.com/site/rcorcs/posts/calculatingthepiconstant
The file "Pi as an integral" explains this method used in this post.

烟酉 2024-07-11 05:40:40

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 的 6 位正确小数,而 Gregory-Leibniz Series 在 100 万次迭代中返回 PI 的 5 位正确小数:

在此处输入图像描述

我的代码可以测试>>> 此处

PI (π) can be calculated by using infinite series. Here are two examples:

Gregory-Leibniz Series:

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

C# method :

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 Series:

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

C# method:

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

The input parameter n for both functions represents the number of iteration.

The Nilakantha Series in comparison with Gregory-Leibniz Series converges more quickly. The methods can be tested with the following code:

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

The following output shows that Nilakantha Series returns six correct decimals of PI with one hundred iterations whereas Gregory-Leibniz Series returns five correct decimals of PI with one million iterations:

enter image description here

My code can be tested >> here

缺⑴份安定 2024-07-11 05:40:40
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();

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

         }
     }
 }
十雾 2024-07-11 05:40:39

对不同算法的良好概述:

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

不过,我确实鼓励您尝试一下,收敛速度非常

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

请注意,如果您对性能感兴趣,那么以有界精度工作(通常需要“双精度”、“浮点”……输出)并没有真正意义,因为在这种情况下明显的答案只是对值进行硬编码。

Good overview of different algorithms:

I'm not sure about the complexity claimed for the Gauss-Legendre-Salamin algorithm in the first link (I'd say O(N log^2(N) log(log(N)))).

I do encourage you to try it, though, the convergence is really fast.

Also, I'm not really sure about why trying to convert a quite simple procedural algorithm into a recursive one?

Note that if you are interested in performance, then working at a bounded precision (typically, requiring a 'double', 'float',... output) does not really make sense, as the obvious answer in such a case is just to hardcode the value.

欢烬 2024-07-11 05:40:39

什么是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 is 4 * c / r 或 PI is 4 * c / h

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

What is PI? The circumference of a circle divided by its diameter.

In computer graphics you can plot/draw a circle with its centre at 0,0 from a initial point x,y, the next point x',y' can be found using a simple formula:
x' = x + y / h : y' = y - x' / h

h is usually a power of 2 so that the divide can be done easily with a shift (or subtracting from the exponent on a double). h also wants to be the radius r of your circle. An easy start point would be x = r, y = 0, and then to count c the number of steps until x <= 0 to plot a quater of a circle. PI is 4 * c / r or PI is 4 * c / h

Recursion to any great depth, is usually impractical for a commercial program, but tail recursion allows an algorithm to be expressed recursively, while implemented as a loop. Recursive search algorithms can sometimes be implemented using a queue rather than the process's stack, the search has to backtrack from a deadend and take another path - these backtrack points can be put in a queue, and multiple processes can un-queue the points and try other paths.

So尛奶瓶 2024-07-11 05:40:39

像这样计算:

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

你有 Pi !!!

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

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

Calculate like this:

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

You have got Pi !!!

This is the simplest method I know of.

The value of PI slowly converges to the actual value of Pi (3.141592165......). If you iterate more times, the better.

找个人就嫁了吧 2024-07-11 05:40:39

这是一个很好的方法(来自 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 近似值。

Here's a nice approach (from the main Wikipedia entry on pi); it converges much faster than the simple formula discussed above, and is quite amenable to a recursive solution if your intent is to pursue recursion as a learning exercise. (Assuming that you're after the learning experience, I'm not giving any actual code.)

The underlying formula is the same as above, but this approach averages the partial sums to accelerate the convergence.

Define a two parameter function, pie(h, w), such that:

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

So your first opportunity to explore recursion is to code that "horizontal" computation as the "width" parameter increases (for "height" of zero).

Then add the second dimension with this formula:

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

which is used, of course, only for values of h greater than zero.

The nice thing about this algorithm is that you can easily mock it up with a spreadsheet to check your code as you explore the results produced by progressively larger parameters. By the time you compute pie(10,10), you'll have an approximate value for pi that's good enough for most engineering purposes.

空宴 2024-07-11 05:40:39
Enumerable.Range(0, 100000000).Aggregate(0d, (tot, next) => tot += Math.Pow(-1d, next)/(2*next + 1)*4)
Enumerable.Range(0, 100000000).Aggregate(0d, (tot, next) => tot += Math.Pow(-1d, next)/(2*next + 1)*4)
甜中书 2024-07-11 05:40:38

如何使用:

double pi = Math.PI;

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

How about using:

double pi = Math.PI;

If you want better precision than that, you will need to use an algorithmic system and the Decimal type.

左岸枫 2024-07-11 05:40:38

如果您仔细研究这个非常好的指南:

并行编程模式:使用 .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;
}

If you take a close look into this really good guide:

Patterns for Parallel Programming: Understanding and Applying Parallel Patterns with the .NET Framework 4

You'll find at Page 70 this cute implementation (with minor changes from my side):

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;
}
盛夏尉蓝 2024-07-11 05:40:38

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

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 早很多年。 这是我本科生时一位物理学教授教我的。

There are a couple of really, really old tricks I'm surprised to not see here.

atan(1) == PI/4, so an old chestnut when a trustworthy arc-tangent function is
present is 4*atan(1).

A very cute, fixed-ratio estimate that makes the old Western 22/7 look like dirt
is 355/113, which is good to several decimal places (at least three or four, I think).
In some cases, this is even good enough for integer arithmetic: multiply by 355 then divide by 113.

355/113 is also easy to commit to memory (for some people anyway): count one, one, three, three, five, five and remember that you're naming the digits in the denominator and numerator (if you forget which triplet goes on top, a microsecond's thought is usually going to straighten it out).

Note that 22/7 gives you: 3.14285714, which is wrong at the thousandths.

355/113 gives you 3.14159292 which isn't wrong until the ten-millionths.

Acc. to /usr/include/math.h on my box, M_PI is #define'd as:
3.14159265358979323846
which is probably good out as far as it goes.

The lesson you get from estimating PI is that there are lots of ways of doing it,
none will ever be perfect, and you have to sort them out by intended use.

355/113 is an old Chinese estimate, and I believe it pre-dates 22/7 by many years. It was taught me by a physics professor when I was an undergrad.

做个ˇ局外人 2024-07-11 05:40:37

如果你想要递归:

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

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

If you want recursion:

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

This would become, after some rewriting:

PI = 2 * F(1);

with F(i):

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

Isaac Newton (you may have heard of him before ;) ) came up with this trick.
Note that I left out the end condition, to keep it simple. In real life, you kind of need one.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文