- sortprogs.inc
- weitere Sortroutinen
- (6.54 KiB) 79-mal heruntergeladen
- sort.inc
- Meine Sortroutine(n)
- (22.56 KiB) 64-mal heruntergeladen
Lieber Theo & Co.
Jetzt kapiere ich langsam, daß mit Pointer(<integer>) kein "Zeiger" im eigentlichen Sinne gesetzt wird (für mich waren Pointer immer Zeiger auf einen Speicherort), sondern dieser dann einfach als Platzhalter für diese Integerzahl dient. Daher dann natürlich auch in der Vergleichsfunktion result := word(p)-word(q) - wenn zB Word-Variablen.
Hat lang gedauert, bis der Groschen gefallen ist...
Da ich nicht direkt TList.Sort verfolgen konnte, mußte ich mich mit dem jeweilgen Aufruf der Vergleichsfunktion IntSort begnügen. Aber um da feststellen zu können, welche Werte bzw. Elemente im Array gerade miteinander verglichen werden, mußte ich doch auf "normale" Zeiger zurückgreifen. Und zur Ausgabe der Werte benütze ich eine (bereits initialisierte) TStringlist (namens NameList):
Code: Alles auswählen
TYPE IntWord = Word; // zum Ausprobieren
array15 = array[0..14] of IntWord;
var aa:array15 = (4,1,1,2,5,2,3,1,2,8,4,1,6,9,2);
ww:array15;
SA: TArraySort;
procedure SortWerte(s:string); overload; // Speichern in StringList
begin namelist.Add(s); end;
procedure SortWerte(x,y:dword; px,py:intword); overload;
var s:string;
begin s:= sysutils.format('%4d %5d %3d %5d %3d %6d',[sortcount, x,y, px,py, px-py]);
SortWerte(s);
end;
Function IntSort(p,q:pointer):integer;
var n:integer; pp,qq:intword; pofs,qofs:word; s:string;
begin
pofs:=(p-@aa) div sizeof(aa[0]); qofs:=(q-@aa) div sizeof(aa[0]);
inc(sortcount); pp:=intword(p^); qq:=intword(q^);
SortWerte(pofs,qofs, pp,qq);
result:=intWord(p^)-intword(q^);
end;
Procedure SortInt;
var n,i:integer; liste:TList;
begin
n:=length(aa); liste:=TList.create; sortcount:=0;
SortWerte('TList-Sort');
for i:=0 to n-1 do Liste.Add(@aa[i]);
Liste.sort(@IntSort);
for i:=0 to n-1 do ww[i]:=intWord(Liste.Items[i]^);
end;
Meine SortierProzedur (mit den verschiedenen möglichen Algorithmen) habe ich wieder etwas geändert, um auch dynamische Arrays einlesen zu können. Ich brauche dazu aber die Angabe eines Ptr-arrays mit Zeigern auf dieses. In varinta sehe ich keine Möglichkeit, vom Programm selber erkennen zu lassen, ob es sich bei der Variablen um ein RangedArray handelt oder ein Openarray, obwohl der Compiler das ja sehr wohl unterscheidet (der liest aber auch den Quelltext, und das geht ja jetzt nicht). Oder gibt es da doch eine Möglichkeit?
Auch intern bin ich auf FPtr: array of pointer umgestiegen. Es könnte ja sein, daß sonst das REcordarray als Block grüßer wird als die gerade noch mögliche maximale Blockgröße im Speicher.
Die beiden Prozeduren (für einfache Arrays bzw RecordArrays) lauten jetzt:
Code: Alles auswählen
procedure SortArray(a:PChar; zg: array of pointer; cnt:dword; size:integer; // nur 1 Feld
v:variant; modus:array of integer);
// modus = [SortProc, Skip_doubles, ReverseCount]
procedure SortRecords(a:Pchar; zg: array of pointer; cnt:dword; size:integer;
v:array of word; offs:array of pchar; seq:array of integer;
modus:array of integer);
Dabei sind
Code: Alles auswählen
var a = der Array selber; cnt = Anzahl Records; size = Größe eines Records
v = variant des/der zu vergleichenden Feldes/Felder
offs = die dazugehörigen Offsets im Record
seq = Sortierreihenfolge als IntegerArray (beginnend mit 1), wenn negativ, dann Sortierrichtung abwärts
modus = [SortProc, Skip_doubles, ReverseCount] mit
Sortproc: 0 = KOLQSort, 1 = LazQSort, 2 = JumpSort
Skip_doubles: 1, wenn Verdoppelungen gelöscht werden sollen
ReverseCount: integer = (bei JumpSort) minimale Anzahl aufeinander folgender Elemente in absteigender Linie ( a[n]>a[n+1] ), die dann umgedreht wird
und dann der Aufruf mit
Code: Alles auswählen
Type intword = word; // zum Ausprobieren
Type array15 = array[0..14] of intword;
var aa:array15 = (4,1,1,2,5,2,3,1,2,8,4,1,6,9,2);
ww:array15;
Type
TSortType = (LazQ, KOLQ, Jump, Bubble, Shaker, Selection, Insertion, Shell,
Quick, UsualInsertion, UsualQuick);
Procedure SortVergleich;
var i: TSortType; n:integer; s:string;
begin
namelist.clear;
move(aa,ww, sizeof(aa));
Sortint;
move(aa,ww, sizeof(aa));
for i:= low(TSortType) to high(TSortType) do
begin
move(ww,aa, sizeof(aa));
SA.SortArray(@aa, nil, length(aa), sizeof(aa[0]), aa[0], [byte(i)]); // LazQSort
s:=''; for n:=0 to length(aa)-1 do s:= s + intToStr(aa[n]) + ' ';
namelist.add(s+#13#10);
end;
namelist.SaveToFile(LazDocs+'SortArray.txt');
halt;
end;
Habe jetzt auch den Fehler in meiner Quicksort-Portierung entdeckt:
Ich hatte übersehen, daß Lazarus beim Bestimmen des Pivot- (Dreh-, Angelpunkt) Elements p der Liste wohl auf die (schon geordnete) Liste zugreift:
p := Liste^[(L+R) div 2],
dann aber während des ganzen Prozedurdurchlaufs, auch wenn zwischendrin Liste^[(L+R) div 2] selbst verändert wird (mit einem anderen ListenElement vertauscht), es selber unverändert bleibt. Bei meinem Programm nahme ich beide zu vergleichenden Elemente jeweils aus der sortierten Liste.
Ich mußte also nur in meinem Programm eine neue Vergleichsfunktion LSort(x,y:integer):integer einfügen, in der nur das zweite Element aus der sich ändernden Liste genommen wird.
So sieht meine Sortierfunktion nun folgendermaßen aus (schon angepaßt an Sortierung beliebiger Records mit array[variants] und array[offsets] zur Bestimmung des Ortes der zu vergleichenden Record-Felder:
Code: Alles auswählen
Function TSortArrayType.SeqSort(seq:array of word; p,q:PChar):integer;
// sortieren gemäß SortierSequenz
var n:word; i:integer; POffs,QOffs:Pchar;
begin
result:=0; i:=0;
repeat
n:=seq[i];
POffs:=p+Foffsets[n]; QOffs:=q+ Foffsets[n];
result:= aufab[n]*VarTypeSort(Ftypes[n], poffs,qoffs);
if result<>0 then
exit;
inc(i);
until i>=length(seq);
end;
function TSortArrayType.Sort(x,y:dword):integer; // Vergleich zweier Elemente
var p,q:pchar;
begin
p:= FPtr[x]; q:= Fptr[y];
//p:=FData+x*FSize; q :=FData +y*FSize;
if Felder = 1 then
result:= VarTypeSort(Ftype, p, q)
else
result:= SeqSort(FSeq, p, q);
end;
function TSortArrayType.LSort(x,y:dword):integer; // Lazarus Quicksort
begin result:= sort(x, pp^[y]); end;
function TSortArrayType.PPSort(x,y:dword):integer;
begin result:= sort(pp^[x], pp^[y]); end;
Wenn ich jetzt hiermit LazQSort aufrufe, dann stimmt das Ergebnis mit dem von Tlist.Sort überein.
Dann brauche ich auch die gleichen 61 Vergleichsoperationen...
Ich dachte, mein Sortieralgorithmus würde weniger Vergleiche benötigen, aber der braucht auch 53, KOL hingegen nur 44!
Übrigens, mit der Einfügung von if p<>FList^
bzw p<>FList^[j] bei den beiden while's
Code: Alles auswählen
while Compare(P, FList^[i]) > 0 do
I := I + 1;
while Compare(P, FList^[J]) < 0 do
J := J - 1;
konnte ich auch gleich 10 compare's vermeiden (damit hätte dann Lazarus doch wieder weniger als mein Programm)
Und mit der Abfrage zu Beginn, wenn R-L = 1, dann nur Tausch (falls [L]>[R]), wurde es noch ein compare weniger (also 50):
Code: Alles auswählen
if R-L=1 then
begin
if Compare(Flistt^[L], Flist[R]>0) then swap(L,R);
exit;
end;
Es würde sich wohl fast lohnen, die KOL-Version in Lazarus zu übernehmen...
Habe noch eine ganze Reihe anderer Sortieralgorithmen gefunden, die ich an mein Programm angepasst habe (v.a. mußte ich einige Male auch dort PPsort durch LSort ersetzen). Da gibt es auch eine eigene Quicksort-Routine, die sogar auf nur 47 Compare-Aufrufe kommt.
Aber am wenigsten (zumindest bei diesem Array) braucht KOL.