Encontrar un área exclusiva en intersecciones circulares

17

¡Aquí hay un rompecabezas de geometría engañosamente desafiante para ti!

Dado un círculo Ay notros círculos B[n], encuentre el área total contenida dentro de la Acual no está dentro de ningún círculo B.

Su código debe ser lo más corto posible.

Entrada

Su entrada debe contener la siguiente información:

  • Un número de coma flotante para representar el radio del círculo A.
  • Una lista de números de coma flotante para representar los radios de los círculos B.
  • Una lista de los centros de círculos en B. Su programa puede esperar los centros en coordenadas polares o cartesianas.
  • Opcionalmente, puede recibir el número nde círculos en B. Esta entrada no es necesaria.

Se supondrá que el centro del círculo Aes el origen, es decir, el punto (0, 0).

Se garantiza que no hay dos círculos en Bson idénticos, pero se no garantiza que: todos los círculos de Bintersecan A, todos los centros de Bestán fuera A, o no hay dos círculos en Bintersecan entre sí. Asegúrese de que su solución pueda manejar varios casos extremos.

Puede recibir entradas en cualquier orden y en forma de entrada de texto (a través de stdin o el equivalente de su idioma), parámetros de función o argumentos de línea de comandos.

Si elige recibir entrada de texto, debe haber delimitadores ASCII imprimibles de uno o dos caracteres entre las entradas.

Salida

Su programa o función debe generar un único número de punto flotante que represente el área total de Ano dentro de ninguno de los círculos de B. Sus respuestas deben tener una precisión de al menos tres cifras significativas para todos los casos de prueba.

Se aplican reglas generales de .

Su solución no debe basarse en puntos de muestreo dentro de los círculos para determinar un área.

Los elementos incorporados que localizan automáticamente las intersecciones de los círculos, encuentran áreas dentro de las intersecciones de los círculos o resuelven este problema de inmediato no están permitidos.

Casos de prueba

En cada imagen, el círculo Ase delinea en azul, con círculos Ben verde y negro lleno. El área que debe devolverse se llena de rojo.

(Un agradecimiento especial a Rainer P. por verificar mis soluciones)

Caso de prueba 1:

A = {x: 0, y: 0, rad: 50}
B[0] = {x: 0, y: 0, rad: 100}

Caso de prueba 1

Result: 0.00

Caso de prueba 2:

A = {x: 0, y: 0, rad: 100.000000}
B[0] = {x: 100.000000, y: 0.000000, rad: 50.000000}
B[1] = {x: 30.901699, y: -95.105652, rad: 50.000000}
B[2] = {x: -80.901699, y: -58.778525, rad: 50.000000}
B[3] = {x: -80.901699, y: 58.778525, rad: 50.000000}
B[4] = {x: 30.901699, y: 95.105652, rad: 50.000000}

Caso de prueba 2

Result: 1.3878e+04

Caso de prueba 3:

A = {x: 0, y: 0, rad: 138}
B[0] = {x: 100, y: 0, rad: 100}
B[1] = {x: -50, y: -86, rad: 100} 
B[2] = {x: -93, y: 135, rad: 50}

Caso de prueba 3

Result: 1.8969e+04

Caso de prueba 4:

A = {x: 0, y: 0, rad: 121.593585}
B[0] = {x: 81.000000, y: 107.000000, rad: 59.841457}
B[1] = {x: -152.000000, y: -147.000000, rad: 50.000000}
B[2] = {x: 43.000000, y: -127.000000, rad: 105.118980}
B[3] = {x: 0.000000, y: -72.000000, rad: 57.870545}
B[4] = {x: -97.000000, y: -81.000000, rad: 98.488578}
B[5] = {x: -72.000000, y: 116.000000, rad: 66.468037}
B[6] = {x: 2.000000, y: 51.000000, rad: 50.000000}

Caso de prueba 4

Result: 1.1264e+04

Caso de prueba 5:

A = {x: 0, y: 0, rad: 121.605921}
B[0] = {x: 0.000000, y: -293.000000, rad: 250.000000}
B[1] = {x: 0.000000, y: -56.000000, rad: 78.230429}
B[2] = {x: 0.000000, y: -102.000000, rad: 100.000000}

Caso de prueba 5

Result: 2.6742e+04

Lectura sugerida:

Fewell, MP "Área de superposición común de tres círculos". Octubre de 2006. Web. http://dspace.dsto.defence.gov.au/dspace/bitstream/1947/4551/4/DSTO-TN-0722.PR.pdf .

BrainSteel
fuente
Intenté resolver esto yo mismo hace dos años mientras trabajaba en esto , en función de lo simple que es el problema para dos círculos. Terminé leyendo el periódico que vinculaste ... y decidí ir con Monte Carlo a la zona. "Su solución no debe basarse en puntos de muestreo dentro de los círculos para determinar un área". D:
Martin Ender
No parece tener un caso de prueba en el que un círculo Bcontenga otro. Podría valer la pena agregar eso.
Martin Ender
¿Podrías revisar tu tercer caso de prueba? Me estoy poniendo 1.8970e+04.
LegionMammal978
@ MartinBüttner Yo también me encontré con el problema por accidente. No estoy demasiado satisfecho con mi solución, pero parece funcionar. Trataré de hacer una pequeña prueba para ese caso, ¡gracias!
BrainSteel
@ LegionMammal978 Sí, parece que el caso está mal. Tengo los datos siguientes para las intersecciones entre los círculos: B[0] - A intersection: 20653.659515, B[1] - A intersection: 20757.824115, B[1] - B[0] intersection: 1841.847766, B[2] - A intersection: 1289.164541, lo que da 18969.69009como la respuesta.
BrainSteel

Respuestas:

14

Python 2, 631 bytes

from cmath import*
C=input()
O,R=C[0]
def I(p,r,q,s):
 try:q-=p;d=abs(q*q);x=(r*r-s*s+d)/d/2;return[p+q*(x+z*(r*r/d-x*x)**.5)for z in-1j,1j]
 except:return[]
S=sorted
V=S(i.real for p,r in C for c in C for i in[p-r,p+r]+I(p,r,*c)if-R<=(i-O).real<=R)
A=pi*R*R
for l,r in zip(V,V[1:]):
 H=[]
 for p,t in C:
    try:
     for s in-1,1:a,b=[p.imag+s*(t*t-(p.real-x)**2)**.5for x in l,r];H+=[a+b,a,b,s,t,p],
    except:0
 a,b=H[:2];H=S(H[2:]);n=0;c=a
 for d in H:
    s=d[3];z=.5;H*=d<b
    for q,w,e,_,t,y in(c,min(d,b))*(n-s<(a<d)or[0]*n>H):\
g=phase((l+w*1j-y)/(r+e*1j-y));A-=abs(g-sin(g)).real*t*t/2-z*q*(r-l);z=-z
    n-=s
    if(a<d)*s*n==-1:c=d
print A

Los saltos de línea precedidos \son de fácil lectura y no se cuentan en la puntuación.

Lee la entrada a través de STDIN como una lista de (center, radius)pares, donde centerhay un número complejo en el formulario X+Yj. El primer círculo de la lista es un (cuyo centro no tiene que estar en el origen), y el resto de la lista es B . Imprime el resultado en STDOUT.

Ejemplo

Input:  (0+0j, 138),  (100+0j, 100), (-50+-86j, 100), (-93+135j, 50)
Output: 18969.6900901

Explicación

Esta es una variación (más larga y mucho más fea: P) en mi solución al área de Martin Büttner de un desafío de polígono autoinsectable. Utiliza el mismo truco de dividir el problema en partes lo suficientemente pequeñas, para lo cual se vuelve más manejable.

Encontramos los puntos de intersección entre todos los pares de círculos (considerando A y los círculos en B ), y pasamos una línea vertical a través de cada uno de ellos. Además, pasamos todas las líneas verticales tangentes a cualquiera de los círculos. Tiramos todas las líneas que no lo hacen se cruzan Una , por lo que todas las líneas restantes están entre las tangentes izquierdo y derecho de una .

Figura 1

Estamos buscando el área de la intersección de A y la unión de los círculos en B , el área de color rojo oscuro en la ilustración de arriba. Esta es el área que tenemos que restar de A para obtener el resultado.

Entre cada par de líneas verticales consecutivas, esta área se puede dividir en un conjunto de trapecios verticales (o triángulos, o segmentos de línea, como casos especiales de trapecios), con un segmento de arco "en exceso" al lado de cada pata. El hecho de que pasemos tantas líneas verticales como lo hacemos garantiza que el área delimitada no es realmente más complicada que eso, ya que de lo contrario tendría que haber un punto de intersección adicional y, por lo tanto, una línea vertical adicional, entre las dos líneas en pregunta.

Figura 2

Para encontrar estos trapecios y segmentos de arco, para cada par de líneas verticales consecutivas, encontramos los segmentos de arco de cada uno de los círculos que se encuentran correctamente entre estas dos líneas (por supuesto, algunos círculos pueden no tener segmento de arco entre un par de líneas dado .) En la siguiente ilustración, estos son los seis segmentos de arco amarillo (brillante y oscuro), cuando se consideran las dos líneas verticales rojas. Tenga en cuenta que, dado que pasamos todas las líneas verticales tangentes a los círculos, si un círculo tiene un segmento de arco entre las dos líneas, necesariamente intersecta ambas líneas, lo que simplifica el resto del algoritmo.

No todos estos arcos son relevantes; sólo estamos interesados en los que están en el límite de la intersección entre la A y la unión de B . Para encontrarlos, clasificamos los arcos de arriba a abajo (tenga en cuenta que los arcos pueden no cruzarse adecuadamente entre sí, ya que esto implicaría la existencia de otra línea vertical entre los dos que estamos considerando, por lo que tiene sentido hablar sobre un arco arbitrario sobre o debajo de cualquier otro.)

figura 3

Atravesamos los arcos, en orden, mientras contamos el número de círculos B que estamos dentro, n . Si n cambia de 0 a 1 mientras estamos dentro de A , o si estamos en el arco superior de A mientras n no es cero, entonces el arco actual corresponde a un tramo de un trapecio. Si n cambia de 1 a 0 mientras estamos dentro de A , o si estamos en el arco inferior de A mientras nes distinto de cero, entonces el arco actual corresponde a la otra pata del mismo trapecio. Una vez que encontramos un par tal de arcos (los dos arcos de color amarillo brillante en la ilustración anterior), calculamos el área del trapezoide correspondiente, y de los dos segmentos de arco, y añadirlo a la superficie total se restan de la A .

Calcular el área de A , así como las áreas de los trapecios, es bastante sencillo. El área de cada segmento de arco es el área del sector circular correspondiente, menos el área del triángulo cuyos vértices son los dos puntos finales del segmento de arco y el centro del círculo correspondiente.

Figura 4

Ana
fuente
1
Este es el tipo de solución que estaba considerando, excepto que habría encontrado el área objetivo directamente al encontrar los triarcs y trapearcs que estaban en A pero no en B (cuyas áreas se deben encontrar restando un segmento de arco de un triángulo o trapecio).
quintopia
@quintopia Esto podría incluso ser un poco más corto, ya que nos ahorra la necesidad de calcular el área de A , y todo lo que se necesita es probablemente jugar un poco con la condición en n .
Ell
@quintopia OTOH, tendrás que tener en cuenta la posibilidad de tener un arco positivo al lado de una de las patas, si es un segmento de arco de A , entonces quién sabe ...
Ell
Excelente solucion. Un problema casi idéntico a esto estaba atrapado en mi cabeza anoche, y realmente quería que alguien lo resolviera. Su solución es mejor que las ideas con las que estaba trabajando.
Logic Knight