Pergunta

Como posso calcular o valor do PI usando C#?

Eu estava pensando que seria por meio de uma função recursiva. Em caso afirmativo, como seria e há alguma equação matemática para apoiá-la?

Não sou muito exigente com o desempenho, principalmente com como fazê-lo do ponto de vista do aprendizado.

Foi útil?

Solução

Se você quiser recursão:

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

Isso se tornaria, depois de algumas reescritas:

PI = 2 * F(1);

com F(eu):

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

Isaac Newton (você já deve ter ouvido falar dele antes;)) inventou esse truque.Observe que deixei de fora a condição final, para simplificar.Na vida real, você meio que precisa de um.

Outras dicas

Que tal usar:

double pi = Math.PI;

Se você quiser uma precisão melhor que essa, precisará usar um sistema algorítmico e do tipo Decimal.

Se você der uma olhada neste guia realmente bom:

Padrões para programação paralela:Compreendendo e aplicando padrões paralelos com o .NET Framework 4

Você encontrará na página 70 esta implementação fofa (com pequenas alterações da minha 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;
}

Existem alguns truques muito, muito antigos que estou surpreso por não ver aqui.

atan (1) == pi/4, então uma velha castanha quando está presente uma função confiável de arc-tangente é 4*atan (1).

Uma estimativa muito fofa de razão fixa que faz com que o antigo ocidental 22/7 pareça com a sujeira é 355/113, o que é bom para vários lugares decimais (pelo menos três ou quatro, eu acho).Em alguns casos, isso é bom o suficiente para aritmética inteira:multiplique por 355 e depois divida por 113.

355/113 também é fácil de memorizar (pelo menos para algumas pessoas):conte um, um, três, três, cinco, cinco e lembre-se de que você está nomeando os dígitos no denominador e no numerador (se você esquecer qual trigêmeo fica em cima, um pensamento de um microssegundo geralmente resolverá o problema).

Observe que 22/7 oferece:3,14285714, que está errado nas milésimas.

355/113 dá 3,14159292, o que não está errado até os dez milionésimos.

Ac.para /usr/include/math.h na minha caixa, M_PI é #definido como:3.14159265358979323846, o que provavelmente é bom o mais longe possível.

A lição que você obtém ao estimar o PI é que existem muitas maneiras de fazê -lo, nenhuma será perfeita e você deve resolvê -las pelo uso pretendido.

355/113 é uma estimativa chinesa antiga e acredito que seja muitos anos anterior a 22/7.Foi-me ensinado por um professor de física quando eu era estudante.

Boa visão geral de diferentes algoritmos:

Não tenho certeza sobre a complexidade reivindicada para o algoritmo Gauss-Legendre-Salamin no primeiro link (eu diria O(N log^2(N) log(log(N)))).

Eu encorajo você a tentar, porém, a convergência é realmente rápido.

Além disso, não tenho certeza sobre por que tentar converter um algoritmo processual bastante simples em um algoritmo recursivo?

Observe que se você estiver interessado em desempenho, então trabalhar com uma precisão limitada (normalmente, exigindo um 'duplo', 'float',...saída) realmente não faz sentido, já que a resposta óbvia nesse caso é apenas codificar o valor.

Aqui está um artigo sobre cálculo de PI em C#:

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

O que é IP?A circunferência de um círculo dividida pelo seu diâmetro.

Na computação gráfica você pode traçar/desenhar um círculo com centro em 0,0 a partir de um ponto inicial x,y, o próximo ponto x',y' pode ser encontrado usando uma fórmula simples:x' = x + y / h :y' = y - x' / h

h geralmente é uma potência de 2 para que a divisão possa ser feita facilmente com um deslocamento (ou subtraindo do expoente duplo).h também quer ser o raio r do seu círculo.Um ponto de partida fácil seria x = r, y = 0 e, em seguida, contar c o número de passos até x <= 0 para traçar um quarto de círculo.PI é 4*c/r ou PI é 4*c/h

A recursão em grande profundidade geralmente é impraticável para um programa comercial, mas a recursão final permite que um algoritmo seja expresso recursivamente, embora implementado como um loop.Às vezes, algoritmos de pesquisa recursiva podem ser implementados usando uma fila em vez da pilha do processo, a pesquisa precisa voltar de um beco sem saída e seguir outro caminho - esses pontos de retrocesso podem ser colocados em uma fila e vários processos podem retirar os pontos da fila e tentar outros caminhos.

Calcule assim:

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

Você tem Pi !!!

Este é o método mais simples que conheço.

O valor de PI converge lentamente para o valor real de Pi (3,141592165......).Se você repetir mais vezes, melhor.

Aqui está uma boa abordagem (de a principal entrada da Wikipedia sobre pi);ela converge muito mais rápido do que a fórmula simples discutida acima e é bastante adequada para uma solução recursiva se sua intenção for buscar a recursão como um exercício de aprendizagem.(Supondo que você esteja buscando a experiência de aprendizado, não estou fornecendo nenhum código real.)

A fórmula subjacente é a mesma acima, mas esta abordagem calcula a média das somas parciais para acelerar a convergência.

Defina uma função de dois 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

Portanto, sua primeira oportunidade de explorar a recursão é codificar esse cálculo "horizontal" à medida que o parâmetro "largura" aumenta (para "altura" igual a zero).

Em seguida, adicione a segunda dimensão com esta fórmula:

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

que é usado, é claro, apenas para valores de h maiores que zero.

O bom desse algoritmo é que você pode facilmente fazer uma simulação com uma planilha para verificar seu código à medida que explora os resultados produzidos por parâmetros progressivamente maiores.No momento em que calcular pie(10,10), você terá um valor aproximado para pi que é bom o suficiente para a maioria dos propósitos de engenharia.

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

Em qualquer cenário de produção, eu obrigaria você a procurar o valor, até o número desejado de casas decimais, e armazená-lo como um 'const' em algum lugar onde suas classes possam acessá-lo.

(a menos que você esteja escrevendo um software científico específico para 'Pi' ...)

A respeito de...

...como fazer isso do ponto de vista do aprendizado.

Você está tentando aprender a programar métodos científicos?ou para produzir software de produção?Espero que a comunidade veja isso como uma pergunta válida e não como um problema.

Em ambos os casos, acho que escrever seu próprio Pi é um problema resolvido.Dmitry já mostrou a constante 'Math.PI'.Ataque outro problema no mesmo espaço!Opte por aproximações genéricas de Newton ou algo inteligente.

@Thomas Kammeyer:

Observe que Atan(1.0) é frequentemente codificado, então 4*Atan(1.0) não é realmente um 'algoritmo' se você estiver chamando uma função Atan de biblioteca (algumas já sugeridas de fato prosseguem substituindo Atan(x) por uma série (ou produto infinito) para ele e, em seguida, avaliando-o em x=1.

Também, há muito poucos casos em que você precisaria de pi com mais precisão do que algumas dezenas de bits (que pode ser facilmente codificado!).Trabalhei em aplicações em matemática onde, para calcular alguns objetos matemáticos (bastante complicados) (que eram polinômios com coeficientes inteiros), tive que fazer aritmética em números reais e complexos (incluindo calcular pi) com uma precisão de até um alguns milhões de bits...mas isso não é muito frequente 'na vida real' :)

Você pode procurar o seguinte exemplo código.

Eu gosto este papel, que explica como calcular π com base em uma expansão em série de Taylor para Arctangent.

O artigo começa com a simples suposição de que

Atan(1) = π/4 radianos

Atan(x) pode ser estimado iterativamente com a série de Taylor

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

O artigo aponta por que isso não é particularmente eficiente e prossegue com uma série de refinamentos lógicos na técnica.Eles também fornecem um programa de exemplo que calcula π com alguns milhares de dígitos, completo com código-fonte, incluindo as rotinas matemáticas de precisão infinita necessárias.

O link a seguir mostra como calcular a constante pi com base na sua definição como integral, que pode ser escrita como limite de um somatório, é muito interessante:https://sites.google.com/site/rcorcs/posts/calculatingthepicoconstantO arquivo “Pi como integral” explica esse método utilizado neste post.

Primeiro, observe que C# pode usar o campo Math.PI do .NET framework:

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

O recurso interessante aqui é que é um duplo de precisão total que você pode usar ou comparar com resultados computados.As guias nessa URL têm constantes semelhantes para C++, F# e Visual Basic.

Para calcular mais lugares, você pode escrever seu próprio código de precisão estendida.Um que seja rápido de codificar e razoavelmente rápido e fácil de programar é:

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

Esta fórmula e muitas outras, incluindo algumas que convergem a taxas surpreendentemente rápidas, como 50 dígitos por termo, estão na Wolfram:

Fórmulas Wolfram Pi

PI(π) pode ser calculado usando série infinita.Aqui estão dois exemplos:

Série Gregory-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;
}

Série 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;
}

O parâmetro de entrada n para ambas as funções representa o número de iterações.

A Série Nilakantha em comparação com a Série Gregory-Leibniz converge mais rapidamente.Os métodos podem ser testados com o seguinte 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();
}

A saída a seguir mostra que a série Nilakantha retorna seis decimais corretos de PI com cem iterações, enquanto a série Gregory-Leibniz retorna cinco decimais corretos de PI com um milhão de iterações:

enter image description here

Meu código pode ser testado >> aqui

Aqui está uma boa maneira:Calcule uma série de 1/x ^ 2 para x de 1 até o que você quiser - quanto maior o número - melhor o resultado da torta.Multiplique o resultado por 6 e para sqrt().Aqui está o código em c# (somente 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 em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top