LC 1515. Best Position for a Service Centre
https://leetcode.com/problems/best-position-for-a-service-centre/
https://leetcode-cn.com/problems/best-position-for-a-service-centre/solution/
题解写的非常好,我只看了前面两种解法。其中梯度下降方法学习ML的时候遇到过,但是没有想到可以在这里使用, 或许今后可以在工作中多多使用,好像也没有那么困难。另外一个方法就是爬山法,用于凸函数的话可以得到最优解。
我总结了一下爬山法的大致框架:
- 首先随机选择一个起始点(x, y),以及探索距离 `step`
- 开始下面的迭代
- 在各个方向上,以 `step` 去做探索
- 如果在某个方向上有更好的结果,那么跳到这个方向上
- 如果没有更好的,那么 `step=step/2`
- 迭代直到 `step` 到非常小的值
class Solution:
def getMinDistSum(self, positions: List[List[int]]) -> float:
step = 100
xs = [x[0] for x in positions]
ys = [x[1] for x in positions]
n = len(positions)
xc = sum(xs) / n
yc = sum(ys) / n
def dist(xc, yc):
res = 0
for i in range(n):
t0 = (xc - xs[i]) ** 2
t1 = (yc - ys[i]) ** 2
res += (t0 + t1) ** 0.5
return res
while step > 1e-7:
iter = False
for dx, dy in ((-1, 0), (1, 0), (0, -1), (0, 1)):
xc2 = xc + dx * step
yc2 = yc + dy * step
if dist(xc2, yc2) < dist(xc, yc):
iter = True
xc, yc = xc2, yc2
break
if not iter:
step = step / 2
ans = round(dist(xc, yc), 5)
return ans
梯度下降算法好像也不是特别困难,最主要的是对cost function求导。求导函数在题解连接里面有,也不是特别复杂。虽然题解说有一定几率能过,但是显然这个几率有点低,不太适合在比赛时候使用。
class Solution2:
def getMinDistSum(self, positions: List[List[int]]) -> float:
decay = 1 - 0.001
batchSize = 100
eps = 1e-7
alpha = 1
xs = [x[0] for x in positions]
ys = [x[1] for x in positions]
n = len(positions)
xc = sum(xs) / n
yc = sum(ys) / n
def dist(xc, yc):
res = 0
for i in range(n):
t0 = (xc - xs[i]) ** 2
t1 = (yc - ys[i]) ** 2
res += (t0 + t1) ** 0.5
return res
value = dist(xc, yc)
while True:
random.shuffle(positions)
dx, dy = 0, 0
for i in range(min(n, batchSize)):
d = ((xc - xs[i]) ** 2 + (yc - ys[i]) ** 2) ** 0.5
dx += (xc - xs[i]) / (d + eps)
dy += (yc - ys[i]) / (d + eps)
xc -= alpha * dx
yc -= alpha * dy
alpha *= decay
newValue = dist(xc, yc)
if abs(newValue - value) < 1e-7:
break
value = newValue
ans = round(value, 5)
return ans