-
Speicher-Ansicht: Bei Tree Sort, Merge Sort, Timsort, Heap Sort, Counting Sort, Radix Sort, Bucket Sort und Bitonic Sort wird der Arbeitsspeicher unterhalb des Arrays visualisiert. Sie sehen genau, wo Daten zwischengespeichert werden — ob als Baumstruktur (BST, Heap), Hilfs-Arrays (Merge), Häufigkeitstabelle (Counting), Bucket-Verteilung (Radix, Bucket) oder Sortiernetz (Bitonic).
+
Speicher-Ansicht: Bei Tree Sort, Merge Sort, Timsort, Heap Sort, Counting Sort, Radix Sort und Bucket Sort wird der Arbeitsspeicher unterhalb des Arrays visualisiert. Sie sehen genau, wo Daten zwischengespeichert werden — ob als Baumstruktur (BST, Heap), Hilfs-Arrays (Merge), Häufigkeitstabelle (Counting) oder Bucket-Verteilung (Radix, Bucket).
@@ -657,26 +644,6 @@
-
-
Parallel — Netzwerk-Verfahren
-
-
-
-
Bitonic Sort
-
- Nicht stabil
- In-place
- Parallelisierbar
- O(n log²n)
-
-
Funktionsweise: Basiert auf dem Konzept der „bitonischen Sequenz" — einer Folge, die erst steigt und dann fällt (oder umgekehrt). Der Algorithmus arbeitet in Phasen: Zuerst werden Paare zu bitonischen Sequenzen der Länge 2 sortiert (abwechselnd aufsteigend/absteigend). Dann werden diese zu bitonischen Sequenzen der Länge 4 gemergt, dann 8, usw. Der „Bitonic Merge" vergleicht dabei Elemente mit festem Abstand und tauscht sie so, dass eine sortierte Sequenz entsteht. Das Vergleichsmuster ist datenunabhängig — es steht vor der Ausführung fest.
-
Darstellung: Im Haupt-Canvas verbinden orange Bögen die verglichenen Positionen — sie springen in einem streng regelmäßigen Muster durch das Array, das sich von den Daten völlig unbeeindruckt zeigt; rote Bögen zeigen Tausche. Unterhalb erscheint das vollständige Sortiernetz: n horizontale Drähte repräsentieren die Array-Positionen, senkrechte Komparatoren verbinden jeweils die verglichenen Positionen. Vergangene Phasen leuchten gedimmt blau, die aktuelle orange mit Leuchteffekt, künftige Phasen sind abgedimmt — so ist das vollständige, datenunabhängige Vergleichsprogramm auf einen Blick sichtbar.
-
Wann einsetzen: Der Algorithmus der Wahl für GPU-Sortierung und Hardware-Implementierungen (FPGAs, ASICs). Da das Vergleichsnetzwerk vollständig vorab feststeht und keine Datenabhängigkeiten hat, können alle Vergleiche einer Phase echt parallel ausgeführt werden. Auf einer GPU mit p Prozessoren erreicht Bitonic Sort O(n/p · log²n). Wird in CUDA-Bibliotheken, Grafik-Pipelines und überall dort eingesetzt, wo massiv parallele Hardware zur Verfügung steht. Die Einschränkung: Array-Größe muss eine Zweierpotenz sein (wird intern aufgefüllt).
-
-
-
-
-
O(n·k) — Nicht-vergleichsbasiert
@@ -824,10 +791,8 @@ const $btnPasteCustom = document.getElementById('btnPasteCustom');
const $customArrayCount = document.getElementById('customArrayCount');
const $sizeSlider = document.getElementById('sizeSlider');
const $speedSlider = document.getElementById('speedSlider');
-const $threadSlider = document.getElementById('threadSlider');
const $sizeVal = document.getElementById('sizeVal');
const $speedVal = document.getElementById('speedVal');
-const $threadVal = document.getElementById('threadVal');
const $statComp = document.getElementById('statCompares');
const $statSwap = document.getElementById('statSwaps');
const $statMove = document.getElementById('statMoves');
@@ -875,24 +840,10 @@ function updateSizeLabel() {
$sizeVal.textContent = $sizeSlider.value;
}
-// Parallele Algorithmen: Bitonic Sort
-const PARALLEL_ALGOS = new Set(['bitonic']);
-
+// ================================================================
// Algorithmen mit Memory-Visualisierung
+// ================================================================
const MEM_ALGOS = new Set(['tree', 'merge', 'timsort', 'heap', 'counting', 'radix', 'bucket']);
-const MAX_THREADS = navigator.hardwareConcurrency || 4;
-
-function updateThreadSlider() {
- const isParallel = PARALLEL_ALGOS.has($algoSelect.value);
- $threadSlider.disabled = !isParallel;
- if (isParallel) {
- $threadSlider.max = String(MAX_THREADS);
- } else {
- $threadSlider.max = '1';
- $threadSlider.value = '1';
- }
- $threadVal.textContent = $threadSlider.value;
-}
function setStats(compares, swaps, moves, step) {
$statComp.textContent = String(compares);
@@ -1760,97 +1711,6 @@ function buildSteps(algoName) {
break;
}
- case 'bitonic': {
- const n = arr.length;
- // Arbeite auf separatem Array mit Zweierpotenz-Länge
- let size = 1;
- while (size < n) size <<= 1;
- // Padding: eindeutige Werte oberhalb des Maximums (alle > origMax → Sortierung korrekt,
- // aber unterschiedlich → Balken visuell unterscheidbar wenn sie in sichtbare Positionen geraten)
- let origMax = 0;
- for (let i = 0; i < n; i++) { if (arr[i] > origMax) origMax = arr[i]; }
- const bArr = arr.slice();
- let padLo = origMax * 1.05 + 1;
- let padHi = origMax * 1.25 + 1;
- let padCount = size - n;
- for (let i = n; i < size; i++) {
- bArr.push(padCount > 1 ? padLo + (padHi - padLo) * (i - n) / (padCount - 1) : padLo);
- }
-
- // Vorberechnung des Sortiernetzes (datenunabhängig)
- const allStages = [];
- function collectMergeStages(lo, cnt) {
- if (cnt <= 1) return;
- let half = cnt >> 1;
- let stage = [];
- for (let i = lo; i < lo + half; i++) {
- if (i < n && (i + half) < n) stage.push([i, i + half]);
- }
- if (stage.length > 0) allStages.push(stage);
- collectMergeStages(lo, half);
- collectMergeStages(lo + half, half);
- }
- function collectSortStages(lo, cnt) {
- if (cnt <= 1) return;
- let half = cnt >> 1;
- collectSortStages(lo, half);
- collectSortStages(lo + half, half);
- collectMergeStages(lo, cnt);
- }
- collectSortStages(0, size);
-
- // Stage-Index: synchron mit bitonicMerge hochgezählt
- let snapStageId = 0;
-
- // Snap-Wrapper: zeigt nur die ersten n Elemente + Netz-Zustand
- function bSnap(type, indices) {
- let visIdx = indices.filter(function(idx) { return idx < n; });
- for (let k = 0; k < n; k++) arr[k] = bArr[k];
- out.push({
- type: type, indices: visIdx, array: arr.slice(),
- compares: compares, swaps: swaps, moves: moves,
- mem: { type: 'bitonic_network', stages: allStages, currentStage: snapStageId, n: n }
- });
- }
-
- function bitonicCompareSwap(i, j, dir) {
- if (i < n && j < n) {
- compares++;
- bSnap('compare', [i, j]);
- }
- if ((bArr[i] > bArr[j]) === dir) {
- let tmp = bArr[i]; bArr[i] = bArr[j]; bArr[j] = tmp;
- if (i < n && j < n) {
- swaps++;
- bSnap('swap', [i, j]);
- }
- }
- }
- function bitonicMerge(lo, cnt, dir) {
- if (cnt <= 1) return;
- let half = cnt >> 1;
- let hasVisible = false;
- for (let i = lo; i < lo + half; i++) {
- if (i < n && (i + half) < n) hasVisible = true;
- bitonicCompareSwap(i, i + half, dir);
- }
- if (hasVisible) snapStageId++; // nächste Phase
- bitonicMerge(lo, half, dir);
- bitonicMerge(lo + half, half, dir);
- }
- function bitonicSortRec(lo, cnt, dir) {
- if (cnt <= 1) return;
- let half = cnt >> 1;
- bitonicSortRec(lo, half, true);
- bitonicSortRec(lo + half, half, false);
- bitonicMerge(lo, cnt, dir);
- }
- bitonicSortRec(0, size, true);
- // Ergebnis zurückkopieren (nur die ersten n Elemente, Padding verwerfen)
- for (let i = 0; i < n; i++) arr[i] = bArr[i];
- break;
- }
-
case 'bucket': {
const n = arr.length;
if (n === 0) break;
@@ -2273,7 +2133,6 @@ function drawMemory(state) {
else if (state.type === 'counting') drawCountingArray(state, W, H);
else if (state.type === 'radix') drawRadixBuckets(state, W, H);
else if (state.type === 'buckets') drawBuckets(state, W, H);
- else if (state.type === 'bitonic_network') drawBitonicNetwork(state, W, H);
}
function drawBST(state, W, H) {
@@ -2539,67 +2398,6 @@ function drawBuckets(state, W, H) {
}
}
-function drawBitonicNetwork(state, W, H) {
- let stages = state.stages, cur = state.currentStage, n = state.n;
- let numS = stages.length;
- if (numS === 0 || n < 2) return;
-
- let PAD_L = 24, PAD_R = 8, PAD_T = 10, PAD_B = 10;
- let wireSpacing = (H - PAD_T - PAD_B) / Math.max(n - 1, 1);
- let stageW = (W - PAD_L - PAD_R) / numS;
-
- // Horizontale Drähte
- for (let w = 0; w < n; w++) {
- let wy = PAD_T + w * wireSpacing;
- memCtx.strokeStyle = 'rgba(100,120,160,0.3)';
- memCtx.lineWidth = 1;
- memCtx.beginPath();
- memCtx.moveTo(PAD_L, wy);
- memCtx.lineTo(W - PAD_R, wy);
- memCtx.stroke();
- }
-
- // Index-Labels links
- memCtx.fillStyle = 'rgba(140,160,190,0.5)';
- memCtx.font = '500 9px Inter, sans-serif';
- memCtx.textAlign = 'right';
- memCtx.textBaseline = 'middle';
- for (let w = 0; w < n; w++) {
- memCtx.fillText(String(w), PAD_L - 4, PAD_T + w * wireSpacing);
- }
-
- // Komparatoren
- for (let s = 0; s < numS; s++) {
- let cx = PAD_L + (s + 0.5) * stageW;
- let isCur = (s === cur);
- let isPast = (s < cur);
- let color = isCur ? COLORS.compare
- : isPast ? 'rgba(60,160,255,0.35)'
- : 'rgba(150,170,200,0.15)';
- let lw = isCur ? 2 : 1;
- let R = isCur ? 3 : 2;
-
- for (let ci = 0; ci < stages[s].length; ci++) {
- let a = stages[s][ci][0], b = stages[s][ci][1];
- let y1 = PAD_T + a * wireSpacing, y2 = PAD_T + b * wireSpacing;
- if (isCur) {
- memCtx.save();
- memCtx.shadowColor = COLORS.compare;
- memCtx.shadowBlur = 8;
- memCtx.strokeStyle = color; memCtx.lineWidth = lw;
- memCtx.beginPath(); memCtx.moveTo(cx, y1); memCtx.lineTo(cx, y2); memCtx.stroke();
- memCtx.restore();
- } else {
- memCtx.strokeStyle = color; memCtx.lineWidth = lw;
- memCtx.beginPath(); memCtx.moveTo(cx, y1); memCtx.lineTo(cx, y2); memCtx.stroke();
- }
- memCtx.fillStyle = color;
- memCtx.beginPath(); memCtx.arc(cx, y1, R, 0, Math.PI * 2); memCtx.fill();
- memCtx.beginPath(); memCtx.arc(cx, y2, R, 0, Math.PI * 2); memCtx.fill();
- }
- }
-}
-
// ================================================================
// Memory View: Label + Resize (Label nur bei Typwechsel, Resize immer)
// ================================================================
@@ -2627,8 +2425,6 @@ function updateMemView(memState) {
$memLabel.innerHTML = ' Arbeitsspeicher: Radix-Buckets (10 Ziffern)';
} else if (memState.type === 'buckets') {
$memLabel.innerHTML = ' Arbeitsspeicher: Bucket-Verteilung';
- } else if (memState.type === 'bitonic_network') {
- $memLabel.innerHTML = ' Sortiernetz: ' + memState.stages.length + ' Phasen \u00b7 ' + memState.n + ' Dr\u00e4hte';
}
lucide.createIcons({ nodes: [$memLabel] });
}
@@ -2639,7 +2435,6 @@ function updateMemView(memState) {
else if (memState.type === 'counting') resizeCanvas(memCanvas, memCtx, 0.25, 250, 160);
else if (memState.type === 'radix') resizeCanvas(memCanvas, memCtx, 0.3, 300, 160);
else if (memState.type === 'buckets') resizeCanvas(memCanvas, memCtx, 0.3, 300, 160);
- else if (memState.type === 'bitonic_network') resizeCanvas(memCanvas, memCtx, 0.35, 300, 140);
drawMemory(memState);
}
@@ -2682,14 +2477,6 @@ function scheduleNext() {
if (animTimer !== null) clearInterval(animTimer);
let delay = getDelay();
- // FIX: Thread-Slider hat nun einen funktionalen Effekt auf die Geschwindigkeit
- if (PARALLEL_ALGOS.has($algoSelect.value)) {
- const threads = parseInt($threadSlider.value, 10);
- if (threads > 1) {
- delay = Math.max(1, Math.round(delay / threads));
- }
- }
-
// Start timing on first tick
if (startTime === 0) startTime = performance.now();
animTimer = window.setInterval(tick, delay);
@@ -2836,16 +2623,10 @@ $sizeSlider.addEventListener('input', function() {
});
$algoSelect.addEventListener('change', function() {
- updateThreadSlider();
if (!isRunning) doReset();
writeUrlState();
});
-$threadSlider.addEventListener('input', function() {
- $threadVal.textContent = $threadSlider.value;
- if (isRunning && !isPaused) scheduleNext();
-});
-
$presetSelect.addEventListener('change', function() {
if (!isRunning) doReset();
writeUrlState();
@@ -2960,7 +2741,6 @@ function makeTouchSlider(slider) {
makeTouchSlider($sizeSlider);
makeTouchSlider($speedSlider);
-makeTouchSlider($threadSlider);
// ================================================================
// URL State Persistence
@@ -3024,7 +2804,6 @@ readUrlState();
refreshColors();
updateSpeedLabel();
updateSizeLabel();
-updateThreadSlider();
resizeCanvas();
generateArray();
arrayFresh = true;
diff --git a/test_algorithms.js b/test_algorithms.js
index 5f19e2c..5d49682 100644
--- a/test_algorithms.js
+++ b/test_algorithms.js
@@ -233,7 +233,6 @@ const ALGORITHMS = [
'bubble', 'selection', 'insertion', 'cocktail',
'merge', 'heap', 'quick', 'quick3way', 'dualpivot', 'introsort',
'shell', 'tree', 'timsort',
- 'bitonic',
'counting', 'radix', 'bucket',
'pancake', 'cycle', 'bogo',
];