Millionen von 3D-Punkten: Wie finde ich die 10 Punkte, die einem bestimmten Punkt am nächsten liegen?

68

Ein Punkt in 3-d wird durch (x, y, z) definiert. Der Abstand d zwischen zwei beliebigen Punkten (X, Y, Z) und (x, y, z) beträgt d = Sqrt [(Xx) ^ 2 + (Yy) ^ 2 + (Zz) ^ 2]. Jetzt gibt es eine Million Einträge in einer Datei, jeder Eintrag ist ein Punkt im Raum, in keiner bestimmten Reihenfolge. Wenn ein Punkt (a, b, c) gegeben ist, finden Sie die nächsten 10 Punkte. Wie würden Sie die Millionen Punkte speichern und wie würden Sie diese 10 Punkte aus dieser Datenstruktur abrufen.

Kazoom
quelle
1
Erstellen und füllen Sie die Datenstruktur, bevor oder nachdem Sie erfahren haben, worum es geht (a, b, c)? Davids Antwort funktioniert beispielsweise nicht, wenn Sie zuerst die Datenstruktur erstellen und dann ein Benutzer (a, b, c) eingibt und sofort eine Antwort wünscht.
MatrixFrog
3
Guter Punkt (kein Wortspiel beabsichtigt!) Wenn (a, b, c) nicht im Voraus bekannt ist, ist es natürlich eher ein Problem, die vorhandene Liste von Punkten für die Suche nach 3D-Position zu optimieren, als die Suche tatsächlich durchzuführen.
David Z
6
Es sollte wirklich geklärt werden, ob die Kosten für die Vorbereitung der Datenstruktur und die Speicherung der Millionen Punkte in dieser Datenstruktur berücksichtigt werden müssen oder nur die Abrufleistung zählt. Wenn diese Kosten keine Rolle spielen, gewinnt kd-tree unabhängig davon, wie oft Sie die Punkte abrufen. Wenn diese Kosten eine Rolle spielen, sollten Sie auch angeben, wie oft Sie die Suche ausführen möchten (bei einer geringen Anzahl von Suchvorgängen gewinnt Brute Force, bei größeren kd gewinnt).
Unvernunft

Antworten:

96

Millionen Punkte sind eine kleine Zahl. Der einfachste Ansatz funktioniert hier (Code, der auf KDTree basiert, ist langsamer (zum Abfragen nur eines Punktes)).

Brute-Force-Ansatz (Zeit ~ 1 Sekunde)

#!/usr/bin/env python
import numpy

NDIM = 3 # number of dimensions

# read points into array
a = numpy.fromfile('million_3D_points.txt', sep=' ')
a.shape = a.size / NDIM, NDIM

point = numpy.random.uniform(0, 100, NDIM) # choose random point
print 'point:', point
d = ((a-point)**2).sum(axis=1)  # compute distances
ndx = d.argsort() # indirect sort 

# print 10 nearest points to the chosen one
import pprint
pprint.pprint(zip(a[ndx[:10]], d[ndx[:10]]))

Starte es:

$ time python nearest.py 
point: [ 69.06310224   2.23409409  50.41979143]
[(array([ 69.,   2.,  50.]), 0.23500677815852947),
 (array([ 69.,   2.,  51.]), 0.39542392750839772),
 (array([ 69.,   3.,  50.]), 0.76681859086988302),
 (array([ 69.,   3.,  50.]), 0.76681859086988302),
 (array([ 69.,   3.,  51.]), 0.9272357402197513),
 (array([ 70.,   2.,  50.]), 1.1088022980015722),
 (array([ 70.,   2.,  51.]), 1.2692194473514404),
 (array([ 70.,   2.,  51.]), 1.2692194473514404),
 (array([ 70.,   3.,  51.]), 1.801031260062794),
 (array([ 69.,   1.,  51.]), 1.8636121147970444)]

real    0m1.122s
user    0m1.010s
sys 0m0.120s

Hier ist das Skript, das Millionen 3D-Punkte generiert:

#!/usr/bin/env python
import random
for _ in xrange(10**6):
    print ' '.join(str(random.randrange(100)) for _ in range(3))

Ausgabe:

$ head million_3D_points.txt

18 56 26
19 35 74
47 43 71
82 63 28
43 82 0
34 40 16
75 85 69
88 58 3
0 63 90
81 78 98

Mit diesem Code können Sie komplexere Datenstrukturen und Algorithmen testen (z. B. ob sie tatsächlich weniger Speicher verbrauchen oder schneller als der oben einfachste Ansatz). Es ist erwähnenswert, dass es im Moment die einzige Antwort ist, die Arbeitscode enthält.

Lösung basierend auf KDTree (Zeit ~ 1,4 Sekunden)

#!/usr/bin/env python
import numpy

NDIM = 3 # number of dimensions

# read points into array
a = numpy.fromfile('million_3D_points.txt', sep=' ')
a.shape = a.size / NDIM, NDIM

point =  [ 69.06310224,   2.23409409,  50.41979143] # use the same point as above
print 'point:', point


from scipy.spatial import KDTree

# find 10 nearest points
tree = KDTree(a, leafsize=a.shape[0]+1)
distances, ndx = tree.query([point], k=10)

# print 10 nearest points to the chosen one
print a[ndx]

Starte es:

$ time python nearest_kdtree.py  

point: [69.063102240000006, 2.2340940900000001, 50.419791429999997]
[[[ 69.   2.  50.]
  [ 69.   2.  51.]
  [ 69.   3.  50.]
  [ 69.   3.  50.]
  [ 69.   3.  51.]
  [ 70.   2.  50.]
  [ 70.   2.  51.]
  [ 70.   2.  51.]
  [ 70.   3.  51.]
  [ 69.   1.  51.]]]

real    0m1.359s
user    0m1.280s
sys 0m0.080s

Teilweise Sortierung in C ++ (Zeit ~ 1,1 Sekunden)

// $ g++ nearest.cc && (time ./a.out < million_3D_points.txt )
#include <algorithm>
#include <iostream>
#include <vector>

#include <boost/lambda/lambda.hpp>  // _1
#include <boost/lambda/bind.hpp>    // bind()
#include <boost/tuple/tuple_io.hpp>

namespace {
  typedef double coord_t;
  typedef boost::tuple<coord_t,coord_t,coord_t> point_t;

  coord_t distance_sq(const point_t& a, const point_t& b) { // or boost::geometry::distance
    coord_t x = a.get<0>() - b.get<0>();
    coord_t y = a.get<1>() - b.get<1>();
    coord_t z = a.get<2>() - b.get<2>();
    return x*x + y*y + z*z;
  }
}

int main() {
  using namespace std;
  using namespace boost::lambda; // _1, _2, bind()

  // read array from stdin
  vector<point_t> points;
  cin.exceptions(ios::badbit); // throw exception on bad input
  while(cin) {
    coord_t x,y,z;
    cin >> x >> y >> z;    
    points.push_back(boost::make_tuple(x,y,z));
  }

  // use point value from previous examples
  point_t point(69.06310224, 2.23409409, 50.41979143);
  cout << "point: " << point << endl;  // 1.14s

  // find 10 nearest points using partial_sort() 
  // Complexity: O(N)*log(m) comparisons (O(N)*log(N) worst case for the implementation)
  const size_t m = 10;
  partial_sort(points.begin(), points.begin() + m, points.end(), 
               bind(less<coord_t>(), // compare by distance to the point
                    bind(distance_sq, _1, point), 
                    bind(distance_sq, _2, point)));
  for_each(points.begin(), points.begin() + m, cout << _1 << "\n"); // 1.16s
}

Starte es:

g++ -O3 nearest.cc && (time ./a.out < million_3D_points.txt )
point: (69.0631 2.23409 50.4198)
(69 2 50)
(69 2 51)
(69 3 50)
(69 3 50)
(69 3 51)
(70 2 50)
(70 2 51)
(70 2 51)
(70 3 51)
(69 1 51)

real    0m1.152s
user    0m1.140s
sys 0m0.010s

Prioritätswarteschlange in C ++ (Zeit ~ 1,2 Sekunden)

#include <algorithm>           // make_heap
#include <functional>          // binary_function<>
#include <iostream>

#include <boost/range.hpp>     // boost::begin(), boost::end()
#include <boost/tr1/tuple.hpp> // get<>, tuple<>, cout <<

namespace {
  typedef double coord_t;
  typedef std::tr1::tuple<coord_t,coord_t,coord_t> point_t;

  // calculate distance (squared) between points `a` & `b`
  coord_t distance_sq(const point_t& a, const point_t& b) { 
    // boost::geometry::distance() squared
    using std::tr1::get;
    coord_t x = get<0>(a) - get<0>(b);
    coord_t y = get<1>(a) - get<1>(b);
    coord_t z = get<2>(a) - get<2>(b);
    return x*x + y*y + z*z;
  }

  // read from input stream `in` to the point `point_out`
  std::istream& getpoint(std::istream& in, point_t& point_out) {    
    using std::tr1::get;
    return (in >> get<0>(point_out) >> get<1>(point_out) >> get<2>(point_out));
  }

  // Adaptable binary predicate that defines whether the first
  // argument is nearer than the second one to given reference point
  template<class T>
  class less_distance : public std::binary_function<T, T, bool> {
    const T& point;
  public:
    less_distance(const T& reference_point) : point(reference_point) {}

    bool operator () (const T& a, const T& b) const {
      return distance_sq(a, point) < distance_sq(b, point);
    } 
  };
}

int main() {
  using namespace std;

  // use point value from previous examples
  point_t point(69.06310224, 2.23409409, 50.41979143);
  cout << "point: " << point << endl;

  const size_t nneighbours = 10; // number of nearest neighbours to find
  point_t points[nneighbours+1];

  // populate `points`
  for (size_t i = 0; getpoint(cin, points[i]) && i < nneighbours; ++i)
    ;

  less_distance<point_t> less_distance_point(point);
  make_heap  (boost::begin(points), boost::end(points), less_distance_point);

  // Complexity: O(N*log(m))
  while(getpoint(cin, points[nneighbours])) {
    // add points[-1] to the heap; O(log(m))
    push_heap(boost::begin(points), boost::end(points), less_distance_point); 
    // remove (move to last position) the most distant from the
    // `point` point; O(log(m))
    pop_heap (boost::begin(points), boost::end(points), less_distance_point);
  }

  // print results
  push_heap  (boost::begin(points), boost::end(points), less_distance_point);
  //   O(m*log(m))
  sort_heap  (boost::begin(points), boost::end(points), less_distance_point);
  for (size_t i = 0; i < nneighbours; ++i) {
    cout << points[i] << ' ' << distance_sq(points[i], point) << '\n';  
  }
}

Starte es:

$ g++ -O3 nearest.cc && (time ./a.out < million_3D_points.txt )

point: (69.0631 2.23409 50.4198)
(69 2 50) 0.235007
(69 2 51) 0.395424
(69 3 50) 0.766819
(69 3 50) 0.766819
(69 3 51) 0.927236
(70 2 50) 1.1088
(70 2 51) 1.26922
(70 2 51) 1.26922
(70 3 51) 1.80103
(69 1 51) 1.86361

real    0m1.174s
user    0m1.180s
sys 0m0.000s

Linearer suchbasierter Ansatz (Zeit ~ 1,15 Sekunden)

// $ g++ -O3 nearest.cc && (time ./a.out < million_3D_points.txt )
#include <algorithm>           // sort
#include <functional>          // binary_function<>
#include <iostream>

#include <boost/foreach.hpp>
#include <boost/range.hpp>     // begin(), end()
#include <boost/tr1/tuple.hpp> // get<>, tuple<>, cout <<

#define foreach BOOST_FOREACH

namespace {
  typedef double coord_t;
  typedef std::tr1::tuple<coord_t,coord_t,coord_t> point_t;

  // calculate distance (squared) between points `a` & `b`
  coord_t distance_sq(const point_t& a, const point_t& b);

  // read from input stream `in` to the point `point_out`
  std::istream& getpoint(std::istream& in, point_t& point_out);    

  // Adaptable binary predicate that defines whether the first
  // argument is nearer than the second one to given reference point
  class less_distance : public std::binary_function<point_t, point_t, bool> {
    const point_t& point;
  public:
    explicit less_distance(const point_t& reference_point) 
        : point(reference_point) {}
    bool operator () (const point_t& a, const point_t& b) const {
      return distance_sq(a, point) < distance_sq(b, point);
    } 
  };
}

int main() {
  using namespace std;

  // use point value from previous examples
  point_t point(69.06310224, 2.23409409, 50.41979143);
  cout << "point: " << point << endl;
  less_distance nearer(point);

  const size_t nneighbours = 10; // number of nearest neighbours to find
  point_t points[nneighbours];

  // populate `points`
  foreach (point_t& p, points)
    if (! getpoint(cin, p))
      break;

  // Complexity: O(N*m)
  point_t current_point;
  while(cin) {
    getpoint(cin, current_point); //NOTE: `cin` fails after the last
                                  //point, so one can't lift it up to
                                  //the while condition

    // move to the last position the most distant from the
    // `point` point; O(m)
    foreach (point_t& p, points)
      if (nearer(current_point, p)) 
        // found point that is nearer to the `point` 

        //NOTE: could use insert (on sorted sequence) & break instead
        //of swap but in that case it might be better to use
        //heap-based algorithm altogether
        std::swap(current_point, p);
  }

  // print results;  O(m*log(m))
  sort(boost::begin(points), boost::end(points), nearer);
  foreach (point_t p, points)
    cout << p << ' ' << distance_sq(p, point) << '\n';  
}

namespace {
  coord_t distance_sq(const point_t& a, const point_t& b) { 
    // boost::geometry::distance() squared
    using std::tr1::get;
    coord_t x = get<0>(a) - get<0>(b);
    coord_t y = get<1>(a) - get<1>(b);
    coord_t z = get<2>(a) - get<2>(b);
    return x*x + y*y + z*z;
  }

  std::istream& getpoint(std::istream& in, point_t& point_out) {    
    using std::tr1::get;
    return (in >> get<0>(point_out) >> get<1>(point_out) >> get<2>(point_out));
  }
}

Messungen zeigen, dass die meiste Zeit damit verbracht wird, Arrays aus der Datei zu lesen, tatsächliche Berechnungen in der Größenordnung weniger Zeit in Anspruch nehmen.

jfs
quelle
6
Schön zu schreiben. Um das Lesen von Dateien auszugleichen, habe ich Ihre Python-Implementierungen mit einer Schleife um die Suche ausgeführt, die 100 Mal ausgeführt wird (jedes Mal, wenn Sie sich um einen anderen Punkt kümmern und den kd-Baum nur einmal erstellen). Die Bruteforce hat immer noch gewonnen. Ich kratzte mir am Kopf. Aber dann habe ich Ihre Blattgröße untersucht und Sie haben dort einen Fehler - Sie setzen die Blattgröße auf 1000001, und das wird nicht gut funktionieren. Nach dem Setzen der Blattgröße auf 10 gewann kd (35s bis 70s für 100 Punkte, wobei die meisten 35s für den Bau des Baumes ausgegeben wurden und 100 Abfragen von 10 Punkten eine Sekunde dauerten).
Unvernunft
4
Wenn Sie also den kd-Baum vorberechnen können, gewinnt er die Brute Force um Größenordnungen (ganz zu schweigen davon, dass Sie bei wirklich großen Datenmengen, wenn Sie einen Baum haben, nicht alle Daten im Speicher lesen müssen ).
Unvernunft
1
@goran: Wenn ich leafsize auf 10 setze, dauert es ~ 10 Sekunden (statt 1 Sekunde), um einen Punkt abzufragen. Ich bin damit einverstanden, dass kd-tree gewinnen sollte, wenn die Aufgabe darin besteht, mehrere (> 10) Punkte abzufragen.
JFS
@Unreason: Pririty Queue- und Linear Search-basierte Implementierungen oben lesen NICHT alle Daten im Speicher.
JFS
4
von scipy.spatial import cKDTree ist cython, sucht> 50-mal schneller als der reine Python-KDTree (in 16d auf meinem alten Mac ppc).
Denis
20

Wenn sich die Millionen Einträge bereits in einer Datei befinden, müssen sie nicht alle in eine Datenstruktur im Speicher geladen werden. Behalten Sie einfach ein Array mit den bisher gefundenen Top-Ten-Punkten bei, scannen Sie die Millionen Punkte und aktualisieren Sie dabei Ihre Top-Ten-Liste.

Dies ist O (n) in der Anzahl der Punkte.

Wille
quelle
1
Dies funktioniert gut, aber das Array ist nicht der effizienteste Datenspeicher, da Sie es bei jedem Schritt überprüfen oder sortiert halten müssen, was problematisch sein kann. Davids Antwort über einen Min-Haufen erledigt das für Sie, ist aber ansonsten die gleiche Lösung. Wenn der Benutzer nur 10 Punkte möchte, sind diese Bedenken vernachlässigbar. Wenn der Benutzer jedoch plötzlich die nächsten 100 Punkte wünscht, sind Sie in Schwierigkeiten.
Karl
@ Karl: Die Frage gibt 10 Punkte an. Ich denke, dieses Detail einzubeziehen, ist seitens des Fragestellers absichtlich. Also beschreibt Will eine sehr gute Lösung für das, was gefragt wurde.
Nixuz
@ Karl, es ist oft überraschend, wie gut der Compiler und die CPU darin sind, die dümmsten Schleifen zu optimieren, um die klügsten Algorithmen zu schlagen. Unterschätzen Sie niemals die Geschwindigkeit, die erzielt werden kann, wenn eine Schleife im On-Chip-RAM laufen kann.
Ian Mercer
1
Millionen Einträge sind noch nicht in der Datei enthalten - Sie können auswählen, wie sie in der Datei gespeichert werden sollen. :) Diese Auswahl zum Speichern impliziert, dass Sie auch jede zugehörige Indexierungsstruktur vorberechnen können. Kd-tree gewinnt, da es nicht die gesamte Datei lesen muss <O (n).
Unvernunft
1
Ich habe die Implementierung Ihrer Antwort stackoverflow.com/questions/2486093/… veröffentlicht (obwohl ich Heap anstelle der linearen Suche verwende und dies für die Aufgabe völlig unnötig ist)
jfs
14

Sie können die Punkte in einem k-dimensionalen Baum (kd-Baum) speichern . Kd-Bäume sind für die Suche nach dem nächsten Nachbarn optimiert (Finden der n Punkte, die einem bestimmten Punkt am nächsten liegen).

Mipadi
quelle
1
Ich denke, hier ist ein Octree angesagt.
Gabe
11
Die Komplexität, die zum Erstellen eines Kd-Baums erforderlich ist, ist höher als die Komplexität, die zum Erstellen einer linearen Suche nach den 10 Schrankpunkten erforderlich ist. Die wahre Kraft eines kd-Baums entsteht, wenn Sie viele Abfragen an einem Punktesatz durchführen.
Nixuz
1
kd-tree kann im wirklichen Leben langsamer sein als der Brute-Force-Ansatz stackoverflow.com/questions/2486093/…
jfs
2
Dies ist die Antwort, die ich in einem Interview geben würde. Es kommt nicht selten vor, dass Interviewer eine weniger präzise Sprache verwenden, und zwischen den Zeilen zu lesen scheint höchstwahrscheinlich das zu sein, wonach sie suchen. Wenn ich der Interviewer wäre und jemand die Antwort "Ich würde die Punkte in einer alten Reihenfolge speichern und einen linearen Scan durchführen, um die 10 Punkte zu finden" geben würde und diese Antwort aufgrund meiner ungenauen Formulierung begründen würde, wäre ich ziemlich unbeeindruckt .
Jason Orendorff
3
@ Jason Orendorff: Ich würde definitiv in einem technischen Interview die Verwendung eines kd-Baums für ein solches Problem diskutieren. Ich würde jedoch auch erklären, warum für das gegebene spezifische Problem die einfachere lineare Suchmethode nicht nur asymptotisch schneller ist, sondern auch schneller läuft. Dies zeigt ein tieferes Verständnis der Komplexität von Algorithmen, der Kenntnis von Datenstrukturen und der Fähigkeit, unterschiedliche Lösungen für ein Problem in Betracht zu ziehen.
Nixuz
10

Ich denke, dies ist eine knifflige Frage, die prüft, ob Sie nicht versuchen, Dinge zu übertreiben.

Betrachten Sie den einfachsten Algorithmus, den die Leute bereits oben angegeben haben: Führen Sie eine Tabelle mit zehn bisher besten Kandidaten und gehen Sie alle Punkte nacheinander durch. Wenn Sie einen näheren Punkt als einen der zehn bisher besten finden, ersetzen Sie ihn. Was ist die Komplexität? Nun, wir müssen jeden Punkt aus der Datei einmal betrachten , seine Entfernung (oder das Quadrat der tatsächlichen Entfernung) berechnen und mit dem 10. nächstgelegenen Punkt vergleichen. Wenn es besser ist, fügen Sie es an der entsprechenden Stelle in die Tabelle mit den 10 bisher besten ein.

Wie komplex ist das? Wir betrachten jeden Punkt einmal, also sind es n Berechnungen der Entfernung und n Vergleiche. Wenn der Punkt besser ist, müssen wir ihn an der richtigen Position einfügen. Dies erfordert einige weitere Vergleiche, aber dies ist ein konstanter Faktor, da die Tabelle der besten Kandidaten eine konstante Größe von 10 hat.

Wir erhalten einen Algorithmus, der in linearer Zeit abläuft, O (n) in der Anzahl der Punkte.

Aber jetzt überlegen Sie, was die Untergrenze für einen solchen Algorithmus ist. Wenn es keine Ordnung in den Eingangsdaten ist, wir müssen an jedem Punkt Blick zu sehen , ob es nicht einer der engstenen Einsen ist. Soweit ich sehen kann, ist die Untergrenze Omega (n) und daher ist der obige Algorithmus optimal.

Krystian
quelle
1
Hervorragender Punkt! Da Sie die Datei einzeln lesen müssen, um eine Datenstruktur zu erstellen, ist O (n) so niedrig wie möglich . Nur wenn die Frage nach dem Finden der nächsten 10 Punkte wiederholt wird, ist etwas anderes wichtig! Und du hast es gut erklärt, denke ich.
Zan Lynx
5

Die Entfernung muss nicht berechnet werden. Nur das Quadrat der Entfernung sollte Ihren Bedürfnissen entsprechen. Sollte schneller sein, denke ich. Mit anderen Worten, Sie können das sqrtBit überspringen .

Agnel Kurian
quelle
4

Dies ist keine Hausaufgabenfrage, oder? ;-);

Mein Gedanke: Durchlaufen Sie alle Punkte und legen Sie sie in einen minimalen Haufen oder eine begrenzte Prioritätswarteschlange, die durch die Entfernung vom Ziel bestimmt wird.

David Z.
quelle
1
sicher, aber es ist nicht bekannt, was das Ziel ist. :)
Unreason
4

Diese Frage testet im Wesentlichen Ihr Wissen und / oder Ihre Intuition über Raumpartitionierungsalgorithmen . Ich würde sagen, dass das Speichern der Daten in einem Octree die beste Wahl ist. Es wird häufig in 3D-Engines verwendet, die genau diese Art von Problem behandeln (Speichern von Millionen von Scheitelpunkten, Raytracing, Auffinden von Kollisionen usw.). Die Suchzeit wird log(n)im schlimmsten Fall in der Größenordnung liegen (glaube ich).

Kai
quelle
2

Einfacher Algorithmus:

Speichern Sie die Punkte als Liste von Tupeln, scannen Sie die Punkte, berechnen Sie die Entfernung und führen Sie eine Liste mit den nächstgelegenen Punkten.

Kreativer:

Gruppieren Sie Punkte in Regionen (z. B. den durch "0,0,0" bis "50,50,50" oder "0,0,0" bis "-20, -20, -20" beschriebenen Würfel) kann vom Zielpunkt aus in sie "indizieren". Überprüfen Sie, in welchem ​​Würfel das Ziel liegt, und durchsuchen Sie nur die Punkte in diesem Würfel. Wenn dieser Würfel weniger als 10 Punkte enthält, überprüfen Sie die "benachbarten" Würfel und so weiter.

Bei weiterer Überlegung ist dies kein sehr guter Algorithmus: Wenn Ihr Zielpunkt näher an der Wand eines Würfels liegt als 10 Punkte, müssen Sie auch nach dem benachbarten Würfel suchen.

Ich würde mich für den kd-tree-Ansatz entscheiden und den nächstgelegenen Knoten entfernen (oder markieren) und erneut nach dem neuen nächstgelegenen Knoten suchen. Spülen und wiederholen.

Jeff Fleischbällchen Yang
quelle
2

Wenn für zwei beliebige Punkte P1 (x1, y1, z1) und P2 (x2, y2, z2) der Abstand zwischen den Punkten d ist, müssen alle folgenden Bedingungen erfüllt sein:

|x1 - x2| <= d 
|y1 - y2| <= d
|z1 - z2| <= d

Halten Sie die 10 am nächsten, während Sie über Ihren gesamten Satz iterieren, aber halten Sie auch den Abstand zur 10. am nächsten. Sparen Sie sich viel Komplexität, indem Sie diese drei Bedingungen verwenden, bevor Sie die Entfernung für jeden Punkt berechnen, den Sie betrachten.

Kirk Broadhurst
quelle
1

im Grunde eine Kombination der ersten beiden Antworten über mir. Da sich die Punkte in einer Datei befinden, müssen sie nicht gespeichert werden. Anstelle eines Arrays oder eines Min-Heaps würde ich einen Max-Heap verwenden, da Sie nur nach Entfernungen suchen möchten, die kleiner als der 10. nächstgelegene Punkt sind. Für ein Array müssten Sie jede neu berechnete Entfernung mit allen 10 von Ihnen beibehaltenen Entfernungen vergleichen. Für einen minimalen Haufen müssen Sie 3 Vergleiche mit jeder neu berechneten Entfernung durchführen. Bei einem maximalen Heap führen Sie nur dann einen Vergleich durch, wenn der neu berechnete Abstand größer als der Wurzelknoten ist.

Yiling
quelle
0

Berechnen Sie die Entfernung für jeden von ihnen und wählen Sie (1..10, n) in O (n) Zeit. Das wäre der naive Algorithmus, denke ich.

Rubys
quelle
0

Diese Frage muss weiter definiert werden.

1) Die Entscheidung bezüglich der Algorithmen, die Daten vorindizieren, variiert sehr stark, je nachdem, ob Sie die gesamten Daten im Speicher halten können oder nicht.

Mit kdtrees und octrees müssen Sie die Daten nicht im Speicher halten, und die Leistung profitiert von dieser Tatsache, nicht nur, weil der Speicherbedarf geringer ist, sondern einfach, weil Sie nicht die gesamte Datei lesen müssen.

Mit bruteforce müssen Sie die gesamte Datei lesen und die Entfernungen für jeden neuen Punkt, nach dem Sie suchen, neu berechnen.

Dies ist jedoch möglicherweise nicht wichtig für Sie.

2) Ein weiterer Faktor ist, wie oft Sie nach einem Punkt suchen müssen.

Wie von JF Sebastian angegeben, ist Bruteforce manchmal sogar bei großen Datenmengen schneller. Beachten Sie jedoch, dass seine Benchmarks das Lesen des gesamten Datensatzes von der Festplatte messen (was nicht erforderlich ist, sobald kd-tree oder octree erstellt und irgendwo geschrieben wurden). und dass sie nur eine Suche messen.

Unvernunft
quelle