Golf il cerchio più piccolo!


29

Il problema:

Dato un insieme di punti non vuoto nel piano cartesiano, trova il cerchio più piccolo che li racchiude tutti ( link Wikipedia ).

Questo problema è banale se il numero di punti è tre o meno (se c'è un punto, il cerchio ha un raggio di zero; se ci sono due punti, il segmento di linea che unisce i punti è il diametro del cerchio; se ci sono tre punti (non lineari), è possibile ottenere l'equazione di un cerchio che li tocca tutti se formano un triangolo non ottuso, o un cerchio che tocca solo due punti e racchiude il terzo se il triangolo è ottuso). Quindi, per il bene di questa sfida, il numero di punti dovrebbe essere maggiore di tre.

La sfida:

  • Input: un elenco di 4 o più punti non lineari. I punti dovrebbero avere coordinate X e Y; le coordinate possono essere float. Per facilitare la sfida, nessun punto deve condividere la stessa coordinata X.
    Per esempio:[(0,0), (2,1), (5,3), (-1,-1)]
  • Output: una tupla di valori, (h,k,r)tale che è l'equazione del cerchio più piccolo che racchiude tutti i punti.(xh)2+(yk)2=r2

Regole:

  • Puoi scegliere qualsiasi metodo di input adatto al tuo programma.
  • L'output deve essere stampato STDOUTo restituito da una funzione.
  • Le lingue "normali", di uso generale, sono preferite, ma qualsiasi esolang è accettabile.
  • Puoi presumere che i punti non siano lineari.
  • Questo è code-golf, quindi vince il programma più piccolo in byte. Il vincitore verrà selezionato una settimana dopo la pubblicazione della sfida.
    • Includi la lingua che hai usato e la lunghezza in byte come intestazione nella prima riga della tua risposta: # Language: n bytes

Casi test:

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

Buon golf !!!


Sfida correlata:



8
"Se ci sono tre punti (non lineari), è possibile ottenere l'equazione di un cerchio che li tocca tutti", ma potrebbe non essere il più piccolo cerchio racchiuso. Per i tre vertici di un triangolo ottuso, il cerchio racchiuso più piccolo è quello il cui diametro è il lato più lungo.
Anders Kaseorg,

2
@Arnauld Come te. Per il caso di test 2, ottengo il centro (-1,73, 0,58) e per il caso di test 3 (5,5, -4).
Robin Ryder,

@Arnauld grazie per il tuo commento. Ho modificato il post di conseguenza
Barranka il

@Arnauld oops, scusa. Infatti. Aldo, correggendo con le tue osservazioni
Barranka,

Risposte:


26

Wolfram Language (Mathematica) , 28 27 byte

#~BoundingRegion~"MinDisk"&

Provalo online!

Gli incorporati sono utili qui. L'output è un oggetto disco con il centro e il raggio. Come altri, ho riscontrato che il 2o e il 3o caso di test sono diversi dalla domanda.

Grazie a @lirtosiast per aver salvato un byte!

Se è richiesto un elenco come output, questo può essere fatto in 35 byte (al costo di ulteriori 8 byte) . Grazie a @Roman per averlo segnalato.


3
Mi aspettavo un Mathematica integrato. Ma non mi aspettavo che Mathematica avesse "oggetti disco".
Robin Ryder,

36 byte per ottenere il formato di output corretto:Append@@BoundingRegion[#,"MinDisk"]&
Roman

20

JavaScript (ES6),  298 ... 243  242 byte

Restituisce un array [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

Provalo online!

o vedere una versione formattata

Come?

Metodo

Per ogni coppia di punti , generiamo il cerchio cui diametro è .(A,B)(X,Y,r)AB

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

Per ogni tripla di punti distinti , generiamo il cerchio che circoscrive il triangolo .(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

Per ogni cerchio generato, testiamo se ogni punto è racchiuso al suo interno:(x,y)

(xX)2+(yY)2r

E alla fine restituiamo il più piccolo cerchio valido.

Implementazione

Nel codice JS, la formula da calcolare per il cerchio circoscritto del triangolo è leggermente semplificata. Supponendo , definiamo , portando a:(X,Y)d0q=Cx2+Cy2

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

In questo modo, la funzione di supporto richiede solo due parametri per calcolare ciascuna coordinata:F(j,k)

  • (ByCy,CyAy) perX
  • (CxBx,AxCx) perY

Il terzo parametro utilizzato nella (cioè la sua effettiva tesi ) viene utilizzato per calcolare quando , il che significa che il triangolo è degenere e dobbiamo tentare di utilizzare il diametro invece.Fs(X,Y)d=0


forse scrivere un ottimizzatore numerico di tipo Newton è più breve ...
qwr

Hai una prova di correttezza? Vedo che questo è un approccio plausibile, ma più di questo sembra richiedere un bel po 'di lavoro.
Peter Taylor,

3
@PeterTaylor L'algoritmo è citato su Wikipedia: un ingenuo algoritmo risolve il problema nel tempo O (n ^ 4) testando i cerchi determinati da tutte le coppie e triple di punti . Ma sfortunatamente, non esiste alcun collegamento a una prova.
Arnauld

Incontrerà il problema della precisione senza soluzione?
l4m2

1
@Arnauld Se hai bisogno di alcuni numeri strani da raggiungere, posso dire che va bene; Se anche fallire in una situazione facile che potrebbe essere un problema
l4m2

14

R , 59 byte

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

Provalo online!

Accetta input come vettore di coordinate complesse. Modè la distanza (modulo) nel piano complesso ed nlmè una funzione di ottimizzazione: trova la posizione del centro (output as estimate) che minimizza la distanza massima ai punti di input e fornisce la distanza corrispondente (output as minimum), ovvero il raggio . Preciso a 3-6 cifre; il piè di pagina TIO arrotonda l'output a 2 cifre.

nlmaccetta un vettore numerico come input: l' y%*%c(1,1i)azienda lo converte in un complesso.


9

Gelatina , 100 98 byte

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

Provalo online!

Contrariamente alla mia risposta in lingua Wolfram , Jelly ha bisogno di un sacco di codice per raggiungere questo obiettivo (a meno che non mi manchi qualcosa!). Questo programma completo prende l'elenco dei punti come argomento e restituisce il centro e il raggio del cerchio più piccolo racchiuso. Genera cerchi per tutte le serie di tre punti e diametri per tutte le serie di due punti, controlli che includono tutti i punti e quindi seleziona quello con il raggio più piccolo.

Il codice per il bit make_circumcircle è stato ispirato dal codice di questo sito , a sua volta ispirato a Wikipedia.


1
Non capisco questo codice, ma piuttosto che diametri e cerchi separati, potresti generare tutti i set di tre punti con duplicati e filtrare gli elenchi di tre punti identici?
Lirtosiast

2

APL (NARS), 348 caratteri, 696 byte

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

Questa è una 'implementazione' di formule nella soluzione Arnauld ... Risultati e commenti:

  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: trova la combinazione di alfa ojet nel set omega

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

((X, Y), r) da ora rappresentano una circonferenza del raggio r e centrano in (XY).

c: trova se il punto in ⍺ è all'interno della circonferenza ((XY) r) in ⍵ (risultato <= 0) ot è esterno (risultato> 0) Nel caso di input di circonferenza in ⍵ è ⍬ come input, esso restituirebbe 1 (fuori dalla circonferenza) ogni possibile input in ⍺.

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

p: se ⍵ è un array di ((XY) r); per ciascuna delle ((XY) r) in ⍵ scrive 1 se tutti i punti dell'array ⍺ sono interni a ((XY) r) altrimenti scrive 0 NB Ecco qualcosa che non va perché ho dovuto arrotondare a epsilon = 1e¯ 13. in altre parole in casi limite di punti nel piano (e probabilmente costruito apposta) non è assicurato al 100%

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

s2: da 2 punti in ⍵, restituisce la circonferenza nel formato ((XY) r) che ha diametro in quei 2 punti

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

s3: da 3 punti restituisce la circonferenza nel formato ((XY) r) passando attraverso quei tre punti Se ci sono problemi (per esempio i punti sono allineati), fallirebbe e ritornerebbe ⍬.

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

nota che -. × trova il determinante di una matrice nxn e

  ⎕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: da n punti in ⍵, trova le circonferenze di tipo di quelle trovate da s2 o quelle di tipo s3 che contengono tutti gli n punti.

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

s1: dall'insieme trovato dalla s sopra calcola quelli che hanno raggio minimo e restituisce il primo che ha raggio minimo. I tre numeri come matrici (la prima e la seconda coordinata sono le coordinate del centro, mentre la terza è il raggio della circonferenza trovata).

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

2

Python 2 (PyPy) , 244 242 byte

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]

Provalo online!

Questo utilizza l'algoritmo O (n ^ 4) a forza bruta, che scorre attraverso ogni coppia e triangolo di punti, calcola il centro e mantiene il centro che necessita del raggio più piccolo per racchiudere tutti i punti. Calcola il circumcentro di 3 punti trovando l'intersezione di due bisettrici perpendicolari (o, se due punti sono uguali, utilizza il punto medio con il terzo).


Benvenuti in PPCG! Dato che stai usando Python 2, puoi salvare 1 byte convertendo i due spazi sulla 5a riga in una scheda.
Stephen

P={x+y*1j for x,y in input()}salva anche 2 byte.
Mr. Xcoder,

1

Python 212 190 byte

Questa soluzione non è corretta e ora devo lavorare, quindi non ho tempo per ripararla.

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

Provalo online!

Ho capito quali sono i due punti più lontani e poi ho generato un'equazione per un cerchio basato su quei punti!

So che questo non è il più corto in Python, ma è il migliore che potessi fare! Anche questo è il mio primo tentativo di fare uno di questi, quindi per favore sii comprensivo!


2
Questo può essere giocato ancora un po '. Ad esempio, è possibile ridurre if g>c:\n c=g\n d=[e,f]a if g>c:c=g;d=[e,f], risparmiando un sacco di spazio bianco. Non penso che sia necessario inizializzare d in anticipo, anche usando due variabili e E,F=e,fnella riga 10 renderà il tuo printmolto più breve. Penso che una soluzione con maxe una comprensione dell'elenco sarebbe ancora più breve di due loop, ma potrei sbagliarmi. Purtroppo, tuttavia, la tua soluzione non è corretta. Per i punti (-1,0), (0,1,41), (0,5, 0), (1,0) la soluzione calcola un cerchio attorno a 0 con raggio 1. Ma il punto (1, 1,41) non è in questo cerchio.
Black Owl Kai,

Benvenuto! La ringrazio per la risposta. Come indicato nel commento sopra, la soluzione non è corretta. Un suggerimento per la soluzione di forza bruta: il cerchio più piccolo possibile tocca due punti o tre punti. Puoi iniziare a calcolare l'equazione per il cerchio che tocca ciascuna coppia di punti e controllare se ce n'è uno che racchiude tutti i punti. Quindi calcola l'equazione per il cerchio che tocca ogni tripletta di punti e controlla se ce n'è una che racchiude tutti i punti. Una volta ottenuti tutti i cerchi possibili, seleziona quello con il raggio più piccolo.
Barranka,

1
Bene, proverò a farlo funzionare, e poi aggiornerò la risposta. Devo capire come verificare se un punto è contenuto in un cerchio.
Ben Morrison,

Una volta che hai il centro e il raggio del cerchio, controlla se la distanza tra il centro e ciascun punto è minore o uguale al raggio; se questa condizione è vera per tutti i punti, quel cerchio è un candidato
Barranka il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.