Prawdziwa rotacja Czebyszewa

15

Jest to wyzwanie inspirowane rotacją Czebyszewa . Proponuję przyjrzeć się tam odpowiedziom, aby uzyskać inspirację do tego wyzwania.

W punkcie na płaszczyźnie znajduje się unikalny kwadrat (prostokąt o równych bokach), który jest wyśrodkowany na początku i przecina ten punkt ( interaktywne demo ):

wprowadź opis zdjęcia tutaj

Biorąc pod uwagę punkt p i odległość d , zwróć punkt uzyskany przez przesunięcie odległości d od p , przeciwnie do ruchu wskazówek zegara (i zgodnie z ruchem wskazówek zegara dla ujemnego d ), wzdłuż obwodu kwadratu wyśrodkowanego na początku, który przecina p . Twoja odpowiedź musi zawierać co najmniej 4 cyfry dziesiętne.

Przypadki testowe:

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

Następujące przypadki testowe pochodzą z oryginalnego wyzwania Martina Endera i wszystkie mają wartość 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
źródło
Czy prawie wszystkie z nich nie mogą być tylko nieznacznie zmienione w porównaniu z innym wyzwaniem? To wydaje się niepotrzebnym dodatkiem.
ATaco
1
@ATaco Nie, to trochę bardziej skomplikowane.
orlp
Czy odległość należy obliczać wzdłuż obwodu, zaczynając od p?
Gábor Fekete
@ GáborFekete Co jeszcze?
orlp
Tak, rozumiem, przypadki testowe implikują to, ale nie zostało to wyraźnie określone. Na początku myślałem, że zacznie się od dodatniego przecięcia na osi x.
Gábor Fekete

Odpowiedzi:

4

Python 2, 363 335 296 266 262 258 256 233 bajtów

Woo, 130 bajtów utraconych! Dzięki Neil za uratowanie 4 bajtów, Nathan Merrill za uratowanie 2 bajtów i xnor za uratowanie śmiesznych 23 bajtów!

Ogólna idea jest następująca: możemy zmniejszyć przebytą odległość, przyjmując jej moduł względem obwodu kwadratu. Obwód jest definiowany jako 8-krotność największej z dwóch współrzędnych, ponieważ punkt musi na nim spoczywać. Następnie, po przyjęciu modułu, gwarantujemy, że nie zachodzą na siebie. Gwarantuje to również, że zawsze musimy poruszać się w kierunku przeciwnym do ruchu wskazówek zegara, ponieważ moduł daje pozytywny wynik.

Stamtąd używam po prostu tego, co wiemy z podanych współrzędnych xiy, aby dowiedzieć się, gdzie jesteśmy: góra, dół, lewo, prawo lub w rogu i określić kierunek, który może być jednym z 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'

Po tym jest to tak proste, jak zapętlanie, gdy jeszcze mamy odległość do przebycia, i na podstawie kierunku, który odejmujemy lub dodajemy do odpowiedniej współrzędnej, i mówimy pętli, w którym kierunku idziemy dalej.

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)

Chociaż dość długo, z pewnością działa. Oto kilka przykładowych operacji wejścia / wyjścia:

[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

Wypróbuj online lub uruchom testy .

Kade
źródło
Czy s=max(x,y,-x,-y)działa
Neil,
@Neil Tak, dziękuję!
Kade
(s>0)*(d>0)jest s>0<d. Dane wyjściowe mogą być "%.4f "*2%tuple(p). if s:d=d%(8*s)może być d%(s*8or 1). (r+1)może być ~-r. 1*(x>-s)może po prostu być (x>-s). abs(y)==abs(x)może byćy*y==x*x
xnor
@xnor Wow, dzięki! Zmieniłem tylko te cienkie, które (x>-s)nie wymagały nawiasów ani ~-rdekretu, więc użyłem -~r.
Kade
3

JavaScript (ES6), 147 bajtów

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]

Objaśnienie: Działa, próbując dodać wektor kierunku, trzymając się w granicach kwadratu. Każde przekroczenie jest rekurencyjnie cofane z kierunkiem obróconym w kierunku przeciwnym do ruchu wskazówek zegara o 90 °. Kierunek jest faktycznie kodowany za pomocą flagi pionowej vi jednostki, wtak że wektory (1, 0), (0, 1), (-1, 0) i (0, -1) są kodowane z v0, 1, 0 , 1 i wodpowiednio 1, 1, -1, -1. Wektor kierunku może początkowo nie wskazywać odpowiedniego kierunku, ale nigdy nie jest skierowany do tyłu, więc ostatecznie obróci się w użytecznym kierunku.

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
źródło
Może to być spowodowane tym, że moja przeglądarka (Opera 40.0.2308.81), ale wygląda na to, że ma trochę błędu zaokrąglania, f(42.234, 234.12, 2303.34) -> [-234.12, 80.09399999999988]co oznacza, że ​​nie ma 4-cyfrowej precyzji. Może dodanie formatowania wyjściowego to naprawi? Dobra odpowiedź! :)
Kade
@Shebang Techniczne formatowanie wyjściowe wymagałoby zaokrąglenia, a zatem wprowadziłoby potencjalny błąd zaokrąglania. Wygenerowane liczby są możliwie najbliższe w granicach arytmetyki zmiennoprzecinkowej, co nie powinno dawać dokładnych wyników dla dowolnych reprezentacji dziesiętnych. Trzymaj się ułamków binarnych, jeśli chcesz dokładnych odpowiedzi.
Neil,