Алгоритм ближайшего соседа Quadtree

Я реализовал структуру дерева квадрантов для n точек, а также метод возврата массива точек внутри заданного прямоугольника. Кажется, я не могу найти алгоритм для эффективного поиска точки, ближайшей к другой заданной точке. Я упускаю что-то очевидное? Я предполагаю, что рекурсивное решение - правильный подход?

Я работаю в Objective C, но псевдокод будет в порядке. Кроме того, я фактически храню широчайшие, длинные данные, а расстояние между точками находится по большому кругу.

EDIT: Это код вставки и разделения моего дерева.

- (BOOL)insert:(id<PASQuadTreeDataPoint>)dataPoint {

    BOOL pointAdded = false;

    // If the point lies within the region
    if(CGRectContainsPoint(self.region, dataPoint.point)) {

        // If there are less than 4 points then add this point
        if(self.dataPoints.count < kMaxPointsPerNode) {
            [self.dataPoints addObject:dataPoint];
            pointAdded = true;
        }
        else {

            // Subdivide into 4 quadrants if not already subdivided
            if(northEast == nil) [self subdivide];

            // Attempt to add the point to one of the 4 subdivided quadrants
            if([northEast insert:dataPoint]) return true;
            if([southEast insert:dataPoint]) return true;
            if([southWest insert:dataPoint]) return true;
            if([northWest insert:dataPoint]) return true;
        }
    }

    return pointAdded;
}

- (void)subdivide {

    // Compute the half width and the origin
    CGFloat width = self.region.size.width * 0.5f;
    CGFloat height = self.region.size.height * 0.5f;
    CGFloat x = self.region.origin.x;
    CGFloat y = self.region.origin.y;

    // Create a new child quadtree with the region divided into quarters
    self.northEast = [PASQuadTree quadTreeWithRegion:CGRectMake(x + width, y, width, height)];
    self.southEast = [PASQuadTree quadTreeWithRegion:CGRectMake(x + width, y + height, width, height)];
    self.southWest = [PASQuadTree quadTreeWithRegion:CGRectMake(x, y + height, width, height)];
    self.northWest = [PASQuadTree quadTreeWithRegion:CGRectMake(x, y, width, height)];
}

EDIT: Написал этот код, чтобы найти наименьший узел (лист), который будет содержать точку:

-(PASQuadTree *)nodeThatWouldContainPoint:(CGPoint)point {

    PASQuadTree *node = nil;

    // If the point is within the region
    if (CGRectContainsPoint(self.region, point)) {

        // Set the node to this node
        node = self;

        // If the node has children
        if (self.northEast != nil) {

            // Recursively check each child to see if it would contain the point
            PASQuadTree *childNode = [self.northEast nodeThatWouldContainPoint:point];
            if (!childNode) childNode = [self.southEast nodeThatWouldContainPoint:point];
            if (!childNode) childNode = [self.southWest nodeThatWouldContainPoint:point];
            if (!childNode) childNode = [self.northWest nodeThatWouldContainPoint:point];
            if (childNode) node = childNode;
        }
    }

    return node;
}

Ближе, но без сигары!


person Magic Bullet Dave    schedule 30.12.2013    source источник


Ответы (1)


Найдите наименьший квадрат с точкой поиска в центре и ровно с одной другой точкой внутри этого прямоугольника (вам нужно выполнить логарифмическое количество поисков).

Пусть x будет расстоянием до другой точки.

Затем найдите все точки внутри квадрата со стороной, равной 2x, с центром вокруг вашей первой точки. Для каждой точки внутри этого квадрата рассчитайте расстояние от точки поиска и найдите ближайшую.

ОБНОВЛЕНИЕ: Как найти один квадрат с центром вокруг точки поиска, который содержит ровно одну другую точку?

Найдите случайную точку. Пусть расстояние до этой случайной точки равно x. Найдите все точки в квадрате размера x с центром вокруг точки поиска. Если в этом квадрате ненулевое количество точек, то случайным образом выберите точку и повторите. Если точек нет, увеличьте размер квадрата поиска до (2-0,5)*x (на следующем шаге (2-0,25)*x и так далее.

person ElKamina    schedule 30.12.2013
comment
Спасибо, ЭльКамина, попробую и опубликую свой код в вопросе. В чем я не уверен, так это в том, что каждый узел будет иметь четыре точки и будет сегментирован на меньшие прямоугольники только тогда, когда емкость узла будет заполнена. Могут ли некоторые из этих точек также быть ближайшими точками? - person Magic Bullet Dave; 31.12.2013
comment
ОК, есть код для поиска наименьшей области, которая будет содержать точку (см. исходный вопрос). Пытаясь элегантно найти окружающие прямоугольники, плюс необходимо учитывать 4 точки, содержащиеся в каждом узле. Любые указатели будут полезны. Привет Дэйв. - person Magic Bullet Dave; 31.12.2013
comment
@MagicBulletDave Я просто использую функциональность quadtree, которая эффективно возвращает массив точек в заданном прямоугольнике. Я не использую никакие другие свойства. Я обновил свой ответ с дополнительным объяснением. - person ElKamina; 31.12.2013
comment
Спасибо, ЭльКамина, было немного глупо :о) - person Magic Bullet Dave; 31.12.2013
comment
Каждый дочерний элемент должен иметь указатель на своего родителя. с этим простым изменением вы в основном можете перемещаться вверх. Когда вы найдете наименьший прямоугольник, если у него недостаточно точек, вы можете подняться вверх и проверить критерии поиска по отношению к родителю. Другим решением было бы соединение листовых узлов с указателем на своего родственного узла, создающего дважды LinkedList. Вы в основном предварительно обрабатываете дерево квадрантов. Во время поиска вы находите наименьшую сетку в дереве квадрантов, оттуда вы идете вправо и влево в LinkedList, чтобы найти правильные сетки с вашими критериями поиска. - person Reza; 29.12.2020