Identificare i triangoli


11

Contare la quantità di triangoli in un'immagine è un compito comunemente usato nei test del cervello. Ti viene data un'immagine che contiene forme costituite da triangoli. È quindi necessario trovare tutti i triangoli possibili nell'immagine.

Compito

Ti viene dato un elenco di righe in un formato a tua scelta. È quindi necessario generare un elenco di triangoli trovati in quello

Ingresso

Ti viene dato un elenco di linee, ognuna data da quattro coordinate intere (es. x1 y1 x2 y2). È possibile scegliere il formato di input, purché sia ​​chiaramente documentato. Esempi:

0 4 8 1
0 4 9 5
8 1 9 5
2 8 0 4
9 5 2 8

[[0, 4, 8, 1], [0, 4, 9, 5], [8, 1, 9, 5], [2, 8, 0, 4], [9, 5, 2, 8]]

Ecco lo stesso input di un'immagine:

disegno a triangolo

Un altro, con intersezioni (solo in un formato per risparmiare spazio):

[[2, 1, 5, 0], [2, 1, 2, 7], [5, 0, 6, 6], [5, 0, 2, 7], [6, 6, 2, 1], [2, 7, 6, 6]]

disegno a triangolo

Produzione

È necessario generare un elenco di tutti i triangoli, ciascuno dato da sei coordinate in virgola mobile (ad es. x1 y1 x2 y2 x3 y3), Nell'immagine specificata dall'input. Questi potrebbero non essere numeri interi, poiché le linee possono incrociarsi in qualsiasi punto. È possibile scegliere il formato di output, purché sia ​​chiaramente documentato. Output di esempio per gli input di esempio sopra:

0 4 8 1 9 5
0 4 9 5 2 8

[[0, 4, 8, 3, 9, 5], [0, 4, 9, 5, 2, 8]]
[[2, 1, 5, 0, 2, 7], [2, 1, 5, 0, 6, 6], [5, 0, 6, 6, 2, 7], [2, 1, 6, 6, 2, 7], [2, 1, 5, 0, 3.674, 3.093], [5, 0, 6, 6, 3.674, 3.093], [6, 6, 2, 7, 3.674, 3.093], [2, 7, 2, 1, 3.674, 3.093]]

Si può presumere che

  • non ci sono casi limite in cui una linea attraversa un'intersezione ma nessuna linea, come

    [[0, 9, 1, 8], [1, 8, 2, 9], [2, 9, 3, 8], [3, 8, 4, 9], [4, 9, 0, 9]]
    
  • non ci sono angoli superiori a 179 gradi, come

    [[0, 0, 0, 1], [0, 1, 0, 2], [0, 2, 0, 0]]
    

Regole

  • Puoi usare qualsiasi lingua tu voglia.
  • Non è necessario utilizzare risorse esterne.
  • Si applicano scappatoie standard .

punteggio

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


È sufficiente identificare 3 cicli o dobbiamo gestire casi limite più complessi? Ad esempio il "pentagono" definito da [0,9],[1,8],[2,9],[3,8],[4,9]è in realtà una W con una linea tracciata nella parte superiore. Non sono triangoli o 2 triangoli?
Level River St,

@steveverrill Supponiamo che i casi limite possano essere ignorati.
PurkkaKoodari,

Ok. E [0,0],[1,0],[2,0],[1,2]un quadrilatero con un angolo di 180 gradi. Nessun triangolo o 1 triangolo?
Level River St,

Questo non sarebbe un triangolo, ma puoi anche supporre che non arrivi neanche.
PurkkaKoodari,

Risposte:


1

PostGIS, 162

Penso che questo sia conforme alle regole, è una query per PostGIS, che è un'estensione di PostgreSQL. Si presume che l'input sia una tabella di coordinate per ogni linea chiamata L L'output è un insieme di righe con la definizione poligonale per i triangoli formati.

SELECT ST_AsText(D)FROM(SELECT(ST_Dump(ST_Polygonize(B))).geom D FROM(SELECT ST_Union(ST_MakeLine(ST_Point(A,B),ST_Point(C,D)))B FROM L)A)B WHERE ST_NPoints(D)=4;

In uso sembra il seguente

-- Create a table for the input
CREATE TABLE L (A INT, B INT, C INT,D INT);
INSERT INTO L VALUES(2, 1, 5, 0), (2, 1, 2, 7), (5, 0, 6, 6), (5, 0, 2, 7), (6, 6, 2, 1), (2, 7, 6, 6);

SELECT ST_AsText(D)FROM(SELECT(ST_Dump(ST_Polygonize(B))).geom D FROM(SELECT ST_Union(ST_MakeLine(ST_Point(A,B),ST_Point(C,D)))B FROM L)A)B WHERE ST_NPoints(D)=4;

-- Cleanup
DROP TABLE L;

L'output è il seguente

POLYGON((5 0,2 1,3.67441860465116 3.09302325581395,5 0))
POLYGON((6 6,5 0,3.67441860465116 3.09302325581395,6 6))
POLYGON((3.67441860465116 3.09302325581395,2 7,6 6,3.67441860465116 3.09302325581395))
POLYGON((2 7,3.67441860465116 3.09302325581395,2 1,2 7))

7

Mathematica 915 395 401 405

Aggiornare

Questa sfida di programmazione è molto più difficile di quanto sembri inizialmente.

Il presente approccio funziona con casi semplici, in cui esiste solo una singola intersezione lungo la lunghezza di qualsiasi segmento di linea. Con più incroci lungo un segmento, è necessario tenere traccia di tutti i punti di intersezione lungo ciascuna linea e creare nuovi sotto-segmenti (quindi bordi del grafico aggiuntivi) che collegano la nuova intersezione a tutti i punti di intersezione lungo la linea di destinazione.

Nonostante tale limitazione, può valere la pena condividere la logica alla base dell'attuale approccio.


I segmenti della linea di input sono trattati come regioni. Se si intersecano, il centroide sarà le coordinate dell'intersezione. Dobbiamo eliminare quelle intersezioni che si verificano ai vertici dei segmenti di linea. Le linee che non si intersecano avranno un centroide indeterminato.

Vengono creati quattro nuovi bordi per ciascun punto di intersezione. Collegano il punto di intersezione ai quattro vertici delle due linee che si intersecano.

Un grafico come quello in basso a destra viene generato utilizzando sia i bordi vecchi che quelli nuovi.

I vertici sono le coordinate dei rispettivi punti. I cicli, ovvero gli anelli chiusi di tre vertici, saranno triangoli a condizione che i tre vertici non siano collineari.

Attualmente controlliamo se un "triangolo" ha un'area indeterminata. (Per qualche motivo non restituisce un'area di 0 per tre punti collineari.)


Un semplice esempio

Qui di seguito sono (a) figura stampata nel piano delle coordinate e (b) il grafico che mostra i nodi trovati così come il nodo di intersezione, {114/23, 314/69}. In quest'ultimo caso, i vertici non si trovano nelle rispettive coordinate cartesiane.

Può sembrare che siano più spigoli nella figura destra che a sinistra. Ma ricorda che ci sono bordi del grafico sovrapposti a sinistra. Ogni diagonale corrisponde effettivamente a 3 bordi del grafico!


grafici

    f@w_ :=(h@{a_, b_, c_, d_} := (r = RegionCentroid@RegionIntersection[Line@{a, b}, Line@{c, d}];
     {r <-> a, r <-> b, r <-> c, r <-> d});
      Cases[FindCycle[Graph[Union@Join[w /. {{a_, b_Integer}, {c_, d_}} :> {a, b} <-> {c, d},
      Cases[Flatten[h /@ Cases[{Length[Union@#] < 4, #} & /@ (FlattenAt[#, {{1}, {2}}] & /@ 
      Subsets[w, {2}]),{False, c_} :> c]], Except[{Indeterminate, _} <-> _]]]], {3}, 50],
      x_ /; NumericQ[RegionMeasure@Triangle[x[[All, 1]]]]][[All, All, 1]]//N//Grid)

Ogni riga sotto è un triangolo.

f[{{{2,8},{8,1}},{{0,4},{8,1}},{{0,4},{9,5}},{{8,1},{9,5}},{{2,8},{0,4}},{{9,5},{2,8}}}]

coords


Un esempio più complesso

f@{{{9, 5}, {0, -10}}, {{9, 5}, {0, 2}},  {{9, 5}, {2, -1}}, {{0, -10}, {2, -1}}, {{0, -10}, {-2, -1}}, {{-9, 5}, {0, -10}}, {{-9, 5}, {0, 2}}, {{-9, 5}, {-2, -1}}, {{0, 2}, {0, -10}}, {{-9, 5}, {2, -1}}, {{9, 5}, {-2, -1}}, {{-9, 5}, {9, 5}}}

Ecco il grafico corrispondente alle coordinate di input . I vertici sono alle coordinate cartesiane previste. (Se si esegue il codice golfed visualizzerà i vertici altrove nel rispetto delle etichette e dei bordi dei vertici. Per leggibilità, ho assegnato le coordinate dei vertici usando una quantità di codice aggiuntivo, non necessaria per la soluzione.)

Graph2


Ecco il grafico derivato.
Include il punto di intersezione derivato (0,1/11), in cui alcune delle linee di input si incrociano.

diciannove

Il codice ha trovato 19 triangoli. Nove hanno il punto, (0,1/11)come uno dei vertici.

nineteen2


Ok. Ora è sotto forma di una funzione.
DavidC,

4

Java, 1051 1004

(Programma pienamente funzionante)

Ho pensato che questa fosse una bella sfida non solo per golfare un po 'di codice, ma principalmente per esercitarsi a scrivere funzioni matematiche.

E per disegnare una "base" ho realizzato questo in Java * Aspetta che tutti inizino a ridere * .

Codice

import java.util.*;class P{double x,y;static P l(double... i){double a=i[0],b=i[1],c=i[2],d=i[3],e=i[4],f=i[5],k,l,x=(k=i[7]-f)*(c-a)-(l=i[6]-e)*(d-b),n=(l*(b-f)-k*(a-e))/x,m=((c-a)*(b-f)-(d-b)*(a-e))/x;P p=new P();p.x=a+n*(c-a);p.y=b+n*(d-b);return(n>=0&n<=1&m>=0&m<=1&x!=0)?p:null;}public static void main(String[]p){Set<String>v=new HashSet();P q,w,e;Integer a,b,c,d,k,f,g,h,i,j,m,l,r,t,y,z;int[][]x=new int[l=p.length/4][4];for(c=0;c<l;c++){for(d=0;d<4;){x[c][d]=l.parseInt(p[c*4+d++]);}}z=x.length;for(r=0;r<z;r++){a=x[r][0];b=x[r][1];c=x[r][2];d=x[r][3];for(t=0;t<z;t++){if(t!=r){k=x[t][0];f=x[t][1];g=x[t][2];h=x[t][3];q=l(a,b,c,d,k,f,g,h);if(q!=null){for(y=0;y<z;y++){if(y!=r&y!=t){i=x[y][0];j=x[y][1];m=x[y][2];l=x[y][3];w=l(a,b,c,d,i,j,m,l);e=l(k,f,g,h,i,j,m,l);if(w!=null&&e!=null&&q.x!=e.x&q.y!=e.y&!v.contains(""+r+y+t)){v.add(""+r+t+y);v.add(""+r+y+t);v.add(""+t+r+y);v.add(""+t+y+r);v.add(""+y+r+t);v.add(""+y+t+r);System.out.printf("%s %s %s %s %s %s\n",q.x,q.y,w.x,w.y,e.x,e.y);}}}}}}}}}

Ingresso

Numeri interi separati da spazio. In coppie di 4 (x1, y1, x2, y2)

2 1 5 0 2 1 2 7 5 0 6 6 5 0 2 7 6 6 2 1 2 7 6 6

Output (l'output reale non viene arrotondato a 3 decimali)

Ogni linea contiene un triangolo Ogni linea è composta da punti flottanti separati da spazio in coppie di 2 (x1, y1, x2, y2, x3, y3). (Nota: l'ordine dei 3 punti che formano il triangolo non è definito.)

5.0 0.0 2.0 1.0 6.0 6.0
5.0 0.0 2.0 1.0 2.0 7.0
5.0 0.0 2.0 1.0 3.674 3.093
2.0 7.0 2.0 1.0 3.674 3.093
2.0 1.0 2.0 7.0 6.0 6.0
5.0 0.0 6.0 6.0 3.674 3.093
5.0 0.0 6.0 6.0 2.0 7.0
3.674 3.093 2.0 7.0 6.0 6.0

Spiegazione

Ho iniziato a scrivere un metodo per trovare l'intersezione tra due linee non infinite. Il metodo risultante è per uno stile Java piuttosto corto (246). Invece di lasciare che il metodo di input sia composto da 8 punti doppi o due (P), scelgo di usare un parametro arbitrario per proteggere enormi quantità di caratteri. Per ridurre al minimo l'utilizzo dell'operatore dell'array, ciascun parametro utilizzato più di 2 volte viene inserito nella propria variabile.

static P l(double... i){double a=i[0],b=i[1],c=i[2],d=i[3],e=i[4],f=i[5],k,l,x=(k=i[7]-f)*(c-a)-(l=i[6]-e)*(d-b),n=(l*(b-f)-k*(a-e))/x,m=((c-a)*(b-f)-(d-b)*(a-e))/x;P p=new P();p.x=a+n*(c-a);p.y=b+n*(d-b);return(n>=0&n<=1&m>=0&m<=1&x!=0)?p:null;}

Altre spiegazioni da aggiungere ... (probabilmente questa risposta può essere giocata ancora di più)


0

BBC BASIC

Emulatore su http://www.bbcbasic.co.uk/bbcwin/bbcwin.html

Mi aspetto che questo finisca nel 400.

Input Output

Ogni volta che l'utente inserisce una nuova riga, il programma verifica se sono stati creati nuovi triangoli e li emette immediatamente, vedere di seguito.

Viene creato un nuovo triangolo ogni volta che la nuova linea si interseca con due linee preesistenti che si intersecano reciprocamente (tranne quando tutte e tre le linee si intersecano in un punto, che è un caso speciale che deve essere trattato.)

inserisci qui la descrizione dell'immagine

Codice

Il programma principale è il più semplice possibile. Alla fine è la funzione, che svolge il complesso compito di rilevare le intersezioni, secondo la formula in http://en.wikipedia.org/wiki/Line%E2%80%93line_intersection

La funzione restituisce zero se non vi è intersezione e un numero in virgola mobile diverso da zero se presente. Ha anche un effetto collaterale: le coordinate dell'intersezione vengono aggiunte alla stringa z $. Inoltre, nella BBC di base le variabili di una funzione sono visibili al programma principale a condizione che il programma principale non abbia una variabile con lo stesso nome (anche dopo che la funzione è terminata).

Pertanto il programma principale ha accesso alle variabili xe y, e me n, che memorizzano le coordinate delle intersezioni correnti e precedenti. Questo è usato per rilevare se abbiamo davvero trovato un triangolo e non solo tre linee che si intersecano in un punto.

  DIM a(99),b(99),c(99),d(99)                                                    :REM declare 4 arrays to hold the ata
  y=0                                                                            :REM x and y are only initialized
  x=0                                                                            :REM to avoid a no such varialbe error later
  FOR i=0 TO 99                                                                  :REM for each input line
    INPUT a(i),b(i),c(i),d(i)
    FOR j=0 TO i-1                                                               :REM iterate through all combinations of 2 previous lines
      FOR k=0 TO j-1
        z$=""                                                                    :REM clear z$, three function calls on next line will write the triangle (if found) to it
        IF i>j AND j>k AND FNf(i,j)*FNf(i,k)*FNf(j,k)<>0 IF x<>m OR y<>n PRINT z$:REM to avoid printing the same triangle twice, print only if j,k,i in lexicographic order. Also reject if x,y (3rd FNf call) and m,n (2nd FNf call) are the same: this means a point, not a triangle.
      NEXT
    NEXT
  NEXT

  DEF FNf(g,h)                                                                   :REM returns zero if no intersection found, otherwise a floating point value
  m=x                                                                            :REM backup previous x and y
  n=y                                                                            :REM to use in test for point versus triangle
  p=a(g)-c(g)
  q=b(g)-d(g)
  r=a(h)-c(h)
  s=b(h)-d(h)
  t=a(g)*d(g)-b(g)*c(g)
  u=a(h)*d(h)-b(h)*c(h)
  e=p*s-q*r                                                                      :REM following method in wikipedia, calculate denominator of expression
  IF e<>0 x=(t*r-u*p)/e : y=(t*s-u*q)/e: z$=z$+" "+STR$(x)+" "+STR$(y)           :REM if denominator not zero, calculate x and y and append a string copy to z$
  IF (a(g)-x)*(c(g)-x)>0 OR (b(g)-y)*(d(g)-x)>0 OR(a(h)-x)*(c(h)-x)>0 OR(b(h)-y)*(d(h)-y)>0 e=0
  =e          :REM return e                                                      :REM previous line sets e to zero if the intersection falls outside the line segment. This is detected when both are on the same side of the intersection, which yields a positive multiplication result.
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.