Calculez pi à 5 décimales


15

Cela vient de http://programmers.blogoverflow.com/2012/08/20-controversial-programming-opinions/

"Étant donné que Pi peut être estimé en utilisant la fonction 4 * (1 - 1/3 + 1/5 - 1/7 +…) avec plus de termes donnant une plus grande précision, écrivez une fonction qui calcule Pi avec une précision de 5 décimales. "

  • Remarque, l'estimation doit être effectuée en calculant la séquence donnée ci-dessus.

8
Vous devriez probablement ajouter quelques règles supplémentaires, sinon vous obtiendrez des réponses comme (python)p=lambda:3.14159
Matt

1
Avez-vous vu codegolf.stackexchange.com/questions/506/… , qui est très similaire? À tout le moins, les fonctions trigonométriques devraient être interdites pour ce problème car elles permettent des solutions triviales telles que ce programme QBASIC:? INT (4E5 * ATN (1)) / 1E5
PleaseStand

Je pense que vous devez exiger que l'algorithme soit une approximation successive: plus vous calculez, plus vous vous rapprochez de pi.
DavidC

@DavidCarraher, bien que cela soit mathématiquement inévitable en utilisant cette série, d'un point de vue analytique numérique, c'est très douteux. Une série alternée à convergence lente est un enfant de l'affiche pour la perte de signification.
Peter Taylor

2
Dupe, mais il est tellement vieux qu'il n'est pas là: stackoverflow.com/q/407518/12274
JB

Réponses:


10

JavaScript, 46 58 56 45 octets

Mise à jour ES6 : Il s'avère que nous disposons de plus de fonctionnalités maintenant que cinq ans se sont écoulés.

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

Cette version ( 45 octets; oui, letc'est obligatoire) fonctionne en mode strict ES6 en théorie . En pratique, vous pouvez l'exécuter en V8 (par exemple avec un nœud) avec --use-strict --harmony-tailcalls; la fonction Propre Tailcalls n'est pas encore largement implémentée, hélas. Cependant, c'est un comportement spécifié, donc ça devrait aller.

Si nous voulons nous en tenir à ce qui est largement implémenté et ne pas nécessiter de mode strict, nous pouvons simplement utiliser la syntaxe de la flèche fatale ES6 pour les fonctions, mais sinon conserver la même implémentation qu'auparavant (suggérée par Brian H) pour un coût de 48 octets.

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

Le choix du nom pour le paramètre unique n'a pas vraiment d' importance, mais nous pourrions aussi bien choisir l'un des noms que nous utilisons afin de minimiser la pollution de portée mondiale.


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

Cette version est une expression de fonction; ajoutez deux caractères (par exemple " f") si vous voulez qu'il soit nommé. Cette version saisit les mondiaux aet i; cela pourrait être évité si nous ajoutions " a,i" à la liste des paramètres.

Utilise une version reformulée de l'algorithme afin de contourner le besoin de soustraction.

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

Voici une version "simple" sans cet ajustement:

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

qui horloge à 64 62 caractères.

Merci à @ardnew pour la suggestion de se débarrasser de l' 4*avant avant le return.


Histoire

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 très beau travail, prenant en compte la soustraction.
acolyte

1
grand travail, mais doit être écrit comme une fonction appropriée
ardnew

@ardnew: Merci, j'ai dû manquer ce détail lorsque j'ai lu la description du problème. Je l'ai mis à jour et c'est maintenant une expression de fonction appelable (lambda); Je ne sais pas si cela est autorisé ou s'il faut lui donner un nom. Si tel est le cas, il s'agit tout de même de deux caractères supplémentaires.
FireFly

1
@FireFly vous pouvez également raser 2 caractères en changeant a+=2/i/-~-~i;return 4*aena+=8/i/-~-~i;return a
ardnew

@ardnew: oh, génial; n'y a pas pensé. : D
FireFly

8

Python 59 octets

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

Cela imprime 1000 chiffres; légèrement plus que le nombre requis 5. Au lieu d'utiliser l'itération prescrite, il utilise ceci:

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

Le 6637(dénominateur le plus intérieur) peut être formulé comme:

chiffres * 2 * log 2 (10)

Cela implique une convergence linéaire. Chaque itération plus profonde produira un bit binaire supplémentaire de pi .

Si , cependant, vous insistez pour utiliser l'identité tan -1 , une convergence similaire peut être obtenue, si cela ne vous dérange pas de traiter le problème légèrement différemment. Jetons un œil aux sommes partielles:

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

il est évident que chaque terme saute d'avant en arrière de chaque côté du point de convergence; la série a une convergence alternée. De plus, chaque terme est plus proche du point de convergence que le terme précédent. il est absolument monotone par rapport à son point de convergence. La combinaison de ces deux propriétés implique que la moyenne arithmétique de deux termes voisins est plus proche du point de convergence que l'un ou l'autre des termes eux-mêmes. Pour vous donner une meilleure idée de ce que je veux dire, considérez l'image suivante:

Partial Sums

La série externe est l'original, et la série interne est trouvée en prenant la moyenne de chacun des termes voisins. Une différence remarquable. Mais ce qui est vraiment remarquable, c'est que cette nouvelle série a également une convergence alternée, et est absolument monotone par rapport à son point de convergence. Cela signifie que ce processus peut être appliqué à maintes reprises, ad nauseum.

D'accord. Mais comment?

Quelques définitions formelles. Soit P 1 (n) le n ème terme de la première séquence, P 2 (n) le n ème terme de la deuxième séquence, et de même P k (n) le n ème terme de la k ème séquence tel que défini ci-dessus .

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, ...]

Sans surprise, ces coefficients suivent exactement les coefficients binomiaux et peuvent être exprimés comme une seule ligne du triangle de Pascal. Puisqu'une ligne arbitraire du triangle de Pascal est triviale à calculer, une série arbitrairement «profonde» peut être trouvée, simplement en prenant les n premières sommes partielles, multipliez chacune par le terme correspondant dans la k ème ligne du triangle de Pascal et en divisant par 2 k-1 .

De cette façon, une précision complète en virgule flottante 32 bits (~ 14 décimales) peut être obtenue avec seulement 36 itérations, point auquel les sommes partielles n'ont même pas convergé sur la deuxième décimale. Ce n'est évidemment pas joué au golf:

# 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

Si vous vouliez une précision arbitraire, cela peut être obtenu avec une petite modification. Ici encore, calcul de 1000 chiffres:

# 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

La valeur initiale de p commence 2 10 plus grande, pour contrer les effets de division entière de s / d lorsque d devient plus grand, ce qui fait que les derniers chiffres ne convergent pas. Remarquez ici encore que 3318c'est aussi:

chiffres * log 2 (10)

Le même nombre d'itérations que le premier algorithme (réduit de moitié car t diminue de 1 au lieu de 2 à chaque itération). Encore une fois, cela indique une convergence linéaire: un bit binaire de pi par itération. Dans les deux cas, 3318 itérations sont nécessaires pour calculer 1000 chiffres de pi , soit un quota légèrement meilleur que 1 million d'itérations pour en calculer 5.


C'est bien mieux que ma solution: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
Ceci est très similaire à mon approche , qui se trouve être une forme différente de la vôtre. Dans le mien, as k → ∞, f(-1,k)s'approche de votre somme d'Euler.
Simply Beautiful Art

1
Très cool; analyse et explication impressionnantes, merci.
jeremy radcliff

Juste une petite chose. Ne vouliez-vous pas dire après le P_1 = ..., P_2 = ..., P_3 = ..., P_4 = ...", ... multipliez chacun par le terme correspondant dans la kthligne du triangle de Pascal et en divisant par 2^{k-1}", au lieu de la nthligne et 2^{n-1}?.
jeremy radcliff

@ jeremyradcliff Je l'ai fait, oui. Merci pour la correction.
primo

5

Mathematica 42 39 34 33 31 26 32

Approche d'Archimède 26 caractères

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

Cela atteint le critère lorsque l'entrée est 822.

Question: Quelqu'un sait-il comment il a calculé le péché de 180 degrés? Je ne.


Approche de Leibniz (série de Gregory) 32 caractères

C'est la même fonction que le poseur de problèmes a donné comme exemple. Il atteint le critère en environ un demi-million d'itérations.

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

Approche Madhava-Leibniz 37 caractères

Cette variation utilise quelques caractères supplémentaires mais converge vers le critère en seulement 9 itérations!

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

tous les calculent par l'algorithme donné dans la définition du problème?
acolyte du

L'approche de @acolyte Leibniz (désormais la première répertoriée) est en effet celle mentionnée dans la description du problème. C'est très lent à converger. Une légère variation (Madhava-Leibniz) converge très rapidement.
DavidC

Le sinus de 180 ° est assez facile. C'est 180 ° / N qui peut devenir délicat en dehors des suspects habituels pour N.
JB

Veuillez expliquer, @JB Tricky sur mesure?
DavidC

Cette entrée doit indiquer "32" car seule l'approche de Leibniz remplit les conditions (en comptant les caractères dans le code comme indiqué, j'obtiens 34, mais les deux espaces peuvent être supprimés en toute sécurité, ce qui donne effectivement une longueur de 32).
celtschk


4

Java (67 caractères)

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

Notez que cela évite la perte de signification en additionnant les nombres dans le bon ordre.


il s'agit également d'un code C entièrement conforme. si elle est affichée en C, vous pouvez changer while(--i>0)pour while(i--)et enregistrer 2 caractères
ardnew

1
@ardnew, c'est vrai, mais avec C il y a des trucs beaucoup plus intéressants à jouer ...
Peter Taylor



3

C (GCC) (44 caractères)

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

Cela fait 41 caractères, mais il doit également être compilé avec -O2pour que l'optimiseur élimine la récursivité de la queue. Cela repose également sur un comportement indéfini par rapport à l'ordre dans lequel les ++sont exécutés; merci à ugoren de l'avoir signalé. J'ai testé avec gcc 4.4.3 sous Linux 64 bits.

Notez qu'à moins que l'optimiseur ne réordonne également la somme, il ajoutera à partir du plus petit nombre, donc il évite la perte de signification.

Appelez le p().


Votre appel récursif ne l'est q()pas p(). Et je ne pense pas que cela -O2devrait être compté (mais si vous le comptez, c'est 4 caractères en raison de l'espace requis).
ugoren

Aussi: 1. gcc 4.1.1 n'optimise pas la récursivité (et je ne vois pas comment cela pourrait), donc la pile déborde. 2. il doit être appelé comme p(0). 3. Enregistrez un caractère par return++i.... 4. Deux ++irend le comportement indéfini.
ugoren

@ugoren, merci pour vos commentaires. Dans l'ordre: q- cela m'apprendra à revérifier après avoir renommé. Je pense que je suis la pratique normale de compter -O2comme 3 caractères, mais nous pouvons l'ouvrir sur méta si vous voulez; meta.codegolf.stackexchange.com/questions/19 est la seule discussion pertinente que je puisse trouver. J'ai ajouté la version de gcc que j'utilise et qui me permet de l'appeler comme p(). L'enregistrement du caractère arrête l'optimiseur et donne une erreur de segmentation. Je préciserai que j'utilise un comportement non défini, selon meta.codegolf.stackexchange.com/questions/21
Peter Taylor

J'ai ajouté une réponse à la méta-question sur les drapeaux. À propos p()- êtes-vous sûr que l'appel à p()partir de n'importe quel contexte fonctionnerait? Ou est-ce juste ce qui se trouvait sur la pile lors de votre test?
ugoren

@ugoren, j'ai peut-être toujours eu de la chance. Même si je l'appelle deux fois de suite, la seconde renvoie toujours la valeur correcte. gcc semble produire du code légèrement différent pour p()vs p(0), mais je ne sais pas quel comportement il documente et je ne suis pas vraiment un programmeur C.
Peter Taylor

3

J, 26 caractères

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

Passé de 100 éléments de séquence à 1e6 éléments. De plus, il s'agit maintenant d'un code balisé et peut être copypassé du navigateur vers la console sans erreur.

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

3
-/4%>:2*i.1e6- 13 caractères. (Merci à b_jonas dans #jsoftware de m'avoir fait réaliser que cela -/fonctionne pour calculer une somme avec un signe alterné. [C'est parce que tous les opérateurs de J sont de priorité égale et associatifs à droite, donc -/ 1 2 3 4<=>1 - (2 - (3 - 4)) <=> 1 - 2 + 3 - 4.])
FireFly

c'est soigné et deux fois plus génial. Ou même 2 ^ 10 plus génial!
fftw

@FireFly c'est beau
Jonah

2

Javascript - 33 caractères

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

Appelez en ppassant un nombre impair positif xet il calculera Pi avec des (x-1)/2termes.


2

Rubis - 82 caractères

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)

Essayez-le: https://repl.it/LQ8w

L'approche utilise indirectement la série donnée en utilisant une approche d'accélération numérique. La sortie résultante est

pi ≈ 3.14159265161

contre.

pi = 3.14159265359

Ça commence par

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

Et puis, puisque c'est alternatif, nous pouvons accélérer la convergence en utilisant

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

Et cela s'applique à plusieurs reprises:

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

Et pour plus de simplicité, f(n) = f(n,n) .


Rubis - 50 caractères

Si cela ne vous dérange pas de courir très longtemps, vous pouvez simplement utiliser

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

ou

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

1

C, 69 caractères

float p,b;void main(a){b++<9e6?p+=a/b++,main(-a):printf("%f\n",4*p);}
  • Exécuter sans paramètres de ligne de commande (donc a est initialisé à 1).
  • Doit être compilé avec optimisation.
  • void mainest étrange et non standard, mais fait fonctionner les choses. Sans cela, la récursivité est implémentée comme un véritable appel, conduisant à un débordement de pile. Une alternative est d'ajouter return.
  • Deux caractères 4*peuvent être enregistrés s'ils s'exécutent avec trois paramètres de ligne de commande.

Vous pouvez raccourcir cela à int main(a)ou même main(a), GCC ne donne qu'un avertissement. Et cela donnera un avertissement de void maintoute façon, et peut-être même parce que vous n'avez qu'un seul argument main.
nyuszika7h

1

Clojure - 79 caractères

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

Cela crée une fonction sans arguments qui calculera un flottant qui se rapproche de pi correctement à cinq décimales. Notez que cela ne lie pas la fonction à un nom tel que pi, donc ce code doit être évalué en place avec evalas (<code>)ou lié à un nom auquel cas la solution est

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

pour 82 caractères

Sur

(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 caractères

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

Je ne sais pas si je peux l'obtenir beaucoup plus petit sans enfreindre la règle de l'algorithme.


1
Et pour 45 ans? <?for(;1e6>$j;)$p+=($i=-$i|4)/~-$j+=2;echo$p;
primo

J'essayais de trouver cela, mais je n'ai pas réussi à faire fonctionner les opérations au niveau du bit. Merci pour la suggestion!
TwoScoopsofPig

Vous pouvez supprimer le dernier point-virgule pour enregistrer 1 caractère.
nyuszika7h

1

Perl - 43 39 caractères

pas sûr des règles sur les sous-programmes anonymes, mais voici une autre implémentation utilisant la construction en série de @ 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 caractères

Je ne peux pas battre de loin le résultat de Peter Taylor, mais voici le mien:

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

Version non golfée:

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

Modifier: enregistré quelques caractères à l'aide de l'opérateur ternaire.


0

Python - 56 caractères

Meh, Mon python-fu n'est pas assez fort. Je ne pouvais pas voir plus de raccourcis, mais peut-être qu'un golfeur plus expérimenté pourrait trouver quelque chose à couper ici?

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

Vous pouvez utiliser Python 3 pour enregistrer un octet pour la division flottante ( 4.-> 4). Dans d'autres nouvelles, je viens de trouver un cas où Python 3 bat réellement Python 2 dans le golf de code!
nyuszika7h

0

Rubis - 54 caractères

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

Mon premier essai sur console

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

63 caractères.


Vous pouvez enregistrer un octet en utilisant def a;au lieu de def a().
nyuszika7h

Un autre en supprimant le dernier point-virgule.
nyuszika7h

0

Perl (76 caractères)

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

(Résultat: 3.14159052)

Pas la solution la plus courte possible, mais peut-être intéressante. C'est géométrique. Je calcule l'aire sous un cercle.

J'ai une autre approche amusante, mais c'est vraiment lent. Il compte le nombre de points discrets dans un carré qui sont en dessous d'un quart de cercle et calcule pi à partir de celui-ci:

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

Il attend le nombre d'itérations comme argument de ligne de commande. Ici, vous pouvez voir comment le temps d'exécution est lié à la précision. ;)

$ 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 caractères)

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

Légèrement plus court:

+/(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 octets

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

Je fournirais un SQL Fiddle, mais cela va trop de boucles pour trouver les fractions 1/3 1/5 1/7 etc. et donne des erreurs lol. Cependant, si vous passez @B<100000à 1000alors il s'exécute (évidemment pas au même nombre de chiffres de précision).


0

Befunge, 129 octets

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

Essayez-le en ligne!

Au cas où quelqu'un se demanderait, c'est un éléphant.

Éléphant de dessin animé

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.