# теория · шаг 4 из 7
k-центр: самая дальняя точка, гарантия 2 и почему лучше нельзя
Коротко
Задача k-центра: поставить k складов так, чтобы самый дальний клиент был от ближайшего склада как
можно ближе. Жадное правило: первый склад где угодно, каждый следующий - у клиента, который сейчас
дальше всех от складов. Радиус выходит не больше 2 · OPT, и доказательство само выдаёт нижнюю
границу: k + 1 точек, попарно удалённых не меньше чем на найденный радиус. А лучше двойки не может
ни один быстрый алгоритм, если P ≠ NP: при расстояниях 1 и 2 приближение лучше двух решало бы
NP-полную задачу о доминирующем множестве.
Если ты докажешь гарантию 2 для выбора самой дальней точки, предъявишь по её ответу нижнюю границу оптимума и объяснишь, почему множитель меньше двух означал бы P = NP, листай до «Теперь сам».
Как думаешь, куда ставить второй склад
Двенадцать магазинов на карте, три склада. Цель - чтобы худшая доставка была как можно короче: важен самый дальний магазин, а не средний. Первый склад поставили у магазина в левом нижнем углу.
Ответь себе до чтения: куда ставить второй? В «центр тяжести» оставшихся магазинов? Рядом с самой
плотной группой? Правило этого урока делает то, что кажется грубым: ставит склад прямо у магазина,
который сейчас хуже всех обслужен. И это правило не просто разумно - лучшего множителя, чем у него, не
найдёт никто, пока не доказано P = NP.
На что это похоже
Пожарные части в новом районе. Первую построили. Где вторая? Там, куда первая доезжает дольше всего: именно этот дом сгорит первым. Третью - там, куда теперь дольше всего из обеих. Каждая новая часть закрывает самое больное место на текущий момент.
Почему это работает: если после всех построек остался дом в r минутах от ближайшей части, то каждая
часть когда-то тоже была «самым больным местом» - то есть стояла не ближе r к предыдущим. Значит, на
карте есть k + 1 точек, попарно далёких друг от друга. Никакая расстановка k частей не обслужит их
все быстро: двум точкам придётся делить одну часть.
Механизм: самая дальняя точка и свидетели
Задача. Точки, расстояния с неравенством треугольника, число k. Выбрать k центров среди точек и
минимизировать радиус - наибольшее расстояние от точки до ближайшего центра.
Жадное правило (Гонсалес, 1985). Первый центр - любая точка. Пока центров меньше k, новым
центром становится точка, наиболее удалённая от уже выбранных.
Гарантия 2. Пусть жадность закончила с радиусом r, и точка x удалена от всех центров на r.
Каждый центр в момент выбора был самой дальней точкой, и расстояния до центров только уменьшаются по мере
добавления новых. Поэтому k центров и точка x - это k + 1 точек, попарно удалённых не меньше чем на
r. Теперь возьми оптимальное решение с радиусом OPT. В нём k центров и k + 1 точек-свидетелей -
две из них обслуживает один центр c. По неравенству треугольника расстояние между ними не больше
d(·, c) + d(c, ·) ≤ 2 · OPT. Но оно не меньше r. Значит, r ≤ 2 · OPT.
Обрати внимание, что доказательство дало больше, чем гарантию: OPT ≥ r / 2 - и это число известно
после работы алгоритма. Свидетели - готовый сертификат, который можно показать заказчику.
Почему не лучше двух. Доминирующее множество: набор вершин графа, такой, что каждая вершина либо
в наборе, либо соседствует с вершиной из набора. Вопрос «есть ли доминирующее множество из k вершин»
NP-полон. Сделаем из графа задачу о центрах: расстояние 1 между соседями, 2 между остальными
(неравенство треугольника выполняется: 2 ≤ 1 + 1). Радиус любого набора из k < n центров - 1 или 2, и радиус 1
бывает ровно тогда, когда центры образуют доминирующее множество. Алгоритм с множителем α < 2 при
оптимуме 1 обязан выдать радиус меньше 2, то есть 1, - и тем самым ответить на NP-полный вопрос. Такой
приём - разрыв между ответами «да» и «нет», который приближение обязано различить, - подробно разобран
в главе о пределах приближенияРазбирается в разделе 22, глава «Пределы приближения - PTAS, APX и почему дальше нельзя»: Разрыв: почему некоторые задачи нельзя приблизить.
Правило
Для k-центра с неравенством треугольника выбор каждый раз самой дальней точки даёт радиус не больше
2 · OPT: центры вместе с самой дальней оставшейся точкой - k + 1 точек, попарно удалённых хотя бы на
радиус, и две из них в оптимуме делят центр. Множитель 2 неулучшаем при P ≠ NP.
Разбор: двенадцать магазинов, три склада
dalnie - жадный выбор, optimum_troek - перебор всех троек центров, svidetel - самая дальняя точка после
выбора.
import math
def rasst(a, b):
return math.sqrt((a[0] - b[0]) ** 2 + (a[1] - b[1]) ** 2)
def do_centrov(tochki, centry):
d = []
for p in tochki:
luchshee = math.inf
for c in centry:
luchshee = min(luchshee, rasst(p, tochki[c]))
d.append(luchshee)
return d
def dalnie(tochki, k):
centry = [0]
while len(centry) < k:
d = do_centrov(tochki, centry)
dalnij = 0
for i in range(len(tochki)):
if d[i] > d[dalnij]:
dalnij = i
centry.append(dalnij)
return centry
def optimum_troek(tochki):
n = len(tochki)
luchshee = math.inf
for a in range(n):
for b in range(a + 1, n):
for c in range(b + 1, n):
r = max(do_centrov(tochki, [a, b, c]))
luchshee = min(luchshee, r)
return luchshee
tochki = [[1, 1], [2, 3], [3, 1], [8, 2], [9, 4], [10, 1],
[4, 9], [5, 11], [6, 9], [14, 10], [2, 6], [12, 7]]
centry = dalnie(tochki, 3)
d = do_centrov(tochki, centry)
r = max(d)
svidetel = 0
for i in range(len(tochki)):
if d[i] > d[svidetel]:
svidetel = i
print(centry, round(r, 3), svidetel)
print(round(r / 2, 3), round(optimum_troek(tochki), 3))
[0, 9, 7] 9.0 5
4.5 5.831
Первый склад у магазина 0 (1, 1). Дальше всех от него магазин 9 (14, 10) - второй склад там. Третий -
у магазина 7 (5, 11), верхней группы. Радиус 9: магазин 5 (10, 1) в девяти единицах от первого
склада. Перебор всех 220 троек говорит, что лучше - 5,831, отношение 1,54.
Но главное - строка без перебора: 4.5. Магазины 0, 9, 7 и 5 попарно удалены не меньше чем на 9, а
значит, любые три склада оставят какую-то пару из них на одном складе, и худшая доставка будет не меньше
4,5. Отчёт без перебора выглядит так: «наш план - 9, лучший возможный - не меньше 4,5». Точный оптимум
5,831 лежит внутри вилки.
Откуда потеря. Перебор находит оптимум со складами у магазинов 10 (2, 6), 5 (10, 1) и 9 (14, 10) -
одинокая точка в углу есть и в нём. Проигрыш жадности - в первом складе: он стоит в углу (1, 1), и
левым группам достаётся склад, а не середина между ними. Старт решает, а правило «самая дальняя» его не
исправляет.
Разбор: где множитель 2 становится точным
Граф превращён в таблицу расстояний 1 и 2, как в доводе о невозможности. Два графа на шести вершинах, два центра.
def tablica(n, ryobra):
d = []
for i in range(n):
d.append([2] * n)
d[i][i] = 0
for r in ryobra:
d[r[0]][r[1]] = 1
d[r[1]][r[0]] = 1
return d
def radius(d, centry):
hudshee = 0
for v in range(len(d)):
blizh = 2
for c in centry:
blizh = min(blizh, d[v][c])
hudshee = max(hudshee, blizh)
return hudshee
def dalnie(d, k):
centry = [0]
while len(centry) < k:
dalnij = 0
hud = -1
for v in range(len(d)):
blizh = 2
for c in centry:
blizh = min(blizh, d[v][c])
if blizh > hud:
hud = blizh
dalnij = v
centry.append(dalnij)
return centry
def luchshie(d):
n = len(d)
best = [2, []]
for a in range(n):
for b in range(a + 1, n):
if radius(d, [a, b]) < best[0]:
best = [radius(d, [a, b]), [a, b]]
return best
cikl = tablica(6, [[0, 1], [1, 2], [2, 3], [3, 4], [4, 5], [5, 0]])
c = dalnie(cikl, 2)
print(c, radius(cikl, c), luchshie(cikl))
zvezdy = tablica(6, [[0, 1], [0, 2], [3, 4], [3, 5], [1, 2]])
c = dalnie(zvezdy, 2)
print(c, radius(zvezdy, c), luchshie(zvezdy))
[0, 2] 2 [1, [0, 3]]
[0, 3] 1 [1, [0, 3]]
Цикл из шести вершин: доминирующее множество {0, 3} есть (каждая вершина цикла рядом с 0 или 3),
оптимум 1. Жадность выбрала 0, затем первую из самых дальних - вершину 2 (расстояние 2), и вершина 4
осталась на расстоянии 2: ответ ровно вдвое хуже оптимума. Во втором графе жадности повезло: самая
дальняя от 0 вершина - 3, и она сразу закрыла вторую звезду.
Этот разбор и есть довод о невозможности в действии. В мире расстояний 1 и 2 у любого алгоритма ответ либо 1, либо 2 - промежуточного нет. Множитель 2 позволяет ошибаться ровно на эту ступеньку. Множитель 1,99 ошибаться уже не позволяет, а значит, обязан находить доминирующие множества.
Что ломается без этого
Нет неравенства треугольника. Шаг «две точки у одного центра, значит, между ними не больше
2 · OPT» держится на нём. Без него гарантии нет никакой - как у коммивояжёра.
Путают k-центр с k-средними. k-центр минимизирует худшее расстояние и чувствителен к выбросам: одна дальняя точка оттягивает на себя целый склад. Кластеризация k-средних минимизирует сумму квадратов расстояний - это другая цель, к выбросам она тоже чувствительна (квадрат большого расстояния велик), но выброс в ней сдвигает центр группы, а не обязательно забирает центр себе. Решают её локальным поискомРазбирается в разделе 22, глава «Локальный поиск, отжиг, эвристики»: Локальный поиск: сосед, улучшение и локальный оптимум Ллойда. Взять одну цель и мерить другой - частая ошибка постановки.
Ищут множитель 1,5 «посложнее». Если P ≠ NP, его нет - для этой задачи двойка не рубеж
сегодняшних знаний, а предел.
Зачем это в работе
Размещение складов, пунктов выдачи, кешей CDN, станций скорой помощи - везде, где обещание звучит как
«не дальше N минут до любого клиента». Самая дальняя точка - двадцать строк, O(n · k) вычислений
расстояний, если хранить для каждой точки расстояние до ближайшего центра (программа урока для
простоты пересчитывает его заново - O(n · k²)), и к ответу прилагается сертификат: k + 1 точек, которые попарно далеки, и «лучше половины
нашего радиуса не сможет никто». Это ответ на вопрос «а может, другой подрядчик сделает лучше» - не
словами, а точками на карте.
Похожая идея - стандартный способ рассадить начальные центры для k-средних: в k-means++ следующий центр выбирается случайно, с вероятностью, пропорциональной квадрату расстояния до ближайшего центра, так что дальние точки выбираются чаще. Разнесённые центры спасают от плохого начала.
Теперь сам
- Точки на прямой:
0, 1, 2, 10, 11, 12, 20. Два центра, первый - в точке 0. Что выберет жадность, каков радиус, какую нижнюю границу дают свидетели и каков оптимум? - Почему в доказательстве свидетелей
k + 1, а неk? - Кто-то утверждает, что нашёл быстрый алгоритм для k-центра с множителем 1,8 при любых расстояниях с неравенством треугольника. Что из этого следует?
Ответ. 1. Дальше всех от 0 - точка 20, она второй центр. Радиус 10: точка 10 в десяти и от 0, и от
20. Свидетели 0, 20, 10 попарно не ближе 10, граница OPT ≥ 5. Оптимум 8: центры 2 и 12 - точки 0, 1,
2, 10 в пределах 8 от центра 2, а 11, 12, 20 в пределах 8 от центра 12. Меньше нельзя: точку 20 должен
закрыть какой-то центр, и если это не она сама, то он не дальше 12, а значит, в восьми и больше от неё;
если же центр в 20, второй центр должен быть не дальше 8 и от 0, и от 11 (до 20 от 11 - девять), то
есть стоять между 3 и 8 - а точек там нет. Отношение 10 / 8 = 1,25.
-
Из
kточек каждая может получить собственный центр в оптимуме, и пары у одного центра не выйдет. Принцип ящиков требует одну лишнюю точку. -
Что
P = NP: применив его к таблице расстояний 1 и 2, можно было бы находить доминирующие множества. Скорее всего, в алгоритме или в его анализе ошибка.
Главное
Самая дальняя точка - и алгоритм, и доказательство: точки, которые жадность выбрала, сами свидетельствуют, что оптимум не меньше половины её ответа. А расстояния 1 и 2 показывают, почему двойку не победить: на такой ступеньке приближение лучше двух уже не приближение, а точный ответ.
Комментарии
Пока нет комментариев. Будь первым!
Оставить комментарий