Реальный Чебышевский Вращение

15

Это вызов, вдохновленный вращением Чебышева . Я предлагаю посмотреть на ответы там, чтобы получить вдохновение для этого вызова.

Для данной точки на плоскости существует уникальный квадрат (прямоугольник с равными сторонами), который центрируется в начале координат и пересекает эту точку ( интерактивная демонстрация ):

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

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

Testcases:

(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)
orlp
источник
Разве почти все они не могут быть слегка изменены по сравнению с другими проблемами? Это кажется ненужным дополнением.
ATaco
1
@ATaco Нет, все немного сложнее.
orlp
Следует ли рассчитывать расстояние по периметру, начиная с p?
Габор Фекете
@ GáborFekete Что еще?
orlp
Да, я вижу, тестовые примеры подразумевают это, но это не указано явно. Сначала я подумал, что это начнется с положительного пересечения по оси X.
Габор Фекете

Ответы:

4

Python 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)?
Нил
@ Нейл Да, спасибо большое!
Каде
(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] означающая, что он не имеет 4-значной точности. Может быть, добавление выходного форматирования исправит это? Хороший ответ, хотя! :)
Каде
@Shebang Технически форматирование вывода потребует округления и, следовательно, внесения потенциальной ошибки округления. Сгенерированные числа являются самыми близкими возможными в пределах арифметики с плавающей точкой, которая не должна давать точные результаты для произвольных десятичных представлений. Придерживайтесь двоичных дробей, если хотите точных ответов.
Нил