Identifier les triangles

11

Compter la quantité de triangles dans une image est une tâche couramment utilisée dans les tests cérébraux. Vous obtenez une image contenant des formes composées de triangles. Vous devez ensuite trouver tous les triangles possibles dans l'image.

Tâche

Vous obtenez une liste de lignes dans un format de votre choix. Vous devez ensuite sortir une liste de triangles trouvés dans ce

Contribution

Vous obtenez une liste de lignes, chacune étant donnée par quatre coordonnées entières (par exemple. x1 y1 x2 y2). Vous pouvez choisir le format d'entrée, tant qu'il est clairement documenté. Exemples:

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

Voici la même entrée qu'une image:

dessin de triangle

Un autre, avec des intersections (seulement dans un format pour économiser de l'espace):

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

dessin de triangle

Production

Vous devez produire une liste de tous les triangles, chacun étant donné par six coordonnées à virgule flottante (par exemple. x1 y1 x2 y2 x3 y3), Dans l'image spécifiée par l'entrée. Il peut ne pas s'agir d'entiers, car les lignes peuvent se croiser à tout moment. Vous pouvez choisir le format de sortie, tant qu'il est clairement documenté. Exemples de sorties pour les exemples d'entrées ci-dessus:

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

Vous pouvez supposer que

  • il n'y a pas de cas où une ligne traverse une intersection mais pas de lignes, comme

    [[0, 9, 1, 8], [1, 8, 2, 9], [2, 9, 3, 8], [3, 8, 4, 9], [4, 9, 0, 9]]
    
  • il n'y a pas d'angles supérieurs à 179 degrés, comme

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

Règles

  • Vous pouvez utiliser la langue de votre choix.
  • Aucune ressource externe ne doit être utilisée.
  • Des échappatoires standard s'appliquent.

Notation

Il s'agit de , donc la réponse la plus courte en octets l' emporte.

PurkkaKoodari
la source
Est-il suffisant d'identifier 3 cycles ou devons-nous gérer des cas de bord plus complexes? Par exemple, le "pentagone" défini par [0,9],[1,8],[2,9],[3,8],[4,9]est en fait un W avec une ligne tracée en haut. N'est-ce pas des triangles ou 2 triangles?
Level River St
@steveverrill Disons que les cas marginaux peuvent être ignorés.
PurkkaKoodari
D'accord. Et Et [0,0],[1,0],[2,0],[1,2]Un "quadrilatère" avec un angle de 180 degrés. Pas de triangles ou 1 triangle?
Level River St
Ce ne serait pas un triangle, mais vous pouvez supposer que cela ne vient pas non plus.
PurkkaKoodari

Réponses:

1

PostGIS, 162

Je pense que cela est conforme aux règles, c'est une requête pour PostGIS, qui est une extension de PostgreSQL. L'entrée est supposée être un tableau de coordonnées pour chaque ligne appelée L La sortie est un ensemble de lignes avec la définition de polygone pour les triangles formés.

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;

En cours d'utilisation, il ressemble à ce qui suit

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

La sortie est la suivante

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
la source
7

Mathematica 915 395 401 405

Mise à jour

Ce défi de programmation est beaucoup plus difficile qu'il n'y paraît à première vue.

La présente approche fonctionne avec des cas simples, où il n'y a qu'une seule intersection le long de n'importe quel segment de ligne. Avec plusieurs croisements le long d'un segment, il est nécessaire de garder une trace de tous les points d'intersection le long de chaque ligne et de créer de nouveaux sous-segments (donc des bords de graphique supplémentaires) reliant la nouvelle intersection à tous les points d'intersection le long de la ligne cible.

Malgré cette limitation, il peut être utile de partager la logique qui sous-tend l'approche actuelle.


Les segments de ligne d'entrée sont traités comme des régions. S'ils se croisent, le centroïde sera les coordonnées de l'intersection. Nous devons éliminer les intersections qui se produisent aux sommets des segments de ligne. Les lignes qui ne se coupent pas auront un centroïde indéterminé.

Quatre nouvelles arêtes sont créées pour chaque point d'intersection. Ils relient le point d'intersection aux quatre sommets des deux lignes d'intersection.

Un graphique tel que celui ci-dessous à droite est généré en utilisant à la fois l'ancien et le nouveau bord.

Les sommets sont les coordonnées des points respectifs. Les cycles, c'est-à-dire les boucles fermées de trois sommets seront des triangles à condition que les trois sommets ne soient pas colinéaires.

Actuellement, nous vérifions si un "triangle" a une zone indéterminée. (Pour une raison quelconque, il ne renvoie pas une zone de 0 pour trois points colinéaires.)


Un exemple simple

Ci - dessous , sont (a) la figure tracée dans le plan de coordonnées et (b) le graphique représentant les noeuds donnés, ainsi que le noeud d'intersection, {114/23, 314/69}. Dans ce dernier, les sommets ne sont pas situés aux coordonnées cartésiennes respectives.

Il peut sembler que ce sont plus de bords sur la figure de droite que sur la gauche. Mais rappelez-vous qu'il y a des bords de graphique qui se chevauchent à gauche. Chaque diagonale correspond en fait à 3 arêtes de graphe!


graphiques

    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)

Chaque ligne ci-dessous est un triangle.

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 exemple plus complexe

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

Voici le graphique correspondant aux coordonnées d' entrée . Les sommets sont à leurs coordonnées cartésiennes attendues. (Si vous exécutez le code golfé, il affichera les sommets ailleurs tout en respectant les étiquettes et les arêtes des sommets. Pour plus de lisibilité, j'ai attribué les coordonnées des sommets en utilisant une poignée de code supplémentaire, pas nécessaire pour la solution.)

graph2


Voici le graphique dérivé.
Il comprend le point d'intersection dérivé (0,1/11), où certaines des lignes d'entrée se croisent.

dix-neuf

Le code a trouvé 19 triangles. Neuf d'entre eux ont le point, (0,1/11)comme l'un des sommets.

dix-neuf2

DavidC
la source
D'accord. C'est maintenant sous la forme d'une fonction.
DavidC
4

Java, 1051 1004

(Programme entièrement fonctionnel)

Je pensais que c'était un beau défi non seulement pour jouer au code, mais surtout pour pratiquer l'écriture de fonctions mathématiques.

Et pour dessiner une "ligne de base", j'ai fait celle-ci en Java * Attend que tout le monde commence à rire * .

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

Contribution

Entiers séparés par des espaces. Par paire de 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

Sortie (la sortie réelle n'est pas arrondie à 3 décimales)

Chaque ligne contient un triangle Chaque ligne se compose de points flottants séparés par des espaces en paires de 2 (x1, y1, x2, y2, x3, y3). (Remarque: l'ordre des 3 points qui forment le triangle n'est pas défini.)

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

Explication

J'ai commencé à écrire une méthode pour trouver l'intersection entre deux lignes non infinies. La méthode résultante est pour un style Java assez court (246). Au lieu de laisser l'entrée de la méthode se composer de 8 doubles ou deux points (P), je choisis d'utiliser un paramètre arbitraire pour sécuriser des quantités massives de caractères. Pour minimiser l'utilisation de l'opérateur de tableau, chaque paramètre utilisé plus de 2 fois est placé dans sa propre variable.

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

Plus d'explications à ajouter ... (cette réponse peut probablement être jouée encore plus)

Rolf ツ
la source
0

BBC BASIC

Émulateur sur http://www.bbcbasic.co.uk/bbcwin/bbcwin.html

Je m'attends à ce que cela se joue dans les 400.

Entrée sortie

Chaque fois que l'utilisateur entre dans une nouvelle ligne, le programme vérifie si de nouveaux triangles ont été créés et les sort immédiatement, voir ci-dessous.

Un nouveau triangle est créé là où la nouvelle ligne coupe avec deux lignes préexistantes qui se croisent également (sauf lorsque les trois lignes se coupent en un point, ce qui est un cas spécial qui doit être traité.)

entrez la description de l'image ici

Code

Le programme principal est à peu près aussi simple que possible. À la fin se trouve la fonction, qui effectue la tâche complexe de détection des intersections, selon la formule de http://en.wikipedia.org/wiki/Line%E2%80%93line_intersection

La fonction renvoie zéro s'il n'y a pas d'intersection et un nombre à virgule flottante différent de zéro s'il y en a. Il a également un effet secondaire: les coordonnées de l'intersection sont ajoutées à la chaîne z $. De plus, dans BBC basic, les variables d'une fonction sont visibles pour le programme principal à condition que le programme principal ne possède pas de variable du même nom (même après la fin de la fonction).

Par conséquent, le programme principal a accès aux variables xet y, et met n, qui stockent les coordonnées des intersections actuelles et précédentes. Ceci est utilisé pour détecter si nous avons vraiment trouvé un triangle et pas seulement trois lignes se coupant en un point.

  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
la source