Encontrar área exclusiva nas interseções de círculos

17

Aqui está um quebra-cabeça de geometria enganosamente desafiador para você!

Dado um círculo Ae noutros círculos B[n], encontre a área total contida dentro dela e Aque não esteja em nenhum círculo de B.

Seu código deve ser o mais curto possível.

Entrada

Sua entrada deve conter as seguintes informações:

  • Um número de ponto flutuante para representar o raio do círculo A.
  • Uma lista de números de ponto flutuante para representar os raios dos círculos B.
  • Uma lista dos centros de círculos em B. Seu programa pode esperar os centros em coordenadas polares ou cartesianas.
  • Opcionalmente, você pode receber o número nde círculos em B. Esta entrada não é necessária.

Deve-se assumir que o centro do círculo Aé a origem, ou seja, o ponto (0, 0).

É garantido que há dois círculos em Bsão idênticos, mas é não garantida que: todos os círculos de Bintersecção A, todos os centros de Bestão fora A, ou há dois círculos em Bcruzam entre si. Verifique se sua solução pode lidar com vários casos extremos.

Você pode receber entrada em qualquer ordem e na forma de entrada de texto (por meio de stdin ou equivalente do seu idioma), parâmetros de função ou argumentos da linha de comando.

Se você optar por receber entrada de texto, deve haver delimitadores ASCII imprimíveis de um ou dois caracteres entre as partes da entrada.

Resultado

Seu programa ou função deve gerar um único número de ponto flutuante representando a área total que Anão está dentro de nenhum dos círculos de B. Suas respostas devem ter precisão de pelo menos três números significativos para todos os casos de teste.

Aplicam-se as regras gerais de .

Sua solução não deve contar com pontos de amostragem dentro dos círculos para determinar uma área.

Os incorporados que localizam automaticamente interseções de círculos, localizam áreas dentro de interseções de círculos ou resolvem esse problema imediatamente não são permitidos.

Casos de teste

Em cada imagem, o círculo Aé destacado em azul, com círculos Bdestacados em verde e preto preenchido. A área que deve ser retornada é preenchida em vermelho.

(Agradecimentos especiais a Rainer P. por verificar minhas soluções)

Caso de teste 1:

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

Caso de teste 1

Result: 0.00

Caso de teste 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 teste 2

Result: 1.3878e+04

Caso de teste 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 teste 3

Result: 1.8969e+04

Caso de teste 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 teste 4

Result: 1.1264e+04

Caso de teste 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 teste 5

Result: 2.6742e+04

Leitura sugerida:

Ademais, MP "Área de sobreposição comum de três círculos". Outubro de 2006. Web. http://dspace.dsto.defence.gov.au/dspace/bitstream/1947/4551/4/DSTO-TN-0722.PR.pdf .

BrainSteel
fonte
Tentei resolver isso sozinho há dois anos enquanto trabalhava nisso , com base em quão simples é o problema para dois círculos. Acabei lendo o jornal que você vinculou ... e decidi ir com Monte Carlo na área. "Sua solução não deve depender de pontos de amostragem dentro dos círculos para determinar uma área." D:
Martin Ender
Você não parece ter um caso de teste em que um círculo Bcontenha outro. Pode valer a pena acrescentar isso.
Martin Ender
Você poderia verificar seu terceiro caso de teste? Estou recebendo 1.8970e+04.
precisa saber é o seguinte
@ MartinBüttner Eu também me deparei com o problema por acidente. Não estou muito satisfeito com minha solução, mas parece funcionar. Vou tentar fazer um pequeno teste para esse caso, obrigado!
precisa saber é o seguinte
@ LegionMammal978 Sim, parece que o caso está errado. Eu tenho os seguintes dados para intersecções entre 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, que produz 18969.69009como resposta.
precisa saber é o seguinte

Respostas:

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

Quebras de linha precedidas por \são de fácil leitura e não são contadas na pontuação.

Lê a entrada através de STDIN como uma lista de (center, radius)pares, onde centeré um número complexo no formulário X+Yj. O primeiro círculo na lista é A (cujo centro não tem que estar na origem), e do resto da lista é B . Imprime o resultado em STDOUT.

Exemplo

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

Explicação

Essa é uma variação (mais longa e muito mais feia: P) da minha solução para o desafio Área de um polígono de auto-interseção de Martin Büttner . Ele usa o mesmo truque de dividir o problema em pedaços pequenos o suficiente, para os quais se torna mais gerenciável.

Encontramos os pontos de interseção entre todos os pares de círculos (considerando A e os círculos em B ) e passamos uma linha vertical por cada um deles. Além disso, passamos todas as linhas verticais tangentes a qualquer um dos círculos. Nós jogar fora todas as linhas que não se cruzam A , de modo que todas as linhas restantes são entre as tangentes esquerdo e direito de uma .

figura 1

Estamos procurando a área da interseção de A e a união dos círculos em B - a área vermelha escura na ilustração acima. Esta é a área que temos que subtrair de A para obter o resultado.

Entre cada par de linhas verticais consecutivas, essa área pode ser dividida em um conjunto de trapézios verticais (ou triângulos ou segmentos de linha, como casos especiais de trapézios), com um segmento de arco "excessivo" próximo a cada perna. O fato de passarmos tantas linhas verticais quanto garantimos que a área delimitada não é realmente mais complicada do que isso, pois, caso contrário, teria que haver um ponto adicional de interseção e, portanto, uma linha vertical adicional, entre as duas linhas em questão.

Figura 2

Para encontrar esses trapézios e segmentos de arco, para cada par de linhas verticais consecutivas, encontramos os segmentos de arco de cada um dos círculos que se encontram adequadamente entre essas duas linhas (é claro, alguns círculos podem não ter segmento de arco entre um determinado par de linhas .) Na ilustração abaixo, esses são os seis segmentos de arco amarelo (claro e escuro), ao considerar as duas linhas verticais vermelhas. Observe que, como passamos todas as linhas verticais tangentes aos círculos, se um círculo possui um segmento de arco entre as duas linhas, ele cruza necessariamente as duas linhas, o que simplifica o restante do algoritmo.

Nem todos esses arcos são relevantes; estamos interessados ​​apenas nos que estão no limite da interseção entre A e a união de B . Para encontrá-los, classificamos os arcos de cima para baixo (observe que os arcos podem não se cruzar adequadamente, pois isso implicaria a existência de outra linha vertical entre os dois que estamos considerando e, portanto, faz sentido conversar sobre um arco arbitrário estar acima ou abaixo de qualquer outro.)

Figura 3

Atravessamos os arcos, em ordem, mantendo a contagem do número de círculos B em que estamos atualmente, n . Se n mudar de 0 para 1 enquanto estivermos dentro de A , ou se estivermos no arco superior de A enquanto n for diferente de zero, o arco atual corresponderá a uma perna de um trapézio. Se n mudar de 1 para 0 enquanto estivermos no interior for diferente de zero, o arco atual corresponderá à outra perna do mesmo trapézio. Quando encontramos um par de arcos (os dois arcos amarelos brilhantes na ilustração acima), calculamos a área do trapézio correspondente e dos dois segmentos de arco e o adicionamos à área total a ser subtraída de A , ou se estivermos no arco inferior de A enquanto n um .

O cálculo da área de A , bem como das áreas dos trapézios, é bastante simples. A área de cada segmento do arco é a área do setor circular correspondente, menos a área do triângulo cujos vértices são os dois pontos finais do segmento do arco e o centro do círculo correspondente.

Figura 4

Ell
fonte
11
Esse é o tipo de solução que eu estava considerando, exceto que eu teria encontrado a área alvo diretamente encontrando os triarcs e trapearcs que estavam em A, mas não em B (cujas áreas devem ser encontradas subtraindo um segmento de arco de um triângulo ou trapézio).
quintopia 11/01
@quintopia Isso pode até ser um pouquinho mais curto, já que nos poupa a necessidade de calcular a área de A , e tudo o que precisamos é provavelmente brincar um pouco com a condição em n .
1111 Ell
@quintopia OTOH, você vai ter que conta para a possibilidade de ter um arco de positivo ao lado de uma das pernas, se é um segmento de arco de A , então quem sabe ...
Ell
Excelente solução. Um problema quase idêntico estava preso na minha cabeça ontem à noite, e eu realmente queria que alguém o resolvesse. Sua solução é melhor do que as idéias com as quais eu estava trabalhando.
Logic Knight