Distanza triangolare di Manhattan


26

La distanza di Manhattan su una griglia regolare è il numero di passi ortogonali che uno deve prendere per raggiungere una cella da un'altra. I passaggi ortogonali sono quelli che attraversano i bordi delle celle della griglia (al contrario degli angoli, che ci darebbe la distanza di Chebyshev ).

Possiamo definire una distanza simile su altre griglie, ad esempio la griglia triangolare. Possiamo indirizzare le singole celle nella griglia con il seguente schema di indicizzazione, in cui ogni cella contiene una x,ycoppia:

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

Ora la distanza di Manhattan su questa griglia è di nuovo il numero minimo di passi attraverso i bordi per passare da una cella all'altra. Così si può passare da 3,1a 2,1, 4,1o 3,2, ma non a qualsiasi altro triangolo, dal momento che quelli sarebbero stati attraversando punti piuttosto che bordi.

Ad esempio, la distanza da 2,1a 5,2è 4. Il percorso più breve non è generalmente unico, ma un modo per fare la distanza in 4 passi è:

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

La sfida

Date due coppie di coordinate e dallo schema di indirizzamento sopra riportato, restituiscono la distanza di Manhattan tra di loro.x1,y1x2,y2

Puoi presumere che tutti e quattro gli input siano numeri interi non negativi, ciascuno inferiore a 128. Puoi prenderli in qualsiasi ordine e raggruppati arbitrariamente (quattro argomenti separati, un elenco di quattro numeri interi, due coppie di numeri interi, una matrice 2x2, .. .).

È possibile scrivere un programma o una funzione e utilizzare uno dei metodi standard per ricevere input e fornire output.

È possibile utilizzare qualsiasi linguaggio di programmazione , ma si noti che queste scappatoie sono vietate per impostazione predefinita.

Questo è , quindi la risposta valida più breve - misurata in byte - vince.

Casi test

Ogni caso di test è indicato come .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

Le scappatoie che hanno ricevuto valutazioni nette negative devono essere incluse tra le scappatoie ufficiali?
DavidC

@DavidC No. Dalla domanda sulla scappatoia: "[...] la scappatoia descritta in qualsiasi risposta che è a +5 o superiore e ha almeno il doppio di voti rispetto a quelli che possono essere considerati inaccettabili per la comunità "
Martin Ender,

È consentito prendere un quinto input, che inizia da 0 per impostazione predefinita (il risultato)? Quindi non avrò bisogno di aggiungere (a,b,x,y)->c(a,b,x,y,0)(chiamando il metodo separato ccon i quattro argomenti e 0come quinto argomento) alla mia risposta.
Kevin Cruijssen,

3
@KevinCruijssen No sorry. Argomenti aggiuntivi e fissi sono un po 'troppo facilmente abusabili (e solo consentire 0 come caso speciale sembra strano).
Martin Ender,

@MartinEnder Ok, l'ho pensato, ma non può mai far male chiedere. In tal caso, la mia risposta a 190 byte rimane. Anche se ho risposto a metà anno fa, un caso di test non stava funzionando. Ho risposto di nuovo alla domanda proprio ora, ed è stato in grado di correggere l'errore nella mia risposta.
Kevin Cruijssen,

Risposte:


7

JavaScript (ES6), 84 78 byte

6 byte salvati grazie a Neil

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

Casi test

Soluzione ricorsiva iniziale, 100 88 81

Risparmiato 12 byte grazie a ETHproductions
Risparmiato 7 byte grazie a Neil

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

Come funziona

Sebbene si applichi essenzialmente alla versione corrente, la seguente spiegazione si riferisce più specificamente alla versione iniziale:

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)

Andare da (x0, y) a (x1, y) è banale perché possiamo attraversare i bordi laterali dal triangolo sorgente a quello target. La distanza di Manhattan in questo caso è | x0 - x1 | .

La parte difficile sono i passaggi verticali. Per passare dalla riga y0 alla riga y1 , dobbiamo tenere conto di questi due parametri:

  • L'orientamento del triangolo corrente
  • Se y0 è minore o maggiore di y1

L'orientamento di un triangolo è dato dalla parità di x + y :

  • se è pari, il triangolo è rivolto verso l'alto
  • se è strano, il triangolo è rivolto verso il basso

Possiamo andare verso il basso da un triangolo rivolto verso l'alto (utile quando y0 <y1 ) e verso l'alto da un triangolo rivolto verso il basso (utile quando y0> y1 ).

Combinando l'orientamento del triangolo con il confronto tra y0 e y1 , otteniamo la formula x + y0 + (y0> y1? 1: 0) il cui risultato è anche se possiamo andare nella direzione desiderata e dispari se no.

Se non possiamo raggiungere direttamente la riga successiva, dobbiamo prima ottenere un allineamento corretto aggiornando x :

  • se x non è ancora uguale a x1 , vogliamo sicuramente spostarci nella direzione corretta, quindi la incrementiamo se x è minore di x1 e la diminuiamo se x è maggiore di x1
  • se x è già uguale a x1 , possiamo incrementarlo o diminuirlo

Casi test


È ... un sacco di operazioni matematiche molto piccole ... Ma non potresti saltare del ntutto la variabile e aggiungere solo 1 al risultato di ogni iterazione? ( 90 caratteri penso)
ETHproductions

@ETHproductions Ad essere sincero, l'ho pubblicato senza alcun serio golf. Ma questa è sicuramente la prima cosa da fare. Grazie!
Arnauld,

1
Inoltre, penso che l'operatore abbia la precedenza sui &mezzi che puoi farea+b+(b>d)&1 per salvare 2 byte
ETHproductions

Sono arrivato a 81, penso: 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

Penso che potrebbe essere possibile salvare un altro byte usando un curry intelligente.
Neil,

5

Python 2, 74 byte

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
Puoi, per favore, spiegare questa parte **(x^y^(Y>=y)):?
Dead Possum

1
@DeadPossum Muoversi di una distanza di 1 in verticale può richiedere 1 o 3 mosse; non c'è modo di dirlo guardando solo le parità, quindi devi confrontare i valori y.
feersum

2

Lotto, 99 byte

@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

Spiegazione: Un movimento solo orizzontale prende semplicemente la differenza di coordinata x assoluta. Per x abbastanza grande, il movimento verticale esegue solo un passo in più per la differenza di coordinate y assoluta, ma per x piccola, richiede quattro passi in più per due differenze di coordinate y, più uno o tre passi per una differenza dispari. Questo è calcolato come due passaggi per differenza più un fattore di correzione. Il più grande dei due passi corretti e la somma delle differenze assolute è quindi il risultato, sebbene questo sia esso stesso calcolato come il più grande della differenza delle coordinate y assolute corrette e la distanza delle coordinate x assolute aggiunta alla differenza delle coordinate y assolute non corrette .

  • @cmd/cset/a" - Valuta le espressioni separate da virgola e stampa l'ultima
  • x=%3-%1,x*=x>>31|1X=|X2-X1|
  • y=%4-%2,w=y>>31,y*=w|1w=y1>y2y=|y2-y1|
  • z=x+(y+x&1)*(-(%1+%2+w&1)|1)-yc=(y+(Xmod2))(1-2((X1+y1+w)mod2)),z=X+c-y
  • z*=z>>31,x+y+zmun'X(X,y-c)+y=X+y-mion(0,X+c-y)

2

Gelatina , 24 byte

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

Provalo online!

(X,y),(X,Y)

d=|y-Y|+max(|X-X|,|y-Y|+((X+y+X+Y)mod-2)Xy(Yy))=|y-Y|+max(|X-X|,|y-Y|+[-(|X-X|+|y-Y|mod2)]X+y+(Yy))=max(|X-X|+|y-Y|,2|y-Y|+[-(|X-X|+|y-Y|mod2)](Yy)+X+y).

¢=(Yy)+X+y

L=[|X-X|,|y-Y|]somma(L)f(L)f

L=[un',B]-((un'+B)mod2)¢2B


2

racchetta / schema, 214 byte

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

La risposta di Port of my Pyth , che a sua volta utilizza approssimativamente lo stesso approccio della risposta Python di feersum . Prende l'input come un elenco di coppie di coordinate,(X1,X2),(y1,y2)

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

Provalo online!

Abbattersi

ÆÄ` © I˜OÉ (IøнOIθD {Q + m + M® + Programma completo. I rappresenta l'input valutato.
ÆÄ Ridurre le coppie per sottrazione, prendere i valori assoluti.
  `© Scaricali separatamente sulla pila e conserva il secondo
                            uno, | y1-y2 | nel registro C.
    I˜O Spingere la somma degli input appiattiti sulla pila.
       É (Prendi la sua parità e negala.
         Iøн Push [x1, y1].
            O Prendi x1 + y1 (sommali).
             IθD {Q Quindi controlla se la seconda coppia è ordinata (y1 ≤ y2).
                  + E somma quello con x1 + y1.
                   m Esponente. Spingere la parità sopra ** il risultato.
                    + E aggiungi la seconda differenza assoluta a quella.
                     M® + Di conseguenza, spingere il numero più grande nella pila
                            più il valore memorizzato nel registro C.

Io non sono sicuro al 100%, ma non posso cambiare il ©per De togliere il ®? Sembra funzionare per il caso attualmente nel tuo TIO, ma non sono sicuro che segua lo stesso percorso per ogni caso.
Kevin Cruijssen,

1
@KevinCruijssen EDIT : No, perché Mil comportamento ne risentirebbe. Non funziona per [[0, 127], [0, 0]].
Mr. Xcoder,

2

Python 2 , 74 72 71 byte

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

Provalo online! Il link include casi di test. Modifica: salvato 2 byte grazie a @JoKing. Salvato un ulteriore byte grazie a @ Mr.Xcoder. Sulla base della seguente formula ho trovato in questa domanda :

|un'io-Bio|+|(un'io-un'j2)-(Bio-Bj2)|+|un'j+12-Bj+12|

12090 (che spiega le due aggiunte) e le coordinate nell'uso questione legata inferiore 1-indicizzazione. Dal momento che prendiamo le differenze, questo si annulla per la maggior parte del tempo e ci rimane:

|un'io-Bio|+|(un'io-un'j+12)-(Bio-Bj+12)|+|un'j2-Bj2|

-un'j+12=-un'j2


Puoi trasformarlo in una riga rimuovendo la nuova riga
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)dovrebbe salvare 3 byte.
Mr. Xcoder,

1

Pyth , 31 28 byte

(X1,X2),(y1,y2)

+eKaMQg#hK+eK^%ssQ_2+shCQSIe

Provalo qui! oppure prova la suite di test!

Abbattersi

+ eKaMQg # hK + eK ^% ssQ_2xxFhCQSIe Programma completo. Q = eval (input ()).
  KaMQ Memorizza le differenze [| x1-x2 |, | y1-y2 |] in K.
 e Recupera quest'ultimo (| y1-y2 |).
+ g # E aggiungilo al valore più grande tra:
        hK - La testa di K (| x1-x2 |)
          + - E il risultato dell'aggiunta:
           eK La fine di K (| y1-y2 |).
             ^ - con il risultato di esponenziale:
              % ssQ_2 La somma del Q appiattito, modulo -2.
                                        Produce -1 se x1 + x2 + y1 + y2 è dispari, 0 altrimenti.
                    xxFhCQSIe - dal risultato di questa espressione:
                       hCQ Trasponi Q e ottieni la testa (x1, y1).
                     xF Riduzione di XOR bit a bit.
                          SIe Verifica se l'elenco [y1, y2] è ordinato.
                    x Dopodiché, xo il risultato del bool (0/1).

1

05AB1E , 16 byte

(X1,X2),(y1,y2)

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

Provalo online! oppure prova la suite di test! (Utilizza una versione leggermente modificata del codice ( ®anziché² ), per gentile concessione di Kevin Cruijssen )


Bella risposta! Non è qualcosa da giocare a golf, ma quando si ©+®passa a DŠ+è più facile impostare una suite di test. ;) Ecco quella suite di test, e tutti i casi di test hanno effettivamente successo (ignora l'intestazione disordinata; p).
Kevin Cruijssen,

@KevinCruijssen L'ho avuto come versione alternativa, ma non mi è venuto in mente che avrei potuto scrivere una suite di test ... Grazie, lo aggiungerò
Mr. Xcoder il

1
@KevinCruijssen Ho rovesciato altri due (molto ovvi ...!) Byte e sono riuscito a interrompere ancora di più la compatibilità della suite di test, quindi l'ho mantenuta così com'è: P Grazie per la modifica, a proposito.
Mr. Xcoder,


1

Java 8, 157 190 188 144 142 141 127 byte

(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 byte (157 → 190) a causa di una correzione di bug.
-44 byte (188 → 144) convertendo il metodo ricorsivo in un metodo a ciclo singolo.
-14 byte grazie a @ceilingcat .

Spiegazione:

Provalo qui.

(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
Suggerisci z*z<c*cinvece di(z<0?-z:z)<(c<0?-c:c)
ceilingcat il

@ceilingcat Ah, bello. Grazie!
Kevin Cruijssen,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.