Учитывая набор из n точек, найдите две точки, наиболее удаленные друг от друга. Нам нужно использовать алгоритм 2-приближения, что означает, что на самом деле нам не нужно точно найти эти две точки, нам просто нужно найти набор точек, расстояние между которыми составляет как минимум половину самого дальнего расстояния, алгоритм должен работать в линейное время.
Я попробовал WSPD (разложение хорошо разделенных пар), но построение самого WSPD занимает время O(n*logn). Так что это не верное решение.
Спасибо за ответ! Они могут быть многомерными, и требуется O(d*n), d — размерность. И набор точек сохраняется в списке. Мне пришла в голову идея: сначала мы можем вычислить минимальный охватывающий диск, используя рандомизированный инкрементальный алгоритм, и нам просто нужно найти две точки, расстояние до которых больше или равно радиусу этого диска. Но я не знаю, как найти такую пару точек.





Это может сработать, но тщательно протестируйте его на данных и проверьте:
class FindPair(object):
def __init__(self, points):
self.P = points
self.dists = [0 for i in range(len(self.P))]
def _shifted(self):
Pm = []
for x, y, z in self.P:
Pm += [(x + 10, y + 10, z + 10)]
return Pm
def _eucl(self, x, y, z):
return x ** 2 + y ** 2 + z ** 2
def _search(self):
Pm = self._shifted()
for i, (x, y, z) in enumerate(Pm):
self.dists[i] = self._eucl(x, y, z)
return self.dists
def get_max_dist(self):
self._search()
min_dist, max_dist = min(self.dists), max(self.dists)
imin, imax = self.dists.index(min_dist), self.dists.index(max_dist)
return imin, imax
def get_points(self):
i, j = self.get_max_dist()
return self.P[i], self.P[j]
base_points = ((0, 0, 0), (1, 0, 0), (1, 1, 0), (0, 1, 0), (-1, 1, 0), (-1, 0, 0), (-1, -1, 0), (0, -1, 0), (1, -1, 0),
(0, 0, 1), (1, 0, 1), (1, 1, 1), (0, 1, 1), (-1, 1, 1), (-1, 0, 1), (-1, -1, 1), (0, -1, 1), (1, -1, 1),
(0, 0, -1), (1, 0, -1), (1, 1, -1), (0, 1, -1), (-1, 1, -1), (-1, 0, -1), (-1, -1, -1), (0, -1, -1), (1, -1, -1))
populations = []
for i in range(100, 105):
for j in range(200, 205):
for k in range(300, 305):
for x, _, _ in base_points:
for _, y, _ in base_points:
for _, _, z in base_points:
populations += [(x + i, y + j, z + k)]
finder = FindPair(populations)
print(finder.get_points())
print(len(populations))
((99, 199, 299), (105, 205, 305))
2460375
Этот подход аналогичен набору «воздушных змеев в небе», все они привязаны к одному основанию в земле. Мы хотим найти два кайта с максимальным и минимальным расстоянием до основания в земле.
import math
class FindPair(object):
def __init__(self, points):
self.P = points
self.dists = [0 for i in range(len(self.P))]
def _shifted(self):
Pm = []
for x, y, z in self.P:
Pm += [(x + 1000, y + 1000, z + 1000)]
return Pm
def _eucl(self, x, y, z):
return x ** 2 + y ** 2 + z ** 2
def _search(self):
Pm = self._shifted()
for i, (x, y, z) in enumerate(Pm):
self.dists[i] = self._eucl(x, y, z)
return self.dists
def get_max_dist(self):
self._search()
min_dist, max_dist = min(self.dists), max(self.dists)
imin, imax = self.dists.index(min_dist), self.dists.index(max_dist)
return imin, imax
def get_points(self):
i, j = self.get_max_dist()
return self.P[i], self.P[j]
class BruteForce(object):
def __init__(self, points):
self.P = points
def _euclidean_distance(self, p1, p2):
return math.sqrt((p1[0] - p2[0]) ** 2 + (p1[1] - p2[1]) ** 2 + (p1[2] - p2[2]) ** 2)
def get_max_dist(self):
max_dist = 0
max_pair = (None, None)
for i in range(len(self.P)):
for j in range(i + 1, len(self.P)):
dist = self._euclidean_distance(self.P[i], self.P[j])
if dist > max_dist:
max_dist = dist
max_pair = (self.P[i], self.P[j])
return max_pair
base_points = ((0, -3, 0), (1, 3, 0), (0, 3, 0), (-1, 3, 0), (0, 1, 0))
# populations = []
# for i in range(100, 105):
# for j in range(200, 205):
# for k in range(300, 305):
# for x, _, _ in base_points:
# for _, y, _ in base_points:
# for _, _, z in base_points:
# populations += [(x + i, y + j, z + k)]
finder = FindPair(base_points)
print(finder.get_points())
brute_force = BruteForce(base_points)
print(brute_force.get_max_dist())
# print(len(populations))
((0, -3, 0), (1, 3, 0))
((0, -3, 0), (1, 3, 0))
@btilly в комментариях, похоже, прав. Хотя я еще не уверен, потому что этот подход «слишком прост» для поставленной задачи, требующей методов аппроксимации (возможно, даже NP-полных).
Проблемы могут возникнуть, когда точки находятся слишком далеко друг от друга и имеют бесконечные размеры, и их смещение может быть непростым или вообще нежелательным или может слишком усложнить вычисления. Если у нас бесконечные измерения, то это NP-полная задача.
Однако для 1000 измерений мы можем изменить уравнение расстояния до x1 ^ 2 + x2 ^ 2 + x3 ^ 2 + ... + x1000 ^ 2.
Если наши числа x, y, z,... слишком велики, мы могли бы нормализовать все числа до диапазона небольших чисел и использовать вычисления двойной точности.
Есть еще кое-что, что следует принять во внимание.
Я думаю, что здесь отражены почти все ключевые идеи, но это не совсем правильно. Что произойдет, если минимальное и максимальное расстояние будут в одном направлении?
@btilly Возможно, ты прав. Однако я не слежу за этим. Можете ли вы выделить несколько пунктов и посмотреть, как это направление будет противоречить?
@btilly Я использовал район Мур и изначально добавил 24 направления в base_points?
Для исходного решения [(0, -3, 0), (1, 3, 0), (0, 3, 0), (-1, 3, 0), (0, 1, 0)] является контрпримером. Проблема вообще в использовании минимальной точки — она не нужна.
@btilly Я получаю эти два балла: ((0, -3, 0), (1, 3, 0)). Какие два балла мы должны получить?
Хм, я неправильно прочитал? Видимо так. Если да, то что вы используете в качестве базовых точек, если у вас 1000 измерений?
Увидев некоторые идеи @user24714692, я увидел простой ответ.
c множества точек. Оно обладает тем свойством, что для любой точки p1 в точках, не находящейся в центре, должна быть другая точка p2 «по другую сторону от центра», то есть с (p1-c) ⋅ (p2-c) < 0.p1, самую дальнюю точку от c. Самое дальнее расстояние между двумя точками не может быть более чем в 2 раза больше расстояния этой точки от центра.p2, самую дальнюю точку от p1. Любая точка «по другую сторону центра» от p2 должна быть дальше центра. И вот (p1, p2) наш ответ.Вот код для этого.
# This is O(d) where d is the number of dimensions.
def norm2 (point):
return sum((point[i] * point[i] for i in range(len(point))))
# This is O(n*d)
def center (points):
return tuple((
sum(point[i] for point in points)/len(points)
for i in range(len(points[0]))
))
# This is O(d)
def difference (point1, point2):
return tuple((
point1[i] - point2[i]
for i in range(len(point1))
))
# This is O(n*d)
def farthest_from_position(points, position):
farthest_point = points[0]
farthest_d2 = norm2(difference(farthest_point, position))
for point in points:
d2 = norm2(difference(point, position))
if farthest_d2 < d2:
farthest_d2 = d2
farthest_point = point
return farthest_point
# This is O(n*d)
# Returns a pair guaranteed to be at least half the maximum distance apart.
def far_pair(points):
c = center(points)
p1 = farthest_from_position(points, c)
p2 = farthest_from_position(points, p1)
return (p1, p2)
Удивительно, но ответ на этот вопрос довольно прост:
Случайным образом выберите точку из A, найдите самую дальнюю точку B от A, верните расстояние и готово!
Привет! Несколько вопросов: (1) Какая структура данных содержит набор точек? Это просто бесструктурный список пунктов? (2) Находятся ли точки в 2-мерном, 3-мерном или более крупномерном пространстве?