Salida del enésimo número racional de acuerdo con la secuencia Stern-Brocot

30

La secuencia Stern-Brocot es una secuencia similar a Fibonnaci que se puede construir de la siguiente manera:

  1. Inicialice la secuencia con s(1) = s(2) = 1
  2. Establecer contador n = 1
  3. Añadir s(n) + s(n+1)a la secuencia
  4. Añadir s(n+1)a la secuencia
  5. Incremento n, regrese al paso 3

Esto es equivalente a:

s (n) = \ begin {cases} 1 & \ textrm {if} n = 1 \ s (\ frac n 2) & \ textrm {if} n \ textrm {es par} \ s (\ frac {n-1 } 2) + s (\ frac {n + 1} 2) & \ textrm {de lo contrario} \ end {casos}

Entre otras propiedades, la secuencia Stern-Brocot se puede utilizar para generar todos los números racionales positivos posibles. Cada número racional se generará exactamente una vez, y siempre aparecerá en sus términos más simples; por ejemplo, 1/3es el cuarto número racional en la secuencia, pero los números equivalentes 2/6, 3/9etc., no aparecerán en absoluto.

Podemos definir el enésimo número racional como r(n) = s(n) / s(n+1), donde s(n)está el enésimo número de Stern-Brocot, como se describió anteriormente.

Su desafío es escribir un programa o función que genere el enésimo número racional generado utilizando la secuencia Stern-Brocot.

  • Los algoritmos descritos anteriormente están indexados en 1; si su entrada está indexada en 0, indique su respuesta
  • Los algoritmos descritos son solo para fines ilustrativos, la salida se puede derivar de la forma que desee (excepto la codificación rígida)
  • La entrada puede ser a través de STDIN, parámetros de función o cualquier otro mecanismo de entrada razonable
  • Ouptut puede ser STDOUT, consola, valor de retorno de función o cualquier otro flujo de salida razonable
  • La salida debe ser como una cadena en el formulario a/b, donde ay bson las entradas relevantes en la secuencia Stern-Brocot. Evaluar la fracción antes de la salida no está permitido. Por ejemplo, para entrada 12, la salida debe ser 2/5, no 0.4.
  • Las lagunas estándar no están permitidas

Este es el , por lo que la respuesta más corta en bytes ganará.

Casos de prueba

Los casos de prueba aquí son 1 indexados.

n    r(n)
--  ------
1    1/1
2    1/2
3    2/1
4    1/3
5    3/2
6    2/3
7    3/1
8    1/4
9    4/3
10   3/5
11   5/2
12   2/5
13   5/3
14   3/4
15   4/1
16   1/5
17   5/4
18   4/7
19   7/3
20   3/8
50   7/12
100  7/19
1000 11/39

Entrada de OEIS: A002487
Excelente video de Numberphile que discute la secuencia: fracciones infinitas

Sok
fuente
¿Puede la salida usar Trues en lugar de 1s?
Loovjo
1
@Loovjo No, True/2no es una fracción válida (en lo que a mí respecta). Por otro lado, Trueno siempre es así 1, algunos lenguajes se utilizan -1para evitar posibles errores al aplicar operadores bit a bit. [cita requerida]
Sok
relacionado
flawr
2
@Sok cita
mbomb007
1
@Sok pero en Python, Truees equivalente 1y True/2lo sería 1/2.
Leaky Nun

Respuestas:

3

Jalea , 14 bytes

3ẋḶŒpḄċ
’Ç”/⁸Ç

Pruébalo en línea!

Ooh, parece que puedo superar la respuesta aceptada por @Dennis, y en el mismo idioma. Esto funciona utilizando una fórmula de OEIS: la cantidad de formas de expresar (el número menos 1) en hiperbinario (es decir, binario con 0, 1, 2 como dígitos). A diferencia de la mayoría de los programas Jelly (que funcionan como un programa completo o una función), este funciona solo como un programa completo (porque envía parte de la salida a stdout y devuelve el resto; cuando se usa como un programa completo, el valor de retorno se envía a stdout implícitamente, por lo que toda la salida está en el mismo lugar, pero esto no funcionaría para el envío de una función).

Esta versión del programa es muy ineficiente. Puede crear un programa mucho más rápido que funcione para todas las entradas de hasta 2ⁿ colocando n justo después de en la primera línea; el programa tiene un rendimiento O ( n × 3ⁿ), por lo que mantener n pequeño es bastante importante aquí. El programa como escrito establece n igual a la entrada, que es claramente lo suficientemente grande, pero también claramente demasiado grande en casi todos los casos (pero bueno, ahorra bytes).

Explicación

Como es habitual en mis explicaciones de Jelly, el texto entre llaves (por ejemplo {the input}) muestra algo que Jelly completa automáticamente debido a la falta de operandos en el programa original.

Función de ayuda (calcula el n º denominador, es decir, el n + numerador 1 Tes):

3ẋḶŒpḄċ
3ẋ       3, repeated a number of times equal to {the argument}
  Ḷ      Map 3 to [0, 1, 2]
   Œp    Take the cartesian product of that list
     Ḅ   Convert binary (or in this case hyperbinary) to a number
      ċ  Count number of occurrences of {the argument} in the resulting list

Los primeros cinco bytes están básicamente generando todos los números hiperbinarios posibles hasta una longitud dada, por ejemplo, con la entrada 3, la salida de es [[0,1,2], [0,1,2], [0,1,2 ]] entonces el producto cartesiano es [[0,0,0], [0,0,1], [0,0,2], [0,1,0],…, [2,2,1], [2,2,2]]. básicamente simplemente multiplica la última entrada por 1, la penúltima entrada por 2, la entrada antepenúltima por 4, etc., y luego agrega; aunque esto normalmente se usa para convertir binario a decimal, puede manejar bien el dígito 2 y, por lo tanto, también funciona para hiperbinario. Luego contamos el número de veces que aparece la entrada en la lista resultante, para obtener una entrada apropiada en la secuencia. (Afortunadamente, el numerador y el denominador siguen la misma secuencia).

Programa principal (solicita el numerador y el denominador, y formatea la salida):

’Ç”/⁸Ç
’Ç      Helper function (Ç), run on {the input} minus 1 (‘)
  ”/    {Output that to stdout}; start again with the constant '/'
    ⁸Ç  {Output that to stdout}; then run the helper function (Ç) on the input (⁸)

De alguna manera sigo escribiendo programas que toman casi tantos bytes para manejar E / S como lo hacen para resolver el problema real ...


fuente
Dios mío, no estabas bromeando acerca de que era ineficiente: ¡en TIO 12 lleva 20 años completarlo, y 13 veces por completo! Aceptado, aunque no puedo verificar todos los casos de prueba.
Sok
11

CJam (20 bytes)

1_{_@_2$%2*-+}ri*'/\

Demo en línea . Tenga en cuenta que esto está indexado a 0. Para hacerlo 1 indexado, reemplace la inicial 1_por T1.

Disección

Esto utiliza la caracterización debido a Moshe Newman que

la fracción a(n+1)/a(n+2)se puede generar a partir de la fracción anterior a(n)/a(n+1) = xpor1/(2*floor(x) + 1 - x)

Si x = s/tentonces obtenemos

  1 / (2 * floor(s/t) + 1 - s/t)
= t / (2 * t * floor(s/t) + t - s)
= t / (2 * (s - s%t) + t - s)
= t / (s + t - 2 * (s % t))

Ahora, si asumimos eso sy tsomos coprimos, entonces

  gcd(t, s + t - 2 * (s % t))
= gcd(t, s - 2 * (s % t))
= gcd(t, -(s % t))
= 1

Entonces a(n+2) = a(n) + a(n+1) - 2 * (a(n) % a(n+1))funciona perfectamente.

1_           e# s=1, t=1
{            e# Loop...
  _@_2$%2*-+ e#   s, t = t, s + t - 2 * (s % t)
}
ri*          e# ...n times
'/\          e# Separate s and t with a /
Peter Taylor
fuente
Me encanta la metodología aquí, ¡respuesta increíble!
Sok
Si se desplaza hacia abajo por la entrada OEIS, encontrará que Mike Stay ya envió esa fórmula.
Neil
11

Haskell, 78 77 65 58 bytes

Robar descaradamente el enfoque optimizado nos da:

(s#t)0=show s++'/':show t
(s#t)n=t#(s+t-2*mod s t)$n-1
1#1

¡Gracias a @nimi por jugar unos pocos bytes con una función infija!

(Todavía) usa indexación basada en 0.


El viejo enfoque:

s=(!!)(1:1:f 0)
f n=s n+s(n+1):s(n+1):(f$n+1)
r n=show(s n)++'/':(show.s$n+1)

Maldita sea el formato de salida ... Y los operadores de indexación. EDITAR: Y precedencia.

Dato curioso: si las listas heterogéneas fueran algo, la última línea podría ser:

r n=show>>=[s!!n,'/',s!!(n+1)]
ThreeFx
fuente
Usar un guardia para atar s!!ndebería ser un byte más corto:f n|x<-s!!n=x:x+x+1:f$n+1
Laikoni
@Laikoni s!!n+1no lo es, (s!!n)+1pero s!!(n+1)es por eso que no puedo hacer eso: /
ThreeFx
De hecho, eso debería haber sido obvio. Es solo ... ¡tantos s!!nallí!
Laikoni
1
Se puede utilizar ++'/':(show.s$n+1)en rguardar un byte.
nimi
1
Cambiar a un una función infija: (s#t)0=show..., (s#t)n=t#(s+t-2*mod s t)$n-1, r=1#1. Incluso puede omitir r, es decir, la última línea es justa 1#1.
nimi
6

Jalea , 16 bytes

L‘Hị⁸Sṭ
1Ç¡ṫ-j”/

Pruébalo en línea! o verificar todos los casos de prueba .

Cómo funciona

1Ç¡ṫ-j”/  Main link. Argument: n

1         Set the return value to 1.
 Ç¡       Apply the helper link n times.
   ṫ-     Tail -1; extract the last two items.
     j”/  Join, separating by a slash.


L‘Hị⁸Sṭ   Helper link. Argument: A (array)

L         Get the length of A.
 ‘        Add 1 to compute the next index.
  H       Halve.
   ị⁸     Retrieve the item(s) of A at those indices.
          If the index in non-integer, ị floors and ceils the index, then retrieves
          the items at both indices.
    S     Compute the sum of the retrieved item(s).
     ṭ    Tack; append the result to A.
Dennis
fuente
5

05AB1E , 34 33 25 23 bytes

XX‚¹GÂ2£DO¸s¦ìì¨}R2£'/ý

Explicación

XX‚                        # push [1,1]
   ¹G           }          # input-1 times do
     Â                     # bifurcate
      2£                   # take first 2 elements of reversed list
        DO¸                # duplicate and sum 1st copy, s(n)+s(n+1)
           s¦              # cut away the first element of 2nd copy, s(n)
             ìì            # prepend both to list
               ¨           # remove last item in list
                 R2£       # reverse and take the first 2 elements
                    '/ý    # format output
                           # implicitly print

Pruébalo en línea

Guardado 2 bytes gracias a Adnan.

Emigna
fuente
¿Esto también funciona ?: XX‚¹GÂ2£DO¸s¦ìì¨}R2£'/ý.
Adnan
@Adnan De hecho. Olvidé que ýpuede formatear una lista. Agradable.
Emigna
4

MATL , 20 bytes

FT+"@:qtPXnosV47]xhh

Esto utiliza la caracterización en términos de coeficientes binomiales dados en la página OEIS .

El algoritmo funciona en teoría para todos los números, pero en la práctica está limitado por la precisión numérica de MATL, por lo que no funciona para entradas grandes. El resultado es preciso para entradas de hasta20 al menos hasta.

Pruébalo en línea!

Explicación

FT+      % Implicitly take input n. Add [0 1] element-wise. Gives [n n+1]
"        % For each k in [n n+1]
  @:q    %   Push range [0 1 ... k-1]
  tP     %   Duplicate and flip: push [k-1 ... 1 0]
  Xn     %   Binomial coefficient, element-wise. Gives an array
  os     %   Number of odd entries in that array
  V      %   Convert from number to string
  47     %   Push 47, which is ASCII for '\'
]        % End for each
x        % Remove second 47
hh       % Concatenate horizontally twice. Automatically transforms 47 into '\'
         % Implicitly display
Luis Mendo
fuente
4

Python 2, 85 81 bytes

x,s=input(),[1,1]
for i in range(x):s+=s[i]+s[i+1],s[i+1]
print`s[x-1]`+"/"+`s[x]`

Este envío está 1 indexado.

Usando una función recursiva, 85 bytes:

s=lambda x:int(x<1)or x%2 and s(x/2)or s(-~x/2)+s(~-x/2)
lambda x:`s(x)`+"/"+`s(x+1)`

Si un resultado como True/2es aceptable, aquí hay uno de 81 bytes:

s=lambda x:x<1 or x%2 and s(x/2)or s(-~x/2)+s(~-x/2)
lambda x:`s(x)`+"/"+`s(x+1)`
Loovjo
fuente
3

JavaScript (ES6), 43 bytes

f=(i,n=0,d=1)=>i?f(i-1,d,n+d-n%d*2):n+'/'+d

1 indexado; cambie a n=1para 0 indexado. La página OEIS vinculada tiene una relación de recurrencia útil para cada término en términos de los dos términos anteriores; Simplemente lo reinterpreté como una recurrencia para cada fracción en términos de la fracción anterior. Lamentablemente, no tenemos TeX en línea, por lo que solo tendrá que pegarlo en otro sitio para descubrir cómo funcionan estos formatos:

unasisiuna+si-2(unamodsi)
Neil
fuente
3

Python 2, 66 bytes

f=lambda n:1/n or f(n/2)+n%2*f(-~n/2)
lambda n:`f(n)`+'/'+`f(n+1)`

Utiliza la fórmula recursiva.

xnor
fuente
3

C (GCC), 79 bytes

Utiliza indexación basada en 0.

s(n){return!n?:n%2?s(n/2):s(-~n/2)+s(~-n/2);}r(n){printf("%d/%d",s(n),s(n+1));}

Ideona

Betseg
fuente
1
x?:yes una extensión de gcc.
rici
3

En realidad, 18 bytes

11,`│;a%τ@-+`nk'/j

Pruébalo en línea!

Esta solución usa la fórmula de Peter y también está indexada en 0. Gracias a Leaky Nun por un byte.

Explicación:

11,`│;a%τ@-+`nk'/j
11                  push 1, 1
  ,`│;a%τ@-+`n      do the following n times (where n is the input):
                      stack: [t s]
    │                 duplicate the entire stack ([t s t s])
     ;                dupe t ([t t s t s])
      a               invert the stack ([s t s t t])
       %              s % t ([s%t s t t])
        τ             multiply by 2 ([2*(s%t) s t t])
         @-           subtract from s ([s-2*(s%t) s t])
           +          add to t ([t+s-2*(s%t) t])
                      in effect, this is s,t = t,s+t-2*(s%t)
              k'/j  push as a list, join with "/"
Mego
fuente
@LeakyNun Esperaré esa mejora hasta que haya una aclaración del OP.
Mego
2

MATL , 32 30 bytes

1i:"tt@TF-)sw@)v]tGq)V47bG)Vhh

Esto utiliza un enfoque directo: genera suficientes miembros de la secuencia, selecciona los dos deseados y formatea la salida.

Pruébalo en línea!

Luis Mendo
fuente
2

R, 93 bytes

f=function(n)ifelse(n<3,1,ifelse(n%%2,f(n/2-1/2)+f(n/2+1/2),f(n/2)))
g=function(n)f(n)/f(n+1)

Literalmente la implementación más simple. Trabajando un poco en el golf.

usuario5957401
fuente
2

m4, 131 bytes

define(s,`ifelse($1,1,1,eval($1%2),0,`s(eval($1/2))',`eval(s(eval(($1-1)/2))+s(eval(($1+1)/2)))')')define(r,`s($1)/s(eval($1+1))')

Define una macro rtal que se r(n)evalúa de acuerdo con la especificación. Realmente no jugué nada, simplemente codifiqué la fórmula.

Programa hombre
fuente
2

Ruby, 49 bytes

Esto está indexado a 0 y utiliza la fórmula de Peter Taylor. Sugerencias de golf bienvenidas.

->n{s=t=1;n.times{s,t=t,s+t-2*(s%t)};"#{s}/#{t}"}
Sherlock9
fuente
1

> <> , 34 + 2 = 36 bytes

Después de ver la excelente respuesta de Peter Taylor, volví a escribir mi respuesta de prueba (que era una vergonzosa 82 bytes, usando una recursión muy torpe).

&01\$n"/"on;
&?!\:@}:{:@+@%2*-&1-:

Espera que la entrada esté presente en la pila, por lo que +2 bytes para el -vindicador. Pruébalo en línea!

Sok
fuente
1

Octava, 90 bytes

function f(i)S=[1 1];for(j=1:i/2)S=[S S(j)+S(j+1) (j+1)];end;printf("%d/%d",S(i),S(i+1));end
dcsohl
fuente
1

C #, 91 90 bytes

n=>{Func<int,int>s=_=>_;s=m=>1==m?m:s(m/2)+(0==m%2?0:s(m/2+1));return$"{s(n)}/{s(n+1)}";};

Lanza a Func<int, string> . Esta es la implementación recursiva.

Sin golf:

n => 
{
    Func<int,int> s = _ => _; // s needs to be set to use recursively. _=>_ is the same byte count as null and looks cooler.
    s = m =>
        1 == m ? m               // s(1) = 1
        : s(m/2) + (0 == m%2 ? 0 // s(m) = s(m/2) for even
        : s(m/2+1));             // s(m) = s(m/2) + s(m/2+1) for odd
    return $"{s(n)}/{s(n+1)}";
};

Editar: -1 byte. Resulta que C # no necesita un espacio entre returny $para las cadenas interpoladas.

Leche
fuente
1

J, 29 bytes

([,'/',])&":&([:+/2|]!&i.-)>:

Uso

Los valores grandes de n requieren un sufijo xque denota el uso de enteros extendidos.

   f =: ([,'/',])&":&([:+/2|]!&i.-)>:
   f 1
1/1
   f 10
3/5
   f 50
7/12
   f 100x
7/19
   f 1000x
11/39
millas
fuente
100cuenta como un "gran valor"?
dcsohl
1
@dcsohl En este método, se calculan los coeficientes binomiales, y para n = 100, el mayor calculado es C (72, 28) = 75553695443676829680> 2 ^ 64 y requerirá números enteros extendidos para evitar valores de coma flotante.
millas
1

Mathematica, 108 106 101 bytes

(s={1,1};n=1;a=AppendTo;t=ToString;Do[a[s,s[[n]]+s[[++n]]];a[s,s[[n]]],#];t@s[[n-1]]<>"/"<>t@s[[n]])&
numbermaniac
fuente
1

R , 84 bytes

function(n,K=c(1,1)){for(i in 1:n)K=c(K,K[i]+K[i+1],K[i+1])
paste0(K[i],"/",K[i+1])}

Pruébalo en línea!

La implementación anterior de R no sigue las especificaciones, devuelve un punto flotante en lugar de una cadena, así que aquí hay uno que sí lo hace.

Giuseppe
fuente