Numera los racionales positivos

14

Se puede demostrar que los números racionales positivos son numerables con el siguiente proceso:

  1. Cero tiene el ordinal 0
  2. Organice los otros números en una cuadrícula de modo que la fila a, la columna b contenga a / b
  3. Trace un zig-zag diagonal de arriba a la derecha de abajo a la izquierda
  4. Mantenga una cuenta corriente de los números únicos encontrados a lo largo del zig-zag

Aquí hay una foto del zig-zag:

Comience en 1/1, movimiento inicial a la derecha

Entonces, los números encontrados son, en orden

1/1, 2/1, 1/2, 1/3, 2/2, 3/1, 4/1, 3/2, 2/3, 1/4, 1/5, 2/4, 3/3, 4/2, 5/1, 6/1, 5/2, 4/3, 3/4, 2/5, 1/6, 1/7, 2/6, 3/5, 4/4, 5/3 ...

Y los números únicos y simplificados encontrados son

1, 2, 1/2, 1/3, 3, 4, 3/2, 2/3, 1/4, 1/5, 5, 6, 5/2, 4/3, 3/4, 2/5, 1/6, 1/7, 3/5, 5/3, ...

Desafío:

  • Dados dos enteros mayores que cero p y q , genera el número ordinal de p / q
  • pyq no son necesariamente co-prime
  • El código más corto gana
  • Las lagunas estándar están prohibidas

Casos de prueba:

Aquí están los primeros 24 números racionales encontrados, y el resultado deseado para cada uno:

1/1: 1
2/1: 2
1/2: 3
1/3: 4
2/2: 1
3/1: 5
4/1: 6
3/2: 7
2/3: 8
1/4: 9
1/5: 10
2/4: 3
3/3: 1
4/2: 2
5/1: 11
6/1: 12
5/2: 13
4/3: 14
3/4: 15
2/5: 16
1/6: 17
1/7: 18
2/6: 4
3/5: 19

Y, para más casos de prueba, aquí están los 200 primeros números racionales positivos en orden:

1, 2, 1/2, 1/3, 3, 4, 3/2, 2/3, 1/4, 1/5, 
5, 6, 5/2, 4/3, 3/4, 2/5, 1/6, 1/7, 3/5, 5/3, 
7, 8, 7/2, 5/4, 4/5, 2/7, 1/8, 1/9, 3/7, 7/3, 
9, 10, 9/2, 8/3, 7/4, 6/5, 5/6, 4/7, 3/8, 2/9, 
1/10, 1/11, 5/7, 7/5, 11, 12, 11/2, 10/3, 9/4, 8/5, 
7/6, 6/7, 5/8, 4/9, 3/10, 2/11, 1/12, 1/13, 3/11, 5/9, 
9/5, 11/3, 13, 14, 13/2, 11/4, 8/7, 7/8, 4/11, 2/13, 
1/14, 1/15, 3/13, 5/11, 7/9, 9/7, 11/5, 13/3, 15, 16, 
15/2, 14/3, 13/4, 12/5, 11/6, 10/7, 9/8, 8/9, 7/10, 6/11, 
5/12, 4/13, 3/14, 2/15, 1/16, 1/17, 5/13, 7/11, 11/7, 13/5, 
17, 18, 17/2, 16/3, 15/4, 14/5, 13/6, 12/7, 11/8, 10/9, 
9/10, 8/11, 7/12, 6/13, 5/14, 4/15, 3/16, 2/17, 1/18, 1/19, 
3/17, 7/13, 9/11, 11/9, 13/7, 17/3, 19, 20, 19/2, 17/4, 
16/5, 13/8, 11/10, 10/11, 8/13, 5/16, 4/17, 2/19, 1/20, 1/21, 
3/19, 5/17, 7/15, 9/13, 13/9, 15/7, 17/5, 19/3, 21, 22, 
21/2, 20/3, 19/4, 18/5, 17/6, 16/7, 15/8, 14/9, 13/10, 12/11, 
11/12, 10/13, 9/14, 8/15, 7/16, 6/17, 5/18, 4/19, 3/20, 2/21, 
1/22, 1/23, 5/19, 7/17, 11/13, 13/11, 17/7, 19/5, 23, 24, 
23/2, 22/3, 21/4, 19/6, 18/7, 17/8, 16/9, 14/11, 13/12, 12/13, 
11/14, 9/16, 8/17, 7/18, 6/19, 4/21, 3/22, 2/23, 1/24, 1/25 

Grite a la pregunta inversa , donde el primer movimiento es hacia abajo, por lo que no puede usar las respuestas para generar casos de prueba adicionales.

HAEM
fuente
Me pregunto si hay esquemas de numeración alternativos que generen un código más corto.
qwr
1
Numeradores de fracciones: oeis.org/A157807 denominadores: oeis.org/A157813 No hay coincidencias para la secuencia ordinal: oeis.org/…
qwr
Veo. tienes que reducir la fracción y luego contar. no es solo el zig-zag
don brillante

Respuestas:

4

Jalea ,  21  20 bytes

Probablemente superable por varios bytes utilizando algunas matemáticas inteligentes ...

:g/
ǵSRRUĖ€UÐeẎÇ€Qi

Un enlace monádico que acepta una lista de los [p,q]cuales devuelve el número natural asignado p/q.

Pruébalo en línea! O ver el conjunto de pruebas .

¿Cómo?

Primera nota de que el N º diagonal encontró contiene todos los números racionales de la cuadrícula para los que la suma del numerador y el denominador es igual a N + 1 , por lo que dada una función que reduce un [p,q]par de forma más simple ([p/gcd(p,q),q/gcd(p,q)] ) podemos construir las diagonales como la medida de lo necesita ser *, reducir todas las entradas, desduplicar y encontrar el índice de la entrada simplificada.

* en realidad uno más aquí para guardar un byte

:g/ - Link 1, simplify a pair: list of integers, [a, b]
  / - reduce using:
 g  - Greatest Common Divisor -> gcd(a, b)
:   - integer division (vectorises) -> [a/gcd(a,b), b/gcd(a,b)]

ǵSRRUĖ€UÐeẎÇ€Qi - Main Link: list of integers, [p, q]
Ç                - call last Link as a monad (simplify)
 µ               - start a new monadic chain (call that V)
  S              - sum -> the diagonal V will be in plus one
   R             - range -> [1,2,3,...,diag(V)+1]
    R            - range (vectorises) -> [[1],[1,2],[1,2,3],...,[1,2,3,...,diag(V)+1]]
     U           - reverse each       -> [[1],[2,1],[3,2,1],[diag(V)+1,...,3,2,1]]
      Ė€         - enumerate €ach     -> [[[1,1]],[[1,2],[2,1]],[[1,3],[2,2],[3,1]],[[1,diag(V)+1],...,[diag(V)-1,3],[diag(V),2],[diag(V)+1,1]]]
         Ðe      - apply only to the even indexed items:
        U        -   reverse each     -> [[[1,1]],[[2,1],[1,2]],[[1,3],[2,2],[3,1]],[[4,1],[3,2],[2,3],[1,4]],...]
           Ẏ     - tighten            -> [[1,1],[2,1],[1,2],[1,3],[2,2],[3,1],[4,1],[3,2],[2,3],[1,4],...]
            Ç€   - for €ach: call last Link as a monad (simplify each)
                 -                    -> [[1,1],[2,1],[1,2],[1,3],[1,1],[3,1],[4,1],[3,2],[2,3],[1,4],...]
              Q  - de-duplicate       -> [[1,1],[2,1],[1,2],[1,3],[3,1],[4,1],[3,2],[2,3],[1,4],...]
               i - index of V in that list
Jonathan Allan
fuente
3

Perl 6 ,  94  90 bytes

->\p,\q{(({|(1…($+=2)…1)}…*)Z/(1,{|(1…(($||=1)+=2)…1)}…*)).unique.first(p/q,:k)+1}

Pruébalo

{(({|(1…($+=2)…1)}…*)Z/(1,{|(1…(($||=1)+=2)…1)}…*)).unique.first($^p/$^q):k+1}

Pruébalo

Básicamente, esto genera toda la secuencia de valores y se detiene cuando encuentra una coincidencia.

Expandido:

{  # bare block lambda with placeholder parameters $p,$q

  (
      ( # sequence of numerators

        {
          |( # slip into outer sequence (flatten)

            1      # start at one
            
            (
              $    # state variable
              += 2 # increment it by two each time this block is called
            )
            
            1      # finish at one
          )

        }
         * # never stop generating values
      )


    Z/   # zip using &infix:« /  » (generates Rats)


      ( # sequence of denominators

        1,  # start with an extra one

        {
          |( # slip into outer sequence (flatten)

            1
            
            (
              ( $ ||= 1 ) # state variable that starts with 1 (rather than 0)
              += 2        # increment it by two each time this is called
            )
            
            1
          )
        }
         * # never stop generating values
      )


  ).unique            # get only the unique values
  .first( $^p / $^q ) # find the first one that matches the input
  :k                  # get the index instead (0 based)
  + 1                 # add one               (1 based)
}

({1…($+=2)…1}…*)genera la secuencia infinita de numeradores ( |(…)se usa arriba para aplanar)

(1 2 1)
(1 2 3 4 3 2 1)
(1 2 3 4 5 6 5 4 3 2 1)
(1 2 3 4 5 6 7 8 7 6 5 4 3 2 1)
(1 2 3 4 5 6 7 8 9 10 9 8 7 6 5 4 3 2 1)

(1,{1…(($||=1)+=2)…1}…*) genera la secuencia infinita de denominadores

1
(1 2 3 2 1)
(1 2 3 4 5 4 3 2 1)
(1 2 3 4 5 6 7 6 5 4 3 2 1)
(1 2 3 4 5 6 7 8 9 8 7 6 5 4 3 2 1)
(1 2 3 4 5 6 7 8 9 10 11 10 9 8 7 6 5 4 3 2 1)
Brad Gilbert b2gills
fuente
3

Python 2 , 157 144 137 134 126 125 bytes

def f(p,q):a=[((j-i)/(i+1.))**(j%-2|1)for j in range(p-~q)for i in range(j)];return-~sorted(set(a),key=a.index).index(p*1./q)

Pruébalo en línea!

4 bytes guardados debido al Sr. Xcoder ; 1 byte de Jonathon Frech .

Como señaló el Sr. Xcoder, podemos mejorar un poco en Python 3, ya que, entre otras cosas, la división de números enteros predetermina los resultados flotantes y podemos descomprimir más fácilmente list:

Python 3 , 117 bytes

def f(p,q):a=[((j-i)/-~i)**(j%-2|1)for j in range(p-~q)for i in range(j)];return-~sorted({*a},key=a.index).index(p/q)

Pruébalo en línea!

Chas Brown
fuente
128 bytes (-6) intercambiando la fracción y usando **(j%-2|1)y p-~q.
Sr. Xcoder
@Señor. Xcoder: ¡Hoy estás en el módulo negativo! :) Creo que todavía necesito el +1al final, ya que 1,1'debe' dar 1, no 0.
Chas Brown
125 bytes .
Jonathan Frech
124 bytes :) Sí, el módulo negativo resulta ser realmente útil.
Sr. Xcoder
En realidad 117 bytes en Python 3
Mr. Xcoder
3

Python 3 ,157, 146, 140133 bytes

def f(p,q):a=[(i+i-abs(j-i-i))/(abs(j-i-i+.5)+.5)for i in range(p+q)for j in range(4*i)];return sorted(set(a),key=a.index).index(p/q)

Pruébalo en línea!

ganó 11 bytes gracias a Jonathan Frech

ganó 6 bytes más y luego 7 gracias a Chas Brown

bobrobbob
fuente
1
146 bytes .
Jonathan Frech
140 bytes .
Chas Brown
@bobrobbob: ¡Bienvenido a PPCG! No estoy muy seguro de cómo funciona su algoritmo (aunque claramente lo hace); pero experimentalmente, parece que puede guardar algunos bytes más reemplazándolos range(max(p,q)+1)con range(p+q).
Chas Brown
1
Puede guardar algunos bytes más utilizando en {*a}lugar de set(a).
Sr. Xcoder
2

J, 41 , 35 , 30 bytes

-11 bytes gracias a FrownyFrog

%i.~0~.@,@,[:]`|./.[:%/~1+i.@*

Pruébalo en línea!

publicación original de 41 bytes con explicación

%>:@i.~[:([:~.@;[:<@|.`</.%"1 0)~(1+i.@*)

sin golf

% >:@i.~ [: ([: ~.@; [: <@|.`</. %"1 0)~ 1 + i.@*

explicación

                  +
                  | Everything to the right of this
                  | calculates the list
p (left arg)      |                                      create the
divided by q      |                                      diagonals.  yes,
      (right)     |                                     +this is a         +create this list
   |              |        ++ finally rmv ^alternately  |primitive ADVERB  |1..(p*q), and pass
   |   + the index          | the boxes,  |box, or      |in J              |it as both the left
   |   | of that  |         | and remove  |reverse and  |                  |and right arg to the
   |   | in the   |         | any dups    |box, each    |                  |middle verb, where each
   |   | list on  |         |             |diagonal     |                  |element will divide the
   |   | the right|         |             |             |       +----------+entire list, producing
   |   | plus 1   |         |             |             |       |          |the undiagonalized grid
   |   |          |         |             |             |       |          |
   |   |          |         |             |             |       |          |
   |   |          +         |             |             |       |          |
  ┌+┬──|──────────┬─────────|─────────────|─────────────|───────|──────────|─────────────┐
  │%│┌─+───────┬─┐│┌──┬─────|─────────────|─────────────|───────|────────┬─|────────────┐│
  │ ││┌──┬─┬──┐│~│││[:│┌────|─────────────|─────────────|───────|─────┬─┐│┌+┬─┬────────┐││
  │ │││>:│@│i.││ │││  ││┌──┬|───────┬─────|─────────────|───────|────┐│~│││1│+│┌──┬─┬─┐│││
  │ ││└──┴─┴──┘│ │││  │││[:│+──┬─┬─┐│┌──┬─|─────────────|─┬─────|───┐││ │││ │ ││i.│@│*││││
  │ │└─────────┴─┘││  │││  ││~.│@│;│││[:│┌|───────────┬─+┐│┌─┬─┬+──┐│││ │││ │ │└──┴─┴─┘│││
  │ │             ││  │││  │└──┴─┴─┘││  ││+────────┬─┐│/.│││%│"│1 0││││ ││└─┴─┴────────┘││
  │ │             ││  │││  │        ││  │││┌─┬─┬──┐│<││  ││└─┴─┴───┘│││ ││              ││
  │ │             ││  │││  │        ││  ││││<│@│|.││ ││  ││         │││ ││              ││
  │ │             ││  │││  │        ││  │││└─┴─┴──┘│ ││  ││         │││ ││              ││
  │ │             ││  │││  │        ││  ││└────────┴─┘│  ││         │││ ││              ││
  │ │             ││  │││  │        ││  │└────────────┴──┘│         │││ ││              ││
  │ │             ││  │││  │        │└──┴─────────────────┴─────────┘││ ││              ││
  │ │             ││  ││└──┴────────┴────────────────────────────────┘│ ││              ││
  │ │             ││  │└──────────────────────────────────────────────┴─┘│              ││
  │ │             │└──┴──────────────────────────────────────────────────┴──────────────┘│
  └─┴─────────────┴──────────────────────────────────────────────────────────────────────┘

Pruébalo en línea!

Jonás
fuente
35:1+~.@;@([:<@|.`</.%~/~)@(1+i.@*)i.%
FrownyFrog
¡gracias muy amable! Lo actualizaré completamente más tarde, ya que requerirá rehacer la explicación ...
Jonah
Y 30:%i.~0~.@,@,[:]`|./.[:%/~1+i.@*
FrownyFrog
eso es inteligente, use 0 y ~. para evitar el boxeo y el incremento, me encanta
Jonah
2

Python 3, 121 bytes

import math
def o(x,y):
 p=q=n=1
 while x*q!=p*y:a=p+q&1;p+=1+a*~-~-(p<2);q+=1-~-a*~-~-(q<2);n+=math.gcd(p,q)<2
 return n
orlp
fuente
2

Óxido, 244 bytes

Creé una fórmula simple para encontrar el ordinal "simple" de un zigzag "simple", sin las restricciones del rompecabezas, usando fórmulas de números triangulares: https://www.mathsisfun.com/algebra/triangular-numbers.html . Esto se modificó con el módulo 2 para tener en cuenta los zig-zags que invierten su dirección en cada fila diagonal del rompecabezas. Esta es la función h ()

Luego, para el truco principal de este rompecabezas: cómo 'no contar' ciertos valores repetidos, como 3/3 vs 1/1, 4/2 vs 2/1, en el camino en zig-zag. Ejecuté los ejemplos 1-200, y noté que la diferencia entre un contador triangular simple en zig-zag y el que desea el rompecabezas tiene un patrón. El patrón de números "perdidos" es 5, 12, 13, 14, 23, etc., lo que resultó en un éxito en el OEIS. Es uno descrito por Robert A Stump, en https://oeis.org/A076537 , para "deduplicar" números como 3/3, 4/2 y 1/1, puede verificar si GCD> 1 para el x, y de todos los ordinales "anteriores" en zigzag. Este es el bucle 'for' y g () que es el mcd.

Supongo que con algunos mcd incorporados hubiera sido más corto, pero no pude encontrar uno muy fácilmente (soy un poco nuevo en Rust e Integer me confundió), y me gusta el hecho de que este usa aritmética de enteros directos, y sin construcciones o bibliotecas de ningún tipo.

fn f(x:i64,y:i64)->i64 {
        fn h(x:i64,y:i64)->i64 {let s=x+y;(s*s-3*s+4)/2-1+(s+1)%2*x+s%2*y}
        fn g(x:i64,y:i64)->i64 {if x==0 {y} else {g(y%x,x)}}
        let mut a=h(x,y);
        for i in 1..x+y {for j in 1..y+x {if h(i,j)<h(x,y) && g(i,j)>1 {a-=1;}}}
        a
}
don brillante
fuente
1

JavaScript (ES6), 86 bytes

Toma entrada en la sintaxis de curry (p)(q).

p=>q=>(g=x=>x*y?x*q-y*p?g(x+d,g[x/=y]=g[x]||++n,y-=d):n:g(x+!~d,y+=!~(d=-d)))(d=n=y=1)

Pruébalo en línea!

Arnauld
fuente
0

Javascript, 79 bytes

a=(p,q)=>p*q==1?1:1+((p+q)%2?q==1?a(p-1,q):a(p+1,q-1):p==1?a(p,q-1):a(p-1,q+1))

(Soy nuevo en el golf de código, por lo que esto probablemente pueda mejorarse fácilmente)

Explicación

let a = (p, q) => p * q == 1 // If they are both 1
    ? 1
    // Do a recursive call and increment the result by 1
    : 1 + (
        (p + q) % 2 // If on an even-numbered diagonal
        ? q == 1 // If at the beginning of the diagonal
            ? a(p - 1, q) // Go to previous diagonal
            : a(p + 1, q - 1) // Go one back
        : p == 1 // rougly the same
            ? a(p, q - 1)
            : a(p - 1, q + 1)
    )
Bary12
fuente
44
(3,5)debe dar lugar a 19(no 24) ya que (1,1)==(2,2)==(3,3), (2,4)==(1,2), (4,2)==(2,1)y (2,6)==(1,3). (es decir, (2,2)debería resultar en 1no 5, etc.)
Jonathan Allan