Squarefinder - Individuazione di tetragoni regolari


27

Immagina un gruppo di rettangoli disegnati sul piano, ogni rettangolo con i suoi vertici a coordinate intere e i suoi lati paralleli agli assi:

inserisci qui la descrizione dell'immagine

I rettangoli suddividono il piano in un numero di regioni disgiunte, colorate in rosso e blu in basso:

inserisci qui la descrizione dell'immagine

Il tuo obiettivo è trovare il numero di tali regioni che sono quadrati perfetti. Nell'esempio sopra, ci sono tre:

inserisci qui la descrizione dell'immagine

Si noti che il grande quadrato nel mezzo non viene conteggiato in quanto non è una singola regione, ma è invece costituito da diverse regioni disgiunte più piccole.

Ingresso

È possibile scrivere una funzione o un programma completo per questa sfida.

L'input sarà 4nnumeri interi non negativi che definiscono i nrettangoli nel piano. Ogni rettangolo è rappresentato da due vertici opposti, ad esempio 4 9 7 8rappresenta il rettangolo con vertici opposti (4, 9)e (7, 8). Nota che questo rettangolo può anche essere rappresentato come 7 8 4 9o 4 8 7 9.

Il formato di input esatto è flessibile (ad es. Stringa separata da spazio, stringa separata da virgola, singola matrice di numeri interi, elenco di tuple di coordinate e così via) ma sii ragionevole e fornisci un esempio di come eseguire il codice nel tuo post. Non è possibile riordinare l'input.

Per semplicità, puoi presumere che non ci siano due bordi sovrapposti, questo include la sovrapposizione a un vertice. In particolare, ciò implica che nessun rettangolo toccherà da bordo a bordo o da angolo a angolo e che i rettangoli avranno un'area diversa da zero.

Produzione

Il programma dovrebbe stampare o restituire un singolo numero intero, ovvero il numero di regioni quadrate.

punteggio

Questo è il codice golf, quindi vince il codice nel minor numero di byte.


Casi test

Ingresso:

0 0 5 5
6 8 10 4
14 16 11 13
19 1 18 2

Produzione:

4

Questo è semplicemente quattro quadrati disgiunti:

inserisci qui la descrizione dell'immagine


Ingresso:

2 1 3 11
1 10 5 19
6 10 11 3
8 8 15 15
13 13 9 5
15 1 19 7
17 19 19 17

Produzione:

3

Questo è il caso di test di esempio all'inizio del post.


Ingresso:

0 9 15 12
6 3 18 15
9 6 12 20
13 4 17 8

Produzione:

7

inserisci qui la descrizione dell'immagine


Ingresso:

5 9 11 10
5 12 11 13
6 8 7 14
9 8 10 14
13 8 14 9
13 10 14 14

Produzione:

14

inserisci qui la descrizione dell'immagine


Ingresso:

0 99999 100000 0

Produzione:

0

Questo è solo un grande rettangolo.


Ingresso:

0 99999 100000 0
2 1 142857 285714

Produzione:

1

Due grandi rettangoli che si sovrappongono.

Risposte:


9

SQL (POSTGIS), 286 269 261 240 226 218 216

Questa è una query per l'estensione PostGIS a PostgreSQL. Non ho contato i valori di input nel totale.

SELECT SUM(1)FROM(SELECT(ST_Dump(ST_Polygonize(g))).geom d FROM(SELECT ST_Union(ST_Boundary(ST_MakeEnvelope(a,b,c,d)))g FROM(VALUES
-- Coordinate input
(2, 1, 3, 11)
,(1, 10, 5, 19)
,(6, 10, 11, 3)
,(8, 8, 15, 15)
,(13, 13, 9, 5)
,(15, 1, 19, 7)
,(17, 19, 19, 17)
)i(a,b,c,d))i)a WHERE(ST_XMax(d)-ST_XMin(d))^2+(ST_YMax(d)-ST_YMin(d))^2=ST_Area(d)*2

Spiegazione

La query crea geometrie per ciascuna coppia di coordinate. Unisce gli anelli esterni per allineare correttamente le linee. Trasforma i risultati in poligoni e verifica la larghezza rispetto all'altezza e l'area raddoppiata rispetto alla somma di ciascun lato quadrato.

Verrà eseguito come query autonoma su qualsiasi database PostgreSQL con l'estensione PostGIS.

Modifica Trovato un altro paio.


1
... e Haskell
Optimizer il

@optimizer Dubito che durerà :)
MickyT

@MickyT Questo si è trasformato in una sana competizione. :)
Zgarb,

@zgarb ha un po ':-) ma non credo di avere nient'altro da fare.
MickyT

13

Python 2, 480 436 386 352 byte

exec u"""s=sorted;H=[];V=[]
FRIinput():
 S=2*map(s,zip(*R))
 FiI0,1,2,3:
    c=S[i][i/2];a,b=S[~i]
    FeIs(H):
     C,(A,B)=e
     if a<C<b&A<c<B:e[:]=C,(A,c);H+=[C,(c,B)],;V+=[c,(a,C)],;a=C
    V+=[c,(a,b)],;H,V=V,H
print sum(a==A==(d,D)&c==C==(b,B)&B-b==D-d&1-any(d<X[0]<D&b<y<B Fy,XIH)Fb,aIH FB,AIH Fd,cIV FD,CIV)""".translate({70:u"for ",73:u" in ",38:u" and "})

Prende un elenco di coppie di coordinate tramite STDIN nel formato:

[  [(x, y), (x, y)],  [(x, y), (x, y)],  ...  ]

e stampa il risultato su STDOUT.


Il programma effettivo, dopo la sostituzione della stringa, è:

s=sorted;H=[];V=[]
for R in input():
 S=2*map(s,zip(*R))
 for i in 0,1,2,3:
    c=S[i][i/2];a,b=S[~i]
    for e in s(H):
     C,(A,B)=e
     if a<C<b and A<c<B:e[:]=C,(A,c);H+=[C,(c,B)],;V+=[c,(a,C)],;a=C
    V+=[c,(a,b)],;H,V=V,H
print sum(a==A==(d,D) and c==C==(b,B) and B-b==D-d and 1-any(d<X[0]<D and b<y<B for y,X in H)for b,a in H for B,A in H for d,c in V for D,C in V)

Spiegazione

Invece di armeggiare con poligoni complessi, questo programma si occupa di semplici segmenti di linea. Per ogni rettangolo di input, aggiungiamo ciascuno dei suoi quattro bordi a un elenco di segmenti collettivi, individualmente. L'aggiunta di un segmento all'elenco procede come segue: testiamo ciascuno dei segmenti esistenti per l'intersezione con il nuovo segmento; se troviamo un incrocio, dividiamo entrambi i segmenti nel punto di incrocio e continuiamo. Per semplificare le cose, in realtà teniamo due elenchi di segmenti separati: uno orizzontale e uno verticale. Poiché i segmenti non si sovrappongono, i segmenti orizzontali possono intersecare solo i segmenti verticali e viceversa. Meglio ancora, significa che tutte le intersezioni (senza considerare i bordi dello stesso rettangolo) sono "appropriate", cioè non abbiamo intersezioni a forma di T, quindi "entrambi i lati" di ciascun segmento sono veramente divisi.

Dopo aver creato l'elenco o i segmenti, iniziamo a contare i quadrati. Per ogni combinazione di quattro segmenti (in particolare due segmenti orizzontali e due verticali), testiamo se formano un quadrato. Inoltre, verifichiamo che nessun vertice si trovi all'interno di questo quadrato (cosa che può accadere se, ad esempio, abbiamo un piccolo quadrato all'interno di uno più grande). Questo ci dà la quantità desiderata. Si noti che anche se il programma verifica ciascuna combinazione quattro volte in ordini diversi, il particolare ordinamento delle coordinate del segmento garantisce che contiamo ogni quadrato una sola volta.


1
Sono piuttosto impressionato dalla velocità con cui hai risolto questo problema e dal modo in cui hai affrontato il problema! I loop for mi fanno andare "sicuramente si può fare qualcosa ..."
Sp3000,

@ Sp3000 Sì. Ho provato ad usare itertoolsper i loop ma alla fine è stato più lungo. Posso radere qualche byte con exec+ sostituzioni di stringhe, ma niente di troppo eccitante.
Ell

4

Haskell, 276 266 250 237 225 222 217 byte

Viene sempre più breve ... e più offuscato.

(x#i)l=mapM id[[min x i..max x i-1],l]
(y!j)l f=and[p l==p(f[y,j])|p<-map elem$f[y..j]]
s[h,v]=sum[1|[x,j]<-h,[y,i]<-v,x<i,i-x==j-y,(y!j)h$x#i,(x!i)v$y#j]
n=s.foldr(\(x,y,i,j)->zipWith(++)[x#i$[y,j],y#j$[x,i]])[[],[]]

Valuta n [(0,0,5,5),(6,8,10,4),(14,16,11,13),(19,1,18,2)]per il primo caso di test. Penso che mi sto avvicinando ai limiti del golf con questo algoritmo su Haskell.

Questa funzione è così lenta (almeno O (n 3 ) dove n è il perimetro totale di tutti i rettangoli nell'input) che non posso valutarlo negli ultimi due casi di test. Quando l'ho compilato con le ottimizzazioni attivate ed eseguito sulla versione ridotta 400 volte [(0,249,250,0),(2,1,357,714)]dell'ultimo test, è terminato in poco più di 12 secondi. Sulla base di questo, il vero caso di test sarebbe finito in circa 25 anni.

Spiegazione (parziale, lo espanderò quando avrò tempo)

Prima costruiamo due elenchi he vcome segue. Per ogni rettangolo nell'input, dividiamo il suo bordo in segmenti di lunghezza 1. Gli endpoint ovest dei segmenti orizzontali sono memorizzati in h, e gli endpoint sud dei segmenti verticali in v, come elenchi [x,y]di lunghezza 2. Le coordinate in vsono memorizzate in un ordine inverso forma [y,x]per motivi di golf. Quindi passiamo semplicemente su entrambi gli elenchi e cerchiamo il bordo orizzontale [x,j]e il bordo verticale in modo [i,y]tale che ( x < ie i-x == j-yquindi siano gli angoli nord-ovest e sud-est di un quadrato), e controlliamo che i bordi del quadrato siano negli elenchi corretti he v, mentre l'interno le coordinate non lo sono. Il numero delle istanze positive della ricerca è l'output.


Ben fatto, penso che dovrò concedere ora :)
MickyT

@MickyT È passata una settimana, quindi per ora ho accettato la risposta di Zgarb, ma se riesci a batterlo in seguito il segno di spunta potrebbe spostarsi! Onestamente sono rimasto molto colpito da quanto siete riusciti ad andare
lontano

@ Merita vittoria meritata :-)
MickyT il

@ Sp3000 grazie per una bella piccola sfida.
MickyT,

@ Grazie Sp3000! Mi sono divertito molto a giocare a golf.
Zgarb,
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.