Найти ближайшую точку каждой точки (ближайший сосед)

Я пишу метод, который принимает в качестве входных данных массив точек и находит для каждой точки в массиве ближайшую к ней точку, кроме самой себя. В настоящее время я делаю это методом грубой силы (проверяя каждую точку с каждой другой точкой). В моей текущей реализации массив не отсортирован, но я могу отсортировать его по значениям p.x с помощью метода CompareByX. Я проверяю время работы алгоритма, и это занимает очень много времени при больших значениях n. Я не очень хорошо разбираюсь в этом вопросе и очень мало знаю о различных типах структур данных, любая простая помощь будет отличной!

Мой текущий код:

import java.util.*;
import java.lang.*;
import java.io.*;

class My2dPoint {
  double x;
  double y;

  public My2dPoint(double x1, double y1) {
    x=x1;
    y=y1;
  }

}


class CompareByX implements Comparator<My2dPoint> {
    public int compare(My2dPoint p1, My2dPoint p2) {
    if (p1.x < p2.x) return -1;
        if (p1.x == p2.x) return 0;
        return 1;
    }
}

    /* An object of the above comparator class is used by java.util.Arrays.sort() in main to sort an array of points by x-coordinates */

class Auxiliaries {

    public static double distSquared(My2dPoint p1, My2dPoint p2) {
        double result;
        result = (p1.x-p2.x)*(p1.x-p2.x) + (p1.y-p2.y)*(p1.y-p2.y);
        return result;
    }

}

public class HW3 {
    public static void main (String argv []) throws IOException {
        int range = 1000000; // Range of x and y coordinates in points

        System.out.println("Enter the number of points");

        InputStreamReader reader1 = new InputStreamReader(System.in);
        BufferedReader buffer1 = new BufferedReader(reader1);
        String npoints = buffer1.readLine();
        int numpoints = Integer.parseInt(npoints);

        // numpoints is now the number of points we wish to generate

        My2dPoint inputpoints [] = new My2dPoint [numpoints];

        // array to hold points

        int closest [] = new int [numpoints];

        // array to record soln; closest[i] is index of point closest to i'th

        int px, py;
        double dx, dy, dist;
        int i,j;
        double currbest;
        int closestPointIndex;
        long tStart, tEnd;

        for (i = 0; i < numpoints; i++) {

          px = (int) ( range * Math.random());
          dx = (double) px;
          py = (int) (range * Math.random());
          dy = (double) py;
          inputpoints[i] = new My2dPoint(dx, dy);

        }

        // array inputpoints has now been filled



        tStart = System.currentTimeMillis();

        // find closest [0]


        closest[0] = 1;
        currbest = Auxiliaries.distSquared(inputpoints[0],inputpoints[1]);
        for (j = 2; j < numpoints; j++) {
           dist = Auxiliaries.distSquared(inputpoints[0],inputpoints[j]);
           if (dist < currbest) {
               closest[0] = j;
               currbest = dist;
           }
        }

        // now find closest[i] for every other i 

        for (i = 1; i < numpoints; i++) {
            closest[i] = 0;
            currbest = Auxiliaries.distSquared(inputpoints[i],inputpoints[0]);
            for (j = 1; j < i; j++) {
              dist = Auxiliaries.distSquared(inputpoints[i],inputpoints[j]);
              if (dist < currbest) {
               closest[i] = j;
               currbest = dist;
          }
            }

            for (j = i+1; j < numpoints; j++) {
              dist = Auxiliaries.distSquared(inputpoints[i],inputpoints[j]);
              if (dist < currbest) {
          closest[i] = j;
                  currbest = dist;
          }
            }
        }

        tEnd = System.currentTimeMillis();
        System.out.println("Time taken in Milliseconds: " + (tEnd - tStart));
    }
}

person user642012    schedule 02.03.2011    source источник


Ответы (6)


Грубая сила для поиска ближайшего соседа возможна только для небольшого количества точек.

Возможно, вы захотите изучить kd-деревья или пространственные структуры данных в целом.

Вот демонстрация kd-Tree. Это то, что говорит Википедия.

person ma cılay    schedule 02.03.2011
comment
Ближайший сосед -> дерево kd. Верно. - person Waldheinz; 03.03.2011

Я бы определенно сначала отсортировал по x. Затем я бы использовал расстояние x между точками в качестве быстрого теста отклонения: если у вас есть расстояние до одного соседа, любой ближайший сосед должен быть ближе по x. Это позволяет избежать всех вычислений distSquared для точек вне диапазона x. Каждый раз, когда вы находите ближайшего соседа, вы также сужаете диапазон x, который вам нужно искать.

Кроме того, если P2 является ближайшим соседом к P1, то я бы использовал P1 в качестве начального предположения для ближайшего соседа к P2.

РЕДАКТИРОВАТЬ: Если подумать, я бы отсортировал по тому измерению, которое имеет наибольший диапазон.

person Ted Hopp    schedule 02.03.2011

Есть несколько довольно стандартных способов улучшить этот вид поиска, и то, насколько сложным вы хотите стать, зависит от того, сколько точек вы ищете.

Довольно распространенным простым способом является сортировка точек по X или Y. Затем для каждой точки вы ищете ближайшие точки, двигаясь как вперед, так и назад в массиве. Вспомните, как далеко находится ближайшая точка, которую вы нашли, и когда разница в X (или Y) больше этой, вы знаете, что не может быть более близкой точки, которую можно найти.

Вы также можете разделить пространство с помощью дерева. В Википедии есть страница с некоторыми возможными алгоритмами. Иногда стоимость их установки превышает то, что вы экономите. Это то, что вы должны решить, основываясь на том, сколько точек вы ищете.

person DJClayworth    schedule 02.03.2011

Либо используйте kd-дерево, либо используйте хорошую библиотеку для поиска ближайшего соседа. Weka включает один.

person Fred Foo    schedule 02.03.2011

Другая возможность, более простая, чем создание kd-дерева, заключается в использовании матрицы соседства.

Сначала поместите все свои точки в 2D квадратную матрицу. Затем вы можете запустить полную или частичную пространственную сортировку, чтобы точки упорядочились внутри матрицы.

Точки с маленьким Y могли перемещаться в верхние строки матрицы, и точно так же точки с большим Y попадали в нижние строки. То же самое произойдет с точками с маленькими координатами X, которые должны переместиться в столбцы слева. И симметрично, точки с большим значением X пойдут в правые столбцы.

После того, как вы выполнили пространственную сортировку (есть много способов добиться этого, как с помощью последовательных, так и параллельных алгоритмов), вы можете найти ближайшие точки к заданной точке P, просто посетив соседние ячейки, где точка P фактически хранится в матрице соседства.

Вы можете прочитать более подробную информацию об этой идее в следующей статье (вы найдете ее PDF-копии в Интернете): Моделирование сверхмассивной толпы на графическом процессоре на основе эмерджентного поведения.

Шаг сортировки дает вам интересные варианты. Вы можете использовать только четно-нечетную транспонированную сортировку, описанную в статье, которую очень просто реализовать (возможно, даже в CUDA). Если вы запустите только один проход, это даст вам частичную сортировку, которая уже может быть полезна, если ваша матрица почти отсортирована. То есть, если ваши точки движутся медленно, это сэкономит вам много вычислений.

Если вам нужна полная сортировка, вы можете запустить такой четно-нечетный проход несколько раз (как описано на следующей странице Википедии):

http://en.wikipedia.org/wiki/Odd%E2%80%93even_sort

Если изменения небольшие, одного или двух четно-нечетных проходов будет достаточно, чтобы снова отсортировать массив.

person mgmalheiros    schedule 08.02.2014
comment
В вырожденном случае, скажем, все точки расположены вдоль оси x, будет ли этот метод работать? - person Bob Coder; 18.03.2014
comment
Конечно, если у вас неоднородное распределение точек, у вас будут не очень хорошие окрестности. В предельном случае, как вы сказали, когда все точки расположены только на одной оси, вы все равно отсортируете их в матрицу, но есть вероятность, что многие близлежащие точки в пространстве окажутся далеко внутри матрицы. Таким образом, чем более однородным будет пространственное распределение, тем лучше будет ваша матрица и более согласованными окрестностями внутри матрицы. Я поместил тот же пример кода для сортировки в PasteBin здесь. - person mgmalheiros; 19.03.2014
comment
Я провел тест на дегенеративном случае. Он сортирует его, но некоторые близлежащие точки оказываются далеко друг от друга. Интересно, можно ли придумать более надежный алгоритм? Есть мнения? p.s. спасибо за ответ, я ценю это. - person Bob Coder; 19.03.2014
comment
@BobCoder: Это еще хуже, когда вы складываете координаты x и y вместе. Например, переведите координаты x и y в двоичный код и соедините оба значения. Затем рассортируйте точки. Это упорядочивает точки вдоль z-кривой, также известной как кривая монстра. У него гораздо лучшие пространственные свойства, он относительно прост и быстр. - person Gigamegs; 10.08.2014

Если ваши точки расположены относительно близко друг к другу, вы можете отсортировать по расстоянию от какой-либо точки (я думаю, что это может быть любая точка, но, возможно, это должна быть точка, для которой все точки находятся в одном квадранте, если эта точка рассматривается как точка). источник).

Допустим, интересующая точка находится в точке A и находится на расстоянии D.

Выберите ближайшую точку, которая находится в пределах некоторых относительно небольших n индексов от точки A в отсортированном списке (использование большего n обеспечивает, вероятно, лучшее начальное предположение, но это займет больше времени). Если эта точка имеет линейное расстояние g от точки A, вы знаете, что ближайшая точка должна быть не более чем g от A. Таким образом, вам нужно учитывать только точки в списке с расстоянием между D-g и D+g.

Составление диаграммы может помочь понять это. Если кому интересно, добавлю схему.

person drew.neely    schedule 19.02.2019