Secuencia de cruce de cuadrícula

17

Si toma una hoja de papel cuadriculado y dibuja una línea inclinada que va munidades a la derecha y nunidades hacia arriba, cruza las líneas de cuadrícula n-1horizontales y m-1verticales en alguna secuencia. Escribir código para generar esa secuencia.

Por ejemplo, m=5y n=3da:

Rejilla que cruza para m = 5, n = 3

Posiblemente relacionado: Generar ritmos euclidianos , inclinaciones de Fibonacci , FizzBuzz

Entrada: dos enteros positivos m,nque son relativamente primos

Salida: Devuelve o imprime los cruces como una secuencia de dos tokens distintos. Por ejemplo, podría ser una cadena de Hy V, una lista de Truey False, o 0'sy 1' impresa en líneas separadas. Puede haber un separador entre tokens siempre que sea siempre el mismo y no, digamos, un número variable de espacios.

Casos de prueba:

El primer caso de prueba da salida vacía o sin salida.

1 1 
1 2 H
2 1 V
1 3 HH
3 2 VHV
3 5 HVHHVH
5 3 VHVVHV
10 3 VVVHVVVHVVV
4 11 HHVHHHVHHHVHH
19 17 VHVHVHVHVHVHVHVHVVHVHVHVHVHVHVHVHV
39 100 HHVHHHVHHVHHHVHHVHHHVHHVHHHVHHHVHHVHHHVHHVHHHVHHVHHHVHHHVHHVHHHVHHVHHHVHHVHHHVHHVHHHVHHHVHHVHHHVHHVHHHVHHVHHHVHHHVHHVHHHVHHVHHHVHHVHHHVHH

En el formato (m,n,output_as_list_of_0s_and_1s):

(1, 1, [])
(1, 2, [0])
(2, 1, [1])
(1, 3, [0, 0])
(3, 2, [1, 0, 1])
(3, 5, [0, 1, 0, 0, 1, 0])
(5, 3, [1, 0, 1, 1, 0, 1])
(10, 3, [1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1])
(4, 11, [0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0])
(19, 17, [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1])
(39, 100, [0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0])
xnor
fuente
2
Hoy en PPCG: Algoritmo de dibujo lineal de Bresenham
Sparr
Según el formato alternativo que agregó, ¿se puede / debe repetir la entrada como parte de la salida? De lo contrario, no entiendo por qué la entrada y la salida son parte de la misma lista.
Reto Koradi
@RetoKoradi No, no debe incluir la entrada. Lo puse en tuplas para que sea más fácil procesar los casos de prueba.
xnor
Puedo predecir la respuesta, pero no está de más preguntar: ¿Sería aceptable usar el carácter de espacio como uno de los tokens de salida? Una consecuencia sería que podría haber espacios iniciales / finales significativos en la salida. No habría otros espacios, por lo que todos los espacios serían significativos.
Reto Koradi
@RetoKoradi No, porque los espacios finales no son visibles.
xnor

Respuestas:

7

Ruby, 92; Avestruz 0.7.0 , 38

f=->m,n{((1..m-1).map{|x|[x,1]}+(1..n-1).map{|x|[1.0*x*m/n,0]}).sort_by(&:first).map &:last}
:n;:m1-,{)o2W}%n1-,{)m n/*0pW}%+_H$_T%

La salida para ambos usa 1's y 0's (ej. 101101).


Aquí hay una explicación del avestruz:

:n;:m    store n and m as variables, keep m on the stack
1-,      from ex. 5, generate [0 1 2 3]
{...}%   map...
  )        increment, now 5 -> [1 2 3 4]
  o        push 1 (the digit 1 is special-cased as `o')
  2W       wrap the top two stack elements into an array
           we now have [[1 1] [2 1] [3 1] [4 1]]
n1-,     doing the same thing with n
{...}%   map...
  )        increment, now 3 -> [1 2]
  m n/*    multiply by slope to get the x-value for a certain y-value
  0        push 0
  pW       wrap the top two stack elements (2 is special-cased as `p')
+        concatenate the two arrays
_H$      sort-by first element (head)
_T%      map to last element (tail)

Y una explicación de cómo funciona todo, usando el código Ruby como guía:

f = ->m,n {
    # general outline:
    # 1. collect a list of the x-coordinates of all the crossings
    # 2. sort this list by x-coordinate
    # 3. transform each coordinate into a 1 or 0 (V or H)
    # observation: there are (m-1) vertical crossings, and (n-1) horizontal
    #   crossings. the vertical ones lie on integer x-values, and the
    #   horizontal on integer y-values
    # collect array (1)
    (
        # horizontal
        (1..m-1).map{|x| [x, 1] } +
        # vertical
        (1..n-1).map{|x| [1.0 * x * m/n, 0] }  # multiply by slope to turn
                                               # y-value into x-value
    )
    .sort_by(&:first)  # sort by x-coordinate (2)
    .map &:last        # transform into 1's and 0's (3)
}
Pomo de la puerta
fuente
5

Python, 53

Esto utiliza la salida de la lista Verdadero / Falso. Nada especial aquí.

lambda m,n:[x%m<1for x in range(1,m*n)if x%m*(x%n)<1]
Feersum
fuente
4

Pyth - 32 24 bytes

Jmm,chk@Qddt@Qd2eMS+hJeJ

Toma entrada a través de stdin con el formato [m,n]. Imprime el resultado en stdout como una lista de 0 y 1, donde 0 = V y 1 = H.

Pruébelo en línea


Explicación:

J                           # J = 
 m             2            # map(lambda d: ..., range(2))
  m        t@Qd             # map(lambda k: ..., range(input[d] - 1))
   ,chk@Qdd                 # [(k + 1) / input[d], d]
                eMS+hJeJ    # print map(lambda x: x[-1], sorted(J[0] + J[-1])))
Tyilo
fuente
Puede guardar un byte utilizando el operador de mapa sintáctico para el final de la asignación. eMes el mismo que med.
Maltysen
Además, puede sacarlo @"VH"ya que puede imprimir 0y en 1lugar de Vy H.
Maltysen
Puede guardar otro byte utilizando la asignación en línea con J. Esto es lo que tengo hasta ahora en 25 bytes: pyth.herokuapp.com/…
Maltysen
@ Maltysen, gracias, creo que puedes eliminarlo jkya que el resultado puede ser una lista.
Tyilo
Puedes ver mi comentario sobre la asignación en línea.
Maltysen
4

Código de máquina IA-32, 26 bytes

Hexdump del código:

60 8b 7c 24 24 8d 34 11 33 c0 2b d1 74 08 73 03
03 d6 40 aa eb f2 61 c2 04 00

Empecé con el siguiente código C:

void doit(int m, int n, uint8_t* out)
{
    int t = m;
    while (true)
    {
        if (t >= n)
        {
            t -= n;
            *out++ = 1;
        }
        else
        {
            t += m;
            *out++ = 0;
        }
        if (t == n)
            break;
    }
}

Escribe la salida en el búfer suministrado. No devuelve la longitud de la salida, pero en realidad no es necesaria: la longitud de salida siempre es m + n - 2:

int main()
{
    char out[100];
    int m = 10;
    int n = 3;
    doit(m, n, out);
    for (int i = 0; i < m + n - 2; ++i)
    {
        printf("%d ", out[i]);
    }
}

Para convertir el código C en código de máquina, primero hice algunos ajustes, para hacer que una de las if/elseramas esté vacía, y comparar con en 0lugar de n:

void doit(int m, int n, char* out)
{
    int t = n;
    while (true)
    {
        int r = 0;
        t -= m;
        if (t == 0)
            break;
        if (t >= 0)
        {
        }
        else
        {
            t += m + n;
            ++r;
        }
        *out++ = r;
    }
}

Desde aquí, escribir el código de ensamblaje en línea es sencillo:

__declspec(naked) void __fastcall doit(int x, int y, char* out)
{
    _asm
    {
        pushad;                 // save all registers
        mov edi, [esp + 0x24];  // edi is the output address
        lea esi, [ecx + edx];   // esi is the sum m+n
    myloop:                     // edx is the working value (t)
        xor eax, eax;           // eax is the value to write (r)
        sub edx, ecx;
        jz myout;
        jae mywrite;
        add edx, esi;
        inc eax;
    mywrite:
        stosb;                  // write one value to the output
        jmp myloop;
    myout:
        popad;                  // restore all registers
        ret 4;                  // return (discarding 1 parameter on stack)
    }
}
anatolyg
fuente
Tengo curiosidad: ¿por qué funciona este algoritmo?
xnor
@xnor Informalmente, rastrea la secuencia de fizzbuzz . Aquí testá la "distancia a buzz". Si la distancia es al menos n, ve fizz, de lo contrario ve buzz; actualizar la distancia; repita hasta que sea 0.
anatolyg
3

Python - 125 bytes

Utiliza un algoritmo muy simple, solo incrementa las coordenadas y detecta cuándo cruza las líneas e imprime. Estoy buscando traducir a Pyth.

a,b=input()
i=1e-4
x=y=l=o=p=0
k=""
while len(k)<a+b-2:x+=i*a;y+=i*b;k+="V"*int(x//1-o//1)+"H"*int(y//1-p//1);o,p=x,y
print k

Que mientras el bucle verifica el número de l ines y luego verifica si alguno de los valores superó un límite int restando.

Toma entradas como 39, 100de stdin e imprime como HHVHHHVHHVHHHVHHVHHHVHHVHHHVHHHVHHVHHHVHHVHHHVHHVHHHVHHHVHHVHHHVHHVHHHVHHVHHHVHHVHHHVHHHVHHVHHHVHHVHHHVHHVHHHVHHHVHHVHHHVHHVHHHVHHVHHHVHHstdout en una línea.

Maltysen
fuente
3

CJam, 15 bytes

Ll~_:*,\ff{%!^}

Pruébalo aquí

Imprime 01para V y10 para H.

Explicación

L          e# An empty list.
l~         e# Evaluate the input.
_:*,       e# [0, m*n).
\          e# The input (m and n).
ff{%!      e# Test if each number in [0, m*n) is divisible by m and n.
^}         e# If divisible by m, add an 10, or if divisible by n, add an 01 into
           e# the previous list. If divisible by neither, the two 0s cancel out.
           e# It's just for output. So we don't care about what the previous list
           e# is -- as long as it contains neither 0 or 1.

La línea diagonal cruza una línea horizontal por cada 1 / n de toda la línea diagonal, y cruza una línea vertical por cada 1 / m.

jimmy23013
fuente
¿Agregará una explicación para esto? Es muy intrigante, pero al menos desde un primer vistazo rápido no entiendo por qué funciona. Al jugar con él, noto que solo funciona para valores que son números primos relativos (que se dan en la descripción del problema), mientras que el mío funciona para todos los valores. Entonces, las matemáticas subyacentes son obviamente muy diferentes.
Reto Koradi
Después de dejar que se hunda un poco más, creo que entiendo al menos la parte del algoritmo. Tiene que estudiar la lógica de generación de salida más tarde.
Reto Koradi
@RetoKoradi Editado.
jimmy23013
2

TI-BASIC, 32

Prompt M,N
For(X,1,MN-1
gcd(X,MN
If log(Ans
Disp N=Ans
End

Sencillo. Utiliza una secuencia de 0y 1, separada por saltos de línea. Las ventajas de TI-BASIC son la gcd(multiplicación implícita y de dos bytes , pero sus desventajas son el bucle For que incluye el valor final y los 5 bytes gastados para la entrada.

lirtosiast
fuente
1

Haskell, 78 bytes

import Data.List
m#n=map snd$sort$[(x,0)|x<-[1..m-1]]++[(y*m/n,1)|y<-[1..n-1]]

Ejemplo de uso:

*Main> 19 # 17
[0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0]
*Main> 10 # 3
[0,0,0,1,0,0,0,1,0,0,0]

Cómo funciona: haga una lista de los valores de x de todos los cruces verticales (x,0)para xen [1,2, ..., m-1] ( 0indica vertical) y agregue la lista de los valores de x de todos los cruces horizontales (y*m/n,1)para yen [1,2, ..., n-1] ( 1indica horizontal). Ordenar y tomar los segundos elementos de los pares.

Maldición del día: nuevamente tengo que gastar 17 bytes en el importporque sortestá dentro Data.Listy no en la biblioteca estándar.

nimi
fuente
1

KDB (Q), 44 bytes

{"HV"0=mod[asc"f"$1_til[x],1_(x*til y)%y;1]}

Explicación

Encuentra todo x valores del eje de los puntos de intersección y ordénelos. Si mod 1 es cero, su "V", distinto de cero es "H".

Prueba

q){"HV"0=mod[asc"f"$1_til[x],1_(x*til y)%y;1]}[5;3]
"VHVVHV"
WooiKent Lee
fuente
1

CJam, 26 24 bytes

l~:N;:M{TN+Mmd:T;0a*1}*>

Pruébalo en línea

Muy sencillo, prácticamente una implementación directa de un algoritmo de tipo Bresenham.

Explicación:

l~    Get input and convert to 2 integers.
:N;   Store away N in variable, and pop from stack.
:M    Store away M in variable.
{     Loop M times.
  T     T is the pending remainder.
  N+    Add N to pending remainder.
  M     Push M.
  md    Calculate div and mod.
  :T;   Store away mod in T, and pop it from stack
  0a    Wrap 0 in array so that it is replicated by *, not multiplied.
  *     Emit div 0s...
  1     ... and a 1.
}*      End of loop over M.
>       Pop the last 1 and 0.

El último 01debe aparecer porque el bucle fue hasta el punto final, que no es parte de la salida deseada. Tenga en cuenta que podemos no sólo reducir el número de bucle 1. De lo contrario, para N > M, todos los 0s de la última iteración estarán ausentes, mientras que sólo necesitamos para deshacerse de la última 0.

Reto Koradi
fuente
1
Puedes usar >para ;W<.
jimmy23013
@ jimmy23013 Buena idea. Como sé que tengo una 1pila en la parte superior, también podría usarla productivamente.
Reto Koradi