You are given an array positions
where each element is a pair [x, y]
representing the coordinates of a customer in a 2D plane. Your task is to determine the optimal position [cx, cy]
to build a new service centre such that the sum of the Euclidean distances from the service centre to all customers is minimized.
[cx, cy]
.1 <= positions.length <= 50
positions[i].length == 2
, 0 <= positions[i][0], positions[i][1] <= 100
At first glance, this problem seems to ask for a "center" point that minimizes the total distance to all customers. The naive approach would be to check every possible point in the plane, but this is not feasible due to the infinite number of possible points and real-number precision.
Unlike the geometric median in 1-D (where the median minimizes sum of distances), in 2-D there is no closed-form solution for the geometric median. We need an efficient way to approximate this point. The centroid (arithmetic mean) minimizes the sum of squared distances, but not the sum of distances. Therefore, we need an iterative optimization technique.
The best-known approach for this is Weiszfeld's algorithm, which is a practical method to find the geometric median in higher dimensions.
To solve this problem efficiently and accurately, we use Weiszfeld's algorithm, which iteratively improves the guess for the service centre location.
(xi, yi)
, compute the distance d = sqrt((x - xi)^2 + (y - yi)^2)
.
1/d
.(x, y)
is:
x = sum(xi / di) / sum(1 / di)
y = sum(yi / di) / sum(1 / di)
1e-7
), or a maximum number of iterations is reached.
(x, y)
is the location that minimizes the sum of distances to all customers.
Design Choices:
Sample Input:
positions = [[0,0],[1,0],[0,1]]Step-by-step:
x = (0+1+0)/3 = 0.333...
, y = (0+0+1)/3 = 0.333...
1/distance
.(0.29289, 0.29289)
(the geometric median).This process ensures the sum of distances from the service centre to all customers is minimized.
class Solution:
def getMinDistSum(self, positions):
import math
n = len(positions)
# Initial guess: centroid
x = sum(p[0] for p in positions) / n
y = sum(p[1] for p in positions) / n
eps = 1e-7
for _ in range(100):
num_x = num_y = denom = 0
for px, py in positions:
d = math.hypot(x - px, y - py)
if d < eps:
# If very close to a customer, use their position
x, y = px, py
break
num_x += px / d
num_y += py / d
denom += 1 / d
else:
new_x = num_x / denom
new_y = num_y / denom
if abs(new_x - x) < eps and abs(new_y - y) < eps:
break
x, y = new_x, new_y
# Return the minimized sum of distances
return sum(math.hypot(x - px, y - py) for px, py in positions)
class Solution {
public:
double getMinDistSum(vector<vector<int>>& positions) {
int n = positions.size();
double x = 0, y = 0;
for (auto& p : positions) {
x += p[0];
y += p[1];
}
x /= n;
y /= n;
const double eps = 1e-7;
for (int iter = 0; iter < 100; ++iter) {
double num_x = 0, num_y = 0, denom = 0;
bool close = false;
for (auto& p : positions) {
double dx = x - p[0], dy = y - p[1];
double d = sqrt(dx * dx + dy * dy);
if (d < eps) {
x = p[0];
y = p[1];
close = true;
break;
}
num_x += p[0] / d;
num_y += p[1] / d;
denom += 1.0 / d;
}
if (close) continue;
double new_x = num_x / denom;
double new_y = num_y / denom;
if (abs(new_x - x) < eps && abs(new_y - y) < eps) break;
x = new_x;
y = new_y;
}
double res = 0;
for (auto& p : positions) {
res += sqrt((x - p[0]) * (x - p[0]) + (y - p[1]) * (y - p[1]));
}
return res;
}
};
class Solution {
public double getMinDistSum(int[][] positions) {
int n = positions.length;
double x = 0, y = 0;
for (int[] p : positions) {
x += p[0];
y += p[1];
}
x /= n;
y /= n;
final double eps = 1e-7;
for (int iter = 0; iter < 100; ++iter) {
double num_x = 0, num_y = 0, denom = 0;
boolean close = false;
for (int[] p : positions) {
double dx = x - p[0], dy = y - p[1];
double d = Math.hypot(dx, dy);
if (d < eps) {
x = p[0];
y = p[1];
close = true;
break;
}
num_x += p[0] / d;
num_y += p[1] / d;
denom += 1.0 / d;
}
if (close) continue;
double new_x = num_x / denom;
double new_y = num_y / denom;
if (Math.abs(new_x - x) < eps && Math.abs(new_y - y) < eps) break;
x = new_x;
y = new_y;
}
double res = 0;
for (int[] p : positions) {
res += Math.hypot(x - p[0], y - p[1]);
}
return res;
}
}
var getMinDistSum = function(positions) {
let n = positions.length;
let x = 0, y = 0;
for (let p of positions) {
x += p[0];
y += p[1];
}
x /= n;
y /= n;
const eps = 1e-7;
for (let iter = 0; iter < 100; ++iter) {
let num_x = 0, num_y = 0, denom = 0;
let close = false;
for (let p of positions) {
let dx = x - p[0], dy = y - p[1];
let d = Math.hypot(dx, dy);
if (d < eps) {
x = p[0];
y = p[1];
close = true;
break;
}
num_x += p[0] / d;
num_y += p[1] / d;
denom += 1 / d;
}
if (close) continue;
let new_x = num_x / denom;
let new_y = num_y / denom;
if (Math.abs(new_x - x) < eps && Math.abs(new_y - y) < eps) break;
x = new_x;
y = new_y;
}
let res = 0;
for (let p of positions) {
res += Math.hypot(x - p[0], y - p[1]);
}
return res;
};
The "Best Position for a Service Centre" problem asks for a location in 2D space minimizing the sum of Euclidean distances to a set of customers. While brute-force is infeasible due to infinite possible positions, Weiszfeld's algorithm efficiently finds the geometric median using simple, iterative updates. The approach is both elegant and practical, leveraging the properties of the Euclidean plane and converging quickly for small datasets. This solution highlights the power of iterative optimization for problems without closed-form solutions.