Bir Çokgenin Centroid'ini Bulun


16

Gönderen Wikipedia :

N köşe noktası ( x 0 , y 0 ), ( x 1 , y 1 ), ..., ( x n - 1 , y n − 1 ) ile tanımlanan kendiliğinden kesişmeyen kapalı bir çokgenin sentroidi nokta ( C x , C y ), burada

Centroid için formül

ve burada A çokgenin imzalı alandır,

Çokgen Alanı için Formül

Bu formüllerde, köşelerin çokgenin çevresi boyunca meydana gelme sırasına göre numaralandırıldığı varsayılmaktadır. Ayrıca, tepe noktasının ( x n , y n ) ( x 0 , y 0 ) ile aynı olduğu varsayılır , yani son durumda i + 1 i = 0'a çevrilmelidir . Noktalar saat yönünde sıralanırsa, yukarıdaki gibi hesaplanan A alanının negatif bir işareti olacağını unutmayın; ancak bu durumda bile centroid koordinatları doğru olacaktır.


  • Sırayla bir köşe listesi verildiğinde (saat yönünde veya saat yönünün tersine), köşelerle temsil edilen kendiliğinden kesişmeyen kapalı çokgenin centroidini bulun.
    • Bu yardımcı olursa, girdinin yalnızca CW veya yalnızca CCW olduğunu varsayabilirsiniz. Buna ihtiyacınız varsa cevabınızda söyleyin.
  • Koordinatların tamsayı olması gerekmez ve negatif sayılar içerebilir.
  • Girdi her zaman geçerli olur ve en az üç köşe içerir.
  • Girişlerin yalnızca dilinizin yerel kayan noktalı veri türüne uygun şekilde işlenmesi gerekir.
  • Girdi numaralarının her zaman ondalık nokta içereceğini varsayabilirsiniz.
  • Giriş tamsayılarının .veya ile biteceğini varsayabilirsiniz .0.
  • Giriş için karmaşık sayılar kullanabilirsiniz.
  • Çıktı en yakın binde bire doğru olmalıdır.

Örnekler

[(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

Her çokgeni bir koordinat düzleminde görmek için, koordinatları köşeli parantez olmadan bu sayfanın "Düzenle" menüsüne yapıştırın .

Sonuçlarımı korkunç olan bu Poligon Centroid Nokta Hesaplayıcı'yı kullanarak onayladım . Tüm köşeleri bir kerede girebileceğiniz veya ilk kez yazdığınızda -işaretinizi silmeye çalışmayan birini bulamadım . Python çözümümü, insanların cevap verme şansı bulduktan sonra kullanımınız için göndereceğim.


İlk iki set için tüm x ve y çalışmalarını ortalamanın çok daha basit tekniği, ancak üçüncü set için değil. Farkı ne merak ediyorum ...
ETHproductions

1
@ETHproductions Üçüncü çokgen dışbükey değildir.
JungHwan Min

1
@ETHproductions Bir çokgene sahip bir daireye yaklaşırsanız, orta noktayı neredeyse etkilemezken ve çokgen dışbükeyliğini korurken, ortalama noktayı o noktaya yakın daha fazla nokta kullanarak daire üzerindeki bir noktaya rastgele taşıyabilirsiniz.
Christian Sievers

2
@ETHproductions Aslında dışbükeylik nedeni değil . Tüm xs ve ys ortalamalarını almak , tüm ağırlığı gövdeye dağıtmak yerine köşelere yerleştirir. İlki düzenli olduğu için işe yarıyor, bu nedenle her iki yöntem de simetri merkezinde sona eriyor. İkincisi çalışır çünkü üçgenler için her iki yöntem de aynı noktaya götürür.
Ton Hospel

1
G / Ç için karmaşık sayılar kullanabilir miyiz?
xnor

Yanıtlar:


16

Jöle , 25 24 22 21 18 bayt

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

Problemde gösterilen formülü uygular.

@ Jonathan Allan'ın yardımıyla 3 bayt kurtardı.

Çevrimiçi deneyin! veya Tüm test senaryolarını doğrulayın.

açıklama

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

Sen yerini alabilir ṁL‘$ṡ2ile ṙ1ż@veyażṙ1$
Jonathan Allan

@JonathanAllan Teşekkürler, ayrıca takastan ṙ-żkaçınmak ve başka bir bayt kaydetmek için döndürebilirim
mil

Evet, elbette!
Jonathan Allan

17

Mathematica, 23 bayt

RegionCentroid@*Polygon

Al OLDUĞUNU Jelly!

Edit: Bir sadece Jelly yenmek değil ...

açıklama

Polygon

Belirtilen noktalarda köşeleri olan bir çokgen oluşturun.

RegionCentroid

Çokgenin sentroidini bulun.


2
Beni dövüyorsun, ama muhtemelen sahip olduğumdan daha kısa bir yol var,
mil

3
@miles aw ... :(
JungHwan Min

4

J, 29 bayt

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

Problemde gösterilen formülü uygular.

kullanım

   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

açıklama

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

Maksima, 124111 116112106 bayt

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

Maxima ile tecrübeli değilim, bu yüzden herhangi bir ipucu bekliyoruz.

Kullanımı:

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

3

Raket 420 bayt

(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 yapmak:

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

Çıktı:

'(-1.7266666666666677 8.01666666666667)
'(5.8010476997538465 15.067381276150996)

3

R 129 127 bayt

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}

Adların R-listesini giriş olarak alan isimsiz fonksiyon. Adı verilen eşdeğeri örn.

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

Açık ve açık

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
}

Son adım ( c(sum((x+X)*p),sum((y+Y)*p))/sum(p)*2/6) hem Cxve ' nin hesaplanmasının vektörize edilmiş bir yoludur Cy. Formüllerde toplam Cxve Cybir vektör içinde depolanır ve sonuç olarak "de toplama bölünür A" *2/6. Örneğin:

(SUMinCx, SUMinCy) / SUMinA / 3

ve ardından örtük olarak yazdırılır.

R-fiddle'da deneyin


*2/6olabilir /3mi?
mbomb007

@ mbomb007 Çok zahmetli bir şekilde açık, sanırım diğer kısmı golf yaparken yakaladım. omuz silkme
Billywob

Zarif, sapplybu listelerle uğraşmaktan hoşlanıyorum ! Burada golf için bir kapsam olabilir, izin verilen girdinin ne kadar esnek olduğundan emin değilim. Sadece bir dizi koordinat girmenize izin verilirse c(-15.21,0.8,10.1,-0.3,-0.07,23.55), işlevinizin ilk satırlarını değiştirerek 17 bayt kaydedebilirsiniz y=l[s<-seq(2,sum(1|l),2)];x=l[-s];. Yani, yher çift dizinli öğe için ayar lve xher tek dizinli öğe olacak şekilde ayarlama.
16:13, rturnbull

Daha da iyisi, bir matris (veya dizi) girebilsek, örneğin matrix(c(-15.21,0.8,10.1,-0.3,-0.07,23.55),2), o zaman fonksiyonunuzun başlangıcı olabilir x=l[1,];y=l[2,];, bu da 35 byte tasarruf sağlar. (Bu durumda giriş matrisi aktarılabilir x=l[,1];y=l[,2];.) Tabii ki, en kolay çözüm xve ynoktaları sadece ayrı vektörler olarak function(x,y)
giriliyorsa

@ rturnbull Ben yorum OP istedi ve özellikle tuples (tabii ki R çok rahatsız edici) bir listesini istedi bu yüzden matris yaklaşım izin verilmiyor sanmıyorum. Ve öyle olsa bile, girdinin vektör kısmı (yani c(...)) olması ve matris dönüşümünün fonksiyon içinde yapılması gerekirdi.
Billywob

2

Python, 156127 bayt

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

Boşver.

Bu, her bir nokta çiftini [x, y]karmaşık bir sayı olarak alır x + y*jve elde edilen centroidi aynı formatta karmaşık bir sayı olarak çıkarır.

Nokta çifti [a, b]ve her bir nokta çifti için gereken [c, d]değer a*d - b*cmatrisin determinantından hesaplanabilir.

| a b |
| c d |

Kompleks değerler, karmaşık aritmetik kullanılması a + b*jve c + d*jolarak kullanılabilir

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

Hayali kısmın determinant ile eşdeğer olduğuna dikkat edin. Ayrıca, karmaşık değerlerin kullanılması, noktaların diğer işlemlerde kolayca bileşen olarak toplanmasına olanak tanır.


2

R + sp (46 bayt)

spPaketin kurulu olduğunu varsayar ( https://cran.r-project.org/web/packages/sp/ )

Köşelerin listesini alır (örneğin list(c(0.,0.), c(1.,0.), c(1.,1.), c(0.,1.)))

Bir Çokgenin "labpt" inin sentroid olduğu gerçeğinden faydalanır.

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

2

JavaScript (ES6), 102

Formülün doğrudan uygulanması

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]

Ölçek

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 bayt

Karmaşık sayılar kullanmaz.

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

Çevrimiçi deneyin

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

Aslında, 45 40 39 bayt

Bu, mil 'Jelly cevabına benzer bir algoritma kullanır . Bir nokta ürünü kullanarak belirleyicileri hesaplamanın daha kısa bir yolu vardır, ancak şu anda Actually'nin nokta ürününde, yüzer listelerle çalışmadığı bir hata var. Golf önerileri hoş geldiniz. Çevrimiçi deneyin!

;\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.

Daha kısa, rekabetçi olmayan bir sürüm

Bu, karmaşık sayılar kullanan başka bir 24 baytlık sürümdür. Rekabetçi değil çünkü bu meydan okumayı sonlandıran hata düzeltmelerine dayanıyor. Çevrimiçi deneyin!

;\│¥)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 bayt

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

Çıktı yardımcı yapıdır 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};
}

Kullanımı:

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 bayt

Güncelleme: yerine [a b c d 1]bir işlev olarak kullanıyorum ve argüman sadece bu vektör dizinlerin bir listesidir. 1hesaplanırken sentinel değeri olarak kullanılırA .

Güncelleme 2: precalculating değil Ade letkullanarak, (rest(cycle %))giriş vektörler birer ofset olsun.

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

Orijinal versiyon:

#(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))]))

Daha az golf sahasında:

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

FHerhangi bir geri arama ile toplamı uygulayan bir yardımcı işlev oluşturur l. İçin Asürekli geri arama dönüşleri 1X ve Y koordinatlarına oysa kendi işlevlere sahiptir. (conj(subvec v 1)(v 0))Bu yolu takip etmek kolaydır, ilk elemanını düşer ve sonuna kadar ekler x_ive x_(i+1). Belki de ortadan kaldırılması gereken bazı tekrarlar vardır, özellikle de sonunda (map F[....

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.