Трикутна відстань на Манхеттені


26

The Манхеттен відстань на регулярній сітці число ортогональних кроків потрібно вжити , щоб досягти одну клітку від іншої. Ортогональні сходинки - це ті, що проходять через краї комірок сітки (на відміну від кутів, які давали б нам відстань Чебишева ).

Ми можемо визначити подібну відстань на інших сітках, наприклад трикутної сітки. Ми можемо адресувати окремі комірки в сітці за такою схемою індексації, де кожна комірка містить anx,y пару:

    ____________________________________...
   /\      /\      /\      /\      /\
  /  \ 1,0/  \ 3,0/  \ 5,0/  \ 7,0/  \
 / 0,0\  / 2,0\  / 4,0\  / 6,0\  / 8,0\
/______\/______\/______\/______\/______\...
\      /\      /\      /\      /\      /
 \ 0,1/  \ 2,1/  \ 4,1/  \ 6,1/  \ 8,1/
  \  / 1,1\  / 3,1\  / 5,1\  / 7,1\  /
   \/______\/______\/______\/______\/___...
   /\      /\      /\      /\      /\
  /  \ 1,2/  \ 3,2/  \ 5,2/  \ 7,2/  \
 / 0,2\  / 2,2\  / 4,2\  / 6,2\  / 8,2\  
/______\/______\/______\/______\/______\...
\      /\      /\      /\      /\      /
 \ 0,3/  \ 2,3/  \ 4,3/  \ 6,3/  \ 8,3/
  \  / 1,3\  / 3,3\  / 5,3\  / 7,3\  /
   \/______\/______\/______\/______\/___...
   /\      /\      /\      /\      /\
  .  .    .  .    .  .    .  .    .  .
 .    .  .    .  .    .  .    .  .    .

Тепер відстань на Манхеттені в цій сітці знову - це мінімальна кількість кроків по краях, щоб дістатися від однієї комірки до іншої. Таким чином, ви можете перейти від 3,1до2,1 , 4,1або 3,2, але не до іншого трикутника, оскільки це будуть точки перетину, а не ребра.

Так , наприклад, відстань від 2,1до 5,2це 4. Найкоротший шлях, як правило, не унікальний, але один із способів зробити відстань за 4 кроки - це:

2,1 --> 3,1 --> 3,2 --> 4,2 --> 5,2

Змагання

З огляду на дві координатні пари та з вищевказаної схеми адресації поверніть відстань між ними на Манхеттен.x1,y1x2,y2

Ви можете припустити, що всі чотири входи є невід’ємними цілими числами, кожен менше 128. Ви можете приймати їх у будь-якому порядку та довільно групувати (чотири окремих аргументи, список чотирьох цілих чисел, дві пари цілих чисел, матриця 2x2, .. .).

Ви можете написати програму або функцію та використовувати будь-який із стандартних методів отримання вводу та надання виводу.

Ви можете використовувати будь-яку мову програмування , але зауважте, що ці лазівки за замовчуванням заборонені.

Це , тому найкоротший вірний відповідь - вимірюється в байтах - виграє.

Випробування

Кожен тестовий випадок задається як .x1,y1 x2,y2 => result

1,2 1,2 => 0
0,1 1,1 => 1
1,0 1,1 => 3
2,1 5,2 => 4
0,0 0,127 => 253
0,0 127,0 => 127
0,0 127,127 => 254
0,127 127,0 => 254
0,127 127,127 => 127
127,0 127,127 => 255
75,7 69,2 => 11
47,58 36,79 => 42
77,9 111,23 => 48
123,100 111,60 => 80
120,23 55,41 => 83
28,20 91,68 => 111
85,107 69,46 => 123
16,25 100,100 => 159
62,85 22,5 => 160
92,26 59,113 => 174
62,22 35,125 => 206

Чи повинні входити в офіційні лазівки лазівки, які отримали чисті негативні оцінки?
DavidC

@DavidC Ні. З питань лазівки: "[...] лазівка, описана у будь-якій відповіді, яка знаходиться на рівні +5 або вище і має щонайменше вдвічі більше обновлених даних, ніж знищені канали, може вважатись неприйнятною для спільноти "
Мартін Ендер

Чи можемо ми взяти п'ятий вхід, який починається з 0 за замовчуванням (результат)? Тоді мені не потрібно буде додавати (a,b,x,y)->c(a,b,x,y,0)(виклик розділеного методуc з чотирма аргументами і 0як п'ятий аргумент) до своєї відповіді.
Kevin Cruijssen

3
@KevinCruijssen Не вибачте. Додаткові, виправлені аргументи є занадто легко зловживаючими (і просто дозволяти 0 як особливий вигляд здається дивним).
Мартін Ендер

@MartinEnder Добре, думав так, але ніколи не завадить запитати. У цьому випадку моя 190-байтна відповідь залишається. Незважаючи на те, що я відповів наполовину рік тому, один тестовий випадок не вдався. Я знову перейшов до цього питання і зміг виправити помилку у моїй відповіді.
Kevin Cruijssen

Відповіді:


7

JavaScript (ES6), 84 78 байт

Збережено 6 байт завдяки Нілу

(a,b,c,d,x=a>c?a-c:c-a,y=b>d?b-d:d-b,z=x>y?x:y)=>y+z+(x+z&1?a+b+(b>d)&1||-1:0)

Тестові справи

Початковий рекурсивний розчин, 100 88 81

Збережено 12 байт завдяки ETHproductions
Збережено 7 байт завдяки Нілу

f=(a,b,c,d,e=b==d|a+b+(b>d)&1)=>a-c|b-d&&f(e?a+1-2*(a>c):a,e?b:b+1-2*(b>d),c,d)+1

Як це працює

Хоча це все ще поширюється на поточну версію, наступне пояснення конкретніше стосується початкової версії:

f=(a,b,c,d)=>b-d?a+b+(b>d)&1?f(a+1-2*(a>c),b,c,d)+1:f(a,b+1-2*(b>d),c,d)+1:Math.abs(a-c)

Перехід від (x0, y) до (x1, y) є тривіальним, оскільки ми можемо пройти по бічних ребрах увесь шлях від вихідного трикутника до цільового. Манхеттенська відстань у цьому випадку дорівнює | x0 - x1 | .

Хитра частина - вертикальні сходи. Щоб перейти від рядка y0 до рядка y1 , ми повинні врахувати ці два параметри:

  • Орієнтація поточного трикутника
  • Будь y0 менший чи більший за y1

Орієнтація трикутника задається паритетом x + y :

  • якщо рівний, то трикутник спрямований вгору
  • якщо це не дивно, трикутник спрямований вниз

Ми можемо спуститися вниз від трикутника вгору (корисно, коли y0 <y1 ) та вгору від трикутника, що вказує вниз (корисно, коли y0> y1 ).

Комбінуючи орієнтацію трикутника з порівнянням y0 та y1 , ми отримуємо формулу x + y0 + (y0> y1? 1: 0) , результат якого навіть якщо ми можемо піти в потрібному напрямку і непарний, якщо ні.

Якщо ми не зможемо досягти наступного ряду безпосередньо, спершу потрібно отримати правильне вирівнювання, оновивши x :

  • якщо x ще не дорівнює x1 , ми, безумовно, хочемо рухатись у правильному напрямку, тому збільшуємо його, якщо x менше x1, і зменшуємо його, якщо x більше x1
  • якщо x вже дорівнює x1 , ми можемо його збільшити або зменшити

Тестові справи


Це ... багато дуже маленьких математичних операцій ... Але чи не вдалося ви пропустити nзмінну взагалі і просто додати 1 до результату кожної ітерації? ( 90 символів, я думаю)
ETHproductions

@ETHproductions Якщо чесно, я розмістив це без серйозного гольфу. Але це, безумовно, перше, що потрібно зробити. Спасибі!
Арнольд

1
Крім того, я думаю, що перевагу операторів у &засобах, які ви можете зробити, a+b+(b>d)&1щоб зберегти 2 байти
ETHproductions

Знизив це до 81 року, я думаю:f=(a,b,c,d,e=b==d|a+b+(b>d)&1)=>a-c|b-d&&f(e?a+1-2*(a>c):a,e?b:b+1-2*(b>d),c,d)+1
Ніл

Я думаю, що можливо вдасться зберегти ще один байт, використовуючи трохи розумного currying.
Ніл

5

Python 2, 74 байти

lambda x,y,X,Y:abs(y-Y)+max(x-X,X-x,abs(y-Y)+((x+y+X+Y)%-2)**(x^y^(Y>=y)))

1
Чи можете ви, будь ласка, пояснити цю частину **(x^y^(Y>=y)):?
Мертвий Поссум

1
@DeadPossum Переміщення по відстані 1 вертикально може зробити 1 або 3 ходи; немає можливості сказати, просто подивившись на паритети, тому вам доведеться порівнювати значення y.
feersum

2

Пакетна, 99 байт

@cmd/cset/a"x=%3-%1,x*=x>>31|1,y=%4-%2,w=y>>31,y*=w|1,z=x+(y+x&1)*(-(%1+%2+w&1)|1)-y,z*=z>>31,x+y+z

Пояснення: Рух, що стосується лише горизонту, просто приймає абсолютну різницю x-координат. Для досить великого х вертикальний рух займає лише один додатковий крок на абсолютну різницю координат у, але для малого х потрібно чотири додаткові кроки на дві різниці координат y, плюс один або три кроки для непарної різниці. Це вираховується як два кроки на різницю плюс поправочний коефіцієнт. Чим більша кількість виправлених двох кроків, так і сума абсолютних різниць - це результат, хоча він сам розраховується як більша відкоригована абсолютна різниця y-координат і абсолютна відстань x-координат, додана до некоректованої абсолютної різниці y-координати .

  • @cmd/cset/a" - Оцінює вирази, розділені комами, і друкує останні
  • x=%3-%1,x*=x>>31|1x=|x2x1|
  • y=%4-%2,w=y>>31,y*=w|1w=y1>y2y=|y2y1|
  • z=x+(y+x&1)*(-(%1+%2+w&1)|1)-yc=(y+(xmod2))(12((x1+y1+w)mod2)),z=x+cy
  • z*=z>>31,x+y+zmax(x,yc)+y=x+ymin(0,x+cy)

2

Желе , 24 байти

⁴<³¬Ḋ;³S
SḂN*¢+ḊḤ$
ạµS»Ç

Спробуйте в Інтернеті!

(х,у),(Х,Y)

d=|yY|+max(|xX|,|yY|+((x+y+X+Y)mod2)xy(Yy))=|yY|+max(|xX|,|yY|+[(|xX|+|yY|mod2)]x+y+(Yy))=max(|xX|+|yY|,2|yY|+[(|xX|+|yY|mod2)](Yy)+x+y).

¢=(Yy)+x+y

L=[|xX|,|yY|]sum(L)f(L)f

L=[a,b]((a+b)mod2)¢2b


2

ракетка / схема, 214 байт

(define(f x y X Y)(let m((p x)(q y)(c 0))
(let((k(+ c 1))(d(- Y q)))
(cond((= 0(- X p)d)c)
((and(> d 0)(even?(+ p q)))(m p(+ q 1)k))
((and(< d 0)(odd?(+ p q)))(m p(- q 1)k))
((< p X)(m(+ p 1)q k))
(else(m(- p 1)q k))))))

2

05AB1E , 24 байти

(x1,x2),(y1,y2)

ÆÄ`©I˜OÉ(IøнOIθD{Q+m+M®+

Спробуйте в Інтернеті!

Зламатися

ÆÄ` © I˜OÉ (IønOIθD {Q + m + M® + Повна програма. Я представляє оцінений вхід.
ÆÄ Зменшити пари відніманням, взяти абсолютні значення.
  `© Скиньте їх окремо на стек і зберігайте другий
                            один, | y1-y2 | у реєстрі С.
    I˜O Натисніть суму вирівняного вводу на стек.
       É (Візьміть її паритет і заперечуйте його.
         Iøn Push [x1, y1].
            O Візьміть x1 + y1 (підсумовуйте їх).
             IθD {Q Потім перевірте, чи відсортовано другу пару (y1 ≤ y2).
                  + І підсумовуємо, що з x1 + y1.
                   м. Експоненція. Підсуньте паритет вище ** результату.
                    + І додайте до цього другу абсолютну різницю.
                     M® + Як результат, натисніть на стек найбільшу кількість
                            плюс значення, збережене в регістрі С.

Я не 100% впевнений, але ви не можете змінити , ©щоб Dі видалити ®? Здається, це працює у цій справі, яка наразі є у вашому TIO, але я не впевнений, чи слід він однаковому шляху для кожного випадку.
Кевін Кройсейсен

1
@KevinCruijssen EDIT : Ні, тому Mщо на це вплине поведінка. Невдачі для [[0, 127], [0, 0]].
Містер Xcoder

2

Пітон 2 , 74 72 71 байт

lambda c,a,d,b:abs(a-b)+abs(a+(-c-a)/2-b-(-d-b)/2)+abs((c+a)/2-(d+b)/2)

Спробуйте в Інтернеті! Посилання включає тестові випадки. Редагувати: збережено 2 байти завдяки @JoKing. Подальший байт збережено завдяки @ Mr.Xcoder. Виходячи з наступної формули, яку я знайшов у цьому запитанні :

|аi-бi|+|(аi-аj2)-(бi-бj2)|+|аj+12-бj+12|

Системи координат відрізняються трьома способами; координати обмінюються (що пояснює мій дещо дивний порядок імен параметрів), координати під кутом120 а не 90 (which explains the two additions) and the coordinates in the linked question use inferior 1-indexing. Since we're taking differences this cancels out most of the time and we are left with:

|аi-бi|+|(аi-аj+12)-(бi-бj+12)|+|аj2-бj2|

Потім можна пограти в гольф, зазначивши це -аj+12=-аj2.


Ви можете зробити його одноколірним, видаливши новий рядок
Jo King

1
lambda c,a,d,b:abs(a-b)+abs(a+-(c+a)/2-b--(d+b)/2)+abs((c+a)/2-(d+b)/2)має зберегти 3 байти.
Містер Xcoder

1

Pyth , 31 28 байт

Uses roughly the same approach as in feersum's Python answer. Takes input as a list of pairs of coordinates, (x1,x2),(y1,y2). Fixed a bug for -1 byte.

+eKaMQg#hK+eK^%ssQ_2+shCQSIe

Try it here! or Try the test suite!

Breakdown

+eKaMQg#hK+eK^%ssQ_2xxFhCQSIe     Full program. Q = eval(input()).
  KaMQ                            Store the differences [|x1-x2|, |y1-y2|] in K.
 e                                Retrieve the latter (|y1-y2|).
+     g#                          And add it to the greatest value between:
        hK                          - The head of K (|x1-x2|)
          +                         - And the result of adding:
           eK                           The end of K (|y1-y2|).
             ^                      - with the result of exponentiating:
              %ssQ_2                    The sum of the flattened Q, modulo -2.
                                        Yields -1 if x1+x2+y1+y2 is odd, 0 otherwise.
                    xxFhCQSIe       - by the result of this expression:
                       hCQ              Transpose Q and get the head (x1, y1).
                     xF                 Reduce by bitwise XOR.
                          SIe           And check if the list [y1, y2] is sorted.
                    x                   After which, xor the result by the bool (0/1).

1

05AB1E, 16 bytes

Uses a modified version of Neil's answer, optimised for stack-based languages like 05AB1E. Takes input as two pairs of coordinates, (x1,x2),(y1,y2), separated by a newline from STDIN. Initially I merged that with my other 05AB1E answer but then decided to post it separately because it's very, very different.

+D(‚2÷Æ`²Æ©+®)ÄO

Try it online! or Try the test suite! (Uses a slightly modified version of the code (® instead of ²), courtesy of Kevin Cruijssen)


Nice answer! Not something to golf, but when you change ©+® to DŠ+ it's easier to set up a test suite. ;) Here is that test suite, and all test cases are indeed succeeding (ignore the messy header ;p).
Kevin Cruijssen

@KevinCruijssen I had that as an alternate version, but it didn't occur to me that I could write a test suite... Thanks, I'll add it
Mr. Xcoder

1
@KevinCruijssen I golfed off two more (very obvious...!) bytes, and succeeded breaking the test suite compatibility even more, so I kept it as-is :P Thanks for the edit, by the way.
Mr. Xcoder


1

Java 8, 157 190 188 144 142 141 127 bytes

(a,b,x,y)->{int r=0,c=1,z=1;for(;(c|z)!=0;r--){c=x-a;z=y-b;if((z<0?-z:z)<(c<0?-c:c)|a%2!=b%2?z<0:z>0)b+=z<0?-1:1;else a+=c<0?-1:1;}return~r;}

+33 bytes (157 → 190) due to a bug fix.
-44 bytes (188 → 144) converting the recursive method to a single looping method.
-14 bytes thanks to @ceilingcat.

Explanation:

Try it here.

(a,b,x,y)->{          // Method with four integers as parameter and integer return-type
                      // (a=x1; b=y1; x=x2; y=y2)
  int r=0,            //  Result-integer `r`, starting at 0
      c=1,z=1;        //  Temp integers for the differences, starting at 1 for now
  for(;(c|z)!=0;      //  Loop until both differences are 0
      r--){           //    After every iteration: decrease the result `r` by 1
    c=x-a;            //   Set `c` to x2 minus x1
    z=y-b;            //   Set `z` to y2 minus y1
    if(z*Z            //   If the absolute difference between y2 and y1
       <c*c)          //   is smaller than the absolute difference between x2 and x1
       |a%2!=b%2?     //   OR if the triangle at the current location is facing downwards
         z<0          //       and we have to go upwards,
        :z>0)         //      or it's facing upwards and we have to go downwards
      b+=z<0?-1:1;    //    In/decrease y1 by 1 depending on where we have to go
    else              //   Else:
     a+=c<0?-1:1;}    //    In/decrease x1 by 1 depending on where we have to go
  return~r;           //  Return `-r-1` as result

1
Suggest z*z<c*c instead of (z<0?-z:z)<(c<0?-c:c)
ceilingcat

@ceilingcat Ah, nice one. Thanks!
Kevin Cruijssen
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.