![]() |
Wie kann man viele Punkte schnell vergleichen?
Hallo zusammen,
ich habe Punkte, die ich in einem Diagramm anzeigen lasse (verbunden ist es natürlich eine Kurve). D.h. jeder Punkt besitzt einen x- und eine y-Wert. Nun habe ich mehrere Fenster festgelegt, mit denen ich die Kurvenabschnitte auswerte. Ein Beispiel mit 2 Fenstern ist folgendes: x = 12,5 bis 17,8 x = 16,9 bis 20 Ich kann beliebig viele Fenster (Rechtecke) vorgeben und ich möchte die Punkte darin schnell ausfindig machen, damit ich diese mit anderen Dingen vergleichen kann. Würden sich die Fenster nicht überschneiden, könnte ich die Kurve in einer Schleife ein einziges Mal durchlaufen (von links nach rechts). Da sich die Fenster überschneiden untersuche ich aktuell pro Fenster die Kurve vom niedrigsten x-Wert bis zum Ende des Fensters (dann kann ich den Suchdurchlauf natürlich abbrechen). D.h. es sind viele Vergleiche nötig, um überhaupt herauszufinden, ab wann die Kurve in das Fenster eintritt. Ich kann nicht direkt auf die Punkte zugreifen, da ich erst herausfinden muss, welcher Punkt in welchem Fenster liegt. Ein Zahlenbeispiel zur Verdeutlichung: Punkte:
Code:
Die Fenster sind wie folgt definiert:
0 0,5 1 1,5 2 2,5 3 3,5 4 4,5 5
Code:
Mein Ziel ist es, den Punkt direkt vor dem Fenster und den ersten im Fenster zu ermitteln und beim Austritt ebenfalls. Hier zeige ich nur den Suchvorgang allgemein.
Fenster 1:
x = 0,7 bis 1,8 Fenster 2: x = 1,4 bis 3,7 2 Bereiche -> 2 Schleifen: Schleife 1 fürs 1. Fenster: Diese läuft von x = 0 bis x = 2 und gibt mir als Werte innerhalb des Fensters 1 und 1,5 zurück. Schleife 2 fürs 2. Fenster: Diese läuft von x = 0 bis x = 4 und gibt mir als Werte innerhalb des Fensters 1,5 bis 3,5 zurück. Bei tausenden Werten und vielen Fenstern kann das lange dauern. Geht es irgendwie schneller? Die Punkte liegen übrigens in 2 Arrays vor: in einem sind die x- und im anderen die y-Werte definiert. Grüße, Matze |
AW: Wie kann man viele Punkte schnell vergleichen?
Dreh es doch um, und lauf die Punkte nur einmal durch und vergleich pro Punkt, ob dieser in einem der Fenster liegt, koennte schneller sein, solange weniger Fenster als Punkt vorhanden sind. Vielleicht kann man auch was mit Sortierung noch rausholen.
|
AW: Wie kann man viele Punkte schnell vergleichen?
Wenn die Punkte-Liste sortiert ist, dann kannst du doch über eine
![]() Also den ersten Wert, welcher größer-gleich deinem Anfan für das Rechteck ist. Diese Suche wäre dann mit O(log n) schon recht schnell, so daß bei 1.000.000 Punkten schon nach maximal 20 Vergleichen der Anfang gefunden wäre. |
AW: Wie kann man viele Punkte schnell vergleichen?
Puh, das wird komplex. Aber stimmt, das wäre evtl. schneller. Danke für den Hinweis.
Wie angedeutet, brauche ich jeweils den Punkt direkt vor dem Fenster, dann den ersten und letzten im Fenster und den ersten Punkt, der wieder aus dem Fenster draußen ist. D.h. ich müsste immer den aktuellen und den nächsten Punkt mit den Fensterkanten vergleichen. Und es kommen zu den x-Wertenauch noch die y-Werte hinzu und die Vergleiche mit den Fensterober- und -unterkanten. Ohje .... @Himi: Ich weiß nicht, ob das funktioniert, da ich keinen bestimmten Wert suche. D.h. die Suche würde bei mir fast immer "kein Treffer" liefern. |
AW: Wie kann man viele Punkte schnell vergleichen?
Klar funktioniert das.
Du suchst also einfach nur den wert, welcher Größer-Gleich deinem Startpunkt ist und welcher am nähesten an diesem Punkt liegt. Schau dir mal die Find-Methode der TStringList an. Über diese wird zwar ein bestimmter Wert gesucht, aber wenn nix gefunden wird, dann gibt diese die Position zurück welcher der Wert eingefügt werden könnte. |
AW: Wie kann man viele Punkte schnell vergleichen?
Ich guck mal, ob ich den Algorithmus verstehe und ihn umsetzen kann.
|
AW: Wie kann man viele Punkte schnell vergleichen?
Wie viele Punkte sind es denn ungefähr? Wie wäre es mit einem
![]() PS: Unten ist auch eine Seite mit Beispielen verlinkt. |
AW: Wie kann man viele Punkte schnell vergleichen?
Im 3D-Bereich wird zum "culling" (Ablehnen von Polygonen/Vertices) gerne das
![]() Das dürfte mehr oder minder State of the Art sein, es sei denn es gibt für 2D eine noch flottere Klamotte die mir dann unbekannt wäre :) |
AW: Wie kann man viele Punkte schnell vergleichen?
Wenn ich das recht verstehe, betrachtest du lediglich die X-Werte der Punkte mit den jeweiligen Fenstergrenzen. Die Y-Werte interessieren dabei noch gar nicht.
Die Punkte scheinen in X aufsteigend sortiert zu sein, sonst funktioniert dein Algorithmus nämlich nicht. Wenn man nun die Fenstergrenzen ebenfalls sortiert und mit Index-Arrays arbeitet, kann man mit zwei Durchläufen die Punktbereiche in den Fenstern ermitteln. Hier der passende Code:
Delphi-Quellcode:
Die Funktion GetSortOrder ruft einen angepassten Quicksort auf:
{ Punkte gibt die X-Werte der Punkte an, FensterLo die Untergrenzen, FensterHi die Obergrenzen der Fenster.
insideLo und insideHi geben die Indizes der Punkte an, die gerade noch in dem jeweiligen Fenster liegen } procedure FindeFenster(const Punkte, FensterLo, FensterHi: array of Double; var insideLo, insideHi: TIntegerDynArray); var CntPunkte: Integer; CntFenster: Integer; orderLo: TIntegerDynArray; orderHi: TIntegerDynArray; I: Integer; idx: Integer; rng: Integer; begin CntPunkte := Length(Punkte); CntFenster := Length(FensterLo); Assert(CntPunkte > 0); Assert(CntFenster > 0); Assert(CntFenster = Length(FensterHi)); SetLength(insideLo, CntFenster); SetLength(insideHi, CntFenster); { orderLo enthält eine Liste von Indizes auf FensterLo, so daß die Werte aufsteigend sortiert sind. orderHi enthält eine Liste von Indizes auf FensterHi, so daß die Werte aufsteigend sortiert sind. Läßt sich mit einem angepassten QuickSort leicht implementieren. } orderLo := GetSortOrder(FensterLo); orderHi := GetSortOrder(FensterHi); { erster Durchlauf: untere Grenzen ermitteln } idx := 0; rng := orderLo[idx]; for I := 0 to Length(Punkte) - 1 do begin if Punkte[I] >= FensterLo[rng] then begin { untere Grenze des nächsten Fensters überschritten } insideLo[rng] := I; { die folgenden Fenster prüfen bis wir eins finden, in dem wir noch nicht sind } Inc(idx); while idx < CntFenster do begin rng := orderLo[idx]; if Punkte[I] < FensterLo[rng] then Break; insideLo[rng] := I; Inc(idx); end; { schon alle Fenster überprüft? } if idx >= CntFenster then Break; rng := orderLo[idx] end; end; { zweiter Durchlauf: obere Grenzen ermitteln } idx := CntFenster - 1; rng := orderHi[idx]; for I := CntPunkte - 1 downto 0 do begin if Punkte[I] <= FensterHi[rng] then begin { obere Grenze des nächsten Fensters unterschritten } insideHi[rng] := I; { die folgenden Fenster prüfen bis wir eins finden, in dem wir noch nicht sind } Dec(idx); while idx >= 0 do begin rng := orderHi[idx]; if Punkte[I] > FensterHi[rng] then Break; insideHi[rng] := I; Dec(idx); end; { schon alle Fenster überprüft? } if idx < 0 then Break; rng := orderHi[idx]; end; end; end;
Delphi-Quellcode:
procedure QuickSort(const Values: array of Double; var Index: array of Integer; L, R: Integer);
var I, J, T: Integer; P: Double; begin repeat I := L; J := R; P := Values[Index[(L + R) shr 1]]; repeat while (Values[Index[I]] < P) do Inc(I); while (Values[Index[J]] > P) do Dec(J); if I <= J then begin if I <> J then begin T := Index[I]; Index[I] := Index[J]; Index[J] := T; end; Inc(I); Dec(J); end; until I > J; if L < J then QuickSort(Values, Index, L, J); L := I; until I >= R; end; function GetSortOrder(const Values: array of Double): TIntegerDynArray; var I: Integer; begin SetLength(result, Length(Values)); for I := 0 to Length(result) - 1 do result[I] := I; QuickSort(Values, result, 0, Length(result) - 1); end; |
AW: Wie kann man viele Punkte schnell vergleichen?
Öhm, sortiert und nur für eine Achse ginge das doch noch VIEL einfacher! Die Fenstergrenzen müssen dafür auch nach gleicher Weise wie die Punkte sortiert sein, und du kommst mit nur einem Durchlauf aus.
Mal so ein Fetzen Pseudocode:
Delphi-Quellcode:
Und schwupps stehen im TWindows-Array alle Punkte, die links und rechts der X-Fenstergrenzen liegen, in O(n).
type
TWindow = class; TEdge = class public Value: Float; IsLeftEdge: Boolean; // im Konstruktor zuweisen, wird noch wichtig :) Window: TWindow; // im Konstruktor zuweisen, quasi der Parent, also Rückbezug end; TWindow = class public Left, Right: TEdge; LeftPointLeft: Float; LeftPointRight: Float; RightPointLeft: Float; RightPointRight: Float; end; var Windows: array of TWindow; Edges: array of TEdge; // referenzen auf alle Edges aus dem Windows-Array sortiert nach Value CurrentEdgeIndex: Integer; begin for i := 0 to maxPoints do begin if Points[i].X > Edges[CurrentEdgeIndex].Value then begin if Edges[CurrentEdgeIndex].IsLeftEdge then begin Edges[CurrentEdgeIndex].Window.LeftPointLeft := Points[i-1].Value; Edges[CurrentEdgeIndex].Window.LeftPointRight := Points[i].Value; end else begin Edges[CurrentEdgeIndex].Window.RightPointLeft := Points[i-1].Value; Edges[CurrentEdgeIndex].Window.RightPointRight := Points[i].Value; end; inc(CurrentEdgeIndex); end; end; end; Man könnte natürlich auch die Punkte in die TEdge Instanzen werfen, und sich dieses IsLeftEdge damit sparen:
Delphi-Quellcode:
Das spart evtl. noch (sehr minimal) Zeit in der Schleife (ein Vergleich und ein paar Dereferenzierungen, uhu), und die Werte stehen ggf. an sinnvollerer Stelle, je nach dem wie du es nachher brauchst.
type
TWindow = class; TEdge = class public Value: Float; LeftValue, RightValue: Float; end; TWindow = class public Left, Right: TEdge; end; var Windows: array of TWindow; Edges: array of TEdge; // referenzen auf alle Edges aus dem Windows-Array sortiert nach Value CurrentEdgeIndex: Integer; begin for i := 0 to maxPoints do begin if Points[i].X > Edges[CurrentEdgeIndex].Value then begin Edges[CurrentEdgeIndex].LeftValue := Points[i-1].Value; Edges[CurrentEdgeIndex].RightValue := Points[i].Value; inc(CurrentEdgeIndex); end; end; end; |
AW: Wie kann man viele Punkte schnell vergleichen?
@Medium: Es gibt da neben ein paar Grenzfällen, in denen der Code einen ERangeError auslöst (z.B. erster Punkt liegt innerhalb des ersten Fensters, letzter Punkt liegt außerhalb des letzten Fensters), noch das Problem, daß er fehlerhafte Resultate liefert. Mit den gegebenen Werten ergänzt um ein paar weitere Fenster mit Spezialfällen (drittes Fenster umschließt erstes, kompletter Bereich, genau ein Punkt, nur ein Punkt im Bereich) bekomme ich folgende Ergebnisse:
Code:
Punkte: 0.0 0.5 1.0 1.5 2.0 2.5 3.0 3.5 4.0 4.5 5.0
erwartet: ------------------------------------------------------------- 0.7-1.8: 1.0, 1.5 1.4-3.7: 1.5, 2.0, 2.5, 3.0, 3.5 0.6-2.1: 1.0, 1.5, 2.0 0.0-5.0: 0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0 2.0-2.0: 2.0 2.9-3.1: 3.0 erhalten: ------------------------------------------------------------- 0.7-1.8: 1.5 , 2.0 1.4-3.7: 0.6-2.1: 1.0 , 1.5 , 2.0 , 2.5 , 3.0 , 3.5 0.0-5.0: 2.0-2.0: 3.0 2.9-3.1: 4.5 |
AW: Wie kann man viele Punkte schnell vergleichen?
Hab jetzt nur mal auf die Schnelle das Find der TStringList versucht anzupassen und hoffe es stimmt soweit auch. :stupid:
Delphi-Quellcode:
Das Array muß natürlich nach X sortiert sein.
type TMyArray = array of record X, Y: Integer; end;
function FindLowerXIndex(const Arr: TMyArray; LowX: Integer): Integer; var L, H, I: Integer; begin L := 0; H := High(Arr); while L <= H do begin I := (L + H) shr 1; if Arr[I].X < LowX then L := I + 1 else H := I - 1; end; Result := L; end; |
AW: Wie kann man viele Punkte schnell vergleichen?
Zitat:
Zitat:
Schöne Grüße, Medium |
AW: Wie kann man viele Punkte schnell vergleichen?
Huch, schön, dass euch das Thema auch so interessiert. Erst einmal danke für die zahlreichen Beiträge.
Zitat:
1 Million Punkte zu prüfen hat heute ca. 2-3 Sekunden gedauert nach meinem bisherigen Vorgehen. Das ist natürlich langsam. Die binäre Suche und die anderen Algorithmen werden vermutlich nicht funktionieren, zumindest nicht wie gewünscht. Würde die Steigung der Kurve immer positiv sein, wäre denkbar, mit dieser Suche, die jeweils linken und rechten Kanten der Fenster zu bestimmen. Ist die Steigung innerhalb eines Fensters sehr groß und tritt die Kurve unten in das Fenster ein, komme ich mit den Algorithmen dennoch nur zur linken Kante. D.h. ich muss sämtliche Werte in den Fenstern weiterhin prüfen, da hier der Y-Wert ausschlaggebend ist. Eine weitere Schwierigkeit ist, dass die Kurve auch "zurück laufen" kann. Es handelt sich also nicht um eine mathematische Funktion. Ja, langsam wird's kompliziert. ;) Grüße, Matze |
AW: Wie kann man viele Punkte schnell vergleichen?
Zitat:
Wenn ja, wär es vielleicht sogar anzudenken, das ganze über die Grafikkarte und OpenGL zu machen und die Punkte als Vertices in die Grafikkarte zu jagen. Dann hättest du schlussendlich so ziemlich alle Möglichkeiten, die dir Occlusion Tests der Hardware so bieten. Aber ich denke, für dein Projekt ist das wohl etwas zuviel des Guten? |
AW: Wie kann man viele Punkte schnell vergleichen?
Zitat:
[edit] Das wäre jetzt der Punkt, wo ich anfangen würde, über manuelle Optimierungen und Multithreading das meiste aus der Implementierung herauszuholen, da ich bezweifle, dass es in diesem Fall eine schnellere Lösung gibt, als für jeden Punkt den Vergleich auszuführen, wenn die Daten in Echtzeit hereinkommen. Denn jede optimierte Suche setzt irgendeine Sortierung voraus, und die minimale Laufzeit für einen Sortieralgorithmus beträgt laut Wikipedia O(n*log(n)). Ein einfacher Durchlauf über alle Punkte hingegen hat eine lineare Laufzeit von O(n), und ist somit bei deinen Datenmengen wahrscheinlich schneller als die Sortierung allein. Disclaimer: Ich habe kein Informatik studiert (noch nicht ;)). [/edit] |
AW: Wie kann man viele Punkte schnell vergleichen?
Ähm jain. Sie kommen nicht direkt in Echtzeit rein, da das ganze voraussichtlich unter Windows laufen wird.
Die Daten werde ich jedoch puffern und anschließend, wenn sie alle da sind, auswerten. Parallel zum Einlesen die Auswertung vorzunehmen wäre auf der einen Seite nett, aber dann müsste ich ständig prüfen, ob die Werte überhaupt so weit eingetroffen sind, dass sie ein Fenster ausfüllen. Und das ist wohl nicht möglich. Wenn die Werte bsp. in ein Fenster reichen, aber noch nicht drüber raus, dann habe ich unnötig herum gerechnet. Über die Grafikkarte lasse ich das nicht laufen. Das soll auf CPU-Ebene ablaufen. |
AW: Wie kann man viele Punkte schnell vergleichen?
Ändern sich die Daten denn zwischen zwei Messungen komplett, oder kommen z.B. nur rechts neue Werte hinzu und die alten verschieben sich nach links o.ä.?
|
AW: Wie kann man viele Punkte schnell vergleichen?
Die Kurvenform wird in den meisten Fällen ähnlich sein.
Vor jeder Messung werden die Daten jedoch gelöscht. |
AW: Wie kann man viele Punkte schnell vergleichen?
Bei Punkt und Ebene fällt mir immer die
![]() vergiss die erste Idee... 2: Idee: Berechne die Abstände zum Mittelpunkt des Rechtecks. Ist die Differenz (Positiv und Negativ) zum Rand minimal dann sind das die gesuchten Punkte. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 03:43 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz