Гольф найменше коло!


29

Проблема:

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

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

Змагання:

  • Введення: Список з 4 або більше нелінійних точок. Точки повинні мати координати X і Y; координати можуть бути поплавцями. Щоб полегшити виклик, жодна дві точки не повинна мати однакову координату X.
    Наприклад:[(0,0), (2,1), (5,3), (-1,-1)]
  • Вихід: Набір значень, (h,k,r)таких, що - рівняння найменшого кола, яке охоплює всі точки.(xh)2+(yk)2=r2

Правила:

  • Ви можете вибрати метод введення, який відповідає вашій програмі.
  • Вихід повинен бути надрукований STDOUTабо повернутий функцією.
  • "Нормальні", загальномовні мови, переважні, але будь-який езоланг є прийнятним.
  • Можна припустити, що точки не є колінними.
  • Це код-гольф, тому виграє найменша програма в байтах. Переможець буде обраний через тиждень після опублікування виклику.
    • Будь ласка, включіть мову, яку ви використовували, і довжину в байтах як заголовка в першому рядку вашої відповіді: # Language: n bytes

Тестові приклади:

  • 1:
    • Вхід: [(-8,0), (3,1), (-6.2,-8), (3,9.5)]
    • Вихід: [-1.6, 0.75, 9.89]
  • 2:
    • Вхід: [(7.1,-6.9), (-7,-9), (5,10), (-9.5,-8)]
    • Вихід: [-1.73, 0.58, 11.58]
  • 3:
    • Вхід: [(0,0), (1,2), (3,-4), (4,-5), (10,-10)]
    • Вихід: [5.5, -4, 7.5]
  • 4:
    • Вхід: [(6,6), (-6,7), (-7,-6), (6,-8)]
    • Вихід: [0, -0.5, 9.60]

Щасливого гольфу !!!


Пов'язані завдання:



8
"Якщо є три (нелінійні) точки, можна отримати рівняння кола, яке торкається їх усіх" - але це може бути не найменший коло, що вкладається. Для трьох вершин тупого трикутника найменший огороджувальний круг - той, діаметр якого - найдовша сторона.
Андерс Касеорг

2
@Arnauld Те саме, що і ти. Для тестового випадку 2 я отримую центр (-1,73, 0,58), а для тестового випадку 3 (5,5, -4).
Робін Райдер

@Arnauld дякую за ваш коментар. Я відповідно відредагував пост
Barranka

@Arnauld ой, вибач. Справді. Альдо, виправляючи свої спостереження
Barranka

Відповіді:


26

Мова Вольфрама (Mathematica) , 28 27 байт

#~BoundingRegion~"MinDisk"&

Спробуйте в Інтернеті!

Тут зручні вбудовані. Вихід - об’єкт диска з центром і радіусом. Як і інші, я вважав, що 2-й і 3-й тестові випадки відрізняються від питання.

Дякуємо @lirtosiast за збереження байта!

Якщо список потрібен як вихід, це можна зробити в 35 байт (ціною додаткових 8 байт) . Дякуємо @Roman, що вказав на це.


3
Я очікував, що вбудована програма Mathematica. Але я не очікував, що у Mathematica будуть "дискові об'єкти".
Робін Райдер

36 байт, щоб отримати правильний вихідний формат:Append@@BoundingRegion[#,"MinDisk"]&
Роман

20

JavaScript (ES6),  298 ... 243  242 байт

Повертає масив [x, y, r].

p=>p.map(m=([c,C])=>p.map(([b,B])=>p.map(([a,A])=>p.some(([x,y])=>H(x-X,y-Y)>r,F=s=>Y=(d?((a*a+A*A-q)*j+(b*b+B*B-q)*k)/d:s)/2,d=c*(A-B)+a*(j=B-C)+b*(k=C-A),r=H(a-F(a+b),A-F(A+B,X=Y,j=c-b,k=a-c)))|r>m?0:o=[X,Y,m=r]),q=c*c+C*C),H=Math.hypot)&&o

Спробуйте в Інтернеті!

або переглянути відформатовану версію

Як?

Метод

Для кожної пари точок (A,B) , ми створюємо коло (X,Y,r) , діаметр якого Б .AB

X=Ax+Bx2,Y=Ay+By2,r=(AxBx2)2+(AyBy2)2

Для кожної трійки різних точок (A,B,C) , ми створюємо коло (X,Y,r) , який огинає трикутник B C .ABC

d=Ax(ByCy)+Bx(CyAy)+Cx(AyBy)
X=(Ax2+Ay2)(ByCy)+(Bx2+By2)(CyAy)+(Cx2+Cy2)(AyBy)2d
Y=(Ax2+Ay2)(CxBx)+(Bx2+By2)(AxCx)+(Cx2+Cy2)(BxAx)2d
r=(XAx)2+(YAy)2

(x,y)

(xX)2+(yY)2r

And we eventually return the smallest valid circle.

Implementation

In the JS code, the formula to compute (X,Y) for the triangle's circumscribed circle is slightly simplified. Assuming d0, we define q=Cx2+Cy2, leading to:

X=(Ax2+Ay2q)(ByCy)+(Bx2+By2q)(CyAy)2d
Y=(Ax2+Ay2q)(CxBx)+(Bx2+By2q)(AxCx)2d

This way, the helper function F requires only two parameters (j,k) to compute each coordinate:

  • (ByCy,CyAy) for X
  • (CxBx,AxCx) for Y

The third parameter used in F (i.e. its actual argument s) is used to compute (X,Y) when d=0, meaning that the triangle is degenerate and we have to try to use the diameter instead.


maybe writing a Newton-type numerical optimizer is shorter...
qwr

Do you have a proof of correctness? I can see that this is a plausible approach, but more than that seems to require quite a bit of work.
Peter Taylor

3
@PeterTaylor The algorithm is mentioned on Wikipedia: a naive algorithm solves the problem in time O(n^4) by testing the circles determined by all pairs and triples of points. But unfortunately, there's no link to a proof.
Arnauld

Will it meet problem of precision making there no solution?
l4m2

1
@Arnauld If you need some strange numbers to reach, I may say that's fine; If it even fail in easy situation that may be a problem
l4m2

14

R, 59 bytes

function(x)nlm(function(y)max(Mod(x-y%*%c(1,1i))),0:1)[1:2]

Try it online!

Takes input as a vector of complex coordinates. Mod is the distance (modulus) in the complex plane and nlm is an optimization function: it finds the position of the center (output as estimate) which minimizes the maximum distance to the input points, and gives the corresponding distance (output as minimum), i.e. the radius. Accurate to 3-6 digits; the TIO footer rounds the output to 2 digits.

nlm takes a numeric vector as input: the y%*%c(1,1i) business converts it to a complex.


9

Jelly, 100 98 bytes

_²§½
1ịṭƊZIṚṙ€1N1¦,@ṭ@²§$µḢZḢ×Ø1œị$SḤ÷@P§
ZṀ+ṂHƲ€_@Ç+ḷʋ⁸,_²§½ʋḢ¥¥
œc3Ç€;ŒcZÆm,Hñ/$Ʋ€$ñ_ƭƒⱮṀ¥Ðḟ⁸ṚÞḢ

Try it online!

In contrast to my Wolfram language answer, Jelly needs quite a lot of code to achieve this (unless I’m missing something!). This full program takes the list of points as its argument and returns the centre and radius of the smallest enclosing circle. It generates circumcircles for all sets of three points, and diameters for all sets of two points, checks which include all of the points and then picks the one with the smallest radius.

Code for the make_circumcircle bit was inspired by code at this site, in turn inspired by Wikipedia.


1
I don't understand this code, but rather than diameters and circumcircles separately, could you generate all sets of three points with duplicates and filter out the lists of three identical points?
lirtosiast

2

APL(NARS), 348 chars, 696 bytes

f←{h←{0=k←⍺-1:,¨⍵⋄(k<0)∨k≥i←≢w←⍵:⍬⋄↑,/{w[⍵],¨k h w[(⍳i)∼⍳⍵]}¨⍳i-k}⋄1≥≡⍵:⍺h⍵⋄⍺h⊂¨⍵}
c←{⍵≡⍬:1⋄(x r)←⍵⋄(-r*2)++/2*⍨⍺-x}
p←{(b k)←⍺ ⍵⋄∧/¨1e¯13≥{{⍵{⍵c⍺}¨b}k[⍵]}¨⍳≢k}
s2←{(+/k),√+/↑2*⍨-/k←2÷⍨⍵}
s3←{0=d←2×-.×m←⊃{⍵,1}¨⍵:⍬⋄m[;1]←{+/2*⍨⍵}¨⍵⋄x←d÷⍨-.×m⋄m[;2]←{1⊃⍵}¨⍵⋄y←d÷⍨--.×m⋄(⊂x y),√+/2*⍨(x y)-1⊃⍵}
s←{v/⍨⍵p v←(s2¨2 f⍵)∪s3¨3 f⍵}
s1←{↑v/⍨sn=⌊/sn←{2⊃⍵}¨v←s⍵}

This is one 'implementation' of formulas in Arnauld solution...Results and comments:

  s1 (¯8 0)(3 1)(¯6.2 ¯8)(3 9.5)
¯1.6 0.75  9.885469134 
  s1 (7.1 ¯6.9)(¯7 ¯9)(5 10)(¯9.5 ¯8)
¯1.732305109 0.5829680042  11.57602798 
  s1 (0 0)(1 2)(3 ¯4)(4 ¯5)(10 ¯10)
5.5 ¯4  7.5 
  s1 (6 6)(¯6 7)(¯7 ¯6)(6 ¯8)
0 ¯0.5  9.604686356 
  s1 (6 6)(¯6 7)(¯7 ¯6)(6 ¯8)(0 0)(1 2)(3 ¯4)(4 ¯5)(10 ¯10)
2 ¯1.5  11.67261753 
  s1 (6 6)(¯6 7)(¯7 ¯6)(6 ¯8)(1 2)(3 ¯4)(4 ¯5)(10 ¯10)(7.1 ¯6.9)(¯7 ¯9)(5 10)(¯9.5 ¯8)
1.006578947 ¯1.623355263  12.29023186 
  s1 (1 1)(2 2)(3 3)(4 4)
2.5 2.5  2.121320344 
  ⎕fmt s3 (1 1)(2 2)(3 3)(4 4)
┌0─┐
│ 0│
└~─┘

f: finds the combination of alpha ojets in the omega set

f←{h←{0=k←⍺-1:,¨⍵⋄(k<0)∨k≥i←≢w←⍵:⍬⋄↑,/{w[⍵],¨k h w[(⍳i)∼⍳⍵]}¨⍳i-k}⋄1≥≡⍵:⍺h⍵⋄⍺h⊂¨⍵}

((X,Y), r) from now represent one circonference of radius r and center in (X Y).

c: finds if the point in ⍺ is inside the circumference ((X Y) r) in ⍵ (result <=0) ot it is external (result >0) In the case of circumference input in ⍵ it is ⍬ as input, it would return 1 (out of circumference) each possible input in ⍺.

c←{⍵≡⍬:1⋄(x r)←⍵⋄(-r*2)++/2*⍨⍺-x}

p: if ⍵ is an array of ((X Y) r); for each of the ((X Y) r) in ⍵ writes 1 if all points in the array ⍺ are internal to ((X Y) r) otherwise writes 0 NB Here's something that do not goes because I had to round to epsilon= 1e¯13. in other words in limit cases of points in the plane (and probably built on purpose) it is not 100% solution insured

p←{(b k)←⍺ ⍵⋄∧/¨1e¯13≥{{⍵{⍵c⍺}¨b}k[⍵]}¨⍳≢k}

s2: from 2-point in ⍵, it returns the circumference in the format ((X Y) r) that has diameter in those 2 points

s2←{(+/k),√+/↑2*⍨-/k←2÷⍨⍵}

s3: from 3 points it return the circumference in the format ((X Y) r) passing through those three points If there are problems (for example points are aligned), it would fail and return ⍬.

s3←{0=d←2×-.×m←⊃{⍵,1}¨⍵:⍬⋄m[;1]←{+/2*⍨⍵}¨⍵⋄x←d÷⍨-.×m⋄m[;2]←{1⊃⍵}¨⍵⋄y←d÷⍨--.×m⋄(⊂x y),√+/2*⍨(x y)-1⊃⍵}

note that -.× find the determinant of a matrix nxn and

  ⎕fmt ⊃{⍵,1}¨(¯8 0)(3 1)(¯6.2 ¯8)
┌3─────────┐     
3 ¯8    0 1│     |ax  ay 1|
│  3    1 1│   d=|bx  by 1|=ax(by-cy)-bx(ay-cy)+cx(ay-by)=ax(by-cy)+bx(cy-ay)+cx(ay-by)
│ ¯6.2 ¯8 1│     |cx  cy 1|
└~─────────┘

s: from n points in ⍵, it finds the type circumferences of those found by s2 or those of type s3 that contain all n points.

s←{v/⍨⍵p v←(s2¨2 f⍵)∪s3¨3 f⍵}

s1: from the set found from the s above calculates those that have minimum radius and returns the first one that has minimal radius. The three numbers as arrays (the first and second coordinates are the coordinates of the center, while the third is the radius of the circumference found).

s1←{↑v/⍨sn=⌊/sn←{2⊃⍵}¨v←s⍵}

2

Python 2 (PyPy), 244 242 bytes

P={complex(*p)for p in input()}
Z=9e999,
for p in P:
 for q in{p}^P:
	for r in{p}^P:R,S=1j*(p-q),q-r;C=S.imag and S.real/S.imag-1jor 1;c=(p+q-(S and(C*(p-r)).real/(C*R).real*R))/2;Z=min(Z,(max(abs(c-t)for t in P),c.imag,c.real))
print Z[::-1]

Try it online!

This uses the brute-force O(n^4) algorithm, iterating through each pair and triangle of points, calculating the centre, and keeping the centre that needs the smallest radius to enclose all points. It calculates the circumcentre of 3 points via finding the intersection of two perpendicular bisectors (or, if two points are equal it uses the midpoint with the third).


Welcome to PPCG! Since you are using Python 2, you can save 1 byte by converting the two spaces on the 5th line to a tab.
Stephen

P={x+y*1j for x,y in input()} saves 2 bytes as well.
Mr. Xcoder

1

Python 212 190 bytes

This solution is incorrect, and I have to work now so I do not have time to fix it.

a=eval(input())
b=lambda a,b: ((a[0]-b[0])**2+(a[1]-b[1])**2)**.5
c=0
d=1
for e in a:
    for f in a:
        g=b(e,f)
        if g>c:
            c=g
            d=[e,f]
print(((d[0][0]+d[1][0])/2,(d[0][1]+d[1][1])/2,c/2))

Try it online!

I figured out which two points are furthest and then I generated an equation for a circle based off those points!

I know this isn't the shortest in python, but it's the best I could do! Also this is my first attempt at doing one of these, so please be understanding!


2
This can be golfed some more. For example, you can shorten if g>c:\n c=g\n d=[e,f] to if g>c:c=g;d=[e,f], saving you a lot of whitespace. I don't think you need to initialise d beforehand, also using two variables and E,F=e,f in line 10 will make your print a lot shorter. I think a solution with max and a list comprehension would be even shorter than two loops, but I might be wrong. Sadly, however, your solution is not correct. For the points (-1,0), (0,1.41), (0.5, 0), (1,0) your solution computes a circle around 0 with radius 1. But the point (1, 1.41) is not in that circle.
Black Owl Kai

Welcome! Thank you for your answer. As pointed in the comment above, your solution is not correct. A hint for brute-force solution: The smallest possible circle touches either two points or three points. You can start calculating the equation for the circle that touches each pair of points and check if there's one that encloses all points. Then calculate the equation for the circle that touches each triplet of points and check if there's one that encloses all points. Once you get all the possible circles, select the one with the smallest radius.
Barranka

1
Гаразд, я спробую змусити його працювати, а потім оновлю відповідь. Мені потрібно розібратися, як перевірити, чи міститься точка у колі.
Бен Моррісон

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