Rotación real de Chebyshev

15

Este es un desafío inspirado en la rotación de Chebyshev . Sugiero buscar respuestas allí para inspirarse en este desafío.

Dado un punto en el plano, hay un cuadrado único (un rectángulo con lados iguales) que se centra en el origen e intersecta ese punto ( demostración interactiva ):

ingrese la descripción de la imagen aquí

Dado un punto p y una distancia d , devuelve el punto obtenido moviendo la distancia d desde p , en sentido antihorario (y en sentido horario para d negativo ), a lo largo del perímetro del cuadrado centrado en el origen que interseca p . Su respuesta debe tener una precisión de al menos 4 dígitos decimales.

Casos de prueba:

(0, 0), 100 -> (0, 0)
(1, 1), 81.42 -> (-0.4200, 1.0000)
(42.234, 234.12), 2303.34 -> (-234.1200, 80.0940)
(-23, -39.234), -234.3 -> (39.2340, -21.8960)

Los siguientes casos de prueba son del desafío original de Martin Ender, y todos son con d = 1 :

(0, 0)       -> (0, 0)
(1, 0)       -> (1, 1)
(1, 1)       -> (0, 1)
(0, 1)       -> (-1, 1)
(-1, 1)      -> (-1, 0)
(-1, 0)      -> (-1, -1)
(-1, -1)     -> (0, -1)
(0, -1)      -> (1, -1)
(1, -1)      -> (1, 0)
(95, -12)    -> (95, -11)
(127, 127)   -> (126, 127)
(-2, 101)    -> (-3, 101)
(-65, 65)    -> (-65, 64)
(-127, 42)   -> (-127, 41)
(-9, -9)     -> (-8, -9)
(126, -127)  -> (127, -127)
(105, -105)  -> (105, -104)
orlp
fuente
¿No podrían casi todos estos cambios solo ligeramente del otro desafío? Esto parece una adición innecesaria.
ATaco
1
@ATaco No, es bastante más complicado.
orlp
¿Debería calcularse la distancia a lo largo del perímetro comenzando en p?
Gábor Fekete
@ GáborFekete ¿Qué más?
orlp
Sí, ya veo, los casos de prueba implican esto, pero no se menciona explícitamente. Al principio pensé que comenzaría en la intersección positiva en el eje x.
Gábor Fekete

Respuestas:

4

Python 2, 363 335 296 266 262 258 256 233 Bytes

Woo, 130 bytes perdidos! ¡Gracias a Neil por guardar 4 bytes, Nathan Merrill por guardar 2 bytes y xnor por guardar unos ridículos 23 bytes!

La idea general es esta: podemos reducir la distancia recorrida tomando su módulo contra el perímetro del cuadrado. El perímetro se define como 8 veces la mayor de las dos coordenadas, ya que el punto debe descansar sobre él. Luego, después de tomar el módulo, se garantiza que no habrá superposición. También garantiza que solo tengamos que movernos en sentido antihorario, ya que el módulo da un resultado positivo.

A partir de ahí, simplemente uso lo que sabemos de las coordenadas x e y dadas para determinar dónde estamos: arriba, abajo, izquierda, derecha o en una esquina, y determinar la dirección, que puede ser una de 0, 1, 2, 3:

0 --> we are on the 'top', moving 'left'
1 --> we are on the 'left', moving 'down'
2 --> we are on the 'bottom', moving 'right'
3 --> we are on the 'right', moving 'up'

Después de esto, es tan simple como hacer un bucle mientras todavía tenemos que recorrer la distancia, y en función de la dirección que restamos o sumamos a la coordenada apropiada, y le decimos al bucle en qué dirección vamos a seguir.

p,d=input()
x,y=p
s=max(x,y,-x,-y)
d=d%(s*8or 1)
r=[(y<s)*[2,[3,x>-s][x<s]][y>-s],[2*(y<0),3*(y<=0)][x>0]][y*y==x*x]
while s>0<d:f=1-2*(r<2);m=abs(f*s-p[r%2]);j=d>m;p[r%2]=[p[r%2]+f*d,f*s][j];r=-~r%4;d=(d-m)*j
print"%.4f "*2%tuple(p)

Si bien es bastante largo, ciertamente funciona. Aquí hay algunos ejemplos de E / S:

[0, 0], 100 --> 0.0000 0.0000
[1, 1], 81.42 --> -0.4200 1.0000
[42.234, 234.12], 2303.34 --> -234.1200 80.0940
[-23, -39.234], -234.3 --> 39.2340 -21.8960

Pruébelo en línea o ejecute casos de prueba .

Kade
fuente
Funciona s=max(x,y,-x,-y)?
Neil
@Neil Lo hace, ¡muchas gracias!
Kade
(s>0)*(d>0)es s>0<d. La salida puede ser "%.4f "*2%tuple(p). if s:d=d%(8*s)puede ser d%(s*8or 1). (r+1)puede ser ~-r. 1*(x>-s)puede ser simplemente (x>-s). abs(y)==abs(x)puede sery*y==x*x
xnor
@xnor Wow, gracias! Solo los delgados que cambié son los que (x>-s)no necesitaban paréntesis y ~-rdecrementos, así que solía -~r.
Kade
3

JavaScript (ES6), 147 bytes

f=(x,y,d,s=Math.max(x,y,-x,-y),c=(d/8%s+s)%s*8,v=0,w=x+y>0?1:-1,b=(v?x:y)*w+c-s)=>c?b>0?f(v?s*w:x,v?y:s*w,d,s,b,!v,v?w:-w):[x+c*w*v,y+c*w*!v]:[x,y]

Explicación: funciona intentando agregar el vector de dirección mientras se mantiene dentro de los límites del cuadrado. Cualquier sobreimpulso se hace retroceder recursivamente con la dirección girada en sentido antihorario 90 °. La dirección se codifica realmente usando una bandera vertical vy una unidad wpara que los vectores (1, 0), (0, 1), (-1, 0) y (0, -1) se codifiquen con v0, 1, 0 , 1 y wde 1, 1, -1, -1 respectivamente. El vector de dirección puede no apuntar inicialmente en una dirección adecuada, pero nunca apunta hacia atrás, por lo que eventualmente rotará en una dirección utilizable.

f=(x,y,d,                   Input parameters
 s=Math.max(x,y,-x,-y),     Calculate half the side of the square
 c=(d/8%s+s)%s*8,           Reduce the distance modulo the perimeter
 v=0,                       Initial vertical flag
 w=x+y>0?1:-1,              Initial direction
 b=(v?x:y)*w+c-s)=>         Will we overshoot the corner?
  c?b>0?f(v?s*w:x,v?y:s*w,  Advance to the next corner
          d,s,b,!v,v?w:-w): Rotate the direction
        [x+c*w*v,y+c*w*!v]: Advance the remaining amout
    [x,y]                   Nothing to do, zero input
Neil
fuente
Esto podría deberse a que mi navegador (Opera 40.0.2308.81), pero parece que tiene un pequeño error de redondeo, f(42.234, 234.12, 2303.34) -> [-234.12, 80.09399999999988]lo que significa que no tiene una precisión de 4 dígitos. ¿Quizás agregar algún formato de salida solucionaría esto? Buena respuesta sin embargo! :)
Kade
@Shebang Técnicamente, el formato de salida requeriría redondeo y, por lo tanto, introduciría un posible error de redondeo. Los números generados son los más cercanos posibles dentro de los límites de la aritmética de coma flotante, lo que no debe esperarse que dé resultados exactos para representaciones decimales arbitrarias. Apégate a las fracciones binarias si quieres respuestas exactas.
Neil