Trova il centroide di un poligono


16

Da Wikipedia :

Il centroide di un poligono chiuso non autointersecante definito da n vertici ( x 0 , y 0 ), ( x 1 , y 1 ), ..., ( x n - 1 , y n − 1 ) è il punto ( C x , C y ), dove

Formula per il centroide

e dove A è l'area firmata del poligono,

Formula per area del poligono

In queste formule si presume che i vertici siano numerati in ordine di occorrenza lungo il perimetro del poligono. Inoltre, si presume che il vertice ( x n , y n ) sia lo stesso di ( x 0 , y 0 ), il che significa che i + 1 nell'ultimo caso devono essere circondati da i = 0 . Notare che se i punti sono numerati in senso orario, l'area A , calcolata come sopra, avrà un segno negativo; ma le coordinate del centroide saranno corrette anche in questo caso.


  • Dato un elenco di vertici in ordine (in senso orario o antiorario), trova il centroide del poligono chiuso non autointersecante rappresentato dai vertici.
    • Se aiuta, puoi supporre che l'input sia solo CW o solo CCW. Dillo nella tua risposta se lo richiedi.
  • Le coordinate non devono essere numeri interi e possono contenere numeri negativi.
  • L'input sarà sempre valido e conterrà almeno tre vertici.
  • Gli input devono essere gestiti solo per adattarsi al tipo di dati in virgola mobile nativo della tua lingua.
  • Si può presumere che i numeri di input conterranno sempre un punto decimale.
  • Si può presumere che gli interi di input finiscano in .o.0 .
  • È possibile utilizzare numeri complessi per l'input.
  • L'output deve essere accurato al millesimo più vicino.

Esempi

[(0.,0.), (1.,0.), (1.,1.), (0.,1.)]        -> (0.5, 0.5)
[(-15.21,0.8), (10.1,-0.3), (-0.07,23.55)]  -> -1.727 8.017
[(-39.00,-55.94), (-56.08,-4.73), (-72.64,12.12), (-31.04,53.58), (-30.36,28.29), (17.96,59.17), (0.00,0.00), (10.00,0.00), (20.00,0.00), (148.63,114.32), (8.06,-41.04), (-41.25,34.43)]   -> 5.80104769975, 15.0673812762

Per vedere ogni poligono su un piano di coordinate, incolla le coordinate senza le parentesi quadre nel menu "Modifica" di questa pagina .

Ho confermato i miei risultati usando questo poligono Centroid Point Calculator , che è terribile. Non sono riuscito a trovarne uno in cui è possibile inserire tutti i vertici contemporaneamente o che non ha tentato di cancellare il -segno quando lo si digita per primo. Pubblicherò la mia soluzione Python per il tuo uso dopo che le persone avranno avuto la possibilità di rispondere.


La tecnica molto più semplice di calcolare la media di tutte le opere di xey per i primi due set, ma non per il terzo. Mi chiedo cosa faccia la differenza ...
ETHproductions

1
@ETHproductions Il terzo poligono non è convesso.
JungHwan Min

1
@ETHproductions Se si approssimano un cerchio con un poligono, è possibile spostare il punto medio arbitrariamente vicino a un punto sul cerchio utilizzando più punti vicini a quel punto, quasi senza influenzare il centroide e mantenendo il poligono convesso.
Christian Sievers,

2
@ETHproductions In realtà la convessità non è la ragione. La media di tutte le xs ymette tutto il peso nei vertici invece che distribuito sul corpo. Il primo funziona perché è regolare, quindi entrambi i metodi finiscono nel centro di simmetria. Il secondo funziona perché per i triangoli entrambi i metodi portano allo stesso punto.
Ton Hospel,

1
Possiamo usare numeri complessi per l'I / O?
xnor

Risposte:


16

Gelatina , 25 24 22 21 18 byte

S×3÷@×"
ṙ-żµÆḊçS€S

Applica la formula mostrata nel problema.

Salvato 3 byte con l'aiuto di @ Jonathan Allan.

Provalo online! oppure Verifica tutti i casi di test.

Spiegazione

S×3÷@×"  Helper link. Input: determinants on LHS, sum of pairs on RHS
S        Sum the determinants
 ×3      Multiply by 3
     ×"  Vectorized multiply between determinants and sums
   ÷@    Divide that by the determinant sum multipled by 3 and return

ṙ-żµÆḊçS€S  Main link. Input: 2d list of points
ṙ-          Rotate the list of points by 1 to the right
  ż         Interleave those with the original points
            This creates all overlapping slices of length 2
   µ        Start new monadic chain
    ÆḊ      Get the determinant of each slice
       S€   Get the sum of each slice (sum of pairs of points)
      ç     Call the helper link
         S  Sum and return

È possibile sostituire ṁL‘$ṡ2con ṙ1ż@ożṙ1$
Jonathan Allan il

@JonathanAllan Grazie, posso anche ruotare ṙ-żper evitare lo scambio e salvare un altro byte
miglia

Oh si, certamente!
Jonathan Allan,

17

Mathematica, 23 byte

RegionCentroid@*Polygon

Prendi QUELLO , Jelly!

Modifica: Uno non batte semplicemente Jelly ...

Spiegazione

Polygon

Genera un poligono con vertici nei punti specificati.

RegionCentroid

Trova il centroide del poligono.


2
Beh, mi hai battuto, ma probabilmente c'è un modo più breve di quello che ho, non ho ancora una completa comprensione di Jelly
miglia

3
@miles aw ... :(
JungHwan Min

4

J, 29 byte

2+/@(+/\(*%3*1#.])-/ .*\)],{.

Applica la formula mostrata nel problema.

uso

   f =: 2+/@(+/\(*%3*1#.])-/ .*\)],{.
   f 0 0 , 1 0 , 1 1 ,: 0 1
0.5 0.5
   f _15.21 0.8 , 10.1 _0.3 ,: _0.07 23.55
_1.72667 8.01667
   f _39 _55.94 , _56.08 _4.73 , _72.64 12.12 , _31.04 53.58 , _30.36 28.29 , 17.96 59.17 , 0 0 , 10 0 , 20 0 , 148.63 114.32 , 8.06 _41.04 ,: _41.25 34.43
5.80105 15.0674

Spiegazione

2+/@(+/\(*%3*1#.])-/ .*\)],{.  Input: 2d array of points P [[x1 y1] [x2 y2] ...]
                           {.  Head of P
                         ]     Get P
                          ,    Join, makes the end cycle back to the front
2                              The constant 2
2                      \       For each pair of points
                  -/ .*        Take the determinant
2    +/\                       Sum each pair of points
         *                     Multiply the sum of each pair by its determinant
          %                    Divide each by
             1#.]              The sum of the determinants
           3*                  Multiplied by 3
 +/@                           Sum and return

4

Massimi, 124 118 116 112 106 byte

f(l):=(l:endcons(l[1],l),l:sum([3,l[i-1]+l[i]]*determinant(matrix(l[i-1],l[i])),i,2,length(l)),l[2]/l[1]);

Non ho esperienza con Maxima, quindi qualsiasi suggerimento è il benvenuto.

Uso:

(%i6) f([[-15.21,0.8], [10.1,-0.3], [-0.07,23.55]]);
(%o6)              [- 1.726666666666668, 8.016666666666668]

3

Racchetta 420 byte

(let*((lr list-ref)(getx(lambda(i)(lr(lr l i)0)))(gety(lambda(i)(lr(lr l i)1)))(n(length l))(j(λ(i)(if(= i(sub1 n))0(add1 i))))
(A(/(for/sum((i n))(-(*(getx i)(gety(j i)))(*(getx(j i))(gety i))))2))
(cx(/(for/sum((i n))(*(+(getx i)(getx(j i)))(-(*(getx i)(gety(j i)))(*(getx(j i))(gety i)))))(* 6 A)))
(cy(/(for/sum((i n))(*(+(gety i)(gety(j i)))(-(*(getx i)(gety(j i)))(*(getx(j i))(gety i)))))(* 6 A))))
(list cx cy))

Ungolfed:

(define(f l)
  (let* ((lr list-ref)
         (getx (lambda(i)(lr (lr l i)0)))
         (gety (lambda(i)(lr (lr l i)1)))
         (n (length l))
         (j (lambda(i) (if (= i (sub1 n)) 0 (add1 i))))
         (A (/(for/sum ((i n))
                (-(* (getx i) (gety (j i)))
                  (* (getx (j i)) (gety i))))
              2))
         (cx (/(for/sum ((i n))
                 (*(+(getx i)(getx (j i)))
                   (-(*(getx i)(gety (j i)))
                     (*(getx (j i))(gety i)))))
               (* 6 A)))
         (cy (/(for/sum ((i n))
                 (*(+(gety i)(gety (j i)))
                   (-(*(getx i)(gety (j i)))
                     (*(getx (j i))(gety i)))))
               (* 6 A))))
    (list cx cy)))

test:

(f '[(-15.21 0.8)  (10.1 -0.3)  (-0.07 23.55)] ) 
(f '[(-39.00 -55.94)  (-56.08 -4.73)  (-72.64 12.12)  (-31.04 53.58) 
     (-30.36 28.29)  (17.96 59.17)  (0.00 0.00)  (10.00 0.00)  
     (20.00 0.00) (148.63 114.32)  (8.06 -41.04)  (-41.25 34.43)])

Produzione:

'(-1.7266666666666677 8.01666666666667)
'(5.8010476997538465 15.067381276150996)

3

R, 129 127 byte

function(l){s=sapply;x=s(l,`[`,1);y=s(l,`[`,2);X=c(x[-1],x[1]);Y=c(y[-1],y[1]);p=x*Y-X*y;c(sum((x+X)*p),sum((y+Y)*p))/sum(p)/3}

Funzione senza nome che accetta come input un elenco R di tuple. L'equivalente denominato può essere chiamato usando ad esempio:

f(list(c(-15.21,0.8),c(10.1,-0.3),c(-0.07,23.55)))

Ungolfed e spiegato

f=function(l){s=sapply;                           # Alias for sapply
              x=s(l,`[`,1);                       # Split list of tuples into vector of first elements
              y=s(l,`[`,2);                       # =||= but for second element 
              X=c(x[-1],x[1]);                    # Generate a vector for x(i+1)
              Y=c(y[-1],y[1]);                    # Generate a vector for y(i+1)
              p=x*Y-X*y;                          # Calculate the outer product used in both A, Cx and Cy
              c(sum((x+X)*p),sum((y+Y)*p))/sum(p)/3    # See post for explanation
}

Il passaggio finale ( c(sum((x+X)*p),sum((y+Y)*p))/sum(p)*2/6) è un modo vettoriale di calcolare entrambi Cxe Cy. La somma nelle formule per Cxe Cyviene memorizzata in un vettore e conseguentemente divisa per la "somma in A" *2/6. Per esempio:

(SUMinCx, SUMinCy) / SUMinA / 3

e quindi implicitamente stampato.

Provalo su R-violino


*2/6potrebbe essere /3?
mbomb007,

@ mbomb007 È così evidentemente minuzioso, immagino di essere stato coinvolto nel golf dell'altra parte. / scrollata di spalle
Billywob,

Elegante, mi piace il tuo modo di sapplygestire quelle liste! Potrebbe esserci spazio per il golf qui, non sono sicuro di quanto sia flessibile l'ingresso consentito. Se ti è permesso inserire solo una sequenza di coordinate, ad esempio c(-15.21,0.8,10.1,-0.3,-0.07,23.55), puoi salvare 17 byte sostituendo le prime righe della tua funzione con y=l[s<-seq(2,sum(1|l),2)];x=l[-s];. Cioè, l'impostazione yper essere ogni elemento di indicizzazione pari le xper ogni elemento di indicizzazione dispari.
rturnbull,

Ancora meglio, tuttavia, sarebbe se possiamo inserire una matrice (o matrice), come matrix(c(-15.21,0.8,10.1,-0.3,-0.07,23.55),2), come può essere l'inizio della tua funzione x=l[1,];y=l[2,];, che consente di risparmiare 35 byte. (La matrice di input potrebbe essere trasposta, nel qual caso x=l[,1];y=l[,2];.) Naturalmente, la soluzione più semplice di tutte è se i punti xe ysono semplicemente inseriti come vettori separati function(x,y), ma non credo che sia permesso ...
rturnbull

@rturnbull Ho chiesto a OP di commentare e in particolare voleva un elenco di tuple (molto scomodo in R ovviamente), quindi non credo che l'approccio a matrice sia permesso. E anche se lo fosse, l'input dovrebbe essere la parte vettoriale (cioè c(...)) e la conversione della matrice dovrebbe essere fatta all'interno della funzione.
Billywob,

2

Python, 156 127 byte

def f(p):n=len(p);p=p+p[:1];i=s=0;exec'd=(p[i].conjugate()*p[i+1]).imag;s+=d;p[i]=(p[i]+p[i+1])*d;i+=1;'*n;print sum(p[:n])/s/3

Ungolfed:

def f(points):
  n = len(points)
  points = points + [points[0]]
  determinantSum = 0
  for i in range(n):
    determinant = (points[i].conjugate() * points[i+1]).imag
    determinantSum += determinant
    points[i] = (points[i] + points[i+1]) * determinant
  print sum(points[:n]) / determinantSum / 3

Ideone.

Questo prende ogni coppia di punti [x, y]come un numero complesso x + y*je genera il centroide risultante come un numero complesso nello stesso formato.

Per la coppia di punti [a, b]e [c, d], il valore a*d - b*cnecessario per ciascuna coppia di punti può essere calcolato dal determinante della matrice

| a b |
| c d |

Usando l'aritmetica complessa, i valori complessi a + b*je c + d*jpossono essere usati come

conjugate(a + b*j) * (c + d*j)
(a - b*j) * (c + d*j)
(a*c + b*d) + (a*d - b*c)*j

Si noti che la parte immaginaria è equivalente al determinante. Inoltre, l'utilizzo di valori complessi consente di sommare facilmente i punti in termini di componenti nelle altre operazioni.


2

R + sp (46 byte)

Presuppone che il sppacchetto sia installato ( https://cran.r-project.org/web/packages/sp/ )

Prende un elenco di vertici, (ad esempio list(c(0.,0.), c(1.,0.), c(1.,1.), c(0.,1.)))

Sfrutta il fatto che il "labpt" di un poligono è il centroide.

function(l)sp::Polygon(do.call(rbind,l))@labpt

2

JavaScript (ES6), 102

Implementazione diretta della formula

l=>[...l,l[0]].map(([x,y],i)=>(i?(a+=w=t*y-x*u,X+=(t+x)*w,Y+=(u+y)*w):X=Y=a=0,t=x,u=y))&&[X/3/a,Y/3/a]

Test

f=
l=>[...l,l[0]].map(([x,y],i)=>(i?(a+=w=t*y-x*u,X+=(t+x)*w,Y+=(u+y)*w):X=Y=a=0,t=x,u=y))&&[X/3/a,Y/3/a]

function go()
{
  var c=[],cx,cy;
  // build coordinates array
  I.value.match(/-?[\d.]+/g).map((v,i)=>i&1?t[1]=+v:c.push(t=[+v]));
  console.log(c+''),
  [cx,cy]=f(c);
  O.textContent='CX:'+cx+' CY:'+cy;
  // try to display the polygon
  var mx=Math.max(...c.map(v=>v[0])),
    nx=Math.min(...c.map(v=>v[0])),
    my=Math.max(...c.map(v=>v[1])),
    ny=Math.min(...c.map(v=>v[1])),  
    dx=mx-nx, dy=my-ny,
    ctx=C.getContext("2d"),
    cw=C.width, ch=C.height,
    fx=(mx-nx)/cw, fy=(my-ny)/ch, fs=Math.max(fx,fy)
  C.width=cw
  ctx.setTransform(1,0,0,1,0,0);
  ctx.beginPath();
  c.forEach(([x,y],i)=>ctx.lineTo((x-nx)/fs,(y-ny)/fs));
  ctx.closePath();
  ctx.stroke();
  ctx.fillStyle='#ff0000';
  ctx.fillRect((cx-nx)/fs-2,(cy-ny)/fs-2,5,5);
}
go()
#I { width:90% }
#C { width:90%; height:200px;}
<input id=I value='[[-15.21,0.8], [10.1,-0.3], [-0.07,23.55]]'>
<button onclick='go()'>GO</button>
<pre id=O></pre>
<canvas id=C></canvas>


1

Python 2, 153 byte

Non utilizza numeri complessi.

P=input()
A=x=y=0;n=len(P)
for i in range(n):m=-~i%n;a=P[i][0];b=P[i][1];c=P[m][0];d=P[m][1];t=a*d-b*c;A+=t;x+=t*(a+c);y+=t*(b+d)
k=1/(3*A);print x*k,y*k

Provalo online

Ungolfed:

def centroid(P):
    A=x=y=0
    n=len(P)
    for i in range(n):
        m=-~i%n
        x0=P[i][0];y0=P[i][1]
        x1=P[m][0];y1=P[m][1]
        t = x0*y1 - y0*x1
        A += t/2.
        x += t * (x0 + x1)
        y += t * (y0 + y1)
    k = 1/(6*A)
    x *= k
    y *= k
    return x,y

1

In realtà, 45 40 39 byte

Questo utilizza un algoritmo simile alla risposta Jelly di miglia . C'è un modo più breve per calcolare i determinanti usando un prodotto punto, ma al momento c'è un bug con il prodotto punto di Actually in cui non funzionerà con gli elenchi di float. Suggerimenti di golf benvenuti. Provalo online!

;\Z♂#;`i¥`M@`i│N@F*)F@N*-`M;Σ3*)♀*┬♂Σ♀/

Ungolfing

         Implicit input pts.
;\       Duplicate pts, rotate right.
Z        Zip rot_pts and pts together.
♂#       Convert the iterables inside the zip to lists
         (currently necessary due to a bug with duplicate)
;        Duplicate the zip.
`...`M   Get the sum each pair of points in the zip.
  i        Flatten the pair to the stack.
  ¥        Pairwise add the two coordinate vectors.
@        Swap with the other zip.
`...`M   Get the determinants of the zip.
  i│       Flatten to stack and duplicate entire stack.
           Stack: [a,b], [c,d], [a,b], [c,d]
  N@F*)    Push b*c and move it to BOS.
  F@N*     Push a*d.
  -        Get a*d-b*c.
;Σ3*)    Push 3 * sum(determinants) and move it to BOS.
♀*       Vector multiply the determinants and the sums.
┬        Transpose the coordinate pairs in the vector.
♂Σ       Sum the x's, then the y's.
♀/       Divide the x and y of this last coordinate pair by 3*sum(determinants).
         Implicit return.

Una versione più breve e non competitiva

Questa è un'altra versione a 24 byte che utilizza numeri complessi. Non è competitivo perché si basa su correzioni di bug che postdatano questa sfida. Provalo online!

;\│¥)Z`iá*╫@X`M;Σ3*)♀*Σ/

Ungolfing

         Implicit input a list of complex numbers, pts.
;\       Duplicate pts, rotate right.
│        Duplicate stack. Stack: rot_pts, pts, rot_pts, pts.
¥)       Pairwise sum the two lists of points together and rotate to BOS.
Z        Zip rot_pts and pts together.
`...`M   Map the following function over the zipped points to get our determinants.
  i        Flatten the list of [a+b*i, c+d*i].
  á        Push the complex conjugate of a+bi, i.e. a-b*i.
  *        Multiply a-b*i by c+d*i, getting (a*c+b*d)+(a*d-b*c)*i.
           Our determinant is the imaginary part of this result.
  ╫@X      Push Re(z), Im(z) to the stack, and immediately discard Re(z).
           This map returns a list of these determinants.
;        Duplicate list_determinants.
Σ3*)     Push 3 * sum(list_determinants) and rotate that to BOS.
♀*Σ      Pairwise multiply the sums of pairs of points and the determinants and sum.
/        Divide that sum by 3*sum(list_determinants).
         Implicit return.

1

C ++ 14, 241 byte

struct P{float x;float y;};
#define S(N,T)auto N(P){return 0;}auto N(P a,P b,auto...V){return(T)*(a.x*b.y-b.x*a.y)+N(b,V...);}
S(A,1)S(X,a.x+b.x)S(Y,a.y+b.y)auto f(auto q,auto...p){auto a=A(q,p...,q)*3;return P{X(q,p...,q)/a,Y(q,p...,q)/a};}

L'output è la struttura di supporto P,

Ungolfed:

 //helper struct
struct P{float x;float y;};

//Area, Cx and Cy are quite similar
#define S(N,T)\  //N is the function name, T is the term in the sum
auto N(P){return 0;} \   //end of recursion for only 1 element
auto N(P a,P b,auto...V){ \ //extract the first two elements
  return (T)*(a.x*b.y-b.x*a.y) //compute with a and b
         + N(b,V...); \        //recursion without first element
}

//instantiate the 3 formulas
S(A,1)
S(X,a.x+b.x)
S(Y,a.y+b.y)


auto f(auto q,auto...p){
  auto a=A(q,p...,q)*3; //q,p...,q appends the first element to the end
  return P{X(q,p...,q)/a,Y(q,p...,q)/a};
}

Uso:

f(P{0.,0.}, P{1.,0.}, P{1.,1.}, P{0.,1.})
f(P{-15.21,0.8}, P{10.1,-0.3}, P{-0.07,23.55})

1

Clojure, 177 156 143 byte

Aggiornamento: Invece di un callback sto usando [a b c d 1]come funzione e l'argomento è solo un elenco di indici per questo vettore. 1viene utilizzato come valore sentinella durante il calcolo A.

Aggiornamento 2: non precalcolato Aalet , utilizzando (rest(cycle %))per ottenere offset dei vettori di input di uno.

#(let[F(fn[I](apply +(map(fn[[a b][c d]](*(apply +(map[a b c d 1]I))(-(* a d)(* c b))))%(rest(cycle %)))))](for[i[[0 2][1 3]]](/(F i)(F[4])3)))

Versione originale:

#(let[F(fn[L](apply +(map(fn[[a b][c d]](*(L[a b c d])(-(* a d)(* c b))))%(conj(subvec % 1)(% 0)))))A(*(F(fn[& l]1))3)](map F[(fn[v](/(+(v 0)(v 2))A))(fn[v](/(+(v 1)(v 3))A))]))

A livello di golf meno:

(def f (fn[v](let[F (fn[l](apply +(map
                                    (fn[[a b][c d]](*(l a b c d)(-(* a d)(* c b))))
                                    v
                                    (conj(subvec v 1)(v 0)))))
                  A (* (F(fn[& l] 1)) 3)]
                [(F (fn[a b c d](/(+ a c)A)))
                 (F (fn[a b c d](/(+ b d)A)))])))

Crea una funzione di supporto Fche implementa la somma con qualsiasi callback l. Per Ail callback ritorna costantemente 1mentre le coordinate X e Y hanno le loro funzioni. (conj(subvec v 1)(v 0))elimina il primo elemento e si aggiunge alla fine, in questo modo è facile tenere traccia di x_ie x_(i+1). Forse c'è ancora qualche ripetizione da eliminare, specialmente alla fine (map F[....

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.