diff --git a/sorting_visualization.html b/sorting_visualization.html index b5d8dfa..f3c97fb 100644 --- a/sorting_visualization.html +++ b/sorting_visualization.html @@ -204,7 +204,7 @@

- Sortier-Algorithmen v0.2.11 + Sortier-Algorithmen v0.2.12

Interaktive Visualisierung mit schrittweiser Animation

@@ -256,6 +256,7 @@ + @@ -316,7 +317,7 @@
-
+
+

Funktionsweise: Der „dümmste" Sortieralgorithmus: Das Array wird zufällig gemischt (Fisher-Yates-Shuffle), dann wird geprüft, ob es jetzt zufällig sortiert ist. Wenn nicht, wird erneut gemischt. Das wird wiederholt, bis das Array durch Zufall in der richtigen Reihenfolge landet. Es gibt keine Garantie, dass das jemals passiert — im Erwartungswert braucht man n! Versuche (bei n = 8 sind das 40.320 Mischvorgänge).

-

Darstellung: Bogo Sort erzeugt ein hektisches Blitz-Muster: Das gesamte Array blinkt rot auf (Shuffle), dann wandern kurze orange Bögen von links nach rechts durch das Array (Sortiertheitscheck). Scheitert die Prüfung, folgt sofort der nächste rote Shuffle. Die kleinen Wertemarken unter den Balken zeigen bei jedem Shuffle-Versuch eine neue zufällige Anordnung. Der Fortschrittsbalken bewegt sich kaum — tausende Schritte bei nur 8 Elementen zeigen eindrücklich, wie nutzlos rein zufälliges Ausprobieren ist. Die Array-Größe ist automatisch auf 8 begrenzt.

+

Darstellung: Bogo Sort erzeugt ein hektisches Blitz-Muster: Das gesamte Array blinkt rot auf (Shuffle), dann wandern kurze orange Bögen von links nach rechts durch das Array (Sortiertheitscheck). Scheitert die Prüfung, folgt sofort der nächste rote Shuffle. Die kleinen Wertemarken unter den Balken zeigen bei jedem Shuffle-Versuch eine neue zufällige Anordnung. Der Fortschrittsbalken bewegt sich kaum — tausende Schritte bei nur 8 Elementen zeigen eindrücklich, wie nutzlos rein zufälliges Probieren ist. Die Array-Größe ist automatisch auf 8 begrenzt.

Wann einsetzen: Niemals in der Praxis. Bogo Sort dient als Lehr- und Abschreckungsbeispiel: Er zeigt eindrücklich, warum zufälliges Probieren exponentiell schlecht skaliert. Bei nur 8 Elementen braucht er im Schnitt ~40.000 Versuche, bei 10 Elementen schon ~3,6 Millionen. Der Name ist ein Wortspiel auf „bogus" (falsch/wertlos). In der theoretischen Informatik dient er als Referenz für die schlechteste denkbare Komplexitätsklasse. Die Array-Größe wird hier automatisch auf 8 begrenzt, um den Browser nicht einzufrieren.

+
+

Fisher-Yates Shuffle

+
+ Stabil + In-place + O(n) Zeit · O(1) Speicher +
+

Funktionsweise: Der Fisher-Yates-Algorithmus (auch Knuth-Shuffle) mischt ein Array uniform zufällig. Er läuft rückwärts: Für jede Position i wird ein zufälliger Index j zwischen 0 und i gewählt und die beiden Elemente getauscht. Jede der n! Permutationen wird mit gleicher Wahrscheinlichkeit erzeugt.

+

Darstellung: Rote Bögen zeigen jeden Tausch — keine Vergleiche, nur Tausche. Die Anzahl der Tausche ist immer n-1 (bei n Elementen). Das macht die Effizienz des Mischens im Vergleich zum Sortieren direkt sichtbar.

+

Wann einsetzen: Zum Erzeugen von Testdaten, zum „Nochmal-Mischen" eines sortierten Arrays, oder als Gegenbeweis: Ein einziger Shuffle macht jede Sortierung rückgängig. Mischen ist O(n), Sortieren ist O(n log n) — also ist Mischen immer schneller als Sortieren.

+
+ @@ -802,6 +820,7 @@ const $btnPlayPause = document.getElementById('btnPlayPause'); const $btnStepBack = document.getElementById('btnStepBack'); const $btnStep = document.getElementById('btnStep'); const $btnReset = document.getElementById('btnReset'); +const $btnShuffle = document.getElementById('btnShuffle'); const $btnTheme = document.getElementById('btnTheme'); const $progressFill = document.getElementById('progressFill'); const $stepCounter = document.getElementById('stepCounter'); @@ -1756,6 +1775,21 @@ function buildSteps(algoName) { break; } + case 'shuffle': { + const n = arr.length; + if (n === 0) break; + // Fisher-Yates Shuffle (Knuth variant) + for (let i = n - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + if (j !== i) { + [arr[i], arr[j]] = [arr[j], arr[i]]; + swaps++; + snap('swap', [i, j]); + } + } + break; + } + case 'pancake': { const n = arr.length; function flip(end) { @@ -2606,6 +2640,28 @@ $btnStepBack.addEventListener('click', function() { $btnReset.addEventListener('click', doReset); +$btnShuffle.addEventListener('click', function() { + stopTimer(); + isRunning = false; + isPaused = false; + // Fisher-Yates Shuffle on current array + for (let i = array.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + if (j !== i) { + [array[i], array[j]] = [array[j], array[i]]; + } + } + steps = []; + stepIndex = 0; + startTime = 0; + elapsedMs = 0; + setStats(0, 0, 0, 0); + $statTime.textContent = '0 ms'; + renderCurrent(); + updateButtonStates(); + $stepExplanation.textContent = 'Array gemischt \u2713'; +}); + // ================================================================ // Slider & Input Events // ================================================================ diff --git a/test_algorithms.js b/test_algorithms.js index 5d49682..0396fb6 100644 --- a/test_algorithms.js +++ b/test_algorithms.js @@ -234,7 +234,7 @@ const ALGORITHMS = [ 'merge', 'heap', 'quick', 'quick3way', 'dualpivot', 'introsort', 'shell', 'tree', 'timsort', 'counting', 'radix', 'bucket', - 'pancake', 'cycle', 'bogo', + 'pancake', 'cycle', 'bogo', 'shuffle', ]; const Bogo_MAX_SIZE = 6; @@ -331,6 +331,7 @@ console.log('═'.repeat(50)); const NORMAL_SIZES = [10, 50]; for (const algo of ALGORITHMS) { + if (algo === 'shuffle') continue; if (verbose) console.log(`\n📦 ${algo}:`); for (const preset of PRESETS) { for (const size of NORMAL_SIZES) { @@ -340,6 +341,44 @@ for (const algo of ALGORITHMS) { } } +// ── Shuffle-Tests (speziell: nicht sortiert, nur permutiert) ── +if (verbose) console.log('\n📦 shuffle:'); +for (const preset of PRESETS) { + for (const size of NORMAL_SIZES) { + totalTests++; + const arr = generatePresetArray(preset, size); + const sortedArr = arr.slice().sort((a, b) => a - b); + context.array = arr.slice(); + + try { + const steps = buildSteps('shuffle'); + const lastStep = steps[steps.length - 1]; + + if (lastStep.type !== 'done') { + throw new Error(`Letzter Step type '${lastStep.type}', erwartet 'done'`); + } + + if (lastStep.array.length !== arr.length) { + throw new Error(`Array-Länge geändert: ${lastStep.array.length} statt ${arr.length}`); + } + + const sortedResult = lastStep.array.slice().sort((a, b) => a - b); + if (!arraysEqual(sortedResult, sortedArr)) { + throw new Error(`Werte stimmen nicht überein nach Shuffle`); + } + + passed++; + if (verbose) { + console.log(` ✅ shuffle + ${preset} (${size} Elemente, ${steps.length} Steps)`); + } + } catch (e) { + failed++; + failures.push({ algo: 'shuffle', preset, size, error: e.message }); + console.log(` ❌ shuffle + ${preset} (${size} Elemente): ${e.message}`); + } + } +} + // Edge Cases if (verbose) console.log('\n📦 Edge Cases:');