Cuenta los cuadrados

18

Desafío

Origami (papel plegable) es una forma creativa de arte. Hasta donde yo sé, el maestro de Origami prefiere el papel cuadrado. Comencemos desde el principio: convierta un papel rectangular en uno cuadrado.

Entonces el papel se divide en cuadrados. Eliminamos el cuadrado más grande que comparte un borde más corto con la forma actual, paso a paso (ver la imagen a continuación). Y si la parte restante después de un paso es menor o igual que 0.001 * (area of the original paper), el papel no se puede dividir más. Es posible que al final no quede nada.

Su tarea es calcular cuántos cuadrados se forman durante el proceso. El cuadrado en el último paso que hace que el papel no se pueda dividir se cuenta en la salida.

Ejemplo (un papel de 1.350ancho / alto), la salida es 10:

ejemplo de corte

Entrada y salida

Entrada: relación ancho / alto para el papel rectangular, un decimal (o un entero sin el punto) de 1.002a 1.999con un paso mínimo de 0.001. También puede usar cualquier otro formato razonable que describa la relación. Solo mencionalo en tu respuesta.

Salida: recuento cuadrado, un entero.

Ejemplo de E / S

Se utiliza un formato de mapeo para mantener ordenada la página, mientras que su código no necesita admitir una entrada de lista ni ser una función de mapeo.

1.002 => 251
1.003 => 223
1.004 => 189
1.005 => 161
1.006 => 140
1.007 => 124
1.008 => 111
1.009 => 100

Lista de todas las respuestas.

Gracias a @LuisMendo, aquí está el gráfico de respuestas.

grafico

Observaciones

  • Este es un código de golf, por lo que el código más corto gana
  • Presta atención a las lagunas estándar
  • Es su libertad decidir cómo manejar la entrada y salida, pero deben seguir las restricciones estándar.

Por cierto...

  • Comenta si tienes algo poco claro sobre el desafío
  • Personalmente, sugeriría que su respuesta contenga una explicación si está utilizando un lenguaje de golf
  • Gracias a @GregMartin, lea su respuesta para una buena explicación matemática del desafío.

Código de ejemplo

Aquí hay una versión no codificada del código C ++:

#include <iostream>
#include <utility>

int f (double m)
{
    double n = 1, k = 0.001;
    int cnt = 0;
    k *= m;                       // the target minimum size
    while(m*n >= k)
    {
        m -= n;                   // extract a square
        if(n > m)
            std::swap(n, m);      // keep m > n
        ++ cnt;
    }
    return cnt;
}

int main()
{
    double p;
    std::cin >> p;
    std::cout << f(p);
    return 0;
}

Todos los cálculos relacionados en el código de ejemplo necesitan una precisión de 6 dígitos decimales, que está cubierto float.

Keyu Gan
fuente
¿Pueden usarse dos números que forman la razón como entradas?
Luis Mendo
@LuisMendo sí, como tu deseo.
Keyu Gan el
2
¡Buen desafío!
flawr
55
La lista de respuestas produce un buen gráfico
Luis Mendo
1
@KeyuGan ¡Por supuesto, adelante! Avíseme si necesita una versión con algún otro formato
Luis Mendo

Respuestas:

2

MATL , 19 bytes

`SZ}y-htG/p1e-3>}x@

La entrada es una matriz con los dos números que definen la relación original, como [1, 1.009]. (No es necesario que los números se ordenen o que uno de ellos sea 1.)

Pruébalo en línea!

Explicación

`        % Do...while loop
  S      %   Sort array. Takes 1×2 array as input (implicit) the first time
  Z}     %   Split array into its 2 elements: first the minimum m, then the maximum M
  y      %   Duplicate m onto the top of the stack. The stack now contains m, M, m
  -      %   Subtract. The stack now contains m, M-m
  h      %   Concatenate into [m, M-m]. This is the remaining piece of paper
  t      %   Duplicate
  G/     %   Divide by input, element-wise
  p      %   Product of array. Gives ratio of current piece's area to initial area
  1e-3>  %   True if this ratio exceeds 1e-3. In that case the loop continues
}        % Finally (execute after last iteration, but still within the loop)
  x      %   Delete last piece of paper
  @      %   Push current loop counter. This is the result
         % End (implicit)
         % Display (implicit)
Luis Mendo
fuente
6

Haskell , 71 70 65 63 62 61 58 56 bytes

¡Gracias a @xnor por algunas mejoras ingeniosas!

(n#m)e|e>n*m*1e3=0|n<m=m#n$e|d<-n-m=(d#m)e+1
n!m=n#m$n*m

Pruébalo en línea!

falla
fuente
Piensa que m==nal final puede ser 1>0porque es la única posibilidad que queda. O, tal vez los casos podrían reorganizarse para permitir un enlace aquí.
xnor
En realidad, ¿es necesario el caso de igualdad? Si n>mse expande n>=my se escribe el primer cheque e>m*n*1000, eso debería dar 1igualdad.
xnor
@xnor Buena idea, gracias!
falla
1
Moviéndose alrededor de los guardias por 56:(n#m)e|e>n*m*1e3=0|n<m=m#n$e|d<-n-m=(d#m)e+1;n!m=n#m$n*m
xnor
¡Guau, usar el d<-n-mas otherwisees realmente genial!
flawr
4

JavaScript (ES6), 59 58 bytes

f=(m,n=!(k=m/1e3,c=0))=>m*n<k?c:(c++,m-=n)<n?f(n,m):f(m,n)

Prueba

Arnauld
fuente
4

Mathematica, no competitiva (21 bytes)

¡Esta respuesta no es competitiva porque no responde la pregunta real! Pero sí responde una variante de la pregunta, y proporciona una excusa para resaltar algunas matemáticas interesantes.

Tr@*ContinuedFraction

Función simbólica que toma un número racional positivo como entrada (cuyo numerador y denominador representan las dimensiones del papel original) y devuelve un entero positivo. Por ejemplo, Tr@*ContinuedFraction[1350/1000]vuelve 10. ( ContinuedFractionactúa de manera diferente en los números de coma flotante debido a problemas de precisión, por lo que se necesita un número racional como entrada en este contexto).

¡Una interpretación interesante del procedimiento geométrico descrito en el problema (cortar cuadrados de un rectángulo repetidamente) es que es una implementación del algoritmo euclidiano para encontrar los divisores comunes más grandes! Considere el ejemplo en la pregunta misma, con relación1.35, que podría modelarse teniendo un trozo de papel con dimensiones (1350,1000). Cada vez que se corta un cuadrado, el número más pequeño se resta del número más grande; entonces los rectángulos resultantes en este ejemplo tienen dimensiones (350,1000), luego (350,650), luego (350,300), luego (50,300), luego (50,250) y (50,200) y (50,150) y (50,100) y (50, 50), y también (50,0) una vez que quitamos el último cuadrado de sí mismo. Así es exactamente como funciona el algoritmo euclidiano (módulo la diferencia entre división y sustracción repetida), y de hecho vemos que 50 es de hecho el MCD de 1350 y 1000.

Típicamente en el algoritmo euclidiano, uno realiza un seguimiento de estas dimensiones intermedias y descarta el número de sustracciones; sin embargo, uno puede registrar alternativamente cuántas veces restamos un número del otro antes de que la diferencia sea demasiado pequeña y tengamos que cambiar lo que estamos restando. Ese método de grabación es precisamente la fracción continua de un número racional. (Las fracciones continuas de números irracionales, que nunca terminan, también son súper geniales, pero no relevantes aquí.) Por ejemplo, en el ejemplo 1350/1000, restamos 1000 1veces, luego 350 2veces, luego 300 1veces, luego 50 6veces; por lo tanto, la fracción continua de 1350/1000 es {1,2,1,6}. Matemáticamente, hemos reescrito 1350/1000 como 1+ 1 / ( 2+ 1 / ( 1+ 1 /6)), que puedes verificar.

Entonces, para este problema, si no se detiene cuando los cuadrados se hacen más pequeños que un determinado umbral, sino que simplemente cuenta todos los cuadrados finitos antes de que se detenga, entonces el número total de cuadrados es igual al número total de sustracciones, es decir la suma de todos los enteros en la fracción continua, ¡y eso es precisamente lo que Tr@*ContinuedFractioncalcula la composición de funciones ! (Para el ejemplo dado 1.35, obtiene la respuesta que desea el OP, porque el cuadrado final es lo suficientemente grande como para contar todos los cuadrados. Pero Tr@*ContinuedFraction[1001/1000], por ejemplo, rinde 1001, ya que cuenta el cuadrado enorme y los 1000 de los cuadrados pequeños 1x1000 .)

Greg Martin
fuente
1
Si bien esto es realmente interesante, la etiqueta no competitiva está reservada para idiomas que son más nuevos que el desafío . Independientemente de eso, todas las respuestas deben resolver el desafío. Por lo tanto, esta respuesta realmente debería eliminarse. ¿Sería capaz de reconstruir a partir de la lista de fracciones continuas dónde cortarlo para que esto pueda convertirse en una solución igualmente interesante pero válida?
Martin Ender
1
Tenía un picor mental que rascar cuando escribía esta respuesta, pero estoy de acuerdo en que esta es una respuesta que vale la pena eliminar de acuerdo con los estándares de la comunidad. (¡Gracias por sus comentarios suaves pero precisos!) Si TPTB desea retrasar su eliminación durante 24 horas, podría completar el enfoque para obtener la respuesta correcta ... si no, elimínelo y no tenga resentimientos.
Greg Martin
3

Mathematica, 64 53 bytes

({t=1,#}//.{a_,b_}/;1000a*b>#:>Sort@{++t;a,b-a};t-1)&

Una solución imperativa (estilo C) tiene exactamente la misma longitud:

(For[t=a=1;b=#,1000a*b>#,If[a>b,a-=b,b-=a];++t];t-1)&
Martin Ender
fuente
2

C (CCG / Clang), 61 59 bytes

c,k;f(m,n){for(k=m*n;m*n/k;m>n?(m-=n):(n-=m))++c;return c;}

La entrada es dos enteros (ancho y alto) sin punto, como f(1999,1000).

Espero que alguien pueda salvar un byte empujando a C al club de 58 bytes. ;)

Keyu Gan
fuente
Sugerir eliminar los paréntesis alrededorm-=n
ceilingcat
1

C, 59 bytes

s,a,n=1e3;C(m){for(a=m;m*n>a;s++)m>n?m-=n:(n-=m);return s;}

Pruébalo en línea

La entrada es un número entero que es la relación ancho / alto en milésimas (por ejemplo, 1002 para 1.002: 1).

Versión sin golf

int C(int m)
{
    int n = 1000;
    int a = m;
    int s = 0;

    while (m * n > a)
    {
        if (m > n)
            m -= n;
        else
            n -= m;

        s++;
    }

    return s;
}
bmann
fuente