Qual è il modo più veloce per ottenere il valore di π?


322

Sto cercando il modo più veloce per ottenere il valore di π, come una sfida personale. Più specificamente, sto usando modi che non implicano l'uso di #definecostanti simili M_PIo la codifica forzata del numero.

Il seguente programma mette alla prova i vari modi in cui sono a conoscenza. La versione di assemblaggio in linea è, in teoria, l'opzione più veloce, sebbene chiaramente non portatile. L'ho incluso come base per il confronto con le altre versioni. Nei miei test, con i built-in, la 4 * atan(1)versione è più veloce su GCC 4.2, perché piega automaticamente atan(1)in una costante. Con -fno-builtinspecificato, la atan2(0, -1)versione è più veloce.

Ecco il principale programma di test ( 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 gli elementi inline assembly ( fldpi.c) che funzioneranno solo per i sistemi x86 e x64:

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

E uno script di compilazione che crea tutte le configurazioni che sto 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

Oltre al test tra vari flag di compilatore (ho confrontato anche 32 bit contro 64 bit perché le ottimizzazioni sono diverse), ho anche provato a cambiare l'ordine dei test. Ma comunque, la atan2(0, -1)versione esce sempre in cima ogni volta.


38
Dev'esserci un modo per farlo nella metaprogrammazione C ++. Il tempo di esecuzione sarà davvero buono, ma il tempo di compilazione non lo sarà.
David Thornley,

1
Perché consideri l'utilizzo di atan (1) diverso dall'uso di M_PI? Comprenderei il motivo per cui vuoi farlo se utilizzassi solo operazioni aritmetiche, ma con atan non vedo il punto.
erikkallen,

9
la domanda è: perché non vorresti usare una costante? ad esempio, definito da una biblioteca o da solo? Il calcolo del Pi è uno spreco di cicli della CPU, poiché questo problema è stato risolto più e più volte ancora per un numero di cifre significative molto più grandi del necessario per i calcoli quotidiani
Tilo,

2
@ HopelessN00b Nel dialetto inglese parlo, "ottimizzazione" è scritta con una "s", non una "z" (che è pronunciata come "zed", BTW, non "zee" ;-)). (Questa non è la prima volta che devo ripristinare anche questo tipo di modifica, se guardi la cronologia delle recensioni.)
Chris Jester-Young,

Risposte:


205

Il metodo Monte Carlo , come detto, applica alcuni grandi concetti, ma è, chiaramente, non il più veloce, non da un colpo lungo, non da alcuna misura ragionevole. Inoltre, tutto dipende dal tipo di precisione che stai cercando. Il più veloce π che conosco è quello con le cifre hard coded. Guardando Pi e Pi [PDF] , ci sono molte formule.

Ecco un metodo che converge rapidamente - circa 14 cifre per iterazione. PiFast , l'attuale applicazione più veloce, utilizza questa formula con FFT. Scriverò solo la formula, poiché il codice è semplice. Questa formula fu quasi trovata da Ramanujan e scoperta da Chudnovsky . In realtà è come ha calcolato diversi miliardi di cifre del numero, quindi non è un metodo da ignorare. La formula traboccerà rapidamente e, poiché stiamo dividendo i fattoriali, sarebbe vantaggioso ritardare tali calcoli per rimuovere i termini.

inserisci qui la descrizione dell'immagine

inserisci qui la descrizione dell'immagine

dove,

inserisci qui la descrizione dell'immagine

Di seguito è riportato l' algoritmo Brent-Salamin . Wikipedia afferma che quando un e b sono "abbastanza vicino", quindi (a + b) ² / 4t sarà un'approssimazione di π. Non sono sicuro di cosa significhi "abbastanza vicino", ma dai miei test, un'iterazione ha ottenuto 2 cifre, due hanno 7 e tre hanno 15, ovviamente questo è con i doppi, quindi potrebbe avere un errore basato sulla sua rappresentazione e il vero calcolo potrebbe essere più accurato.

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)

Infine, che ne dici di un po 'di golf (800 cifre)? 160 personaggi!

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
Supponendo che tu stia cercando di implementare tu stesso il primo, sqr (k3) non sarebbe un problema? Sono abbastanza sicuro che finirebbe con un numero irrazionale che dovrai stimare (IIRC, tutte le radici che non sono numeri interi sono irrazionali). Tutto il resto sembra piuttosto semplice se stai usando un'aritmetica di precisione infinita, ma quella radice quadrata è un rompicapo. Il secondo include anche un sqrt.
Bill K,

2
nella mia esperienza, "abbastanza vicino" di solito significa che c'è un'approssimazione della serie di Taylor.
Stephen,

117

Mi piace molto questo programma, perché si avvicina a π osservando la propria area.

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 si sostituisce _ con -F <00 || --F-OO-- dovrebbe essere più facile seguire :-)
Pat

1
oppure, se si sostituisce _ con "if (il carattere precedente è '-') {OO--;} F--;"
FryGuy,

6
stampa 0,25 qui -.-
Johannes Schaub - litb

8
Questo programma è stato eccezionale nel 1998, ma è stato interrotto perché i moderni preprocessori sono più liberali con l'inserimento di spazi attorno alle macro espansioni per impedire che cose come questa funzionino. Purtroppo è una reliquia.
Chris Lutz,

38
Passa --traditional-cppa cpp per ottenere il comportamento previsto.
Nietzche-jou,

78

Ecco una descrizione generale di una tecnica per il calcolo di pi che ho imparato al liceo.

Condivido questo solo perché penso che sia abbastanza semplice che chiunque possa ricordarlo, a tempo indeterminato, inoltre ti insegna il concetto di metodi "Monte-Carlo" - che sono metodi statistici per arrivare a risposte che non sembrano immediatamente essere deducibile attraverso processi casuali.

Disegna un quadrato e scrivi un quadrante (un quarto di un semicerchio) all'interno di quel quadrato (un quadrante con raggio uguale al lato del quadrato, in modo da riempire il più possibile il quadrato)

Ora lancia un dardo nel quadrato e registra dove atterra, ovvero scegli un punto casuale in qualsiasi punto all'interno del quadrato. Certo, è atterrato all'interno della piazza, ma è all'interno del semicerchio? Registra questo fatto.

Ripeti questo processo più volte e scoprirai che esiste un rapporto tra il numero di punti all'interno del semicerchio e il numero totale lanciato, chiama questo rapporto x.

Poiché l'area del quadrato è r volte r, si può dedurre che l'area del semicerchio è x volte r volte r (cioè x volte r al quadrato). Quindi x volte 4 ti darà pi.

Questo non è un metodo rapido da usare. Ma è un bell'esempio di metodo Monte Carlo. E se ti guardi intorno, potresti scoprire che molti problemi, al di fuori delle tue capacità computazionali, possono essere risolti con tali metodi.


2
Questo è il metodo che abbiamo usato per calcolare Pi in un progetto Java a scuola. Ho appena usato un randomizzatore per trovare le coordinate x, y e più "freccette" abbiamo lanciato più vicino a Pi siamo venuti.
Jeff Keslinke,

55

A fini di completezza, una versione del modello C ++, che, per una build ottimizzata, calcolerà un'approssimazione di PI al momento della compilazione e si adatterà a un singolo valore.

#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 per I> 10, le build ottimizzate possono essere lente, allo stesso modo per le esecuzioni non ottimizzate. Per 12 iterazioni credo che ci siano circa 80k chiamate a value () (in assenza di memoria).


Corro questo e ottengo "pi ~ 3.14159265383"
maxwellb

5
Bene, questo è preciso ai 9dp. Stai obiettando a qualcosa o stai semplicemente facendo un'osservazione?
jon-hanson,

qual è il nome dell'algoritmo usato qui per calcolare PI?
Sebastião Miranda,

1
La formula di @ sebastião-miranda Leibniz , con un'accelerazione media, migliora la convergenza. pi_calc<0, J>calcola ogni termine successivo dalla formula e il non specializzato pi_calc<I, J>calcola la media.
jon-hanson,

43

In realtà c'è un intero libro dedicato (tra le altre cose) ai metodi veloci per il calcolo di \ pi: 'Pi and the AGM', di Jonathan e Peter Borwein ( disponibile su Amazon ).

Ho studiato un po 'l'AGM e gli algoritmi correlati: è piuttosto interessante (anche se a volte non banale).

Nota che per implementare la maggior parte degli algoritmi moderni per calcolare \ pi, avrai bisogno di una libreria aritmetica multiprecisione ( GMP è una buona scelta, anche se è passato un po 'di tempo dall'ultima volta che l'ho usato).

La complessità temporale dei migliori algoritmi è in O (M (n) log (n)), dove M (n) è la complessità temporale per la moltiplicazione di due numeri interi n-bit (M (n) = O (n log (n) log (log (n))) usando algoritmi basati su FFT, che di solito sono necessari quando si calcolano le cifre di \ pi, e tale algoritmo è implementato in GMP).

Nota che anche se la matematica dietro gli algoritmi potrebbe non essere banale, gli algoritmi stessi sono in genere poche righe di pseudo-codice e la loro implementazione è di solito molto semplice (se scegli di non scrivere la tua aritmetica multiprecisione :-)).


42

Le seguenti risposte spiegano esattamente come farlo nel modo più rapido possibile, con il minimo sforzo di elaborazione . Anche se non ti piace la risposta, devi ammettere che è davvero il modo più veloce per ottenere il valore di PI.

Il modo più veloce per ottenere il valore di Pi è:

1) scegli il tuo linguaggio di programmazione preferito 2) carica la sua libreria matematica 3) e scopri che Pi è già definito lì - pronto per l'uso!

Nel caso in cui non hai una libreria matematica a portata di mano ..

Il secondo modo più veloce (soluzione più universale) è:

cercare Pi su Internet, ad esempio qui:

http://www.eveandersson.com/pi/digits/1000000 (1 milione di cifre .. qual è la tua precisione in virgola mobile?)

o qui:

http://3.141592653589793238462643383279502884197169399375105820974944592.com/

o qui:

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

È davvero veloce trovare le cifre che ti servono per qualsiasi aritmetica di precisione che desideri utilizzare e, definendo una costante, puoi assicurarti di non perdere tempo prezioso della CPU.

Non solo questa è una risposta in parte divertente, ma in realtà, se qualcuno andasse avanti e calcolasse il valore di Pi in una vera applicazione ... sarebbe una perdita abbastanza grande di tempo della CPU, no? Almeno non vedo una vera applicazione per provare a ricalcolare questo.

Caro moderatore: ti preghiamo di notare che l'OP ha chiesto: "Il modo più veloce per ottenere il valore di PI"


Caro Tilo: tieni presente che l'OP ha detto: "Sto cercando il modo più veloce per ottenere il valore di π, come una sfida personale. Più specificamente, sto usando modi che non implicano l'uso di costanti #define come M_PI o inserire il numero nel codice .
Max

Caro @Max: ti preghiamo di notare che l'OP ha modificato la sua domanda originale dopo che ho risposto - non è certo colpa mia;) La mia soluzione è ancora il modo più veloce e risolve il problema con qualsiasi precisione in virgola mobile desiderata e senza cicli della CPU elegantemente :)
Tilo,

Oh scusa, non me ne sono reso conto. Solo un pensiero, le costanti codificate non avrebbero meno precisione del calcolo di pi? Immagino che dipenda dalla lingua e dalla volontà del creatore di inserire tutte le cifre :-)
Max

1
Accidenti ho dimenticato di aggiungere Dear Tilo
Max

27

La formula BBP ti consente di calcolare l'ennesima cifra - in base 2 (o 16) - senza dover prima preoccuparti nemmeno delle precedenti cifre n-1 :)


23

Invece di definire pi come costante, uso sempre acos(-1).


2
cos (-1) o acos (-1)? :-P Quello (quest'ultimo) è uno dei casi di test nel mio codice originale. È tra i miei preferiti (insieme ad atan2 (0, -1), che è davvero lo stesso di acos (-1), tranne che acos è di solito implementato in termini di atan2), ma alcuni compilatori ottimizzano per 4 * atan (1) !
Chris Jester-Young,

21

Questo è un metodo "classico", molto facile da implementare. Questa implementazione in python (non il linguaggio più veloce) lo fa:

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)

Puoi trovare maggiori informazioni qui .

Comunque, il modo più veloce per ottenere un valore preciso di pi in python è:

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

Ecco il pezzo di sorgente per il metodo gmpy pi, non penso che il codice sia utile come il commento in questo 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;
}

EDIT: ho avuto alcuni problemi con taglia e incolla e rientro, puoi trovare la fonte qui .


20

Se per più veloce intendi più veloce per digitare il codice, ecco la soluzione golfscript :

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

18

Se sei disposto a usare un'approssimazione, 355 / 113è buono per 6 cifre decimali e ha l'ulteriore vantaggio di essere utilizzabile con le espressioni intere. Non è così importante in questi giorni, poiché il "coprocessore matematico in virgola mobile" ha smesso di avere un significato, ma una volta era abbastanza importante.


18

Usa la formula simile a quella di 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.

Implementato in Scheme, ad esempio:

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


16

Con doppie:

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

Questo sarà accurato fino a 14 cifre decimali, abbastanza per riempire un doppio (probabilmente la precisione è dovuta al troncamento del resto dei decimali nelle tangenti dell'arco).

Anche Seth, è 3.14159265358979323846 3 , non 64.


16

Pi ha esattamente 3 anni! [Prof. Frink (Simpson)]

Scherzo, ma eccone uno in C # (.NET-Framework richiesto).

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

Calcola PI in fase di compilazione con D.

(Copiato da 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
Sfortunatamente, le tangenti sono arctangents basate su pi, invalidando in qualche modo questo calcolo.
Grant Johnson,

14

Questa versione (in Delphi) non è niente di speciale, ma è almeno più veloce della versione che Nick Hodge ha pubblicato sul suo blog :). Sulla mia macchina, ci vogliono circa 16 secondi per fare un miliardo di iterazioni, per un valore di 3.14159265 25879 (la parte accurata è in grassetto).

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

Ai vecchi tempi, con parole di piccole dimensioni e operazioni in virgola mobile lente o inesistenti, facevamo cose del genere:

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

Per le applicazioni che non richiedono molta precisione (ad esempio i videogiochi), questo è molto veloce ed è abbastanza preciso.


11
Per un uso più preciso 355 / 113. Molto preciso per la dimensione dei numeri coinvolti.
David Thornley,

Solo per curiosità: 22/7 è3 + 1/7
Agnius Vasiliauskas

13

Se vuoi calcolare un'approssimazione del valore di π (per qualche motivo), dovresti provare un algoritmo di estrazione binaria. Il miglioramento di Bellard di BBP fornisce PI in O (N ^ 2).


Se si desidera ottenere un'approssimazione del valore di π per eseguire calcoli, quindi:

PI = 3.141592654

Certo, questa è solo un'approssimazione e non del tutto accurata. È spento da poco più di 0,00000000004102. (quattro diecimila bilioni, circa 4 / 10.000.000.000 ).


Se vuoi fare matematica con π, procurati una carta e una matita o un pacchetto di algebra per computer e usa il valore esatto di π, π.

Se vuoi davvero una formula, questa è divertente:

π = - i ln (-1)


La tua formula dipende da come la definisci nel piano complesso. Deve essere non contiguo lungo una linea nel piano complesso ed è abbastanza comune che quella linea sia l'asse reale negativo.
erikkallen,

12

Il metodo di Brent pubblicato sopra da Chris è molto buono; Il Brent è generalmente un gigante nel campo dell'aritmetica di precisione arbitraria.

Se tutto ciò che vuoi è l'ennesima cifra, la famosa formula BBP è utile in esadecimale


1
Il metodo Brent non è stato pubblicato da me; è stato pubblicato da Andrea e mi è capitato di essere l'ultima persona a modificare il post. :-) Ma sono d'accordo, quel post merita un voto positivo.
Chris Jester-Young,

1

Calcolo π dall'area del cerchio :-)

<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

L'algoritmo Chudnovsky è piuttosto veloce se non ti dispiace eseguire una radice quadrata e una coppia si inverte. Converge alla doppia precisione in sole 2 iterazioni.

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

risultati:

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

Approccio migliore

Per ottenere l'output di costanti standard come pi o i concetti standard, dovremmo prima andare con i metodi integrati disponibili nella lingua che stai utilizzando. Restituirà un valore nel modo più veloce e migliore. Sto usando Python per eseguire il modo più veloce per ottenere il valore di PI.

  • pi variabile della libreria matematica . La libreria matematica memorizza la variabile pi come costante.

math_pi.py

import math
print math.pi

Esegui lo script con l'utilità time di linux /usr/bin/time -v python math_pi.py

Produzione:

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
  • Usa il metodo arc cos della matematica

acos_pi.py

import math
print math.acos(-1)

Esegui lo script con l'utilità time di linux /usr/bin/time -v python acos_pi.py

Produzione:

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

Esegui lo script con l'utilità time di linux /usr/bin/time -v python bbp_pi.py

Produzione:

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

Quindi il modo migliore è usare i metodi integrati forniti dalla lingua perché sono i più veloci e i migliori per ottenere l'output. In python usa math.pi

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.