Was ist der schnellste Weg, um den Wert von π zu erhalten?


321

Ich suche nach dem schnellsten Weg, um den Wert von π als persönliche Herausforderung zu erhalten. Insbesondere verwende ich Methoden, bei denen keine #defineKonstanten wie M_PIoder die Codierung der Zahl in verwendet werden.

Das folgende Programm testet die verschiedenen mir bekannten Möglichkeiten. Die Inline-Assembly-Version ist theoretisch die schnellste Option, obwohl sie eindeutig nicht portierbar ist. Ich habe es als Basis für den Vergleich mit den anderen Versionen aufgenommen. In meinen Tests mit integrierten Funktionen ist die 4 * atan(1)Version unter GCC 4.2 am schnellsten, da sie die automatisch atan(1)in eine Konstante faltet . Mit -fno-builtinangegeben ist die atan2(0, -1)Version am schnellsten.

Hier ist das Haupttestprogramm ( 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;
}

Und das Inline-Assembly-Material ( fldpi.c), das nur für x86- und x64-Systeme funktioniert:

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

Und ein Build-Skript, das alle Konfigurationen erstellt, die ich 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

Abgesehen von Tests zwischen verschiedenen Compiler-Flags (ich habe auch 32-Bit mit 64-Bit verglichen, weil die Optimierungen unterschiedlich sind), habe ich auch versucht, die Reihenfolge der Tests zu ändern. Trotzdem hat die atan2(0, -1)Version jedes Mal die Nase vorn.


38
Es muss eine Möglichkeit geben, dies in der C ++ - Metaprogrammierung zu tun. Die Laufzeit wird wirklich gut sein, die Kompilierungszeit jedoch nicht.
David Thornley

1
Warum betrachten Sie die Verwendung von atan (1) anders als die Verwendung von M_PI? Ich würde verstehen, warum Sie dies tun möchten, wenn Sie nur arithmetische Operationen verwendet haben, aber mit atan verstehe ich den Punkt nicht.
Erikkallen

9
Die Frage ist: Warum sollten Sie keine Konstante verwenden wollen? zB entweder von einer Bibliothek oder von Ihnen selbst definiert? Das Berechnen von Pi ist eine Verschwendung von CPU-Zyklen, da dieses Problem immer und immer wieder auf eine Reihe von signifikanten Stellen gelöst wurde, die viel größer sind als für tägliche Berechnungen erforderlich
Tilo

2
@ HopelessN00b Im Dialekt der englischen Sprache ich spreche, „Optimierung“ ist Dinkel mit einem „s“, kein „z“ (die als „zed“, BTW ausgesprochen wird, nicht „zee“ ;-)). (Dies ist nicht das erste Mal, dass ich diese Art der Bearbeitung zurücksetzen musste, wenn Sie sich den Überprüfungsverlauf ansehen.)
Chris Jester-Young

Antworten:


204

Die Monte-Carlo-Methode wendet, wie bereits erwähnt, einige großartige Konzepte an, ist jedoch eindeutig nicht die schnellste, bei weitem nicht und in keinem vernünftigen Maße. Außerdem hängt alles davon ab, welche Genauigkeit Sie suchen. Das schnellste π, das ich kenne, ist das mit den hartcodierten Ziffern. Bei Pi und Pi [PDF] gibt es viele Formeln.

Hier ist eine Methode, die schnell konvergiert - ungefähr 14 Stellen pro Iteration. PiFast , die derzeit schnellste Anwendung, verwendet diese Formel mit der FFT. Ich schreibe nur die Formel, da der Code einfach ist. Diese Formel wurde fast von Ramanujan gefunden und von Chudnovsky entdeckt . Es ist tatsächlich so, wie er mehrere Milliarden Ziffern der Zahl berechnet hat - es ist also keine Methode, sie zu ignorieren. Die Formel wird schnell überlaufen, und da wir Fakultäten teilen, wäre es vorteilhaft, solche Berechnungen zu verzögern, um Terme zu entfernen.

Geben Sie hier die Bildbeschreibung ein

Geben Sie hier die Bildbeschreibung ein

wo,

Geben Sie hier die Bildbeschreibung ein

Unten ist der Brent-Salamin-Algorithmus . Wikipedia erwähnt, dass (a + b) ² / 4t eine Näherung von π ist , wenn a und b "nahe genug" sind . Ich bin mir nicht sicher, was "nah genug" bedeutet, aber aus meinen Tests ergab eine Iteration 2 Ziffern, zwei 7 und drei 15, natürlich mit Doppel, daher kann es aufgrund der Darstellung und zu einem Fehler kommen Die wahre Berechnung könnte genauer sein.

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)

Und wie wäre es mit Pi Golf (800 Stellen)? 160 Zeichen!

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
Angenommen, Sie versuchen, den ersten selbst zu implementieren, wäre sqr (k3) dann kein Problem? Ich bin mir ziemlich sicher, dass es eine irrationale Zahl geben würde, die Sie schätzen müssen (IIRC, alle Wurzeln, die keine ganzen Zahlen sind, sind irrational). Alles andere sieht ziemlich einfach aus, wenn Sie eine Arithmetik mit unendlicher Präzision verwenden, aber diese Quadratwurzel ist ein Deal Breaker. Die zweite enthält auch ein sqrt.
Bill K

2
Nach meiner Erfahrung bedeutet "nah genug" normalerweise, dass es sich um eine Taylorreihen-Annäherung handelt.
Stephen

117

Ich mag dieses Programm wirklich, weil es sich π annähert, indem es seinen eigenen Bereich betrachtet.

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
Wenn Sie _ durch -F <00 || --F-OO-- ersetzen, sollte es einfacher sein zu folgen :-)
Pat

1
oder, wenn Sie _ durch "if (vorheriges Zeichen ist '-') {OO--;} F--;" ersetzen
FryGuy

6
es druckt hier 0,25 -.-
Johannes Schaub - litb

8
Dieses Programm war 1998 großartig, wurde aber gebrochen, weil moderne Präprozessoren liberaler sind, indem sie Leerzeichen um Makroerweiterungen einfügen, um zu verhindern, dass solche Dinge funktionieren. Es ist leider ein Relikt.
Chris Lutz

38
Übergeben Sie --traditional-cppan cpp , um das beabsichtigte Verhalten zu erhalten.
Nietzche-jou

78

Hier ist eine allgemeine Beschreibung einer Technik zur Berechnung des Pi, die ich in der High School gelernt habe.

Ich teile dies nur, weil ich denke, dass es einfach genug ist, dass sich jeder auf unbestimmte Zeit daran erinnern kann, und es lehrt Sie das Konzept der "Monte-Carlo" -Methoden - statistische Methoden, um zu Antworten zu gelangen, die nicht sofort zu sein scheinen durch zufällige Prozesse ableitbar.

Zeichnen Sie ein Quadrat und schreiben Sie einen Quadranten (ein Viertel eines Halbkreises) in dieses Quadrat (einen Quadranten mit einem Radius, der der Seite des Quadrats entspricht, damit er so viel wie möglich vom Quadrat ausfüllt).

Werfen Sie nun einen Pfeil auf das Quadrat und notieren Sie, wo er landet. Wählen Sie also einen beliebigen Punkt innerhalb des Quadrats. Natürlich ist es auf dem Platz gelandet, aber ist es im Halbkreis? Notieren Sie diese Tatsache.

Wiederholen Sie diesen Vorgang viele Male - und Sie werden feststellen, dass es ein Verhältnis der Anzahl der Punkte innerhalb des Halbkreises zur Gesamtzahl der geworfenen Punkte gibt. Nennen Sie dieses Verhältnis x.

Da die Fläche des Quadrats r mal r ist, können Sie daraus schließen, dass die Fläche des Halbkreises x mal r mal r ist (dh x mal r Quadrat). Daher erhalten Sie x mal 4 pi.

Dies ist keine schnelle Methode. Aber es ist ein schönes Beispiel für eine Monte-Carlo-Methode. Und wenn Sie sich umschauen, werden Sie möglicherweise feststellen, dass viele Probleme, die ansonsten außerhalb Ihrer Rechenfähigkeiten liegen, mit solchen Methoden gelöst werden können.


2
Dies ist die Methode, mit der wir Pi in einem Java-Projekt in der Schule berechnet haben. Ich habe nur einen Zufallsgenerator verwendet, um die x-, y-Koordinaten zu ermitteln. Je mehr 'Pfeile' wir geworfen haben, desto näher kamen wir Pi.
Jeff Keslinke

55

Der Vollständigkeit halber wird eine C ++ - Vorlagenversion, die für einen optimierten Build eine Näherung von PI zur Kompilierungszeit berechnet und auf einen einzelnen Wert inline berechnet.

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

Hinweis für I> 10, optimierte Builds können langsam sein, ebenso für nicht optimierte Läufe. Ich glaube, für 12 Iterationen gibt es ungefähr 80.000 Aufrufe von value () (ohne Memoisierung).


Ich
führe

5
Nun, das stimmt mit 9dp überein. Widersprechen Sie etwas oder machen Sie nur eine Beobachtung?
Jon-Hanson

Wie heißt der Algorithmus, der hier zur Berechnung des PI verwendet wird?
Sebastião Miranda

1
@ sebastião-miranda Leibniz 'Formel mit einer durchschnittlichen Beschleunigung verbessert die Konvergenz. pi_calc<0, J>berechnet jeden aufeinanderfolgenden Term aus der Formel und der nicht spezialisierte pi_calc<I, J>berechnet den Durchschnitt.
Jon-Hanson

43

Es gibt tatsächlich ein ganzes Buch, das (unter anderem) schnellen Methoden zur Berechnung von \ pi gewidmet ist: 'Pi und die Hauptversammlung' von Jonathan und Peter Borwein ( erhältlich bei Amazon ).

Ich habe die Hauptversammlung und verwandte Algorithmen ziemlich viel studiert: Es ist ziemlich interessant (wenn auch manchmal nicht trivial).

Beachten Sie, dass Sie zur Implementierung der meisten modernen Algorithmen zur Berechnung von \ pi eine arithmetische Bibliothek mit mehreren Genauigkeiten benötigen ( GMP ist eine gute Wahl, obwohl es eine Weile her ist, seit ich es das letzte Mal verwendet habe).

Die Zeitkomplexität der besten Algorithmen liegt in O (M (n) log (n)), wobei M (n) die Zeitkomplexität für die Multiplikation von zwei n-Bit-Ganzzahlen (M (n) = O (n) ist log (n) log (log (n))) unter Verwendung von FFT-basierten Algorithmen, die normalerweise bei der Berechnung von Ziffern von \ pi benötigt werden, und ein solcher Algorithmus ist in GMP implementiert.

Beachten Sie, dass die Mathematik hinter den Algorithmen zwar nicht trivial ist, die Algorithmen selbst jedoch normalerweise aus wenigen Zeilen Pseudocode bestehen und ihre Implementierung in der Regel sehr einfach ist (wenn Sie keine eigene Multipräzisionsarithmetik schreiben möchten :-)).


42

Im Folgenden wird genau beantwortet , wie dies auf schnellstmögliche Weise und mit geringstem Rechenaufwand möglich ist . Auch wenn Ihnen die Antwort nicht gefällt, müssen Sie zugeben, dass dies in der Tat der schnellste Weg ist, um den Wert von PI zu ermitteln.

Der SCHNELLSTE Weg, um den Wert von Pi zu erhalten, ist:

1) Wählen Sie Ihre bevorzugte Programmiersprache 2) Laden Sie die Mathematikbibliothek 3) und stellen Sie fest, dass Pi dort bereits definiert ist - einsatzbereit!

Falls Sie keine Mathematikbibliothek zur Hand haben ..

Der zweitschnellste Weg (universellere Lösung) ist:

Pi im Internet nachschlagen, zB hier:

http://www.eveandersson.com/pi/digits/1000000 (1 Million Stellen. Wie hoch ist Ihre Gleitkommapräzision?)

oder hier:

http://3.141592653589793238462643383279502884197169399375105820974944592.com/

oder hier:

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

Es ist sehr schnell, die Ziffern zu finden, die Sie für die gewünschte Präzisionsarithmetik benötigen, und indem Sie eine Konstante definieren, können Sie sicherstellen, dass Sie keine wertvolle CPU-Zeit verschwenden.

Dies ist nicht nur eine teilweise humorvolle Antwort, sondern in Wirklichkeit wäre es eine ziemlich große Verschwendung von CPU-Zeit, wenn jemand den Wert von Pi in einer realen Anwendung berechnen würde, nicht wahr? Zumindest sehe ich keine echte Anwendung, um dies neu zu berechnen.

Sehr geehrter Moderator, bitte beachten Sie, dass das OP gefragt hat: "Schnellster Weg, um den Wert von PI zu erhalten"


Lieber Tilo, bitte beachten Sie, dass das OP sagte: "Ich suche nach dem schnellsten Weg, um den Wert von π als persönliche Herausforderung zu erhalten. Insbesondere verwende ich Wege, bei denen keine # define-Konstanten wie M_PI verwendet werden , oder die Nummer in fest codieren .
Max

Lieber @Max, bitte beachten Sie, dass das OP seine ursprüngliche Frage bearbeitet hat , nachdem ich sie beantwortet habe - das ist kaum meine Schuld;) Meine Lösung ist immer noch der schnellste Weg und löst das Problem mit jeder gewünschten Gleitkommapräzision und ohne CPU-Zyklen elegant :)
Tilo

Oh, tut mir leid, ich habe es nicht bemerkt. Nur ein Gedanke, hätten die hartcodierten Konstanten nicht weniger Genauigkeit als die Berechnung von pi? Ich denke, es hängt davon ab, welche Sprache es ist und wie bereit der Schöpfer ist, alle Ziffern einzugeben :-)
Max

1
Verdammt, ich habe vergessen, Dear Tilo
Max

27

Mit der BBP-Formel können Sie die n-te Ziffer - in Basis 2 (oder 16) - berechnen, ohne sich zuerst um die vorherigen n-1-Ziffern kümmern zu müssen :)


23

Anstatt pi als Konstante zu definieren, benutze ich immer acos(-1).


2
cos (-1) oder acos (-1)? :-P Das (letzteres) ist einer der Testfälle in meinem ursprünglichen Code. Es gehört zu meinen bevorzugten (zusammen mit atan2 (0, -1), was wirklich dasselbe ist wie acos (-1), außer dass acos normalerweise in Bezug auf atan2 implementiert wird), aber einige Compiler optimieren für 4 * atan (1) !
Chris Jester-Young

21

Dies ist eine "klassische" Methode, die sehr einfach zu implementieren ist. Diese Implementierung in Python (nicht die schnellste Sprache) macht es:

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)

Weitere Informationen finden Sie hier .

Der schnellste Weg, um einen genauen Wert von pi in Python zu erhalten, der so viel ist, wie Sie möchten, ist:

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

Hier ist die Quelle für die gmpy pi-Methode. Ich denke, der Code ist in diesem Fall nicht so nützlich wie der Kommentar:

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: Ich hatte einige Probleme mit Ausschneiden und Einfügen und Einrücken. Die Quelle finden Sie hier .


20

Wenn Sie mit "am schnellsten" meinen, dass Sie den Code am schnellsten eingeben, finden Sie hier die Lösung für golfscript :

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

18

Wenn Sie bereit sind, eine Näherung zu verwenden, 355 / 113ist dies gut für 6 Dezimalstellen und hat den zusätzlichen Vorteil, dass es mit ganzzahligen Ausdrücken verwendet werden kann. Das ist heutzutage nicht so wichtig, da "Gleitkomma-Mathe-Co-Prozessor" keine Bedeutung mehr hatte, aber es war einmal ziemlich wichtig.


18

Verwenden Sie die Machin-ähnliche Formel

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.

Im Schema implementiert, zum Beispiel:

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


16

Mit Doppel:

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

Dies ist bis zu 14 Dezimalstellen genau genug, um ein Doppel zu füllen (die Ungenauigkeit ist wahrscheinlich darauf zurückzuführen, dass die restlichen Dezimalstellen in den Bogen-Tangenten abgeschnitten sind).

Auch Seth, es ist 3.14159265358979323846 3 , nicht 64.


16

Pi ist genau 3! [Prof. Frink (Simpsons)]

Witz, aber hier ist einer in C # (.NET-Framework erforderlich).

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

Berechnen Sie den PI zur Kompilierungszeit mit D.

(Von DSource.org kopiert )

/** 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
Leider sind Tangenten Arkustangens, die auf pi basieren, was diese Berechnung etwas ungültig macht.
Grant Johnson

14

Diese Version (in Delphi) ist nichts Besonderes, aber sie ist zumindest schneller als die Version, die Nick Hodge in seinem Blog gepostet hat :). Auf meinem Rechner es etwa 16 Sekunden dauert , eine Milliarde Iterationen zu tun, einen Wert zu geben 3,14159265 (ist der genaue Teil fett gedruckt) 25879.

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

Früher haben wir mit kleinen Wortgrößen und langsamen oder nicht existierenden Gleitkommaoperationen solche Dinge gemacht:

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

Für Anwendungen, die nicht viel Präzision erfordern (z. B. Videospiele), ist dies sehr schnell und genau genug.


11
Für mehr Genauigkeit verwenden 355 / 113. Sehr genau für die Größe der beteiligten Zahlen.
David Thornley

Nur aus Neugier: 22/7 ist3 + 1/7
Agnius Vasiliauskas

13

Wenn Sie (aus irgendeinem Grund) eine Annäherung an den Wert von π berechnen möchten , sollten Sie einen binären Extraktionsalgorithmus ausprobieren. Bellards Verbesserung von BBP ergibt PI in O (N ^ 2).


Wenn Sie eine Annäherung an den Wert von π erhalten möchten, um Berechnungen durchzuführen, dann:

PI = 3.141592654

Zugegeben, das ist nur eine Annäherung und nicht ganz richtig. Es ist etwas mehr als 0,00000000004102. (vier Zehn-Billionenstel, ungefähr 4 / 10.000.000.000 ).


Wenn Sie mit π rechnen möchten , besorgen Sie sich einen Stift und Papier oder ein Computeralgebra-Paket und verwenden Sie den genauen Wert von π, π.

Wenn Sie wirklich eine Formel wollen, macht diese Spaß:

π = - i ln (-1)


Ihre Formel hängt davon ab, wie Sie ln in der komplexen Ebene definieren. Es muss entlang einer Linie in der komplexen Ebene nicht zusammenhängend sein, und es ist durchaus üblich, dass diese Linie die negative reelle Achse ist.
Erikkallen

12

Die oben von Chris veröffentlichte Methode von Brent ist sehr gut. Brent ist im Allgemeinen ein Riese auf dem Gebiet der Arithmetik mit beliebiger Genauigkeit.

Wenn Sie nur die N-te Ziffer möchten, ist die berühmte BBP-Formel in hex nützlich


1
Die Brent-Methode wurde von mir nicht veröffentlicht. Es wurde von Andrea gepostet und ich war zufällig die letzte Person, die den Beitrag bearbeitet hat. :-) Aber ich stimme zu, dieser Beitrag verdient eine positive Bewertung.
Chris Jester-Young

1

Berechnung von π aus der Kreisfläche :-)

<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

Der Chudnovsky-Algorithmus ist ziemlich schnell, wenn es Ihnen nichts ausmacht, eine Quadratwurzel und ein paar Umkehrungen durchzuführen. Es konvergiert in nur 2 Iterationen zur doppelten Genauigkeit.

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

Ergebnisse:

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

Besserer Ansatz

Um die Ausgabe von Standardkonstanten wie pi oder den Standardkonzepten zu erhalten, sollten wir zunächst die integrierten Methoden verwenden, die in der von Ihnen verwendeten Sprache verfügbar sind. Es wird auf schnellste und beste Weise einen Wert zurückgegeben. Ich benutze Python, um den schnellsten Weg zu finden, um den Wert von pi zu erhalten.

  • pi Variable der Mathematikbibliothek . Die Mathematikbibliothek speichert die Variable pi als Konstante.

math_pi.py

import math
print math.pi

Führen Sie das Skript mit dem Zeitdienstprogramm von Linux aus /usr/bin/time -v python math_pi.py

Ausgabe:

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
  • Verwenden Sie die Arc-Cos-Methode der Mathematik

acos_pi.py

import math
print math.acos(-1)

Führen Sie das Skript mit dem Zeitdienstprogramm von Linux aus /usr/bin/time -v python acos_pi.py

Ausgabe:

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

Führen Sie das Skript mit dem Zeitdienstprogramm von Linux aus /usr/bin/time -v python bbp_pi.py

Ausgabe:

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

Der beste Weg ist also, integrierte Methoden zu verwenden, die von der Sprache bereitgestellt werden, da sie die schnellsten und besten sind, um die Ausgabe zu erhalten. Verwenden Sie in Python math.pi

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.