Pregunta

¿Cómo puedo calcular el valor de PI usando C#?

Estaba pensando que sería a través de una función recursiva. De ser así, ¿cómo se vería? ¿Existen ecuaciones matemáticas que lo respalden?

No soy demasiado exigente con el rendimiento, principalmente con cómo abordarlo desde el punto de vista del aprendizaje.

¿Fue útil?

Solución

Si quieres recursividad:

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

Esto se convertiría, después de algunas reescrituras:

PI = 2 * F(1);

con F(i):

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

A Isaac Newton (es posible que hayas oído hablar de él antes;)) se le ocurrió este truco.Tenga en cuenta que omití la condición final para simplificar las cosas.En la vida real, necesitas uno.

Otros consejos

¿Qué tal usar:

double pi = Math.PI;

Si desea una mayor precisión que esa, deberá utilizar un sistema algorítmico y el tipo Decimal.

Si echas un vistazo de cerca a esta guía realmente buena:

Patrones para programación paralela:Comprensión y aplicación de patrones paralelos con .NET Framework 4

Encontrarás en la página 70 esta linda implementación (con cambios menores por mi parte):

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

Hay un par de trucos muy, muy antiguos que me sorprende no ver aquí.

atan (1) == Pi/4, por lo que una castaña vieja cuando está presente una función de arco de arco confiable es 4*atan (1).

Una estimación de relación fija muy linda que hace que el antiguo occidental 22/7 parezca suciedad es 355/113, lo cual es bueno para varios decimales (al menos tres o cuatro, creo).En algunos casos, esto es incluso suficiente para la aritmética de enteros:multiplica por 355 y luego divide por 113.

355/113 también es fácil de memorizar (al menos para algunas personas):cuenta uno, uno, tres, tres, cinco, cinco y recuerda que estás nombrando los dígitos del denominador y del numerador (si olvidas qué triplete va arriba, un pensamiento de un microsegundo normalmente lo aclarará).

Tenga en cuenta que 22/7 le ofrece:3,14285714, que está mal en las milésimas.

355/113 te da 3.14159292, lo cual no está mal hasta las diezmillonésimas.

Acc.a /usr/include/math.h en mi cuadro, M_PI está #definido como:3.14159265358979323846, que probablemente sea bueno en lo que respecta.

La lección que obtiene al estimar PI es que hay muchas formas de hacerlo, ninguna será perfecta y debe resolverlos mediante el uso previsto.

355/113 es una estimación china antigua y creo que es anterior a 22/7 por muchos años.Me lo enseñó un profesor de física cuando era estudiante.

Buena descripción general de diferentes algoritmos:

No estoy seguro de la complejidad afirmada para el algoritmo Gauss-Legendre-Salamin en el primer enlace (yo diría O(N log^2(N) log(log(N)))).

Te animo a que lo pruebes, aunque la convergencia es en realidad rápido.

Además, no estoy muy seguro de por qué intentar convertir un algoritmo procesal bastante simple en uno recursivo.

Tenga en cuenta que si está interesado en el rendimiento, entonces trabaje con una precisión limitada (por lo general, requiere un 'doble', 'flotante',...salida) realmente no tiene sentido, ya que la respuesta obvia en tal caso es simplemente codificar el valor.

Aquí hay un artículo sobre el cálculo de PI en C#:

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

¿Qué es PI?La circunferencia de un círculo dividida por su diámetro.

En gráficos por computadora puedes trazar/dibujar un círculo con su centro en 0,0 desde un punto inicial x,y, el siguiente punto x',y' se puede encontrar usando una fórmula simple:x' = x + y/h :y' = y - x' / h

h suele ser una potencia de 2, por lo que la división se puede realizar fácilmente con un desplazamiento (o restando del exponente un doble).h también quiere ser el radio r de tu círculo.Un punto de partida fácil sería x = r, y = 0, y luego contar c el número de pasos hasta x <= 0 para trazar un cuarto de círculo.PI es 4*c/r o PI es 4*c/h

La recursividad a gran profundidad suele ser poco práctica para un programa comercial, pero la recursividad de cola permite que un algoritmo se exprese de forma recursiva, mientras se implementa como un bucle.Los algoritmos de búsqueda recursiva a veces se pueden implementar usando una cola en lugar de la pila del proceso, la búsqueda tiene que retroceder desde un callejón sin salida y tomar otro camino; estos puntos de retroceso se pueden poner en una cola, y múltiples procesos pueden quitar los puntos de la cola e intentar otros caminos.

Calcula así:

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

¡¡¡Tienes Pi!!!

Este es el método más simple que conozco.

El valor de PI converge lentamente al valor real de Pi (3,141592165......).Si repites más veces, mejor.

He aquí un buen enfoque (de la entrada principal de Wikipedia sobre pi);converge mucho más rápido que la fórmula simple analizada anteriormente y es bastante susceptible a una solución recursiva si su intención es seguir la recursividad como ejercicio de aprendizaje.(Suponiendo que lo que busca es la experiencia de aprendizaje, no le daré ningún código real).

La fórmula subyacente es la misma que la anterior, pero este enfoque promedia las sumas parciales para acelerar la convergencia.

Defina una función de dos parámetros, pie(h, w), tal que:

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

Entonces, su primera oportunidad de explorar la recursividad es codificar ese cálculo "horizontal" a medida que aumenta el parámetro de "ancho" (para una "altura" de cero).

Luego agregue la segunda dimensión con esta fórmula:

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

que se utiliza, por supuesto, sólo para valores de h mayores que cero.

Lo bueno de este algoritmo es que puedes simularlo fácilmente con una hoja de cálculo para verificar tu código mientras exploras los resultados producidos por parámetros progresivamente más grandes.Cuando calcules pie(10,10), tendrás un valor aproximado de pi que es lo suficientemente bueno para la mayoría de los propósitos de ingeniería.

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

En cualquier escenario de producción, lo obligaría a buscar el valor, con la cantidad deseada de puntos decimales, y almacenarlo como una 'constante' en algún lugar donde sus clases puedan acceder a él.

(a menos que esté escribiendo software científico específico 'Pi'...)

Acerca de...

...cómo hacerlo desde el punto de vista del aprendizaje.

¿Estás intentando aprender a programar métodos científicos?¿O producir software de producción?Espero que la comunidad vea esto como una pregunta válida y no como una minucia.

En cualquier caso, creo que escribir tu propio Pi es un problema resuelto.Dmitry ya mostró la constante 'Math.PI'.¡Ataca otro problema en el mismo espacio!Opte por aproximaciones genéricas de Newton o algo ingenioso.

@Thomas Kammeyer:

Tenga en cuenta que Atan(1.0) suele estar codificado, por lo que 4*Atan(1.0) no es realmente un 'algoritmo' si está llamando a una función de biblioteca Atan (unas cuantas ya sugeridas de hecho proceden reemplazando Atan(x) por una serie (o producto infinito) para él, luego evaluándolo en x=1.

También, Hay muy pocos casos en los que necesitarías pi con más precisión que unas pocas decenas de bits. (¡que se puede codificar fácilmente!).He trabajado en aplicaciones de matemáticas donde, para calcular algunos objetos matemáticos (bastante complicados) (que eran polinomios con coeficientes enteros), tenía que hacer aritmética con números reales y complejos (incluido el cálculo de pi) con una precisión de hasta un unos cuantos millones de bits...pero esto no es muy frecuente 'en la vida real' :)

Puedes buscar el siguiente ejemplo. código.

Me gusta este papel, que explica cómo calcular π basándose en una expansión en serie de Taylor para Arctangente.

El artículo comienza con la simple suposición de que

Atan(1) = π/4 radianes

Atan(x) se puede estimar de forma iterativa con la serie de Taylor

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

El artículo señala por qué esto no es particularmente eficiente y continúa realizando una serie de mejoras lógicas en la técnica.También proporcionan un programa de muestra que calcula π con unos pocos miles de dígitos, completo con el código fuente, incluidas las rutinas matemáticas de precisión infinita necesarias.

El siguiente enlace muestra cómo calcular la constante pi basándose en su definición como integral, que se puede escribir como límite de una sumatoria, es muy interesante:https://sites.google.com/site/rcorcs/posts/calculatingthepiconstantEl archivo "Pi como integral" explica este método utilizado en esta publicación.

Primero, tenga en cuenta que C# puede usar el campo Math.PI del marco .NET:

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

La buena característica aquí es que es un doble de precisión total que puedes usar o comparar con los resultados calculados.Las pestañas en esa URL tienen constantes similares para C++, F# y Visual Basic.

Para calcular más lugares, puedes escribir tu propio código de precisión extendida.Uno que es rápido de codificar y razonablemente rápido y fácil de programar es:

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

Esta fórmula y muchas otras, incluidas algunas que convergen a velocidades sorprendentemente rápidas, como 50 dígitos por término, se encuentran en Wolfram:

Fórmulas de Wolfram Pi

PI (π) se puede calcular usando series infinitas.Aquí hay dos ejemplos:

Serie Gregorio-Leibniz:

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

Método 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;
}

Serie Nilakantha:

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

Método 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;
}

El parámetro de entrada n para ambas funciones representa el número de iteraciones.

La Serie Nilakantha en comparación con la Serie Gregory-Leibniz converge más rápidamente.Los métodos se pueden probar con el siguiente código:

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

El siguiente resultado muestra que la serie Nilakantha devuelve seis decimales correctos de PI con cien iteraciones, mientras que la serie Gregory-Leibniz devuelve cinco decimales correctos de PI con un millón de iteraciones:

enter image description here

Mi código se puede probar >> aquí

Aquí hay una buena manera:Calcule una serie de 1/x^2 para x desde 1 hasta lo que quiera: cuanto mayor sea el número, mejor será el resultado del pastel.Multiplica el resultado por 6 y por sqrt().Aquí está el código en C# (solo principal):

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;
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top