Autor |
Beitrag |
Heiko
      
Beiträge: 3169
Erhaltene Danke: 11
|
Verfasst: Mi 05.10.05 15:21
Kann jemand vlt. mir sagen, welches Sortierverfahren am schnellsten ist für Strings, denn QuickSort ist ja bekanntlich nicht die schnellste Variante. Die Anzahl der Strings beläuft sich da von 0-10 000, wobei es meistens im Bereich von 500-2000 ist.
PS: Am liebsten wäre es mit gleich mit einem Link zu der Erklärung des Verfahrens und am besten gleich mit Quelltext, der aber nicht zwingend dabei sein muss  .
|
|
uall@ogc
      
Beiträge: 1826
Erhaltene Danke: 11
Win 2000 & VMware
Delphi 3 Prof, Delphi 7 Prof
|
Verfasst: Mi 05.10.05 15:40
Wofür brauchst du was schnelleres als Qicksort? Das wird doch wohl für deine Zwecke reichen. Qucksort hat schon nen laufzeitverhalten von n*log(n) was schnell ist, gibt noch heapsort und mergesort die gleich schnell sind. aber glaub für die paar elemente ist das egal. ich kenn nur einen alg mit dem laufzeitverhalten n, aber den kannste auf strings nicht anwenden.
_________________ wer andern eine grube gräbt hat ein grubengrabgerät
- oder einfach zu viel zeit
|
|
Gausi
      
Beiträge: 8548
Erhaltene Danke: 477
Windows 7, Windows 10
D7 PE, Delphi XE3 Prof, Delphi 10.3 CE
|
Verfasst: Mi 05.10.05 15:45
Wenn es sehr viele relativ kurze Strings sind, würde sich RadixSort empfehlen. den müsste man evtl. nur anpassen, da deine Elemente (Strings) nicht alle gleich lang sind.
Da du aber nur ziemlich wenige Strings sortieren musst, und du für jedes Zeichen einen Durchlauf machen musst (z.B wenn du das Wort "Sortierverfahren" in deiner Stringliste hast, musst du 16 Durchläufe machen), dürfte der Aufwand von O(N LogN) im Mittel bei Quicksort in deinem Fall fast besser sein als der lineare Aufwand bei RadixSort.
Hinzu kommt ein erhöhter Speicherbedarf für RadixSort.
Überschlagsrechnung:
Quelltext 1: 2: 3:
| 16 * N ?<? logN * N 16 ?<? logN 2^16 ?<? N |
Fazit: Wenn du so in etwa mehr als 2^16 (bzw. 2^x, wobei x die Länge deines längsten Wortes ist) Strings sortieren musst, dann würde ich von Quicksort abraten, ansonsten bleib besser dabei.
Anmerkung: Ich weiss, dass in der Laufzeit von Quicksort auch noch Konstanten drin sind, und man deshalb eigentlich nicht so rechnen kann, aber das Prinzip sollte klar sein...
_________________ We are, we were and will not be.
|
|
Grishnak
      
Beiträge: 221
Windows XP Home
Delphi 7 PE, Delphi 2005 PE
|
Verfasst: Mi 05.10.05 15:47
@Heiko: Wichtig zu wissen: Sind diese Strings a) aufsteigend vorsortiert, b) absteigend vorsortiert oder c) unsortiert? Bei aufsteigend vorsortierten Feldern ist Quicksort mWn nicht so effektiv und wird von "primitiveren" Algorithmen geschlagen. Ansonsten kenne ich Quicksort als schnellste Möglichkeit.
_________________ Mach' etwas idiotensicher und irgendjemand erfindet einen besseren Idioten!
|
|
Gausi
      
Beiträge: 8548
Erhaltene Danke: 477
Windows 7, Windows 10
D7 PE, Delphi XE3 Prof, Delphi 10.3 CE
|
Verfasst: Mi 05.10.05 15:55
Grishnak hat folgendes geschrieben: | Bei aufsteigend vorsortierten Feldern ist Quicksort mWn nicht so effektiv und wird von "primitiveren" Algorithmen geschlagen. |
Das kommt sehr darauf an, wie man bei Quicksort das Pivotelement wählt. Die Methode "Sort" bei Delphi verwendet einen Quicksort mit Pivot-Element = mittleres Element(*), sodass bei auf-/ absteigend sortierten Listen eine optimale Aufteilung in zwei Teilfolgen erreicht wird. Daher zweifele ich deine Aussage zumindest teilweise an.
Wenn man als Pivotelement jedoch das erste/letzte Element wählt, bekommt man bei solchen Listen natürlich eine quadratische Laufzeit. Da Bubblesort die Vorsortierung in dem Fall besser ausnutzt, liegt dann Quicksort weit hinten.
(*) hab ich herausgefunden, als ich Sort aufgerufen habe mit einer Liste von 10.000 Elementen, die dadurch entstanden ist, dass ich 2 identische 5000er Listen aneinandergehägt habe. Der Sortiervorgang hat solange gedauert, dass ich das Prog mit Gewalt beenden musste. Hab ich diese Liste vorher "randomisiert", oder eine sortierte Liste mit 10.000 Elementen sortiert, war er in nullkommanix fertig.
_________________ We are, we were and will not be.
|
|
Udontknow
      
Beiträge: 2596
Win7
D2006 WIN32, .NET (C#)
|
Verfasst: Mi 05.10.05 16:03
Ein allgemeine Praxishinweis (vielleicht ein wenig Offtopic, aber bei 5000-10000 Strings und trotdem langer Wartezeit liegt es vielleicht sogar daran):
Variablen immer mit const oder var an Subroutinen übergeben, ansonsten kann sich die benötigte Zeit dramatisch erhöhen, da das Programm bei jedem Aufruf von Subroutinen erst den Variableninhalt kopieren muß. Bei Pointern, Integerwerten etc. ist das egal, aber alle Variablen, die größer als ein Pointer (also 4 Byte) sind, sollten so an Subroutinen übergeben werden (es sei denn, das spezielle Verhalten, daß Variablen innehrhalb der Routine änderbar sind, aber die Änderung nicht an die aufgerufene "Ebene" zurückgegeben werden, ist erwünscht).
Cu,
Udontknow
|
|
Grishnak
      
Beiträge: 221
Windows XP Home
Delphi 7 PE, Delphi 2005 PE
|
Verfasst: Mi 05.10.05 16:54
@Gausi:
Ich hab jetzt mal 50.000 Zufalls-Strings der Länge 8 in einer StringList gespeichert und mittels Sort-Methode sortieren lassen. Das ganze wurde anschließend nochmals sortiert.
Dabei hat das erneute Sortierten der bereits sortierten Liste ca. 66% der Zeit gedauert, die das Sortieren einer unsortierten Liste dauerte. Das deutet mMn nicht auf die Wahl des mittleren Elementes als Pivot-Element hin!
_________________ Mach' etwas idiotensicher und irgendjemand erfindet einen besseren Idioten!
|
|
Gausi
      
Beiträge: 8548
Erhaltene Danke: 477
Windows 7, Windows 10
D7 PE, Delphi XE3 Prof, Delphi 10.3 CE
|
Verfasst: Mi 05.10.05 17:05
Grishnak hat folgendes geschrieben: | @Gausi:
Dabei hat das erneute Sortierten der bereits sortierten Liste ca. 66% der Zeit gedauert, die das Sortieren einer unsortierten Liste dauerte. Das deutet mMn nicht auf die Wahl des mittleren Elementes als Pivot-Element hin! |
Hast du dich hier vertippt, oder bestätigst du damit nicht meine Aussage? Wenn das sortieren der sortierten Liste nur 66% der Zeit zum sortieren der unsortieren Liste benötigt, dann ist das weniger
Wie ich sagte, basiert meine Vermutung auf einem Test, den ich gemacht habe. Und es erscheint mir auch sinnvoll, nicht das letzt Element zu nehmen, da es doch öfter vorkommt, dass man eine fast sortierte Liste novhmal schnell "drübersortiert".
Und Schwankung um den Faktor 2 sind durchaus noch im "Messfehlerbereich" 
_________________ We are, we were and will not be.
|
|
Heiko 
      
Beiträge: 3169
Erhaltene Danke: 11
|
Verfasst: Mi 05.10.05 17:12
So bin jetzt wieder vom Einkaufen zurück und kann auf ein paar Nachfragen jetzt Antwort geben  .
@Länge der Strings: Sie ist ganz verschieden. Wie sich bei dieser Typendeklaration sicherlich denken kann.
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17:
| type TMusikInfo=record DateiPfad: String; Titel: String; Interpret: String; Album: String; Genre: String; Jahr: Integer; TitelNr: Integer; Kommentar: String; Komponist: String; Encoder: String; Copyright: String; Sprache: String; Link: String; Format: String; end; |
Ich will also immer nach einem dieser Elemente sortieren (dynamisches Array), wodurch ich vorher nicht sagen kann, wie lang ein String ist, da es ja je nach dem Kriterium verschieden ist und ich nicht für jedes Element ein extra Verfahren haben will.
@Vorsortiert: Jein, die sind nach einem Kriterium sortiert, welches keine Rolle für das Suchverfahren besitzten wird. Sprich: Es kann sein das die Liste nach dem titel sortiert ist und jetzt nach dem Album sortiert werden soll  .
|
|
Udontknow
      
Beiträge: 2596
Win7
D2006 WIN32, .NET (C#)
|
Verfasst: Mi 05.10.05 17:43
Noch einmal offtopic:
Weitere Geschwindigkeitsvorteile gewinnst du, indem du in deinem Array nur Pointer auf per Getmem erstellte Records speicherst, sodaß beim Sortieren nicht der gesamte Record, sondern nur der Pointer auf einen Record ausgetauscht wird.
Cu,
Udontknow
|
|
Heiko 
      
Beiträge: 3169
Erhaltene Danke: 11
|
Verfasst: Mi 05.10.05 17:45
Mhm, ich kenne mich nicht so gut mit Pointern aus. Das mit GetMem und so bekomme ich hin, aber wie ich genau auf die einzelnen Elemente zeigen kann nicht. Muss ich mir mal angucken  .
//Edit: Wie macht man es am besten, wenn in man die Größe eines Strings nicht kennt, den man einlesen will?
|
|
Udontknow
      
Beiträge: 2596
Win7
D2006 WIN32, .NET (C#)
|
Verfasst: Mi 05.10.05 18:19
Hier mal ein objektorientierter Ansatz. Dadurch, daß nun der Record als Objekt (sprich Pointer) definiert ist, sollte das auch schon einen Effekt auf die Geschwindigkeit haben.
Ich hätte es wohl von Anfang an als DB-Projekt konzipiert, aber naja...
Du musst ein TMusikinfos-Objekt erstellen. Bei diesem kannst du dann sooft du eben Daten hast Add aufrufen und den neuen Eintrag mit Daten bestücken. Sortiert wird diese Liste dann über die angegebenen zwei Methoden (ist leider unflexibel, da man die einzelnen Felder des Records nicht dynamisch über einen Index ansprechen kann, wie man es bei Feldern eines Datasets machen könnte).
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146:
| unit U_Musikinfo;
interface
uses SysUtils, Classes;
type TMusikInfo=Class DateiPfad: String; Titel: String; Interpret: String; Album: String; Genre: String; Jahr: Integer; TitelNr: Integer; Kommentar: String; Komponist: String; Encoder: String; Copyright: String; Sprache: String; Link: String; Format: String; end;
type TMusikInfos=class private FItems:TList; function GetItems(Index: Integer): TMusikInfo; procedure SetItems(Index: Integer; const Value: TMusikInfo); function GetCount: Integer; public function Add:TMusikInfo; procedure Delete(Index:Integer); procedure Clear; property Items[Index:Integer]:TMusikInfo read GetItems write SetItems; property Count:Integer read GetCount;
procedure SortierenNachDateiPfad; procedure SortierenNachTitel;
constructor Create; virtual; destructor Destroy; override; end;
implementation
{$R *.dfm}
function Vergleich_Titel(Item1, Item2: Pointer): Integer; var I1,I2:TMusikInfo; begin I1:=TMusikInfo(Item1); I2:=TMusikInfo(Item2);
Result:=0; if I1.Titel<I2.Titel then Result:=-1 else if I1.Titel>I2.Titel then Result:=1 end;
function Vergleich_DateiPfad(Item1, Item2: Pointer): Integer; var I1,I2:TMusikInfo; begin I1:=TMusikInfo(Item1); I2:=TMusikInfo(Item2);
Result:=0; if I1.DateiPfad<I2.DateiPfad then Result:=-1 else if I1.DateiPfad>I2.DateiPfad then Result:=1 end;
function TMusikInfos.GetItems(Index: Integer): TMusikInfo; begin Result:=TMusikInfo(FItems[Index]); end;
procedure TMusikInfos.SetItems(Index: Integer; const Value: TMusikInfo); begin FItems[Index]:=Value; end;
function TMusikInfos.Add: TMusikInfo; begin Result:=TMusikInfo.Create; FItems.Add(Result); end;
procedure TMusikInfos.Delete(Index: Integer); begin TMusikInfo(FItems[Index]).Free; FItems.Delete(Index); end;
procedure TMusikInfos.Clear; begin While FItems.Count>0 do Delete(0); end;
function TMusikInfos.GetCount: Integer; begin Result:=FItems.Count; end;
constructor TMusikInfos.Create; begin FItems:=TList.Create; end;
destructor TMusikInfos.Destroy; begin Clear; FItems.Free; inherited; end;
procedure TMusikInfos.SortierenNachTitel; begin FItems.Sort(Vergleich_Titel); end;
procedure TMusikInfos.SortierenNachDateiPfad; begin FItems.Sort(Vergleich_DateiPfad); end;
end. |
Cu,
Udontknow
|
|
Gausi
      
Beiträge: 8548
Erhaltene Danke: 477
Windows 7, Windows 10
D7 PE, Delphi XE3 Prof, Delphi 10.3 CE
|
Verfasst: Mi 05.10.05 18:31
Also, wenn deine Strings SO aussehen  , dann kann ich dir aus eigener Erfahrung sagen: Nimm ruhig die Standardvariante mit dem Quicksort. Das ist schnell genug. Und insbesondere beim Sortieren nach Pfad dürfte Quicksort wesentlich schneller sein.
Ich hab auch ziemlich viele solche "Strings", und wenn ich auf den Header in meinem VirtualTreeView klicke, dann gibt es ne Verzögerung von ca. 0,5 Sekunden, bis "1910 Fruitgum Company - Simon Says" ganz oben und "ZZ Top - What Would You Do" ganz unten ist  Und bei mir ist die Anzahl noch etwas über deinem Maximalbereich (0-10.000)
_________________ We are, we were and will not be.
|
|
Grishnak
      
Beiträge: 221
Windows XP Home
Delphi 7 PE, Delphi 2005 PE
|
Verfasst: Mi 05.10.05 19:03
@Gausi: "Schneller" ist nicht das gleiche wie "schnell"! Wenn ein Sortiert-Algorithmus ca. 2/3 der Zeit für das sortieren eines bereits sortierten Feldes braucht, dann ist er dafür weniger geeignet - genau das wollte ich andeuten! Aber: back-to-topic!
_________________ Mach' etwas idiotensicher und irgendjemand erfindet einen besseren Idioten!
|
|
alzaimar
      
Beiträge: 2889
Erhaltene Danke: 13
W2000, XP
D6E, BDS2006A, DevExpress
|
Verfasst: Mi 05.10.05 19:21
Soweit ich weiss, soll Mergesort etwas schneller sein, aber ich denke, es lohnt den Aufwand nicht. Kopier dier das Quicksort von TStringlist und experimentier damit rum.
Wenn Du noch schneller werden willst, dann nimm ein iteratives Quicksort, das ist nochmal 10% schneller. Grundsätzlich sollte auf Rekursion verzichtet werden (so schon, wie sie ist), wenn es schnell gehen soll.
Hier, QnD (Quick'n Dirty):
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37:
| procedure QuickSortIt (a : TIntegerArray; L,R : Integer); var sp, I, J,p,t: Integer; s : Array [0..100,0..1] Of Integer; begin fillchar (s,sizeOf(s),0); sp := 1; s[0,0] := L; s[0,1] := R; While sp>0 Do Begin dec (sp); l := s[sp,0]; r := s[sp,1];
repeat I := L; J := R; P := a[(L + R) shr 1]; repeat while a[i]<p do inc (i); while a[j]>p do dec (j); if I <= J then begin T := a[I];a[I] := a[J];a[J] := T; Inc(I); Dec(J); end; until I > J; if L < J then begin s[sp,0] := l; s[sp,1] := j; inc (sp); End; L := I; until I >= R; End; end; |
_________________ Na denn, dann. Bis dann, denn.
|
|
Gausi
      
Beiträge: 8548
Erhaltene Danke: 477
Windows 7, Windows 10
D7 PE, Delphi XE3 Prof, Delphi 10.3 CE
|
Verfasst: Mi 05.10.05 19:25
Ist ja eigentlich nicht OT, da es ja um geeignete (=schnelle) Sortierverfahren geht. Ich hatte dich da falsch verstanden. Ich dachte, du vergleichst beide Verfahren, aber die Angaben waren wohl für Quicksort bei 2 Listen.
Das widerspricht aber nicht meiner Vermutung. Sie bestätigt nur die Güte von Quicksort und die Tatsache, dass "im Mittel" die Liste gut aufgeteilt wird. Nur daher kommt ja die schnelle Laufzeitabschätzung.
D.h. eine unsortierte Liste wird nur um 30% langsamer sortiert, als wenn man eine optimale Aufteilung hat (ich gehe weiter von meiner Vermutung aus).
Es ist richtig, dass Quicksort keine Vorsortierung ausnutzen kann. Deshalb ist Quicksort nie dann optimal, wenn man sortierte Listen sortiert. Aber Qicksort ist in vielen Fällen sehr "quick", daher ja auch der Name. In der Praxis hat der sich ganz einfach bewährt.
Und da du hier keinen "Spezialfall" hast, wo du ganz spezielle Voraussetzungen an deine Liste stellst, würde ich einfach dabei bleiben. Einen spürbar schnelleren Algo wirst du kaum finden. (und das ist wieder voll On-Topic  )
_________________ We are, we were and will not be.
|
|
MSCH
      
Beiträge: 1448
Erhaltene Danke: 3
W7 64
XE2, SQL, DevExpress, DevArt, Oracle, SQLServer
|
Verfasst: Mi 05.10.05 19:33
ich auch mal; bei einer solchen Menge, die wie ich vermute, als File of Record (?) irgentwo auf der Platte dümpelt, ist da nicht eine kleine Datenbank besser? Es reicht ja dazu Access und Ado. Und du hast deine Probleme mehr oder weniger gelöst, wenn du dort die richtigen Indizes setzt. Dann ist das Laden und Umsortieren nur noch abhängig von der Hardware.
grez
msch
_________________ ist das politisch, wenn ich linksdrehenden Joghurt haben möchte?
|
|
alzaimar
      
Beiträge: 2889
Erhaltene Danke: 13
W2000, XP
D6E, BDS2006A, DevExpress
|
Verfasst: Mi 05.10.05 19:40
Kurze Frage am Rande:
Wozu sortieren? Zum schnellen finden? Dann wäre eine Dictionary (Hashliste) ideal. Einfügen ist O(1), Suchen ist O(1), Löschen auch. Nur mit der sortierten Ausgabe haperts.
Eine weitere Möglichkeit könnten die Skiplist sein. Das hat ein nahezu lineares Laufzeitverhalten.
_________________ Na denn, dann. Bis dann, denn.
|
|
Heiko 
      
Beiträge: 3169
Erhaltene Danke: 11
|
Verfasst: Mi 05.10.05 20:25
Ne, die ich erListe sortiere ich nicht zum schnellen finden. Das sortieren soll für den mp3-Tag-Editor von [url= www.killprocess.de.vu]KillProcess[/url] sein. Dabei suche ich nach allen Musikdateien und will dem User die Daten dann nach z.B. dem Titel sortiert präsentieren.
|
|
alzaimar
      
Beiträge: 2889
Erhaltene Danke: 13
W2000, XP
D6E, BDS2006A, DevExpress
|
Verfasst: Mi 05.10.05 20:33
Ach so. Na dann Quicksort. Oder einfach TStringlist.Sort einfach gehts doch nicht. Und ob der anwender 100 oder 120ms wartet ist doch peng.
Hier: Teste selbst
Einloggen, um Attachments anzusehen!
_________________ Na denn, dann. Bis dann, denn.
|
|
|