Buenas aproximaciones racionales de pi

22

Escriba un programa que imprima todas las buenas aproximaciones racionales de pi con denominador <1000000, en orden de denominador creciente. a/bes una "buena aproximación racional" de pi si está más cerca de pi que cualquier otra racional con un denominador no mayor que b.

La salida debe tener 167 líneas en total, y comenzar y terminar así:

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

El programa más corto gana.

Keith Randall
fuente

Respuestas:

23

Golfscript, 71 70 69 caracteres

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

(Asume que no le pasa nada en stdin)

No quiero escuchar más quejas de personas que no tienen constantes incorporadas para pi. ¡Ni siquiera tengo números de coma flotante!

Ver http://en.wikipedia.org/wiki/Continued_fraction#Best_rational_approximations para el fondo.

# 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
;
Peter Taylor
fuente
1
Bueno, técnicamente GolfScript tiene números de coma flotante y constantes para PI. Se llama "#{Math.PI}".
Konrad Borowski,
2
@ GlitchMr, ¿de qué manera una cadena es un número de coma flotante?
Peter Taylor
Realmente me gustaría ver esto desenrollado con comentarios.
primo
Asombroso. La primera línea 2\!:^2^..292^15.2/3]ya me dejó alucinado.
primo
@PeterTaylor Atado . ¿Podemos hacerlo mejor?
Eelvex
11

Mathematica, 67 63

Esto no va a ser rápido, pero creo que es técnicamente correcto.

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

Round[π, x]da la fracción más cercana a π en pasos de x. Esto es "listable", lo mismo Round[π,1/Range@1*^6]ocurre con todas las fracciones 1/10^6en orden. La lista resultante con muchas aproximaciones racionales "malas" se //.procesa repetidamente ( ) eliminando cualquier elemento que esté más lejos de π que el anterior.

Señor mago
fuente
Bastante bueno, pero no puedo probarlo porque no tengo Mathematica.
Keith Randall
@Keith, aquí está la lógica. Round[Pi, x]da la fracción más cercana a Pien pasos de x. Esto es "listable", así lo Round[Pi,1/Range@1*^6]hace para todas las fracciones hasta 1/10 ^ 6 en orden. La lista resultante con muchas aproximaciones racionales "malas" se //.procesa repetidamente ( ) eliminando cualquier elemento que esté más lejos de pi que el anterior.
Mr.Wizard
Mathematica superando a GolfScript. Ordenado.
SpellingD
En 61: Select[Round[f=Pi,1/Range@1*^6],If[#<f,f=#;True]&@Abs[#-Pi]&]... pero inútil dado el sesgo dominante
Dr. belisarius
Yarr, Matie. Sería mágico en este código.
Michael Stern
7

Perl, 77 caracteres

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

Un desafío menor es que Perl no tiene una constante π incorporada disponible, así que primero tuve que calcularlo como atan2(0,-1). Estoy seguro de que esto será superado por los idiomas más adecuados para el trabajo, pero no está mal para un idioma diseñado principalmente para el procesamiento de texto.

Ilmari Karonen
fuente
1
Se podría cambiar 999999a 1e6y guardar 3 caracteres.
Toto
@ M42: ¡Gracias! Hasta 82 caracteres ahora.
Ilmari Karonen
Realmente agradable, $ = para obtener un entero. Lo siento, no puedo votar dos veces.
Toto
No puedo hacer que esto funcione:String found where operator expected at prog.pl line 1, near "say"$=/$_""
Keith Randall
@KeithRandall: necesita el -M5.01conmutador (y Perl 5.10.0 o posterior) para el saycomando. Perdón por no mencionar eso.
Ilmari Karonen
5

Python, 96 93 89 caracteres

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 caracteres, algoritmo diferente

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: tenía menos caracteres para escribir p=3.14159265359;que from math import*. ¡Malditas esas importaciones de palabras!

Steven Rumbalski
fuente
1
Algunas abreviaturas: 1.0-> 1., 10**6->1e6
Keith Randall
He actualizado con tus mejoras. Muchas gracias.
Steven Rumbalski
@KeithRandall, pero el segundo de ellos hace que la salida viole la especificación.
Peter Taylor
En el segundo enfoque no hay necesidad de la variable p. Eso es 4 caracteres.
Ante
@ PeterTaylor: No entiendo. ¿Cómo viola la especificación?
Steven Rumbalski
4

JS (95 caracteres)

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)

Imprime 167 líneas.

JiminP
fuente
4

Ruby 1.9, 84 caracteres

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]*?/)}
Howard
fuente
@ Peter Taylor Tienes razón. Tienes que usar Ruby 1.9.
Howard
4

C99, 113 caracteres

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

Necesito compilar -lm, y probablemente lleno de comportamiento indefinido, pero funciona para mí.

Thomas
fuente
2

Scala - 180 caracteres

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)

// sin golf: 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) 
  }  
}

La anotación tailrec es solo una verificación, para verificar, que es recursiva, lo que a menudo es una mejora del rendimiento.

usuario desconocido
fuente
No puedo hacer que esto funcione:pi.scala:1 error: not found: value math
Keith Randall
¿Estás usando Scala 2.8?
usuario desconocido el
Mi scala dice "versión desconocida", raro. En ideone.com, usan 2.8.0 y todavía recibo errores.
Keith Randall el
Pruébelo en simplyscala.com , funciona para mí. Para scala-2.8, reemplazar mathcon Mathpodría ser suficiente. Mencioné simplescala en este metathread, si lo busca de nuevo: meta.codegolf.stackexchange.com/a/401/373
usuario desconocido
OK, eso funciona.
Keith Randall
2

Mathematica 18 17 caracteres

Elegí usar, como una medida de "mejor", el número de términos en una representación de fracción continua de π. Según este criterio, las mejores aproximaciones racionales de π son sus convergentes.

Hay 10 convergentes de π con un denominador menor a un millón. Esto es menor que los 167 términos solicitados, pero lo incluyo aquí porque puede ser de interés para otros.

Convergents[π, 10] 

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

Si realmente desea ver el denominador para el primer convergente, le costará 11 caracteres adicionales:

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}

Para aquellos que estén interesados, lo siguiente muestra las relaciones entre los convergentes, los cocientes parciales y la expresión de fracción continua de los convergentes de π:

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]

fracciones continuas

Disculpe el formato inconsistente de las fracciones continuas.

DavidC
fuente
Eso es casi la mitad de una solución, pero es la mitad más fácil. Mi solución GolfScript codifica una representación adecuada de la fracción continua en solo 2 caracteres más.
Peter Taylor
Pero no usaste fracciones continuas para tu solución a esta pregunta, ¿verdad?
DavidC
Sí. Era la forma obvia de hacerlo.
Peter Taylor
Además de ser conciso, esto es mucho más rápido que la mayoría o todas las otras soluciones que se han publicado.
Michael Stern
1

C # 140 129 caracteres

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

Código sin comprimir

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++;
    }
}
Jader Dias
fuente
2
varNo siempre es tu amigo. Al eliminarlo a su favor double, obtiene la capacidad de fusionar declaraciones, pierde el requisito de usar literales dobles y puede guardar 16 caracteres. OTOH, la pregunta pide un programa, por lo que perderá algunos al agregar una declaración de clase y un Mainmétodo.
Peter Taylor
1

J, 69 65

Nuevo

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

Todavía un enfoque de fuerza bruta pero mucho más rápido y un poco más corto.

Antiguo

Una simple "fuerza bruta":

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

haga una lista de a/bsy luego descarte aquellos que están más lejos de π para algunos b'<b.

Nota: Cambie 1e3a 1e6para obtener la lista completa. Ve a hacer otra cosa y vuelve más tarde.

Eelvex
fuente