Número de agujeros en un polígono.


11

El problema : cuente el número de agujeros en un polígono conectado. La conectividad del polígono está garantizada por la condición de que cada triángulo en la triangulación de entrada comparte al menos 1 lado con otro triángulo y que solo hay uno de esos conjuntos de triángulos conectados.

La entrada es una lista Lde npuntos en el plano y una lista Tde 3 tuplas con entradas de 0...n-1. Para cada elemento en Tla tupla (t_1,t_2,t_3)representa los tres vértices (de la lista L) de un triángulo en la triangulación. Tenga en cuenta que esta es una triangulación en el sentido de 'triangulación de polígono' , debido a esto nunca habrá dos triángulos en Tesa superposición. Una estipulación adicional es que no tendrá que desinfectar la entrada Ly Tno contiene repeticiones.

Ejemplo 1 : si L = {{0,0},{1,0},{0,1},{1,2}}y T = {{0,1,2},{1,2,3}}luego el polígono especificado tiene un recuento de agujeros de 0.

Figura 1

Ejemplo 2 : si L = {{0,0},{1,0},{2,0},{2,1},{2,2},{1,2},{0,2},{0,1},{.5,.5},{1.5,.5},{1.5,1.5},{.5,1.5}}y T = {{5,6,11},{5,10,11},{4,5,10},{3,8,10},{2,3,9},{2,8,9},{1,2,8},{0,1,8},{0,8,11},{0,7,11},{6,7,11},{3,4,10}}luego la entrada del polígono debería dar como resultado una salida de 2.

Figura 2

La tarea es escribir el programa más corto (o función) que toma Ly Tcomo entrada y devuelve el número de agujeros. El 'ganador' será reconocido como la entrada con el menor recuento de caracteres (fecha de finalización tentativa 1 de junio).

Ejemplo de formato de entrada (tenga en cuenta la indexación 0):

0,0
1,0
0,1
1,2
0,1,2
1,2,3    

1
"La conectividad del polígono está garantizada por la condición de que cada triángulo en la triangulación de entrada comparte al menos 1 lado con otro triángulo". -- No. Esa no es una condición suficiente. Tomemos, por ejemplo T=1,2,3/1,2,4/5,6,7/5,6,8,. Cada triángulo comparte una arista con otro triángulo, pero la triangulación está desconectada
John Dvorak

¿Podemos suponer que la entrada representa una triangulación parcial válida (no hay dos triángulos superpuestos y ningún triángulo está presente dos veces) y la triangulación está conectada?
John Dvorak


¿Podemos suponer también que la entrada está conectada al borde en el sentido de que no es posible eliminar un conjunto finito de puntos para que la forma se desconecte? (ej .: T=1,2,3/1,4,5está conectado pero no conectado al borde)
John Dvorak

2
No estoy seguro de por qué este negocio sobre las fechas de finalización ha comenzado a surgir recientemente. Se le permite cambiar la respuesta aceptada, por lo que no es necesario establecer una fecha de finalización. Es razonable tener una idea mental de que esperará una semana antes de seleccionar una respuesta para no asustar a la gente a pensar que la primera respuesta es inmejorable, pero mientras esté activo en el sitio puede cambiar la respuesta seleccionada si alguien publica uno mejor. Las discusiones relevantes incluyen meta.codegolf.stackexchange.com/q/542/194 y meta.codegolf.stackexchange.com/q/193/194
Peter Taylor

Respuestas:


5

GolfScript (23 caracteres)

~.{2*2/~}%{$}%.&,@@+,-)

Asume el formato de entrada utilizando la notación de matriz GolfScript y las coordenadas entre comillas (o integrales). P.ej

$ golfscript codegolf11738.gs <<<END
[["0" "0"] ["1" "0"] ["2" "0"] ["2" "1"] ["2" "2"] ["1" "2"] ["0" "2"] ["0" "1"] [".5" ".5"] ["1.5" ".5"] ["1.5" "1.5"] [".5" "1.5"]] [[5 6 11] [5 10 11] [4 5 10] [3 8 10] [2 3 9] [2 8 9] [1 2 8] [0 1 8] [0 8 11] [0 7 11] [6 7 11] [3 4 10]]
END
2

( Equivalente en línea )

o

$ golfscript codegolf11738.gs <<<END
[[0 0] [1 0] [0 1] [1 2]] [[0 1 2] [1 2 3]]
END
0

( Equivalente en línea )


5

Python, 71

Lo que sigue es un programa (no una función ) que calcula el número deseado.

len(set().union(*(map(frozenset,zip(t,t[1:]+t))for t in T)))-len(L+T)+1

Ejemplo de uso:

>>> L = ((0,0),(1,0),(2,0),(2,1),(2,2),(1,2),(0,2),(0,1),(.5,.5),(1.5,.5),(1.5,1.5),(.5,1.5))
>>> T = ((5,6,11),(5,10,11),(4,5,10),(3,8,10),(2,3,9),(2,8,9),(1,2,8),(0,1,8),(0,8,11),(0,7,11),(6,7,11),(3,4,10))
>>> len(set().union(*(map(frozenset,zip(t,t[1:]+t))for t in T)))-len(L+T)+1
2

+1 por usar el splat, usar frozenset en lugar de ordenar, zip (no puedo decir que lo haya usado antes, necesito familiarizarme).
Kaya

3

APL, 36

{1+(⍴⊃∪/{{⍵[⍋⍵]}¨,/3 2⍴⍵,⍵}¨⍵)-⍴⍺,⍵}

La función toma Lcomo argumento izquierdo y Tcomo derecho.

Por ejemplo:

      L←(0 0)(1 0)(0 1)(1 2)
      T←(0 1 2)(1 2 3)
      L{1+(⍴⊃∪/{{⍵[⍋⍵]}¨,/3 2⍴⍵,⍵}¨⍵)-⍴⍺,⍵}T
0
      L←(0 0)(1 0)(2 0)(2 1)(2 2)(1 2)(0 2)(0 1)(.5 .5)(1.5 .5)(1.5 1.5)(.5 1.5)
      T←(5 6 11)(5 10 11)(4 5 10)(3 8 10)(2 3 9)(2 8 9)(1 2 8)(0 1 8)(0 8 11)(0 7 11)(6 7 11)(3 4 10)
      L{1+(⍴⊃∪/{{⍵[⍋⍵]}¨,/3 2⍴⍵,⍵}¨⍵)-⍴⍺,⍵}T
2

Explicación, de derecha a izquierda:

  • ⍴⍺,⍵concatena los dos vectores de entrada y devuelve su longitud ( V + F)
  • Pasando al siguiente bloque:
    • ¨⍵ aplica la función de la izquierda a cada elemento del argumento derecho y devuelve el resultado
    • ⍵,⍵ devuelve el argumento correcto concatenado consigo mismo
    • 3 2⍴da forma al argumento del vector en tres pares. En este caso, combina los elementos primero y segundo, tercero y primero, y segundo y tercero del vector.
    • ,/ une el argumento del vector
    • ⍵[⍋⍵] ordena el argumento correcto
    • ∪/ filtra cualquier duplicado
    • ⍴⊃ convierte un escalar anidado en un vector y devuelve su longitud.
    • Toda la función devuelve el número de aristas en la forma ( E)
  • 1 se explica por sí mismo (espero ...)

Toda la función vuelve 1+E-(V+F), o 1-(F+V-E).


Más o menos exactamente lo que hace mi solución GolfScript. Me sorprende que sea mucho más largo que GolfScript.
Peter Taylor

@ PeterTaylor ¡Me sorprendió que su solución de GolfScript fuera mucho más corta! (Pero, de nuevo, es GolfScript)
Volatilidad

2

Mathematica, 93 (no mucho golf todavía)

f[l_, t_] :=  Max@MorphologicalComponents[Erosion[Graphics[
                                                        GraphicsComplex[l, Polygon[t + 1]]], 1]] - 1

(Espacios añadidos para mayor claridad)

Pruebas:

f[{{0, 0}, {1, 0}, {0, 1}, {1, 2}}, {{0, 1, 2}, {1, 2, 3}}]
(*
 -> 0
*)

{l, t} = {{{0, 0}, {1,   0}, {2,    0}, {2,     1}, {2,    2}, {1, 2}, {0, 2}, 
           {0, 1}, {.5, .5}, {1.5, .5}, {1.5, 1.5}, {.5, 1.5}}, 

           {{5, 6, 11}, {5, 10, 11}, {4, 5, 10}, {3, 8, 10}, {2, 3,  9}, 
            {2, 8,  9}, {1,  2,  8}, {0, 1,  8}, {0, 8, 11}, {0, 7, 11}, {6, 7, 11}, {3, 4, 10}}};
f[l, t]
 (*
  -> 2
 *)

¿No se basa esto en los triángulos o agujeros que tienen cierto tamaño mínimo (el argumento para Erosion)?
John Dvorak

@ JanDvorak Quizás esté equivocado, pero creo que a menos que use una aritmética de precisión infinita, cualquier solución funcionará hasta que alcance un cierto tamaño mínimo (tendrá que decidir si tres puntos están alineados o no). Es solo que en este tipo de solución, el problema se declara explícitamente.
Dr. belisarius

Si utiliza el enfoque topológico, no tiene que hacerlo. Si hay tres puntos colineales, entonces necesita un triángulo de área cero allí; de lo contrario, tiene un agujero.
John Dvorak

@belisarius. Aquí está la respuesta que recibí del Soporte Técnico de Wolfram sobre la discrepancia entre nuestros resultados: "Hola, gracias por su correo electrónico. He confirmado que su código da resultados diferentes en Mac y Windows. No creo que este sea el comportamiento previsto, así que He presentado un informe con nuestros desarrolladores sobre el tema. Me aseguraré de transmitir cualquier información útil que obtenga de nuestros desarrolladores sobre este tema. Avíseme si tiene más preguntas ... Soporte técnico Wolfram Research , Cía."
DavidC

@DavidCarraher "Sí, tengo más preguntas: ¿me enviarán un cheque por cada error?"
Dr. belisarius

2

Ruby, 239 caracteres (227 cuerpos)

def f t
e=t.flat_map{|x|x.permutation(2).to_a}.group_by{|x|x}.select{|_,x|x.one?}.keys
n=Hash[e]
(u,n=n,n.dup;e.map{|x|a,b=*x;n[a]=n[n[a]]=n[b]})until n==u
n.values.uniq.size+e.group_by(&:last).map{|_,x|x.size}.reduce(-1){|x,y|x+y/2-1}
end

Tenga en cuenta que solo estoy considerando la topología. No estoy usando las posiciones de vértice de ninguna manera.

llamador (espera T en formato Mathematica o JSON):

input = gets.chomp
input.gsub! "{", "["
input.gsub! "}", "]"
f eval(input)

Prueba:

f [[0,1,2],[1,2,3]]
#=> 0
f [[5, 6, 11], [5, 10, 11], [4, 5, 10], [3, 8, 10], [2, 3, 9], [2, 8, 9], [1, 2, 8], [0, 1, 8], [0, 8, 11], [0, 7, 11], [6, 7, 11], [3, 4, 10]]
#=> 2
f [[1,2,3],[3,4,5],[5,6,1],[2,3,4],[4,5,6],[6,1,2]]
#=> 1

Yay, un enfoque característico euler. Así lo hice en Python.
Kaya

2
@Kaya. (Ver Egg of Columbus en.wikipedia.org/wiki/Egg_of_Columbus ) Una vez que alguien ha dado una respuesta euleriana a su pregunta, la probabilidad de que otros la sigan aumenta enormemente. Les puedo asegurar que es mucho más desafiante y gratificante descubrir el enfoque por cuenta propia, solo después de hacer la conexión con el trabajo de Euler con los poliedros.
DavidC

2

Mathematica 76 73 72 67 62

Después de mucha experimentación, me di cuenta de que la ubicación precisa de los vértices no era una preocupación, por lo que representé el problema con los gráficos. Los invariantes esenciales, el número de triángulos, bordes y vértices permanecieron invariables (siempre que se evitara el cruce de línea).

Había dos tipos de "triángulos" internos en el gráfico: aquellos en los que presumiblemente había una cara, es decir, un triángulo "lleno", y aquellos donde no los había. El número de caras internas no tenía ninguna relación con los bordes o vértices. Eso significaba que hacer agujeros en gráficos completamente "rellenos" solo reducía el número de caras. Jugué sistemáticamente con variaciones entre triángulos, haciendo un seguimiento de las caras, vértices y bordes. Finalmente, me di cuenta de que el número de agujeros siempre era igual a 1 - # caras - # vértices + # bordes. Esto resultó ser 1 menos la característica de Euler (que solo conocía en el contexto de los poliedros regulares (aunque la longitud de los bordes claramente no tenía importancia).

La siguiente función devuelve el número de agujeros cuando se introducen los vértices y triángulos. A diferencia de mi presentación anterior, no se basa en un escaneo de una imagen. Puede pensarlo como 1 - característica de Euler, es decir, 1 - (F + V -E) donde F= #caras, V= # vértices, E= # aristas. La función devuelve el número de agujeros, 1 - (F + V -E)dadas las caras reales (triángulos) y vértices.

Se puede demostrar fácilmente que la eliminación de cualquier triángulo en el exterior del complejo no tiene ningún efecto en la característica de Euler, independientemente de si comparte uno o 2 lados con otros triángulos.

Nota: La minúscula vse utilizará en lugar de la Lde la formulación original; es decir, contiene los vértices mismos (no V, el número de vértices)

fse usa para la Tformulación original; es decir, contiene los triángulos, representados como el triple ordenado de los índices de vértice.

Código

z=Length;1-z@#2-z@#+z[Union@@(Sort/@{#|#2,#2|#3,#3|#}&@@@#2)]&

(Gracias a Mr. Wizard por eliminar 5 caracteres al eliminar la regla de reemplazo).


Ejemplo 1

v = {{0, 0}, {1, 0}, {0, 1}, {1, 2}}; f = {{0, 1, 2}, {1, 2, 3}};

z=Length;1-z@#2-z@#+z[Union@@(Sort/@{#|#2,#2|#3,#3|#}&@@@#2)]&[v, f]

0 0

Cero agujeros.


Ejemplo 2

v = {{0, 0}, {1, 0}, {2, 0}, {2, 1}, {2, 2}, {1, 2}, {0, 2}, {0, 1} , {.5, .5}, {1.5, .5}, {1.5, 1.5}, {.5, 1.5}}; f = {{5, 6, 11}, {5, 10, 11}, {4, 5, 10}, {3, 8, 10}, {2, 3, 9}, {2, 8, 9} , {1, 2, 8}, {0, 1, 8}, {0, 8, 11}, {0, 7, 11}, {6, 7, 11}, {3, 4, 10}};

z=Length;1-z@#2-z@#+z[Union@@(Sort/@{#|#2,#2|#3,#3|#}&@@@#2)]&[v, f]

2

Por lo tanto, 2 agujeros son en el ejemplo 2.


¿básicamente está rasterizando la triangulación y volcando una biblioteca de gráficos en esa imagen? ¿No falla eso si un agujero es demasiado pequeño?
John Dvorak

1
su segundo ejemplo devuelve 0 aquí (es por eso que no lo he usado MorphologicalEulerNumber[]). Mma 9.01, Win XP.
Dr. belisarius

Estoy usando 9.0.1 también, pero en una Mac. ¿Estás diciendo que Mathematica devuelve una respuesta diferente de la mía en Windows? Si es así, eso suena como un error (en la versión de Windows XP).
DavidC


@ Jan Dvorak. MorphologicalEulerNumbera veces requiere una imagen; se niega a aceptar un objeto gráfico. En estos casos, el tamaño del agujero y la resolución son críticos (ver codegolf.stackexchange.com/questions/8706/… ). Pero aquí funciona directamente con el objeto Graphics, que contiene explícitamente todos los vértices. Imaginé (o esperaba) que usaría un enfoque que no dependiera de la imagen. Ojalá supiera cómo intentó resolver el problema. Tal vez algunos detalles en el código fuente de la función aclaren las cosas.
DavidC

1

Python, 107

Me di cuenta de que tomar las parejas directamente era más corto from itertools import*y escribir combinations(). Sin embargo, también noté que mi solución se basaba en las caras triangulares de entrada que tenían sus vértices listados en orden consistente. Por lo tanto, las ganancias en el recuento de personajes no son tan grandes.

f=lambda l,t:1-len(l+t)+len(set([tuple(sorted(m))for n in[[i[:2],i[1:],[i[0],i[2]]]for i in t]for m in n]))

Python, 115

Enfoque característico de Euler, la verbosidad de itertools parece imposible de evitar. Me pregunto si sería más barato usar una técnica más directa para hacer pares de vértices.

from itertools import*
f=lambda l,t:1-len(l+t)+len(set([m for n in[list(combinations(i,2)) for i in t]for m in n]))

Ejemplo de uso:

> f([[0,0],[1,0],[0,1],[1,2]],[[0,1,2],[1,2,3]])
> 0
> f([[0,0],[1,0],[2,0],[2,1],[2,2],[1,2],[0,2],[0,1],[.5,.5],[1.5,.5],[1.5,1.5],[.5,1.5]],[[5,6,11],[5,10,11],[4,5,10],[3,8,10],[2,3,9],[2,8,9],[1,2,8],[0,1,8],[0,8,11],[0,7,11],[6,7,11],[3,4,10]])
> 2
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.