Справжнє обертання Чебишева


15

Це виклик, натхненний Чебішевим Ротацією . Я пропоную переглянути відповіді, щоб отримати натхнення для цього виклику.

Враховуючи точку на площині, є унікальний квадрат (прямокутник з рівними сторонами), який зосереджений на початку та перетинає цю точку ( інтерактивна демонстрація ):

введіть тут опис зображення

Задавши точку p та відстань d , поверніть точку, отриману, переміщуючи відстань d від p , проти годинникової стрілки (і за годинниковою стрілкою за від’ємною d ) по периметру квадрата, орієнтованого на початок, що перетинає p . Ваша відповідь повинна бути точною, принаймні до 4-х цифр після коми.

Тести:

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

Наступні тестові випадки - це оригінальний виклик Мартіна Ендера, і всі вони мають 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)

Чи не можна майже все це змінити лише злегка від іншого виклику? Це здається непотрібним доповненням.
Атако

1
@ATaco Ні, це зовсім трохи складніше.
orlp

Чи слід обчислювати відстань по периметру, починаючи з p?
Габор Фекете

@ GáborFekete Що ще?
orlp

Так, я бачу, тестові випадки мають на увазі це, але це не вказано прямо. Спочатку я думав, що він розпочнеться з позитивного перетину на осі x.
Габор Фекете

Відповіді:


4

Пітон 2, 363 335 296 266 262 258 256 233 байт

Ву, втрачено 130 байтів! Дякуємо Нілу за збереження 4 байтів, Натану Мерріл за збереження 2 байтів та xnor за збереження смішних 23 байтів!

Загальна ідея така: ми можемо зменшити пройдену відстань, взявши його модуль по периметру квадрата. Периметр визначається як 8 разів найбільший з двох координат, оскільки точка повинна опиратися на нього. Потім, після прийняття модуля, ми гарантовано не будемо перекриватись. Це також гарантує, що нам залишається рухатися лише проти годинникової стрілки, оскільки модуль дає позитивний результат.

Звідти я просто використовую те, що ми знаємо з вказаних координат x і y, щоб визначити, де ми: вгорі, внизу, вліво, вправо або в кутку, і визначити напрямок, який може бути одним із 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'

Після цього це так само просто, як циклічно, поки нам ще належить пройти відстань, і на основі напрямку ми віднімаємо чи додаємо до відповідної координати та повідомляємо циклу, у якому напрямку йдемо далі.

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)

Незважаючи на те, що це досить довго, воно, безумовно, працює. Ось кілька прикладів вводу / виводу:

[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

Спробуйте в Інтернеті або запустити тестові приклади .


Чи s=max(x,y,-x,-y)працює?
Ніл

@Neil Це, дякую купу!
Кейд

(s>0)*(d>0)є s>0<d. Вихід може бути "%.4f "*2%tuple(p). if s:d=d%(8*s)може бути d%(s*8or 1). (r+1)може бути ~-r. 1*(x>-s)може просто бути (x>-s). abs(y)==abs(x)може бутиy*y==x*x
xnor

@xnor Вау, дякую! Тільки тонки, які я змінив - це те, що (x>-s)не потрібні дужки та ~-rдекременти, тому я використовував -~r.
Кейд

3

JavaScript (ES6), 147 байт

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]

Пояснення: Працює, намагаючись додати вектор напряму, зберігаючи в межах квадрата. Будь-який перевищення рекурсивно передається назад з напрямком, повернутим проти годинникової стрілки на 90 °. Напрямок кодується фактично за допомогою вертикального прапора vта одиниці, wтак що вектори (1, 0), (0, 1), (-1, 0) і (0, -1) кодуються з v0, 1, 0 , 1 і w1, 1, -1, -1 відповідно. Вектор напряму може спочатку не вказувати у відповідному напрямку, але він ніколи не вказує назад, тому з часом повернеться у придатному напрямку.

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

Це може бути тому, що мій веб-переглядач (Opera 40.0.2308.81), але схоже, що він має трохи помилок округлення, f(42.234, 234.12, 2303.34) -> [-234.12, 80.09399999999988]тобто він не має чотиризначної точності. Можливо, додавання вихідного форматування виправить це? Хороша відповідь, хоча! :)
Каде

@Shebang Технічно форматування виходу вимагає округлення, а отже, вводить потенційну помилку округлення. Згенеровані числа є найближчими можливими в межах арифметики з плаваючою комою, що не слід очікувати, що дасть точні результати для довільних десяткових представлень. Дотримуйтесь двійкових дробів, якщо ви хочете точних відповідей.
Ніл
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.