Berechnen Sie π mit quadratischer Konvergenz


20

Schreiben Sie eine Funktion oder ein vollständiges Programm, das eine positive Zahl annimmt nund nSchritte eines iterativen Algorithmus zur Berechnung von π ausführt , der eine quadratische Konvergenz aufweist (dh die Anzahl der genauen Stellen bei jeder Iteration ungefähr verdoppelt), und dann 2 n richtige Stellen zurückgibt oder ausgibt (einschließlich) der Anfang 3). Ein solcher Algorithmus ist der Gauß-Legendre-Algorithmus . Sie können jedoch auch einen anderen Algorithmus verwenden, wenn Sie dies vorziehen.

Beispiele:

Eingabe 1→ Ausgabe 3.1
Eingabe 2→ Ausgabe 3.141
Eingabe 5→ Ausgabe3.1415926535897932384626433832795

Bedarf:

  • Jede Iteration des Algorithmus muss eine konstante Anzahl von Grundoperationen wie Addition, Subtraktion, Multiplikation, Division, Potenz und Wurzel (mit ganzzahligem Exponenten / Grad) ausführen wenn es sich um eine oder mehrere interne Schleifen handelt. Trigonometrische Funktionen und Potenzen mit komplexen Zahlen sind keine Grundoperationen.
  • Es wird erwartet, dass der Algorithmus einen Initialisierungsschritt aufweist, der auch eine konstante Anzahl von Operationen aufweisen muss.
  • Wenn der Algorithmus 1 oder 2 weitere Iterationen benötigt, um 2 n richtige Stellen zu erhalten, können Sie n+2statt nur bis zu Iterationen ausführen n.
  • Wenn es nicht klar genug ist, nach den richtigen 2 n Ziffern, muss Ihr Programm nicht alles drucken sonst (wie richtigere Ziffern, falsche Ziffern oder das Gesamtwerk von Shakespeare).
  • Ihr Programm muss Werte nvon 1 bis mindestens 20 unterstützen.
  • Ihr Programm sollte nauf einem modernen Computer für = 20 nicht länger als eine Stunde dauern (keine harte Regel, aber versuchen Sie es vernünftig zu halten).
  • Das Programm darf nach der Initialisierung und der ersten Iteration des Algorithmus nicht mehr als 20 genaue Stellen erhalten.
  • Das Programm muss unter Linux mit frei verfügbarer Software lauffähig sein.
  • Der Quellcode darf nur ASCII-Zeichen enthalten.

Wertung:

Direkter Code Golf, kürzester Code gewinnt.

Gewinner:

Der Gewinner ist Digital Trauma, ich habe endlich seinen Code auf n = 20 laufen lassen (nur Spaß). Der Sonderpreis geht an primo für seine sehr schnelle Python-Lösung und seinen anderen Algorithmus :)


1
Die quadratische Konvergenz ist der Fehler ~ N ^ (1/2) . Was Sie beschreiben, ist der exponentielle Konvergenzfehler ~ 2 ^ (- N) .
Du

@yo 'sagst du, dass Wikipedia falsch ist?
Aditsu

1
Zumindest irreführend zu sagen: "Quadratische Konvergenz" entspricht dort ~q^(n^2)dem 1. Abschnitt und ~q^2dort dem 2. Abschnitt.
Du

1
Ich verstehe Codegolf nicht: Sicherlich könnte jeder seine eigene Programmiersprache speziell für eine einzelne Aufgabe wie diese schreiben und dann ein Programm mit beispielsweise 0 Bytes schreiben.
theonlygusti

2
@theonlygusti, das wäre eine Standardlücke und würde disqualifiziert werden
aditsu

Antworten:


14

Gleichstrom, 99 Bytes

Golf gespielt:

2?dsi1+^k1dddsa2v/sb4/stsp[lalb*vlalb+2/dla-d*lp*ltr-stsasblp2*spli1-dsi0<m]dsmxK2/1-klalb+d*4lt*/p

Mit Leerzeichen und Kommentaren für "Lesbarkeit":

2?dsi               # Push 2. push input n, duplicate and store in i
1+^k                # Set calculation precision to 2^(n+1)
1dddsa              # Push four 1s. Store 1st in a
2v/sb               # Store 1/sqrt(2) in b
4/st                # Store 1/4 in t
sp                  # Store 1 in p
[                   # Start iteration loop macro
lalb*v              # Save sqrt(a*b) on stack
lalb+2/d            # Save a[i+1] = (a[i]+b[i])/2 on stack and duplicate
la-d*lp*ltr-        # Save t-p(a[i]-a[i+1])^2 on the stack
st                  # Store t result from stack
sa                  # Store a result from stack
sb                  # Store b result from stack
lp2*sp              # Store 2p in p
li1-dsi0<m]         # Decrement iteration counter i; recurse into macro if < 0
dsmx                # Duplicate, store and run macro
K2/1-k              # Set display precision to 2^n-1
lalb+d*4lt*/        # Save (a+b)^2/4t on stack
p                   # Print result

dcmuss angegeben werden, wie viele Stellen der Genauigkeit verwendet werden sollen. Die Berechnungsgenauigkeit muss höher sein als die endgültige Anzeigegenauigkeit, daher wird die Berechnungsgenauigkeit auf 2^(n+1)Ziffern festgelegt. Ich habe die Genauigkeit der Ausgabe mit n = 10 gegen http://www.angio.net/pi/digits/pi1000000.txt überprüft .

Dies verlangsamt sich dramatisch für größere n; Auf meiner VM dauert n = 12 1,5 Minuten. Wenn Sie ein paar verschiedene Beispiele durchgehen, ist die Zeitkomplexität O (e ^ n) (nicht überraschend). Eine Extrapolation auf n = 20 hätte eine Laufzeit von 233 Tagen. Naja. Zumindest besser als der Hitzetod des Universums.

Dies ist mäßig ausgeprägt - der Stapel wird verwendet, um temporäre Variablen während der Berechnungen jeder Iteration zu eliminieren. Möglicherweise wird der Stapel jedoch häufiger verwendet, um diese zu verkürzen.

$ dc glpi.dc <<< 1
3.1
$ dc glpi.dc <<< 2
3.141
$ dc glpi.dc <<< 5
3.1415926535897932384626433832795
$ time dc glpi.dc <<< 7
3.1415926535897932384626433832795028841971693993751058209749445923078\
164062862089986280348253421170679821480865132823066470938446

real    0m0.048s
user    0m0.039s
sys 0m0.000s
$ 

Wenn Sie keine dcAusgabe mit 70 Zeichen umbrechen möchten, können Sie die Umgebungsvariable DC_LINE_LENGTHauf 0 setzen:

$ DC_LINE_LENGTH=0 dc glpi.dc <<< 8
3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117067982148086513282306647093844609550582231725359408128481117450284102701938521105559644622948954930381964428810975665933446128475648233786783165271201909145648
$ 

2
Haha, "Lesbarkeit". Trifft nicht wirklich auf Gleichstrom zu. ;)
Alex A.

Es scheint viel mehr als 32 Stellen für Eingabe 5 zu drucken
aditsu

Ich habe eine Regel hinzugefügt, plus eine weitere über die Laufzeit (aber nicht wirklich streng). Mir gefällt auch nicht, wie Ihre Ausgabe in mehrere Zeilen mit Backslashes aufgeteilt wird. Ist das eine Einschränkung von DC?
Aditsu

Ich fürchte, die Ausgabe ist falsch für n = 6
aditsu

1
Super, und du hast es auch unter 100 :) Könntest du auch das aktuelle 99-Zeichen-Programm ohne Leerzeichen / Kommentare posten?
Aditsu

10

R, 156 Bytes

Beginnen wir diese Party ... mit der absolut naivsten Implementierung des Gauss-Legendre-Algorithmus, die es je gab.

for(i in 1:scan()){if(i<2){a=p=Rmpfr::mpfr(1,2e6);t=a/4;b=t^t}else{x=(a+b)/2;b=(a*b)^.5;t=t-p*(a-x)^2;a=x;p=2*p};o=(a+b)^2/(4*t)};cat(Rmpfr::format(o,2^i))

Ungolfed + Erklärung:

# Generate n approximations of pi, where n is read from stdin
for (i in 1:scan()) {

    # Initial values on the first iteration
    if (i < 2) {
        a <- p <- Rmpfr::mpfr(1, 1e7)
        t <- a/4
        b <- t^t
    } else {
        # Compute new values
        x <- (a + b) / 2
        b <- (a*b)^0.5
        t <- t - p*(a - x)^2

        # Store values for next iteration
        a <- x
        p <- 2*p
    }

    # Approximate pi 
    o <- (a + b)^2 / (4*t)
}

# Print the result with 2^n digits
cat(Rmpfr::format(o, 2^i))

Die mpfr()Funktion ist Bestandteil des RmpfrPakets. Es wird ein mpfrObjekt erstellt, das das erste Argument als Wert und das zweite Argument als Anzahl der Genauigkeitsbits verwendet. Wir ordnen aund pzu 1 und definieren tbasierend auf a(undb basierend auf t), dass der mpfrTyp sich auf alle vier Variablen überträgt, wodurch die Präzision durchgehend erhalten bleibt.

Wie bereits erwähnt, ist hierfür das R-Paket erforderlich Rmpfr, das als Abkürzung für "R Multiple Precision Floating Point Reliable" (Gleitkommazuverlässig mit mehrfacher Genauigkeit) steht. Das Paket verwendet GMP im Hintergrund. Leider kann die Basis R nicht hochpräzise arithmetisch arbeiten, daher die Paketabhängigkeit.

Nicht haben Rmpfr? Kein Schweiß.install.packages("Rmpfr")und all deine Träume werden wahr.

Beachten Sie, dass 2e6als Genauigkeit angegeben wurde. Das heißt, wir haben 2.000.000 Bits Genauigkeit, was ausreicht, um die Genauigkeit für mindestens n= 20 zu halten. (Hinweis:n = 20 dauert auf meinem Computer sehr lange, aber weniger als eine Stunde.)

Der Ansatz hier ist buchstäblich nur ein Aufstoßen der Formeln auf der Wikipedia-Seite, aber hey, wir müssen irgendwo anfangen.

Jede Eingabe ist wie immer willkommen!


Ich musste eine Menge davon umschreiben, aber ich muss immer noch anerkennen, dass Peter Taylor mir geholfen hat, 70 Bytes von meiner ersten Partitur zu entfernen. Mit den Worten von DigitalTrauma: "boom".


7

Python 2, 214 Bytes

Diese Herausforderung war eine gute Ausrede für mich, das Dezimalmodul zu lernen. Die Dezimalzahlen sind genau definierbar und werden durch Quadratwurzeln unterstützt. Ich habe die Genauigkeit auf eine konservative Schätzung der Genauigkeit in Abhängigkeit von der Schleifenzahl eingestellt.

Aktualisieren

Ich habe das Programm aktualisiert, um Genauigkeit und Geschwindigkeit auf Kosten des Golfspiels zu verbessern. Durch die Verwendung der Dezimalmethode sqrt()und das Ersetzen der x**2Verwendung durch x*xist sie jetzt 200-mal schneller. Dies bedeutet, dass es jetzt 20 Schleifen berechnen kann, die in 6,5 Stunden ein Ergebnis mit einer Million Stellen ergeben. Die Dezimalzahlen enthalten häufig einen Fehler in der letzten Ziffer (verursacht durch Operationen an der Genauigkeitsgrenze). Das Programm verwendet und verwirft daher jetzt 5 zusätzliche Ziffern, sodass nur genaue Ziffern gedruckt werden.

from decimal import*
d=Decimal
e=input()
getcontext().prec=5+(1<<e)
k=d(1)
j=d(2)
g=j*j
h=k/j
a=k
b=k/j.sqrt()
t=k/g
p=k
for i in[0]*e:f=a;a,b=(a+b)/j,(a*b).sqrt();c=f-a;t-=c*c*p;p+=p
l=a+b
print str(l*l/g/t)[:-5]

Probelauf:

$ echo 1 | python min.py 
3.1
$ echo 2 | python min.py 
3.141
$ echo 3 | python min.py 
3.1415926
$ echo 5 | python min.py 
3.1415926535897932384626433832795
$ echo 12 | python min.py
3.141592653589793238462643383279502884197169399375105820974944592307816406286208
99862803482534211706798214808651328230664709384460955058223172535940812848111745
02841027019385211055596446229489549303819644288109756659334461284756482337867831
65271201909145648566923460348610454326648213393607260249141273724587006606315588
17488152092096282925409171536436789259036001133053054882046652138414695194151160
94330572703657595919530921861173819326117931051185480744623799627495673518857527
24891227938183011949129833673362440656643086021394946395224737190702179860943702
77053921717629317675238467481846766940513200056812714526356082778577134275778960
91736371787214684409012249534301465495853710507922796892589235420199561121290219
60864034418159813629774771309960518707211349999998372978049951059731732816096318
59502445945534690830264252230825334468503526193118817101000313783875288658753320
83814206171776691473035982534904287554687311595628638823537875937519577818577805
32171226806613001927876611195909216420198938095257201065485863278865936153381827
96823030195203530185296899577362259941389124972177528347913151557485724245415069
59508295331168617278558890750983817546374649393192550604009277016711390098488240
12858361603563707660104710181942955596198946767837449448255379774726847104047534
64620804668425906949129331367702898915210475216205696602405803815019351125338243
00355876402474964732639141992726042699227967823547816360093417216412199245863150
30286182974555706749838505494588586926995690927210797509302955321165344987202755
96023648066549911988183479775356636980742654252786255181841757467289097777279380
00816470600161452491921732172147723501414419735685481613611573525521334757418494
68438523323907394143334547762416862518983569485562099219222184272550254256887671
79049460165346680498862723279178608578438382796797668145410095388378636095068006
42251252051173929848960841284886269456042419652850222106611863067442786220391949
45047123713786960956364371917287467764657573962413890865832645995813390478027590
09946576407895126946839835259570982582262052248940772671947826848260147699090264
01363944374553050682034962524517493996514314298091906592509372216964615157098583
87410597885959772975498930161753928468138268683868942774155991855925245953959431
04997252468084598727364469584865383673622262609912460805124388439045124413654976
27807977156914359977001296160894416948685558484063534220722258284886481584560285
06016842739452267467678895252138522549954666727823986456596116354886230577456498
03559363456817432411251507606947945109659609402522887971089314566913686722874894
05601015033086179286809208747609178249385890097149096759852613655497818931297848
21682998948722658804857564014270477555132379641451523746234364542858444795265867
82105114135473573952311342716610213596953623144295248493718711014576540359027993
44037420073105785390621983874478084784896833214457138687519435064302184531910484
81005370614680674919278191197939952061419663428754440643745123718192179998391015
91956181467514269123974894090718649423196156794520809514655022523160388193014209
37621378559566389377870830390697920773467221825625996615014215030680384477345492
02605414665925201497442850732518666002132434088190710486331734649651453905796268
56100550810665879699816357473638405257145910289706414011097120628043903975951567
71577004203378699360072305587631763594218731251471205329281918261861258673215791
98414848829164470609575270695722091756711672291098169091528017350671274858322287
18352093539657251210835791513698820914442100675103346711031412671113699086585163
98315019701651511685171437657618351556508849099898599823873455283316355076479185
35893226185489632132933089857064204675259070915481416549859461637180270981994309
92448895757128289059232332609729971208443357326548938239119325974636673058360414
28138830320382490375898524374417029132765618093773444030707469211201913020330380
19762110110044929321516084244485963766983895228684783123552658213144957685726243
34418930396864262434107732269780280731891544110104468232527162010526522721116603
96665573092547110557853763466820653109896526918620564769312570586356620185581007
29360659876486117

Der ungolfed Code:

from decimal import *
d = Decimal

loops = input()
# this is a conservative estimate for precision increase with each loop:
getcontext().prec = 5 + (1<<loops)

# constants:
one = d(1)
two = d(2)
four = two*two
half = one/two

# initialise:
a = one
b = one / two.sqrt()
t = one / four
p = one

for i in [0]*loops :
    temp = a;
    a, b = (a+b)/two, (a*b).sqrt();
    pterm = temp-a;
    t -= pterm*pterm * p;
    p += p

ab = a+b
print str(ab*ab / four / t)[:-5]

4
Hehhalf = one/two
Digital Trauma

Es scheint, dass Sie nicht die richtige Anzahl von Ziffern drucken. Und ich frage mich, ob die Langsamkeit auf den unnötigen Gebrauch von zurückzuführen ist **.
Aditsu

1
@Aditsu, ich habe die Genauigkeit auf die erwartete Stellenzahl reduziert (aber wenn ich eine perfekte Genauigkeit von einer Iteration wegwerfe, jucken meine Zähne). Guter Vorschlag zur **Wirkung. Ich fand viel Geschwindigkeit, indem ich sie loswurde. Ich kann die 20 Loops in 1 Stunde nicht erreichen. Vielleicht mit Pypy oder Cython? Hmmm. Ich werde das in Betracht ziehen.
Logic Knight

Viel besser :) Für dieses Problem ist es weniger schlimm, gute Genauigkeit wegzuwerfen, als mit schlechter Genauigkeit fortzufahren. Das 1-Stunden-Limit basiert auf meinem CJAM / Java-Testcode, der mit Java 8 ausgeführt wird. Hat Python möglicherweise keine effiziente Multiplikation / Division / SQLT für große Zahlen (Karatsuba & Co.)?
Aditsu

@aditsu: Ich glaube, dass Ganzzahlen Karatsuba (und genau das) haben - aber mit einer 32-Bit-Extremitätengröße anstelle einer 64-Bit-Extremitätengröße. Wer kennt sich mit Decimal aus?

5

Python (2.7) - 131 Bytes

from gmpy import*
n=input()
p=a=fsqrt(mpf(8,4<<n));b=0
exec"a=fsqrt(a/2);b=1/(a-a*b+b/a+1);p*=b+a*a*b;a+=1/a;"*n
print`p`[5:2**n+6]

Update: Jetzt mit gmpyanstatt gmpy2. Aus irgendeinem Grund wird beim gmpy2Festlegen der Genauigkeit für einen einzelnen Wert keine Übertragung auf andere Werte ausgeführt. Das Ergebnis einer Berechnung wird auf die Genauigkeit des aktuellen Kontexts zurückgesetzt. Präzision breitet sich aus gmpy, was mir intuitiver erscheint. Es ist auch wesentlich weniger ausführlich.

Mit einem der vielen Algorithmen von Borwein und Borwein , leicht überarbeitet. n = 20 dauert auf meiner Box ca. 11 Sekunden. Nicht die effizienteste Methode, aber immer noch nicht schlecht.


Refactoring

Der ursprüngliche Algorithmus war der folgende:




Refactoring wurde schrittweise durchgeführt, aber das Endergebnis ist das




Die Hauptvereinfachung findet in p n + 1 statt . Es ist auch etwas schneller, weil eine Division eliminiert wurde.

Die aktuelle Implementierung drückt eine n zurück in eine Iteration der Berechnung von p n + 1 , die für eine andere Initialisierung ermöglicht p 0 ( 2√2 ), ist aber ansonsten identisch.


Beispielnutzung

$ echo 1 | python pi-borwein.py
3.1

$ echo 2 | python pi-borwein.py
3.141

$ echo 5 | python pi-borwein.py
3.1415926535897932384626433832795

$ echo 10 | python pi-borwein.py
3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117067982148086513282306647093844609550582231725359408128481117450284102701938521105559644622948954930381964428810975665933446128475648233786783165271201909145648566923460348610454326648213393607260249141273724587006606315588174881520920962829254091715364367892590360011330530548820466521384146951941511609433057270365759591953092186117381932611793105118548074462379962749567351885752724891227938183011949129833673362440656643086021394946395224737190702179860943702770539217176293176752384674818467669405132000568127145263560827785771342757789609173637178721468440901224953430146549585371050792279689258923542019956112129021960864034418159813629774771309960518707211349999998372978049951059731732816096318595024459455346908302642522308253344685035261931188171010003137838752886587533208381420617177669147303598253490428755468731159562863882353787593751957781857780532171226806613001927876611195909216420198938095257201065485863278

Großartig, aber Ihnen fehlt eine Ziffer für n = 7
aditsu

Auch ist es dieser Algorithmus ?
Aditsu

@aditsu behoben, und ja, das ist es.
Primo

Jetzt ist die letzte Ziffer falsch für n = 5
aditsu

1
@Aditsu pip install gmpyarbeitete für mich; gmpyund gmpy2sind separate Pakete. Es hängt jedoch von den veralteten ab distutils.
Primo

3

bc und Newtons Methode, 43 Bytes

Newtons Methode zum Auffinden von Nullen einer Funktion konvergiert quadratisch und der Algorithmus ist viel einfacher als für Gauß-Legendre. Es läuft im Grunde auf Folgendes hinaus:

xnew = xold - f (xold) / f '(xold)

Also hier kommt das entsprechende Snippet:

n=20;x=3;scale=2^n;while(n--)x-=s(x)/c(x);x

Ein bisschen besser lesbar:

/* desired number of iterations */
n = 20;

/* starting estimate for pi */
x = 3;

/* set precision to 2^n */
scale = 2^n;

/* perform n iteration steps */
while(n--)
  // f:=sin, f'=cos
  x -= s(x)/c(x)

Um dies zu testen, starte bc -lin einer Shell und füge das obige Snippet ein. Seien Sie bereit, eine Weile zu warten. n=20läuft seit ca. 5min und ein ende ist noch nicht in sicht. n=10dauert ca. 40s.


4
Nicht sicher, ob Sinus und Cosinus als "Grundoperationen wie Addition, Subtraktion, Multiplikation, Division und Potenz (einschließlich Wurzeln)" qualifiziert sind . Wenn Sie jedoch Ihren eigenen Sinus / Cosinus rollen würden, wäre dies wahrscheinlich akzeptabel.
Primo

1
Ordentliche Formel - es heißt, dass π ein fester Punkt von f (x) = x - tan (x) ist
Casey Chu
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.