Adiacenza esagonale


28

Esempio di spirale esagonale

L'immagine sopra mostra una griglia esagonale di esagoni. A ogni cella della griglia viene assegnato un indice, a partire dal centro e ruotando a spirale in senso antiorario come mostrato. Nota che la griglia continuerà indefinitamente - l'immagine sopra è semplicemente la prima sezione. Il prossimo esagono sarebbe adiacente a 60 e 37.

Il tuo compito è determinare se due celle specificate su questa griglia sono adiacenti.

Scrivi un programma o una funzione che, dati due indici di cella, stampa / restituisce un valore di verità se le due celle sono adiacenti e un valore di falsa in caso contrario.

Se non limitato da motivi pratici, il codice dovrebbe teoricamente funzionare per input di qualsiasi dimensione.

Casi di prova veritieri:

0, 1
7, 18
8, 22
24, 45
40, 64
64, 65

Casi di prova Falsey:

6, 57
29, 90
21, 38
38, 60
40, 63
41, 39
40, 40

Questo è quindi vince la risposta più breve in byte. Le spiegazioni, anche per le lingue non esoteriche, sono incoraggiate.

Risposte:


7

Elisir , 263 257 264 223 214 218 214 byte

a=fn x,y->i=&(&1*(&1-1)*3+1)
[x,y]=Enum.sort [x,y]
if x<1,do: y in 1..6,else: (y-x==1||fn->a=y-trunc i.((r=(:math.sqrt(12*x-3)+3)/6)+1)
t=trunc r
a in [0,1,rem(b=x-i.(t)+1, t)<1&&b-t*6!=0&&2]||b<2&&a==-1 end.())end

Provalo online!

versione non golfata

def get_ring(x) do
    1/6*(:math.sqrt(12*x-3)+3)
end

def inv_get_ring(x), do: x*(x-1)*3+1

def ring_base(x), do: inv_get_ring(trunc(x))

def is_corner(x) do
    ring = trunc(get_ring(x))
    inv_ring = ring_base(ring)
    stuff = (x-inv_ring+1)
    rem(stuff, ring) == 0
end

def is_last(x),do: ring_base(get_ring(x)+1)-1 == x
def is_first(x),do: ring_base(get_ring(x)) == x

def hex_adj(x, y) do
    {x, y} = {min(x,y), max(x,y)}
    cond do 
        x == 0 ->y in 1..6      
        y-x==1 -> true
        true ->
            adj = trunc(inv_get_ring(get_ring(x)+1))
            number = if is_corner(x)&&!is_last(x), do: 2, else: 1
            if y-adj in 0..number do
                true
            else
                is_first(x) && y == adj-1
            end
    end
end
  • trunc(number) Restituisce la parte intera del numero
  • rem(a,b) Restituisce il resto di a / b
  • cond do end Ciò equivale ad altre clausole if o switch in molte lingue imperative

Spiegazione

get_ring (indice)

Calcola il "ring" dell'indice.

Esempio: 1 per 1-6, 2 per 7-18, ecc.

Questo vale solo se il risultato è floored. Le cifre finali rappresentano la distanza di quella tessera attorno all'anello.

inv_get_ring (anello)

Calcola l'inverso di get_ring(index).

ring_base (anello)

Calcola l'indice della prima tessera nell'anello.

is_corner (indice)

Gli angoli sono piastrelle che hanno tre piastrelle adiacenti nell'anello esterno. L'anello più interno è costituito interamente da angoli.

Esempi: 21,24,27,30,33,36

is_last (indice)

È vero se questo indice è il più alto del suo anello.

is_first (indice)

È vero se questa è la piastrella di base dell'anello.


2
Ho modificato la risposta per includere una correzione per il caso limite :)
Garuno,

Ho seguito la tua versione golfata attraverso le prime iterazioni ma poi mi è sembrato che tu abbia cambiato approccio. La tua attuale versione golfata è ancora equivalente alla versione non golfata?
John Michael Law,

Sì! Ho appena appreso che puoi dichiarare le variabili in linea nell'elisir. Questo mi ha dato la possibilità di liberarmi delle funzioni lambda all'inizio del codice. Ho semplicemente misurato le variabili un po ', per renderle più efficienti.
Garuno,

5

MATL , 47 45 44 43 41 byte

s:"JH3/^6:^t5)w5:&)@qY"w@Y"]vYs0hG)d|Yo1=

Provalo online! Oppure verifica tutti i casi di test .

Come bonus, il codice genera una spirale esagonale che traccia le posizioni dei centri cellulari, che può essere vista graficamente su MATL Online modificando l'ultima parte del codice.

Spiegazione

Idea generale    Il codice costruisce prima una spirale esagonale sufficientemente grande con un passo unitario. La spirale è definita come un vettore di numeri complessi che rappresentano le posizioni dei centri cellulari. L'indicizzazione in quel vettore con i numeri di input e il calcolo della differenza assoluta fornisce la distanza tra le due celle indicate. Le celle sono adiacenti se e solo se il risultato è 1. Tuttavia, a causa di imprecisioni in virgola mobile, è necessario arrotondare prima di confrontare con 1.

Costruire la spirale    La spirale avrà un numero di "strati" pari alla somma dei due input. Questo è (molto) più grande del necessario e garantisce che le celle di input siano presenti nella spirale.

Per costruire la spirale, viene prima calcolato il numero complesso j 2/3 (dove j è l'unità immaginaria). Aumentare questo agli esponenti da 1 a 6 fornisce una serie base di spostamenti, in modo tale che seguendo tali spostamenti in ordine traccerebbe un esagono. Questo esagono formerebbe lo strato più interno della spirale, tranne per il fatto che sarebbe "chiuso". In realtà, vogliamo che quell'esagono "cresca" nell'ultimo passaggio e quindi tracciamo un esagono più grande, con il doppio del numero di punti (allineati in gruppi di due), per formare il successivo strato della spirale; vedi illustrazione qui . Il livello successivo avrà tre volte più punti del primo (in gruppi di tre); vedi qui .

Per fare ciò, il quinto spostamento dall'insieme di base (che punta nella direzione sud-est) viene scelto come passo "crescente". Lo strato k inizia con quel passo, seguito dai primi cinque passi base ripetuti k volte, seguito dal sesto passo (direzione est) ripetuto k −1 volte. Si spera che questo diventi più chiaro guardando le due figure collegate sopra.

Il vettore risultante, inclusi tutti i livelli, rappresenta gli spostamenti complessi che tracciano la spirale. La somma cumulativa fornisce le coordinate effettive dei centri cellulari.

Infine, la cella iniziale, situata a 0, è collegata alla fine di questo vettore. Questo perché MATL utilizza l'indicizzazione modulare basata su 1 e l'indice 0 fa riferimento all'ultima voce di un array.

Test di adiacenza    Le due celle fornite dai numeri di input vengono selezionate, le loro coordinate vengono sottratte e il valore assoluto viene arrotondato e confrontato con 1.

Codice commentato

s         % Implicitly input array of two numbers. Push their sum, say S
:         % Range [1 2 ... S]
"         % For each k in [1 2 ... S]
  J       %   Push 1j
  H3/     %   Push 2, then 3, then divide: gives 2/3
  ^       %   Power
  6:      %   Push [1 2 ... 6]
  ^       %   Element-wise power. This is the array of 6 basic displacements
  t5)     %   Duplicate. Get 5th entry
  w5:&)   %   Swap. Push subarray with entries 1st to 5th, then push 6th
  @qY"    %   Repeat the 6th displacement k-1 times
  w@Y"    %   Swap. Repeat 1st to 5th displacements k times
]         % End
v         % Concatenate everything into a column vector
Ys        % Cumulative sum. This gives the cell center coordinates
0h        % Append a 0
G)        % Index by the input vector
d|        % Absolute difference
Yo        % Round to nearest integer
1=        % Does it equal 1? Implicitly display

Potresti aggiungere una spiegazione?
Shaggy,

@Shaggy ho aggiunto una spiegazione generale. Fammi sapere se è chiaro (è difficile da spiegare). Aggiungerò il codice commentato più tardi
Luis Mendo l'

2

05AB1E (legacy) , 30 29 27 byte

α2‹i1q}1ݹ+v12y*3-tîÌy+}Ÿ²å

Provalo online!

Spiegazione del codice:

α2‹i1q}                     : if the absolute diff of the two number is 1 or 0 return 1
                          ²å: return that the second number is in
                         Ÿ  : range of {
       1Ý                   :  create [0, 1]
         ¹+                 :  add the first number to the elements
           v            }   :  map that list
            12y*3-tîÌy+     :  calculate the corresponding value where it's an adjacency
                                }

Spiegazione della matematica:

Ho "sprecato" circa 5 ore facendo questo golf. In breve, ho iniziato a creare un grafico 2d degli input e disegnare Xdove erano adiacenti l'uno all'altro. Poi ho trovato uno schema. L'ho cercato su OEIS e sul bingo! Ho trovato quella sequenza e ho usato la formula indicata sul sito web.


1

C (gcc) , 175 173 byte

Grazie a Peter Taylor per aver scoperto un bug.

Grazie a ceilingcat per -2 byte. Quell'operatore ~ ​​continua ad essere il mio principale punto cieco.

c,r,C,L;y(a){a=a<L*2?L-a:a<L*3?-L:a<5*L?a-L*4:L;}z(a){L=ceil(sqrt(a/3.+.25)-.5);C=y(a-=3*L*~-L);L?L=y((L+a)%(L*6)):0;}f(a,b){z(a);c=C,r=L;z(b);a=a-b&&(abs(c-C)|abs(r-L))<2;}

Provalo online!

Questo approccio si concentra sul trovare la riga e la colonna delle due celle e confrontarle; tutti i vicini non possono avere le loro coordinate corrispondenti differire di più di 1. Muovendosi dal centro verso l'esterno, osserviamo che ogni strato ha 6 celle in più rispetto al precedente. Ciò significa che l '"indice" più alto in ogni strato L è nella forma 6 * (L * (L - 1) * (L - 2) ...) o C = 6 * (L 2 + L) / 2 , dove C è il numero di cella "globale". Mescolando le cose, otteniamo L 2 + L - C / 3 = 0, che fornisce flashback matematici alle superiori. Da ciò, otteniamo la formula ceil (sqrt (1/4 + C / 3) + 0,5). Collegando un indice di cella globale, riceviamo in quale livello si trova la cella.

Poiché la prima cella in ogni strato è naturalmente una più alta della più alta del livello precedente, troviamo L start = (6 * (L - 1) 2 + (L - 1)) / 2, che semplifica a 3 * (L 2 - L). Da ciò otteniamo l'indice di livello L index = C - L start .

Successivamente, vediamo che ogni strato è composto da sei sezioni, ciascuna della lunghezza L. Partendo da nord-est e andando in senso antiorario, vediamo che per le prime due sezioni (1 <= indice L <= 2 * L) , otteniamo la colonna da L - L indice . La sezione successiva L * 2 < indice L <= L * 3 ha tutte le celle che condividono una singola colonna -L. Le due sezioni seguenti sono L * 3 < indice L <= L * 5 con le loro colonne secondo l' indice L - L * 4. E infine la sesta sezione ha tutte le sue celle sulla colonna L. Possiamo spostare i limiti superiori di un passo lungo per salvare alcuni byte nel codice.

Quindi che dire delle file? Per riutilizzare il codice, giriamo la griglia in modo che la cella 44 sia dritta. Quindi eseguiamo la stessa logica delle colonne, ma questa volta chiamiamo i risultati "righe". Ovviamente, invece di girare una griglia, camminiamo solo 1/6 di giro.


@PeterTaylor Buona cattura, grazie!
Gastropner,

1

Python 3, 150 byte

def h(a,b):
 L=[];i=1
 while len(L)<a+b:r=sum((i*[1j**(k/3)]for k in range(4,16,2)),[]);r[0]+=1;L+=r;i+=1
 return.9<abs(sum(L[min(a,b):max(a,b)]))<1.1

La mia soluzione sostanzialmente segue la stessa linea di pensiero di cui sopra Luis Mendo. Se scritto più leggibile, il codice è abbastanza autoesplicativo:

def h(a,b):
    L=[]
    i=1
    while len(L)<a+b:
        l=sum((i*[1j**(k/3)]for k in range(4,16,2)),[])
        l[0]+=1
        L+=l
        i+=1
return .9<abs(sum(L[min(a,b):max(a,b)]))<1.1
  1. la funzione hprocede come segue:
  2. L'elenco L conterrà le posizioni (complesse) di ciascun numero.
  3. i è il numero di squillo.
  4. Nel ciclo while, viene aggiunto un nuovo anello ad ogni iterazione. Invece di capire quanti anelli abbiamo bisogno, continuiamo a costruire l'elenco finché non è abbastanza lungo da contenere un + b, quindi è sicuramente abbastanza lungo da contenere uno di essi.
  5. la 'ring-list' lè una concatenazione di 6 liste di len (i) volte il passo-vettore, dove il passo-vettore è 1j ** (2/3) per una certa potenza. L'intervallo non inizia da 0 ma da 4, il che provoca una rotazione dell'intera griglia. Questo mi permette di fare:
  6. l[0]+=1 nella riga 6, che è il passaggio da un anello all'altro.
  7. L+=l concatena l'elenco completo e la ring-list.
  8. L'elenco L contiene solo vettori di passi, che devono ancora essere sommati (integrati) per ottenere una posizione. Una caratteristica interessante qui è che possiamo semplicemente sommare la sezione dal numero più basso al più alto per ottenere la loro distanza! A causa di errori di arrotondamento, il risultato non sarà esattamente 1, quindi .9 <... <1.1. È interessante notare che il caso zero h(0,0)o h (0,1) è curato implicitamente, poiché la somma di un elenco vuoto è zero. Se potessi essere sicuro che a<b, cioè gli argomenti verrebbero in ordine crescente, potrei radere via altri 14 byte sostituendoli L[min(a,b):max(a,b)]con L[a:b], ma purtroppo!

PS: Non sapevo che fosse una sfida così vecchia, si è presentata in cima qualche giorno fa e mi ha continuato a tormentarmi da :)


Questa è un'ottima risposta! Non preoccuparti per la risposta tardiva, non abbiamo davvero problemi con questo qui su PPCG.
Rɪᴋᴇʀ

0

Mathematica, 111 105 104 byte

r=Floor[(1+Sqrt[(4#-1)/3])/2]&;t=Limit[Pi(#/(3x)+1-x),x->r@#]&;p=r@#*Exp[I*t@#]&;Round@Abs[p@#-p@#2]==1&

Spiegazione:

r=Floor[(1+Sqrt[(4#-1)/3])/2]&definisce una funzione rche accetta input #e calcola la distanza (in numero di celle) dalla cella 0. Lo fa sfruttando il modello nelle ultime celle di ogni distanza / anello: 0 = 3 (0 ^ 2 + 0), 6 = 3 (1 ^ 2 + 1), 18 = 3 (2 ^ 2 + 2), 36 = 3 (3 ^ 2 + 3), ... e invertendo la formula per quel modello. Si noti che per la cella 0, in realtà prende il pavimento di (1/2) + i * (sqrt (3) / 6), che calcola dal punto di vista del componente per ottenere 0 + 0 * i = 0.

Con rdefinito, r@#è l'anello per cella #(all'interno della definizione di un'altra funzione). #+3r@#-3(r@#)^2&non appare esattamente nel codice, ma prende il numero di una cella e sottrae il numero più alto di una cella nell'anello interno successivo, in modo che dia la risposta alla domanda "quale cella dell'anello corrente è questa?" Ad esempio, la cella 9 è la terza cella dell'anello 2, quindi l' r[9]uscita 2 e l' #+3r@#-3(r@#)^2&[9]uscita 3.

Quello che possiamo fare con la funzione sopra è usarlo per trovare l' angolo polare , l'angolo in senso antiorario dal raggio "cella 0, cella 17, cella 58" alla cella in questione. L'ultima cella di ogni anello ha sempre un angolo Pi / 6 e facciamo un giro con incrementi di Pi / (3 * numero_anello). Quindi, in teoria, dobbiamo calcolare qualcosa come Pi / 6 + (which_cell_of_the_current_ring) * Pi / (3 * ring_number). Tuttavia, la rotazione dell'immagine non influisce su nulla, quindi possiamo scartare la parte Pi / 6 (per salvare 6 byte). Combinando questo con la formula precedente e semplificando, otteniamoPi(#/(3r@#)+1-r@#)&

Sfortunatamente, questo non è definito per la cella 0 poiché il suo numero di squillo è 0, quindi dobbiamo aggirare questo. Una soluzione naturale sarebbe qualcosa di simile t=If[#==0,0,Pi(#/(3r@#)+1-r@#)]&. Ma poiché non ci interessa l'angolo per la cella 0 e poiché r@#viene ripetuto, possiamo effettivamente salvare un byte qui cont=Limit[Pi(#/(3x)+1-x),x->r@#]&

Ora che abbiamo il numero di squillo e l'angolo, possiamo trovare la posizione di una cella (al centro) in modo da poter verificare l'adiacenza. Trovare la posizione effettiva è fastidioso perché gli anelli sono esagonali, ma possiamo semplicemente far finta che gli anelli siano cerchi perfetti in modo da considerare il numero dell'anello come la distanza dal centro della cella 0. Questo non sarà un problema poiché l'approssimazione è carina vicino. Usando la forma polare di un numero complesso , possiamo rappresentare questa posizione approssimativa nel piano complesso con una semplice funzione:p = r@#*Exp[I*t@#] &;

La distanza tra due numeri complessi sul piano complesso è data dal valore assoluto della loro differenza, e quindi possiamo arrotondare il risultato per occuparci di eventuali errori dall'approssimazione e verificare se questo è uguale a 1. La funzione che infine questo lavoro non ha un nome, ma lo è Round@Abs[p@#-p@#2]==1&.


Puoi provarlo online nella sandbox di Wolfram Cloud incollando il codice come il seguente e facendo clic su Gear -> "Valuta cella" o premendo Maiusc + Invio o il tastierino numerico Invio:

r=Floor[(1+Sqrt[(4#-1)/3])/2]&;t=Limit[Pi(#/(3x)+1-x),x->r@#]&;p=r@#*Exp[I*t@#]&;Round@Abs[p@#-p@#2]==1&[24,45]

O per tutti i casi di test:

r=Floor[(1+Sqrt[(4#-1)/3])/2]&;t=Limit[Pi(#/(3x)+1-x),x->r@#]&;p=r@#*Exp[I*t@#]&;Round@Abs[p@#-p@#2]==1&//MapThread[#,Transpose[{{0,1},{7,18},{8,22},{24,45},{40,64},{64,65},{6,57},{29,90},{21,38},{38,60},{40,63},{41,39},{40,40}}]]&
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.