2012-02-23 15 views
5

Próbuję zaimplementować prostszą wersję tego algorytmu, ale która działa lepiej niż algorytm kwadratowy. Moim pomysłem jest zasadniczo posortowanie punktów za pomocą tylko współrzędnej x i spróbuj ją rozwiązać. Kiedy już posortuję tablicę punktów według współrzędnej x, chcę przetestować tablicę i zasadniczo pominąć punkty, których odległość jest większa niż pierwsze dwa punkty, w które brałem.Najbliższa para algorytmów punktów

Na przykład mój currentminDist = x;

Jeśli dwie pary punktów, na które patrzę, mają odległość> x (tylko przy jej rozkładzie x coordin), ignoruję punkt i przesuwam go obok tablicy.

Mam pomysł w dół, ale jestem w pewien sposób utknąłem na tym, jak faktycznie to zaimplementować (zwłaszcza część warunku). Mam funkcję, która zwraca mi odległość między dwoma punktami na podstawie ich współrzędnej x.

Jestem zdezorientowany, jak właściwie zapisać moje warunki dla mojej pętli, ponieważ chcę zignorować punkt, jeśli odległość jest zbyt duża i nadal wypełniać moją tablicę, która będzie zawierała odpowiedzi dla najbliższych punktów dla każdego i (jestem aktualnym punktem, na który patrzę).

Wszelkie wskazówki i wskazówki są mile widziane. Nie znam się dobrze na algorytmach kodowania, więc jest to dość frustrujące.

Tutaj jest częścią mojego kodu:

for (i = 0; i < numofmypoints; i++) 
     { 
      for (int j = i + 1; (j < numpofmypoints) && ((inputpoints[j].x - inputpoints[i].x) < currbest); j++) 
      { 
       currdist = Auxilary.distbyX(inputpoints[i],inputpoints[j]); 

       if (currdist < bestdist) 
       { 
       closest[i] = j; 
       bestdist = currdist; 

       } 
      } 
     } 

distbyX jest moja funkcja, która po prostu zwraca odległość między dwoma punktami.

Dzięki!

+0

@Paul: czy trzeba to robić częściej? Nie zapisałbyś twoich punktów w pomocy "quadtree"? http://pl.wikipedia.org/wiki/Quadtree – TacticalCoder

+1

Zauważ, że możesz uzyskać lepszą wydajność niż przy użyciu algorytmu naiwnego, ale nadal będziesz mieć "O (n^2)". – ARRG

+0

Dlaczego w twoim kodzie są 'currbest' i' bestdist'? Jaka jest różnica? – Ishtar

Odpowiedz

4

szybki algorytm za pomocą KD-tree
ten algorytm tworzy KD-drzewo, a następnie znajdzie najbliższą parę dla każdego punktu. Tworzenie drzewa kd to O (n log n), a znalezienie najbliższego sąsiada punktu to O (logn). Kredyt musi pochodzić od Wikipedia, który w jednym artykule wyjaśnia, jak tworzyć kd-drzewa i jak z nich korzystać, aby znaleźć najbliższego sąsiada.

import java.util.*; 

public class Program 
{ 
    public static void main(String[] args) 
    { 
     List<Point> points = generatePoints(); 
     Point[] closest = new Point[points.size()]; 

     KDTree tree = new KDTree(points, 0); // WILL MODIFY 'points' 

     for (int i = 0; i < points.size(); i++) 
     { 
      closest[i] = tree.findClosest(points.get(i)); 
     } 

     for (int i = 0; i < points.size(); i++) 
     { 
      System.out.println(points.get(i) + " is closest to " + closest[i]); 
     } 
    } 

    private static List<Point> generatePoints() 
    { 
     ArrayList<Point> points = new ArrayList<Point>(); 
     Random r = new Random(); 

     for (int i = 0; i < 1000; i++) 
     { 
      points.add(new Point(r.nextInt() % 1000, r.nextInt() % 1000)); 
     } 

     return points; 
    } 
} 

class Point 
{ 
    public static final Point INFINITY 
     = new Point(Double.POSITIVE_INFINITY, 
        Double.POSITIVE_INFINITY); 

    public double[] coord; // coord[0] = x, coord[1] = y 

    public Point(double x, double y) 
    { 
     coord = new double[] { x, y }; 
    } 

    public double getX() { return coord[0]; } 
    public double getY() { return coord[1]; } 

    public double distance(Point p) 
    { 
     double dX = getX() - p.getX(); 
     double dY = getY() - p.getY(); 
     return Math.sqrt(dX * dX + dY * dY); 
    } 

    public boolean equals(Point p) 
    { 
     return (getX() == p.getX()) && (getY() == p.getY()); 
    } 

    public String toString() 
    { 
     return "(" + getX() + ", " + getY() + ")"; 
    } 

    public static class PointComp implements Comparator<Point> 
    { 
     int d; // the dimension to compare in (0 => x, 1 => y) 

     public PointComp(int dimension) 
     { 
      d = dimension; 
     } 

     public int compare(Point a, Point b) 
     { 
      return (int) (a.coord[d] - b.coord[d]); 
     } 
    } 
} 

class KDTree 
{ 
    // 2D k-d tree 
    private KDTree childA, childB; 
    private Point point; // defines the boundary 
    private int d; // dimension: 0 => left/right split, 1 => up/down split 

    public KDTree(List<Point> points, int depth) 
    { 
     childA = null; 
     childB = null; 
     d = depth % 2; 

     // find median by sorting in dimension 'd' (either x or y) 
     Comparator<Point> comp = new Point.PointComp(d); 
     Collections.sort(points, comp); 

     int median = (points.size() - 1)/2; 
     point = points.get(median); 

     // Create childA and childB recursively. 
     // WARNING: subList() does not create a true copy, 
     // so the original will get modified. 
     if (median > 0) 
     { 
      childA = new KDTree(
       points.subList(0, median), 
       depth + 1); 
     } 
     if (median + 1 < points.size()) 
     { 
      childB = new KDTree(
       points.subList(median + 1, points.size()), 
       depth + 1); 
     } 
    } 

    public Point findClosest(Point target) 
    { 
     Point closest = point.equals(target) ? Point.INFINITY : point; 
     double bestDist = closest.distance(target); 
     double spacing = target.coord[d] - point.coord[d]; 
     KDTree rightSide = (spacing < 0) ? childA : childB; 
     KDTree otherSide = (spacing < 0) ? childB : childA; 

     /* 
     * The 'rightSide' is the side on which 'target' lies 
     * and the 'otherSide' is the other one. It is possible 
     * that 'otherSide' will not have to be searched. 
     */ 

     if (rightSide != null) 
     { 
      Point candidate = rightSide.findClosest(target); 
      if (candidate.distance(target) < bestDist) 
      { 
       closest = candidate; 
       bestDist = closest.distance(target); 
      } 
     } 

     if (otherSide != null && (Math.abs(spacing) < bestDist)) 
     { 
      Point candidate = otherSide.findClosest(target); 
      if (candidate.distance(target) < bestDist) 
      { 
       closest = candidate; 
       bestDist = closest.distance(target); 
      } 
     } 

     return closest; 
    } 
} 


Fix do kodu w pytaniu
Jeśli naprawdę nie martw się o złożoności, jedynym problemem z kodem jest to, że czekamy, ale nie do tyłu. Wystarczy powielić wewnętrzną pętlę i uczynić j przejść od (i - 1) do 0:

Point[] points = sort(input()); 
int[] closest = new int[points.length]; 

for (int i = 0; i < points.length; i++) 
{ 
    double bestdist = Double.POSITIVE_INFINITY; 

    for (int j = i + 1; (j < points.length) && ((points[j].x - points[i].x) < bestdist); j++) 
    { 
     double currdist = dist(points[i], points[j]); 

     if (currdist < bestdist) 
     { 
      closest[i] = j; 
      bestdist = currdist; 
     } 
    } 
    for (int j = i - 1; (j >= 0) && ((points[i].x - points[j].x) < bestdist); j--) 
    { 
     double currdist = dist(points[i], points[j]); 

     if (currdist < bestdist) 
     { 
      closest[i] = j; 
      bestdist = currdist; 
     } 
    } 
} 
+0

Nie martwię się o najgorszy przypadek. Zakładam, że wszystkie wartości x są różne. Dlatego chcę spróbować rozwiązać to tak, jak to przedstawiłem. Twój sposób ma sens, gdy mogę użyć struktury danych, aby go rozwiązać, ale zastanawiałem się, czy można to rozwiązać w opisany sposób. Wpadłem na ten problem, nie obliczając najbliższego punktu dla wszystkich punktów, oblicza go tylko dla kilku z nich, a pozostałe są po prostu tym samym punktem powtarzanym w kółko. Dlatego właśnie próbowałem sprawdzić, czy gdzieś pójdę źle. – Paul

+0

Klasyczny problem "Najbliższej pary punktów" polega na znalezieniu pary punktów, które znajdują się najbliżej siebie. Dopiero teraz uświadamiam sobie, że twój problem jest inny - znajdź najbliższego sąsiada dla każdego punktu. Zaktualizuję odpowiedź, gdy tylko będę mógł wymyślić algorytm. – tom

+0

@Paul: Nie mogłem wymyślić sposobu na ulepszenie twojego Sweepline do O (dobrze), więc zrobiłem to używając drzewa kd. – tom