Identificando los triángulos

11

Contar la cantidad de triángulos en una imagen es una tarea comúnmente utilizada en las pruebas cerebrales. Se le da una imagen que contiene formas que consisten en triángulos. Luego debes encontrar todos los triángulos posibles en la imagen.

Tarea

Se le proporciona una lista de líneas en el formato que elija. Luego debe generar una lista de triángulos encontrados en ese

Entrada

Se le da una lista de líneas, cada una dada por cuatro coordenadas enteras (p. Ej. x1 y1 x2 y2). Puede elegir el formato de entrada, siempre que esté claramente documentado. Ejemplos:

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

Aquí está la misma entrada que una imagen:

dibujo triangular

Otro, con intersecciones (solo en un formato para ahorrar espacio):

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

dibujo triangular

Salida

Debe generar una lista de todos los triángulos, cada uno dado por seis coordenadas de punto flotante (p. Ej. x1 y1 x2 y2 x3 y3), En la imagen especificada por la entrada. Estos pueden no ser enteros, ya que las líneas pueden cruzarse en cualquier punto. Puede elegir el formato de salida, siempre que esté claramente documentado. Salidas de ejemplo para las entradas de ejemplo anteriores:

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

Puedes suponer que

  • no hay casos de borde donde una línea cruza una intersección pero no hay líneas, como

    [[0, 9, 1, 8], [1, 8, 2, 9], [2, 9, 3, 8], [3, 8, 4, 9], [4, 9, 0, 9]]
    
  • no hay ángulos superiores a 179 grados, como

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

Reglas

  • Puede usar cualquier idioma que desee.
  • No se deben utilizar recursos externos.
  • Se aplican lagunas estándar .

Puntuación

Este es el , por lo que gana la respuesta más corta en bytes .

PurkkaKoodari
fuente
¿Es suficiente identificar 3 ciclos o tenemos que manejar casos extremos más complejos? Por ejemplo, el "pentágono" definido por [0,9],[1,8],[2,9],[3,8],[4,9]es en realidad una W con una línea dibujada en la parte superior. ¿No hay triángulos o 2 triángulos?
Level River St
@steveverrill Digamos que los casos límite se pueden ignorar.
PurkkaKoodari
Okay. Y Y [0,0],[1,0],[2,0],[1,2]Un "cuadrilátero" con un ángulo de 180 grados. ¿Sin triángulos o 1 triángulo?
Level River St
Eso no sería un triángulo, pero puede suponer que tampoco viene.
PurkkaKoodari

Respuestas:

1

PostGIS, 162

Creo que esto cumple con las reglas, es una consulta para PostGIS, que es una extensión de PostgreSQL. Se supone que la entrada es una tabla de coordenadas para cada línea llamada L La salida es un conjunto de filas con la definición de polígono para los triángulos formados.

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 uso se parece a lo siguiente

-- 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 salida es la siguiente

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

Mathematica 915 395 401 405

Actualizar

Este desafío de programación es mucho más difícil de lo que parece ser.

El enfoque actual funciona con casos simples, donde hay una sola intersección a lo largo de cualquier segmento de línea. Con múltiples cruces a lo largo de un segmento, es necesario realizar un seguimiento de todos los puntos de intersección a lo largo de cada línea y crear nuevos subsegmentos (por lo tanto, bordes de gráficos adicionales) que conectan la nueva intersección a todos los puntos de intersección a lo largo de la línea de destino.

A pesar de esa limitación, puede valer la pena compartir la lógica subyacente al enfoque actual.


Los segmentos de línea de entrada se tratan como regiones. Si se cruzan, el centroide será las coordenadas de la intersección. Necesitamos eliminar esas intersecciones que ocurren en los vértices de los segmentos de línea. Las líneas que no se cruzan tendrán un centroide indeterminado.

Se crean cuatro aristas nuevas para cada punto de intersección. Conectan el punto de intersección con los cuatro vértices de las dos líneas de intersección.

Se genera un gráfico como el de abajo a la derecha utilizando los bordes antiguos y nuevos.

Los vértices son las coordenadas de los puntos respectivos. Los ciclos, es decir, los bucles cerrados de tres vértices serán triángulos siempre que los tres vértices no sean colineales.

Actualmente verificamos si algún "triángulo" tiene un área indeterminada. (Por alguna razón, no devuelve un área de 0 para tres puntos colineales).


Un simple ejemplo

A continuación se presentan (a) la figura trazada en el plano de coordenadas y (b) el gráfico que muestra los nodos dados, así como el nodo de intersección, {114/23, 314/69}. En este último, los vértices no se encuentran en las coordenadas cartesianas respectivas.

Puede parecer que hay más aristas en la figura derecha que en la izquierda. Pero recuerde que hay bordes de gráfico superpuestos a la izquierda. ¡Cada diagonal corresponde en realidad a 3 bordes del gráfico!


gráficos

    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)

Cada fila de abajo es un triángulo.

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 ejemplo mas complejo

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

Aquí está el gráfico correspondiente a las coordenadas de entrada . Los vértices están en sus coordenadas cartesianas esperadas. (Si ejecuta el código de golf, mostrará los vértices en otro lugar mientras respeta las etiquetas y los bordes de los vértices. Para facilitar la lectura, asigné las coordenadas de los vértices usando un puñado de código adicional, que no es necesario para la solución).

Graph2


Aquí está el gráfico derivado.
Incluye el punto de intersección derivado (0,1/11), donde se cruzan algunas de las líneas de entrada.

diecinueve

El código encontró 19 triángulos. Nueve de ellos tienen el punto, (0,1/11)como uno de los vértices.

nineteen2

DavidC
fuente
Okay. Ahora tiene la forma de una función.
DavidC
4

Java, 1051 1004

(Programa totalmente funcional)

Pensé que este es un buen desafío, no solo para jugar golf, sino principalmente para practicar escribir funciones matemáticas.

Y para dibujar una "línea de base" hice esta en Java * Espera a que todos comiencen a reír * .

Código

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

Entrada

Enteros separados por espacios. En pares 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

Salida (la salida real no se redondea a 3 decimales)

Cada línea contiene un triángulo Cada línea consta de puntos flotantes separados por espacios en pares de 2 (x1, y1, x2, y2, x3, y3). (Nota: el orden de los 3 puntos que forman el triángulo no está definido).

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

Explicación

Comencé a escribir un método para encontrar la intersección entre dos líneas no infinitas. El método resultante es para un estilo Java bastante corto (246). En lugar de permitir que el método de entrada consista en 8 dobles o dos Puntos (P), elijo usar un parámetro arbitrario para proteger cantidades masivas de caracteres. Para minimizar el uso del operador de matriz, cada parámetro utilizado más de 2 veces se coloca en su propia 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;}

Se agregará más explicación ... (esta respuesta probablemente se pueda jugar aún más)

Rolf ツ
fuente
0

BBC BASIC

Emulador en http://www.bbcbasic.co.uk/bbcwin/bbcwin.html

Espero que esto llegue a los 400's.

De entrada y salida

Cada vez que el usuario ingresa una nueva línea, el programa verifica si se han creado nuevos triángulos y los emite de inmediato, ver más abajo.

Se crea un nuevo triángulo donde la nueva línea se cruza con dos líneas preexistentes que también se cruzan entre sí (excepto cuando las tres líneas se cruzan en un punto, que es un caso especial que debe tratarse).

ingrese la descripción de la imagen aquí

Código

El programa principal es tan simple como puede ser. Al final está la función, que realiza la compleja tarea de detectar intersecciones, de acuerdo con la fórmula en http://en.wikipedia.org/wiki/Line%E2%80%93line_intersection

La función devuelve cero si no hay intersección y un número de coma flotante distinto de cero si la hay. También tiene un efecto secundario: las coordenadas de la intersección se agregan a la cadena z $. Además, en BBC basic, las variables de una función son visibles para el programa principal siempre que el programa principal no tenga una variable del mismo nombre (incluso después de que la función haya finalizado).

Por lo tanto, el programa principal tiene acceso a las variables xy y, y my n, que almacenan las coordenadas de las intersecciones actuales y anteriores. Esto se usa para detectar si realmente hemos encontrado un triángulo y no solo tres líneas que se cruzan en 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.
Level River St
fuente