Buone approssimazioni razionali di pi


22

Scrivi un programma che stampa tutte le buone approssimazioni razionali di pi con denominatore <1000000, in ordine crescente di denominatore. a/bè una "buona approssimazione razionale" di pi se è più vicina a pi di qualsiasi altra razionale con denominatore non più grande di b.

L'output dovrebbe avere 167 righe totali e iniziare e finire in questo modo:

3/1
13/4
16/5
19/6
22/7
179/57
...
833719/265381
1146408/364913
3126535/995207

Vince il programma più breve.

Risposte:


23

Golfscript, 71 70 69 caratteri

2\!:^2^..292^15.2/3]{(.)2/.9>+{\+.((}*;.}do;;]-1%{^0@{2$*+\}/"/"\n}/;

(Suppone che non passi nulla su stdin)

Non voglio più sentire lamentele da parte di persone che non hanno costanti integrate per pi. Non ho nemmeno numeri in virgola mobile!

Vedi http://en.wikipedia.org/wiki/Continued_fraction#Best_rational_approximations per lo sfondo.

# No input, so the stack contains ""
2\!:^2^..292^15.2/3]
# ^ is used to store 1 because that saves a char by allowing the elimination of whitespace
# Otherwise straightforward: stack now contains [2 1 2 1 1 1 292 1 15 7 3]
# Pi as a continued fraction is 3+1/(7+1/(15+1/(...)))
# If you reverse the array now on the stack you get the first 10 continuants followed by 2
# (rather than 3)
# That's a little hack to avoid passing the denominator 1000000

{
    # Stack holds: ... [c_n c_{n-1} ... c_0]
    (.)2/.9>+
    # Stack holds ... [c_{n-1} ... c_0] c_n (1+c_n)/2+((1+c_n)/2 > 9 ? 1 : 0)
    # (1+c_n)/2 > 9 is an ad-hoc approximation of the "half rule"
    # which works in this case but not in general
    # Let k = (1+c_n)/2+((1+c_n)/2 > 9 ? 1 : 0)
    # We execute the next block k times
    {
        # ... [c_{n-1} ... c_0] z
        \+.((
        # ... [z c_{n-1} ... c_0] [c_{n-1} ... c_0] z-1
    }*
    # So we now have ... [c_n c_{n-1} ... c_0] [(c_n)-1 c_{n-1} ... c_0] ...
    #                    [(c_n)-k+1 c_{n-1} ... c_0] [c_{n-1} ... c_0] c_n-k
    ;
    # Go round the loop until the array runs out
    .
}do

# Stack now contains all the solutions as CFs in reverse order, plus two surplus:
# [2 1 2 1 1 1 292 1 15 7 3] [1 2 1 1 1 292 1 15 7 3] ... [6 3] [5 3] [4 3] [3] [2] []
# Ditch the two surplus ones, bundle everything up in an array, and reverse it
;;]-1%

# For each CF...
{
    # Stack holds ... [(c_n)-j c_{n-1} ... c_0]
    # We now need to convert the CF into a rational in canonical form
    # We unwind from the inside out starting with (c_n)-j + 1/infinity,
    # representing infinity as 1/0
    ^0@
    # ... 1 0 [c_n-j c_{n-1} ... c_0]
    # Loop over the terms of the CF
    {
        # ... numerator denominator term-of-CF
        2$*+\
        # ... (term-of-CF * numerator + denominator) numerator
    }/

    # Presentation
    "/"\n
    # ... numerator "/" denominator newline
}/

# Pop that final newline to avoid a trailing blank line which isn't in the spec
;

1
Bene, tecnicamente GolfScript ha sia numeri in virgola mobile che costante per PI. Si chiama "#{Math.PI}".
Konrad Borowski,

2
@GlitchMr, in che modo una stringa è un numero in virgola mobile?
Peter Taylor,

Mi piacerebbe davvero vederlo srotolato con commenti.
primo

Stupefacente. La prima riga mi ha 2\!:^2^..292^15.2/3]già fatto impazzire.
primo

@PeterTaylor Tied . Possiamo fare di meglio?
Eelvex,

11

Mathematica, 67 63

Questo non sarà veloce, ma credo che sia tecnicamente corretto.

Round[π,1/Range@1*^6]//.x_:>First/@Split[x,#2≥#&@@Abs[π-{##}]&]

Round[π, x]dà la frazione più vicina a π in passi di x. Questo è "elencabile", quindi lo Round[π,1/Range@1*^6]fa per tutte le frazioni 1/10^6in ordine. L'elenco risultante con molte approssimazioni "cattive" razionali viene quindi ripetutamente ( //.) elaborato rimuovendo tutti gli elementi che sono più lontani da π di quello precedente.


Abbastanza bello, ma non posso provarlo perché non ho Mathematica.
Keith Randall,

@Keith, ecco la logica. Round[Pi, x]dà la frazione più vicina a Piin passi di x. Questo è "elencabile", quindi lo Round[Pi,1/Range@1*^6]fa per tutte le frazioni fino a 1/10 ^ 6 in ordine. L'elenco risultante con molte approssimazioni "cattive" razionali viene quindi ripetutamente ( //.) rimosso rimuovendo tutti gli elementi che sono più lontani da pi di quello precedente.
Mr.Wizard,

Mathematica batte GolfScript. Neat.
Ortografia,

Nel 61: Select[Round[f=Pi,1/Range@1*^6],If[#<f,f=#;True]&@Abs[#-Pi]&]... ma inutile dato il pregiudizio dominante
Dr. belisarius,

Yarr, Matie. Sii magico in questo codice.
Michael Stern,

7

Perl, 77 caratteri

$e=$p=atan2 0,-1;($f=abs$p-($==$p*$_+.5)/$_)<$e&&($e=$f,say"$=/$_")for 1..1e6

Una piccola sfida è che Perl non ha una costante π integrata disponibile, quindi per prima cosa ho dovuto calcolarla come atan2(0,-1). Sono sicuro che questo sarà battuto dalle lingue più adatte al lavoro, ma non è male per una lingua progettata principalmente per l'elaborazione del testo.


1
Si potrebbe cambiare 999999per 1e6e salvare 3 caratteri.
Toto

@ M42: grazie! Fino a 82 caratteri ora.
Ilmari Karonen,

Davvero bello, $ = per ottenere numeri interi. Mi dispiace, non posso votare due volte.
Tot

Non riesco a farlo funzionare:String found where operator expected at prog.pl line 1, near "say"$=/$_""
Keith Randall il

@KeithRandall: è necessario l'opzione -M5.01(e Perl 5.10.0 o successive) per il saycomando. Ci scusiamo per non averlo menzionato.
Ilmari Karonen,

5

Python, 96 93 89 caratteri

a=b=d=1.
while b<=1e6:
 e=3.14159265359-a/b;x=abs(e)
 if x<d:print a,b;d=x
 a+=e>0;b+=e<0

Python, 95 93 caratteri, algoritmo diverso

p=3.14159265359;d=1
for a in range(3,p*1e6):
 b=round(a/p);e=abs(p-a/b)
 if e<d:print a,b;d=e

nota: scrivere meno caratteri p=3.14159265359;di from math import*. Accidenti a quelle importazioni prolifiche!


1
Alcuni accorciamenti: 1.0-> 1., 10**6->1e6
Keith Randall il

Ho aggiornato con i tuoi miglioramenti. Grazie mille
Steven Rumbalski il

@KeithRandall, ma il secondo di questi fa sì che l'output violi le specifiche.
Peter Taylor,

Nel secondo approccio non è necessario per la variabile p. Sono 4 caratteri.
Ante,

@PeterTaylor: non capisco. In che modo viola le specifiche?
Steven Rumbalski l'

4

JS (95 caratteri)

for(i=k=1,m=Math;i<1e6;i++)if((j=m.abs((x=m.round(m.PI*i))/i-m.PI))<k)k=j,console.log(x+'/'+i)

Stampa 167 righe.


4

Ruby 1.9, 84 caratteri

m=1;(1..1e6).map{|d|n=(d*q=Math::PI).round;k=(n-q*d).abs/d;k<m&&(m=k;puts [n,d]*?/)}

@Peter Taylor Hai ragione. Devi usare Ruby 1.9.
Howard,

4

C99, 113 caratteri

main(d,n){double e=9,p=2*asin(1),c,a=1;for(;n=d*p+.5,c=fabsl(p-a*n/d),d<1e6;++d)c<e&&printf("%d/%d\n",n,d,e=c);}

-lmHo bisogno di compilare , e probabilmente pieno di comportamento indefinito, ma funziona per me.


2

Scala - 180 caratteri

import math._
def p(z:Int,n:Int,s:Double):Unit=
if(n==1e6)0 else{val q=1.0*z/n
val x=if(abs(Pi-q)<s){println(z+"/"+n)
abs(Pi-q)}else s
if(Pi-q<0)p(z,n+1,x)else p(z+1,n,x)}
p(3,1,1)

// non golfato: 457

val pi=math.Pi
@annotation.tailrec
def toPi (zaehler: Int = 3, nenner: Int = 1, sofar: Double=1): Unit = {
  if (nenner == 1000000) () 
  else {
    val quotient = 1.0*zaehler/nenner
    val diff = (pi - quotient)
    val adiff= math.abs (diff)
    val next = if (adiff < sofar) {
      println (zaehler + "/" + nenner) 
      adiff 
    }
    else sofar
    if (diff < 0) toPi (zaehler, nenner + 1, next) 
    else toPi (zaehler + 1, nenner, next) 
  }  
}

L'annotazione tailrec è solo un controllo, per verificare, che è ricorsivo di coda, che spesso è un miglioramento delle prestazioni.


Non riesco a farlo funzionare:pi.scala:1 error: not found: value math
Keith Randall il

Stai usando Scala 2.8?
Utente sconosciuto il

La mia scala dice "versione sconosciuta", strana. Su ideone.com usano 2.8.0 e continuo a ricevere errori.
Keith Randall,

Provalo su simplyscala.com - funziona per me. Per scala-2.8, la sostituzione mathcon Mathpotrebbe essere sufficiente. Ho citato semplicementescala su questo metatread, se ti capita di cercarlo di nuovo: meta.codegolf.stackexchange.com/a/401/373
utente sconosciuto

OK, funziona.
Keith Randall,

2

Mathematica 18 17 caratteri

Ho scelto di usare, come misura di "migliore", il numero di termini in una rappresentazione di frazione continua di π. Secondo questo criterio, le migliori approssimazioni razionali di π sono i suoi convergenti.

Esistono 10 convergenti di π con un denominatore inferiore a un milione. Questo è inferiore ai 167 termini richiesti, ma lo includo qui perché potrebbe interessare gli altri.

Convergents[π, 10] 

(* out *)
{3, 22/7, 333/106, 355/113, 103993/33102, 104348/33215, 208341/66317,
312689/99532, 833719/265381, 1146408/364913}

Se vuoi davvero vedere il denominatore per il primo convergente, costerà altri 11 caratteri:

Convergents[π, 10] /. {3 -> "3/1"}
(* out *)
{"3/1", 22/7, 333/106, 355/113, 103993/33102, 104348/33215,
208341/66317, 312689/99532, 833719/265381, 1146408/364913}

Per coloro che sono interessati, quanto segue mostra le relazioni tra i convergenti, i quozienti parziali e l'espressione di frazione continua di convergenti di π:

Table[ContinuedFraction[π, k], {k, 10}]
w[frac_] := Row[{Fold[(#1^-1 + #2) &, Last[#], Rest[Reverse[#]]] &[Text@Style[#, Blue, Bold, 14] & /@ ToString /@ ContinuedFraction[frac]]}];
w /@ FromContinuedFraction /@ ContinuedFraction /@ Convergents[π, 10]

continued fractions

Si prega di scusare la formattazione incoerente delle frazioni continue.


È a metà strada verso una soluzione, ma è la metà più semplice. La mia soluzione GolfScript codifica in modo rigido una rappresentazione adeguata della frazione continua in soli 2 caratteri in più.
Peter Taylor,

Ma non hai usato le frazioni continue per la tua soluzione a questa domanda, vero?
DavidC

Sì. Era il modo ovvio per farlo.
Peter Taylor,

Oltre ad essere conciso, questo è molto più veloce della maggior parte o di tutte le altre soluzioni che sono state pubblicate.
Michael Stern,

1

C # 140 129 caratteri

double n=3,d=1,e=d;while(n<4e5){double w=n/d-Math.PI,a=Math.Abs(w);if(a<e){e=a;Console.WriteLine(n+"/"+d);}if(w>0)d++;else n++;}

Codice non compresso

var numerator = 3d;
var denominator = 1d;
var delta = 4d;
while (numerator < 4e5) 
{
    var newDelta = (numerator / denominator) - Math.PI;
    var absNewDelta = Math.Abs(newDelta);
    if (absNewDelta < delta)
    {
        delta = absNewDelta;
        Console.WriteLine(string.Format("{0}/{1}", numerator, denominator));
    }

    if (newDelta > 0)
    {
        denominator++;
    }
    else
    {
        numerator++;
    }
}

2
varnon è sempre tuo amico. Rimuovendolo in favore di doublete, ottieni la possibilità di unire le dichiarazioni, perdere il requisito di usare doppi letterali e puoi risparmiare 16 caratteri. OTOH la domanda richiede un programma, quindi perderai alcuni aggiungendo una dichiarazione di classe e un Mainmetodo.
Peter Taylor,

1

J, 69 65

Nuovo

]`,@.(<&j{.)/({~(i.<./)@j=.|@-l)@(%~(i:3x)+<.@*l=.1p1&)"0>:_i.1e3

Ancora un approccio a forza bruta ma molto più veloce e un po 'più breve.

Vecchio

Una semplice "forza bruta":

(#~({:<<./@}:)\@j)({~(i.<./)@j=.|@-l)@(%~(i:6x)+<.@*l=.1p1&)"0>:i.1e3

fai un elenco di se a/bpoi scarta quelli che sono più lontani da π per alcuni b'<b.

Nota: passare 1e3a 1e6per l'elenco completo. Vai a fare qualcos'altro e ritorna più tardi.

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.