Trójkątna odległość Manhattan

26

Odległość Manhattan na regularnej siatce jest liczba prostopadłych kroki trzeba podjąć, aby osiągnąć jedną komórkę z innego. Kroki ortogonalne to te, które przechodzą przez krawędzie komórek siatki (w przeciwieństwie do rogów, co dałoby nam odległość Czebyszewa ).

Możemy zdefiniować podobną odległość na innych siatkach, na przykład siatce trójkątnej. Możemy adresować poszczególne komórki w siatce za pomocą następującego schematu indeksowania, w którym każda komórka zawiera x,yparę:

    ____________________________________...
   /\      /\      /\      /\      /\
  /  \ 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\  /
   \/______\/______\/______\/______\/___...
   /\      /\      /\      /\      /\
  .  .    .  .    .  .    .  .    .  .
 .    .  .    .  .    .  .    .  .    .

Teraz odległość Manhattanu na tej siatce jest znowu minimalną liczbą kroków wzdłuż krawędzi, aby przejść z jednej komórki do drugiej. Więc można przejść od 3,1do 2,1, 4,1albo 3,2, ale nie do jakiegokolwiek innego trójkąta, ponieważ te byłyby przekraczaniu punktów niż krawędzie.

Na przykład odległość od 2,1do 5,2wynosi 4. Najkrótsza ścieżka zazwyczaj nie jest unikalna, ale jednym ze sposobów na pokonanie dystansu w 4 krokach jest:

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

Wyzwanie

Biorąc pod uwagę dwie pary współrzędnych i z powyższego schematu adresowania, zwróć odległość między nimi na Manhattanie.x1,y1x2,y2

Możesz założyć, że wszystkie cztery wejścia są liczbami całkowitymi nieujemnymi, każde mniejsze niż 128. Możesz je uporządkować w dowolnej kolejności i dowolnie pogrupować (cztery oddzielne argumenty, lista czterech liczb całkowitych, dwie pary liczb całkowitych, macierz 2x2, ... .).

Możesz napisać program lub funkcję i użyć dowolnej ze standardowych metod odbierania danych wejściowych i dostarczania danych wyjściowych.

Możesz używać dowolnego języka programowania , ale pamiętaj, że te luki są domyślnie zabronione.

To jest , więc wygrywa najkrótsza ważna odpowiedź - mierzona w bajtach .

Przypadki testowe

Każdy przypadek testowy jest podany jako .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
Martin Ender
źródło
Czy luki, które uzyskały negatywne oceny netto, należy zaliczyć do oficjalnych luk?
DavidC,
@DavidC Nie. Z pytania dotyczącego luki: „[...] lukę opisaną w każdej odpowiedzi, która ma wartość +5 lub wyższą i ma co najmniej dwa razy więcej głosów pozytywnych niż głosów negatywnych, można uznać za niedopuszczalną dla społeczności „
Martin Ender,
Czy możemy wziąć piąty wpis, który zaczyna się domyślnie od 0 (wynik)? Wtedy nie będę musiał dodawać (a,b,x,y)->c(a,b,x,y,0)(wywołując metodę rozdzieloną cz czterema argumentami i 0jako piąty argument) do mojej odpowiedzi.
Kevin Cruijssen
3
@KevinCruijssen Nie przepraszam. Dodatkowe, ustalone argumenty są zbyt łatwo nadużywalne (a samo dopuszczenie 0 jako specjalnego przypadku wydaje się dziwne).
Martin Ender
@MartinEnder Ok, tak myślałem, ale nigdy nie może cię skrzywdzić pytanie. W takim przypadku moja 190-bajtowa odpowiedź pozostaje. Mimo że rok temu odpowiedziałem o połowę, jeden test nie powiódł się. Ponownie natknąłem się na pytanie i udało mi się naprawić błąd w mojej odpowiedzi.
Kevin Cruijssen

Odpowiedzi:

7

JavaScript (ES6), 84 78 bajtów

Zaoszczędzono 6 bajtów dzięki Neilowi

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

Przypadki testowe

Wstępne rozwiązanie rekurencyjne, 100 88 81

Zaoszczędź 12 bajtów dzięki ETHproductions
Zaoszczędź 7 bajtów dzięki Neilowi

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

Jak to działa

Chociaż nadal zasadniczo dotyczy bieżącej wersji, następujące wyjaśnienie odnosi się bardziej do wersji początkowej:

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)

Przejście od (x0, y) do (x1, y) jest trywialne, ponieważ możemy przejść przez krawędzie boczne od trójkąta źródłowego do docelowego. Odległość Manhattanu w tym przypadku wynosi | x0 - x1 | .

Trudną częścią są pionowe kroki. Aby przejść z rzędu y0 do rzędu y1 , musimy wziąć pod uwagę te dwa parametry:

  • Orientacja bieżącego trójkąta
  • Czy y0 jest mniejsze czy większe od y1

Orientację trójkąta określa parzystość x + y :

  • jeśli jest parzysty, trójkąt jest skierowany w górę
  • jeśli jest nieparzysty, trójkąt jest skierowany w dół

Możemy iść w dół od trójkąta skierowanego w górę (przydatne, gdy y0 <y1 ) i w górę od trójkąta skierowanego w dół (przydatne, gdy y0> y1 ).

Łącząc orientację trójkąta z porównaniem y0 i y1 , otrzymujemy wzór x + y0 + (y0> y1? 1: 0), którego wynik jest nawet jeśli możemy iść w pożądanym kierunku i dziwny, jeśli nie.

Jeśli nie możemy bezpośrednio przejść do następnego wiersza, najpierw musimy uzyskać prawidłowe wyrównanie, aktualizując x :

  • jeśli x nie jest jeszcze równy x1 , zdecydowanie chcemy poruszać się we właściwym kierunku, więc zwiększamy go, jeśli x jest mniejszy niż x1 i zmniejszamy go, jeśli x jest większy niż x1
  • jeśli x już równa się x1 , możemy go zwiększyć lub zmniejszyć

Przypadki testowe

Arnauld
źródło
To ... wiele bardzo małych operacji matematycznych ... Ale czy nie możesz ncałkowicie pominąć zmiennej i po prostu dodać 1 do wyniku każdej iteracji? ( Myślę, że 90 znaków )
ETHprodukcje
@ETHproductions Szczerze mówiąc, opublikowałem to bez poważnego golfa. Ale to zdecydowanie pierwsza rzecz do zrobienia. Dzięki!
Arnauld
1
Myślę też, że operator ma pierwszeństwo &środków, które możesz zrobić, a+b+(b>d)&1aby zaoszczędzić 2 bajty
ETHproductions
Sprowadziłem do 81, myślę: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
Neil
Myślę, że można zaoszczędzić kolejny bajt, używając sprytnego curry.
Neil
5

Python 2, 74 bajty

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)))
feersum
źródło
1
Czy możesz wyjaśnić tę część **(x^y^(Y>=y)):?
Dead Possum,
1
@DeadPossum Poruszanie się o 1 pionową odległość może zająć 1 lub 3 ruchy; nie da się tego stwierdzić, patrząc na parytety, więc musisz porównać wartości y.
feersum
2

Partia, 99 bajtów

@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

Objaśnienie: Ruch tylko horyzontalny przyjmuje absolutną różnicę współrzędnych x. W przypadku wystarczająco dużego x ruch pionowy zajmuje tylko jeden dodatkowy krok na bezwzględną różnicę współrzędnych y, ale dla małego x wymaga czterech dodatkowych kroków na dwie różnice współrzędnych y, plus jeden lub trzy kroki dla różnicy nieparzystej. Oblicza się to jako dwa kroki na różnicę plus współczynnik korygujący. Wynik jest większy z skorygowanych dwóch kroków i sumy różnic bezwzględnych, chociaż sam jest obliczany jako większy z poprawionej bezwzględnej różnicy współrzędnych y i bezwzględnej odległości współrzędnych x dodanej do nieskorygowanej bezwzględnej różnicy współrzędnych y .

  • @cmd/cset/a" - Ocenia wyrażenia oddzielone przecinkami i drukuje ostatnie
  • 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+zObliczamax(x,yc)+y=x+ymin(0,x+cy)
Neil
źródło
2

Galaretka , 24 bajty

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

Wypróbuj online!

Nazwijmy wejście . Pracowałem według formuły feersum:(x,y),(X,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).

Pierwszy wiersz oblicza , wykładnik w formule.¢=(Yy)+x+y

Ostatnia linia pierwsze Oblicza , a następnie oblicza się maksymalnie i , gdzie jest funkcją na środkowej linii.L=[|xX|,|yY|]sum(L)f(L)f

Linia środkowa, ponieważ , Oblicza , który bierze się do „do potęgi, a następnie dodaje .L=[a,b]((a+b)mod2)¢2b

Lynn
źródło
2

rakieta / schemat, 214 bajtów

(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))))))
Kevin
źródło
2

05AB1E , 24 bajty

Port mojej odpowiedzi w Pythonie , który z kolei stosuje z grubsza to samo podejście, co w przypadku odpowiedzi w języku Python . Pobiera dane wejściowe jako listę par współrzędnych, . Naprawiono błąd dla +1 bajtu, a następnie naprawiono kolejny błąd dla +1, ale który dał poprawny wynik dla wszystkich przypadków testowych ...(x1,x2),(y1,y2)

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

Wypróbuj online!

Awaria

©Ę © © I˜OÉ (IøнOIθD {Q + m + M® + Pełny program. Reprezentuje obliczone dane wejściowe.
RedĘ Zmniejsz pary odejmując, weź wartości bezwzględne.
  `© Zrzuć je osobno na stos i przechowuj drugi
                            jeden, | y1-y2 | w rejestrze C.
    I˜O Wciśnij sumę spłaszczonych danych wejściowych na stos.
       É (Weź parzystość i zaneguj ją.
         Iøн Push [x1, y1].
            O Weź x1 + y1 (zsumuj je).
             IθD {Q Następnie sprawdź, czy druga para jest posortowana (y1 ≤ y2).
                  + I zsumuj to z x1 + y1.
                   m Potęga. Przesuń parzystość powyżej ** wyniku.
                    + I dodaj do tego drugą absolutną różnicę.
                     M® + W rezultacie naciśnij największą liczbę na stosie
                            plus wartość przechowywana w rejestrze C.
Pan Xcoder
źródło
Nie jestem 100% pewien, ale nie można zmienić ©, aby Di usunąć ®? Wygląda na to, że działa w przypadku obecnie w TIO, ale nie jestem pewien, czy podąża tą samą ścieżką dla każdego przypadku.
Kevin Cruijssen
1
@KevinCruijssen EDYCJA : Nie, ponieważ Mwpłynęłoby to na jego zachowanie. Nie działa na [[0, 127], [0, 0]].
Pan Xcoder,
2

Python 2 , 74 72 71 bajtów

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

Wypróbuj online! Link zawiera przypadki testowe. Edycja: Zapisano 2 bajty dzięki @JoKing. Zaoszczędzono kolejny bajt dzięki @ Mr.Xcoder. Na podstawie następującego wzoru znalazłem w tym pytaniu :

|aibi|+|(aiaj2)(bibj2)|+|aj+12bj+12|

Układy współrzędnych różnią się na trzy sposoby; współrzędne są wymieniane (co wyjaśnia mój nieco dziwny porządek nazw parametrów), współrzędne są ustawione pod kątem zamiast (co wyjaśnia dwa dodatki), a współrzędne w połączonym pytaniu używają gorszego indeksowania 1. Ponieważ eliminujemy różnice, większość czasu zostaje anulowana i pozostaje nam:12090

|aibi|+|(aiaj+12)(bibj+12)|+|aj2bj2|

Można to następnie rozegrać, zauważając, że .aj+12=aj2

Neil
źródło
Możesz sprawić, że będzie to jedna linijka, usuwając nowy wiersz
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)powinien zapisać 3 bajty.
Pan Xcoder
1

Pyth , 31 28 bajtów

Stosuje w przybliżeniu takie samo podejście, jak w odpowiedzi na pytanie w języku Python feersum . Pobiera dane wejściowe jako listę par współrzędnych, . Naprawiono błąd -1 bajtu.(x1,x2),(y1,y2)

+eKaMQg#hK+eK^%ssQ_2+shCQSIe

Wypróbuj tutaj! lub Wypróbuj zestaw testowy!

Awaria

+ eKaMQg # hK + eK ^% ssQ_2xxFhCQSIe Pełny program. Q = eval (input ()).
  KaMQ Przechowuj różnice [| x1-x2 |, | y1-y2 |] w K.
 e Odzyskaj to drugie (| y1-y2 |).
+ g # I dodaj go do największej wartości między:
        hK - głowa K (| x1-x2 |)
          + - I wynik dodania:
           eK Koniec K (| y1-y2 |).
             ^ - z wynikiem potęgowania:
              % ssQ_2 Suma spłaszczonego Q, modulo -2.
                                        Daje -1, jeśli x1 + x2 + y1 + y2 jest nieparzyste, 0 w przeciwnym razie.
                    xxFhCQSIe - w wyniku tego wyrażenia:
                       hCQ Transponuj Q i zdobądź głowę (x1, y1).
                     xF Zmniejsz przez bitowe XOR.
                          I Sprawdź, czy lista [y1, y2] jest posortowana.
                    x Następnie x lub wynik przez bool (0/1).
Pan Xcoder
źródło
1

05AB1E , 16 bajtów

Korzysta ze zmodyfikowanej wersji odpowiedzi Neila , zoptymalizowanej dla języków opartych na stosie, takich jak 05AB1E. Pobiera dane wejściowe jako dwie pary współrzędnych , oddzielone znakiem nowej linii od STDIN. Początkowo połączyłem to z moją inną odpowiedzią 05AB1E, ale potem postanowiłem opublikować ją osobno, ponieważ jest bardzo, bardzo inna.(x1,x2),(y1,y2)

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

Wypróbuj online! lub Wypróbuj zestaw testowy! (Używa nieco zmodyfikowanej wersji kodu ( ®zamiast ²), dzięki uprzejmości Kevina Cruijssena )

Pan Xcoder
źródło
Niezła odpowiedź! Nie coś do golfa, ale po zmianie ©+®na DŠ+łatwiej skonfigurować zestaw testowy. ;) Oto ten zestaw testów i wszystkie przypadki testowe rzeczywiście się powiodły (zignoruj ​​niechlujny nagłówek; p).
Kevin Cruijssen
@KevinCruijssen Miałem to jako alternatywną wersję, ale nie przyszło mi do głowy, że mogę napisać zestaw testowy ... Dzięki, dodam go
Mr. Xcoder,
1
@KevinCruijssen Grałem w golfa jeszcze dwa (bardzo oczywiste ...!) Bajki i udało mi się jeszcze bardziej złamać kompatybilność zestawu testów, więc zachowałem ją taką, jaka jest: P Nawiasem mówiąc, dziękuję za edycję.
Pan Xcoder
1

Java 8, 157 190 188 144 142 141 127 bajtów

(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 bajtów (157 → 190) z powodu poprawki błędu.
-44 bajty (188 → 144) konwertuje metodę rekurencyjną na pojedynczą metodę zapętlania.
-14 bajtów dzięki @ceilingcat .

Wyjaśnienie:

Wypróbuj tutaj.

(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
Kevin Cruijssen
źródło
1
Zaproponuj z*z<c*czamiast(z<0?-z:z)<(c<0?-c:c)
ceilingcat
@ceilingcat Ah, miło. Dzięki!
Kevin Cruijssen