Idee für eine Datenstruktur zum Speichern von 2D-Daten?

Ich habe ein großes 2D-Gitter, x-by-y. Der Benutzer der Anwendung fügt Daten zu bestimmten Punkten in diesem Raster hinzu. Leider ist das Gitter viel zu groß, um als großes x-by-y-Array implementiert zu werden, da das System, auf dem es läuft, nicht genug Speicher hat.

Was ist eine gute Möglichkeit, dies zu implementieren, so dass nur die Punkte gespeichert werden, denen Daten hinzugefügt wurden?

My first idea was to create a BST of the data points. A hash function such as "(long)x<<32 + y" would be used to compare the nodes.

Ich kam dann zu dem Schluss, dass dies an Effizienz einbüssen könnte, wenn es nicht gut ausbalanciert wäre, so dass ich auf die Idee kam, eine BST mit vergleichbaren BST-Punkten zu haben. Die äußere BST würde die inneren BSTs basierend auf ihren x-Werten vergleichen. Die inneren BSTs würden die Punkte durch ihre y-Werte vergleichen (und sie würden alle das gleiche x haben). Wenn also der Programmierer sehen möchte, ob es einen Punkt bei (5,6) gibt, würden sie die äußere BST nach 5 abfragen. Wenn an dieser Stelle eine innere BST existiert, würde der Programmierer die innere BST nach 6 abfragen. Das Ergebnis würde zurück.

Können Sie sich einen besseren Weg vorstellen, dies umzusetzen?

Edit: In Bezug auf HashMaps: Die meisten HashMaps benötigen ein Array für die Suche. Man würde sagen "data [hash (Point)] = Point ();" um einen Punkt zu setzen und dann den Punkt zu finden, indem man ihn hasht, um den Index zu finden. Das Problem ist jedoch, dass das Array die Größe des Bereichs der Hash-Funktion haben müsste. Wenn dieser Bereich kleiner als die Gesamtzahl der Datenpunkte ist, die hinzugefügt werden, haben sie entweder keinen Platz oder müssen zu einem Überlauf hinzugefügt werden. Da ich die Anzahl der Punkte, die hinzugefügt werden, nicht kenne, müsste ich annehmen, dass diese Zahl kleiner als ein bestimmter Betrag ist, und dann das Array auf diese Größe setzen. Auch hier wird ein sehr großes Array instanziiert (obwohl kleiner als ursprünglich, wenn angenommen wird, dass es weniger Datenpunkte als x * y geben wird). Ich möchte, dass die Struktur linear mit der Datenmenge skaliert wird und keine große Menge verbraucht, wenn sie leer ist.

Es sieht so aus, als ob ich ein SparseArray wäre, wie einige schon erwähnt haben. Sind sie ähnlich implementiert wie eine BST in einer BST?

Edit2: Map<> is an interface. If I were to use a Map then it looks like TreeMap<> would be the best bet. So I would end up with TreeMap< TreeMap< Point> >, similar to the Map< Map< Point> > suggestions that people have made, which is basically a BST inside of a BST. Thanks for the info, though, because I didn't know that the TreeMap<> was basically the Java SDK of a BST.

Edit3: For those whom it may concern, the selected answer is the best method. Firstly, one must create a Point class that contains (x,y) and implements comparable. The Point could potentially be compared by something like (((long)x)<<32)+y). Then one would TreeMap each point to the data. Searching this is efficient because it is in a balanced tree so log(n) cost. The user can also query all of this data, or iterate through it, by using the TreeMap.entrySet() function, which returns a set of Points along with the data.

Zusammenfassend erlaubt dies die platzsparende und such-effiziente Implementierung eines Sparse-Arrays, oder in meinem Fall eines 2D-Arrays, das auch effizient durchlaufen werden kann.

8
@Kiril Raychev: Sobald die Punkte hinzugefügt sind, habe ich vor, alle Daten in der Struktur zu verwenden, um Berechnungen durchzuführen, aber keine Fernabfragen.
hinzugefügt der Autor Reed B, Quelle
@AlexWien: Wenn ich ein primatives hashmap benutze müsste ich dann ein großes Array auf den Stack schieben, wie mein erster Schnitt erklärt. Dies ist fast genauso ineffizient wie bei der Verwendung eines Direct-Mapped-Arrays, da beide beim Start sehr viel Speicherplatz benötigen. Wenn das Mapping dynamisch zugewiesen wird, kann ich sehr wenig Speicher verwenden, wenn es nur wenige Punkte gibt (aber ja, es wird einen Zeiger-Overhead geben).
hinzugefügt der Autor Reed B, Quelle
erfinde das Rad nicht neu, betrachte räumliche Datenstrukturen
hinzugefügt der Autor AlexWien, Quelle
Solange du deine Operationen nicht erklärst, ist es nicht möglich, die beste Struktur zu finden. Ein B-Baum mit Punkten in einem mortonindexierten Array ist ebenfalls möglich. oder ein Raster von Hashmaps
hinzugefügt der Autor AlexWien, Quelle
Ok, scheint, dass die Karte für Ihre Verwendung am besten ist. Aber wenn Sie sich mit Geschwindigkeitsproblemen beschäftigen, sollten Sie eine HashMap verwenden, die nicht objektbasiert ist und 60% Speicherplatz einspart. (Punktobjekt gegen primitive Typen)
hinzugefügt der Autor AlexWien, Quelle
hinzugefügt der Autor GriffeyDog, Quelle
Sie scheinen eher an der zugrunde liegenden Implementierung der Datenstruktur interessiert zu sein, anstatt daran, wie Sie sie verwenden werden. Wenn Sie einige räumliche Abfragen (Punkte mit x zwischen 10 und 40) oder Abfragen am nächsten Nachbarn benötigen, können Sie einige der von AlexWien erwähnten Strukturen oder eine navigierbare Karte verwenden. Wenn Sie nur einen bestimmten Punkt nachschlagen müssen, würde eine einfache alte HashMap einen guten Job machen - docs.oracle.com/javase/6/docs/api/java/util/HashMap.html
hinzugefügt der Autor jmruc, Quelle

8 Antworten

Entweder ein Quadtree , ein k -d-tree oder ein R-Baum .

Speichern Sie den Index für ein großes Punkt-Array in eine der räumlichen Strukturen. Solche räumlichen Strukturen sind vorteilhaft, wenn die Daten nicht gleichmäßig verteilt sind, wie z. B. geografische Daten, die sich auf Städte konzentrieren und keinen Sinn im Meer haben.

Denke darüber nach, ob du das reguläre Gitter vergessen kannst und bleib beim Viererbaum.
(Denken Sie, warum brauchen Sie ein regelmäßiges Gitter? Ein reguläres Gitter ist normalerweise nur eine Vereinfachung)

Verwenden Sie unter keinen Umständen Objekte, um einen Punkt zu speichern. Solch ein Objekt benötigt nur 20 Bytes für die Tatsache, dass es ein Objekt ist! Eine schlechte Idee für einen riesigen Datensatz.

Ein int x [] und ein int [] y oder ein int [] xy Array sind ideal für die Speichernutzung.

Denke darüber nach,

Hanan Samet 's "Grundlagen mehrdimensionaler Datenstrukturen"

(zumindest die Einleitung).

5
hinzugefügt
Dies sind gute Strukturen, aber ein Quadtree wäre nicht optimal, da meine Daten in diskreten Zeilen und Spalten angeordnet sind statt in Punkten, die in einer 2d kontinuierlichen Domäne verteilt sind, wofür der Quadtree entworfen wurde. Danke für die Antwort!
hinzugefügt der Autor Reed B, Quelle
Der Quadtree war nicht für kontinuierliche Koordinaten ausgelegt. Es ist für ganzzahlige Koordinaten, normalerweise eine Potenz von zwei. So diskret. Der Quad-Baum ist ein Index, nicht der Speicher selbst. Früher fand er mit minimalem Aufwand die Punkte in der Nähe. Sie können Ihre Daten als pints (row, col) oder (x, y) speichern. Sind Ihre Daten an einigen Stellen gleich verteilt oder gruppiert?
hinzugefügt der Autor AlexWien, Quelle
@AndreaLigios, ja mit diesen können Sie die Leistung um einen Faktor von 100 bis 1000 erhöhen, im Vergleich zu Ihrer alten Implementierung
hinzugefügt der Autor AlexWien, Quelle
+1, diese Strukturen sind ziemlich cool
hinzugefügt der Autor Andrea Ligios, Quelle

You could use a Map to store your data (you have to write the Pair class). If you need to iterate the data in some specific order, make Pair Comparable, and use NavigableMap

4
hinzugefügt
Wenn ich also jeden Punkt in der Map durchlaufen möchte, ohne jedes mögliche Mapping zu überprüfen, könnte ich die TreeMap.keySet() - Funktion verwenden, um eine Menge aller Schlüsselwerte zu erhalten und sie dann zu durchlaufen?
hinzugefügt der Autor Reed B, Quelle
+1 gute Lösung; Beat mich dazu :) Ich mag auch die Erwähnung von NavigableMap .
hinzugefügt der Autor Vivin Paliath, Quelle
@KirilRaychev Ich habe Point implementiert, um in embedded Systemen zu verwenden, wo java.awt nicht verfügbar war, das ist mehr Arbeit, als es zuerst betrachtet.
hinzugefügt der Autor AlexWien, Quelle
@KirilRaychev: Gute Erklärung.
hinzugefügt der Autor splungebob, Quelle
Warum nicht einfach die Klasse Point verwenden?
hinzugefügt der Autor splungebob, Quelle
@spllungebob meinst du java.awt.Point ? Ich denke, es ist immer eine schlechte Idee, Klassen zu verwenden, die für einen ganz anderen Zweck gedacht sind, nur weil sie die richtigen Eigenschaften haben. Der AWT-Punkt ist veränderbar, kann mit Doppelpunkten gesetzt werden und kann Transformationen anwenden - total nicht, was wir hier brauchen.
hinzugefügt der Autor jmruc, Quelle
@ReedB ja du kannst. Der empfohlene Weg besteht darin, entrySet und nicht keySet zu wiederholen, da dies effizienter ist, aber beides wird ausreichen.
hinzugefügt der Autor jmruc, Quelle

One approach could be Map>. The key on the outer map is the row value, and the key in the inner map is the column value. The value associated with that inner map (of type Data in this case) corresponds to the data at (row, column). Of course, this won't help if you're looking at trying to do matrix operations or such. For that you'll need sparse matrices.

Another approach is to represent the row and column as a Coordinate class or a Point class. You will need to implement equals and hashCode (should be very trivial). Then, you can represent your data as Map or Map.

2
hinzugefügt

Sie könnten eine Liste von Listen eines Objekts haben, und dieses Objekt kann seine horizontale und vertikale Position codieren.

class MyClass
{
    int x;
    int y;
    ...
}
1
hinzugefügt
Aber jedes Mal, wenn ein neues Objekt hinzugefügt wird, weil ich eine eindeutige Menge von Punkten haben möchte, müsste ich die Liste aller Daten durchsuchen, um zu sehen, ob sie bereits existiert, bevor entweder der Datenpunkt aktualisiert oder ein hinzugefügt wird ein neues. Ich habe versucht, diesen ineffizienten Prozess zu vermeiden.
hinzugefügt der Autor Reed B, Quelle
@ReedB ist nicht so ineffizient, besonders wenn Sie eine Liste von Listen mit der äußeren Liste haben, die x entspricht und die innere Liste, die y entspricht . das Suchen wäre eine Zeitkomplexität von O (x + y)
hinzugefügt der Autor Sam I am, Quelle

Mein Vorschlag an Sie ist Commons Math: Die Apache Commons Mathematikbibliothek . Weil es Ihren Tag spart, indem Sie die mathematische Kraft nutzen, die Ihre Anwendung erfordert.

0
hinzugefügt

Ich denke, Sie sind auf dem richtigen Weg, dies auf eine speichereffiziente Weise zu tun - es kann ziemlich einfach implementiert werden, indem Sie eine Karte von Karten verwenden, die in einer Klasse verpackt sind, um eine saubere Schnittstelle für Nachschlagewerke zu geben.

Ein alternativer (und speichereffizienterer) Ansatz wäre die Verwendung einer einzelnen Map, bei der der Schlüssel ein Tupel (x, y) ist. Dies wäre jedoch weniger praktisch, wenn Sie Abfragen wie "geben Sie mir alle Werte, wo x == ein Wert ".

0
hinzugefügt
Die Karte der Karten sieht vielversprechend aus. Wie ich bereits in einigen anderen Kommentaren erwähnt habe, müsste ich, wenn ich eine einzelne Map, die eine TreeMap ist, verwenden, die Knoten basierend auf einer Art Hashwert vergleichen, der aus den beiden Punkten generiert wird, wie meine ursprüngliche Idee einer einzelnen BST . Wenn diese Map eine lineare Map wäre, wie eine Liste, dann wäre das sehr ineffizient, da ich jedes Mal, wenn ich Daten hinzufügen wollte, linear durch die Liste suchen muss, um zu sehen, ob sie bereits existiert, bevor ich sie aktualisiere oder hinzufüge neuer Datenpunkt
hinzugefügt der Autor Reed B, Quelle

Vielleicht möchten Sie sich FlexCompColMatrix, CompColMatrix und andere Sparse-Matrizen-Implementierungen aus dem Matrix-Toolkit-Projekt ansehen .

Die Leistung hängt wirklich vom Schreib/Lese-Verhältnis und von der Dichte der Matrix ab, aber wenn Sie ein Matrix-Paket verwenden, ist es einfacher, zu experimentieren, indem Sie die Implementierung wechseln

0
hinzugefügt

Vielleicht bin ich hier zu einfach, aber ich denke, Sie können einfach eine normale HashMap verwenden. Es würde benutzerdefinierte Point -Objekte als Schlüssel enthalten:

class Point {
    int x;
    int y;
}

Dann überschreiben Sie die equals-Methode (und somit die hashCode-Methode) auf x und y . Auf diese Weise speichern Sie nur Punkte, die Daten enthalten.

0
hinzugefügt
Siehe die Bearbeitung von Hashmaps.
hinzugefügt der Autor Reed B, Quelle