Monday, March 7, 2016

Center of Mass from a list of coordinates and their masses – codegolf.stackexchange.com #JHedzWorlD



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 mas 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!




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 



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.


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

https://ideone.com/BzbQ3e




Julia, 25 bytes


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

Call like f([3 1;0 0;1 4], [2;4;1]).




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




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. 



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. 



#.#2/Tr@#& 

Example:


In[1]:= #.#2/Tr@#&[2,6,2,10,0,2,3,4,0,1,1,1] Out[1]= 7/5, 2 



Seriously, 16 bytes


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

Takes input as [x-coords]n[y-coords]n[masses], and outputs as xbarnybar


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 



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 



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.




Python 3, 95 90 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] 


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] 


JHedzWorlD






Center of Mass from a list of coordinates and their masses – codegolf.stackexchange.com #JHedzWorlD

No comments:

Post a Comment