Rotação Chebyshev real

15

Este é um desafio inspirado pela rotação de Chebyshev . Sugiro que procure respostas para obter inspiração para esse desafio.

Dado um ponto no plano, há um quadrado único (um retângulo com lados iguais) centralizado na origem e cruzando esse ponto ( demonstração interativa ):

insira a descrição da imagem aqui

Dado um ponto p e uma distância d , retorne o ponto obtido movendo a distância d de p , no sentido anti-horário (e no sentido horário para d negativo ), ao longo do perímetro do quadrado centrado na origem que cruza p . Sua resposta deve ter precisão de pelo menos 4 dígitos decimais.

Casos de teste:

(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)

Os seguintes casos de teste são do desafio original de Martin Ender e todos estão com 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
fonte
Quase todos eles não poderiam ser apenas ligeiramente alterados em relação ao outro desafio? Parece uma adição desnecessária.
ATaco 17/10
1
@ ATaco Não, é um pouco mais complicado.
Orlp 17/10/16
A distância deve ser calculada ao longo do perímetro começando em p?
Gábor Fekete 17/10
@ GáborFekete O que mais?
orlp
Sim, entendo, os casos de teste implicam isso, mas não são declarados explicitamente. Primeiro pensei que começaria na interseção positiva no eixo x.
Gábor Fekete 17/10

Respostas:

4

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

Woo, 130 bytes perdidos! Agradecemos a Neil por salvar 4 bytes, Nathan Merrill por salvar 2 bytes e xnor por salvar ridículos 23 bytes!

A idéia geral é a seguinte: podemos reduzir a distância percorrida tomando o módulo dela contra o perímetro da praça. O perímetro é definido como 8 vezes a maior das duas coordenadas, pois o ponto deve estar sobre ele. Depois que o módulo é tomado, garantimos que não temos sobreposição. Ele também garante que só precisamos avançar no sentido anti-horário, pois o módulo fornece um resultado positivo.

A partir daí, basta usar o que sabemos das coordenadas x e y fornecidas para descobrir onde estamos: superior, inferior, esquerda, direita ou em um canto e determinar a direção, que pode ser uma das seguintes 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'

Depois disso, é tão simples quanto fazer um loop enquanto ainda temos distância a percorrer, e com base na direção em que subtraímos ou adicionamos à coordenada apropriada e diz ao loop em qual direção vamos 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)

Embora bastante longo, certamente funciona. Aqui está um exemplo 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

Experimente online ou execute casos de teste .

Kade
fonte
Funciona s=max(x,y,-x,-y)?
Neil
@ Neil Sim, muito obrigado!
Kade
(s>0)*(d>0)é s>0<d. A saída pode ser "%.4f "*2%tuple(p). if s:d=d%(8*s)pode ser d%(s*8or 1). (r+1)pode ser ~-r. 1*(x>-s)pode ser apenas (x>-s). abs(y)==abs(x)pode sery*y==x*x
xnor 18/10
@xnor Uau, obrigado! Somente as que mudei foram as que (x>-s)não precisavam de parênteses e ~-rdecrementos, então usei -~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]

Explicação: Funciona tentando adicionar o vetor de direção, mantendo-o dentro dos limites do quadrado. Qualquer superação é recursivamente passada de volta com a direção girada no sentido anti-horário em 90 °. A direção é realmente codificada usando uma flag vertical ve uma unidade wpara que os vetores (1, 0), (0, 1), (-1, 0) e (0, -1) sejam codificados com v0, 1, 0 , 1 e wde 1, 1, -1, -1, respectivamente. O vetor de direção pode não apontar inicialmente para uma direção adequada, mas nunca aponta para trás e, por fim, girará para uma direção utilizável.

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
fonte
Isso pode ser devido ao meu navegador (Opera 40.0.2308.81), mas parece que há um pequeno erro de arredondamento, pois f(42.234, 234.12, 2303.34) -> [-234.12, 80.09399999999988]significa que ele não tem precisão de 4 dígitos. Talvez adicionar alguma formatação de saída corrija isso? Boa resposta embora! :)
Kade
@Shebang Tecnicamente, a formatação de saída exigiria arredondamentos e, portanto, introduziria um possível erro de arredondamento. Os números gerados são os mais próximos possíveis dentro dos limites da aritmética de ponto flutuante, que não deve ser esperado para fornecer resultados exatos para representações decimais arbitrárias. Atenha-se a frações binárias se quiser respostas exatas.
Neil