การหาพื้นที่พิเศษในสี่แยกวง


17

นี่คือปริศนาเรขาคณิตที่ท้าทายสำหรับคุณ!

ป.ร. ให้ไว้เป็นวงกลมAและnวงการอื่น ๆB[n]พบว่าพื้นที่ทั้งหมดที่มีอยู่ภายในAนั่นคือไม่ได้Bอยู่ภายในวงกลมของใด ๆ

รหัสของคุณควรสั้นที่สุด

อินพุต

ข้อมูลที่คุณป้อนควรมีข้อมูลต่อไปนี้:

  • Aจำนวนจุดลอยตัวเพื่อเป็นตัวแทนของรัศมีของวงกลม
  • รายการหมายเลขลอยจุด A Bไปแทนรัศมีของวงการใน
  • Bรายชื่อศูนย์ของวงการใน โปรแกรมของคุณอาจคาดหวังว่าศูนย์ในพิกัดเชิงขั้วหรือคาร์ทีเซียน
  • คุณอาจได้รับจำนวนnวงกลมใน B หรือไม่จำเป็นต้องป้อนข้อมูลนี้ก็ได้

มันจะได้รับการสันนิษฐานว่าศูนย์กลางของวงกลมเป็นที่มาที่เป็นจุดA(0, 0)

มีการรับประกันว่าจะไม่มีวงกลมสองวงBเหมือนกัน แต่ไม่รับประกันว่า: วงกลมทั้งหมดของBจุดตัดA, ศูนย์กลางทั้งหมดBอยู่ด้านนอกAหรือไม่มีวงกลมสองวงที่อยู่Bตรงข้ามกัน ตรวจสอบให้แน่ใจว่าโซลูชันของคุณสามารถจัดการกับเคสแบบขอบต่างๆ

คุณอาจได้รับการป้อนข้อมูลในลำดับใด ๆ และในรูปแบบของการป้อนข้อความ (ผ่าน stdin หรือเทียบเท่าภาษาของคุณ) พารามิเตอร์ฟังก์ชั่นหรืออาร์กิวเมนต์บรรทัดคำสั่ง

หากคุณเลือกที่จะรับข้อความควรมีตัวคั่น ASCII หนึ่งหรือสองตัวอักษรที่พิมพ์ได้ระหว่างส่วนของอินพุต

เอาท์พุต

โปรแกรมหรือฟังก์ชั่นการส่งออกควรเป็นจำนวนจุดลอยตัวเดียวที่เป็นตัวแทนของพื้นที่ทั้งหมดของคุณไม่ได้อยู่ในแวดวงของA Bคำตอบของคุณควรแม่นยำถึงตัวเลขที่มีนัยสำคัญอย่างน้อยสามตัวสำหรับกรณีทดสอบทั้งหมด

ใช้กฎเกณฑ์ทั่วไปเกี่ยว

โซลูชันของคุณไม่ควรใช้จุดสุ่มตัวอย่างภายในวงกลมเพื่อกำหนดพื้นที่

บิวด์อินที่ค้นหาตำแหน่งทางแยกของวงกลมโดยอัตโนมัติค้นหาพื้นที่ภายในจุดตัดของวงกลมหรือแก้ไขปัญหานี้ทันทีไม่ได้รับอนุญาต

กรณีทดสอบ

ในแต่ละภาพวงกลมAจะมีเส้นขอบสีน้ำเงินโดยมีวงกลมที่ล้อมรอบด้วยBสีเขียวและดำ พื้นที่ที่ควรส่งคืนจะถูกเติมสีแดง

(ขอขอบคุณRainer P. เป็นพิเศษสำหรับการตรวจสอบวิธีแก้ไขปัญหาของฉัน)

กรณีทดสอบ 1:

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

กรณีทดสอบ 1

Result: 0.00

กรณีทดสอบ 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}

กรณีทดสอบ 2

Result: 1.3878e+04

กรณีทดสอบ 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}

กรณีทดสอบ 3

Result: 1.8969e+04

กรณีทดสอบ 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}

กรณีทดสอบ 4

Result: 1.1264e+04

กรณีทดสอบ 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}

กรณีทดสอบ 5

Result: 2.6742e+04

การอ่านที่แนะนำ:

Fewell, MP "พื้นที่ที่พบบ่อยซ้อนกันของสามวงกลม" ต.ค. 2549 เว็บ http://dspace.dsto.defence.gov.au/dspace/bitstream/1947/4551/4/DSTO-TN-0722.PR.pdf


ฉันพยายามที่จะแก้ปัญหานี้ด้วยตัวเองเมื่อสองปีก่อนในขณะที่ทำสิ่งนี้โดยพิจารณาจากปัญหาง่าย ๆ สำหรับสองวงกลม ฉันอ่านบทความที่คุณเชื่อมโยง ... และตัดสินใจไปกับ Monte Carlo'ing พื้นที่ "โซลูชันของคุณไม่ควรใช้จุดสุ่มตัวอย่างภายในวงกลมเพื่อกำหนดพื้นที่" D:
Martin Ender

คุณดูเหมือนจะไม่มีกรณีทดสอบที่มีวงกลมวงหนึ่งอยู่ในBนั้น อาจจะมีมูลค่าเพิ่มที่
Martin Ender

คุณสามารถตรวจสอบกรณีทดสอบที่สามของคุณได้ไหม 1.8970e+04ฉันได้รับ
LegionMammal978

@ MartinBüttnerฉันเจอปัญหาเรื่องอุบัติเหตุด้วย ฉันไม่พอใจกับวิธีแก้ปัญหาของฉันมากเกินไป แต่ดูเหมือนว่าจะใช้งานได้ ฉันจะลองทำการทดสอบเล็กน้อยสำหรับกรณีนั้นขอบคุณ!
BrainSteel

@ LegionMammal978 ใช่ดูเหมือนว่ากรณีนั้นผิด ผมมีข้อมูลต่อไปนี้สำหรับทางแยกระหว่างวงการ: 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ซึ่งอัตราผลตอบแทนที่18969.69009เป็นคำตอบ
BrainSteel

คำตอบ:


14

Python 2, 631 ไบต์

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

ตัวแบ่งบรรทัดนำหน้า\มีไว้เพื่อให้อ่านง่ายและจะไม่ถูกนับในคะแนน

อ่านการป้อนข้อมูลผ่าน STDIN เป็นรายการของ(center, radius)คู่ที่เป็นตัวเลขที่ซับซ้อนในรูปแบบcenter X+Yjวงกลมครั้งแรกในรายการเป็น(ซึ่งเป็นศูนย์ไม่ได้ที่จะเป็นจุดเริ่มต้น) และส่วนที่เหลือของรายการเป็นB พิมพ์ผลลัพธ์ไปที่ STDOUT

ตัวอย่าง

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

คำอธิบาย

นี่เป็นรูปแบบที่หลากหลาย (ยาวขึ้นและน่าเกลียดมาก: P) ในโซลูชันของฉันกับพื้นที่ของรูปหลายเหลี่ยมของMartin Büttner ในการตัดต่อรูปหลายเหลี่ยมแบบท้าทายตัวเอง มันใช้เคล็ดลับแบบเดียวกันนี้เพื่อแยกปัญหาออกเป็นชิ้นเล็ก ๆ ซึ่งมันสามารถจัดการได้ง่ายขึ้น

เราพบจุดตัดกันระหว่างวงกลมทุกคู่ (พิจารณาทั้งAและวงกลมในB ) และผ่านเส้นแนวตั้งผ่านแต่ละวงกลม นอกจากนี้เราส่งผ่านเส้นแนวตั้งทั้งหมดแทนเจนต์ไปยังวงกลมใด ๆ เราทิ้งทุกสายที่ไม่ได้ตัดเพื่อให้ทุกสายที่เหลืออยู่ระหว่างซ้ายและขวาเสียบ้างของ

รูปที่ 1

เรากำลังมองหาพื้นที่ของจุดตัดของAและสหภาพของวงกลมในB - พื้นที่สีแดงเข้มในภาพประกอบด้านบน นี่คือพื้นที่ที่เราต้องลบออกจากAเพื่อให้ได้ผลลัพธ์

ระหว่างแต่ละคู่ของเส้นแนวตั้งที่ต่อเนื่องกันบริเวณนี้สามารถแบ่งออกเป็นชุดของรูปสี่เหลี่ยมคางหมูแนวตั้ง (หรือสามเหลี่ยมหรือส่วนของเส้นตรงเป็นกรณีพิเศษของรูปสี่เหลี่ยมคางหมู) ด้วยส่วนโค้ง "เกิน" ถัดจากขาแต่ละข้าง ความจริงที่ว่าเราผ่านเส้นแนวตั้งมากที่สุดเท่าที่เราทำรับประกันได้ว่าพื้นที่ที่ถูกล้อมรอบนั้นไม่ซับซ้อนกว่านั้นอย่างแน่นอนเนื่องจากมิฉะนั้นจะต้องมีจุดตัดเพิ่มเติมและด้วยเหตุนี้เส้นแนวตั้งเพิ่มเติมระหว่างสองบรรทัดใน คำถาม.

รูปที่ 2

เพื่อหารูปสี่เหลี่ยมคางหมูและส่วนโค้งเหล่านี้สำหรับเส้นแนวตั้งแต่ละคู่ติดต่อกันเราจะพบส่วนโค้งของวงกลมแต่ละวงที่อยู่ระหว่างเส้นสองเส้นนี้ (แน่นอนว่าวงกลมบางวงอาจไม่มีส่วนโค้งระหว่างคู่ของเส้นที่กำหนด .) ในภาพประกอบด้านล่างนี่คือส่วนโค้งหกสีเหลือง (สว่างและมืด) เมื่อพิจารณาเส้นแนวตั้งสีแดงสองเส้น โปรดทราบว่าเนื่องจากเราส่งผ่านเส้นแนวตั้งทั้งหมดแทนเจนต์ไปยังวงกลมหากวงกลมมีส่วนโค้งระหว่างเส้นสองเส้นมันจำเป็นต้องตัดกันทั้งสองเส้นซึ่งจะทำให้อัลกอริธึมที่เหลือง่ายขึ้น

ไม่ใช่เส้นโค้งทั้งหมดที่เกี่ยวข้อง เรากำลังสนใจเฉพาะในคนที่อยู่ในขอบเขตของการแยกระหว่างและสหภาพของB ในการค้นหาสิ่งเหล่านั้นเราจัดเรียงส่วนโค้งจากบนลงล่าง (โปรดทราบว่าส่วนโค้งอาจไม่ถูกต้องซึ่งกันและกันเนื่องจากสิ่งนี้จะบ่งบอกถึงการมีอยู่ของเส้นแนวตั้งอื่นระหว่างทั้งสองที่เรากำลังพิจารณาอยู่ เกี่ยวกับอาร์คโดยพลการที่อยู่เหนือหรือใต้อีกอันหนึ่ง)

รูปที่ 3

เราสำรวจโค้งในการสั่งซื้อในขณะที่ทำให้การนับจำนวนของBวงการเรากำลังอยู่ภายในn ถ้าnเปลี่ยนจาก 0 เป็น 1 ขณะที่เราอยู่ภายในAหรือถ้าเราอยู่ที่ส่วนโค้งบนของAในขณะที่nไม่ใช่ศูนย์ดังนั้นส่วนโค้งในปัจจุบันจะตรงกับขาข้างหนึ่งของสี่เหลี่ยมคางหมู ถ้าnเปลี่ยนจาก 1 เป็น 0 ในขณะที่เราอยู่ภายในAหรือถ้าเราอยู่ที่ส่วนล่างของAในขณะที่nไม่ใช่ศูนย์ดังนั้นส่วนโค้งปัจจุบันตรงกับขาอีกข้างของสี่เหลี่ยมคางหมูเดียวกัน เมื่อเราพบเช่นคู่ของโค้ง (สองโค้งสีเหลืองสดใสในภาพข้างต้น) เราคำนวณพื้นที่ของรูปสี่เหลี่ยมคางหมูที่สอดคล้องกันและทั้งสองกลุ่มส่วนโค้งและเพิ่มเข้าไปในพื้นที่ทั้งหมดจะถูกหักออกจาก

การคำนวณพื้นที่ของAรวมถึงพื้นที่ของสี่เหลี่ยมคางหมูค่อนข้างตรงไปตรงมา พื้นที่ของแต่ละส่วนอาร์คคือพื้นที่ของเซกเตอร์วงกลมที่สอดคล้องกันลบพื้นที่ของสามเหลี่ยมที่มีจุดยอดเป็นสองจุดปลายทางของส่วนอาร์คและศูนย์กลางของวงกลมที่สอดคล้องกัน

รูปที่ 4


1
นี่เป็นวิธีแก้ปัญหาที่ฉันกำลังพิจารณายกเว้นว่าฉันจะได้พบพื้นที่เป้าหมายโดยตรงโดยการค้นหา triarcs และ trapearcs ที่อยู่ใน A แต่ไม่ใช่ B (ซึ่งจะต้องพบพื้นที่โดยการลบส่วนโค้งออกจากสามเหลี่ยมหรือสี่เหลี่ยมคางหมู)
quintopia

@quintopia นี้อาจได้เป็นบิตขนาดเล็กสั้นกว่าเพราะมันช่วยเราจำเป็นที่จะต้องคำนวณพื้นที่ของและทั้งหมดก็จะอาจจะเล่น bit กับสภาพบนn
Ell

@quintopia OTOH คุณจะต้องคำนึงถึงความเป็นไปได้ที่จะมีส่วนโค้งบวกข้างหนึ่งขาถ้ามันเป็นส่วนอาร์คของAดังนั้นใครจะรู้ ...
Ell

ทางออกที่ดีเยี่ยม ปัญหาเกือบเหมือนกันนี้ติดอยู่ในหัวของฉันเมื่อคืนและฉันต้องการใครสักคนที่จะแก้มัน ทางออกของคุณดีกว่าที่ฉันคิดด้วย
Logic Knight อัศวิน
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.