Calculating Geometric median of 2D points - java

I am calculating Geometric median of some (x,y) points in java. To calculate Geometric median, first i am calculating centroid of all the points, then this centroid is used to calculate Geometric median. My code works fine, but sometimes it goes to an infinite loop (i think.). The problem is with my while condition. This while condition should be change according to input points, but i don't know how. Below I am putting the complete code.
import java.util.ArrayList;
public class GeometricMedian {
private static ArrayList<Point> points = new ArrayList<Point>();
private class Point {
private double x;
private double y;
Point(double a, double b) {
x = a;
y = b;
}
}
public static void main(String[] args) {
GeometricMedian gm = new GeometricMedian();
gm.addPoints();
Point centroid = gm.getCentroid();
Point geoMedian = gm.getGeoMedian(centroid);
System.out.println("GeometricMedian= {" + (float) geoMedian.x + ", "
+ (float) geoMedian.y + "}");
}
public void addPoints() {
points.add(new Point(0, 1));
points.add(new Point(2, 5));
points.add(new Point(3, 1));
points.add(new Point(4, 0));
}
public Point getCentroid() {
double cx = 0.0D;
double cy = 0.0D;
for (int i = 0; i < points.size(); i++) {
Point pt = points.get(i);
cx += pt.x;
cy += pt.y;
}
return new Point(cx / points.size(), cy / points.size());
}
public Point getGeoMedian(Point start) {
double cx = 0;
double cy = 0;
double centroidx = start.x;
double centroidy = start.y;
do {
double totalWeight = 0;
for (int i = 0; i < points.size(); i++) {
Point pt = points.get(i);
double weight = 1 / distance(pt.x, pt.y, centroidx, centroidy);
cx += pt.x * weight;
cy += pt.y * weight;
totalWeight += weight;
}
cx /= totalWeight;
cy /= totalWeight;
} while (Math.abs(cx - centroidx) > 0.5
|| Math.abs(cy - centroidy) > 0.5);// Probably this condition
// needs to change
return new Point(cx, cy);
}
private static double distance(double x1, double y1, double x2, double y2) {
x1 -= x2;
y1 -= y2;
return Math.sqrt(x1 * x1 + y1 * y1);
}
}
Please help me to fix the bug, also if there exitis any better way to calculate Geometric median of some 2D points, write here. Thank you.

I don't see why you need two loops. You only need the loop over all the points. What is the reason for the other one, in your view?

One way to solve this is to iterate certain number of times. This similar to K-Means method where it either converges to a specific threshold or stops after a predefined number of iterations.

Related

How do I get the right amount of change to the slope for my linear regression?

I want to program a linear regression with Processing. But I got mixed up which parameters I have to multiply and then add or subtract from my slope.
I have tried to change the parameters (make them negative, change the learning rate). The b does actually work, but I have got some problems to get the slope the right way.
//Data
float[] P1 = {100,100};
float[] P2 = {200,300};
float[] P3 = {300,250};
float[][] allData = {P1,P2,P3};
//random start values
float w1 = random(0,3);
float b = random(-100,100);
float learningRate = 0.01;
int i = 0;
void setup(){
size(1000,1000);
}
void draw(){
background(255);
axes();
//Draw Points
for(int j=0;j<allData.length;j+=1){
float[] point = allData[j];
advancedPoint(point[0],point[1],color(181, 16, 32),10);
}
//Gradient descend, thats the confusing part...
if(i<10000){
i += 1;
float dcost_dreg = 0;
float dcost_dtar = 0;
for(int j=0;j<allData.length;j+=1){
float[] point = allData[j];
float yTarget = point[1];
float yRegression = w1*point[0] + b;
dcost_dreg += -2*(yRegression-yTarget); //I don't understand these lines
dcost_dtar += -2*(yRegression-yTarget)*point[0];
}
w1 += learningRate * (dcost_dtar/allData.length);
b += learningRate * (dcost_dreg/allData.length) ;//until here
}
//Draw Regression
linearPoints(w1, b);
}
void linearPoints (float w1, float b){
float y;
for(float x=-width; x<width; x=x+0.25){
y = w1*x + b;
strokeWeight(3);
stroke(100,100);
point(x+width/2, -y + height/2);
}
}
void axes(){
for(float a=0; a<height; a=a+0.25){
strokeWeight(1);
stroke(255,100,0);
point(width/2,a);
}
for(float b=0; b<width; b=b+0.25){
stroke(255,100,0);
point(b,height/2);
}
}
void advancedPoint(float x,float y, color c, int size){
strokeWeight(size);
stroke(c);
point(x+width/2,-y+height/2);
}
In theory the program should fit a linear regression through my data.
The linear regression is base on the equation of a Line in the form
y = w1 * x + b
The terms
dcost_dreg += -2*(yRegression-yTarget);
dcost_dtar += -2*(yRegression-yTarget)*point[0];
should calculate the error of the line equation in compare to the sample points, but your calculation is wrong.
The constant error (b error) is the difference of the sample y coordinate and the y coordinate which is calculated by the line equation on the sample x coordinate.
The linear error (w1 error) is calculated by the gradient difference. The gradient difference is the quotient of a height and a width (y/x) rather than a product.
This means the calculation has to be:
dcost_dreg += (yTarget-yRegression);
dcost_dtar += (yTarget-yRegression)/point[0];
The expressions
w1 += learningRate * (dcost_dtar/allData.length);
b += learningRate * (dcost_dreg/allData.length);
Calculate the average error of the samples and apply the correction to the line equation by considering the learning rate.
Change the function draw to solve the issue:
void draw(){
background(255);
axes();
//Draw Points
for(int j=0;j<allData.length;j+=1){
float[] point = allData[j];
advancedPoint(point[0],point[1],color(181, 16, 32),10);
}
//Gradient descend, thats the confusing part...
if(i<10000){
i += 1;
float dcost_dreg = 0;
float dcost_dtar = 0;
for(int j=0;j<allData.length;j+=1){
float[] point = allData[j];
float yTarget = point[1];
float yRegression = w1*point[0] + b;
dcost_dreg += (yTarget-yRegression);
dcost_dtar += (yTarget-yRegression)/point[0];
}
w1 += learningRate * (dcost_dtar/allData.length);
b += learningRate * (dcost_dreg/allData.length);
}
//Draw Regression
linearPoints(w1, b);
}
By the way, recommend to use line() for drawing the axis and the current line equation:
void linearPoints (float w1, float b){
strokeWeight(3);
stroke(100,100,255);
float x0 = -width;
float x1 = width;
float y0 = x0 * w1 + b;
float y1 = x1 * w1 + b;
line(x0+width/2, -y0+height/2, x1+width/2, -y1+height/2);
}
void axes(){
strokeWeight(1);
stroke(255,100,0);
line(0,height/2, width, height/2);
line(width/2, 0, width/2, height);
}

Calulate a series of many slopes

I have a list of coordinates and i currently have a function that deals with said coordinates to find the slope, all in order and paired
public void foo(){
int[] xCoords = {//stuff}
int[] yCoords = {//stuff}
for(int i = 0; i < 100000; i++){
getSlope(x[i], y[i], xcoords, ycoords)
}
}
public int getSlope(int x, int y, int[] x1, int[] y1){
//calculate slope using m = (y - y1) / (x - x1);
double slope;
for(int i = 0; i < x1.length(); i++){
slope = (y - y1[i]) / (x - x1[i]);
return slope;
}
return -2;
}
This is all well and good but I am wondering how can I do this with a massive list of coordinates. getSlope gets called from another method that places a coordinate to be evaluated and this is running really slow O(n^2) i think (for loop in a for loop).
Is there a quicker way to do this?
Full disclosure: This is part of a larger assignment for school so I wouldn't like answers just thoughts related to time complexity and big-Oh.
Edit: for a bit more clarification.
Edit2: for a little more clarification.
Assuming you want to find the slope between two consecutive points in your coordinate arrays:
public void foo(){
int[] xCoords = {//stuff}
int[] yCoords = {//stuff}
double slope;
for(int i = 0; i < xCoords.length-1 && i < yCoords.length-1; i++){
slope = getSlope(xCoords[i], yCoords[i], xCoords[i+1], yCoords[i+1]);
System.out.println(slope);
}
}
public double getSlope(int x, int y, int x1, int y1){
//calculate slope using m = (y - y1) / (x - x1);
return (y - y1) / (x - x1);
}
EDIT:
If what you want is to have multiple slopes with respect to a fixed reference point your method should return more than one value as shown below, but if you want to return a single slope from your method then there is no need for looping and your method does not also need to take array as parameter; you may just pass the two pair coordinates for a single slope calculation.
public void foo(){
int[] xCoords;// = {//stuff}
int[] yCoords;// = {//stuff}
int referenceX = //set to your value
int referenceY = //set to your value
double[] slopes = getSlope(referenceX, referenceY, xCoords, yCoords)
}
public double[] getSlope(int x, int y, int[] x1, int[] y1){
//calculate slope using m = (y - y1) / (x - x1);
double[] slope = new double[(x1.length<y1.length)? x1.length:y1.length];
for(int i = 0; i < x1.length && i < y1.length; i++){
slope[i] = (y - y1[i]) / (x - x1[i]);
}
return slope;
}

Centre of mass of a random 3D polygon (obj file or stl file)

Currently I have an ArrayList of vertices in a 3-dimensional cartesian coordinates system. The polygon is random. It can be a car, a cup or even a dragon.
Assuming the density does not change, how to calculate the centre of mass (x,y,z) of this 3D object?
I am storing the faces and vertices in ArrayList.
public ArrayList<stlFace> StlFaces = new ArrayList<stlFace>();
public ArrayList<VertexGeometric> VertexList = new ArrayList<VertexGeometric>();
I was using this for calculating surface which is proportional to mass of each face or triangle. And to calculate center off mass of each triangle and center of mass of whole object I was using this. I added helper methods getCenter() and getSurface() to Face class to encapsulate calculations specific to just one face/triangle.
public static class Vertex {
public float x = 0;
public float y = 0;
public float z = 0;
public Vertex(float x, float y, float z) {
this.x = x;
this.y = y;
this.z = z;
}
}
public static class Face {
public Vertex v1;
public Vertex v2;
public Vertex v3;
public Face(Vertex v1, Vertex v2, Vertex v3) {
this.v1 = v1;
this.v2 = v2;
this.v3 = v3;
}
public Vertex getCenter() {
Vertex triangleCenter = new Vertex(0, 0, 0);
triangleCenter.x += v1.x;
triangleCenter.x += v2.x;
triangleCenter.x += v3.x;
triangleCenter.y += v1.y;
triangleCenter.y += v2.y;
triangleCenter.y += v3.y;
triangleCenter.z += v1.z;
triangleCenter.z += v2.z;
triangleCenter.z += v3.z;
triangleCenter.x /= 3;
triangleCenter.y /= 3;
triangleCenter.z /= 3;
return triangleCenter;
}
public float getSurface() {
float x1 = v1.x - v2.x;
float x2 = v1.y - v2.y;
float x3 = v1.z - v2.z;
float y1 = v1.x - v3.x;
float y2 = v1.y - v3.y;
float y3 = v1.z - v3.z;
return (float) Math.sqrt(
Math.pow(x2 * y3 - x3 * y2, 2) +
Math.pow(x3 * y1 - x1 * y3, 2) +
Math.pow(x1 * y2 - x2 * y1, 2)
) / 2f;
}
}
public static Vertex calculateMassCenter(List<Face> faces) {
Vertex massCenter = new Vertex(0, 0, 0);
float mass = 0;
for (Face face : faces) {
Vertex triangleCenter = face.getCenter();
float faceMass = face.getSurface();
mass += faceMass;
massCenter.x += faceMass * triangleCenter.x;
massCenter.y += faceMass * triangleCenter.y;
massCenter.z += faceMass * triangleCenter.z;
}
massCenter.x /= mass;
massCenter.y /= mass;
massCenter.z /= mass;
return massCenter;
}

Distance of point from a line not working

I am calculating distance of point from a line. But I am getting wrong distance. Following is my piece of code which is gettting distance from line.
float px,py,something,u;
px=x2-x1;
py=y2-y1;
something = px*px + py*py;
u = ((x - x1) * px + (y - y1) * py) /(something);
if( u > 1)
{
u = 1;
// MinDist=0;
}
else if (u < 0)
{
u = 0;
//MinDist=0;
}
float xx = x1 + u * px;
float yy = y1 + u * py;
float dx = xx - x;
float dy = yy - y;
float dist= (float)Math.sqrt((double)dx*dx +(double) dy*dy);
Dist is giving wrong answer.
From: http://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line#Vector_formulation
distance(x=a+tn, p) = ||(a-p)-((a-p).n)n||
Where:
a = (x1, y1) //First point on line
n = |(x2-x1, y2-y1)| //Normalised direction vector
p = (x, y) //Query point
So, not going to do it all, but make functions and give meaningful names to help you follow the formular:
float[] a = new float[]{x1, y1};
float[] n = new float[]{x2-x1, y2-y1};
normalize(n);
float[] p = new float[]{x, y};
float[] aMinusP = subtract(a, p);
float aMinusPDotn = dot(aMinusP, n);
// vec2a.vec2b
float dot(float[] vec2a, float[] vec2b)
{
return vec2a[0]*vec2b[0] + vec2a[1]*vec2b[1];
}
// ||vec2||
float len(float[] vec2)
{
return (float)Math.Sqrt(dot(vec2, vec2));
}
// vec2/||vec2||
void normalize(float[] vec2)
{
float length = len(vec2);
vec2[0] /= length;
vec2[1] /= length;
}
// vec2a - vec2b
float[] subtract(float[] vec2a, float[] vec2b)
{
return new float[]{vec2a[0]-vec2b[0],vec2a[1]-vec2b[1]};
}

Need a function to limit a line (known by its coordinates) in its length

I need a function which takes a line (known by its coordinates)
and return a line with same angle, but limited to certain length.
My code gives correct values only when the line is turned 'right'
(proven only empirically, sorry).
Am I missing something?
public static double getAngleOfLine(int x1, int y1, int x2, int y2) {
double opposite = y2 - y1;
double adjacent = x2 - x1;
if (adjacent == Double.NaN) {
return 0;
}
return Math.atan(opposite / adjacent);
}
// returns newly calculated destX and destY values as int array
public static int[] getLengthLimitedLine(int startX, int startY,
int destX, int destY, int lengthLimit) {
double angle = getAngleOfLine(startX, startY, destX, destY);
return new int[]{
(int) (Math.cos(angle) * lengthLimit) + startX,
(int) (Math.sin(angle) * lengthLimit) + startY
};
}
BTW: I know that returning arrays in Java is stupid,
but it's just for the example.
It would be easier to just treat it as a vector. Normalize it by dividing my its magnitude then multiply by a factor of the desired length.
In your example, however, try Math.atan2.
In Python because I don't have a Java compiler handy:
import math
def getLengthLimitedLine(x1, y1, x2, y2, lengthLimit):
length = math.sqrt((x2-x1)**2 + (y2-y1)**2)
if length > lengthLimit:
shrink_factor = lengthLimit / length
x2 = x1 + (x2-x1) * shrink_factor
y2 = y1 + (y2-y1) * shrink_factor
return x2, y2
print getLengthLimitedLine(10, 20, 25, -5, 12)
# Prints (16.17, 9.71) which looks right to me 8-)
It's an easy problem if you understand something about vectors.
Given two points (x1, y1) and (x2, y2), you can calculate the vector from point 1 to 2:
v12 = (x2-x1)i + (y2-y2)j
where i and j are unit vectors in the x and y directions.
You can calculate the magnitude of v by taking the square root of the sum of squares of the components:
v = sqrt((x2-x2)^2 + (y2-y1)^2)
The unit vector from point 1 to point 2 equals v12 divided by its magnitude.
Given that, you can calculate the point along the unit vector that's the desired distance away by multiply the unit vector times the length and adding that to point 1.
Encapsulate Line in a class, add a unit method and a scale method.
public class Line {
private float x;
private float y;
public Line(float x1, float x2, float y1, float y2) {
this(x2 - x1, y2 - y1);
}
public Line(float x, float y) {
this.x = x;
this.y = y;
}
public float getLength() {
return (float) Math.sqrt((x * x) + (y * y));
}
public Line unit() {
return scale(1 / getLength());
}
public Line scale(float scale) {
return new Line(x * scale, y * scale);
}
}
Now you can get a line of arbitrary length l by calling
Line result = new Line(x1, x2, y1, y2).unit().scale(l);
No need to use trig, which can have some nasty edge cases. Just use similar triangles:
public static int[] getLengthLimitedLine(int startX, int startY,
int destX, int destY, int lengthLimit)
{
int deltaX = destX - startX;
int deltaY = destY - startY;
int lengthSquared = deltaX * deltaX + deltaY * deltaY;
// already short enough
if(lengthSquared <= lengthLimit * lengthLimit)
return new int[]{destX, destY};
double length = Math.sqrt(lengthSquared);
double newDeltaX = deltaX * lengthLimit / length;
double newDeltaY = deltaY * lengthLimit / length;
return new int[]{(int)(startX + newDeltaX), (int)(startY + newDeltaY)};
}
Just use the Pythagorean theorem, like so:
public static int[] getLengthLimitedLine(int start[], int dest[], int lengthLimit) {
int xlen = dest[0] - start[0]
int ylen = dest[1] - start[1]
double length = Math.sqrt(xlen * xlen + ylen * ylen)
if (length > lengthLimit) {
return new int[] {start[0], start[1],
start[0] + xlen / lengthLimit,
start[1] + ylen / lengthLimit}
} else {
return new int[] {start[0], start[1], dest[0], dest[1];}
}
}

Categories