最小の円をゴルフ!


29

問題:

デカルト平面内の空でない点のセットが与えられた場合、それらすべてを囲む最小の円を見つけます(Wikipediaリンク)。

この問題は、ポイントの数が3以下の場合は簡単です(1つのポイントがある場合、円の半径はゼロです。2つのポイントがある場合、ポイントを結ぶ線分は円の直径です。 3つの(非共直線)ポイント、それらが非鈍角三角形を形成する場合、それらすべてに接触する円の方程式、または2つの点のみに接触し、三角形が鈍角の場合に3番目を囲む円の方程式を取得することができます)。したがって、この課題のために、ポイントの数は3より大きくする必要があります。

チャレンジ:

  • 入力: 4つ以上の非共線点のリスト。ポイントにはX座標とY座標が必要です。座標はフロートにすることができます。課題を簡単にするために、2つのポイントが同じX座標を共有しないようにしてください。
    例えば:[(0,0), (2,1), (5,3), (-1,-1)]
  • 出力:(h,k,r)ような値のタプルは、すべての点を囲む最小の円の方程式です。(xh)2+(yk)2=r2

ルール:

  • プログラムに合った入力方法を選択できます。
  • 出力はSTDOUT、関数に出力されるか、関数によって返される必要があります。
  • 「通常」の汎用言語が推奨されますが、任意のエソランを使用できます。
  • 点は共線ではないと仮定できます。
  • これはコードゴルフなので、バイト単位の最小のプログラムが優先されます。チャレンジが投稿されてから1週間後に勝者が選ばれます。
    • 回答の最初の行に、ヘッダーとして使用した言語とバイト単位の長さを含めてください: # 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
「3つの(同一直線上にない)点がある場合、それらすべてに接触する円の方程式を取得することは可能です」-しかし、それは最小の包囲円ではない場合があります。鈍角三角形の3つの頂点の場合、最小の包囲円は、直径が最長辺であるものです。
アンダース・カセオルグ

2
@Arnauldあなたと同じ。テストケース2の場合、センター(-1.73、0.58)を取得し、テストケース3(5.5、-4)を取得します。
ロビンライダー

@Arnauld、コメントありがとう。私はそれに応じてポストを編集した
Barranka

@Arnauldおっと、すみません。確かに。アルド、あなたの観察で修正
Barranka

回答:


26

Wolfram言語(Mathematica)28 27バイト

#~BoundingRegion~"MinDisk"&

オンラインでお試しください!

ここにはビルトインが便利です。出力は、中心と半径を持つディスクオブジェクトです。他の人と同様に、2番目と3番目のテストケースは質問とは異なることがわかりました。

バイトを保存してくれた@lirtosiastに感謝します!

出力としてリストが必要な場合、これは35バイトで行うことができます(追加で8バイトのコストがかかります)。これを指摘してくれた@Romanに感謝します。


3
Mathematicaが組み込まれていることを期待していました。しかし、Mathematicaに「ディスクオブジェクト」があるとは思っていませんでした。
ロビンライダー

出力形式を正しくするための36バイトAppend@@BoundingRegion[#,"MinDisk"]&
Roman

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)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

そして、最終的に最小の有効な円を返します。

実装

JSコードでは、三角形の外接円のを計算する式がわずかに簡略化されています。と仮定すると、を定義し、次のようになります。(X,Y)d0q=Cx2+Cy2

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

このように、ヘルパー関数は各座標を計算するために2つのパラメーターのみを必要とします。F(j,k)

  • (ByCy,CyAy)のためにX
  • (CxBx,AxCx)のためにY

使用される3番目のパラメーター(つまり、その実際の引数)は、ときにを計算するために使用されます。Fs(X,Y)d=0


多分...ニュートン型数値オプティマイザは短い書き込み
QWR

正しさの証拠はありますか?これはもっともらしいアプローチであることがわかりますが、それ以上にはかなりの作業が必要と思われます。
ピーターテイラー

3
@PeterTaylorアルゴリズムはウィキペディアで言及されています:素朴なアルゴリズムは、点のすべてのペアとトリプルによって決定される円をテストすることにより、時間O(n ^ 4)の問題を解決します。しかし、残念ながら、証拠へのリンクはありません。
アーナルド

精度の問題を解決して解決策はありませんか?
l4m2

1
@Arnauld何かおかしな数字が必要な場合は、それでいいと思うかもしれません。問題のある簡単な状況でも失敗する場合
l4m2

14

R、59バイト

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

オンラインでお試しください!

入力を複素座標のベクトルとして受け取ります。Mod複素平面の距離(モジュラス)でnlmあり、最適化関数です。estimate入力点までの最大距離を最小化する中心の位置(として出力)を見つけ、対応する距離(としての出力minimum)、つまり半径を与えます。3〜6桁の精度。TIOフッターは出力を2桁に丸めます。

nlm入力として数値ベクトルを受け取ります。y%*%c(1,1i)ビジネスはそれを複素数に変換します。


9

ゼリー100 98バイト

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

オンラインでお試しください!

私のWolfram言語の答えとは対照的に、Jellyはこれを達成するために非常に多くのコードを必要とします(何かが足りない限り!)。この完全なプログラムは、引数として点のリストを取り、最小の包囲円の中心と半径を返します。3つの点のすべてのセットに対して外接円を生成し、2つの点のすべてのセットに対して直径を生成し、すべての点を含むチェックを行ってから、半径が最も小さいものを選択します。

make_circumcircleビットのコードは、このサイトのコードに触発され、次にウィキペディアに触発されました。


1
私はこのコードを理解していませんが、直径と外接円を別々にではなく、重複した3点のすべてのセットを生成し、3つの同一点のリストを除外できますか?
リトシアスト

2

APL(NARS)、348文字、696バイト

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

これはArnauldソリューションの数式の1つの「実装」です...結果とコメント:

  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:オメガセット内のアルファオジェットの組み合わせを見つける

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

((X、Y)、r)は、これから(XY)の半径rと中心の1つの円周を表します。

c:inの点が円周内にあるかどうかを調べます((XY)r)in(結果<= 0)またはそれが外部にある(結果> 0)circumferenceの円周入力の場合、入力としてisです1で可能な入力ごとに1(円周外)を返します。

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

p:⍵が((XY)r)の配列である場合; (の((XY)r)のそれぞれについて、配列allのすべての点が((XY)r)の内部にある場合は1を書き込みます。それ以外の場合は0を書き込みます。 13。言い換えれば、平面内のポイントの制限の場合(およびおそらく意図的に構築された場合)、100%のソリューション保証ではありません

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

s2:⍵の2点から、それらの2点に直径を持つ形式((XY)r)で円周を返します

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

s3:3ポイントから、それらの3ポイントを通過する形式((XY)r)で円周を返します。問題がある場合(ポイントの位置合わせなど)、失敗してfailを返します。

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

-。×は行列nxnの行列式を見つけ、

  ⎕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:⍵のn点から、s2で見つかった円周またはn点すべてを含むs3の円周の型円周を見つけます。

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

s1:上記のsで見つかったセットから最小半径を持つものを計算し、最小半径を持つ最初のものを返します。配列としての3つの数値(最初と2番目の座標は中心の座標であり、3番目は見つかった円周の半径です)。

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

2

Python 2(PyPy)244 242バイト

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]

オンラインでお試しください!

これは、ブルートフォースO(n ^ 4)アルゴリズムを使用して、各ペアとポイントの三角形を反復処理し、中心を計算し、すべてのポイントを囲むために最小半径が必要な中心を維持します。2つの垂直な二等分線の交点を見つけることにより、3点の周囲を計算します(または、2点が等しい場合は、3番目の中間点を使用します)。


PPCGへようこそ!Python 2を使用しているため、5行目の2つのスペースをタブに変換することで1バイト節約できます。
スティーブン

P={x+y*1j for x,y in input()}2バイトも節約します。
Xcoder氏

1

Python 212 190バイト

この解決策は間違っているので、今すぐ作業しなければならないので、修正する時間はありません。

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))

オンラインでお試しください!

どの2つのポイントが最も遠いかを把握し、それらのポイントに基づいて円の方程式を生成しました。

私はこれがPythonで最短ではないことを知っていますが、それは私ができる最善です!また、これはこれらのいずれかを行う最初の試みなので、ご理解ください!


2
これはもう少しゴルフすることができます。たとえば、に短縮if g>c:\n c=g\n d=[e,f]するとif g>c:c=g;d=[e,f]、空白を大幅に節約できます。事前にdを初期化する必要はないと思います。また、2つの変数を使用するとE,F=e,f、10行目でprintかなり短くなります。maxリストの内包表記による解決策は、2つのループよりも短いと思いますが、間違っているかもしれません。残念ながら、あなたの解決策は正しくありません。ポイント(-1,0)、(0,1.41)、(0.5、0)、(1,0)の場合、ソリューションは半径1の0を中心とする円を計算しますが、ポイント(1、1.41)はその中にありませんサークル。
ブラックフクロウカイ

ようこそ!ご回答ありがとうございます。上記のコメントで指摘したように、解決策は正しくありません。ブルートフォースソリューションのヒント:可能な最小の円は2点または3点に接触します。ポイントの各ペアに接触する円の方程式の計算を開始し、すべてのポイントを囲むものがあるかどうかを確認できます。次に、ポイントの各トリプレットに接触する円の方程式を計算し、すべてのポイントを囲むものがあるかどうかを確認します。すべての可能な円を取得したら、半径が最小の円を選択します。
16:13のBarranka

1
申し分なく機能するようにしようとします。そして、答えを更新します。ポイントが円に含まれているかどうかを確認する方法を理解する必要があります。
ベン・モリソン

円の中心と半径を取得したら、中心と各ポイント間の距離が半径以下かどうかを確認します。その条件がすべてのポイントに当てはまる場合、その円は候補です
Barranka
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.