Quel est le moyen le plus rapide pour obtenir la valeur de π?


322

Je cherche le moyen le plus rapide pour obtenir la valeur de π, comme défi personnel. Plus précisément, j'utilise des méthodes qui n'impliquent pas l'utilisation de #defineconstantes comme M_PIou le codage en dur du nombre.

Le programme ci-dessous teste les différentes manières que je connais. La version d'assemblage en ligne est, en théorie, l'option la plus rapide, bien qu'elle ne soit clairement pas portable. Je l'ai inclus comme référence pour comparer avec les autres versions. Dans mes tests, avec les intégrés, la 4 * atan(1)version est la plus rapide sur GCC 4.2, car elle se replie automatiquement atan(1)en une constante. Avec -fno-builtinspécifié, la atan2(0, -1)version est la plus rapide.

Voici le programme de test 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;
}

Et les éléments d'assemblage en ligne ( fldpi.c) qui ne fonctionneront que pour les systèmes x86 et x64:

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

Et un script de construction qui construit toutes les configurations que je teste ( 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

En plus de tester entre différents drapeaux du compilateur (j'ai comparé 32 bits contre 64 bits aussi parce que les optimisations sont différentes), j'ai également essayé de changer l'ordre des tests. Mais toujours, la atan2(0, -1)version sort toujours en tête à chaque fois.


38
Il doit y avoir un moyen de le faire dans la métaprogrammation C ++. Le temps d'exécution sera vraiment bon, mais pas le temps de compilation.
David Thornley

1
Pourquoi considérez-vous que l'utilisation d'un atan (1) est différente de celle de M_PI? Je comprendrais pourquoi vous voulez faire cela si vous n'utilisez que des opérations arithmétiques, mais avec atan, je ne vois pas l'intérêt.
erikkallen

9
la question est: pourquoi ne voudriez-vous pas utiliser une constante? par exemple, soit défini par une bibliothèque ou par vous-même? Computing Pi est un gaspillage de cycles CPU, car ce problème a été résolu maintes et maintes fois avec un nombre de chiffres significatifs bien supérieur à celui nécessaire pour les calculs quotidiens
Tilo

2
@ HopelessN00b Dans le dialecte de l'anglais que je parle, "optimisation" est orthographié avec un "s", pas un "z" (qui se prononce comme "zed", BTW, pas "zee" ;-)). (Ce n'est pas la première fois que je dois annuler ce type de montage aussi, si vous regardez l'historique des critiques.)
Chris Jester-Young

Réponses:


205

La méthode de Monte-Carlo , comme mentionné, applique certains grands concepts, mais ce n'est clairement pas le plus rapide, ni à long terme, ni dans une mesure raisonnable. En outre, tout dépend du type de précision que vous recherchez. Le π le plus rapide que je connaisse est celui avec les chiffres codés en dur. En regardant Pi et Pi [PDF] , il y a beaucoup de formules.

Voici une méthode qui converge rapidement - environ 14 chiffres par itération. PiFast , l'application actuelle la plus rapide, utilise cette formule avec la FFT. Je vais juste écrire la formule, car le code est simple. Cette formule a été presque trouvée par Ramanujan et découverte par Chudnovsky . C'est en fait ainsi qu'il a calculé plusieurs milliards de chiffres du nombre - ce n'est donc pas une méthode à ignorer. La formule débordera rapidement et, comme nous divisons les factorielles, il serait alors avantageux de retarder ces calculs pour supprimer les termes.

entrez la description de l'image ici

entrez la description de l'image ici

où,

entrez la description de l'image ici

Voici l' algorithme Brent – ​​Salamin . Wikipedia mentionne que lorsque a et b sont "assez proches", alors (a + b) ² / 4t sera une approximation de π. Je ne sais pas ce que signifie "assez proche", mais d'après mes tests, une itération a obtenu 2 chiffres, deux en ont obtenu 7 et trois en ont eu 15, bien sûr c'est avec des doubles, donc il pourrait y avoir une erreur basée sur sa représentation et le vrai calcul pourrait être plus précis.

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)

Enfin, qu'en est-il du golf pi (800 chiffres)? 160 caractères!

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
En supposant que vous essayez d'implémenter le premier vous-même, sqr (k3) ne serait-il pas un problème? Je suis presque sûr que cela finirait par un nombre irrationnel que vous devrez estimer (IIRC, toutes les racines qui ne sont pas des nombres entiers sont irrationnelles). Tout le reste semble assez simple si vous utilisez l'arithmétique de précision infinie, mais cette racine carrée est un facteur de rupture. Le second comprend également un sqrt.
Bill K

2
d'après mon expérience, «assez proche» signifie généralement qu'il y a une approximation de la série Taylor impliquée.
Stephen

117

J'aime vraiment ce programme, car il se rapproche de π en regardant sa propre zone.

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 vous remplacez _ par -F <00 || --F-OO-- cela devrait être plus facile à suivre :-)
Pat

1
ou, si vous remplacez _ par "if (le caractère précédent est '-') {OO--;} F--;"
FryGuy

6
il imprime 0,25 ici -.-
Johannes Schaub - litb

8
Ce programme était génial en 1998, mais a été interrompu parce que les préprocesseurs modernes sont plus libéraux avec l'insertion d'espaces autour des extensions de macro pour empêcher des choses comme ça de fonctionner. C'est une relique, malheureusement.
Chris Lutz

38
Passez --traditional-cppà cpp pour obtenir le comportement souhaité.
Nietzche-jou

78

Voici une description générale d'une technique de calcul de pi que j'ai apprise au lycée.

Je ne partage cela que parce que je pense que c'est assez simple pour que n'importe qui puisse s'en souvenir, indéfiniment, en plus il vous enseigne le concept de méthodes "Monte-Carlo" - qui sont des méthodes statistiques pour arriver à des réponses qui ne semblent pas immédiatement être déductible par des processus aléatoires.

Dessinez un carré et inscrivez un quadrant (un quart de demi-cercle) à l'intérieur de ce carré (un quadrant de rayon égal au côté du carré, de sorte qu'il remplisse autant de carré que possible)

Maintenant, lancez une fléchette sur le carré et enregistrez où elle atterrit, c'est-à-dire choisissez un point aléatoire n'importe où à l'intérieur du carré. Bien sûr, il a atterri à l'intérieur du carré, mais est-il à l'intérieur du demi-cercle? Enregistrez ce fait.

Répétez ce processus plusieurs fois - et vous constaterez qu'il existe un rapport entre le nombre de points à l'intérieur du demi-cercle et le nombre total lancé, appelez ce rapport x.

Puisque l'aire du carré est r fois r, vous pouvez en déduire que l'aire du demi-cercle est x fois r fois r (c'est-à-dire x fois r au carré). Par conséquent, x fois 4 vous donnera pi.

Ce n'est pas une méthode rapide à utiliser. Mais c'est un bel exemple de méthode Monte Carlo. Et si vous regardez autour de vous, vous constaterez peut-être que de nombreux problèmes, en dehors de vos compétences informatiques, peuvent être résolus par de telles méthodes.


2
C'est la méthode que nous avons utilisée pour calculer Pi dans un projet java à l'école. Je viens d'utiliser un randomiseur pour trouver les coordonnées x, y et plus nous avons lancé de fléchettes, plus nous nous sommes approchés de Pi.
Jeff Keslinke

55

Dans un souci d'exhaustivité, une version de modèle C ++, qui, pour une construction optimisée, calculera une approximation de PI au moment de la compilation et s'alignera sur une seule valeur.

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

Remarque pour I> 10, les versions optimisées peuvent être lentes, de même pour les exécutions non optimisées. Pour 12 itérations, je pense qu'il y a environ 80k appels à value () (en l'absence de mémoisation).


Je lance ceci et reçois "pi ~ 3.14159265383"
maxwellb

5
Eh bien, c'est précis pour 9dp. Êtes-vous opposé à quelque chose ou faites-vous simplement une observation?
jon-hanson

quel est le nom de l'algorithme utilisé ici pour calculer PI?
Sebastião Miranda du

1
La formule de @ sebastião-miranda Leibniz , avec une accélération moyenne améliore la convergence. pi_calc<0, J>calcule chaque terme successif à partir de la formule et le non spécialisé pi_calc<I, J>calcule la moyenne.
jon-hanson

43

Il existe en fait un livre entier dédié (entre autres) aux méthodes rapides pour le calcul de \ pi: 'Pi and the AGM', par Jonathan et Peter Borwein ( disponible sur Amazon ).

J'ai beaucoup étudié l'AGA et les algorithmes associés: c'est assez intéressant (bien que parfois non trivial).

Notez que pour implémenter la plupart des algorithmes modernes pour calculer \ pi, vous aurez besoin d'une bibliothèque arithmétique multiprécision ( GMP est un bon choix, même si cela fait un moment que je ne l'ai pas utilisé pour la dernière fois).

La complexité temporelle des meilleurs algorithmes se trouve dans O (M (n) log (n)), où M (n) est la complexité temporelle pour la multiplication de deux entiers de n bits (M (n) = O (n log (n) log (log (n))) en utilisant des algorithmes basés sur FFT, qui sont généralement nécessaires lors du calcul des chiffres de \ pi, et un tel algorithme est implémenté dans GMP).

Notez que même si les mathématiques derrière les algorithmes ne sont pas triviales, les algorithmes eux-mêmes sont généralement quelques lignes de pseudo-code, et leur implémentation est généralement très simple (si vous choisissez de ne pas écrire votre propre arithmétique multi-précision :-)).


42

Les réponses suivantes expliquent précisément comment procéder de la manière la plus rapide possible - avec le moins d'effort informatique . Même si vous n'aimez pas la réponse, vous devez admettre que c'est en effet le moyen le plus rapide pour obtenir la valeur de PI.

La manière la plus RAPIDE d'obtenir la valeur de Pi est:

1) choisissez votre langage de programmation préféré 2) chargez sa bibliothèque Math 3) et trouvez que Pi y est déjà défini - prêt à l'emploi!

Dans le cas où vous n'avez pas de bibliothèque Math à portée de main ..

La deuxième méthode la plus rapide (solution plus universelle) est:

recherchez Pi sur Internet, par exemple ici:

http://www.eveandersson.com/pi/digits/1000000 (1 million de chiffres .. quelle est votre précision en virgule flottante?)

ou ici:

http://3.141592653589793238462643383279502884197169399375105820974944592.com/

ou ici:

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

Il est très rapide de trouver les chiffres dont vous avez besoin pour l'arithmétique de précision que vous souhaitez utiliser, et en définissant une constante, vous pouvez vous assurer que vous ne perdez pas un temps CPU précieux.

Non seulement c'est une réponse en partie humoristique, mais en réalité, si quelqu'un devait aller de l'avant et calculer la valeur de Pi dans une application réelle .. ce serait une assez grosse perte de temps CPU, n'est-ce pas? Au moins, je ne vois pas de véritable application pour essayer de recalculer cela.

Cher modérateur: veuillez noter que le PO a demandé: "Le moyen le plus rapide pour obtenir la valeur de PI"


Cher Tilo: veuillez noter que l'OP a déclaré: "Je recherche le moyen le plus rapide pour obtenir la valeur de π, en tant que défi personnel. Plus précisément, j'utilise des moyens qui n'impliquent pas l'utilisation de constantes #define comme M_PI , ou coder en dur le nombre .
Max

Cher @Max: veuillez noter que l'OP a modifié sa question d'origine après avoir répondu - ce n'est pas de ma faute;) Ma solution est toujours le moyen le plus rapide et résout le problème avec la précision en virgule flottante souhaitée et sans cycle de processeur avec élégance :)
Tilo

Oh désolé, je ne m'en suis pas rendu compte. Juste une pensée, les constantes codées en dur n'auraient-elles pas moins de précision que le calcul de pi? Je suppose que cela dépend de la langue et de la volonté du créateur de mettre tous les chiffres :-)
Max

1
Merde, j'ai oublié d'ajouter Dear Tilo
Max

27

La formule BBP vous permet de calculer le nième chiffre - en base 2 (ou 16) - sans même avoir à vous soucier des n-1 précédents :)


23

Au lieu de définir pi comme une constante, j'utilise toujours acos(-1).


2
cos (-1) ou acos (-1)? :-P Ce (ce dernier) est l'un des cas de test dans mon code d'origine. C'est parmi mes préférés (avec atan2 (0, -1), qui est vraiment le même que acos (-1), sauf que acos est généralement implémenté en termes d'atan2), mais certains compilateurs optimisent pour 4 * atan (1) !
Chris Jester-Young

21

Il s'agit d'une méthode "classique", très simple à mettre en œuvre. Cette implémentation en python (pas le langage le plus rapide) le fait:

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)

Vous pouvez trouver plus d'informations ici .

Quoi qu'il en soit, le moyen le plus rapide pour obtenir une valeur précise autant que vous le souhaitez de pi en python est:

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

Voici le morceau de source pour la méthode gmpy pi, je ne pense pas que le code soit aussi utile que le commentaire dans ce cas:

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: J'ai eu quelques problèmes avec le copier-coller et l'indentation, vous pouvez trouver la source ici .


20

Si par plus rapide vous voulez dire le plus rapide à taper le code, voici la solution golfscript :

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

18

Si vous êtes prêt à utiliser une approximation, 355 / 113est valable pour 6 chiffres décimaux et a l'avantage supplémentaire d'être utilisable avec des expressions entières. Ce n'est pas aussi important de nos jours, car le "coprocesseur mathématique à virgule flottante" a cessé d'avoir un sens, mais c'était très important une fois.


18

Utilisez la formule de type 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.

Implémenté dans Scheme, par exemple:

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


16

Avec des doubles:

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

Ce sera précis jusqu'à 14 décimales, assez pour remplir un double (l'inexactitude est probablement parce que le reste des décimales dans les tangentes d'arc sont tronquées).

Seth aussi, c'est 3,14159265358979323846 3 , pas 64.


16

Pi est exactement 3! [Prof. Frink (Simpsons)]

Blague, mais en voici une en C # (.NET-Framework requis).

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

Calculer PI au moment de la compilation avec D.

(Copié 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
Malheureusement, les tangentes sont des arctangentes basées sur pi, ce qui invalide quelque peu ce calcul.
Grant Johnson

14

Cette version (en Delphi) n'a rien de spécial, mais elle est au moins plus rapide que la version que Nick Hodge a publiée sur son blog :). Sur ma machine, il faut environ 16 secondes pour effectuer un milliard d'itérations, ce qui donne une valeur de 3,14159265 25879 (la partie précise est en gras).

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

Dans le passé, avec de petites tailles de mots et des opérations à virgule flottante lentes ou inexistantes, nous faisions des choses comme ceci:

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

Pour les applications qui ne nécessitent pas beaucoup de précision (jeux vidéo par exemple), c'est très rapide et assez précis.


11
Pour une utilisation plus précise 355 / 113. Très précis pour la taille des nombres impliqués.
David Thornley

Juste par curiosité: 22/7 est3 + 1/7
Agnius Vasiliauskas

13

Si vous voulez calculer une approximation de la valeur de π (pour une raison quelconque), vous devriez essayer un algorithme d'extraction binaire. L' amélioration de Bellard de BBP donne un PI en O (N ^ 2).


Si vous souhaitez obtenir une approximation de la valeur de π pour effectuer des calculs, alors:

PI = 3.141592654

Certes, ce n'est qu'une approximation, et pas tout à fait exact. Il est décalé d'un peu plus de 0,0000000000004102. (quatre-dix milliardièmes, environ 4 / 10000000000 ).


Si vous voulez faire des calculs avec π, procurez-vous un crayon et du papier ou un paquet d'algèbre informatique, et utilisez la valeur exacte de π, π.

Si vous voulez vraiment une formule, celle-ci est amusante:

π = - i ln (-1)


Votre formule dépend de la façon dont vous définissez ln dans le plan complexe. Il doit être non contigu le long d'une ligne dans le plan complexe, et il est assez courant que cette ligne soit l'axe réel négatif.
erikkallen

12

La méthode de Brent publiée ci-dessus par Chris est très bonne; Le Brent est généralement un géant dans le domaine de l'arithmétique à précision arbitraire.

Si tout ce que vous voulez c'est le Nième chiffre, la fameuse formule BBP est utile en hexadécimal


1
La méthode Brent n'a pas été publiée par moi; il a été publié par Andrea, et il se trouve que je suis la dernière personne à avoir édité le message. :-) Mais je suis d'accord, ce post mérite un vote positif.
Chris Jester-Young

1

Calcul de π à partir de la zone du cercle :-)

<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'algorithme Chudnovsky est assez rapide si cela ne vous dérange pas d'effectuer une racine carrée et quelques inverses. Il converge vers la double précision en seulement 2 itérations.

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

Résultats:

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

Meilleure approche

Pour obtenir la sortie de constantes standard comme pi ou les concepts standard, nous devons d'abord utiliser les méthodes intégrées disponibles dans le langage que vous utilisez. Il renverra une valeur de la manière la plus rapide et la meilleure. J'utilise python pour exécuter le moyen le plus rapide d'obtenir la valeur de pi.

  • variable pi de la bibliothèque mathématique . La bibliothèque mathématique stocke la variable pi comme constante.

math_pi.py

import math
print math.pi

Exécutez le script avec l'utilitaire time de linux /usr/bin/time -v python math_pi.py

Production:

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
  • Utiliser la méthode mathématique arc cos

acos_pi.py

import math
print math.acos(-1)

Exécutez le script avec l'utilitaire time de linux /usr/bin/time -v python acos_pi.py

Production:

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

Exécutez le script avec l'utilitaire time de linux /usr/bin/time -v python bbp_pi.py

Production:

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

La meilleure façon est donc d'utiliser les méthodes intégrées fournies par le langage car elles sont les plus rapides et les meilleures pour obtenir la sortie. En python, utilisez math.pi

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.