Пошук ексклюзивної зони в перехрестях кола


17

Ось оманливий складний пазл для геометрії для вас!

Давши коло Aта nінші кола B[n], знайдіть загальну площу, що міститься в ньому A, не в межах жодного кола B.

Ваш код повинен бути якомога коротшим.

Вхідні дані

Ваша інформація повинна містити таку інформацію:

  • Число з плаваючою комою для відображення радіуса кола A.
  • Список чисел з плаваючою комою для відображення радіусів кіл у B.
  • Перелік центрів гуртків у Росії B. Ваша програма може очікувати центрів у полярних або декартових координатах.
  • За бажанням, ви можете отримати кількість nкіл у Б. Цей введення не потрібно.

Слід вважати, що центром кола 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 "Площа загального перекриття трьох кіл". Жовтень 2006. Веб. http://dspace.dsto.defence.gov.au/dspace/bitstream/1947/4551/4/DSTO-TN-0722.PR.pdf .


Я намагався вирішити це два роки тому , працюючи над цим , виходячи з того, наскільки проста проблема у двох колах. Я в кінцевому підсумку прочитав папір, яку ви зв'язали ... і вирішив поїхати з Монте-Карло'інгу. "Ваше рішення не повинно покладатися на точки вибірки в колах для визначення площі." Д:
Мартін Ендер

Здається, у вас немає тестового випадку, коли одне коло Bмістить інше. Можливо, варто додати це.
Мартін Ендер

Не могли б ви перевірити свій третій тестовий випадок? Я отримую 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

Пітон 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) варіант у моєму вирішенні виклику Мартіна Бюттнера з виклику полігону, що перетинається самопересічним . Він використовує той самий трюк, щоб розбити проблему на досить маленькі шматки, для яких вона стає більш керованою.

Ми знаходимо точки перетину між усіма парами кіл (враховуючи і А , і кола в В ), і проходимо вертикальну лінію через кожну з них. Крім того, ми проходимо всі вертикальні лінії, дотичні до будь-якого з кіл. Ми Викиньте все лінії , які не перетинаються A , так що всі інші лінії між лівим і правим дотичних A .

Фігура 1

Ми шукаємо площу перетину A і об'єднання кіл у B - темно-червону область на малюнку вище. Це область, яку нам потрібно відняти від A, щоб отримати результат.

Між кожною парою послідовних вертикальних ліній ця область може бути розбита на набір вертикальних трапецій (або трикутників, або сегментів ліній, як особливі випадки трапецій), із «надлишковим» сегментом дуги поруч з кожною ногою. Той факт, що ми проходимо стільки вертикальних ліній, скільки ми робимо, гарантує, що обмежена площа дійсно не є більш складною, ніж це, оскільки в іншому випадку повинна бути додаткова точка перетину, а отже, і додаткова вертикальна лінія між двома лініями в питання.

Малюнок 2

Щоб знайти ці сегменти трапеції та дуги, для кожної пари послідовних вертикальних ліній ми знаходимо сегменти дуги кожного з кіл, які належним чином лежать між цими двома лініями (звичайно, деякі кола можуть не мати сегмента дуги між заданою парою ліній .) На малюнку нижче це шість (яскравий і темний) жовтих сегментів дуги, якщо розглядати дві червоні вертикальні лінії. Зауважимо, що, оскільки ми передаємо всі вертикальні прямі дотичні до кіл, якщо коло має відрізок дуги між двома лініями, воно обов'язково перетинає обидві лінії, що спрощує решту алгоритму.

Не всі ці дуги є актуальними; ми зацікавлені тільки в тих , які знаходяться на межі перетину між А і об'єднання B . Щоб знайти їх, ми сортуємо дуги зверху вниз (зауважте, що дуги можуть неправильно перетинатися одна з одною, оскільки це означатиме існування ще однієї вертикальної лінії між двома нами, що ми розглядаємо, і тому є сенс говорити про довільну дугу, що знаходиться вище або нижче будь-якої іншої.)

Малюнок 3

Ми обводимо дуги в порядку, зберігаючи підрахунок кількості кіл В, які ми зараз перебуваємо, n . Якщо n змінюється від 0 до 1, поки ми знаходимось у A , або якщо ми знаходимося у верхній дузі A, тоді як n - ненульовий, то поточна дуга відповідає одній нозі трапеції. Якщо n змінюється від 1 до 0, поки ми знаходимося в A , або якщо ми знаходимося в нижній дузі A, тоді як nє ненульовим, то поточна дуга відповідає іншій ніжці тієї ж трапеції. Після того, як ми знаходимо таку пару дуг (два яскраво - жовтих дуг в малюнку вище), ми обчислити площу відповідної трапеції, а також з двох дугових сегментів, і додати його до загальної площі , щоб бути відняті з A .

Обчислення площі А , а також площ трапецій досить прямо. Площа кожного відрізка дуги - площа відповідного кругового сектора, мінус площа трикутника, вершинами якого є дві кінцеві точки сегмента дуги, і центр відповідного кола.

Малюнок 4


1
Це таке рішення, яке я розглядав, хіба що я знайшов би цільову область безпосередньо, знайшовши триарки та трапеції, які знаходились у A, але не B (ділянки яких слід знайти, віднімаючи сегмент дуги від трикутника або трапеції).
Кінтопія

@quintopia Це може бути навіть трохи коротше, оскільки це позбавляє нас від необхідності обчислити площу А , і все, що потрібно, ймовірно, трохи грає з умовою на n .
Елл

@quintopia OTOH, вам доведеться враховувати можливість мати позитивну дугу поруч із однією з ніжок, якщо це сегмент дуги A , так хто знає ...
Ell

Відмінне рішення. Проблема, майже ідентична цій, застрягла в моїй голові минулої ночі, і я дуже хотів, щоб хтось вирішив її. Ваше рішення краще, ніж ідеї, з якими я працював.
Логічний лицар
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.