diff --git a/csharp-algorithms/BucketSort/BucketSort/BucketSort.csproj b/csharp-algorithms/BucketSort/BucketSort/BucketSort.csproj index 79e7148fd0..62be4f867f 100644 --- a/csharp-algorithms/BucketSort/BucketSort/BucketSort.csproj +++ b/csharp-algorithms/BucketSort/BucketSort/BucketSort.csproj @@ -2,13 +2,13 @@ Exe - net6.0 + net10.0 enable enable - + diff --git a/csharp-algorithms/BucketSort/BucketSort/BucketSortBenchmarks.cs b/csharp-algorithms/BucketSort/BucketSort/BucketSortBenchmarks.cs new file mode 100644 index 0000000000..084c99ee35 --- /dev/null +++ b/csharp-algorithms/BucketSort/BucketSort/BucketSortBenchmarks.cs @@ -0,0 +1,50 @@ +using BenchmarkDotNet.Attributes; + +namespace BucketSort; + +public class BucketSortBenchmarks +{ + private readonly BucketSortMethods _sorter = new(); + private int[] _source = []; + private int[] _work = []; + + [Params(200, 2_000, 20_000)] + public int Size { get; set; } + + [Params("Random", "Sorted")] + public string Input { get; set; } = "Random"; + + [GlobalSetup] + public void Setup() + { + _source = Input == "Random" + ? BucketSortMethods.CreateRandomArray(Size) + : BucketSortMethods.CreateSortedArray(Size); + _work = new int[Size]; + } + + [Benchmark] + public int[] SortArray() + { + _source.CopyTo(_work, 0); + + return _sorter.SortArray(_work); + } + + [Benchmark] + public int[] SortArrayOptimized() + { + _source.CopyTo(_work, 0); + + return _sorter.SortArrayOptimized(_work); + } + + [Benchmark(Baseline = true)] + public int[] ArraySort() + { + _source.CopyTo(_work, 0); + Array.Sort(_work); + + return _work; + } +} diff --git a/csharp-algorithms/BucketSort/BucketSort/BucketSortMethods.cs b/csharp-algorithms/BucketSort/BucketSort/BucketSortMethods.cs index 6a2f5431e3..b1947d3b3c 100644 --- a/csharp-algorithms/BucketSort/BucketSort/BucketSortMethods.cs +++ b/csharp-algorithms/BucketSort/BucketSort/BucketSortMethods.cs @@ -1,155 +1,142 @@ -using BenchmarkDotNet.Attributes; +namespace BucketSort; -namespace BucketSort +public class BucketSortMethods { - public class BucketSortMethods + public int[] SortArray(int[] array) { - public IEnumerable SampleArrays() + if (array.Length <= 1) { - yield return new object[] { CreateRandomArray(200), "Small Unsorted" }; - yield return new object[] { CreateRandomArray(2000), "Medium Unsorted" }; - yield return new object[] { CreateRandomArray(20000), "Large Unsorted" }; - yield return new object[] { CreateSortedArray(200), "Small Sorted" }; - yield return new object[] { CreateSortedArray(2000), "Medium Sorted" }; - yield return new object[] { CreateSortedArray(20000), "Large Sorted" }; + return array; } - [Benchmark] - [ArgumentsSource(nameof(SampleArrays))] - public int[] SortArray(int[] array, string arrayName) - { - List sortedList = new List(); - var minValue = array[0]; - var maxValue = array[0]; - - if (array == null || array.Length <= 1) - { - return array; - } + List sortedList = []; + var minValue = array[0]; + var maxValue = array[0]; - for (int i = 1; i < array.Length; i++) - { - if (array[i] > maxValue) - maxValue = array[i]; - if (array[i] < minValue) - minValue = array[i]; - } + for (int i = 1; i < array.Length; i++) + { + if (array[i] > maxValue) + maxValue = array[i]; + if (array[i] < minValue) + minValue = array[i]; + } - var numberOfBuckets = maxValue - minValue + 1; - List[] bucket = new List[numberOfBuckets]; + var numberOfBuckets = array.Length; + var range = (long)maxValue - minValue + 1; + List[] bucket = new List[numberOfBuckets]; - for (int i = 0; i < numberOfBuckets; i++) - { - bucket[i] = new List(); - } - - for (int i = 0; i < array.Length; i++) - { - var selectedBucket = (array[i] / numberOfBuckets); - bucket[selectedBucket].Add(array[i]); - } + for (int i = 0; i < numberOfBuckets; i++) + { + bucket[i] = []; + } - for (int i = 0; i < numberOfBuckets; i++) - { - int[] temp = BubbleSort(bucket[i]); - sortedList.AddRange(temp); - } + for (int i = 0; i < array.Length; i++) + { + var selectedBucket = (int)((array[i] - (long)minValue) * numberOfBuckets / range); + bucket[selectedBucket].Add(array[i]); + } - return sortedList.ToArray(); + for (int i = 0; i < numberOfBuckets; i++) + { + int[] temp = BubbleSort(bucket[i]); + sortedList.AddRange(temp); } - public static int[] BubbleSort(List listInput) + return sortedList.ToArray(); + } + + public static int[] BubbleSort(List listInput) + { + for (int i = 0; i < listInput.Count - 1; i++) { - for (int i = 0; i < listInput.Count; i++) + for (int j = 0; j < listInput.Count - i - 1; j++) { - for (int j = 0; j < listInput.Count; j++) + if (listInput[j] > listInput[j + 1]) { - if (listInput[i] < listInput[j]) - { - var temp = listInput[i]; - listInput[i] = listInput[j]; - listInput[j] = temp; - } + var temp = listInput[j]; + listInput[j] = listInput[j + 1]; + listInput[j + 1] = temp; } } - return listInput.ToArray(); } + return listInput.ToArray(); + } - [Benchmark] - [ArgumentsSource(nameof(SampleArrays))] - public int[] SortArrayOptimized(int[] array, string arrayName) + public int[] SortArrayOptimized(int[] array) + { + if (array.Length <= 1) { - if (array == null || array.Length <= 1) - { - return array; - } + return array; + } - int maxValue = array[0]; - int minValue = array[0]; + int maxValue = array[0]; + int minValue = array[0]; - for (int i = 1; i < array.Length; i++) + for (int i = 1; i < array.Length; i++) + { + if (array[i] > maxValue) { - if (array[i] > maxValue) - { - maxValue = array[i]; - } + maxValue = array[i]; + } - if (array[i] < minValue) - { - minValue = array[i]; - } + if (array[i] < minValue) + { + minValue = array[i]; } + } - LinkedList[] bucket = new LinkedList[maxValue - minValue + 1]; + var range = (long)maxValue - minValue + 1; + LinkedList[] bucket = new LinkedList[range]; - for (int i = 0; i < array.Length; i++) + for (int i = 0; i < array.Length; i++) + { + if (bucket[array[i] - minValue] == null) { - if (bucket[array[i] - minValue] == null) - { - bucket[array[i] - minValue] = new LinkedList(); - } - - bucket[array[i] - minValue].AddLast(array[i]); + bucket[array[i] - minValue] = new LinkedList(); } - var index = 0; - for (int i = 0; i < bucket.Length; i++) + bucket[array[i] - minValue].AddLast(array[i]); + } + + var index = 0; + + for (int i = 0; i < bucket.Length; i++) + { + if (bucket[i] != null) { - if (bucket[i] != null) + LinkedListNode? node = bucket[i].First; + + while (node != null) { - LinkedListNode node = bucket[i].First; - - while (node != null) - { - array[index] = node.Value; - node = node.Next; - index++; - } + array[index] = node.Value; + node = node.Next; + index++; } } - return array; } - public static int[] CreateRandomArray(int size) - { - var array = new int[size]; - var rand = new Random(); + return array; + } - for (int i = 0; i < size; i++) - array[i] = rand.Next(1, size); + public static int[] CreateRandomArray(int size) + { + var array = new int[size]; + var rand = new Random(); - return array; - } + for (int i = 0; i < size; i++) + array[i] = rand.Next(1, size); - public static int[] CreateSortedArray(int size) - { - var array = new int[size]; + return array; + } - for (int i = 0; i < size; i++) - array[i] = i; + public static int[] CreateSortedArray(int size) + { + var array = new int[size]; - return array; - } + for (int i = 0; i < size; i++) + array[i] = i; + + return array; } } diff --git a/csharp-algorithms/BucketSort/BucketSort/Program.cs b/csharp-algorithms/BucketSort/BucketSort/Program.cs index f391cb0d28..2d4e85098f 100644 --- a/csharp-algorithms/BucketSort/BucketSort/Program.cs +++ b/csharp-algorithms/BucketSort/BucketSort/Program.cs @@ -1,4 +1,4 @@ using BenchmarkDotNet.Running; using BucketSort; -var summary = BenchmarkRunner.Run(); +BenchmarkRunner.Run(args: args); diff --git a/csharp-algorithms/BucketSort/BucketSortTests/BucketSortTests.csproj b/csharp-algorithms/BucketSort/BucketSortTests/BucketSortTests.csproj index c6051695fe..e7c59ebfb8 100644 --- a/csharp-algorithms/BucketSort/BucketSortTests/BucketSortTests.csproj +++ b/csharp-algorithms/BucketSort/BucketSortTests/BucketSortTests.csproj @@ -1,17 +1,22 @@ - net6.0 + net10.0 + enable enable false - - - - + + + + + + + + diff --git a/csharp-algorithms/BucketSort/BucketSortTests/BucketSortUnitTests.cs b/csharp-algorithms/BucketSort/BucketSortTests/BucketSortUnitTests.cs index ac27e198eb..4bc0f32d46 100644 --- a/csharp-algorithms/BucketSort/BucketSortTests/BucketSortUnitTests.cs +++ b/csharp-algorithms/BucketSort/BucketSortTests/BucketSortUnitTests.cs @@ -1,58 +1,110 @@ using BucketSort; -using Microsoft.VisualStudio.TestTools.UnitTesting; -namespace BucketSortTests +namespace BucketSortTests; + +[TestClass] +public class BucketSortUnitTests { - [TestClass] - public class BucketSortUnitTests + [TestMethod] + public void GivenUnsortedArray_WhenArrayIsNotEmpty_ThenReturnSortedArray() + { + var expected = new int[] { 1, 20, 49, 57, 73, 99, 133 }; + var sortFunction = new BucketSortMethods(); + + var sortNormal = sortFunction.SortArray([73, 57, 49, 99, 133, 20, 1]); + var sortOptimized = sortFunction.SortArrayOptimized([73, 57, 49, 99, 133, 20, 1]); + + CollectionAssert.AreEqual(expected, sortNormal); + CollectionAssert.AreEqual(expected, sortOptimized); + } + + [TestMethod] + public void GivenUnsortedArray_WhenSortedWithSortArray_ThenReturnSortedArray() + { + var array = new int[] { 73, 57, 49, 99, 133, 20, 1 }; + var expected = new int[] { 1, 20, 49, 57, 73, 99, 133 }; + var sortFunction = new BucketSortMethods(); + + var sortedArray = sortFunction.SortArray(array); + + Assert.IsNotNull(sortedArray); + CollectionAssert.AreEqual(expected, sortedArray); + } + + [TestMethod] + public void GivenUnsortedArray_WhenArrayIsRandomized_ThenCheckInstanceType() + { + var sortFunction = new BucketSortMethods(); + var array = BucketSortMethods.CreateRandomArray(200); + + var sortedArray = sortFunction.SortArrayOptimized(array); + + Assert.IsInstanceOfType(sortedArray, typeof(int[])); + } + + [TestMethod] + public void GivenUnsortedArray_WhenArrayIsSorted_ThenResultingArrayIsNotNull() { - [TestMethod] - public void GivenUnsortedArray_WhenArrayIsNotEmpty_ThenReturnSortedArray() - { - var array = new int[] { 73, 57, 49, 99, 133, 20, 1 }; - var expected = new int[] { 1, 20, 49, 57, 73, 99, 133 }; - var sortFunction = new BucketSortMethods(); + var sortFunction = new BucketSortMethods(); + var array = BucketSortMethods.CreateRandomArray(200); - var sortOptimized = sortFunction.SortArrayOptimized(array, string.Empty); - var sortNormal = sortFunction.SortArray(array, string.Empty); + var sortedArray = sortFunction.SortArrayOptimized(array); - CollectionAssert.AreEqual(expected, sortOptimized); - CollectionAssert.AreEqual(sortNormal, expected); - } + Assert.IsNotNull(sortedArray); + } - [TestMethod] - public void GivenUnsortedArray_WhenArrayIsRandomized_ThenCheckInstanceType() - { - var sortFunction = new BucketSortMethods(); ; - var array = BucketSortMethods.CreateRandomArray(200); + [TestMethod] + public void GivenArray_WhenArrayHasSingleElement_ThenReturnSortedArray() + { + var array = new int[] { 73 }; + var expected = new int[] { 73 }; + var sortFunction = new BucketSortMethods(); - var sortedArray = sortFunction.SortArrayOptimized(array, string.Empty); + var sortedArray = sortFunction.SortArrayOptimized(array); - Assert.IsInstanceOfType(sortedArray, typeof(int[])); - } + Assert.IsNotNull(sortedArray); + CollectionAssert.AreEqual(expected, sortedArray); + } - [TestMethod] - public void GivenUnsortedArray_WhenArrayIsSorted_ThenResultingArrayIsNotNull() - { - var sortFunction = new BucketSortMethods(); - var array = BucketSortMethods.CreateRandomArray(200); + [TestMethod] + [DataRow(new int[0])] + [DataRow(new[] { 100, 101 })] + [DataRow(new[] { 1000, 1001, 1002 })] + [DataRow(new[] { -50, -10, 3, -50 })] + [DataRow(new[] { 5, 5, 5, 5 })] + [DataRow(new[] { 42, 6, 16, 4, 12, 24, 46, 17 })] + public void GivenEdgeCaseArray_WhenSorted_ThenBothVersionsMatchArraySort(int[] input) + { + var expected = (int[])input.Clone(); + Array.Sort(expected); + var sortFunction = new BucketSortMethods(); - var sortedArray = sortFunction.SortArrayOptimized(array, string.Empty); + CollectionAssert.AreEqual(expected, sortFunction.SortArray((int[])input.Clone())); + CollectionAssert.AreEqual(expected, sortFunction.SortArrayOptimized((int[])input.Clone())); + } - Assert.IsNotNull(sortedArray); - } + [TestMethod] + public void GivenFullIntRange_WhenSorted_ThenSortArrayWorksAndOptimizedThrows() + { + var sortFunction = new BucketSortMethods(); - [TestMethod] - public void GivenArray_WhenArrayHasSingleElement_ThenReturnSortedArray() - { - var array = new int[] { 73 }; - var expected = new int[] { 73 }; - var sortFunction = new BucketSortMethods(); + CollectionAssert.AreEqual( + new[] { int.MinValue, 0, int.MaxValue }, + sortFunction.SortArray([int.MaxValue, int.MinValue, 0])); + Assert.ThrowsExactly( + () => sortFunction.SortArrayOptimized([int.MaxValue, int.MinValue, 0])); + } - var sortedArray = sortFunction.SortArrayOptimized(array, string.Empty); + [TestMethod] + public void GivenRandomArray_WhenSortedWithBothVersions_ThenResultsMatchArraySort() + { + var random = new Random(42); + var input = Enumerable.Range(0, 1_000).Select(_ => random.Next(-500, 500)).ToArray(); + var expected = (int[])input.Clone(); + Array.Sort(expected); + var sortFunction = new BucketSortMethods(); - Assert.IsNotNull(sortedArray); - CollectionAssert.AreEqual(sortedArray, expected); - } + CollectionAssert.AreEqual(expected, sortFunction.SortArray((int[])input.Clone())); + CollectionAssert.AreEqual(expected, sortFunction.SortArrayOptimized((int[])input.Clone())); } -} \ No newline at end of file +}