352 Stimmen

Wie kann man den Wert von π am schnellsten ermitteln?

Ich suche nach dem schnellsten Weg, den Wert von zu erhalten, als persönliche Herausforderung. Genauer gesagt, suche ich nach Wegen, die nicht die Verwendung von #define Konstanten wie M_PI oder die Nummer fest eintippen.

Das folgende Programm testet die verschiedenen mir bekannten Möglichkeiten. Die Inline-Assembly-Version ist theoretisch die schnellste Option, obwohl sie natürlich nicht portabel ist. Ich habe sie als Basis für den Vergleich mit den anderen Versionen aufgenommen. In meinen Tests, mit eingebauten Komponenten, war die 4 * atan(1) Version ist auf GCC 4.2 am schnellsten, weil sie die automatische Faltung der atan(1) in eine Konstante. Mit -fno-builtin angegeben, die atan2(0, -1) Version ist 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 die Inline-Assembly-Sachen ( fldpi.c ), die 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 vom Testen 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 umzukehren. Aber immer noch ist die atan2(0, -1) Version immer noch jedes Mal die Nase vorn hat.

44voto

Tilo Punkte 32417

Die folgenden Antworten wie dies am schnellsten und mit dem geringsten Rechenaufwand möglich ist . Auch wenn Ihnen die Antwort nicht gefällt, müssen Sie zugeben, dass dies tatsächlich der schnellste Weg ist, den Wert von PI zu ermitteln.

Les SCHNELLSTE um den Wert von Pi zu ermitteln:

  1. Wählen Sie Ihre bevorzugte Programmiersprache
  2. seine Mathematikbibliothek laden
  3. und stellen fest, dass Pi dort bereits definiert ist - bereit für die Verwendung!

Für den Fall, dass Sie keine Mathematikbibliothek zur Hand haben.

Les ZWEITENSCHNELLSTE Weg (universellere Lösung) ist:

Pi im Internet nachschlagen, z. B. hier:

http://www.eveandersson.com/pi/digits/1000000 (1 Million Ziffern wie hoch ist Ihre Fließkommagenauigkeit? )

oder hier:

http://3.141592653589793238462643383279502884197169399375105820974944592.com/

oder hier:

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

Es ist wirklich schnell, die Ziffern zu finden, die Sie für eine beliebige 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ürde jeder, der den Wert von Pi in einer realen Anwendung berechnen würde, eine ziemlich große Verschwendung von CPU-Zeit darstellen, nicht wahr? Zumindest sehe ich keine echte Anwendung für den Versuch, diesen Wert neu zu berechnen.

Berücksichtigen Sie auch dass die NASA für die Berechnung interplanetarer Reisen nur 15 Ziffern von Pi verwendet:

Sehr geehrter Moderator: bitte beachten Sie, dass der OP fragte: "Der schnellste Weg, den Wert von PI zu ermitteln"

28voto

Tyler Punkte 27980

Les BBP-Formel ermöglicht die Berechnung der n-ten Ziffer - zur Basis 2 (oder 16) - ohne dass man sich erst mit den vorherigen n-1 Ziffern beschäftigen muss :)

24voto

Andrea Ambu Punkte 36268

Dies ist eine "klassische" Methode, die sehr einfach zu implementieren ist. Diese Implementierung in Python (nicht die schnellste Sprache) tut 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 unter aquí .

Wie auch immer, der schnellste Weg, um einen genauen Wert von Pi in Python zu erhalten, ist:

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

Hier ist der Quelltext für die gmpy pi-Methode, ich glaube nicht, dass der Code in diesem Fall so nützlich ist 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 dem Ausschneiden und Einfügen und der Einrückung, Sie können die Quelle finden aquí .

23voto

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

21voto

Michiel de Mare Punkte 41184

Wenn Sie mit "am schnellsten" meinen, dass Sie den Code am schnellsten eintippen können, dann ist hier die golfscript Lösung:

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

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X