Qual é a maneira mais rápida de obter o valor de π?


322

Estou procurando a maneira mais rápida de obter o valor de π, como um desafio pessoal. Mais especificamente, estou usando maneiras que não envolvem o uso de #defineconstantes como M_PI, ou codificação do número.

O programa abaixo testa as várias maneiras que conheço. A versão de montagem em linha é, em teoria, a opção mais rápida, embora claramente não seja portátil. Eu o incluí como uma linha de base para comparar com as outras versões. Nos meus testes, com embutidos, a 4 * atan(1)versão é mais rápida no GCC 4.2, porque ela dobra automaticamente atan(1)em uma constante. Com -fno-builtinespecificado, a atan2(0, -1)versão é mais rápida.

Aqui está o principal programa de testes ( pitimes.c):

#include <math.h>
#include <stdio.h>
#include <time.h>

#define ITERS 10000000
#define TESTWITH(x) {                                                       \
    diff = 0.0;                                                             \
    time1 = clock();                                                        \
    for (i = 0; i < ITERS; ++i)                                             \
        diff += (x) - M_PI;                                                 \
    time2 = clock();                                                        \
    printf("%s\t=> %e, time => %f\n", #x, diff, diffclock(time2, time1));   \
}

static inline double
diffclock(clock_t time1, clock_t time0)
{
    return (double) (time1 - time0) / CLOCKS_PER_SEC;
}

int
main()
{
    int i;
    clock_t time1, time2;
    double diff;

    /* Warmup. The atan2 case catches GCC's atan folding (which would
     * optimise the ``4 * atan(1) - M_PI'' to a no-op), if -fno-builtin
     * is not used. */
    TESTWITH(4 * atan(1))
    TESTWITH(4 * atan2(1, 1))

#if defined(__GNUC__) && (defined(__i386__) || defined(__amd64__))
    extern double fldpi();
    TESTWITH(fldpi())
#endif

    /* Actual tests start here. */
    TESTWITH(atan2(0, -1))
    TESTWITH(acos(-1))
    TESTWITH(2 * asin(1))
    TESTWITH(4 * atan2(1, 1))
    TESTWITH(4 * atan(1))

    return 0;
}

E o material de montagem em linha ( fldpi.c) que funcionará apenas para sistemas x86 e x64:

double
fldpi()
{
    double pi;
    asm("fldpi" : "=t" (pi));
    return pi;
}

E um script de construção que constrói todas as configurações que estou testando ( build.sh):

#!/bin/sh
gcc -O3 -Wall -c           -m32 -o fldpi-32.o fldpi.c
gcc -O3 -Wall -c           -m64 -o fldpi-64.o fldpi.c

gcc -O3 -Wall -ffast-math  -m32 -o pitimes1-32 pitimes.c fldpi-32.o
gcc -O3 -Wall              -m32 -o pitimes2-32 pitimes.c fldpi-32.o -lm
gcc -O3 -Wall -fno-builtin -m32 -o pitimes3-32 pitimes.c fldpi-32.o -lm
gcc -O3 -Wall -ffast-math  -m64 -o pitimes1-64 pitimes.c fldpi-64.o -lm
gcc -O3 -Wall              -m64 -o pitimes2-64 pitimes.c fldpi-64.o -lm
gcc -O3 -Wall -fno-builtin -m64 -o pitimes3-64 pitimes.c fldpi-64.o -lm

Além de testar entre vários sinalizadores do compilador (também comparei 32 bits contra 64 bits porque as otimizações são diferentes), também tentei mudar a ordem dos testes. Mas ainda assim, a atan2(0, -1)versão ainda sai por cima sempre.


38
Tem que haver uma maneira de fazer isso na metaprogramação em C ++. O tempo de execução será muito bom, mas o tempo de compilação não será.
22630 David Thornley

1
Por que você considera o uso do atan (1) diferente do uso do M_PI? Eu entenderia por que você quer fazer isso se você apenas usou operações aritméticas, mas com atan não vejo o ponto.
erikkallen

9
a pergunta é: por que você não gostaria de usar uma constante? por exemplo, definido por uma biblioteca ou por você mesmo? Computing Pi é um desperdício de ciclos de CPU, como este problema foi resolvido, uma e outra e outra vez para um número de dígitos significativos muito maiores do que o necessário para cálculos diários
Tilo

2
@ HopelessN00b No dialeto do Inglês eu falo, "otimização" é soletrado com um "S", não um "Z" (que é pronunciado como "zed", BTW, não "zee" ;-)). (Esta não é a primeira vez que eu tinha para reverter este tipo de edição, também, se você olhar para a história revisão.)
Chris Jester-Young

Respostas:


205

O método de Monte Carlo , como mencionado, aplica alguns grandes conceitos, mas claramente não é o mais rápido, nem por um longo prazo, nem por qualquer medida razoável. Além disso, tudo depende do tipo de precisão que você está procurando. O π mais rápido que conheço é aquele com os dígitos codificados. Olhando para Pi e Pi [PDF] , existem muitas fórmulas.

Aqui está um método que converge rapidamente - cerca de 14 dígitos por iteração. PiFast , o aplicativo mais rápido atual, usa essa fórmula com a FFT. Vou escrever a fórmula, já que o código é direto. Essa fórmula foi quase encontrada por Ramanujan e descoberta por Chudnovsky . Na verdade, é como ele calculou vários bilhões de dígitos do número - então não é um método para desconsiderar. A fórmula transbordará rapidamente e, como estamos dividindo os fatoriais, seria vantajoso adiar esses cálculos para remover termos.

insira a descrição da imagem aqui

insira a descrição da imagem aqui

Onde,

insira a descrição da imagem aqui

Abaixo está o algoritmo de Brent-Salamin . Wikipedia menciona que, quando um e b são "suficientemente próximo", em seguida, (a + b) ² / 4t será uma aproximação de π. Não sei ao certo o que significa "perto o suficiente", mas, nos meus testes, uma iteração obteve 2 dígitos, dois obtiveram 7 e três tiveram 15, é claro que isso é duplo, portanto, pode haver um erro com base em sua representação e o cálculo verdadeiro pode ser mais preciso.

let pi_2 iters =
    let rec loop_ a b t p i =
        if i = 0 then a,b,t,p
        else
            let a_n = (a +. b) /. 2.0 
            and b_n = sqrt (a*.b)
            and p_n = 2.0 *. p in
            let t_n = t -. (p *. (a -. a_n) *. (a -. a_n)) in
            loop_ a_n b_n t_n p_n (i - 1)
    in 
    let a,b,t,p = loop_ (1.0) (1.0 /. (sqrt 2.0)) (1.0/.4.0) (1.0) iters in
    (a +. b) *. (a +. b) /. (4.0 *. t)

Por fim, que tal um golfe pi (800 dígitos)? 160 caracteres!

int a=10000,b,c=2800,d,e,f[2801],g;main(){for(;b-c;)f[b++]=a/5;for(;d=0,g=c*2;c-=14,printf("%.4d",e+d/a),e=d%a)for(b=c;d+=f[b]*a,f[b]=d%--g,d/=g--,--b;d*=b);}

1
Supondo que você esteja tentando implementar o primeiro, o sqr (k3) não seria um problema? Tenho certeza de que acabaria com um número irracional que você precisará estimar (IIRC, todas as raízes que não são números inteiros são irracionais). Tudo o resto parece bem direto se você estiver usando uma aritmética de precisão infinita, mas essa raiz quadrada é um diferencial. O segundo inclui um sqrt também.
Bill K

2
na minha experiência, 'perto o suficiente' geralmente significa que há uma aproximação da série de Taylor envolvida.
10111 Stephen

117

Eu realmente gosto deste programa, porque ele se aproxima de π olhando para sua própria área.

IOCCC 1988: westley.c

#define _ -F<00||--F-OO--;
int F=00,OO=00;main(){F_OO();printf("%1.3f\n",4.*-F/OO/OO);}F_OO()
{
            _-_-_-_
       _-_-_-_-_-_-_-_-_
    _-_-_-_-_-_-_-_-_-_-_-_
  _-_-_-_-_-_-_-_-_-_-_-_-_-_
 _-_-_-_-_-_-_-_-_-_-_-_-_-_-_
 _-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
 _-_-_-_-_-_-_-_-_-_-_-_-_-_-_
 _-_-_-_-_-_-_-_-_-_-_-_-_-_-_
  _-_-_-_-_-_-_-_-_-_-_-_-_-_
    _-_-_-_-_-_-_-_-_-_-_-_
        _-_-_-_-_-_-_-_
            _-_-_-_
}

1
Se você substituir _ por -F <00 || --F-OO--, será mais fácil segui-lo :-)
Pat

1
ou, se você substituir _ por "if (o caractere anterior for '-') {OO--;} F--;"
FryGuy

6
ele imprime 0,25 aqui -.-
Johannes Schaub - litb 26/02/09

8
Esse programa foi excelente em 1998, mas foi interrompido porque os pré-processadores modernos são mais liberais ao inserir espaços em torno de expansões de macro para impedir que coisas como essa funcionem. É uma relíquia, infelizmente.
23711 Chris Lutz

38
Passe --traditional-cpppara o cpp para obter o comportamento pretendido.
Nietzche-jou

78

Aqui está uma descrição geral de uma técnica para calcular pi que aprendi no ensino médio.

Só compartilho isso porque acho que é simples o suficiente para que alguém possa se lembrar indefinidamente, além de ensinar o conceito de métodos "Monte-Carlo" - que são métodos estatísticos para se chegar a respostas que não parecem imediatamente dedutível através de processos aleatórios.

Desenhe um quadrado e inscreva um quadrante (um quarto de semicírculo) dentro desse quadrado (um quadrante com raio igual à lateral do quadrado, para que ele preencha o máximo possível de quadrados)

Agora jogue um dardo no quadrado e registre onde ele pousa - ou seja, escolha um ponto aleatório em qualquer lugar dentro do quadrado. É claro que ele caiu dentro do quadrado, mas está dentro do semicírculo? Registre esse fato.

Repita esse processo várias vezes - e você encontrará uma proporção do número de pontos dentro do semicírculo versus o número total lançado, chame essa proporção de x.

Como a área do quadrado é r vezes r, você pode deduzir que a área do semicírculo é x vezes r vezes r (ou seja, x vezes r ao quadrado). Portanto x vezes 4 lhe dará pi.

Este não é um método rápido de usar. Mas é um bom exemplo de um método de Monte Carlo. E se você olhar em volta, poderá descobrir que muitos problemas fora das suas habilidades computacionais podem ser resolvidos por esses métodos.


2
Este é o método que usamos para calcular Pi em um projeto java na escola. Apenas usei um randomizador para criar as coordenadas x, y e quanto mais 'dardos' jogamos, mais perto de Pi chegamos.
Jeff Keslinke 02/02/09

55

No interesse da integridade, uma versão do modelo C ++, que, para uma compilação otimizada, calculará uma aproximação do PI no tempo de compilação e alinhará com um único valor.

#include <iostream>

template<int I>
struct sign
{
    enum {value = (I % 2) == 0 ? 1 : -1};
};

template<int I, int J>
struct pi_calc
{
    inline static double value ()
    {
        return (pi_calc<I-1, J>::value () + pi_calc<I-1, J+1>::value ()) / 2.0;
    }
};

template<int J>
struct pi_calc<0, J>
{
    inline static double value ()
    {
        return (sign<J>::value * 4.0) / (2.0 * J + 1.0) + pi_calc<0, J-1>::value ();
    }
};


template<>
struct pi_calc<0, 0>
{
    inline static double value ()
    {
        return 4.0;
    }
};

template<int I>
struct pi
{
    inline static double value ()
    {
        return pi_calc<I, I>::value ();
    }
};

int main ()
{
    std::cout.precision (12);

    const double pi_value = pi<10>::value ();

    std::cout << "pi ~ " << pi_value << std::endl;

    return 0;
}

Nota para I> 10, as compilações otimizadas podem ser lentas, da mesma forma para execuções não otimizadas. Para 12 iterações, acredito que existam cerca de 80 mil chamadas para value () (na ausência de memória).


Eu executar este e obter "pi ~ 3,14159265383"
maxwellb

5
Bem, isso é preciso para 9dp's. Você está se opondo a algo ou apenas fazendo uma observação?
precisa

qual é o nome do algoritmo usado aqui para calcular o PI?
Sebastião Miranda

1
A fórmula de @ sebastião-miranda Leibniz , com uma aceleração média, melhora a convergência. pi_calc<0, J>calcula cada termo sucessivo da fórmula e o não especializado pi_calc<I, J>calcula a média.
jon-Hanson

43

Na verdade, há um livro inteiro dedicado (entre outras coisas) a métodos rápidos para o cálculo de \ pi: 'Pi and the AGM', de Jonathan e Peter Borwein ( disponível na Amazon ).

Estudei bastante a AGM e algoritmos relacionados: é bastante interessante (embora às vezes não seja trivial).

Observe que, para implementar os algoritmos mais modernos para calcular \ pi, você precisará de uma biblioteca aritmética multiprecisão ( GMP é uma boa escolha, embora tenha sido um tempo desde a última vez que o usei).

A complexidade de tempo dos melhores algoritmos está em O (M (n) log (n)), onde M (n) é a complexidade de tempo para a multiplicação de dois números inteiros de n bits (M (n) = O (n log (n) log (log (n))) usando algoritmos baseados em FFT, que geralmente são necessários ao calcular dígitos de \ pi, e esse algoritmo é implementado no GMP).

Observe que, embora a matemática por trás dos algoritmos possa não ser trivial, os algoritmos em si são geralmente algumas linhas de pseudo-código e sua implementação é geralmente muito simples (se você optar por não escrever sua própria aritmética de multiprecisão :-)).


42

As respostas a seguir precisamente como fazer isso da maneira mais rápida possível - com o mínimo esforço de computação . Mesmo se você não gostar da resposta, precisará admitir que é realmente a maneira mais rápida de obter o valor do PI.

A maneira mais rápida de obter o valor de Pi é:

1) escolha sua linguagem de programação favorita 2) carregue sua biblioteca de matemática 3) e descubra que o Pi já está definido lá - pronto para uso!

Caso você não tenha uma biblioteca de matemática em mãos ..

A SEGUNDA maneira MAIS RÁPIDA (solução mais universal) é:

procure Pi na Internet, por exemplo, aqui:

http://www.eveandersson.com/pi/digits/1000000 (1 milhão de dígitos. qual é a sua precisão de ponto flutuante?)

ou aqui:

http://3.141592653589793238462643383279502884197169399375105820974944592.com/

ou aqui:

http://en.wikipedia.org/wiki/Pi

É muito rápido encontrar os dígitos necessários para qualquer precisão aritmética que você gostaria de usar e, definindo uma constante, pode garantir que você não perca tempo precioso na CPU.

Não é apenas uma resposta parcialmente bem-humorada, mas, na realidade, se alguém for em frente e calcular o valor do Pi em um aplicativo real ... isso seria um desperdício bastante grande de tempo da CPU, não seria? Pelo menos não vejo um aplicativo real para tentar recalcular isso.

Prezado moderador, observe que o OP perguntou: "A maneira mais rápida de obter o valor do PI"


Caro Tilo: observe que o OP disse: "Estou procurando a maneira mais rápida de obter o valor de π, como um desafio pessoal. Mais especificamente, estou usando maneiras que não envolvem o uso de definir constantes como M_PI , ou codificar o número em .
Max

Caro @ Max: observe que o OP editou sua pergunta original depois que eu a respondi - isso não é culpa minha;) Minha solução ainda é a maneira mais rápida e resolve o problema com a precisão de ponto flutuante desejada e sem ciclos de CPU elegantes :)
Tilo

Oh, desculpe, eu não percebi. Apenas um pensamento, as constantes codificadas não teriam menos precisão do que calcular pi? Acho que depende de qual idioma e de como o criador está disposto a colocar todos os dígitos em :-)
Max

1
Porra, eu esqueci de adicionar Dear Tilo
Max

27

A fórmula BBP permite calcular o enésimo dígito - na base 2 (ou 16) - sem ter que se preocupar com os dígitos n-1 anteriores primeiro :)


23

Em vez de definir pi como uma constante, eu sempre uso acos(-1).


2
cos (-1) ou acos (-1)? :-P Esse (o último) é um dos casos de teste no meu código original. Está entre os meus preferidos (junto com atan2 (0, -1), que realmente é o mesmo que acos (-1), exceto que o acos geralmente é implementado em termos de atan2), mas alguns compiladores otimizam para 4 * atan (1) !
22420 Chris Jester-Young

21

Este é um método "clássico", muito fácil de implementar. Esta implementação em python (não a linguagem mais rápida) faz isso:

from math import pi
from time import time


precision = 10**6 # higher value -> higher precision
                  # lower  value -> higher speed

t = time()

calc = 0
for k in xrange(0, precision):
    calc += ((-1)**k) / (2*k+1.)
calc *= 4. # this is just a little optimization

t = time()-t

print "Calculated: %.40f" % calc
print "Constant pi: %.40f" % pi
print "Difference: %.40f" % abs(calc-pi)
print "Time elapsed: %s" % repr(t)

Você pode encontrar mais informações aqui .

De qualquer forma, a maneira mais rápida de obter um valor preciso de pi no python é:

from gmpy import pi
print pi(3000) # the rule is the same as 
               # the precision on the previous code

Aqui está a parte da fonte do método gmpy pi, não acho que o código seja tão útil quanto o comentário neste caso:

static char doc_pi[]="\
pi(n): returns pi with n bits of precision in an mpf object\n\
";

/* This function was originally from netlib, package bmp, by
 * Richard P. Brent. Paulo Cesar Pereira de Andrade converted
 * it to C and used it in his LISP interpreter.
 *
 * Original comments:
 * 
 *   sets mp pi = 3.14159... to the available precision.
 *   uses the gauss-legendre algorithm.
 *   this method requires time o(ln(t)m(t)), so it is slower
 *   than mppi if m(t) = o(t**2), but would be faster for
 *   large t if a faster multiplication algorithm were used
 *   (see comments in mpmul).
 *   for a description of the method, see - multiple-precision
 *   zero-finding and the complexity of elementary function
 *   evaluation (by r. p. brent), in analytic computational
 *   complexity (edited by j. f. traub), academic press, 1976, 151-176.
 *   rounding options not implemented, no guard digits used.
*/
static PyObject *
Pygmpy_pi(PyObject *self, PyObject *args)
{
    PympfObject *pi;
    int precision;
    mpf_t r_i2, r_i3, r_i4;
    mpf_t ix;

    ONE_ARG("pi", "i", &precision);
    if(!(pi = Pympf_new(precision))) {
        return NULL;
    }

    mpf_set_si(pi->f, 1);

    mpf_init(ix);
    mpf_set_ui(ix, 1);

    mpf_init2(r_i2, precision);

    mpf_init2(r_i3, precision);
    mpf_set_d(r_i3, 0.25);

    mpf_init2(r_i4, precision);
    mpf_set_d(r_i4, 0.5);
    mpf_sqrt(r_i4, r_i4);

    for (;;) {
        mpf_set(r_i2, pi->f);
        mpf_add(pi->f, pi->f, r_i4);
        mpf_div_ui(pi->f, pi->f, 2);
        mpf_mul(r_i4, r_i2, r_i4);
        mpf_sub(r_i2, pi->f, r_i2);
        mpf_mul(r_i2, r_i2, r_i2);
        mpf_mul(r_i2, r_i2, ix);
        mpf_sub(r_i3, r_i3, r_i2);
        mpf_sqrt(r_i4, r_i4);
        mpf_mul_ui(ix, ix, 2);
        /* Check for convergence */
        if (!(mpf_cmp_si(r_i2, 0) && 
              mpf_get_prec(r_i2) >= (unsigned)precision)) {
            mpf_mul(pi->f, pi->f, r_i4);
            mpf_div(pi->f, pi->f, r_i3);
            break;
        }
    }

    mpf_clear(ix);
    mpf_clear(r_i2);
    mpf_clear(r_i3);
    mpf_clear(r_i4);

    return (PyObject*)pi;
}

Edição: Eu tive alguns problemas com recortar e colar e recuo, você pode encontrar a fonte aqui .


20

Se por mais rápido você quiser digitar mais rapidamente o código, aqui está a solução golfscript :

;''6666,-2%{2+.2/@*\/10.3??2*+}*`1000<~\;

18

Se você estiver disposto a usar uma aproximação, 355 / 113é bom para 6 dígitos decimais e tem a vantagem adicional de ser utilizável com expressões inteiras. Isso não é tão importante nos dias de hoje, já que o "co-processador matemático de ponto flutuante" deixou de ter algum significado, mas era bastante importante uma vez.


18

Use a fórmula semelhante a Machin

176 * arctan (1/57) + 28 * arctan (1/239) - 48 * arctan (1/682) + 96 * arctan(1/12943) 

[; \left( 176 \arctan \frac{1}{57} + 28 \arctan \frac{1}{239} - 48 \arctan \frac{1}{682} + 96 \arctan \frac{1}{12943}\right) ;], for you TeX the World people.

Implementado no esquema, por exemplo:

(+ (- (+ (* 176 (atan (/ 1 57))) (* 28 (atan (/ 1 239)))) (* 48 (atan (/ 1 682)))) (* 96 (atan (/ 1 12943))))


16

Com duplas:

4.0 * (4.0 * Math.Atan(0.2) - Math.Atan(1.0 / 239.0))

Isso terá precisão de até 14 casas decimais, o suficiente para preencher um dobro (a imprecisão é provavelmente porque o restante dos decimais nas tangentes do arco está truncado).

Também Seth, é 3,14159265358979323846 3 , não 64.


16

Pi é exatamente 3! [Prof. Frink (Simpsons)]

Piada, mas aqui está uma em C # (é necessário o .NET-Framework).

using System;
using System.Text;

class Program {
    static void Main(string[] args) {
        int Digits = 100;

        BigNumber x = new BigNumber(Digits);
        BigNumber y = new BigNumber(Digits);
        x.ArcTan(16, 5);
        y.ArcTan(4, 239);
        x.Subtract(y);
        string pi = x.ToString();
        Console.WriteLine(pi);
    }
}

public class BigNumber {
    private UInt32[] number;
    private int size;
    private int maxDigits;

    public BigNumber(int maxDigits) {
        this.maxDigits = maxDigits;
        this.size = (int)Math.Ceiling((float)maxDigits * 0.104) + 2;
        number = new UInt32[size];
    }
    public BigNumber(int maxDigits, UInt32 intPart)
        : this(maxDigits) {
        number[0] = intPart;
        for (int i = 1; i < size; i++) {
            number[i] = 0;
        }
    }
    private void VerifySameSize(BigNumber value) {
        if (Object.ReferenceEquals(this, value))
            throw new Exception("BigNumbers cannot operate on themselves");
        if (value.size != this.size)
            throw new Exception("BigNumbers must have the same size");
    }

    public void Add(BigNumber value) {
        VerifySameSize(value);

        int index = size - 1;
        while (index >= 0 && value.number[index] == 0)
            index--;

        UInt32 carry = 0;
        while (index >= 0) {
            UInt64 result = (UInt64)number[index] +
                            value.number[index] + carry;
            number[index] = (UInt32)result;
            if (result >= 0x100000000U)
                carry = 1;
            else
                carry = 0;
            index--;
        }
    }
    public void Subtract(BigNumber value) {
        VerifySameSize(value);

        int index = size - 1;
        while (index >= 0 && value.number[index] == 0)
            index--;

        UInt32 borrow = 0;
        while (index >= 0) {
            UInt64 result = 0x100000000U + (UInt64)number[index] -
                            value.number[index] - borrow;
            number[index] = (UInt32)result;
            if (result >= 0x100000000U)
                borrow = 0;
            else
                borrow = 1;
            index--;
        }
    }
    public void Multiply(UInt32 value) {
        int index = size - 1;
        while (index >= 0 && number[index] == 0)
            index--;

        UInt32 carry = 0;
        while (index >= 0) {
            UInt64 result = (UInt64)number[index] * value + carry;
            number[index] = (UInt32)result;
            carry = (UInt32)(result >> 32);
            index--;
        }
    }
    public void Divide(UInt32 value) {
        int index = 0;
        while (index < size && number[index] == 0)
            index++;

        UInt32 carry = 0;
        while (index < size) {
            UInt64 result = number[index] + ((UInt64)carry << 32);
            number[index] = (UInt32)(result / (UInt64)value);
            carry = (UInt32)(result % (UInt64)value);
            index++;
        }
    }
    public void Assign(BigNumber value) {
        VerifySameSize(value);
        for (int i = 0; i < size; i++) {
            number[i] = value.number[i];
        }
    }

    public override string ToString() {
        BigNumber temp = new BigNumber(maxDigits);
        temp.Assign(this);

        StringBuilder sb = new StringBuilder();
        sb.Append(temp.number[0]);
        sb.Append(System.Globalization.CultureInfo.CurrentCulture.NumberFormat.CurrencyDecimalSeparator);

        int digitCount = 0;
        while (digitCount < maxDigits) {
            temp.number[0] = 0;
            temp.Multiply(100000);
            sb.AppendFormat("{0:D5}", temp.number[0]);
            digitCount += 5;
        }

        return sb.ToString();
    }
    public bool IsZero() {
        foreach (UInt32 item in number) {
            if (item != 0)
                return false;
        }
        return true;
    }

    public void ArcTan(UInt32 multiplicand, UInt32 reciprocal) {
        BigNumber X = new BigNumber(maxDigits, multiplicand);
        X.Divide(reciprocal);
        reciprocal *= reciprocal;

        this.Assign(X);

        BigNumber term = new BigNumber(maxDigits);
        UInt32 divisor = 1;
        bool subtractTerm = true;
        while (true) {
            X.Divide(reciprocal);
            term.Assign(X);
            divisor += 2;
            term.Divide(divisor);
            if (term.IsZero())
                break;

            if (subtractTerm)
                this.Subtract(term);
            else
                this.Add(term);
            subtractTerm = !subtractTerm;
        }
    }
}

15

Calcular PI em tempo de compilação com D.

(Copiado de DSource.org )

/** Calculate pi at compile time
 *
 * Compile with dmd -c pi.d
 */
module calcpi;

import meta.math;
import meta.conv;

/** real evaluateSeries!(real x, real metafunction!(real y, int n) term)
 *
 * Evaluate a power series at compile time.
 *
 * Given a metafunction of the form
 *  real term!(real y, int n),
 * which gives the nth term of a convergent series at the point y
 * (where the first term is n==1), and a real number x,
 * this metafunction calculates the infinite sum at the point x
 * by adding terms until the sum doesn't change any more.
 */
template evaluateSeries(real x, alias term, int n=1, real sumsofar=0.0)
{
  static if (n>1 && sumsofar == sumsofar + term!(x, n+1)) {
     const real evaluateSeries = sumsofar;
  } else {
     const real evaluateSeries = evaluateSeries!(x, term, n+1, sumsofar + term!(x, n));
  }
}

/*** Calculate atan(x) at compile time.
 *
 * Uses the Maclaurin formula
 *  atan(z) = z - z^3/3 + Z^5/5 - Z^7/7 + ...
 */
template atan(real z)
{
    const real atan = evaluateSeries!(z, atanTerm);
}

template atanTerm(real x, int n)
{
    const real atanTerm =  (n & 1 ? 1 : -1) * pow!(x, 2*n-1)/(2*n-1);
}

/// Machin's formula for pi
/// pi/4 = 4 atan(1/5) - atan(1/239).
pragma(msg, "PI = " ~ fcvt!(4.0 * (4*atan!(1/5.0) - atan!(1/239.0))) );

2
Infelizmente, tangentes são arco-tangentes são baseados em pi, invalidando um pouco esse cálculo.
Grant Johnson

14

Esta versão (em Delphi) não é nada de especial, mas é pelo menos mais rápida que a versão que Nick Hodge postou em seu blog :). Na minha máquina, são necessários 16 segundos para fazer um bilhão de iterações, fornecendo um valor de 3,14159265 25879 (a parte exata está em negrito).

program calcpi;

{$APPTYPE CONSOLE}

uses
  SysUtils;

var
  start, finish: TDateTime;

function CalculatePi(iterations: integer): double;
var
  numerator, denominator, i: integer;
  sum: double;
begin
  {
  PI may be approximated with this formula:
  4 * (1 - 1/3 + 1/5 - 1/7 + 1/9 - 1/11 .......)
  //}
  numerator := 1;
  denominator := 1;
  sum := 0;
  for i := 1 to iterations do begin
    sum := sum + (numerator/denominator);
    denominator := denominator + 2;
    numerator := -numerator;
  end;
  Result := 4 * sum;
end;

begin
  try
    start := Now;
    WriteLn(FloatToStr(CalculatePi(StrToInt(ParamStr(1)))));
    finish := Now;
    WriteLn('Seconds:' + FormatDateTime('hh:mm:ss.zz',finish-start));
  except
    on E:Exception do
      Writeln(E.Classname, ': ', E.Message);
  end;
end.

13

Antigamente, com tamanhos pequenos de palavras e operações de ponto flutuante lentas ou inexistentes, costumávamos fazer coisas como estas:

/* Return approximation of n * PI; n is integer */
#define pi_times(n) (((n) * 22) / 7)

Para aplicativos que não exigem muita precisão (videogames, por exemplo), isso é muito rápido e preciso o suficiente.


11
Para mais precisão, use 355 / 113. Muito preciso para o tamanho dos números envolvidos.
18730 David Thornley

Só por curiosidade: 22/7 é3 + 1/7
Agnius Vasiliauskas

13

Se você deseja calcular uma aproximação do valor de π (por algum motivo), tente um algoritmo de extração binária. A melhoria de BBP por Bellard fornece PI em O (N ^ 2).


Se você deseja obter uma aproximação do valor de π para fazer cálculos, então:

PI = 3.141592654

Concedido, isso é apenas uma aproximação e não é totalmente preciso. Está desativado em pouco mais de 0,00000000004102. (quatro dez trilionésimos, cerca de 4 / 10.000.000.000 ).


Se você deseja fazer contas com π, compre um pacote de lápis e papel ou álgebra computacional e use o valor exato de π, π.

Se você realmente quer uma fórmula, esta é divertida:

π = - i ln (-1)


Sua fórmula depende de como você define ln no plano complexo. Ela deve ser não contígua ao longo de uma linha no plano complexo, e é bastante comum que essa linha seja o eixo real negativo.
22610 erikkallen

12

O método de Brent publicado acima por Chris é muito bom; Brent geralmente é um gigante no campo da aritmética de precisão arbitrária.

Se tudo o que você deseja é o enésimo dígito, a famosa fórmula BBP é útil em hexadecimal


1
O método Brent não foi publicado por mim; foi postada por Andrea e eu acabei de ser a última pessoa que editou a postagem. :-) Mas eu concordo, esse post merece um voto positivo.
22330 Chris Jester-Young

1

Calculando π da área do círculo :-)

<input id="range" type="range" min="10" max="960" value="10" step="50" oninput="calcPi()">
<br>
<div id="cont"></div>

<script>
function generateCircle(width) {
    var c = width/2;
    var delta = 1.0;
    var str = "";
    var xCount = 0;
    for (var x=0; x <= width; x++) {
        for (var y = 0; y <= width; y++) {
            var d = Math.sqrt((x-c)*(x-c) + (y-c)*(y-c));
            if (d > (width-1)/2) {
                str += '.';
            }
            else {
                xCount++;
                str += 'o';
            }
            str += "&nbsp;" 
        }
        str += "\n";
    }
    var pi = (xCount * 4) / (width * width);
    return [str, pi];
}

function calcPi() {
    var e = document.getElementById("cont");
    var width = document.getElementById("range").value;
    e.innerHTML = "<h4>Generating circle...</h4>";
    setTimeout(function() {
        var circ = generateCircle(width);
        e.innerHTML  = "<pre>" + "π = " + circ[1].toFixed(2) + "\n" + circ[0] +"</pre>";
    }, 200);
}
calcPi();
</script>


0

O algoritmo de Chudnovsky é bastante rápido se você não se importa em executar uma raiz quadrada e algumas inversas. Ele converge para a precisão dupla em apenas 2 iterações.

/*
    Chudnovsky algorithm for computing PI
*/

#include <iostream>
#include <cmath>
using namespace std;

double calc_PI(int K=2) {

    static const int A = 545140134;
    static const int B = 13591409;
    static const int D = 640320;

    const double ID3 = 1./ (double(D)*double(D)*double(D));

    double sum = 0.;
    double b   = sqrt(ID3);
    long long int p = 1;
    long long int a = B;

    sum += double(p) * double(a)* b;

    // 2 iterations enough for double convergence
    for (int k=1; k<K; ++k) {
        // A*k + B
        a += A;
        // update denominator
        b *= ID3;
        // p = (-1)^k 6k! / 3k! k!^3
        p *= (6*k)*(6*k-1)*(6*k-2)*(6*k-3)*(6*k-4)*(6*k-5);
        p /= (3*k)*(3*k-1)*(3*k-2) * k*k*k;
        p = -p;

        sum += double(p) * double(a)* b;
    }

    return 1./(12*sum);
}

int main() {

    cout.precision(16);
    cout.setf(ios::fixed);

    for (int k=1; k<=5; ++k) cout << "k = " << k << "   PI = " << calc_PI(k) << endl;

    return 0;
}

Resultados:

k = 1   PI = 3.1415926535897341
k = 2   PI = 3.1415926535897931
k = 3   PI = 3.1415926535897931
k = 4   PI = 3.1415926535897931
k = 5   PI = 3.1415926535897931

0

Melhor Abordagem

Para obter a saída de constantes padrão como pi ou os conceitos padrão, devemos primeiro ir com os métodos internos disponíveis no idioma que você está usando. Ele retornará um valor da maneira mais rápida e melhor. Eu estou usando python para executar a maneira mais rápida de obter o valor de pi.

  • variável pi da biblioteca de matemática . A biblioteca de matemática armazena a variável pi como uma constante.

math_pi.py

import math
print math.pi

Execute o script com o utilitário de tempo do linux /usr/bin/time -v python math_pi.py

Resultado:

Command being timed: "python math_pi.py"
User time (seconds): 0.01
System time (seconds): 0.01
Percent of CPU this job got: 91%
Elapsed (wall clock) time (h:mm:ss or m:ss): 0:00.03
  • Use o método de matemática cos cos

acos_pi.py

import math
print math.acos(-1)

Execute o script com o utilitário de tempo do linux /usr/bin/time -v python acos_pi.py

Resultado:

Command being timed: "python acos_pi.py"
User time (seconds): 0.02
System time (seconds): 0.01
Percent of CPU this job got: 94%
Elapsed (wall clock) time (h:mm:ss or m:ss): 0:00.03

bbp_pi.py

from decimal import Decimal, getcontext
getcontext().prec=100
print sum(1/Decimal(16)**k * 
          (Decimal(4)/(8*k+1) - 
           Decimal(2)/(8*k+4) - 
           Decimal(1)/(8*k+5) -
           Decimal(1)/(8*k+6)) for k in range(100))

Execute o script com o utilitário de tempo do linux /usr/bin/time -v python bbp_pi.py

Resultado:

Command being timed: "python c.py"
User time (seconds): 0.05
System time (seconds): 0.01
Percent of CPU this job got: 98%
Elapsed (wall clock) time (h:mm:ss or m:ss): 0:00.06

Portanto, a melhor maneira é usar os métodos internos fornecidos pelo idioma, pois eles são os mais rápidos e os melhores para obter a saída. Em python, use math.pi

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.