Calcola pi con 5 decimali


15

Questo proviene da http://programmers.blogoverflow.com/2012/08/20-controversial-programming-opinions/

"Dato che Pi può essere stimato usando la funzione 4 * (1 - 1/3 + 1/5 - 1/7 + ...) con più termini che danno una maggiore precisione, scrivi una funzione che calcola Pi con una precisione di 5 cifre decimali. "

  • Nota, la stima deve essere fatta calcolando la sequenza sopra indicata.

8
Probabilmente dovresti aggiungere qualche altra regola, altrimenti otterrai risposte come (python)p=lambda:3.14159
Matt

1
Hai visto codegolf.stackexchange.com/questions/506/… , che è molto simile? Per lo meno, le funzioni di trigger dovrebbero essere vietate per questo problema perché consentono soluzioni banali come questo programma QBASIC:? INT (4E5 * ATN (1)) / 1E5
PleaseStand

Penso che dovresti richiedere che l'algoritmo sia di approssimazione successiva: più a lungo calcoli, più ti avvicini a pi.
DavidC

@DavidCarraher, sebbene matematicamente inevitabile usando questa serie, da un punto di vista analitico numerico è altamente dubbio. Una serie alternata lentamente convergente è un bambino poster per perdita di significato.
Peter Taylor,

2
Dupe, ma è così vecchio che non è qui: stackoverflow.com/q/407518/12274
JB

Risposte:


10

JavaScript, 46 58 56 45 byte

Aggiornamento ES6 : ci sono più funzioni disponibili ora che sono trascorsi cinque anni.

let f=(i=0,a=0)=>i>1e6?a:f(i+4,a+8/-~i/(i+3))

Questa versione ( 45 byte; sì, letè necessaria) funziona in teoria in modalità rigorosa ES6 . In pratica, è possibile eseguirlo in V8 (ad es. Con nodo) con --use-strict --harmony-tailcalls; ahimè, la funzione di coda corretta non è ancora ampiamente implementata. Tuttavia, è un comportamento specificato, quindi dovrebbe andare bene.

Se vogliamo attenerci a ciò che è ampiamente implementato e non richiedere la modalità rigorosa, possiamo semplicemente utilizzare la sintassi della freccia fat ES6 per le funzioni, ma altrimenti mantenere la stessa implementazione di prima (suggerita da Brian H) per un costo di 48 byte.

a=>{for(a=i=0;i<1e6;a+=8/++i/~-(i+=3));return a}

La scelta del nome per il singolo parametro non ha molta importanza, ma potremmo anche scegliere uno dei nomi che utilizziamo in modo da ridurre al minimo l'inquinamento di portata globale.


function(){for(a=i=0;i<1e6;a+=8/++i/~-(i+=3));return a}

Questa versione è un'espressione di funzione; aggiungi due caratteri (ad es. " f") se vuoi che venga chiamato. Questa versione blocca i globali ae i; ciò potrebbe essere evitato se si aggiungesse " a,i" all'elenco dei parametri.

Fa uso di una versione riformulata dell'algoritmo per eludere la necessità di sottrazione.

 1/1 - 1/3  +   1/5 - 1/7   +    1/9 - 1/11  + ...
(3/3 - 1/3) + (7/35 - 5/35) + (11/99 - 9/99) + ...
    2/3     +      2/35     +       2/99     + ...
  2/(1*3)   +    2/(5*7)    +     2/(9*11)   + ...

Ecco una versione "semplice" senza questa regolazione:

function(){for(a=0,i=1;i<1e6;i+=2)a+=[,4,,-4][i%4]/i;return a}

che arriva a 64 62 caratteri.

Grazie a @ardnew per il suggerimento di sbarazzarsi del 4*prima il return.


Storia

function(){for(a=i=0;i<1e6;a+=8/++i/~-(i+=3));return a}     // got rid of `i+=4`; restructured
// Old versions below.
function(){for(a=0,i=1;i<1e6;i+=4)a+=8/i/-~-~i;return a}    // got rid of `4*`
function(){for(a=0,i=1;i<1e6;i+=4)a+=2/i/-~-~i;return 4*a}

oO lavoro molto bello, fattorizzando la sottrazione.
accolito il

1
ottimo lavoro, ma deve essere scritto come una funzione adeguata
ardnew

@ardnew: grazie, devo aver perso quel dettaglio quando ho letto la descrizione del problema. L'ho aggiornato ed è ora un'espressione di funzione richiamabile (lambda); non sono sicuro che ciò sia consentito o che debba essere assegnato un nome. In tal caso, sono comunque solo altri due personaggi.
FireFly,

1
@FireFly puoi anche radere 2 caratteri cambiando a+=2/i/-~-~i;return 4*aina+=8/i/-~-~i;return a
ardnew il

@ardnew: oh, fantastico; non ci ho pensato. : D
FireFly,

8

Python 59 byte

print reduce(lambda x,p:p/2*x/p+2*10**999,range(6637,1,-2))

Questo stampa 1000 cifre; leggermente più di quanto richiesto 5. Invece di utilizzare l'iterazione prescritta, utilizza questo:

pi = 2 + 1/3*(2 + 2/5*(2 + 3/7*(2 + 4/9*(2 + 5/11*(2 + ...)))))

Il 6637(denominatore più interno) può essere formulato come:

cifre * 2 * registro 2 (10)

Ciò implica una convergenza lineare. Ogni iterazione più profonda produrrà un altro bit binario di pi .

Se , tuttavia, insisti nell'usare l'identità tan -1 , si può ottenere una convergenza simile, se non ti dispiace affrontare il problema in modo leggermente diverso. Dando un'occhiata alle somme parziali:

4.0, 2.66667, 3.46667, 2.89524, 3.33968, 2.97605, 3.28374, ...

è evidente che ogni termine salta avanti e indietro su entrambi i lati del punto di convergenza; la serie ha una convergenza alternata. Inoltre, ogni termine è più vicino al punto di convergenza rispetto al termine precedente; è assolutamente monotonico rispetto al suo punto di convergenza. La combinazione di queste due proprietà implica che la media aritmetica di due termini vicini è più vicina al punto di convergenza di uno dei termini stessi. Per darti un'idea migliore di ciò che intendo, considera la seguente immagine:

Partial Sums

La serie esterna è l'originale e la serie interna si trova prendendo la media di ciascuno dei termini vicini. Una differenza notevole. Ma ciò che è veramente straordinario, è che questa nuova serie ha anche una convergenza alternata, ed è assolutamente monotona rispetto al suo punto di convergenza. Ciò significa che questo processo può essere applicato più volte, fino alla nausea.

Ok. Ma come?

Alcune definizioni formali. Let P 1 (n) sia il n esimo termine della prima sequenza, P 2 (n) sia il n esimo termine della seconda sequenza, e similmente P k (n) il n esimo termine della k esima sequenza come sopra definiti .

P 1 = [P 1 (1), P 1 (2), P 1 (3), P 1 (4), P 1 (5), ...]

P 2 = [(P 1 (1) + P 1 (2)) / 2, (P 1 (2) + P 1 (3)) / 2, (P 1 (3) + P 1 (4)) / 2, (P 1 (4) + P 1 (5)) / 2, ...]

P 3 = [(P 1 (1) + 2P 1 (2) + P 1 (3)) / 4, (P 1 (2) + 2P 1 (3) + P 1 (4)) / 4, (P 1 (3) + 2P 1 (4) + P 1 (5)) / 4, ...]

P 4 = [(P 1 (1) + 3P 1 (2) + 3P 1 (3) + P 1 (4)) / 8, (P 1 (2) + 3P 1 (3) + 3P 1 (4) + P 1 (5)) / 8, ...]

Non sorprende che questi coefficienti seguano esattamente i coefficienti binomiali e possano essere espressi come una singola riga del triangolo di Pascal. Da una riga arbitraria del triangolo di Pascal è banale per calcolare, arbitrariamente 'profonda' serie possono essere trovati, semplicemente muovendo i primi n parziali somme, moltiplicare ciascuna con il termine corrispondente nella k esima fila di triangolo di Pascal, e dividendo per 2 k-1 .

In questo modo, è possibile ottenere la massima precisione in virgola mobile a 32 bit (~ 14 posizioni decimali) con solo 36 iterazioni, a quel punto le somme parziali non sono nemmeno convergenti sulla seconda posizione decimale. Questo ovviamente non è golfato:

# used for pascal's triangle
t = 36; v = 1.0/(1<<t-1); e = 1
# used for the partial sums of pi
p = 4; d = 3; s = -4.0

x = 0
while t:
  t -= 1
  p += s/d; d += 2; s *= -1
  x += p*v
  v = v*t/e; e += 1

print "%.14f"%x

Se si desidera una precisione arbitraria, questo può essere ottenuto con una piccola modifica. Qui di nuovo calcolando 1000 cifre:

# used for pascal's triangle
f = t = 3318; v = 1; e = 1
# used for the partial sums of pi
p = 4096*10**999; d = 3; s = -p

x = 0
while t:
  t -= 1
  p += s/d; d += 2; s *= -1
  x += p*v
  v = v*t/e; e += 1

print x>>f+9

Il valore iniziale di p inizia 2 10 più grande, per contrastare gli effetti di divisione di s / d man mano che d diventa più grande, facendo sì che le ultime cifre non convergano. Si noti di nuovo che 3318è anche:

cifre * registro 2 (10)

Lo stesso numero di iterazioni del primo algoritmo (dimezzato perché t diminuisce di 1 anziché 2 per ciascuna iterazione). Ancora una volta, questo indica una convergenza lineare: un bit binario di pi per iterazione. In entrambi i casi sono necessarie 3318 iterazioni per calcolare 1000 cifre di pi , come quota leggermente migliore di 1 milione di iterazioni per calcolare 5.


È molto meglio della mia soluzione:4 * sum(1/(1+i*2) if not i%2 else -1/(1+i*2) for i in xrange(places*10**(places)))
Aaron Hall,

1
Questo è molto simile al mio approccio , che sembra essere una tua forma diversa. Nel mio, come k → ∞, si f(-1,k)avvicina la tua somma di Eulero.
Simply Beautiful Art,

1
Molto bello; analisi e spiegazione fantastiche, grazie.
Jeremy Radcliff,

Solo una piccola cosa Non intendevi dopo P_1 = ..., P_2 = ..., P_3 = ..., P_4 = ..., "... moltiplicare ciascuno per il termine corrispondente nella kthriga del Triangolo di Pascal, e dividendo per 2^{k-1}.", Anziché nthriga e 2^{n-1}?.
Jeremy Radcliff,

@jeremyradcliff L'ho fatto, sì. Grazie per la correzione.
primo

5

Mathematica 42 39 34 33 31 26 32

Approccio di Archimede 26 caratteri

N@#*Sin[180 Degree/#]&

Questo raggiunge il criterio quando l'ingresso è 822.

Domanda: qualcuno sa come ha calcolato il peccato di 180 gradi? Io non.


Approccio di Leibniz (serie di Gregory) 32 caratteri

Questa è la stessa funzione fornita dal poser di problemi come esempio. Raggiunge il criterio in circa mezzo milione di iterazioni.

N@4Sum[(-1)^k/(2k+1),{k,0,10^6}]

Approccio Madhava-Leibniz 37 caratteri

Questa variazione utilizza alcuni caratteri in più ma converge al criterio in solo 9 iterazioni!

N@Sqrt@12 Sum[(-1/3)^k/(2k+1),{k,0,9}]

quelli lo calcolano tutti con l'algoritmo indicato nella definizione del problema?
accolito il

L'approccio di @acolyte Leibniz (ora il primo elencato) è in effetti quello menzionato nella descrizione del problema. È molto lento a convergere. Una leggera variazione su di essa (Madhava-Leibniz) converge molto rapidamente.
DavidC

Il seno di 180 ° è abbastanza facile. È 180 ° / N che può diventare complicato al di fuori dei soliti sospetti per N.
JB il

Spiegare, @JB Tricky per misurare?
DavidC

Questa voce dovrebbe indicare "32" perché solo l'approccio di Leibniz soddisfa i requisiti (contando i caratteri nel codice come indicato, ottengo 34, ma entrambi gli spazi possono essere rimossi in modo sicuro, dando effettivamente una lunghezza di 32).
Celtschk,


4

Java (67 caratteri)

float r(){float p=0,s=4,i=1E6f;while(--i>0)p+=(s=-s)/i--;return p;}

Si noti che ciò evita la perdita di significato aggiungendo i numeri nell'ordine corretto.


anche questo è un codice C pienamente conforme. se spedite come C, si potrebbe cambiare while(--i>0)per while(i--)e salvare 2 caratteri
ardnew

1
@ardnew, vero, ma con C ci sono trucchi molto più interessanti da giocare ...
Peter Taylor,

4

Haskell, 32

foldr(\k->(4/(2*k+1)-))0[0..8^7]

GHCi> foldr (\ k -> (4 / (2 * k + 1) -)) 0 [0..8 ^ 7]
3.141593130426724

Conteggio del nome di una funzione è

34

π=foldr(\k->(4/(2*k+1)-))0[0..8^7]


3

C (GCC) (44 caratteri)

float p(i){return i<1E6?4./++i-p(++i):0;}

Sono 41 caratteri, ma devono anche essere compilati -O2 per ottenere l'ottimizzatore per eliminare la ricorsione della coda. Ciò si basa anche su comportamenti indefiniti rispetto all'ordine in cui ++vengono eseguiti; grazie a ugoren per averlo segnalato. Ho provato con gcc 4.4.3 sotto Linux a 64 bit.

Si noti che, a meno che l'ottimizzatore non riordini anche la somma, si aggiungerà dal numero più piccolo, in modo da evitare la perdita di significato.

Chiama come p().


La tua chiamata ricorsiva è q(), no p(). E non penso che -O2debba essere contato (ma se lo conti, sono 4 caratteri a causa dello spazio richiesto).
ugoren,

Inoltre: 1. gcc 4.1.1 non ottimizza la ricorsione (e non vedo come potrebbe), quindi lo stack trabocca. 2. dovrebbe essere chiamato come p(0). 3. Salva un carattere di return++i.... 4. Due ++irende un comportamento indefinito.
ugoren,

@ugoren, grazie per i tuoi commenti. Per: q- questo mi insegnerà a ricontrollare dopo aver rinominato. Penso di seguire la normale pratica contando -O2come 3 caratteri, ma possiamo aprirlo su meta se vuoi; meta.codegolf.stackexchange.com/questions/19 è l'unica discussione pertinente che posso trovare. Ho aggiunto la versione di gcc che sto usando e che mi permette di chiamarlo come p(). Il salvataggio del carattere interrompe l'ottimizzatore e dà segfault. Chiarirò
Peter Taylor,

Ho aggiunto una risposta alla meta domanda sulle bandiere. Informazioni p()- sei sicuro che chiamare p()da qualsiasi contesto funzionerebbe? O è proprio quello che è successo in pila nel tuo test?
ugoren,

@ugoren, forse sono stato fortunato costantemente. Anche se lo chiamo due volte di seguito, il secondo restituisce comunque il valore corretto. gcc sembra produrre un codice leggermente diverso per p()vs p(0), ma non so quale comportamento documenta e non sono davvero un programmatore C.
Peter Taylor,

3

J, 26 caratteri

+ / + / _ 2 ((4 _4) &%)>: +: i.100

Spostato da 100 elementi in sequenza a 1e6 elementi. Inoltre ora è un codice taggato e potrebbe essere copiato dal browser alla console senza errori.

+/+/_2((4 _4)&%)\>:+:i.1e6

3
-/4%>:2*i.1e6- 13 caratteri. (Grazie a b_jonas in #jsoftware per avermi fatto capire che -/funziona per calcolare una somma con segno alternato. [Questo perché tutti gli operatori in J sono di uguale precedenza e associativi a destra, quindi -/ 1 2 3 4<=> 1 - (2 - (3 - 4))<=> 1 - 2 + 3 - 4.])
FireFly

è pulito e due volte fantastico. O anche altri 2 ^ 10 fantastici!
dal

@FireFly che è bello
Giona

2

Javascript - 33 personaggi

p=x=>4*(1-(x&2))/x+(x>1?p(x-2):0)

Chiama ppassando un numero dispari positivo xe calcolerà Pi con (x-1)/2termini.


2

Rubino - 82 caratteri

def f(n,k=n)k>0?(f(n,k-1)+f(n+1,k-1))/2:n<0?0:f(n-1,0)+(-1)**n/(2*n+1.0)end;4*f(9)

Provalo: https://repl.it/LQ8w

L'approccio utilizza indirettamente le serie indicate utilizzando un approccio di accelerazione numerica. L'output risultante è

pi ≈ 3.14159265161

vs.

pi = 3.14159265359

Comincia con

f(n,0) = 1/1 - 1/3 + 1/5 - ... + ((-1)**n)/(2*n+1)

E poi, poiché questo si alterna, possiamo accelerare la convergenza usando

f(n,1) = (f(n,0) + f(n+1,0))/2

E si applica ripetutamente questo:

f(n,k) = (f(n,k-1) + f(n+1,k-1))/2

E per semplicità, f(n) = f(n,n).


Rubino - 50 caratteri

Se non ti dispiace correre per molto tempo, allora potresti semplicemente usare

def f(n)n<0?0:f(n-1)+(-1)**n/(2*n+1.0)end;4*f(1e7)

o

a=0;for k in 0..1e7 do a+=(-1)**k/(2*k+1.0)end;4*a

1

C, 69 caratteri

float p,b;void main(a){b++<9e6?p+=a/b++,main(-a):printf("%f\n",4*p);}
  • Esegui senza parametri della riga di comando (quindi aviene inizializzato su 1).
  • Deve essere compilato con l'ottimizzazione.
  • void mainè strano e non standard, ma fa funzionare le cose. Senza di essa, la ricorsione è implementata come una vera chiamata, portando a un overflow dello stack. Un'alternativa sta aggiungendo return.
  • 4*È possibile salvare due caratteri , se in esecuzione con tre parametri della riga di comando.

Potresti accorciarlo int main(a)o addirittura main(a), GCC dà solo un avvertimento. E darà void maincomunque un avvertimento , e forse anche perché hai solo un argomento da discutere main.
nyuszika7h

1

Clojure - 79 caratteri

(fn [](* 4(apply +(map #(*(Math/pow -1 %1)(/ 1.0(+ 1 %1 %1)))(range 377000)))))

Questo crea una funzione senza argomenti che calcolerà un float che si avvicina pi correttamente a cinque decimali. Si noti che ciò non associa la funzione a un nome come pi, quindi questo codice deve essere valutato in posizione con evalas (<code>)o associato a un nome nel qual caso la soluzione è

(defn p[](* 4(apply +(map #(*(Math/pow -1 %1)(/ 1.0(+ 1 %1 %1)))(range 377000)))))

per 82 caratteri

Di

(defn nth-term-of-pi [n] (* (Math/pow -1 n) (/ 1.0 (+ 1 n n))))
(defn pi [c] (* 4 (apply + (map nth-term-of-pi (range c)))))
(def  pi-accuracy-constant (loop [c 1000] (if (< (pi c) 3.14159) (recur (inc c)) c)))
; (pi pi-accuracy-constant) is then the value of pi to the accuracy of five decimal places

1

PHP - 56 55 caratteri

<?for($j=$i=-1;1e6>$j;){$p+=($i=-$i)/($j+=2);}echo$p*4;

Non so che posso renderlo molto più piccolo senza infrangere la regola dell'algoritmo.


1
Che ne dici di questo per 45? <?for(;1e6>$j;)$p+=($i=-$i|4)/~-$j+=2;echo$p;
primo

Stavo provando a trovarlo, ma non riuscivo a far funzionare le operazioni bit a bit. Grazie per il suggerimento!
TwoScoopsofPig

Puoi rimuovere l'ultimo punto e virgola per salvare 1 carattere.
nyuszika7h

1

Perl - 43 39 caratteri

non sono sicuro delle regole sulle subroutine anonime, ma ecco un'altra implementazione che usa la costruzione della serie di @ FireFly

sub{$s+=8/((4*$_+2)**2-1)for 0..1e6;$s}

sub p{$s+=(-1)**$_*4/(2*$_+1)for 0..1e6;$s}


0

Java - 92 84 caratteri

Non posso battere di gran lunga il risultato di Peter Taylor, ma ecco il mio:

double d(){float n=0,k=0,x;while(n<9E5){x=1/(1+2*n++);k+=(n%2==0)?-x:x;}return 4*k;}

Versione non golfata:

double d() {
    float n = 0, k = 0, x;
    while (n < 9E5) {
        x = 1 / (1 + 2 * n++);
        k += (n % 2 == 0) ? -x : x;
    }
    return 4 * k;
}

Modifica: salvato alcuni caratteri usando l'operatore ternario.


0

Python - 56 caratteri

Meh, il mio pitone-fu non è abbastanza forte. Non vedevo più scorciatoie ma forse un golfista più esperto potrebbe trovare qualcosa da tagliare qui?

t=s=0
k=i=1
while t<1e6:t,s,i,k=t+1,k*4./i+s,i+2,-k

È possibile utilizzare Python 3 per salvare un byte per la divisione float ( 4.-> 4). In altre notizie, ho appena trovato un caso in cui Python 3 batte Python 2 nel code golf!
nyuszika7h

0

Rubino - 54 caratteri

def a()p=0;1000000.times{|i|p+=8/(4*i*(4*i+2))};p;end;

Il mio primo tentativo su console

def a()i=1;p=0;while i<2**100 do p+=8/(i*(i+2));i+=4;end;p;end;

63 caratteri.


È possibile salvare un byte utilizzando def a;invece di def a().
nyuszika7h

Un altro rimuovendo l'ultimo punto e virgola.
nyuszika7h

0

Perl (76 caratteri)

$y=1e4;for$x(0..1e4-1){$y--while sqrt($x**2+$y**2)>1e4;$a+=$y}print 4*$a/1e8

(Risultato: 3.14159052)

Non la soluzione più breve possibile, ma forse interessante. È geometrico. Calcolo l'area sotto un cerchio.

Ho avuto un altro approccio divertente, ma è molto lento. Conta il numero di punti discreti in un quadrato che si trovano sotto un quarto di cerchio e ne calcola il pi:

$i=shift;for$x(0..$i){for$y(0..$i){$h++if sqrt($x**2+$y**2)<$i}}print$h*4/$i**2

Si aspetta il numero di iterazioni come argomento della riga di comando. Qui puoi vedere come il tempo di esecuzione si riferisce alla precisione. ;)

$ time perl -e '$i=shift;for$x(0..$i){for$y(0..$i){$h++if sqrt($x**2+$y**2)<$i}}print$h*4/$i**2' 100
3.1796
real    0m0.011s
user    0m0.005s
sys 0m0.003s

$ time perl -e '$i=shift;for$x(0..$i){for$y(0..$i){$h++if sqrt($x**2+$y**2)<$i}}print$h*4/$i**2' 1000
3.14552
real    0m0.354s
user    0m0.340s
sys 0m0.004s

$ time perl -e '$i=shift;for$x(0..$i){for$y(0..$i){$h++if sqrt($x**2+$y**2)<$i}}print$h*4/$i**2' 10000
3.14199016
real    0m34.941s
user    0m33.757s
sys 0m0.097s

0

k (25 caratteri)

4 * + /% (i # 1 -1) '1 + 2 ! I: 1000000

Leggermente più corto:

+/(i#4 -4)%1+2*!i:1000000

0

Python (49)

print 4*sum((-1)**i/(2*i+1.)for i in range(9**6))
3.14159 453527



0

SQL, 253 byte

DECLARE @B int=3, @A varchar(max), @C varchar(max)='1'
WHILE @B<100000
BEGIN
SELECT @C=@C+(select case when (@B-1)%4=0 then'+'else'-'end)+
(SELECT cast(cast(1.0/@B as decimal(9,8)) as varchar(max)))
SELECT @B=@B+2
END
EXECUTE('SELECT 4*('+@C+')')

Vorrei fornire un SQL Fiddle, ma questo fa troppi loop in profondità trovando le frazioni 1/3 1/5 1/7 ecc. E dà errori lol. Tuttavia, se si @B<100000passa a 1000quindi funziona (ovviamente non allo stesso numero di cifre di precisione).


0

Befunge, 129 byte

p08p109p^v*86%+55:<$$$<
\$\>:#,_@>+\55+/:#^_"."
v>p"~"/:"~"%08p"~"/00p:2\4%-*"(}"
8^90%"~":+2:+g90*+g80*<
>*:**\/+>"~~"00g:"~"`!|

Provalo online!

Nel caso qualcuno si stia chiedendo, è un elefante.

Elefante del fumetto

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.