1
0
This commit is contained in:
Debug_pro
2026-06-07 18:14:39 +03:00
commit b74b40209b
24 changed files with 2528 additions and 0 deletions

View File

@@ -0,0 +1,31 @@
using Lab9.Models;
using Lab9.Services;
namespace Lab9.Strategies;
public class CommunistSortStrategy : SortStrategyBase
{
public override string Name => "Коммунистическая сортировка";
protected override void ExecuteCore(int[] array, SortMetrics metrics, StepLogger? logger)
{
if (array.Length == 0)
{
return;
}
var average = (int)(array.Sum(x => (long)x) / array.Length);
logger?.Line($"Среднее значение массива: {average}.");
logger?.Line("Последовательно приводим элементы массива к общему значению.");
logger?.BlankLine();
for (var i = 0; i < array.Length; i++)
{
var oldValue = array[i];
logger?.Line($"Уравниваем индекс {i}: {oldValue} -> {average}.");
Assign(array, i, average, metrics);
logger?.ArrayState(array, i);
}
}
}

View File

@@ -0,0 +1,10 @@
using Lab9.Models;
namespace Lab9.Strategies;
public interface ISortStrategy
{
string Name { get; }
SortResult Sort(int[] source, bool ascending, bool enableLogging);
}

View File

@@ -0,0 +1,94 @@
using Lab9.Models;
using Lab9.Services;
namespace Lab9.Strategies;
public class MsdRadixSortStrategy : SortStrategyBase
{
private const int AlphabetSize = 256;
private const int Cutoff = 16;
public override string Name => "MSD Radix Sort";
protected override void ExecuteCore(int[] array, SortMetrics metrics, StepLogger? logger)
{
if (array.Length <= 1)
{
return;
}
var aux = new int[array.Length];
Sort(array, 0, array.Length - 1, 3, aux, metrics, logger);
}
private static void Sort(
int[] array,
int left,
int right,
int byteIndex,
int[] aux,
SortMetrics metrics,
StepLogger? logger)
{
if (right <= left || byteIndex < 0)
{
return;
}
if (right - left <= Cutoff)
{
logger?.Line($"MSD: маленький диапазон [{left}; {right}], переключаемся на сортировку вставками.");
InsertionSortRange(array, left, right, metrics, logger, "MSD/Insertion");
logger?.BlankLine();
return;
}
logger?.Line($"MSD: обрабатываем диапазон [{left}; {right}], байт №{byteIndex}.");
var count = new int[AlphabetSize + 2];
for (var i = left; i <= right; i++)
{
var digit = GetByte(array[i], byteIndex);
count[digit + 2]++;
}
for (var r = 0; r < AlphabetSize + 1; r++)
{
count[r + 1] += count[r];
}
var starts = count.ToArray();
for (var i = left; i <= right; i++)
{
var digit = GetByte(array[i], byteIndex);
aux[count[digit + 1]++] = array[i];
metrics.AddMove();
}
for (var i = left; i <= right; i++)
{
Assign(array, i, aux[i - left], metrics);
}
logger?.ArrayState(array);
logger?.BlankLine();
for (var r = 0; r < AlphabetSize; r++)
{
var start = left + starts[r];
var end = left + starts[r + 1] - 1;
if (start < end)
{
Sort(array, start, end, byteIndex - 1, aux, metrics, logger);
}
}
}
private static int GetByte(int value, int byteIndex)
{
var normalized = unchecked((uint)(value ^ int.MinValue));
return (int)((normalized >> (byteIndex * 8)) & 0xFF);
}
}

View File

@@ -0,0 +1,117 @@
using Lab9.Models;
using Lab9.Services;
using System.Diagnostics;
namespace Lab9.Strategies;
public abstract class SortStrategyBase : ISortStrategy
{
public abstract string Name { get; }
public SortResult Sort(int[] source, bool ascending, bool enableLogging)
{
var array = source.ToArray();
var metrics = new SortMetrics();
var logger = enableLogging ? new StepLogger() : null;
logger?.Header($"Алгоритм: {Name}");
logger?.Line($"Режим: {(ascending ? "По возрастанию" : "По убыванию")}");
logger?.Line("Исходное состояние массива:");
logger?.ArrayState(array);
logger?.BlankLine();
var stopwatch = Stopwatch.StartNew();
ExecuteCore(array, metrics, logger);
if (!ascending && array.Length > 1)
{
logger?.Line("Дополнительный этап: разворот массива для режима по убыванию.");
Reverse(array, 0, array.Length - 1, metrics, logger);
logger?.ArrayState(array);
logger?.BlankLine();
}
stopwatch.Stop();
logger?.Line("Финальный результат:");
logger?.ArrayState(array);
return new SortResult
{
AlgorithmName = Name,
SortedArray = array,
Metrics = metrics,
Elapsed = stopwatch.Elapsed,
Log = logger?.ToString() ?? string.Empty
};
}
protected abstract void ExecuteCore(int[] array, SortMetrics metrics, StepLogger? logger);
protected static int Compare(int left, int right, SortMetrics metrics)
{
metrics.AddComparison();
return left.CompareTo(right);
}
protected static void Assign(int[] array, int index, int value, SortMetrics metrics)
{
array[index] = value;
metrics.AddMove();
}
protected static void Swap(int[] array, int left, int right, SortMetrics metrics)
{
if (left == right)
{
return;
}
(array[left], array[right]) = (array[right], array[left]);
metrics.AddMove(3);
}
protected static void Reverse(int[] array, int left, int right, SortMetrics metrics, StepLogger? logger)
{
while (left < right)
{
logger?.Line($"Меняем местами {array[left]} и {array[right]}.");
Swap(array, left, right, metrics);
logger?.ArrayState(array, left, right);
left++;
right--;
}
}
protected static void InsertionSortRange(
int[] array,
int left,
int right,
SortMetrics metrics,
StepLogger? logger,
string prefix)
{
for (var i = left + 1; i <= right; i++)
{
var current = array[i];
var j = i - 1;
logger?.Line($"{prefix}: берём элемент {current}.");
while (j >= left && Compare(array[j], current, metrics) > 0)
{
logger?.Line($"Сравниваем {array[j]} и {current}. Сдвигаем {array[j]} вправо.");
Assign(array, j + 1, array[j], metrics);
logger?.ArrayState(array, j, j + 1);
j--;
}
logger?.Line($"Ставим элемент {current} на место.");
Assign(array, j + 1, current, metrics);
logger?.ArrayState(array, j + 1);
}
}
}

View File

@@ -0,0 +1,758 @@
using Lab9.Models;
using Lab9.Services;
namespace Lab9.Strategies;
public class TimSortStrategy : SortStrategyBase
{
private const int MinMerge = 32;
private const int InitialMinGallop = 7;
private int[] _array = Array.Empty<int>();
private int[] _temp = Array.Empty<int>();
private int _minGallop;
private readonly List<RunInfo> _runs = new();
private SortMetrics? _metrics;
private StepLogger? _logger;
private struct RunInfo
{
public int Base;
public int Length;
public RunInfo(int runBase, int runLength)
{
Base = runBase;
Length = runLength;
}
}
public override string Name => "TimSort";
protected override void ExecuteCore(int[] array, SortMetrics metrics, StepLogger? logger)
{
if (array.Length <= 1)
{
return;
}
_array = array;
_metrics = metrics;
_logger = logger;
_minGallop = InitialMinGallop;
_runs.Clear();
_temp = Array.Empty<int>();
var minRun = MinRunLength(array.Length);
logger?.Line($"TimSort: вычисленный minrun = {minRun}.");
logger?.BlankLine();
var lo = 0;
var remaining = array.Length;
while (remaining > 0)
{
var runLength = CountRunAndMakeAscending(lo, array.Length);
logger?.Line($"Найдена серия [{lo}; {lo + runLength - 1}] длиной {runLength}.");
if (runLength < minRun)
{
var forcedLength = remaining <= minRun ? remaining : minRun;
logger?.Line($"Увеличиваем серию до {forcedLength} бинарными вставками.");
BinaryInsertionSort(lo, lo + forcedLength, lo + runLength);
runLength = forcedLength;
logger?.ArrayState(_array);
}
PushRun(lo, runLength);
MergeCollapse();
lo += runLength;
remaining -= runLength;
logger?.BlankLine();
}
MergeForceCollapse();
}
private static int MinRunLength(int n)
{
var r = 0;
while (n >= MinMerge)
{
r |= n & 1;
n >>= 1;
}
return n + r;
}
private int CountRunAndMakeAscending(int lo, int hi)
{
var metrics = _metrics!;
var logger = _logger;
var runHi = lo + 1;
if (runHi == hi)
{
return 1;
}
if (Compare(_array[runHi], _array[lo], metrics) < 0)
{
runHi++;
while (runHi < hi && Compare(_array[runHi], _array[runHi - 1], metrics) < 0)
{
runHi++;
}
logger?.Line($"Серия [{lo}; {runHi - 1}] убывающая. Разворачиваем.");
Reverse(_array, lo, runHi - 1, metrics, logger);
}
else
{
runHi++;
while (runHi < hi && Compare(_array[runHi], _array[runHi - 1], metrics) >= 0)
{
runHi++;
}
}
return runHi - lo;
}
private void BinaryInsertionSort(int lo, int hi, int start)
{
var metrics = _metrics!;
var logger = _logger;
if (start == lo)
{
start++;
}
for (var i = start; i < hi; i++)
{
var pivot = _array[i];
var left = lo;
var right = i;
while (left < right)
{
var mid = left + ((right - left) >> 1);
if (Compare(pivot, _array[mid], metrics) < 0)
{
right = mid;
}
else
{
left = mid + 1;
}
}
logger?.Line($"Вставляем элемент {pivot} на позицию {left}.");
for (var j = i; j > left; j--)
{
Assign(_array, j, _array[j - 1], metrics);
}
Assign(_array, left, pivot, metrics);
logger?.ArrayState(_array, left);
}
}
private void PushRun(int runBase, int runLength)
{
_runs.Add(new RunInfo(runBase, runLength));
}
private void MergeCollapse()
{
while (_runs.Count > 1)
{
var n = _runs.Count - 2;
if (n > 0 && _runs[n - 1].Length <= _runs[n].Length + _runs[n + 1].Length)
{
if (_runs[n - 1].Length < _runs[n + 1].Length)
{
n--;
}
MergeAt(n);
}
else if (_runs[n].Length <= _runs[n + 1].Length)
{
MergeAt(n);
}
else
{
break;
}
}
}
private void MergeForceCollapse()
{
while (_runs.Count > 1)
{
var n = _runs.Count - 2;
if (n > 0 && _runs[n - 1].Length < _runs[n + 1].Length)
{
n--;
}
MergeAt(n);
}
}
private void MergeAt(int index)
{
var metrics = _metrics!;
var logger = _logger;
var base1 = _runs[index].Base;
var len1 = _runs[index].Length;
var base2 = _runs[index + 1].Base;
var len2 = _runs[index + 1].Length;
logger?.Line(
$"Сливаем серии [{base1}; {base1 + len1 - 1}] и [{base2}; {base2 + len2 - 1}].");
_runs[index] = new RunInfo(base1, len1 + len2);
_runs.RemoveAt(index + 1);
var k = GallopRight(_array[base2], _array, base1, len1, 0, metrics);
base1 += k;
len1 -= k;
if (len1 == 0)
{
return;
}
len2 = GallopLeft(_array[base1 + len1 - 1], _array, base2, len2, len2 - 1, metrics);
if (len2 == 0)
{
return;
}
if (len1 <= len2)
{
MergeLo(base1, len1, base2, len2);
}
else
{
MergeHi(base1, len1, base2, len2);
}
logger?.ArrayState(_array);
}
private void MergeLo(int base1, int len1, int base2, int len2)
{
var metrics = _metrics!;
EnsureCapacity(len1);
Array.Copy(_array, base1, _temp, 0, len1);
metrics.AddMove(len1);
var cursor1 = 0;
var cursor2 = base2;
var dest = base1;
Assign(_array, dest++, _array[cursor2++], metrics);
len2--;
if (len2 == 0)
{
Array.Copy(_temp, cursor1, _array, dest, len1);
metrics.AddMove(len1);
return;
}
if (len1 == 1)
{
Array.Copy(_array, cursor2, _array, dest, len2);
metrics.AddMove(len2);
Assign(_array, dest + len2, _temp[cursor1], metrics);
return;
}
var minGallop = _minGallop;
while (true)
{
var count1 = 0;
var count2 = 0;
do
{
if (Compare(_array[cursor2], _temp[cursor1], metrics) < 0)
{
Assign(_array, dest++, _array[cursor2++], metrics);
count2++;
count1 = 0;
len2--;
if (len2 == 0)
{
Array.Copy(_temp, cursor1, _array, dest, len1);
metrics.AddMove(len1);
_minGallop = Math.Max(minGallop, 1);
return;
}
}
else
{
Assign(_array, dest++, _temp[cursor1++], metrics);
count1++;
count2 = 0;
len1--;
if (len1 == 1)
{
Array.Copy(_array, cursor2, _array, dest, len2);
metrics.AddMove(len2);
Assign(_array, dest + len2, _temp[cursor1], metrics);
_minGallop = Math.Max(minGallop, 1);
return;
}
}
}
while ((count1 | count2) < minGallop);
do
{
count1 = GallopRight(_array[cursor2], _temp, cursor1, len1, 0, metrics);
if (count1 != 0)
{
Array.Copy(_temp, cursor1, _array, dest, count1);
metrics.AddMove(count1);
dest += count1;
cursor1 += count1;
len1 -= count1;
if (len1 <= 1)
{
break;
}
}
Assign(_array, dest++, _array[cursor2++], metrics);
len2--;
if (len2 == 0)
{
Array.Copy(_temp, cursor1, _array, dest, len1);
metrics.AddMove(len1);
_minGallop = Math.Max(minGallop, 1);
return;
}
count2 = GallopLeft(_temp[cursor1], _array, cursor2, len2, 0, metrics);
if (count2 != 0)
{
Array.Copy(_array, cursor2, _array, dest, count2);
metrics.AddMove(count2);
dest += count2;
cursor2 += count2;
len2 -= count2;
if (len2 == 0)
{
Array.Copy(_temp, cursor1, _array, dest, len1);
metrics.AddMove(len1);
_minGallop = Math.Max(minGallop, 1);
return;
}
}
Assign(_array, dest++, _temp[cursor1++], metrics);
len1--;
if (len1 == 1)
{
break;
}
minGallop--;
}
while (count1 >= InitialMinGallop || count2 >= InitialMinGallop);
if (minGallop < 1)
{
minGallop = 1;
}
minGallop += 2;
if (len1 <= 1 || len2 == 0)
{
break;
}
}
_minGallop = Math.Max(minGallop, 1);
if (len1 == 1)
{
Array.Copy(_array, cursor2, _array, dest, len2);
metrics.AddMove(len2);
Assign(_array, dest + len2, _temp[cursor1], metrics);
}
else if (len1 > 1)
{
Array.Copy(_temp, cursor1, _array, dest, len1);
metrics.AddMove(len1);
}
}
private void MergeHi(int base1, int len1, int base2, int len2)
{
var metrics = _metrics!;
EnsureCapacity(len2);
Array.Copy(_array, base2, _temp, 0, len2);
metrics.AddMove(len2);
var cursor1 = base1 + len1 - 1;
var cursor2 = len2 - 1;
var dest = base2 + len2 - 1;
Assign(_array, dest--, _array[cursor1--], metrics);
len1--;
if (len1 == 0)
{
Array.Copy(_temp, 0, _array, dest - (len2 - 1), len2);
metrics.AddMove(len2);
return;
}
if (len2 == 1)
{
dest -= len1;
cursor1 -= len1;
Array.Copy(_array, cursor1 + 1, _array, dest + 1, len1);
metrics.AddMove(len1);
Assign(_array, dest, _temp[cursor2], metrics);
return;
}
var minGallop = _minGallop;
while (true)
{
var count1 = 0;
var count2 = 0;
do
{
if (Compare(_temp[cursor2], _array[cursor1], metrics) < 0)
{
Assign(_array, dest--, _array[cursor1--], metrics);
count1++;
count2 = 0;
len1--;
if (len1 == 0)
{
Array.Copy(_temp, 0, _array, dest - (len2 - 1), len2);
metrics.AddMove(len2);
_minGallop = Math.Max(minGallop, 1);
return;
}
}
else
{
Assign(_array, dest--, _temp[cursor2--], metrics);
count2++;
count1 = 0;
len2--;
if (len2 == 1)
{
dest -= len1;
cursor1 -= len1;
Array.Copy(_array, cursor1 + 1, _array, dest + 1, len1);
metrics.AddMove(len1);
Assign(_array, dest, _temp[cursor2], metrics);
_minGallop = Math.Max(minGallop, 1);
return;
}
}
}
while ((count1 | count2) < minGallop);
do
{
count1 = len1 - GallopRight(_temp[cursor2], _array, base1, len1, len1 - 1, metrics);
if (count1 != 0)
{
dest -= count1;
cursor1 -= count1;
len1 -= count1;
Array.Copy(_array, cursor1 + 1, _array, dest + 1, count1);
metrics.AddMove(count1);
if (len1 == 0)
{
Array.Copy(_temp, 0, _array, dest - (len2 - 1), len2);
metrics.AddMove(len2);
_minGallop = Math.Max(minGallop, 1);
return;
}
}
Assign(_array, dest--, _temp[cursor2--], metrics);
len2--;
if (len2 == 1)
{
break;
}
count2 = len2 - GallopLeft(_array[cursor1], _temp, 0, len2, len2 - 1, metrics);
if (count2 != 0)
{
dest -= count2;
cursor2 -= count2;
len2 -= count2;
Array.Copy(_temp, cursor2 + 1, _array, dest + 1, count2);
metrics.AddMove(count2);
if (len2 <= 1)
{
break;
}
}
Assign(_array, dest--, _array[cursor1--], metrics);
len1--;
if (len1 == 0)
{
Array.Copy(_temp, 0, _array, dest - (len2 - 1), len2);
metrics.AddMove(len2);
_minGallop = Math.Max(minGallop, 1);
return;
}
minGallop--;
}
while (count1 >= InitialMinGallop || count2 >= InitialMinGallop);
if (minGallop < 1)
{
minGallop = 1;
}
minGallop += 2;
if (len1 == 0 || len2 <= 1)
{
break;
}
}
_minGallop = Math.Max(minGallop, 1);
if (len2 == 1)
{
dest -= len1;
cursor1 -= len1;
Array.Copy(_array, cursor1 + 1, _array, dest + 1, len1);
metrics.AddMove(len1);
Assign(_array, dest, _temp[cursor2], metrics);
}
else if (len2 > 1)
{
Array.Copy(_temp, 0, _array, dest - (len2 - 1), len2);
metrics.AddMove(len2);
}
}
private void EnsureCapacity(int minCapacity)
{
if (_temp.Length >= minCapacity)
{
return;
}
_temp = new int[minCapacity];
}
private static int GallopLeft(
int key,
int[] array,
int baseIndex,
int length,
int hint,
SortMetrics metrics)
{
var lastOffset = 0;
var offset = 1;
if (Compare(key, array[baseIndex + hint], metrics) > 0)
{
var maxOffset = length - hint;
while (offset < maxOffset && Compare(key, array[baseIndex + hint + offset], metrics) > 0)
{
lastOffset = offset;
offset = (offset << 1) + 1;
if (offset <= 0)
{
offset = maxOffset;
}
}
if (offset > maxOffset)
{
offset = maxOffset;
}
lastOffset += hint;
offset += hint;
}
else
{
var maxOffset = hint + 1;
while (offset < maxOffset && Compare(key, array[baseIndex + hint - offset], metrics) <= 0)
{
lastOffset = offset;
offset = (offset << 1) + 1;
if (offset <= 0)
{
offset = maxOffset;
}
}
if (offset > maxOffset)
{
offset = maxOffset;
}
var temp = lastOffset;
lastOffset = hint - offset;
offset = hint - temp;
}
lastOffset++;
while (lastOffset < offset)
{
var middle = lastOffset + ((offset - lastOffset) >> 1);
if (Compare(key, array[baseIndex + middle], metrics) > 0)
{
lastOffset = middle + 1;
}
else
{
offset = middle;
}
}
return offset;
}
private static int GallopRight(
int key,
int[] array,
int baseIndex,
int length,
int hint,
SortMetrics metrics)
{
var offset = 1;
var lastOffset = 0;
if (Compare(key, array[baseIndex + hint], metrics) < 0)
{
var maxOffset = hint + 1;
while (offset < maxOffset && Compare(key, array[baseIndex + hint - offset], metrics) < 0)
{
lastOffset = offset;
offset = (offset << 1) + 1;
if (offset <= 0)
{
offset = maxOffset;
}
}
if (offset > maxOffset)
{
offset = maxOffset;
}
var temp = lastOffset;
lastOffset = hint - offset;
offset = hint - temp;
}
else
{
var maxOffset = length - hint;
while (offset < maxOffset && Compare(key, array[baseIndex + hint + offset], metrics) >= 0)
{
lastOffset = offset;
offset = (offset << 1) + 1;
if (offset <= 0)
{
offset = maxOffset;
}
}
if (offset > maxOffset)
{
offset = maxOffset;
}
lastOffset += hint;
offset += hint;
}
lastOffset++;
while (lastOffset < offset)
{
var middle = lastOffset + ((offset - lastOffset) >> 1);
if (Compare(key, array[baseIndex + middle], metrics) < 0)
{
offset = middle;
}
else
{
lastOffset = middle + 1;
}
}
return offset;
}
}