From d0e1fc4c14fa1ecece8a12d858ef525c4129c64b Mon Sep 17 00:00:00 2001 From: Christoph Ruegg Date: Thu, 24 Apr 2014 22:33:11 +0200 Subject: [PATCH] Sorting: key-value and key-item1-item2 sub-range sorting; insertion sort on very small sets --- src/Numerics/Sorting.cs | 360 ++++++++++++++++++++++++++++------ src/UnitTests/SortingTests.cs | 62 +++--- 2 files changed, 341 insertions(+), 81 deletions(-) diff --git a/src/Numerics/Sorting.cs b/src/Numerics/Sorting.cs index b9a813b2..be9acaf2 100644 --- a/src/Numerics/Sorting.cs +++ b/src/Numerics/Sorting.cs @@ -38,18 +38,6 @@ namespace MathNet.Numerics /// public static class Sorting { - /// - /// Sort a range of a list of keys, in place using the quick sort algorithm. - /// - /// The type of elements in the key list. - /// List to sort. - /// The zero-based starting index of the range to sort. - /// The length of the range to sort. - public static void Sort(IList keys, int index, int count) - { - Sort(keys, index, count, Comparer.Default); - } - /// /// Sort a list of keys, in place using the quick sort algorithm using the quick sort algorithm. /// @@ -58,32 +46,40 @@ namespace MathNet.Numerics /// Comparison, defining the sort order. public static void Sort(IList keys, IComparer comparer = null) { - if (null == comparer) + int count = keys.Count; + if (count <= 1) { - comparer = Comparer.Default; + return; } - // basic cases - if (keys.Count <= 1) + if (null == comparer) { - return; + comparer = Comparer.Default; } - if (keys.Count == 2) + if (count == 2) { if (comparer.Compare(keys[0], keys[1]) > 0) { Swap(keys, 0, 1); } - return; } - // generic list case - var keysList = keys as List; - if (null != keysList) + // insertion sort + if (count <= 10) { - keysList.Sort(comparer); + for (int i = 1; i < count; i++) + { + var key = keys[i]; + int j = i - 1; + while (j >= 0 && comparer.Compare(keys[j], key) > 0) + { + keys[j + 1] = keys[j]; + j--; + } + keys[j + 1] = key; + } return; } @@ -95,8 +91,16 @@ namespace MathNet.Numerics return; } + // generic list case + var keysList = keys as List; + if (null != keysList) + { + keysList.Sort(comparer); + return; + } + // local sort implementation - QuickSort(keys, comparer, 0, keys.Count - 1); + QuickSort(keys, comparer, 0, count - 1); } /// @@ -109,11 +113,47 @@ namespace MathNet.Numerics /// Comparison, defining the sort order. public static void Sort(IList keys, IList items, IComparer comparer = null) { + int count = keys.Count; + if (count <= 1) + { + return; + } + if (null == comparer) { comparer = Comparer.Default; } + if (count == 2) + { + if (comparer.Compare(keys[0], keys[1]) > 0) + { + Swap(keys, 0, 1); + Swap(items, 0, 1); + } + return; + } + + // insertion sort + if (count <= 10) + { + for (int i = 1; i < count; i++) + { + var key = keys[i]; + var item = items[i]; + int j = i - 1; + while (j >= 0 && comparer.Compare(keys[j], key) > 0) + { + keys[j + 1] = keys[j]; + items[j + 1] = items[j]; + j--; + } + keys[j + 1] = key; + items[j + 1] = item; + } + return; + } + #if !PORTABLE // array case var keysArray = keys as TKey[]; @@ -126,7 +166,7 @@ namespace MathNet.Numerics #endif // local sort implementation - QuickSort(keys, items, comparer, 0, keys.Count - 1); + QuickSort(keys, items, comparer, 0, count - 1); } /// @@ -141,38 +181,53 @@ namespace MathNet.Numerics /// Comparison, defining the sort order. public static void Sort(IList keys, IList items1, IList items2, IComparer comparer = null) { + int count = keys.Count; + if (count <= 1) + { + return; + } + if (null == comparer) { comparer = Comparer.Default; } - // local sort implementation - QuickSort(keys, items1, items2, comparer, 0, keys.Count - 1); - } - - /// - /// Sort a list of keys and items with respect to the keys, in place using the quick sort algorithm. - /// - /// The type of elements in the primary list. - /// The type of elements in the secondary list. - /// List to sort. - /// List to to sort on duplicate primaty items, and permute the same way as the key list. - /// Comparison, defining the primary sort order. - /// Comparison, defining the secondary sort order. - public static void SortAll(IList primary, IList secondary, IComparer primaryComparer = null, IComparer secondaryComparer = null) - { - if (null == primaryComparer) + if (count == 2) { - primaryComparer = Comparer.Default; + if (comparer.Compare(keys[0], keys[1]) > 0) + { + Swap(keys, 0, 1); + Swap(items1, 0, 1); + Swap(items2, 0, 1); + } + return; } - if (null == secondaryComparer) + // insertion sort + if (count <= 10) { - secondaryComparer = Comparer.Default; + for (int i = 1; i < count; i++) + { + var key = keys[i]; + var item1 = items1[i]; + var item2 = items2[i]; + int j = i - 1; + while (j >= 0 && comparer.Compare(keys[j], key) > 0) + { + keys[j + 1] = keys[j]; + items1[j + 1] = items1[j]; + items2[j + 1] = items2[j]; + j--; + } + keys[j + 1] = key; + items1[j + 1] = item1; + items2[j + 1] = item2; + } + return; } // local sort implementation - QuickSortAll(primary, secondary, primaryComparer, secondaryComparer, 0, primary.Count - 1); + QuickSort(keys, items1, items2, comparer, 0, count - 1); } /// @@ -185,7 +240,7 @@ namespace MathNet.Numerics /// Comparison, defining the sort order. public static void Sort(IList keys, int index, int count, IComparer comparer = null) { - if (index < 0 || index >= keys.Count) + if (index < 0) { throw new ArgumentOutOfRangeException("index"); } @@ -195,15 +250,14 @@ namespace MathNet.Numerics throw new ArgumentOutOfRangeException("count"); } - if (null == comparer) + if (count <= 1) { - comparer = Comparer.Default; + return; } - // basic cases - if (count <= 1) + if (null == comparer) { - return; + comparer = Comparer.Default; } if (count == 2) @@ -212,7 +266,32 @@ namespace MathNet.Numerics { Swap(keys, index, index + 1); } + return; + } + // insertion sort + if (count <= 10) + { + int to = index + count; + for (int i = index + 1; i < to; i++) + { + var key = keys[i]; + int j = i - 1; + while (j >= index && comparer.Compare(keys[j], key) > 0) + { + keys[j + 1] = keys[j]; + j--; + } + keys[j + 1] = key; + } + return; + } + + // array case + var keysArray = keys as T[]; + if (null != keysArray) + { + Array.Sort(keysArray, index, count, comparer); return; } @@ -224,18 +303,187 @@ namespace MathNet.Numerics return; } + // fall back: local sort implementation + QuickSort(keys, comparer, index, count - 1); + } + + /// + /// Sort a list of keys and items with respect to the keys, in place using the quick sort algorithm. + /// + /// The type of elements in the key list. + /// The type of elements in the item list. + /// List to sort. + /// List to permute the same way as the key list. + /// The zero-based starting index of the range to sort. + /// The length of the range to sort. + /// Comparison, defining the sort order. + public static void Sort(IList keys, IList items, int index, int count, IComparer comparer = null) + { + if (index < 0) + { + throw new ArgumentOutOfRangeException("index"); + } + + if (count < 0 || index + count > keys.Count) + { + throw new ArgumentOutOfRangeException("count"); + } + + if (count <= 1) + { + return; + } + + if (null == comparer) + { + comparer = Comparer.Default; + } + + if (count == 2) + { + if (comparer.Compare(keys[index], keys[index + 1]) > 0) + { + Swap(keys, index, index + 1); + Swap(items, index, index + 1); + } + return; + } + + // insertion sort + if (count <= 10) + { + int to = index + count; + for (int i = index + 1; i < to; i++) + { + var key = keys[i]; + var item = items[i]; + int j = i - 1; + while (j >= index && comparer.Compare(keys[j], key) > 0) + { + keys[j + 1] = keys[j]; + items[j + 1] = items[j]; + j--; + } + keys[j + 1] = key; + items[j + 1] = item; + } + return; + } + +#if !PORTABLE // array case - var keysArray = keys as T[]; - if (null != keysArray) + var keysArray = keys as TKey[]; + var itemsArray = items as TItem[]; + if ((null != keysArray) && (null != itemsArray)) { - Array.Sort(keysArray, index, count, comparer); + Array.Sort(keysArray, itemsArray, index, count, comparer); return; } +#endif + + // fall back: local sort implementation + QuickSort(keys, items, comparer, index, count - 1); + } + + /// + /// Sort a list of keys, items1 and items2 with respect to the keys, in place using the quick sort algorithm. + /// + /// The type of elements in the key list. + /// The type of elements in the first item list. + /// The type of elements in the second item list. + /// List to sort. + /// First list to permute the same way as the key list. + /// Second list to permute the same way as the key list. + /// The zero-based starting index of the range to sort. + /// The length of the range to sort. + /// Comparison, defining the sort order. + public static void Sort(IList keys, IList items1, IList items2, int index, int count, IComparer comparer = null) + { + if (index < 0) + { + throw new ArgumentOutOfRangeException("index"); + } + + if (count < 0 || index + count > keys.Count) + { + throw new ArgumentOutOfRangeException("count"); + } + + if (count <= 1) + { + return; + } + + if (null == comparer) + { + comparer = Comparer.Default; + } + + if (count == 2) + { + if (comparer.Compare(keys[index], keys[index + 1]) > 0) + { + Swap(keys, index, index + 1); + Swap(items1, index, index + 1); + Swap(items2, index, index + 1); + } + return; + } + + // insertion sort + if (count <= 10) + { + int to = index + count; + for (int i = index + 1; i < to; i++) + { + var key = keys[i]; + var item1 = items1[i]; + var item2 = items2[i]; + int j = i - 1; + while (j >= index && comparer.Compare(keys[j], key) > 0) + { + keys[j + 1] = keys[j]; + items1[j + 1] = items1[j]; + items2[j + 1] = items2[j]; + j--; + } + keys[j + 1] = key; + items1[j + 1] = item1; + items2[j + 1] = item2; + } + return; + } + + // fall back: local sort implementation + QuickSort(keys, items1, items2, comparer, index, count - 1); + } + + /// + /// Sort a list of keys and items with respect to the keys, in place using the quick sort algorithm. + /// + /// The type of elements in the primary list. + /// The type of elements in the secondary list. + /// List to sort. + /// List to to sort on duplicate primaty items, and permute the same way as the key list. + /// Comparison, defining the primary sort order. + /// Comparison, defining the secondary sort order. + public static void SortAll(IList primary, IList secondary, IComparer primaryComparer = null, IComparer secondaryComparer = null) + { + if (null == primaryComparer) + { + primaryComparer = Comparer.Default; + } + + if (null == secondaryComparer) + { + secondaryComparer = Comparer.Default; + } // local sort implementation - QuickSort(keys, comparer, index, count - 1); + QuickSortAll(primary, secondary, primaryComparer, secondaryComparer, 0, primary.Count - 1); } + /// /// Recursive implementation for an in place quick sort on a list. /// @@ -616,7 +864,7 @@ namespace MathNet.Numerics /// The list in which the elements are stored. /// The index of the first element of the swap. /// The index of the second element of the swap. - internal static void Swap(IList keys, int a, int b) + static void Swap(IList keys, int a, int b) { if (a == b) { diff --git a/src/UnitTests/SortingTests.cs b/src/UnitTests/SortingTests.cs index e42c69cc..ec02434e 100644 --- a/src/UnitTests/SortingTests.cs +++ b/src/UnitTests/SortingTests.cs @@ -39,15 +39,18 @@ namespace MathNet.Numerics.UnitTests /// /// Test random tuple array sorting. /// - [Test] - public void TestRandomTupleArraySorting() + [TestCase(0)] + [TestCase(2)] + [TestCase(7)] + [TestCase(24)] + [TestCase(1024)] + public void TestRandomTupleArraySorting(int length) { - const int Len = 0x1 << 10; var random = new System.Random(0); - var keys = new int[Len]; - var items = new int[Len]; - var keysCopy = new int[Len]; + var keys = new int[length]; + var items = new int[length]; + var keysCopy = new int[length]; for (var i = 0; i < keys.Length; i++) { @@ -73,17 +76,20 @@ namespace MathNet.Numerics.UnitTests /// /// Test random tuple list sorting. /// - [Test] - public void TestRandomTupleListSorting() + [TestCase(0)] + [TestCase(2)] + [TestCase(7)] + [TestCase(24)] + [TestCase(1024)] + public void TestRandomTupleListSorting(int length) { - const int Len = 0x1 << 10; var random = new System.Random(0); - var keys = new List(Len); - var items = new List(Len); - var keysCopy = new int[Len]; + var keys = new List(length); + var items = new List(length); + var keysCopy = new int[length]; - for (var i = 0; i < Len; i++) + for (var i = 0; i < length; i++) { var value = random.Next(); keys.Add(value); @@ -93,7 +99,7 @@ namespace MathNet.Numerics.UnitTests Sorting.Sort(keys, items); - for (var i = 1; i < Len; i++) + for (var i = 1; i < length; i++) { Assert.IsTrue(keys[i] >= keys[i - 1], "Sort Order - " + i); Assert.AreEqual(items[i], -keys[i], "Items Permutation - " + i); @@ -108,16 +114,19 @@ namespace MathNet.Numerics.UnitTests /// /// Test random triple array sorting. /// - [Test] - public void TestRandomTripleArraySorting() + [TestCase(0)] + [TestCase(2)] + [TestCase(7)] + [TestCase(24)] + [TestCase(1024)] + public void TestRandomTripleArraySorting(int length) { - const int Len = 0x1 << 10; var random = new System.Random(0); - var keys = new int[Len]; - var items1 = new int[Len]; - var items2 = new int[Len]; - var keysCopy = new int[Len]; + var keys = new int[length]; + var items1 = new int[length]; + var items2 = new int[length]; + var keysCopy = new int[length]; for (var i = 0; i < keys.Length; i++) { @@ -145,15 +154,18 @@ namespace MathNet.Numerics.UnitTests /// /// Test applied list sorting. /// - [Test] - public void TestAppliedListSorting() + [TestCase(0)] + [TestCase(2)] + [TestCase(7)] + [TestCase(24)] + [TestCase(1024)] + public void TestAppliedListSorting(int length) { - const int Len = 0x1 << 10; var random = new System.Random(0); var list = new List(); - for (var i = 0; i < Len; i++) + for (var i = 0; i < length; i++) { list.Add(random.Next()); }