====== std.sort — Sortierung ======
→ [[lyx_-_programmiersprache:units|Zurück zur Unit-Übersicht]]
QuickSort für ''int64''-Felder mit Median-of-Three-Pivotwahl und Insertion-Sort für kurze Teilfolgen. Sortiert an Ort und Stelle, ohne zusätzlichen Speicher. Die Unit hat genau eine öffentliche Funktion.
**Autor:** Andreas Röne\\
**Copyright:** 2024–2025 Andreas Röne\\
**Quelle:** ''std/sort.lyx''
Der frühere Sortierfehler (#1458) ist **behoben**. ''Partition'' arbeitet jetzt nach dem Lomuto-Schema mit eindeutigem Trennindex. Nachgeprüft mit ''lyxc 1.0.21A'': 500 Zufallsfelder, jeweils Reihenfolge **und** Elementsumme geprüft — **0 Fehler**. Auch [[lyx_-_programmiersprache:units:stats|std.stats]] (''ArraySort'', ''ArrayMedian'') ist damit wieder verlässlich.
===== Import =====
import std.sort;
----
===== Funktionen =====
^ Signatur ^ Beschreibung ^
| ''SortInt64(arr: int64, len: int64): void'' | Sortiert ''len'' ''int64''-Werte ab Adresse ''arr'' aufsteigend, in-place |
''arr'' ist ein roher Speicherblock mit 8 Byte Schrittweite; ''len'' zählt Elemente, nicht Bytes. ''len <= 1'' kehrt sofort zurück, ''len == 0'' ist zulässig.
Die übrigen Funktionen der Datei (''Partition'', ''MedianOfThree'', ''InsertionSort'', ''QuickSortInternal'', ''SortSwap'', ''SortGet'') sind unit-privat.
----
===== Aufbau =====
SortInt64(arr, len)
└─ QuickSortInternal(arr, low, high)
├─ Teilfolge < 10 Elemente → InsertionSort
└─ sonst: Partition (Median-of-Three) → zwei Rekursionsäste
Der Schwellwert von 10 Elementen ist fest verdrahtet: für kurze Teilfolgen ist Insertion-Sort schneller als die Rekursion. Median-of-Three wählt den Pivot aus erstem, mittlerem und letztem Element und vermeidet damit den Worst-Case bei bereits sortierten Eingaben.
**Laufzeit:** im Mittel O(n log n), im schlechtesten Fall O(n²). Die Sortierung ist **nicht stabil** — gleiche Werte können ihre Reihenfolge tauschen. Bei reinen Zahlen spielt das keine Rolle; wer Indizes mitführt, muss es beachten.
----
===== Beispiel =====
import std.sort;
import std.alloc;
import std.math;
fn zeige(arr: int64, n: int64): void {
var i: int64 := 0;
while (i < n) { Print(IntToStr(peek64(arr + i*8)), " "); i := i + 1; }
PrintLn("");
}
fn istSortiert(arr: int64, n: int64): int64 {
var i: int64 := 1;
while (i < n) { if (peek64(arr + (i-1)*8) > peek64(arr + i*8)) { return 0; } i := i + 1; }
return 1;
}
fn summe(arr: int64, n: int64): int64 {
var i: int64 := 0; var s: int64 := 0;
while (i < n) { s := s + peek64(arr + i*8); i := i + 1; }
return s;
}
fn main(): int64 {
var w: int64 := alloc(10 * 8);
poke64(w, 33); poke64(w+8, 18); poke64(w+16, 43); poke64(w+24, 7);
poke64(w+32, 45); poke64(w+40, 8); poke64(w+48, 28); poke64(w+56, 15);
poke64(w+64, 1); poke64(w+72, 35);
Print("Eingabe: "); zeige(w, 10);
SortInt64(w, 10);
Print("Ergebnis: "); zeige(w, 10);
// Belastungstest: Reihenfolge und Elementsumme ueber viele Zufallsarrays
RandomSeed(11);
var fehler: int64 := 0;
var runde: int64 := 0;
while (runde < 500) {
var a: int64 := alloc(50 * 8);
var i: int64 := 0;
while (i < 50) { poke64(a + i*8, Abs64(Random()) % 1000); i := i + 1; }
var vorher: int64 := summe(a, 50);
SortInt64(a, 50);
if (istSortiert(a, 50) == 0 || summe(a, 50) != vorher) { fehler := fehler + 1; }
free(a, 50*8);
runde := runde + 1;
}
PrintLn("fehlerhaft sortiert: ", IntToStr(fehler), " von 500 Zufallsarrays (n=50)");
// Sonderfaelle
var e: int64 := alloc(8);
poke64(e, 42);
SortInt64(e, 1); PrintLn("n=1 unveraendert: ", IntToStr(peek64(e)));
SortInt64(e, 0); PrintLn("n=0 ueberlebt");
return 0;
}
Ausgabe:
Eingabe: 33 18 43 7 45 8 28 15 1 35
Ergebnis: 1 7 8 15 18 28 33 35 43 45
fehlerhaft sortiert: 0 von 500 Zufallsarrays (n=50)
n=1 unveraendert: 42
n=0 ueberlebt
----
===== Eine Sortierung richtig prüfen =====
Der Belastungstest im Beispiel ist keine Zierde — genau diese Form hätte den früheren Fehler #1458 sofort gezeigt, während die üblichen Testfälle ihn durchgelassen haben. Zwei Dinge gehören zusammen:
* **Reihenfolge** allein übersieht verlorene oder verdoppelte Werte.
* **Elementsumme** allein übersieht Vertauschungen.
Und zwar über **Zufallsfelder**. Bereits sortierte, umgekehrte oder gleichförmige Eingaben liefen bei #1458 alle korrekt durch; nur echte Zufallsdaten fielen durch — 448 von 500.
fn istSortiert(arr: int64, n: int64): int64 {
var i: int64 := 1;
while (i < n) {
if (peek64(arr + (i-1)*8) > peek64(arr + i*8)) { return 0; }
i := i + 1;
}
return 1;
}
----
===== Grenzen =====
* **Nur ''int64''.** Für andere Typen oder eigene Vergleichsregeln gibt es keine Variante mit Vergleichsfunktion.
* **Nur aufsteigend.** Absteigend erhält man durch Umdrehen des sortierten Feldes oder über ''ListInt64SortDesc'' aus [[lyx_-_programmiersprache:units:list|std.list]].
* **Nicht stabil.**
* **Keine Bereichsprüfung.** ''SortInt64'' vertraut auf ''len''; ein zu großer Wert sortiert über den Puffer hinaus.
----
===== Verwandte Units =====
* [[lyx_-_programmiersprache:units:stats|std.stats]] — ''ArraySort'', ''ArrayMedian'' und die übrigen Kennzahlen
* [[lyx_-_programmiersprache:units:list|std.list]] — ''ListInt64Sort''/''ListInt64SortDesc'' für dynamische Listen
* [[lyx_-_programmiersprache:units:math|std.math]] — ''Min64'', ''Max64'', ''Clamp64''