Calculer π avec convergence quadratique


20

Écrivez une fonction ou un programme complet qui prend un nombre positif net exécute les nétapes d'un algorithme itératif pour calculer π qui a une convergence quadratique (c'est-à-dire qu'il double approximativement le nombre de chiffres précis à chaque itération) puis retourne ou imprime 2 n chiffres corrects (y compris le début 3). Un tel algorithme est l'algorithme de Gauss – Legendre , mais vous êtes libre d'utiliser un algorithme différent si vous préférez.

Exemples:

entrée 1→ sortie 3.1
entrée 2→ sortie 3.141
entrée 5→ sortie3.1415926535897932384626433832795

Exigences:

  • Chaque itération de l'algorithme doit effectuer un nombre constant d'opérations de base telles que l'addition, la soustraction, la multiplication, la division, la puissance et la racine (avec exposant / degré entier) - chacune de ces opérations sur les "grands" nombres entiers / décimaux est comptée comme une paire s'il implique une ou plusieurs boucles en interne. Pour être clair, les fonctions trigonométriques et les pouvoirs impliquant des nombres complexes ne sont pas des opérations de base.
  • L'algorithme devrait avoir une étape d'initialisation qui doit également avoir un nombre constant d'opérations.
  • Si l'algorithme a besoin de 1 ou 2 itérations supplémentaires pour arriver à 2 n chiffres corrects, vous pouvez effectuer jusqu'à n+2itérations au lieu de simplement n.
  • Si ce n'était pas assez clair, après les 2 n chiffres corrects , votre programme ne doit pas imprimer autre chose (comme des chiffres plus corrects, des chiffres incorrects ou les œuvres complètes de Shakespeare).
  • Votre programme doit prendre en charge des valeurs comprises nentre 1 et au moins 20.
  • Votre programme ne devrait pas prendre plus d'une heure pour n= 20 sur un ordinateur moderne (ce n'est pas une règle stricte, mais essayez de le garder raisonnable).
  • Le programme ne doit pas obtenir plus de 20 chiffres précis après l'initialisation et la première itération de l'algorithme.
  • Le programme doit être exécutable sous Linux en utilisant un logiciel disponible gratuitement.
  • Le code source doit utiliser uniquement des caractères ASCII.

Notation:

Golf de code simple, le code le plus court gagne.

Gagnant:

Le gagnant est Digital Trauma, j'ai finalement terminé d'exécuter son code sur n = 20 (je plaisante). Un prix spécial va à primo pour sa solution python très rapide et son algorithme différent :)


1
La convergence quadratique est une erreur ~ N ^ (1/2) . Ce que vous décrivez est une erreur de convergence exponentielle ~ 2 ^ (- N) .
Du

@yo 'vous dites que wikipedia est faux?
aditsu

1
Induire en erreur, du moins pour dire: la "convergence quadratique" est ~q^(n^2)selon la 1ère section là-bas et ~q^2selon la 2ème section là-bas.
Yo '18

1
Je ne comprends pas le codegolf: n'importe qui pourrait sûrement écrire son propre langage de programmation spécifiquement pour une seule tâche comme celle-ci, puis écrire un programme de, disons, 0 octet?
theonlygusti

2
@theonlygusti qui serait une faille standard et serait disqualifié
aditsu

Réponses:


14

dc, 99 octets

Golfé:

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

Avec des espaces et des commentaires pour la "lisibilité":

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

dcdoit être informé du nombre de chiffres de précision à utiliser. La précision de calcul doit être supérieure à la précision d'affichage finale, de sorte que la précision de calcul est définie en 2^(n+1)chiffres. J'ai vérifié l'exactitude de la sortie avec n = 10 contre http://www.angio.net/pi/digits/pi1000000.txt .

Cela ralentit considérablement pour un n plus grand; n = 12 prend 1,5 minutes sur ma machine virtuelle. L'exécution de quelques échantillons différents montre que la complexité temporelle est O (e ^ n) (pas surprenant). L'extrapolation à n = 20 aurait un temps d'exécution de 233 jours. Tant pis. Mieux que la chaleur-mort-de-l'univers au moins.

Ceci est modérément golfé - la pile est utilisée pour éliminer les variables temporaires lors des calculs de chaque itération, mais il est peut-être plus utilisé de la pile pour raccourcir davantage.

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

Si vous n'aimez pas dcencapsuler la sortie à 70 caractères, vous pouvez définir la variable d'environnement DC_LINE_LENGTHsur 0:

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

2
Haha, "lisibilité". Ne s'applique pas vraiment au DC. ;)
Alex A.

Il semble imprimer beaucoup plus de 32 chiffres pour l'entrée 5
aditsu

J'ai ajouté une règle pour cela, plus une autre sur le temps d'exécution (mais pas vraiment stricte). Je n'aime pas non plus la façon dont votre sortie est divisée en plusieurs lignes avec des barres obliques inverses, est-ce une limitation de DC?
aditsu

Je crains que la sortie ne soit mauvaise pour n = 6
aditsu

1
Très bien, et vous l'avez aussi sous 100 :) Pourriez-vous également publier le programme de 99 caractères joué au golf sans espace / commentaires?
aditsu

10

R, 156 octets

Commençons cette fête ... avec l'implémentation la plus naïve absolue de l'algorithme de Gauss-Legendre.

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

Non golfé + explication:

# 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()fonction fait partie du Rmpfrpackage. Il crée unmpfr objet en utilisant le premier argument comme valeur et le deuxième argument comme nombre de bits de précision. Nous attribuons aet pà 1, et en définissant tsur la base a(et bsur la base de t), le mpfrtype se propage aux quatre variables, conservant ainsi la précision tout au long.

Comme mentionné, cela nécessite le package R Rmpfr, qui est l'acronyme de «R Multiple Precision Floating point Reliable». Le package utilise GMP en arrière-plan. Malheureusement, la base R n'a pas la capacité de faire de l'arithmétique de haute précision, d'où la dépendance du package.

Vous n'en avez pas Rmpfr? Pas de transpiration. install.packages("Rmpfr")et tous vos rêves deviendront réalité.

Notez que 2e6la précision a été spécifiée. Cela signifie que nous avons 2 000 000 bits de précision, ce qui est suffisant pour maintenir la précision pendant au moins n= 20. (Remarque: n= 20 prend beaucoup de temps mais moins d'une heure sur mon ordinateur.)

L'approche ici est littéralement juste une régurgitation des formules sur la page Wikipedia, mais bon, nous devons commencer quelque part.

Toute contribution est la bienvenue comme toujours!


J'ai dû réécrire beaucoup de choses mais je dois quand même reconnaître que Peter Taylor m'a aidé à faire tomber 70 octets de mon premier score. Pour reprendre les mots de DigitalTrauma, "boom".


7

Python 2, 214 octets

Ce défi m'a présenté une bonne excuse pour apprendre le module Decimal. Les nombres décimaux ont une précision définissable et prennent en charge la racine carrée. J'ai défini la précision sur une estimation prudente de la précision en fonction du nombre de boucles.

Mise à jour

J'ai mis à jour le programme pour améliorer la précision et la vitesse, au détriment du golf. En utilisant la sqrt()méthode décimale et en remplaçant l' x**2utilisation par x*x, c'est désormais 200 fois plus rapide. Cela signifie qu'il peut désormais calculer 20 boucles donnant un résultat à un million de chiffres en 6,5 heures. Les nombres décimaux ont souvent une erreur dans le dernier chiffre (causée par des opérations sur la limite de précision), donc le programme utilise et supprime maintenant 5 chiffres supplémentaires afin que seuls les chiffres précis soient imprimés.

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]

Exemple d'exécution:

$ 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

Le code non golfé:

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

Il semble que vous n'imprimiez pas le nombre correct de chiffres. Et je me demande si la lenteur est due à l'utilisation inutile de **.
aditsu

1
@aditsu, j'ai réduit la précision au nombre de chiffres escompté (mais le fait de jeter une très bonne précision à partir d'une itération me démange les dents). Bonne suggestion sur l' **effet. J'ai trouvé beaucoup de vitesse en m'en débarrassant. Je ne peux cependant pas rencontrer les 20 boucles en 1 heure. Peut-être avec pypy ou Cython? Hmmm. J'y réfléchirai.
Logic Knight

Beaucoup mieux :) Pour ce problème, jeter une bonne précision est moins mal que de continuer dans une mauvaise précision. La limite d'une heure est basée sur mon code de test cjam / java exécuté avec java 8. Peut-être que python n'a pas de multiplication / division / sqrt efficace pour les grands nombres (Karatsuba & co)?
aditsu

@aditsu: Je crois que les entiers ont karatsuba (et juste cela) - mais avec une taille de membre 32 bits plutôt qu'une taille de membre 64 bits. Qui connaît Decimal.

5

Python (2.7) - 131 octets

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]

Mise à jour: utilise maintenant gmpyplutôt que gmpy2. Pour une raison quelconque, en gmpy2définissant la précision sur une seule valeur ne se propage pas à d'autres valeurs. Le résultat de tout calcul revient à la précision du contexte actuel. La précision se propage gmpy, ce qui me semble plus intuitif. C'est aussi beaucoup moins verbeux.

En utilisant l'un des nombreux algorithmes conçus par Borwein et Borwein , légèrement refactorisé. n = 20 prend environ 11 secondes sur ma boîte. Pas la méthode la plus efficace, mais toujours pas mal.


Refactoring

L'algorithme d'origine était le suivant:




Le refactoring a été effectué de manière incrémentielle, mais le résultat final est que




La simplification majeure se produit en p n + 1 . C'est aussi un peu plus rapide en raison de l'élimination d'une division.

L'implémentation actuelle pousse un n avant une itération dans le calcul de p n + 1 , ce qui permet une initialisation différente de p 0 ( 2√2 ), mais est par ailleurs identique.


Exemple d'utilisation

$ 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

Génial, mais il vous manque un chiffre pour n = 7
aditsu

Est-ce aussi cet algorithme ?
aditsu

@aditsu corrigé, et oui c'est le cas.
primo

Maintenant, le dernier chiffre est faux pour n = 5
aditsu

1
@aditsu a pip install gmpyfonctionné pour moi; gmpyet gmpy2sont des packages séparés. Cependant, il s'appuie sur les obsolètes distutils.
primo

3

bc et méthode de Newton, 43 octets

La méthode de Newton pour trouver des zéros de n'importe quelle fonction converge quadratique et l'algorithme est beaucoup plus simple que pour Gauss-Legendre. Cela se résume essentiellement à:

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

Voici donc l'extrait suivant:

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

Un peu plus lisible:

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

Pour tester cela, exécutez bc -ldans un shell et collez l'extrait ci-dessus. Soyez prêt à attendre un moment; n=20fonctionne depuis environ 5 minutes et aucune fin n'est encore en vue. n=10prend environ 40s.


4
Je ne sais pas si le sinus et le cosinus sont qualifiés d ' "opérations de base telles que l'addition, la soustraction, la multiplication, la division et la puissance (y compris les racines)" . Cependant, si vous rouliez votre propre sinus / cosinus, ce serait probablement acceptable.
primo

1
Formule soignée, cependant - il dit que π est un point fixe de f (x) = x - tan (x)
Casey Chu
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.