19
\$\begingroup\$

Here is a quick Monday morning challenge...

Write a function or program in the least number of bytes that:

  • Takes as input a list of [x,y] coordinates
  • Takes as input a list of the [x,y] coordinates' respective masses
  • Outputs the calculated center of mass in the form of [xBar,yBar].

Note:

  • Input can be taken in any form, as long as an array is used.

The center of mass can be calculated by the following formula: Center of Mass calculations

In plain English...

  • To find xBar, multiply each mass by its respective x coordinate, sum the resulting list, and divide it by the sum of all masses.
  • To find yBar, multiply each mass by its respective y coordinate, sum the resulting list, and divide it by the sum of all masses.

Trivial Python 2.7 example:

def center(coord, mass):
    sumMass = float(reduce(lambda a, b: a+b, mass))
    momentX = reduce(lambda m, x: m+x, (a*b for a, b in zip(mass, zip(*coord)[0])))
    momentY = reduce(lambda m, y: m+y, (a*b for a, b in zip(mass, zip(*coord)[1])))
    xBar = momentX / sumMass
    yBar = momentY / sumMass
    return [xBar, yBar]

Test Cases:

> center([[0, 2], [3, 4], [0, 1], [1, 1]], [2, 6, 2, 10])
[1.4, 2.0]

> center([[3, 1], [0, 0], [1, 4]], [2, 4, 1])
[1.0, 0.8571428571428571]

This is code-golf, so the least number of bytes wins!

\$\endgroup\$
14
  • \$\begingroup\$ Since this is just "compute a weighted average of vectors" I'd be quite surprised if we haven't done it before. (At the moment, I can't find anything though.) \$\endgroup\$ Commented Mar 7, 2016 at 14:09
  • \$\begingroup\$ @MartinBüttner I looked as well, and could not find any. If this is a dupe, feel free to close it though. \$\endgroup\$
    – MrPublic
    Commented Mar 7, 2016 at 14:15
  • \$\begingroup\$ Can the input be taken in the other order? Or in the form: [x,y,m],[x,y,m]...? \$\endgroup\$ Commented Mar 7, 2016 at 14:21
  • \$\begingroup\$ @FryAmTheEggman Question edited for valid inputs. \$\endgroup\$
    – MrPublic
    Commented Mar 7, 2016 at 14:28
  • \$\begingroup\$ @MrPublic: What about [(x1,y1,m1), (x2,y2,m2)], e.g. a list of tuples? Or doesn't it matter whether the arguments are tuples, lists or arrays? What about three lists/arrays? \$\endgroup\$
    – Zeta
    Commented Mar 7, 2016 at 14:33

18 Answers 18

21
\$\begingroup\$

MATL, 6 5 bytes

ys/Y*

Input format is a row vector with the masses, then a two-column matrix with the coordinates (in which spaces or commas are optional).

  • First example:

    [2, 6, 2, 10]
    [0,2; 3,4; 0,1; 1,1]
    
  • Second example:

    [2, 4, 1]
    [3,1; 0,0; 1,4]
    

Try it online!

Explanation

Let m denote the vector of masses (first input) and c the matrix of coordinates (second input).

y     % implicitly take two inputs. Duplicate the first.
      % (Stack contains, bottom to top: m, c, m)
s     % sum of m.
      % (Stack: m, c, sum-of-m)
/     % divide.
      % (Stack: m, c-divided-by-sum-of-m)
Y*    % matrix multiplication.
      % (Stack: final result)
      % implicitly display
\$\endgroup\$
2
  • \$\begingroup\$ y is pretty useful!! +1 \$\endgroup\$
    – David
    Commented Mar 7, 2016 at 23:20
  • \$\begingroup\$ @David Yeah! Combined with implicit input, it does a lot of things in this case :-) \$\endgroup\$
    – Luis Mendo
    Commented Mar 7, 2016 at 23:22
7
\$\begingroup\$

Mathematica, 10 bytes

#.#2/Tr@#&

Example:

In[1]:= #.#2/Tr@#&[{2,6,2,10},{{0,2},{3,4},{0,1},{1,1}}]

Out[1]= {7/5, 2}
\$\endgroup\$
1
  • 1
    \$\begingroup\$ I never used Dot. But I will after seeing your usage above! \$\endgroup\$
    – DavidC
    Commented Mar 8, 2016 at 18:43
7
\$\begingroup\$

Mathcad, 19 "bytes"

enter image description here

  • Uses Mathcad's tables for data input
  • Uses Mathcad's built-in vector scalar product for multiplying axis ordinate and mass
  • Uses Mathcad's built-in summation operator for total mass

As Mathcad uses a 2D "whiteboard" and special operators (eg, summation operator, integral operator), and saves in an XML format, an actual worksheet may contain several hundred (or more) characters. For the purposes of Code Golf, I've taken a Mathcad "byte count" to be the number of characters or operators that the user must enter to create the worksheet.

The first (program) version of the challenge takes 19 "bytes" using this definition and the function version takes 41 "bytes".

\$\endgroup\$
2
  • 3
    \$\begingroup\$ First time I've ever seen a Matcad solution here. Very nice. +1. \$\endgroup\$
    – rayryeng
    Commented Mar 7, 2016 at 17:44
  • \$\begingroup\$ Thank you, rayryeng. It's probably because it's a bit of a challenge doing some of the "holes" on the "course" given that Mathcad only has basic string functions and doesn't have human-readable, text-only source code. \$\endgroup\$ Commented Mar 7, 2016 at 18:10
6
\$\begingroup\$

MATLAB / Octave, 18 16 bytes

Thanks to user beaker and Don Muesli for removing 2 bytes!

Given that the coordinates are in a N x 2 matrix x where the first column is the X coordinate and the second column is the Y coordinate, and the masses are in a 1 x N matrix y (or a row vector):

@(x,y)y*x/sum(y)

The explanation of this code is quite straight forward. This is an anonymous function that takes in the two inputs x and y. We perform the weighted summation (the numerator expression of each coordinate) in a linear algebra approach using matrix-vector multiplication. By taking the vector y of masses and multiplying this with the matrix of coordinates x by matrix-vector multiplication, you would compute the weighted sum of both coordinates individually, then we divide each of these coordinates by the sum of the masses thus finding the desired centre of mass returned as a 1 x 2 row vector for each coordinate respectively.

Example runs

>> A=@(x,y)y*x/sum(y)

A = 

    @(x,y)y*x/sum(y)

>> x = [0 2; 3 4; 0 1; 1 1];
>> y = [2 6 2 10];
>> A(x,y)

ans =

    1.4000    2.0000

>> x = [3 1; 0 0; 1 4];
>> y = [2 4 1];
>> A(x,y)

ans =

    1.0000    0.8571

Try it online!

https://ideone.com/BzbQ3e

\$\endgroup\$
2
  • 1
    \$\begingroup\$ You can remove ;, and also ' by properly choosing input format (x as row vector) \$\endgroup\$
    – Luis Mendo
    Commented Mar 7, 2016 at 17:01
  • \$\begingroup\$ @DonMuesli Thanks :) Reduced the byte count by 2. \$\endgroup\$
    – rayryeng
    Commented Mar 7, 2016 at 17:11
6
\$\begingroup\$

Jelly, 6 bytes

S÷@×"S

or

÷S$×"S

Input is via two command-line arguments, masses first, coordinates second.

Try it online!

Explanation

S       Sum the masses.
   x"   Multiply each vector by the corresponding mass.
 ÷@     Divide the results by the sum of masses.
     S  Sum the vectors.

or

÷S$     Divide the masses by their sum.
   ×"   Multiply each vector by the corresponding normalised mass.
     S  Sum the vectors.
\$\endgroup\$
6
\$\begingroup\$

Julia, 25 17 bytes

f(c,m)=m*c/sum(m)

Missed the obvious approach :/ Call like f([3 1;0 0;1 4], [2 4 1]).

\$\endgroup\$
0
5
\$\begingroup\$

CJam, 14 bytes

{_:+df/.f*:.+}

An unnamed function with expects the list of coordinate pairs and the list of masses on the stack (in that order) and leaves the centre of mass in their place.

Test it here.

Explanation

_    e# Duplicate list of masses.
:+d  e# Get sum, convert to double.
f/   e# Divide each mass by the sum, normalising the list of masses.
.f*  e# Multiply each component of each vector by the corresponding weight.
:.+  e# Element-wise sum of all weighted vectors.
\$\endgroup\$
5
\$\begingroup\$

Perl 6, 36 33 30 bytes

{[Z+](@^a Z»*»@^b) X/sum @b}
\$\endgroup\$
4
\$\begingroup\$

Seriously, 16 bytes

╩2└Σ;╛2└*/@╜2└*/

Takes input as [x-coords]\n[y-coords]\n[masses], and outputs as xbar\nybar

Try it online!

Explanation:

╩2└Σ;╛2└*/@╜2└*/
╩                 push each line of input into its own numbered register
 2└Σ;             push 2 copies of the sum of the masses
     ╛2└*/        push masses and y-coords, dot product, divide by sum of masses
          @       swap
           ╜2└*/  push masses and x-coords, dot product, divide by sum of masses
\$\endgroup\$
3
\$\begingroup\$

Haskell, 55 50 bytes

z=zipWith
f a=map(/sum a).foldr1(z(+)).z(map.(*))a

This defines a binary function f, used as follows:

> f [1,2] [[1,2],[3,4]]
[2.3333333333333335,3.333333333333333]

See it pass both test cases.

Explanation

Haskell is not well suited for processing multidimensional lists, so I'm jumping through some hoops here. The first line defines a short alias for zipWith, which we need twice. Basically, f is a function that takes the list of weights a and produces f a, a function that takes the list of positions and produces the center of mass. f a is a composition of three functions:

z(map.(*))a      -- First sub-function:
z         a      --   Zip the list of positions with the mass list a
  map.(*)        --   using the function map.(*), which takes a mass m
                 --   and maps (*m) over the corresponding position vector
foldr1(z(+))     -- Second sub-function:
foldr1           --   Fold (reduce) the list of mass-times-position vectors
       z(+)      --   using element-wise addition
map(/sum a)      -- Third sub-function:
map              --   Map over both coordinates:
   (/sum a)      --     Divide by the sum of all masses
\$\endgroup\$
3
\$\begingroup\$

JavaScript (ES6), 60 bytes

a=>a.map(([x,y,m])=>{s+=m;t+=x*m;u+=y*m},s=t=u=0)&&[t/s,u/s]

Accepts an array of (x, y, mass) "triples" and returns a "tuple".

\$\endgroup\$
2
  • \$\begingroup\$ Are the parentheses around [x,y,m] necessary? iirc, they're not required if there's only one input argument to the arrow function. \$\endgroup\$ Commented Mar 8, 2016 at 6:29
  • \$\begingroup\$ @PatrickRoberts Yes, they're necessary in all cases except that one trivial one of exactly one standard argument. \$\endgroup\$
    – Neil
    Commented Mar 11, 2016 at 22:19
3
\$\begingroup\$

R, 32 25 bytes

function(a,m)m%*%a/sum(m)

edit -7 bytes by switch to matrix algebra (thanks @Sp3000 Julia answer)

pass an array (matrix with 2 columns, x,y) as coordinates and vector m of weights, returns an array with the required coordinates

\$\endgroup\$
2
\$\begingroup\$

PHP, 142 bytes

function p($q,$d){return$q*$d;}function c($f){$s=array_sum;$m=array_map;$e=$f[0];return[$s($m(p,$e,$f[1]))/$s($e),$s($m(p,$e,$f[2]))/$s($e)];}
Exploded view
function p($q, $d) {
  return $q * $d;
}

function c($f) {
  $s = array_sum;
  $m = array_map;
  $e = $f[0];
  return [ $s($m(p,$e,$f[1])) / $s($e),
           $s($m(p,$e,$f[2])) / $s($e) ];
}
Required input
Array[Array]: [ [ mass1, mass2, ... ],
                [ xpos1, xpos2, ... ],
                [ ypos1, ypos2, ... ] ]
Return

Array: [ xbar, ybar ]


The p() function is a basic map, multiplying each [m] value with the corresponding [x] or [y] value. The c() function takes in the Array[Array], presents the array_sum and array_map functions for space, then calculates Σmx/Σm and Σmy/Σm.

Might be possible to turn the calculation itself into a function for space, will see.

\$\endgroup\$
2
\$\begingroup\$

Mathcad, 8 "bytes"

I don't know what I wasn't thinking of in my previous answer. Here's a shorter way making proper use of matrix multiplication. The variable p contains the data - if setting the variable counts towards the total, then add another 2 "bytes" (creation of input table = 1 byte, variable name = 1 byte).

enter image description here

\$\endgroup\$
1
\$\begingroup\$

Python 3, 63 bytes

lambda a,b:[sum(x*y/sum(b)for x,y in zip(L,b))for L in zip(*a)]

Vector operations on lists is long :/

This is an anonymous lambda function - give it a name and call like f([[0,2],[3,4],[0,1],[1,1]],[2,6,2,10]).

\$\endgroup\$
1
\$\begingroup\$

Python 3, 95 90 88 bytes

Solution

lambda c,m:list(map(sum,zip(*[[i[0]*j/sum(m),i[1]*j/sum(m)]for i,j in zip(*([c,m]))])))

Results

>>> f([[0,2],[3,4],[0,1],[1,1]],[2,6,2,10])
[1.3999999999999999, 2.0]
>>> f([[3,1],[0,0],[1,4]],[2,4,1])
[1.0, 0.8571428571428571]

thanks to @Zgarb saving 2 bytes


A recursive solution for fun (95 bytes)

f=lambda c,m,x=0,y=0,s=0:f(c[1:],m[1:],x+c[0][0]*m[0],y+c[0][1]*m[0],s+m[0])if c else[x/s,y/s]

Results

>>> f([[0,2],[3,4],[0,1],[1,1]],[2,6,2,10])
[1.4, 2.0]
>>> f([[3,1],[0,0],[1,4]],[2,4,1])
[1.0, 0.8571428571428571]
\$\endgroup\$
1
  • 2
    \$\begingroup\$ I think *([c]+[m]) could be shortened to *[c,m]. \$\endgroup\$
    – Zgarb
    Commented Mar 7, 2016 at 16:53
0
\$\begingroup\$

Axiom, 158 bytes

c(a:List List Float):List Float==(x:=y:=m:=0.;for i in 1..#a repeat(~index?(3,a.i)=>return[];x:=x+a.i.3*a.i.1;y:=y+a.i.3*a.i.2;m:=m+a.i.3);m=0.=>[];[x/m,y/m])

ungolf it

-- Input List of Coordinate and masses as [[xi,yi,mi]]
-- Return center of mass for the list a as [x,y] Float coordinates
-- or [] if some error occur [for example masses are all 0]
cc(a:List List Float):List Float==
    x:=y:=m:=0.
    for i in 1..#a repeat
         ~index?(3,a.i)=>return []
         x:=x+a.i.3*a.i.1
         y:=y+a.i.3*a.i.2
         m:=m+a.i.3
    m=0.=>return []
    return[x/m,y/m]

results

(21) -> c([[0,2,2],[3,4,6],[0,1,2],[1,1,10]])
   (21)  [1.4,2.0]
                                                         Type: List Float
(22) -> c([[3,1,2],[0,0,4],[1,4,1]])
   (22)  [1.0,0.8571428571 4285714286]
                                                         Type: List Float
\$\endgroup\$
0
\$\begingroup\$

k, 13 bytes

{(+/x*y)%+/y}

Try it online!

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.