1515. 服务中心的最佳位置
一家快递公司希望在新城市建立新的服务中心。公司统计了该城市所有客户在二维地图上的坐标,并希望能够以此为依据为新的服务中心选址:使服务中心 到所有客户的欧几里得距离的总和最小 。
给你一个数组 positions
,其中 positions[i] = [xi, yi]
表示第 i
个客户在二维地图上的位置,返回到所有客户的 欧几里得距离的最小总和 。
换句话说,请你为服务中心选址,该位置的坐标 [xcentre, ycentre]
需要使下面的公式取到最小值:
与真实值误差在 10-5
之内的答案将被视作正确答案。
示例 1:
输入:positions = [[0,1],[1,0],[1,2],[2,1]] 输出:4.00000 解释:如图所示,你可以选 [xcentre, ycentre] = [1, 1] 作为新中心的位置,这样一来到每个客户的距离就都是 1,所有距离之和为 4 ,这也是可以找到的最小值。
示例 2:
输入:positions = [[1,1],[3,3]] 输出:2.82843 解释:欧几里得距离可能的最小总和为 sqrt(2) + sqrt(2) = 2.82843
提示:
1 <= positions.length <= 50
positions[i].length == 2
0 <= xi, yi <= 100
原站题解
java 解法, 执行用时: 66 ms, 内存消耗: 41.9 MB, 提交时间: 2023-09-06 23:35:05
// 梯度下降 class Solution { public double getMinDistSum(int[][] positions) { double eps = 1e-7; double alpha = 1; double decay = 1e-3; int n = positions.length; // 调整批大小 int batchSize = n; double x = 0.0, y = 0.0; for (int[] pos : positions) { x += pos[0]; y += pos[1]; } x /= n; y /= n; while (true) { // 将数据随机打乱 shuffle(positions); double xPrev = x; double yPrev = y; for (int i = 0; i < n; i += batchSize) { int j = Math.min(i + batchSize, n); double dx = 0.0, dy = 0.0; // 计算导数,注意处理分母为零的情况 for (int k = i; k < j; ++k) { int[] pos = positions[k]; dx += (x - pos[0]) / (Math.sqrt((x - pos[0]) * (x - pos[0]) + (y - pos[1]) * (y - pos[1])) + eps); dy += (y - pos[1]) / (Math.sqrt((x - pos[0]) * (x - pos[0]) + (y - pos[1]) * (y - pos[1])) + eps); } x -= alpha * dx; y -= alpha * dy; // 每一轮迭代后,将学习率进行衰减 alpha *= (1.0 - decay); } // 判断是否结束迭代 if (Math.sqrt((x - xPrev) * (x - xPrev) + (y - yPrev) * (y - yPrev)) < eps) { break; } } return getDist(x, y, positions); } public void shuffle(int[][] positions) { Random rand = new Random(); int n = positions.length; for (int i = 0; i < n; i++) { int x = positions[i][0], y = positions[i][1]; int randIndex = rand.nextInt(n); positions[i][0] = positions[randIndex][0]; positions[i][1] = positions[randIndex][1]; positions[randIndex][0] = x; positions[randIndex][1] = y; } } // 计算服务中心 (xc, yc) 到客户的欧几里得距离之和 public double getDist(double xc, double yc, int[][] positions) { double ans = 0; for (int[] pos : positions) { ans += Math.sqrt((pos[0] - xc) * (pos[0] - xc) + (pos[1] - yc) * (pos[1] - yc)); } return ans; } }
java 解法, 执行用时: 8 ms, 内存消耗: 39.3 MB, 提交时间: 2023-09-06 23:34:38
// 爬山法 class Solution { private static int[][] dirs = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}}; public double getMinDistSum(int[][] positions) { double eps = 1e-7; double step = 1; double decay = 0.5; int n = positions.length; double x = 0.0, y = 0.0; for (int[] pos : positions) { x += pos[0]; y += pos[1]; } x /= n; y /= n; while (step > eps) { boolean modified = false; for (int i = 0; i < 4; ++i) { double xNext = x + step * dirs[i][0]; double yNext = y + step * dirs[i][1]; if (getDist(xNext, yNext, positions) < getDist(x, y, positions)) { x = xNext; y = yNext; modified = true; break; } } if (!modified) { step *= (1.0 - decay); } } return getDist(x, y, positions); } // 计算服务中心 (xc, yc) 到客户的欧几里得距离之和 public double getDist(double xc, double yc, int[][] positions) { double ans = 0; for (int[] pos : positions) { ans += Math.sqrt((pos[0] - xc) * (pos[0] - xc) + (pos[1] - yc) * (pos[1] - yc)); } return ans; } }
java 解法, 执行用时: 217 ms, 内存消耗: 38.8 MB, 提交时间: 2023-09-06 23:34:15
// 三分查找 class Solution { public double getMinDistSum(int[][] positions) { double eps = 1e-7; double xLeft = 0.0, xRight = 100.0; while (xRight - xLeft > eps) { // 左 1/3 点 double xFirst = (xLeft + xLeft + xRight) / 3; // 右 1/3 点 double xSecond = (xLeft + xRight + xRight) / 3; if (checkOptimal(xFirst, positions, eps) < checkOptimal(xSecond, positions, eps)) { xRight = xSecond; } else { xLeft = xFirst; } } return checkOptimal(xLeft, positions, eps); } // 计算服务中心 (xc, yc) 到客户的欧几里得距离之和 public double getDist(double xc, double yc, int[][] positions) { double ans = 0; for (int[] pos : positions) { ans += Math.sqrt((pos[0] - xc) * (pos[0] - xc) + (pos[1] - yc) * (pos[1] - yc)); } return ans; } // 固定 xc,使用三分法找出最优的 yc public double checkOptimal(double xc, int[][] positions, double eps) { double yLeft = 0.0, yRight = 100.0; while (yRight - yLeft > eps) { double yFirst = (yLeft + yLeft + yRight) / 3; double ySecond = (yLeft + yRight + yRight) / 3; if (getDist(xc, yFirst, positions) < getDist(xc, ySecond, positions)) { yRight = ySecond; } else { yLeft = yFirst; } } return getDist(xc, yLeft, positions); } }
python3 解法, 执行用时: 3448 ms, 内存消耗: 15.8 MB, 提交时间: 2023-09-06 23:33:33
# 三分查找 class Solution: def getMinDistSum(self, positions: List[List[int]]) -> float: eps = 1e-7 # 计算服务中心 (xc, yc) 到客户的欧几里得距离之和 getDist = lambda xc, yc: sum(((x - xc) ** 2 + (y - yc) ** 2) ** 0.5 for x, y in positions) # 固定 xc,使用三分法找出最优的 yc def checkOptimal(xc: float) -> float: yLeft, yRight = 0.0, 100.0 while yRight - yLeft > eps: yFirst = (yLeft + yLeft + yRight) / 3 ySecond = (yLeft + yRight + yRight) / 3 if getDist(xc, yFirst) < getDist(xc, ySecond): yRight = ySecond else: yLeft = yFirst return getDist(xc, yLeft) xLeft, xRight = 0.0, 100.0 while xRight - xLeft > eps: # 左 1/3 点 xFirst = (xLeft + xLeft + xRight) / 3 # 右 1/3 点 xSecond = (xLeft + xRight + xRight) / 3 if checkOptimal(xFirst) < checkOptimal(xSecond): xRight = xSecond else: xLeft = xFirst return checkOptimal(xLeft)
python3 解法, 执行用时: 152 ms, 内存消耗: 16.1 MB, 提交时间: 2023-09-06 23:33:05
# 爬山法 class Solution: def getMinDistSum(self, positions: List[List[int]]) -> float: dirs = [(-1, 0), (1, 0), (0, -1), (0, 1)] eps = 1e-7 step = 1.0 decay = 0.5 n = len(positions) x = sum(pos[0] for pos in positions) / n y = sum(pos[1] for pos in positions) / n # 计算服务中心 (xc, yc) 到客户的欧几里得距离之和 getDist = lambda xc, yc: sum(((x - xc) ** 2 + (y - yc) ** 2) ** 0.5 for x, y in positions) while step > eps: modified = False for dx, dy in dirs: xNext = x + step * dx yNext = y + step * dy if getDist(xNext, yNext) < getDist(x, y): x, y = xNext, yNext modified = True break if not modified: step *= (1.0 - decay) return getDist(x, y)
python3 解法, 执行用时: 1676 ms, 内存消耗: 16.1 MB, 提交时间: 2023-09-06 23:32:40
# 梯度下降 class Solution: def getMinDistSum(self, positions: List[List[int]]) -> float: eps = 1e-7 alpha = 1.0 decay = 1e-3 n = len(positions) # 调整批大小 batchSize = n x = sum(pos[0] for pos in positions) / n y = sum(pos[1] for pos in positions) / n # 计算服务中心 (xc, yc) 到客户的欧几里得距离之和 getDist = lambda xc, yc: sum(((x - xc) ** 2 + (y - yc) ** 2) ** 0.5 for x, y in positions) while True: # 将数据随机打乱 random.shuffle(positions) xPrev, yPrev = x, y for i in range(0, n, batchSize): j = min(i + batchSize, n) dx, dy = 0.0, 0.0 # 计算导数,注意处理分母为零的情况 for k in range(i, j): pos = positions[k] dx += (x - pos[0]) / (sqrt((x - pos[0]) * (x - pos[0]) + (y - pos[1]) * (y - pos[1])) + eps) dy += (y - pos[1]) / (sqrt((x - pos[0]) * (x - pos[0]) + (y - pos[1]) * (y - pos[1])) + eps) x -= alpha * dx y -= alpha * dy # 每一轮迭代后,将学习率进行衰减 alpha *= (1.0 - decay) # 判断是否结束迭代 if ((x - xPrev) ** 2 + (y - yPrev) ** 2) ** 0.5 < eps: break return getDist(x, y)