Dreiecke identifizieren

11

Das Zählen der Anzahl von Dreiecken in einem Bild ist eine Aufgabe, die häufig bei Gehirntests verwendet wird. Sie erhalten ein Bild, das Formen enthält, die aus Dreiecken bestehen. Sie müssen dann alle möglichen Dreiecke im Bild finden.

Aufgabe

Sie erhalten eine Liste von Zeilen in einem Format Ihrer Wahl. Sie müssen dann eine Liste der darin gefundenen Dreiecke ausgeben

Eingang

Sie erhalten eine Liste von Zeilen, die jeweils durch vier ganzzahlige Koordinaten (z. B. x1 y1 x2 y2) angegeben sind. Sie können das Eingabeformat wählen, sofern es klar dokumentiert ist. Beispiele:

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]]

Hier ist die gleiche Eingabe wie bei einem Bild:

Dreieckszeichnung

Eine andere mit Kreuzungen (nur in einem Format, um Platz zu sparen):

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

Dreieckszeichnung

Ausgabe

Sie müssen eine Liste aller Dreiecke ausgeben, die jeweils durch sechs Gleitkommakoordinaten (z. B. x1 y1 x2 y2 x3 y3) in dem durch die Eingabe angegebenen Bild angegeben sind. Dies sind möglicherweise keine ganzen Zahlen, da sich die Linien an jedem Punkt kreuzen können. Sie können das Ausgabeformat auswählen, sofern es eindeutig dokumentiert ist. Beispielausgaben für die obigen Beispieleingaben:

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]]

Sie können das annehmen

  • Es gibt keine Randfälle, in denen eine Linie einen Schnittpunkt kreuzt, aber keine Linien wie

    [[0, 9, 1, 8], [1, 8, 2, 9], [2, 9, 3, 8], [3, 8, 4, 9], [4, 9, 0, 9]]
    
  • Es gibt keine Winkel über 179 Grad

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

Regeln

  • Sie können jede gewünschte Sprache verwenden.
  • Es dürfen keine externen Ressourcen verwendet werden.
  • Es gelten Standardlücken .

Wertung

Dies ist , daher gewinnt die kürzeste Antwort in Bytes .

PurkkaKoodari
quelle
Ist es ausreichend, 3 Zyklen zu identifizieren, oder müssen wir komplexere Kantenfälle behandeln? Zum Beispiel ist das durch definierte "Fünfeck" [0,9],[1,8],[2,9],[3,8],[4,9]tatsächlich ein W mit einer Linie, die über die Oberseite gezogen wird. Ist das kein Dreieck oder 2 Dreiecke?
Level River St
@steveverrill Nehmen wir an, Randfälle können ignoriert werden.
PurkkaKoodari
In Ordnung. Und und [0,0],[1,0],[2,0],[1,2]ein "Viereck" mit einem Winkel von 180 Grad. Keine Dreiecke oder 1 Dreieck?
Level River St
Das wäre kein Dreieck, aber Sie können davon ausgehen, dass das auch nicht kommt.
PurkkaKoodari

Antworten:

1

PostGIS, 162

Ich denke, dies entspricht den Regeln. Es ist eine Abfrage für PostGIS, eine Erweiterung von PostgreSQL. Es wird angenommen, dass die Eingabe eine Koordinatentabelle für jede Zeile mit der Bezeichnung L ist. Die Ausgabe ist eine Reihe von Zeilen mit der Polygondefinition für die gebildeten Dreiecke.

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;

Im Gebrauch sieht es wie folgt aus

-- 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;

Die Ausgabe ist wie folgt

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))
MickyT
quelle
7

Mathematica 915 395 401 405

Aktualisieren

Diese Programmierherausforderung ist viel schwieriger als es zunächst scheint.

Der vorliegende Ansatz funktioniert mit einfachen Fällen, in denen es nur einen einzigen Schnittpunkt entlang der Länge eines Liniensegments gibt. Bei mehreren Kreuzungen entlang eines Segments ist es erforderlich, alle Schnittpunkte entlang jeder Linie zu verfolgen und neue Untersegmente (daher zusätzliche Diagrammkanten) zu erstellen, die den neuen Schnittpunkt mit allen Schnittpunkten entlang der Ziellinie verbinden.

Trotz dieser Einschränkung kann es sinnvoll sein, die dem aktuellen Ansatz zugrunde liegende Logik zu teilen.


Die Eingabezeilensegmente werden als Regionen behandelt. Wenn sie sich schneiden, ist der Schwerpunkt die Koordinate der Kreuzung. Wir müssen die Schnittpunkte beseitigen, die an den Eckpunkten von Liniensegmenten auftreten. Linien, die sich nicht schneiden, haben einen unbestimmten Schwerpunkt.

Für jeden Schnittpunkt werden vier neue Kanten erstellt. Sie verbinden den Schnittpunkt mit den vier Eckpunkten der beiden Schnittlinien.

Ein Diagramm wie das unten rechts wird mit den alten und neuen Kanten erstellt.

Die Eckpunkte sind die Koordinaten der jeweiligen Punkte. Zyklen, dh geschlossene Schleifen von drei Eckpunkten, sind Dreiecke, vorausgesetzt, die drei Eckpunkte sind nicht kollinear.

Gegenwärtig prüfen wir, ob ein "Dreieck" eine unbestimmte Fläche hat. (Aus irgendeinem Grund wird für drei kollineare Punkte kein Bereich von 0 zurückgegeben.)


Ein einfaches Beispiel

Unten sind (a) die in der Koordinatenebene aufgetragene Figur und (b) der Graph, der die gegebenen Knoten sowie den Schnittpunktknoten zeigt , {114/23, 314/69}. In letzterem befinden sich die Eckpunkte nicht an den jeweiligen kartesischen Koordinaten.

Es scheint, dass es in der rechten Abbildung mehr Kanten gibt als in der linken. Denken Sie jedoch daran, dass sich links überlappende Grafikkanten befinden. Jede Diagonale entspricht tatsächlich 3 Grafikkanten!


Grafiken

    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)

Jede Zeile darunter ist ein Dreieck.

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}}}]

Koordinaten


Ein komplexeres Beispiel

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}}}

Hier ist das Diagramm, das den Eingabekoordinaten entspricht . Die Eckpunkte liegen an ihren erwarteten kartesischen Koordinaten. (Wenn Sie den Golfcode ausführen, werden die Scheitelpunkte an anderer Stelle angezeigt, wobei die Scheitelpunktbeschriftungen und -kanten berücksichtigt werden. Zur besseren Lesbarkeit habe ich die Scheitelpunktkoordinaten mit ein wenig zusätzlichem Code zugewiesen, der für die Lösung nicht erforderlich ist.)

graph2


Hier ist das abgeleitete Diagramm.
Es enthält den abgeleiteten Schnittpunkt (0,1/11), an dem sich einige der Eingangslinien kreuzen.

neunzehn

Der Code fand 19 Dreiecke. Neun von ihnen haben den Punkt (0,1/11)als einen der Eckpunkte.

neunzehn2

DavidC
quelle
In Ordnung. Es ist jetzt in Form einer Funktion.
DavidC
4

Java, 1051 1004

(Voll funktionsfähiges Programm)

Ich dachte, dies ist eine schöne Herausforderung, nicht nur Code zu spielen, sondern vor allem das Schreiben mathematischer Funktionen zu üben.

Und um eine "Grundlinie" zu zeichnen, habe ich diese in Java erstellt. * Wartet darauf, dass alle anfangen zu lachen * .

Code

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);}}}}}}}}}

Eingang

Durch Leerzeichen getrennte Ganzzahlen. In 4er-Paaren (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

Ausgabe (reale Ausgabe rundet nicht auf 3 Dezimalstellen)

Jede Linie enthält ein Dreieck. Jede Linie besteht aus durch Leerzeichen getrennten Gleitkommawerten in Zweierpaaren (x1, y1, x2, y2, x3, y3). (Hinweis: Die Reihenfolge der 3 Punkte, die das Dreieck bilden, ist undefiniert.)

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

Erläuterung

Ich begann eine Methode zu schreiben, um den Schnittpunkt zwischen zwei nicht unendlichen Linien zu finden. Die resultierende Methode ist für einen Java-Stil ziemlich kurz (246). Anstatt die Methodeneingabe aus 8 Doppel- oder zwei Punkten (P) bestehen zu lassen, verwende ich einen beliebigen Parameter, um große Mengen an Zeichen zu sichern. Um die Verwendung des Array-Operators zu minimieren, wird jeder Parameter, der mehr als zweimal verwendet wird, in eine eigene Variable eingefügt.

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;}

Weitere Erklärung hinzugefügt werden ... (diese Antwort kann wahrscheinlich noch mehr Golf gespielt werden)

Rolf ツ
quelle
0

BBC BASIC

Emulator unter http://www.bbcbasic.co.uk/bbcwin/bbcwin.html

Ich erwarte, dass dies bis in die 400er Jahre hinein Golf spielen wird.

Input-Output

Jedes Mal, wenn der Benutzer eine neue Zeile eingibt, prüft das Programm, ob neue Dreiecke erstellt wurden, und gibt diese sofort aus (siehe unten).

Ein neues Dreieck wird überall dort erstellt, wo sich die neue Linie mit zwei bereits vorhandenen Linien schneidet, die sich ebenfalls gegenseitig schneiden (außer wenn sich alle drei Linien an einem Punkt schneiden, was ein Sonderfall ist, der behandelt werden muss).

Geben Sie hier die Bildbeschreibung ein

Code

Das Hauptprogramm ist so einfach wie möglich. Am Ende steht die Funktion, die die komplexe Aufgabe des Erkennens von Schnittpunkten gemäß der Formel in http://en.wikipedia.org/wiki/Line%E2%80%93line_intersection ausführt

Die Funktion gibt Null zurück, wenn kein Schnittpunkt vorhanden ist, und eine Gleitkommazahl ungleich Null, falls vorhanden. Es hat auch einen Nebeneffekt: Die Koordinaten des Schnittpunkts werden an die Zeichenfolge z $ angehängt. Darüber hinaus sind in BBC Basic die Variablen einer Funktion für das Hauptprogramm sichtbar, vorausgesetzt, das Hauptprogramm verfügt nicht über eine gleichnamige Variable (auch nach Beendigung der Funktion).

Daher hat das Hauptprogramm Zugriff auf die Variablen xund yund mund n, die die Koordinaten der aktuellen und vorherigen Schnittpunkte speichern. Dies wird verwendet, um festzustellen, ob wir wirklich ein Dreieck gefunden haben und nicht nur drei Linien, die sich an einem Punkt schneiden.

  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.
Level River St.
quelle