¿Cuál es la forma más rápida de obtener el valor de π?


322

Estoy buscando la forma más rápida de obtener el valor de π, como un desafío personal. Más específicamente, estoy usando formas que no implican el uso de #defineconstantes como M_PI, o codificar el número en.

El siguiente programa prueba las diversas formas que conozco. La versión de ensamblaje en línea es, en teoría, la opción más rápida, aunque claramente no es portátil. Lo he incluido como referencia para compararlo con las otras versiones. En mis pruebas, con funciones integradas, la 4 * atan(1)versión es más rápida en GCC 4.2, porque se pliega automáticamente atan(1)en una constante. Con -fno-builtinespecificado, la atan2(0, -1)versión es más rápida.

Aquí está el programa de prueba principal ( 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;
}

Y el material de ensamblaje en línea ( fldpi.c) que solo funcionará para sistemas x86 y x64:

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

Y un script de compilación que compila todas las configuraciones que estoy probando ( 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

Además de probar entre varios indicadores del compilador (también he comparado 32 bits contra 64 bits porque las optimizaciones son diferentes), también he intentado cambiar el orden de las pruebas. Pero aún así, la atan2(0, -1)versión sigue apareciendo en todo momento.


38
Tiene que haber una manera de hacerlo en la metaprogramación de C ++. El tiempo de ejecución será realmente bueno, pero el tiempo de compilación no lo será.
David Thornley

1
¿Por qué considera usar atan (1) diferente de usar M_PI? Entiendo por qué quieres hacer esto si solo usaste operaciones aritméticas, pero con atan no veo el punto.
erikkallen

99
la pregunta es: ¿por qué no querrías usar una constante? por ejemplo, ¿definido por una biblioteca o usted mismo? Computar Pi es un desperdicio de ciclos de CPU, ya que este problema se ha resuelto una y otra y otra vez a una cantidad de dígitos significativos mucho mayor de lo necesario para los cálculos diarios
Tilo

2
@ HopelessN00b En el dialecto del inglés que hablo, "optimización" se escribe con una "s", no una "z" (que se pronuncia como "zed", por cierto, no "zee" ;-)). (Esta no es la primera vez que tengo que revertir este tipo de edición, también, si nos fijamos en el historial de revisión.)
Chris Jester-Young

Respuestas:


205

El método de Monte Carlo , como se mencionó, aplica algunos conceptos geniales, pero claramente no es el más rápido, ni por asomo, ni por ninguna medida razonable. Además, todo depende del tipo de precisión que esté buscando. El π más rápido que conozco es el que tiene los dígitos codificados. Mirando Pi y Pi [PDF] , hay muchas fórmulas.

Aquí hay un método que converge rápidamente: aproximadamente 14 dígitos por iteración. PiFast , la aplicación más rápida actual, utiliza esta fórmula con la FFT. Simplemente escribiré la fórmula, ya que el código es sencillo. Esta fórmula fue casi encontrada por Ramanujan y descubierta por Chudnovsky . En realidad es cómo calculó varios miles de millones de dígitos del número, por lo que no es un método para ignorarlo. La fórmula se desbordará rápidamente y, dado que estamos dividiendo factoriales, sería ventajoso retrasar dichos cálculos para eliminar los términos.

ingrese la descripción de la imagen aquí

ingrese la descripción de la imagen aquí

dónde,

ingrese la descripción de la imagen aquí

A continuación se muestra el algoritmo Brent-Salamin . Wikipedia menciona que cuando un y b son "lo suficientemente cerca", entonces (a + b) ² / 4t habrá una aproximación de π. No estoy seguro de lo que significa "lo suficientemente cerca", pero de mis pruebas, una iteración obtuvo 2 dígitos, dos obtuvieron 7 y tres tuvieron 15, por supuesto, esto es con dobles, por lo que podría tener un error basado en su representación y El verdadero cálculo podría ser más 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 último, ¿qué tal un poco de golf 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
Suponiendo que intente implementar el primero usted mismo, ¿no sería un problema sqr (k3)? Estoy bastante seguro de que terminaría siendo un número irracional que tendrá que estimar (IIRC, todas las raíces que no son números enteros son irracionales). Todo lo demás parece bastante sencillo si está utilizando aritmética de precisión infinita, pero esa raíz cuadrada es un factor decisivo. El segundo también incluye un sqrt.
Bill K

2
en mi experiencia, "lo suficientemente cerca" generalmente significa que hay una aproximación de la serie taylor involucrada.
Stephen

117

Realmente me gusta este programa, porque se aproxima a π mirando su propia á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
Si reemplaza _ con -F <00 || --F-OO-- debería ser más fácil de seguir :-)
Pat

1
o, si reemplaza _ con "if (el carácter anterior es '-') {OO--;} F--;"
FryGuy

66
imprime 0.25 aquí -.-
Johannes Schaub - litb

8
Este programa fue excelente en 1998, pero se rompió porque los preprocesadores modernos son más liberales al insertar espacios alrededor de las expansiones macro para evitar que cosas como esta funcionen. Es una reliquia, por desgracia.
Chris Lutz

38
Pase --traditional-cppa cpp para obtener el comportamiento deseado.
Nietzche-jou

78

Aquí hay una descripción general de una técnica para calcular pi que aprendí en la escuela secundaria.

Solo comparto esto porque creo que es lo suficientemente simple como para que cualquiera pueda recordarlo, indefinidamente, además le enseña el concepto de los métodos de "Montecarlo", que son métodos estadísticos para llegar a respuestas que no parecen ser inmediatamente deducible a través de procesos aleatorios.

Dibuje un cuadrado e inscriba un cuadrante (un cuarto de semicírculo) dentro de ese cuadrado (un cuadrante con radio igual al lado del cuadrado, de modo que llene la mayor cantidad posible del cuadrado)

Ahora lanza un dardo al cuadrado y registra dónde aterriza, es decir, elige un punto aleatorio en cualquier lugar dentro del cuadrado. Por supuesto, aterrizó dentro del cuadrado, pero ¿está dentro del semicírculo? Registra este hecho.

Repita este proceso muchas veces, y encontrará que hay una relación entre el número de puntos dentro del semicírculo y el número total arrojado, llame a esta relación x.

Dado que el área del cuadrado es r veces r, puede deducir que el área del semicírculo es x veces r veces r (es decir, x veces r al cuadrado). Por lo tanto, x veces 4 te dará pi.

Este no es un método rápido de usar. Pero es un buen ejemplo de un método de Monte Carlo. Y si miras a tu alrededor, puedes encontrar que muchos problemas que de otra forma estarían fuera de tus habilidades computacionales pueden resolverse mediante tales métodos.


2
Este es el método que utilizamos para calcular Pi en un proyecto de Java en la escuela. Solo usé un aleatorizador para obtener las coordenadas x, y y cuantos más 'dardos' lanzáramos, más cerca de Pi llegamos.
Jeff Keslinke

55

En aras de la exhaustividad, una versión de plantilla de C ++, que, para una compilación optimizada, calculará una aproximación de PI en tiempo de compilación, y se alineará en un solo 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;
}

Tenga en cuenta que para I> 10, las compilaciones optimizadas pueden ser lentas, del mismo modo para ejecuciones no optimizadas. Durante 12 iteraciones, creo que hay alrededor de 80k llamadas a value () (en ausencia de memorización).



55
Bueno, eso es exacto para 9dp's. ¿Se opone a algo o simplemente hace una observación?
jon-hanson

¿Cuál es el nombre del algoritmo utilizado aquí para calcular PI?
Sebastião Miranda

1
La fórmula de @sebastião-miranda Leibniz , con una aceleración promedio, mejora la convergencia. pi_calc<0, J>calcula cada término sucesivo de la fórmula y el no especializado pi_calc<I, J>calcula el promedio.
jon-hanson

43

En realidad, hay un libro completo dedicado (entre otras cosas) a métodos rápidos para el cálculo de \ pi: 'Pi and the AGM', de Jonathan y Peter Borwein ( disponible en Amazon ).

Estudié bastante la AGM y los algoritmos relacionados: es bastante interesante (aunque a veces no trivial).

Tenga en cuenta que para implementar los algoritmos más modernos para calcular \ pi, necesitará una biblioteca aritmética de precisión múltiple ( GMP) múltiple es una buena opción, aunque ha pasado un tiempo desde la última vez que la usé).

La complejidad temporal de los mejores algoritmos está en O (M (n) log (n)), donde M (n) es la complejidad temporal para la multiplicación de dos enteros de n bits (M (n) = O (n log (n) log (log (n))) usando algoritmos basados ​​en FFT, que generalmente son necesarios cuando se calculan dígitos de \ pi, y dicho algoritmo se implementa en GMP).

Tenga en cuenta que aunque las matemáticas detrás de los algoritmos pueden no ser triviales, los algoritmos en sí mismos generalmente son unas pocas líneas de pseudocódigo, y su implementación generalmente es muy sencilla (si elige no escribir su propia aritmética de multiprecisión :-)).


42

Lo siguiente responde con precisión cómo hacer esto de la manera más rápida posible, con el menor esfuerzo informático . Incluso si no le gusta la respuesta, debe admitir que es la forma más rápida de obtener el valor de PI.

La forma MÁS RÁPIDA de obtener el valor de Pi es:

1) elija su lenguaje de programación favorito 2) cargue su biblioteca matemática 3) y descubra que Pi ya está definido allí, ¡listo para usar!

En caso de que no tenga una biblioteca matemática a mano ...

La SEGUNDA MANERA MÁS RÁPIDA (solución más universal) es:

busque Pi en Internet, por ejemplo aquí:

http://www.eveandersson.com/pi/digits/1000000 (1 millón de dígitos ... ¿cuál es su precisión de coma flotante?)

o aquí:

http://3.141592653589793238462643383279502884197169399375105820974944592.com/

o aquí:

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

Es realmente rápido encontrar los dígitos que necesita para cualquier aritmética de precisión que le gustaría usar, y al definir una constante, puede asegurarse de no perder el valioso tiempo de la CPU.

No solo es una respuesta en parte humorística, sino que, en realidad, si alguien calculara el valor de Pi en una aplicación real ... sería una gran pérdida de tiempo de CPU, ¿no? Al menos no veo una aplicación real para intentar volver a calcular esto.

Estimado moderador: tenga en cuenta que el OP preguntó: "La forma más rápida de obtener el valor de PI"


Estimado Tilo: tenga en cuenta que el OP dijo: "Estoy buscando la forma más rápida de obtener el valor de π, como un desafío personal. Más específicamente, estoy usando formas que no implican el uso de #definir constantes como M_PI , o codificar el número .
Max

Estimado @Max: tenga en cuenta que el OP editó su pregunta original después de que la respondí, eso no es mi culpa;) Mi solución sigue siendo la forma más rápida y resuelve el problema con cualquier precisión de punto flotante deseada y sin ciclos de CPU con elegancia :)
Tilo

Oh lo siento, no me di cuenta. Solo un pensamiento, ¿las constantes codificadas no tendrían menos precisión que calcular pi? Supongo que depende de qué idioma es y qué tan dispuesto está el creador a poner todos los dígitos :-)
Max

1
Maldición, olvidé agregar Dear Tilo
Max

27

La fórmula BBP le permite calcular el enésimo dígito, en la base 2 (o 16), sin tener que molestarse incluso con los dígitos n-1 anteriores primero :)


23

En lugar de definir pi como una constante, siempre uso acos(-1).


2
cos (-1) o acos (-1)? :-P Ese (este último) es uno de los casos de prueba en mi código original. Es uno de mis preferidos (junto con atan2 (0, -1), que realmente es lo mismo que acos (-1), excepto que acos generalmente se implementa en términos de atan2), pero algunos compiladores optimizan para 4 * atan (1) !
Chris Jester-Young

21

Este es un método "clásico", muy fácil de implementar. Esta implementación en python (no el lenguaje más rápido) lo hace:

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)

Puedes encontrar más información aquí .

De todos modos, la forma más rápida de obtener un valor preciso de pi en python es:

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

Aquí está la fuente del método gmpy pi, no creo que el código sea tan útil como el comentario en este 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;
}

EDITAR: Tuve algunos problemas con cortar y pegar y la sangría, puedes encontrar la fuente aquí .


20

Si por rápido quiere decir más rápido escribir el código, aquí está la solución de golfscript :

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

18

Si está dispuesto a usar una aproximación, 355 / 113es bueno para 6 dígitos decimales y tiene la ventaja adicional de ser utilizable con expresiones enteras. Eso no es tan importante en estos días, ya que el "coprocesador matemático de coma flotante" dejó de tener sentido, pero una vez fue bastante importante.


18

Usa la fórmula tipo 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 en Scheme, por ejemplo:

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


16

Con dobles:

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

Esto será exacto hasta 14 decimales, suficiente para llenar un doble (la imprecisión probablemente se deba a que el resto de los decimales en las tangentes del arco están truncados).

También Seth, es 3.14159265358979323846 3 , no 64.


16

¡Pi es exactamente 3! [Profe. Frink (Simpsons)]

Broma, pero aquí hay una en C # (se requiere .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

Calcule PI en tiempo de compilación con 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
Desafortunadamente, las tangentes son arcotangentes se basan en pi, lo que invalida un poco este cálculo.
Grant Johnson

14

Esta versión (en Delphi) no es nada especial, pero es al menos más rápida que la versión que Nick Hodge publicó en su blog :). En mi máquina, toma aproximadamente 16 segundos hacer mil millones de iteraciones, dando un valor de 3.14159265 25879 (la parte precisa está en negrita).

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

En los viejos tiempos, con tamaños de palabra pequeños y operaciones de punto flotante lentas o inexistentes, solíamos hacer cosas como esta:

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

Para aplicaciones que no requieren mucha precisión (videojuegos, por ejemplo), esto es muy rápido y lo suficientemente preciso.


11
Para mayor precisión de uso 355 / 113. Muy preciso para el tamaño de los números involucrados.
David Thornley

Sólo por curiosidad: 22/7 es3 + 1/7
Agnius Vasiliauskas

13

Si desea calcular una aproximación del valor de π (por alguna razón), debe probar un algoritmo de extracción binario. La mejora de Bellard de BBP da PI en O (N ^ 2).


Si desea obtener una aproximación del valor de π para hacer cálculos, entonces:

PI = 3.141592654

Por supuesto, eso es solo una aproximación, y no del todo exacto. Está apagado por un poco más de 0.00000000004102. (cuatro diez billonésimas, alrededor de 4 / 10,000,000,000 ).


Si quieres hacer matemáticas con π, entonces obtén un lápiz y papel o un paquete de álgebra de computadora, y usa el valor exacto de π, π.

Si realmente quieres una fórmula, esta es divertida:

π = - i ln (-1)


Su fórmula depende de cómo defina ln en el plano complejo. Tiene que ser no contiguo a lo largo de una línea en el plano complejo, y es bastante común que esa línea sea el eje real negativo.
erikkallen

12

El método de Brent publicado anteriormente por Chris es muy bueno; Brent generalmente es un gigante en el campo de la aritmética de precisión arbitraria.

Si todo lo que quieres es el enésimo dígito, la famosa fórmula BBP es útil en hexadecimal


1
El método Brent no fue publicado por mí; Fue publicado por Andrea, y resultó ser la última persona que editó la publicación. :-) Pero estoy de acuerdo, esa publicación merece un voto positivo.
Chris Jester-Young

1

Cálculo de π a partir del área del 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

El algoritmo de Chudnovsky es bastante rápido si no te importa realizar una raíz cuadrada y un par de inversas. Converge a doble precisión en solo 2 iteraciones.

/*
    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

Enfoque mejor

Para obtener la salida de constantes estándar como pi o los conceptos estándar, primero debemos ir con los métodos incorporados disponibles en el lenguaje que está utilizando. Devolverá un valor de la manera más rápida y mejor. Estoy usando python para ejecutar la forma más rápida de obtener el valor de pi.

  • Pi variable de la biblioteca matemática . La biblioteca matemática almacena la variable pi como una constante.

math_pi.py

import math
print math.pi

Ejecute el script con la utilidad de tiempo de Linux /usr/bin/time -v python math_pi.py

Salida:

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
  • Utilice el método de arco cos de matemáticas

acos_pi.py

import math
print math.acos(-1)

Ejecute el script con la utilidad de tiempo de Linux /usr/bin/time -v python acos_pi.py

Salida:

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

Ejecute el script con la utilidad de tiempo de Linux /usr/bin/time -v python bbp_pi.py

Salida:

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

Entonces, la mejor manera es usar métodos incorporados proporcionados por el lenguaje porque son los más rápidos y mejores para obtener la salida. En python use math.pi

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.