Open In App

Geometric Median

Improve
Improve
Like Article
Like
Save
Share
Report

In normal median, we find a point that has minimum sum of distances. Similar concept applies in 2-D space. 
Given N points in 2-D space, the task is to find out a single point (x, y) from which the sum of distances to the input points are minimized (also known as the centre of minimum distance).
Examples: 
 

Input: (1, 1), (3, 3) 
Output: Geometric Median = (2, 2) with minimum distance = 2.82843
Input: (0, 0), (0, 0), (0, 12) 
Output: Geometric Median = (0, 0) with minimum distance = 12 
 


 


Approach: 
At first thought, it seems that the problem asks us to find the Midpoint of the Geometric Centre point (in other words, centroid) of the given input points. Since it is the “centre” point of the input, sum of distances from the centre to all the given input points should automatically be minimized. This process is analogous to finding the Centre of Gravity of N             discrete Mass particles. The first example test case even gives the correct answer. But what happens when we apply the same logic to the second example?
We can clearly see that the Geometric Centre, or the Centroid of (0, 0), (0, 0), (0, 12)             is at (0, 4)             . So according to the Euclidean Distance formula, the total distance to travel from Centroid to all 3 of the input points is 4+4+8 = 16             But the optimal point should be (0, 0)             , giving us a total distance of 12             So, where are we wrong?
Intuitively, you can think that Centroid of input points gives us the Arithmetic Mean of the input points. But what we require is the Central Tendency of the input points such that the cost to reach that central tendency (or in other words, the Euclidean Distance) is minimized. This is called the Geometric Median of a set of points.It is kind of like how conceptually, a Median is drastically different from Mean of given inputs.
There isn’t any defined correct algorithm for finding the Geometric Median. What we do to approach this kind of problems is approximating a solution and determining whether our solution is indeed the Geometric Median or not.
Algorithm 
There are two important variables : 
 

  • current_point – stores the x and y coordinates of the point which could be the Geometric Median.
  • minimum_distance – stores the sum of Euclidean distances from current_point to all input points.


After every approximation, if we find a new point from which the sum of distances is lower, then we update both the values of current point and minimum distance to the new point and new distance.
First, we find the Centroid of the given points, take it as the current_point (or the median) and store the sum of distances in minimum distance. Then, we iterate over the given input points, by turn assuming each input point to be the median, and then calculating the distance to other points. If this distance is lower than the minimum_distance, then we update the old values of current_point and minimum_distance to the new values. Else, the old values remains the same.
Then we enter a while loop. Inside that loop, we move a distance of test_distance (we assume a test_distance of 1000 for this example) from the current_point in all 4             directions (left, up, right, down). Hence we get 4             new points. Then we calculate the distance from these new points to the given input points. If this sum of distances is lower than the previous minimum_distance then we update the old values of current_point and minimum_distance to the new values and repeat the while loop. Else, we divide the test_distance by 2             and then repeat the while loop.
The terminating condition for the while loop is a certain value called the “lower_limit”. Lower the value, higher the accuracy of our approximation. Loop terminates when lower_limit exceeds the test_distance.
Below is the implementation of the above approach:
 

CPP

// C++ implementation of the approach
#include <bits/stdc++.h>
using namespace std;
 
// To store a point in 2-D space
struct Point {
    double x, y;
};
 
// Test points. These points are the left,
// up, right and down relative neighbours
// (arranged circularly) to the
// current_point at a distance of
// test_distance from current_point
Point test_point[] = { { -1.0, 0.0 },
                       { 0.0, 1.0 },
                       { 1.0, 0.0 },
                       { 0.0, -1.0 } };
 
// Lowest Limit till which we are going
// to run the main while loop
// Lower the Limit higher the accuracy
double lower_limit = 0.01;
 
// Function to return the sum of Euclidean
// Distances
double distSum(Point p,
                        Point arr[], int n)
{
    double sum = 0;
    for (int i = 0; i < n; i++) {
        double distx = abs(arr[i].x - p.x);
        double disty = abs(arr[i].y - p.y);
        sum += sqrt((distx * distx) + (disty * disty));
    }
 
    // Return the sum of Euclidean Distances
    return sum;
}
 
// Function to calculate the required
// geometric median
void geometricMedian(Point arr[], int n)
{
 
    // Current x coordinate and y coordinate
    Point current_point;
 
    for (int i = 0; i < n; i++) {
        current_point.x += arr[i].x;
        current_point.y += arr[i].y;
    }
 
    // Here current_point becomes the
    // Geographic MidPoint
    // Or Center of Gravity of equal
    // discrete mass distributions
    current_point.x /= n;
    current_point.y /= n;
 
    // minimum_distance becomes sum of
    // all distances from MidPoint to
    // all given points
    double minimum_distance =
       distSum(current_point, arr, n);
 
    int k = 0;
    while (k < n) {
        for (int i = 0; i < n, i != k; i++) {
            Point newpoint;
            newpoint.x = arr[i].x;
            newpoint.y = arr[i].y;
            double newd =
                   distSum(newpoint, arr, n);
            if (newd < minimum_distance) {
                minimum_distance = newd;
                current_point.x = newpoint.x;
                current_point.y = newpoint.y;
            }
        }
        k++;
    }
 
    // Assume test_distance to be 1000
    double test_distance = 1000;
    int flag = 0;
 
    // Test loop for approximation starts here
    while (test_distance > lower_limit) {
 
        flag = 0;
 
        // Loop for iterating over all 4 neighbours
        for (int i = 0; i < 4; i++) {
 
            // Finding Neighbours done
            Point newpoint;
            newpoint.x = current_point.x
                 + (double)test_distance * test_point[i].x;
            newpoint.y = current_point.y
                 + (double)test_distance * test_point[i].y;
 
            // New sum of Euclidean distances
            // from the neighbor to the given
            // data points
            double newd = distSum(newpoint, arr, n);
 
            if (newd < minimum_distance) {
 
                // Approximating and changing
                // current_point
                minimum_distance = newd;
                current_point.x = newpoint.x;
                current_point.y = newpoint.y;
                flag = 1;
                break;
            }
        }
 
        // This means none of the 4 neighbours
        // has the new minimum distance, hence
        // we divide by 2 and reiterate while
        // loop for better approximation
        if (flag == 0)
            test_distance /= 2;
    }
 
    cout << "Geometric Median = ("
         << current_point.x << ", "
         << current_point.y << ")";
    cout << " with minimum distance = "
         << minimum_distance;
}
 
// Driver code
int main()
{
 
    int n = 2;
    Point arr[n];
    arr[0].x = 1;
    arr[0].y = 1;
    arr[1].x = 3;
    arr[1].y = 3;
    geometricMedian(arr, n);
 
    return 0;
}

                    

Java

import java.io.*;
import java.util.*;
 
// java program to check
// similarity between two triangles.
 
// To store a point in 2-D space
class Point {
    double x, y;
     
    Point(double x, double y){
        this.x = x;
        this.y = y;
    }
};
 
public class GFG {
 
    // Test points. These points are the left,
    // up, right and down relative neighbours
    // (arranged circularly) to the
    // current_point at a distance of
    // test_distance from current_point
    public static Point[] test_point = { new Point(-1, 0),
                                         new Point(0, 1),
                                         new Point(1, 0),
                                         new Point(0, -1)
                                        };
 
 
    // Lowest Limit till which we are going
    // to run the main while loop
    // Lower the Limit higher the accuracy
    public static double lower_limit = 0.01;
     
     
    // Function to return the sum of Euclidean
    // Distances
    static double distSum(Point p, Point[] arr, int n)
    {
        double sum = 0;
        for (int i = 0; i < n; i++) {
            double distx = Math.abs(arr[i].x - p.x);
            double disty = Math.abs(arr[i].y - p.y);
            sum += Math.sqrt((distx * distx) + (disty * disty));
        }
 
        // Return the sum of Euclidean Distances
        return sum;
    }
     
    // Function to calculate the required
    // geometric median
    static void geometricMedian(Point[] arr, int n)
    {
 
        // Current x coordinate and y coordinate
        Point current_point = new Point(0, 0);
 
        for (int i = 0; i < n; i++) {
            current_point.x += arr[i].x;
            current_point.y += arr[i].y;
        }
 
        // Here current_point becomes the
        // Geographic MidPoint
        // Or Center of Gravity of equal
        // discrete mass distributions
        current_point.x /= n;
        current_point.y /= n;
 
        // minimum_distance becomes sum of
        // all distances from MidPoint to
        // all given points
        double minimum_distance = distSum(current_point, arr, n);
 
        int k = 0;
        while (k < n) {
            for (int i = 0; i < n && i != k; i++) {
                Point newpoint = new Point(0, 0);
                newpoint.x = arr[i].x;
                newpoint.y = arr[i].y;
                double newd = distSum(newpoint, arr, n);
                if (newd < minimum_distance) {
                    minimum_distance = newd;
                    current_point.x = newpoint.x;
                    current_point.y = newpoint.y;
                }
            }
            k++;
        }
 
        // Assume test_distance to be 1000
        double test_distance = 1000;
        int flag = 0;
 
        // Test loop for approximation starts here
        while (test_distance > lower_limit) {
 
            flag = 0;
 
            // Loop for iterating over all 4 neighbours
            for (int i = 0; i < 4; i++) {
 
                // Finding Neighbours done
                Point newpoint = new Point(0, 0);
                newpoint.x = current_point.x + (double)test_distance * test_point[i].x;
                newpoint.y = current_point.y + (double)test_distance * test_point[i].y;
 
                // New sum of Euclidean distances
                // from the neighbor to the given
                // data points
                double newd = distSum(newpoint, arr, n);
 
                if (newd < minimum_distance) {
 
                    // Approximating and changing
                    // current_point
                    minimum_distance = newd;
                    current_point.x = newpoint.x;
                    current_point.y = newpoint.y;
                    flag = 1;
                    break;
                }
            }
 
            // This means none of the 4 neighbours
            // has the new minimum distance, hence
            // we divide by 2 and reiterate while
            // loop for better approximation
            if (flag == 0)
                test_distance /= 2;
        }
 
        System.out.println("Geometric Median = (" + (int)current_point.x + ", " + (int)current_point.y + ")");
        System.out.println(" with minimum distance = " + String.format("%.5f", minimum_distance));
    }
     
    public static void main(String[] args) {
        int n = 2;
        Point[] arr = new Point[n];
         
        arr[0] = new Point(1, 1);
        arr[1] = new Point(3, 3);
         
        geometricMedian(arr, n);
    }
}
 
// The code is contributed by Nidhi goel.

                    

Python3

# Python implementation of the approach
import math
 
# To store a point in 2-D space
class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y
 
# Test points. These points are the left,
# up, right and down relative neighbours
# (arranged circularly) to the
# current_point at a distance of
# test_distance from current_point
test_point = [Point(-1, 0),
              Point(0, 1),
              Point( 1, 0),
              Point(0, -1)]
 
 
# Lowest Limit till which we are going
# to run the main while loop
# Lower the Limit higher the accuracy
lower_limit = 0.01
 
# Function to return the sum of Euclidean
# Distances
def distSum(p, arr, n):
    sum = 0
    for i in range(n):
        distx = abs(arr[i].x - p.x)
        disty = abs(arr[i].y - p.y)
        sum = sum + math.sqrt((distx * distx) + (disty * disty))
 
    # Return the sum of Euclidean Distances
    return sum
 
 
# Function to calculate the required
# geometric median
def geometricMedian(arr, n):
 
    # Current x coordinate and y coordinate
    current_point = Point(0, 0)
     
    for i in range(n):
        current_point.x = current_point.x + arr[i].x
        current_point.y = current_point.y + arr[i].y
 
         
    # Here current_point becomes the
    # Geographic MidPoint
    # Or Center of Gravity of equal
    # discrete mass distributions
    current_point.x = current_point.x / n
    current_point.y = current_point.y / n
 
    # minimum_distance becomes sum of
    # all distances from MidPoint to
    # all given points
    minimum_distance = distSum(current_point, arr, n)
 
    k = 0
    while (k < n):
        while(i < n and i != k):
            newpoint = Point(0, 0)
            newpoint.x = arr[i].x
            newpoint.y = arr[i].y
            newd = distSum(newpoint, arr, n)
            if newd < minimum_distance:
                minimum_distance = newd;
                current_point.x = newpoint.x
                current_point.y = newpoint.y
            i = i + 1
        k = k + 1
 
    # Assume test_distance to be 1000
    test_distance = 1000
    flag = 0
 
    # Test loop for approximation starts here
    while test_distance > lower_limit:
 
        flag = 0
 
        # Loop for iterating over all 4 neighbours
        for i in range(4):
 
            # Finding Neighbours done
            newpoint = Point(0,0)
            newpoint.x = current_point.x + test_distance * test_point[i].x
            newpoint.y = current_point.y + test_distance * test_point[i].y
 
            # New sum of Euclidean distances
            # from the neighbor to the given
            # data points
            newd = distSum(newpoint, arr, n)
 
            if newd < minimum_distance:
 
                # Approximating and changing
                # current_point
                minimum_distance = newd
                current_point.x = newpoint.x
                current_point.y = newpoint.y
                flag = 1
                break
 
        # This means none of the 4 neighbours
        # has the new minimum distance, hence
        # we divide by 2 and reiterate while
        # loop for better approximation
        if (flag == 0):
            test_distance = test_distance / 2
 
    print("Geometric Median = (", int(current_point.x), ",", int(current_point.y), ") with minimum distance = ", "{0:.5f}".format(minimum_distance))
 
# Driver code
n = 2
arr = [
    Point(1, 1),
    Point(3, 3)
]
 
geometricMedian(arr, n)
 
# The code is contributed by Nidhi goel

                    

C#

using System;
using System.Collections;
 
// C# implementation of the approach
using System.Globalization;
 
// To store a point in 2-D space
class Point {
  public double x = 0;
  public double y = 0;
};
 
class HelloWorld {
 
  // Test points. These points are the left,
  // up, right and down relative neighbours
  // (arranged circularly) to the
  // current_point at a distance of
  // test_distance from current_point
  public static Point[] test_point = new Point[4];
 
  // Lowest Limit till which we are going
  // to run the main while loop
  // Lower the Limit higher the accuracy
  public static double lower_limit = 0.01;
 
 
  // Function to return the sum of Euclidean
  // Distances
  public static double distSum(Point p,Point[] arr, int n)
  {
    double sum = 0;
    for (int i = 0; i < n; i++) {
      double distx = Math.Abs(arr[i].x - p.x);
      double disty = Math.Abs(arr[i].y - p.y);
      sum += Math.Sqrt((distx * distx) + (disty * disty));
    }
 
    // Return the sum of Euclidean Distances
    return sum;
  }
 
  // Function to calculate the required
  // geometric median
  public static void geometricMedian(Point[] arr, int n)
  {
 
    // Current x coordinate and y coordinate
    Point current_point = new Point();
 
    for (int i = 0; i < n; i++) {
      current_point.x += arr[i].x;
      current_point.y += arr[i].y;
    }
 
    // Here current_point becomes the
    // Geographic MidPoint
    // Or Center of Gravity of equal
    // discrete mass distributions
    current_point.x /= n;
    current_point.y /= n;
 
    // minimum_distance becomes sum of
    // all distances from MidPoint to
    // all given points
    double minimum_distance = distSum(current_point, arr, n);
 
    int k = 0;
    while (k < n) {
      for (int i = 0; i < n; i++) {
 
        if(i == k) continue;
 
        Point newpoint = new Point();;
        newpoint.x = arr[i].x;
        newpoint.y = arr[i].y;
        double newd = distSum(newpoint, arr, n);
        if (newd < minimum_distance) {
          minimum_distance = newd;
          current_point.x = newpoint.x;
          current_point.y = newpoint.y;
        }
      }
      k++;
    }
 
    // Assume test_distance to be 1000
    double test_distance = 1000;
    int flag = 0;
 
    // Test loop for approximation starts here
    while (test_distance > lower_limit) {
 
      flag = 0;
 
      // Loop for iterating over all 4 neighbours
      for (int i = 0; i < 4; i++) {
 
        // Finding Neighbours done
        Point newpoint = new Point();;
        newpoint.x = current_point.x + (double)test_distance * test_point[i].x;
        newpoint.y = current_point.y + (double)test_distance * test_point[i].y;
 
        // New sum of Euclidean distances
        // from the neighbor to the given
        // data points
        double newd = distSum(newpoint, arr, n);
 
        if (newd < minimum_distance) {
 
          // Approximating and changing
          // current_point
          minimum_distance = newd;
          current_point.x = newpoint.x;
          current_point.y = newpoint.y;
          flag = 1;
          break;
        }
      }
 
      // This means none of the 4 neighbours
      // has the new minimum distance, hence
      // we divide by 2 and reiterate while
      // loop for better approximation
      if (flag == 0)
        test_distance /= 2;
    }
 
    // setting precision upto 5 decimal spaces.
    NumberFormatInfo setPrecision = new NumberFormatInfo();   
    setPrecision.NumberDecimalDigits = 5;   
 
    Console.Write("Geometric Median = (" + current_point.x +  ", " + current_point.y + ")");
    Console.WriteLine(" with minimum distance = " + minimum_distance.ToString("N", setPrecision));
 
  }
 
  static void Main() {
 
    // initialising test_point array.
    for(int i = 0; i < 4; i++){
      test_point[i] = new Point();
    }
    test_point[0].x = -1;
    test_point[0].y = 0;
    test_point[1].x = 0;
    test_point[1].y = 1;
    test_point[2].x = 1;
    test_point[2].y = 0;
    test_point[3].x = 0;
    test_point[3].y = -1;
 
    int n = 2;
    Point[] arr = new Point[n];
    for(int i = 0; i < n; i++){
      arr[i] = new Point();
    }
 
    arr[0].x = 1;
    arr[0].y = 1;
    arr[1].x = 3;
    arr[1].y = 3;
    geometricMedian(arr, n);
  }
}
 
// The code is contributed by Nidhi goel.

                    

Javascript

//JavaScript implementation of the approach
 
// To store a point in 2-D space
class Point {
    constructor(x, y){
        this.x = x;
        this.y = y;
    }
}
 
// Test points. These points are the left,
// up, right and down relative neighbours
// (arranged circularly) to the
// current_point at a distance of
// test_distance from current_point
let test_point = [new Point(-1, 0),
                  new Point(0, 1),
                  new Point( 1, 0),
                  new Point(0, -1)];
 
 
// Lowest Limit till which we are going
// to run the main while loop
// Lower the Limit higher the accuracy
let lower_limit = 0.01;
 
// Function to return the sum of Euclidean
// Distances
function distSum(p, arr, n)
{
    let sum = 0;
    for (let i = 0; i < n; i++) {
        let distx = Math.abs(arr[i].x - p.x);
        let disty = Math.abs(arr[i].y - p.y);
        sum += Math.sqrt((distx * distx) + (disty * disty));
    }
 
    // Return the sum of Euclidean Distances
    return sum;
}
 
// Function to calculate the required
// geometric median
function geometricMedian(arr, n)
{
 
    // Current x coordinate and y coordinate
    let current_point = new Point(0, 0);
     
    for (let i = 0; i < n; i++) {
        current_point.x = current_point.x + arr[i].x;
        current_point.y = current_point.y + arr[i].y;
    }
 
    // Here current_point becomes the
    // Geographic MidPoint
    // Or Center of Gravity of equal
    // discrete mass distributions
    current_point.x /= n;
    current_point.y /= n;
 
    // minimum_distance becomes sum of
    // all distances from MidPoint to
    // all given points
    let minimum_distance = distSum(current_point, arr, n);
 
    let k = 0;
    while (k < n) {
        for (let i = 0; i < n, i != k; i++) {
            let newpoint = new Point(0, 0);
            newpoint.x = arr[i].x;
            newpoint.y = arr[i].y;
            let newd = distSum(newpoint, arr, n);
            if (newd < minimum_distance) {
                minimum_distance = newd;
                current_point.x = newpoint.x;
                current_point.y = newpoint.y;
            }
        }
        k++;
    }
 
    // Assume test_distance to be 1000
    let test_distance = 1000;
    let flag = 0;
 
    // Test loop for approximation starts here
    while (test_distance > lower_limit) {
 
        flag = 0;
 
        // Loop for iterating over all 4 neighbours
        for (let i = 0; i < 4; i++) {
 
            // Finding Neighbours done
            let newpoint = new Point();
            newpoint.x = current_point.x + test_distance * test_point[i].x;
            newpoint.y = current_point.y + test_distance * test_point[i].y;
 
            // New sum of Euclidean distances
            // from the neighbor to the given
            // data points
            let newd = distSum(newpoint, arr, n);
 
            if (newd < minimum_distance) {
 
                // Approximating and changing
                // current_point
                minimum_distance = newd;
                current_point.x = newpoint.x;
                current_point.y = newpoint.y;
                flag = 1;
                break;
            }
        }
 
        // This means none of the 4 neighbours
        // has the new minimum distance, hence
        // we divide by 2 and reiterate while
        // loop for better approximation
        if (flag == 0)
            test_distance /= 2;
    }
 
    console.log("Geometric Median = (", current_point.x, ", ", current_point.y, ") with minimum distance = ", minimum_distance.toFixed(5));
}
 
// Driver code
let n = 2;
let arr = [
    new Point(1, 1),
    new Point(3, 3)
];
 
geometricMedian(arr, n);
 
// The code is contributed by Nidhi goel

                    

Output: 
Geometric Median = (2, 2) with minimum distance = 2.82843

 

References: Geometric Median, Center of minimum distance
 



Last Updated : 08 Feb, 2023
Like Article
Save Article
Previous
Next
Share your thoughts in the comments
Similar Reads