Recherche d'une zone exclusive dans les intersections de cercle

17

Voici un puzzle de géométrie trompeusement difficile pour vous!

Étant donné un cercle Aet d' nautres cercles B[n], trouvez l'aire totale contenue à l'intérieur Aqui ne se trouve dans aucun cercle de B.

Votre code doit être aussi court que possible.

Contribution

Votre entrée doit contenir les informations suivantes:

  • Un nombre à virgule flottante pour représenter le rayon du cercle A.
  • Une liste de nombres à virgule flottante pour représenter les rayons des cercles B.
  • Une liste des centres de cercles dans B. Votre programme peut s'attendre à ce que les centres soient en coordonnées polaires ou cartésiennes.
  • Facultativement, vous pouvez recevoir le nombre nde cercles en B. Cette entrée n'est pas requise.

Il faut supposer que le centre du cercle Aest l'origine, c'est-à-dire le point (0, 0).

Il est garanti qu'il n'y a pas deux cercles dans Bidentiques, mais il n'est pas garanti que: tous les cercles d' Bintersection A, tous les centres de Bsont à l'extérieur A, ou pas deux cercles en Bintersection. Assurez-vous que votre solution peut gérer divers cas de bord.

Vous pouvez recevoir des entrées dans n'importe quel ordre et sous forme de saisie de texte (via stdin ou l'équivalent de votre langue), des paramètres de fonction ou des arguments de ligne de commande.

Si vous choisissez de recevoir une entrée de texte, il doit y avoir des délimiteurs ASCII imprimables à un ou deux caractères entre les éléments d'entrée.

Production

Votre programme ou fonction doit générer un seul nombre à virgule flottante représentant la zone totale de Apas dans aucun des cercles de B. Vos réponses doivent être précises à au moins trois chiffres significatifs pour tous les cas de test.

Les règles générales du s'appliquent.

Votre solution ne doit pas s'appuyer sur des points d'échantillonnage dans les cercles pour déterminer une zone.

Les éléments intégrés qui localisent automatiquement les intersections de cercles, trouvent des zones dans les intersections de cercles ou résolvent immédiatement ce problème sont interdits.

Cas de test

Dans chaque image, le cercle Aest délimité en bleu, avec des cercles Bdélimités en vert et noir rempli. La zone à retourner est remplie en rouge.

(Un merci spécial à Rainer P. pour avoir vérifié mes solutions)

Cas de test 1:

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

Cas de test 1

Result: 0.00

Cas de test 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}

Cas de test 2

Result: 1.3878e+04

Cas de test 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}

Cas de test 3

Result: 1.8969e+04

Cas de test 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}

Cas de test 4

Result: 1.1264e+04

Cas de test 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}

Cas de test 5

Result: 2.6742e+04

Lecture suggérée:

Fewell, MP "Zone de chevauchement commun de trois cercles." Octobre 2006. Web. http://dspace.dsto.defence.gov.au/dspace/bitstream/1947/4551/4/DSTO-TN-0722.PR.pdf .

BrainSteel
la source
J'ai essayé de résoudre ce problème moi-même il y a deux ans en travaillant là-dessus , en fonction de la simplicité du problème pour deux cercles. J'ai fini par lire le papier que vous avez lié ... et j'ai décidé d'aller avec Monte Carlo dans la région. "Votre solution ne doit pas s'appuyer sur des points d'échantillonnage dans les cercles pour déterminer une zone." D:
Martin Ender
Vous ne semblez pas avoir un cas de test où un cercle en Bcontient un autre. Cela pourrait valoir la peine d'ajouter cela.
Martin Ender
Pourriez-vous vérifier votre troisième cas de test? Je reçois 1.8970e+04.
LegionMammal978
@ MartinBüttner J'ai moi aussi rencontré le problème lors d'un accident. Je ne suis pas trop satisfait de ma solution, mais elle semble fonctionner. Je vais essayer de faire un petit test pour ce cas, merci!
BrainSteel
@ LegionMammal978 Oui, il semble que l'affaire soit fausse. J'ai les données suivantes pour les intersections entre les cercles: 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, ce qui donne 18969.69009comme réponse.
BrainSteel

Réponses:

14

Python 2, 631 octets

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

Les sauts de ligne précédés de \sont pour une lecture facile et ne sont pas comptés dans la partition.

Lit l'entrée via STDIN sous la forme d'une liste de (center, radius)paires, où centerest un nombre complexe dans le formulaire X+Yj. Le premier cercle de la liste est un (dont le centre ne doit pas être à l'origine), et le reste de la liste est B . Imprime le résultat dans STDOUT.

Exemple

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

Explication

Ceci est une variation (plus longue et beaucoup plus laide: P) de ma solution au défi de Martin Büttner, Zone d'un polygone auto-intersecté . Il utilise la même astuce pour décomposer le problème en morceaux assez petits, pour lesquels il devient plus gérable.

Nous trouvons les points d'intersection entre toutes les paires de cercles (en considérant à la fois A et les cercles en B ) et passons une ligne verticale à travers chacun d'eux. De plus, nous passons toutes les lignes verticales tangentes à l'un des cercles. Nous deux pas toutes les lignes qui ne sont pas Intersection A , de sorte que toutes les lignes restantes sont entre les tangentes gauche et à droite de A .

Figure 1

Nous recherchons l'aire de l'intersection de A et l'union des cercles en B - la zone rouge foncé dans l'illustration ci-dessus. C'est la zone que nous devons soustraire de A pour obtenir le résultat.

Entre chaque paire de lignes verticales consécutives, cette zone peut être décomposée en un ensemble de trapèzes verticaux (ou triangles ou segments de ligne, comme cas particuliers de trapèzes), avec un segment d'arc "en excès" à côté de chaque jambe. Le fait que nous passions autant de lignes verticales que nous garantissons que la zone délimitée n'est en effet pas plus compliquée que cela, car sinon il devrait y avoir un point d'intersection supplémentaire, et donc une ligne verticale supplémentaire, entre les deux lignes de question.

Figure 2

Pour trouver ces trapèzes et segments d'arc, pour chaque paire de lignes verticales consécutives, nous trouvons les segments d'arc de chacun des cercles qui se trouvent correctement entre ces deux lignes (bien sûr, certains cercles peuvent ne pas avoir de segment d'arc entre une paire de lignes donnée .) Dans l'illustration ci-dessous, ce sont les six segments d'arc jaune (lumineux et foncé), si l'on considère les deux lignes verticales rouges. Notez que, puisque nous passons toutes les lignes verticales tangentes aux cercles, si un cercle a un segment d'arc entre les deux lignes, il coupe nécessairement les deux lignes, ce qui simplifie le reste de l'algorithme.

Tous ces arcs ne sont pas pertinents; nous ne nous intéressons à ceux qui sont à la limite de l'intersection entre A et l'union de B . Pour les trouver, nous trions les arcs de haut en bas (notez que les arcs peuvent ne pas se croiser correctement, car cela impliquerait l'existence d'une autre ligne verticale entre les deux que nous considérons, et il est donc logique de parler à propos d'un arc arbitraire au-dessus ou en dessous de tout autre.)

figure 3

Nous parcourons les arcs, dans l'ordre, tout en comptant le nombre de cercles B dans lesquels nous sommes actuellement, n . Si n passe de 0 à 1 alors que nous sommes à l'intérieur de A , ou si nous sommes à l'arc supérieur de A alors que n est non nul, alors l'arc actuel correspond à une jambe d'un trapèze. Si n passe de 1 à 0 alors que nous sommes à l'intérieur de A , ou si nous sommes à l'arc inférieur de A alors que nest différent de zéro, alors l'arc actuel correspond à l'autre jambe du même trapèze. Une fois que nous trouvons une telle paire d'arcs (les deux arcs jaune vif dans l'illustration ci - dessus), on calcule la zone du trapèze correspondant, et des deux segments d'arc, et l' ajouter à la surface totale à soustraire A .

Le calcul de l'aire de A , ainsi que des aires des trapèzes, est assez simple. L'aire de chaque segment d'arc est l'aire du secteur circulaire correspondant, moins l'aire du triangle dont les sommets sont les deux extrémités du segment d'arc et le centre du cercle correspondant.

Figure 4

Aune
la source
1
C'est le genre de solution que j'envisageais, sauf que j'aurais trouvé la zone cible directement en trouvant les triarcs et les trapèdes qui étaient en A mais pas B (dont les zones doivent être trouvées en soustrayant un segment d'arc d'un triangle ou d'un trapèze).
quintopie
@quintopia Cela pourrait même être un peu plus court, car cela nous évite d'avoir à calculer la zone de A , et tout ce qu'il faut, c'est probablement jouer un peu avec la condition sur n .
Ell
@quintopia OTOH, vous devrez tenir compte de la possibilité d'avoir un arc positif à côté de l'une des jambes, s'il s'agit d'un segment d'arc de A , alors qui sait ...
Ell
Excellente solution. Un problème presque identique à celui-ci était coincé dans ma tête la nuit dernière, et je voulais vraiment que quelqu'un le résolve. Votre solution est meilleure que les idées avec lesquelles je travaillais.
Logic Knight