Calcola π con convergenza quadratica


20

Scrivi una funzione o un programma completo che accetta un numero positivo ned esegue i npassaggi di un algoritmo iterativo per il calcolo di π con convergenza quadratica (cioè raddoppia approssimativamente il numero di cifre accurate ad ogni iterazione), quindi restituisce o stampa 2 n cifre corrette (tra cui l'inizio 3). Uno di questi algoritmi è l'algoritmo Gauss – Legendre , ma se preferisci sei libero di usare un algoritmo diverso.

Esempi:

input 1→ input 3.1
input 2→ output 3.141
input 5→ output3.1415926535897932384626433832795

Requisiti:

  • Ogni iterazione dell'algoritmo deve eseguire un numero costante di operazioni di base come addizione, sottrazione, moltiplicazione, divisione, potenza e radice (con esponente / grado intero) - ciascuna di queste operazioni su numeri interi / decimali "grandi" viene conteggiata come una pari se coinvolge uno o più loop internamente. Per essere chiari, funzioni trigonometriche e poteri che coinvolgono numeri complessi non sono operazioni di base.
  • Si prevede che l'algoritmo abbia una fase di inizializzazione che deve avere anche un numero costante di operazioni.
  • Se l'algoritmo necessita di 1 o 2 più iterazioni per arrivare a 2 n cifre corrette, è possibile eseguire fino a n+2iterazioni anziché solo n.
  • Se non fosse abbastanza chiaro, dopo le 2 n cifre corrette , il tuo programma non deve stampare nient'altro (come cifre più corrette, cifre errate o le opere complete di Shakespeare).
  • Il tuo programma deve supportare valori compresi ntra 1 e almeno 20.
  • Il tuo programma non dovrebbe richiedere più di un'ora per n= 20 su un computer moderno (non è una regola difficile, ma cerca di mantenerlo ragionevole).
  • Il programma non deve ottenere più di 20 cifre accurate dopo l'inizializzazione e la prima iterazione dell'algoritmo.
  • Il programma deve essere eseguibile su Linux utilizzando software disponibile gratuitamente.
  • Il codice sorgente deve usare solo caratteri ASCII.

punteggio:

Semplice codice golf, il codice più corto vince.

Vincitore:

Il vincitore è Digital Trauma, ho finalmente finito di eseguire il suo codice su n = 20 (sto scherzando). Il premio speciale va al primo per la sua soluzione python molto veloce e il suo algoritmo diverso :)


1
La convergenza quadratica è errore ~ ​​N ^ (1/2) . Quello che descrivi è un errore di convergenza esponenziale ~ 2 ^ (- N) .
yo

@yo 'stai dicendo che Wikipedia è sbagliata?
aditsu,

1
Ingannevole, almeno per dire: "convergenza quadratica" è ~q^(n^2)secondo la prima sezione lì e ~q^2secondo la seconda sezione lì.
yo'

1
Non capisco codegolf: sicuramente chiunque potrebbe semplicemente scrivere il proprio linguaggio di programmazione appositamente per una singola attività come questa, quindi scrivere un programma di, diciamo, 0 byte?
theonlygusti,

2
@theonlygusti sarebbe una scappatoia standard e verrebbe squalificata
aditsu,

Risposte:


14

dc, 99 byte

golfed:

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

Con spazi bianchi e commenti per "leggibilità":

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

dcbisogna dire quante cifre di precisione dovrebbero essere usate. La precisione di calcolo deve essere superiore alla precisione di visualizzazione finale, quindi la precisione di calcolo è impostata su 2^(n+1)cifre. Ho verificato l'accuratezza dell'output con n = 10 rispetto a http://www.angio.net/pi/digits/pi1000000.txt .

Questo rallenta drasticamente per n maggiore; n = 12 richiede 1,5 minuti sulla mia macchina virtuale. L'esecuzione di alcuni campioni diversi mostra che la complessità temporale è O (e ^ n) (non sorprendente). Estrapolare questo a n = 20 avrebbe una durata di 233 giorni. Oh bene. Meglio della morte di calore dell'universo almeno.

Questo è moderatamente golfato - lo stack viene utilizzato per eliminare le variabili temporanee durante i calcoli di ciascuna iterazione, ma è possibile che sia più utilizzato lo stack per accorciarlo di più.

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

Se non ti piace il dcwrapping dell'output a 70 caratteri, puoi impostare la variabile di ambiente DC_LINE_LENGTHsu 0:

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

2
Haha, "leggibilità". Non si applica davvero a DC. ;)
Alex A.

Sembra stampare molto più di 32 cifre per l'ingresso 5
aditsu

Ho aggiunto una regola per questo, più un'altra sul tempo di esecuzione (ma non proprio severo). Inoltre, non mi piace come il tuo output sia diviso in più righe con barre rovesciate, è una limitazione di cc?
aditsu,

Temo che l'output sia sbagliato per n = 6
aditsu

1
Fantastico, e hai anche meno di 100 :) Potresti anche pubblicare il vero programma 99-char golfizzato senza spazi bianchi / commenti?
aditsu,

10

R, 156 byte

Iniziamo questa festa ... con l'implementazione assolutamente ingenua dell'algoritmo Gauss-Legendre di sempre.

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 + spiegazione:

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

La mpfr()funzione fa parte del Rmpfrpacchetto. Crea un mpfroggetto usando il primo argomento come valore e il secondo argomento come numero di bit di precisione. Assegniamo ae pa 1, e definendo in tbase a a(e in bbase a t), il mpfrtipo propaga a tutte e quattro le variabili, mantenendo così la precisione in tutto.

Come accennato, ciò richiede il pacchetto R Rmpfr, che è l'acronimo di "R Multiple Precision Floating point Affiable". Il pacchetto utilizza GMP in background. Sfortunatamente la base R non ha la capacità di eseguire un'aritmetica di alta precisione, quindi la dipendenza dal pacchetto.

Non hai Rmpfr? Niente sudore. install.packages("Rmpfr")e tutti i tuoi sogni diventeranno realtà.

Si noti che è 2e6stato specificato come precisione. Ciò significa che abbiamo 2.000.000 di bit di precisione, che è sufficiente per mantenere la precisione per almeno n= 20. (Nota: n= 20 richiede molto tempo ma meno di un'ora sul mio computer.)

L'approccio qui è letteralmente solo un rigurgito delle formule sulla pagina di Wikipedia, ma ehi, dobbiamo iniziare da qualche parte.

Ogni input è il benvenuto come sempre!


Ho dovuto riscrivere molto, ma devo ancora riconoscere che Peter Taylor mi ha aiutato a cancellare 70 byte dal mio primo punteggio. Nelle parole di DigitalTrauma, "boom".


7

Python 2, 214 byte

Questa sfida ha rappresentato una buona scusa per imparare il modulo decimale. I numeri decimali hanno precisione definibile e supporto per radice quadrata. Ho impostato la precisione su una stima conservativa dell'accuratezza in base al conteggio dei loop.

Aggiornare

Ho aggiornato il programma per migliorare la precisione e la velocità, a spese del golf. Utilizzando il sqrt()metodo decimale e sostituendo l' x**2utilizzo con x*x, ora è 200 volte più veloce. Ciò significa che ora può calcolare 20 loop dando un risultato di un milione di cifre in 6,5 ore. I numeri decimali hanno spesso un errore nell'ultima cifra (causato da operazioni sul limite di precisione), quindi il programma ora utilizza e scarta 5 cifre extra in modo che vengano stampate solo cifre accurate.

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]

Esecuzione di esempio:

$ 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

Il codice ungolfed:

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

Sembra che non stia stampando il numero corretto di cifre. E mi chiedo se la lentezza sia dovuta all'uso non necessario di **.
aditsu,

1
@aditsu, ho ridotto la precisione del conteggio delle cifre atteso (ma buttare via la precisione perfettamente buona da un'iterazione mi fa prudere i denti). Un buon suggerimento **sull'effetto. Ho trovato molta velocità sbarazzandomi di loro. Tuttavia, non riesco a soddisfare i 20 loop in 1 ora. Forse con pypy o Cython? Hmmm. Lo considererò.
Logic Knight,

Molto meglio :) Per questo problema, gettare via una buona precisione è meno male che continuare con una cattiva precisione. Il limite di 1 ora si basa sul mio codice di test cjam / java eseguito con java 8. Forse python non ha una moltiplicazione / divisione / sqrt efficiente per grandi numeri (Karatsuba & co)?
aditsu,

@aditsu: credo che i numeri interi abbiano karatsuba (e proprio quello) - ma con dimensioni degli arti a 32 bit anziché dimensioni degli arti a 64 bit. Chi conosce Decimal.

5

Python (2.7) - 131 byte

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]

Aggiornamento: ora usando gmpyanziché gmpy2. Per qualsiasi motivo, gmpy2impostando la precisione su un singolo valore non si propaga ad altri valori. Il risultato di qualsiasi calcolo ritorna alla precisione del contesto attuale. La precisione si propaga gmpy, il che mi sembra più intuitivo. È anche molto meno prolisso.

Utilizzando uno dei tanti algoritmi ideati da Borwein e Borwein , leggermente refactored. n = 20 richiede circa 11 secondi sulla mia scatola. Non è il metodo più efficiente, ma non è male.


refactoring

L'algoritmo originale era il seguente:




Il refactoring è stato eseguito in modo incrementale, ma il risultato finale è quello




La maggiore semplificazione avviene in p n + 1 . È anche leggermente più veloce a causa dell'eliminazione di una divisione.

L'implementazione corrente spinge un n indietro un'iterazione nel calcolo di p n + 1 , che consente una diversa inizializzazione di p 0 ( 2√2 ), ma è identico.


Esempio di utilizzo

$ 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

Ottimo, ma ti manca una cifra per n = 7
aditsu

Inoltre, è questo algoritmo ?
aditsu,

@aditsu risolto, e sì lo è.
primo

Ora l'ultima cifra è sbagliata per n = 5
aditsu

1
@aditsu ha pip install gmpylavorato per me; gmpye gmpy2sono pacchetti separati. Tuttavia, si basa sul deprecato distutils.
primo

3

bc e metodo di Newton, 43 byte

Il metodo di Newton per trovare zeri di qualsiasi funzione converge quadraticamente e l'algoritmo è molto più semplice rispetto a Gauss-Legendre. In pratica si riduce a:

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

Quindi ecco lo snippet corrispondente:

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

Un po 'più leggibile:

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

Per testarlo, esegui bc -l una shell e incolla il frammento sopra. Preparati ad aspettare un po '; n=20è in esecuzione da circa 5 minuti e non ha ancora fine. n=10dura circa 40 anni.


4
Non sono sicuro che seno e coseno si qualifichino come "operazioni di base come addizione, sottrazione, moltiplicazione, divisione e potere (comprese le radici)" . Tuttavia, se arrotolassi il tuo seno / coseno, probabilmente sarebbe accettabile.
primo

1
Formula pulita, però - dice che π è un punto fisso di f (x) = x - tan (x)
Casey Chu
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.