Programming Puzzles & Code Golf Stack Exchange is a question and answer site for programming puzzle enthusiasts and code golfers. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

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!

share|improve this question
    
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.) – Martin Ender Mar 7 at 14:09
    
@MartinBüttner I looked as well, and could not find any. If this is a dupe, feel free to close it though. – Mr Public Mar 7 at 14:15
    
Can the input be taken in the other order? Or in the form: [x,y,m],[x,y,m]...? – FryAmTheEggman Mar 7 at 14:21
    
@FryAmTheEggman Question edited for valid inputs. – Mr Public Mar 7 at 14:28
    
@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? – Zeta Mar 7 at 14:33

16 Answers 16

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
share|improve this answer
    
y is pretty useful!! +1 – David Mar 7 at 23:20
    
@David Yeah! Combined with implicit input, it does a lot of things in this case :-) – Luis Mendo Mar 7 at 23:22

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}
share|improve this answer
1  
I never used Dot. But I will after seeing your usage above! – DavidC Mar 8 at 18:43

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

share|improve this answer
1  
You can remove ;, and also ' by properly choosing input format (x as row vector) – Luis Mendo Mar 7 at 17:01
    
@DonMuesli Thanks :) Reduced the byte count by 2. – rayryeng Mar 7 at 17:11

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

share|improve this answer

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".

share|improve this answer
3  
First time I've ever seen a Matcad solution here. Very nice. +1. – rayryeng Mar 7 at 17:44
    
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. – Stuart Bruff Mar 7 at 18:10

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.
share|improve this answer

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.
share|improve this answer

Perl 6, 36 33 30 bytes

{[Z+](@^a Z»*»@^b) X/sum @b}
share|improve this answer

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
share|improve this answer

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
share|improve this answer

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".

share|improve this answer
    
Are the parentheses around [x,y,m] necessary? iirc, they're not required if there's only one input argument to the arrow function. – Patrick Roberts Mar 8 at 6:29
    
@PatrickRoberts Yes, they're necessary in all cases except that one trivial one of exactly one standard argument. – Neil Mar 11 at 22:19

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.

share|improve this answer

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

share|improve this answer

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

share|improve this answer

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]
share|improve this answer
2  
I think *([c]+[m]) could be shortened to *[c,m]. – Zgarb Mar 7 at 16:53

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

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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