Finden eines exklusiven Bereichs in Kreiskreuzungen

17

Hier ist ein täuschend herausforderndes Geometrie-Puzzle für Sie!

Finden Sie bei einem Kreis Aund nanderen Kreisen B[n]die Gesamtfläche, die in Adiesem Kreis enthalten ist, und nicht in einem Kreis von B.

Ihr Code sollte so kurz wie möglich sein.

Eingang

Ihre Eingabe sollte die folgenden Informationen enthalten:

  • Eine Gleitkommazahl zur Darstellung des Kreisradius A.
  • Eine Liste von Gleitkommazahlen zur Darstellung der Radien von Kreisen in B.
  • Eine Liste der Zentren der Kreise in B. Ihr Programm kann die Zentren entweder in Polarkoordinaten oder in kartesischen Koordinaten erwarten.
  • Optional können Sie die Anzahl nder Kreise in B erhalten. Diese Eingabe ist nicht erforderlich.

Es wird angenommen, dass der Mittelpunkt des Kreises Ader Ursprung, das heißt der Punkt ist (0, 0).

Es wird garantiert, dass keine zwei Kreise in Bidentisch sind, aber es wird nicht garantiert, dass: alle BSchnittkreise A, alle Zentren von Baußerhalb Asind oder keine zwei Kreise in Beinander schneiden. Stellen Sie sicher, dass Ihre Lösung verschiedene Edge Cases verarbeiten kann.

Sie können Eingaben in beliebiger Reihenfolge und in Form von Texteingaben (über stdin oder die entsprechende Sprache), Funktionsparametern oder Befehlszeilenargumenten erhalten.

Wenn Sie Texteingaben erhalten möchten, sollten zwischen den Eingaben ein oder zwei druckbare ASCII-Trennzeichen stehen.

Ausgabe

Ihr Programm oder Ihre Funktion sollte eine einzelne Gleitkommazahl ausgeben, die die Gesamtfläche von Ain keinem der Kreise von repräsentiert B. Ihre Antworten sollten für alle Testfälle auf mindestens drei signifikante Zahlen genau sein.

Es gelten die allgemeinen Regeln.

Ihre Lösung sollte sich nicht auf Stichprobenpunkte innerhalb der Kreise stützen, um eine Fläche zu bestimmen.

Integrierte Funktionen, mit denen Schnittpunkte von Kreisen automatisch lokalisiert, Bereiche innerhalb von Schnittpunkten von Kreisen gefunden oder dieses Problem sofort behoben werden können, sind nicht zulässig.

Testfälle

In jedem Bild ist der Kreis Ablau umrandet, die Kreise Bgrün umrandet und schwarz ausgefüllt. Der Bereich, der zurückgegeben werden soll, ist rot ausgefüllt.

(Besonderer Dank geht an Rainer P. für die Prüfung meiner Lösungen)

Testfall 1:

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

Testfall 1

Result: 0.00

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

Testfall 2

Result: 1.3878e+04

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

Testfall 3

Result: 1.8969e+04

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

Testfall 4

Result: 1.1264e+04

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

Testfall 5

Result: 2.6742e+04

Vorgeschlagene Literatur:

Fewell, MP "Bereich der gemeinsamen Überlappung von drei Kreisen." Okt. 2006. Web. http://dspace.dsto.defence.gov.au/dspace/bitstream/1947/4551/4/DSTO-TN-0722.PR.pdf .

BrainSteel
quelle
Ich habe vor zwei Jahren versucht, dies selbst zu lösen, als ich daran arbeitete , basierend darauf, wie einfach das Problem für zwei Kreise ist. Am Ende las ich die Zeitung, die Sie verlinkt haben ... und entschloss mich, mit Monte Carlo durch die Gegend zu ziehen. "Ihre Lösung sollte sich nicht auf Stichprobenpunkte innerhalb der Kreise stützen, um eine Fläche zu bestimmen." D:
Martin Ender
Es scheint keinen Testfall zu geben, in dem ein Kreis Beinen anderen enthält. Könnte es wert sein, das hinzuzufügen.
Martin Ender
Könnten Sie Ihren dritten Testfall überprüfen? Ich bekomme 1.8970e+04.
LegionMammal978
@ MartinBüttner Auch ich bin durch einen Unfall auf das Problem gestoßen. Ich bin nicht sonderlich zufrieden mit meiner Lösung, aber sie scheint zu funktionieren. Ich werde versuchen, einen kleinen Test für diesen Fall zu erstellen, danke!
BrainSteel
@ LegionMammal978 Ja, dieser Fall scheint falsch zu sein. Ich habe folgende Daten für Kreuzungen zwischen den Kreisen: 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, das ergibt 18969.69009als die Antwort.
BrainSteel

Antworten:

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

Die vorangestellten Zeilenumbrüche dienen dem \einfachen Lesen und werden in der Partitur nicht mitgezählt.

Liest die Eingabe über STDIN als Liste von (center, radius)Paaren, wobei centerdas Formular eine komplexe Zahl enthält X+Yj. Der erste Kreis in der Liste A (dessen Zentrums muss nicht am Ursprung sein), und der Rest der Liste ist B . Gibt das Ergebnis an STDOUT aus.

Beispiel

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

Erläuterung

Dies ist eine (längere und viel hässlichere: P) Variante meiner Lösung für Martin Büttners Area of ​​a Self-Intersecting Polygon Challenge. Es wird der gleiche Trick angewendet, bei dem das Problem in ausreichend kleine Teile zerlegt wird, für die es leichter zu handhaben ist.

Wir finden die Schnittpunkte zwischen allen Kreispaaren (unter Berücksichtigung von A und den Kreisen in B ) und ziehen jeweils eine vertikale Linie durch diese. Zusätzlich übergeben wir alle vertikalen Linien, die einen der Kreise berühren. Wir werfen alle Linien weg, die A nicht schneiden , so dass alle verbleibenden Linien zwischen der linken und rechten Tangente von A liegen .

Abbildung 1

Wir suchen nach dem Schnittbereich von A und der Vereinigung der Kreise in B - dem dunkelroten Bereich in der Abbildung oben. Dies ist der Bereich, den wir von A subtrahieren müssen , um das Ergebnis zu erhalten.

Zwischen jedem Paar aufeinanderfolgender vertikaler Linien kann dieser Bereich in eine Reihe vertikaler Trapezoide (oder Dreiecke oder Liniensegmente, als Sonderfälle von Trapezoiden) mit einem "überschüssigen" Bogensegment neben jedem Schenkel unterteilt werden. Die Tatsache, dass wir so viele vertikale Linien wie wir passieren, garantiert, dass der begrenzte Bereich tatsächlich nicht komplizierter ist, da es sonst einen zusätzlichen Schnittpunkt und damit eine zusätzliche vertikale Linie zwischen den beiden Linien in geben müsste Frage.

Figur 2

Um diese Trapezoide und Bogensegmente für jedes Paar aufeinanderfolgender vertikaler Linien zu finden, finden wir die Bogensegmente jedes der Kreise, die ordnungsgemäß zwischen diesen beiden Linien liegen (natürlich haben einige Kreise möglicherweise kein Bogensegment zwischen einem bestimmten Linienpaar .) In der folgenden Abbildung sind dies die sechs (hellen und dunklen) gelben Bogensegmente unter Berücksichtigung der beiden roten vertikalen Linien. Beachten Sie, dass ein Kreis, wenn er ein Bogensegment zwischen den beiden Linien hat, beide Linien schneidet, da wir alle vertikalen Linien tangential zu den Kreisen übergeben, was den Rest des Algorithmus vereinfacht.

Nicht alle diese Bögen sind relevant. Wir interessieren uns nur für diejenigen, die an der Grenze zwischen A und der Vereinigung von B liegen . Um diese zu finden, sortieren wir die Bögen von oben nach unten (beachten Sie, dass sich die Bögen möglicherweise nicht richtig schneiden, da dies die Existenz einer weiteren vertikalen Linie zwischen den beiden, die wir betrachten, implizieren würde, und es daher sinnvoll ist, zu sprechen über oder unter einem beliebigen Bogen.)

Figur 3

Wir durchlaufen die Bögen der Reihe nach und zählen dabei die Anzahl der B- Kreise, in denen wir uns gerade befinden, n . Wenn sich n von 0 auf 1 ändert, während wir in A sind , oder wenn wir am oberen Bogen von A sind, während n ungleich Null ist, entspricht der aktuelle Bogen einem Bein eines Trapezes. Wenn n von 1 nach 0 wechselt, während wir in A sind , oder wenn wir im unteren Bogen von A sind, während nungleich Null ist, dann entspricht der Strombogen dem anderen Schenkel desselben Trapezes. Sobald wir ein solches Bogenpaar gefunden haben (die beiden hellgelben Bögen in der obigen Abbildung), berechnen wir die Fläche des entsprechenden Trapezes und der beiden Bogensegmente und addieren sie zur Gesamtfläche, die von A subtrahiert werden soll .

Die Berechnung der Fläche von A sowie der Flächen der Trapezoide ist relativ einfach. Die Fläche jedes Bogensegments ist die Fläche des entsprechenden Kreissektors abzüglich der Fläche des Dreiecks, dessen Eckpunkte die beiden Endpunkte des Bogensegments sind, und des Mittelpunkts des entsprechenden Kreises.

Figur 4

Ell
quelle
1
Dies ist die Art von Lösung, die ich in Betracht gezogen habe, mit der Ausnahme, dass ich das Zielgebiet direkt gefunden hätte, indem ich die Triarcs und Trapearcs gefunden hätte, die sich in A, aber nicht in B befanden (deren Gebiete durch Subtrahieren eines Bogensegments von einem Dreieck oder Trapez gefunden werden müssen).
Quintopia
@quintopia Dies könnte sogar ein kleines bisschen kürzer sein, da wir dadurch die Fläche von A nicht mehr berechnen müssen und wahrscheinlich nur ein bisschen mit der Bedingung auf n spielen müssen .
Ell
@quintopia OTOH, Sie müssen die Möglichkeit berücksichtigen, dass sich ein positiver Bogen neben einem der Beine befindet, wenn es sich um ein Bogensegment von A handelt. Wer weiß?
Ell
Hervorragende Lösung. Ein fast identisches Problem steckte gestern Abend in meinem Kopf fest, und ich wollte wirklich, dass jemand es löst. Ihre Lösung ist besser als die Ideen, mit denen ich gearbeitet habe.
Logic Knight