diff --git a/src/Numerics.Tests/SortingTests.cs b/src/Numerics.Tests/SortingTests.cs
index c60c1136..4e8f4995 100644
--- a/src/Numerics.Tests/SortingTests.cs
+++ b/src/Numerics.Tests/SortingTests.cs
@@ -175,7 +175,8 @@ namespace MathNet.Numerics.UnitTests
}
// default sorting (Ascending)
- list.Sort();
+ //list.Sort();
+ Sorting.Sort(list);
// just check that the order is as expected, not that the items are correct
for (var i = 1; i < list.Count; i++)
diff --git a/src/Numerics/Sorting.cs b/src/Numerics/Sorting.cs
index 13475410..03e68e14 100644
--- a/src/Numerics/Sorting.cs
+++ b/src/Numerics/Sorting.cs
@@ -38,7 +38,7 @@ namespace MathNet.Numerics
public static class Sorting
{
///
- /// Sort a list of keys, in place using the quick sort algorithm using the quick sort algorithm.
+ /// Sort a list of keys, in place using the introsort algorithm.
///
/// The type of elements in the key list.
/// List to sort.
@@ -46,42 +46,12 @@ namespace MathNet.Numerics
public static void Sort(IList keys, 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);
- }
- return;
- }
-
- // insertion sort
- if (count <= 10)
- {
- 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;
- }
-
// array case
if (keys is T[] keysArray)
{
@@ -97,11 +67,11 @@ namespace MathNet.Numerics
}
// local sort implementation
- QuickSort(keys, comparer, 0, count - 1);
+ IntroSort(keys, comparer, 0, count - 1, (int a, int b) => Swap(keys, a, b));
}
///
- /// Sort a list of keys and items with respect to the keys, in place using the quick sort algorithm.
+ /// Sort a list of keys and items with respect to the keys, in place using the introsort algorithm.
///
/// The type of elements in the key list.
/// The type of elements in the item list.
@@ -111,46 +81,12 @@ namespace MathNet.Numerics
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;
- }
-
// array case
if (keys is TKey[] keysArray && items is TItem[] itemsArray)
{
@@ -159,11 +95,16 @@ namespace MathNet.Numerics
}
// local sort implementation
- QuickSort(keys, items, comparer, 0, count - 1);
+ Action swap = (int a, int b) =>
+ {
+ Swap(keys, a, b);
+ Swap(items, a, b);
+ };
+ IntroSort(keys, comparer, 0, count - 1, swap);
}
///
- /// Sort a list of keys, items1 and items2 with respect to the keys, in place using the quick sort algorithm.
+ /// Sort a list of keys, items1 and items2 with respect to the keys, in place using the introsort algorithm.
///
/// The type of elements in the key list.
/// The type of elements in the first item list.
@@ -175,52 +116,20 @@ namespace MathNet.Numerics
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;
}
- if (count == 2)
- {
- if (comparer.Compare(keys[0], keys[1]) > 0)
- {
- Swap(keys, 0, 1);
- Swap(items1, 0, 1);
- Swap(items2, 0, 1);
- }
- return;
- }
-
- // insertion sort
- if (count <= 10)
- {
- 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
- QuickSort(keys, items1, items2, comparer, 0, count - 1);
+ Action swap = (int a, int b) =>
+ {
+ Swap(keys, a, b);
+ Swap(items1, a, b);
+ Swap(items2, a, b);
+ };
+ IntroSort(keys, comparer, 0, count - 1, swap);
}
///
@@ -470,6 +379,130 @@ namespace MathNet.Numerics
QuickSortAll(primary, secondary, primaryComparer, secondaryComparer, 0, primary.Count - 1);
}
+ ///
+ /// Recursively reorders the given list to satisfy the max heap property.
+ ///
+ /// The type of elements in the key list.
+ /// The list which is turned into a heap.
+ /// The method with which to compare two elements of the heap.
+ /// The index of the heap to heapify.
+ /// The left boundary of the heapify.
+ /// The right boundary of the heapify.
+ /// An Action which takes the given indexes and swaps the key and satellite data.
+ static void MaxHeapify(IList keys, IComparer comparer, int i, int left, int right, Action swapper)
+ {
+ i -= left;
+ int leftChild = 2 * i + 1;
+ int rightChild = 2 * i + 2;
+
+ int largest = i;
+
+ if (leftChild + left >= left && leftChild + left <= right && comparer.Compare(keys[leftChild + left], keys[largest + left]) > 0)
+ {
+ largest = leftChild;
+ }
+
+ if (rightChild + left >= left && rightChild + left <= right && comparer.Compare(keys[rightChild + left], keys[largest + left]) > 0)
+ {
+ largest = rightChild;
+ }
+
+ if (largest != i)
+ {
+ swapper(largest + left, i + left);
+ MaxHeapify(keys, comparer, largest + left, left, right, swapper);
+ }
+ }
+
+ ///
+ ///
+ ///
+ /// The type of elements in the key list.
+ /// The list which is turned into a heap.
+ /// The method with which to compare two elements of the heap.
+ /// The left boundary of the heap.
+ /// The right boundary of the heap.
+ /// An Action which takes the given indexes and swaps the key and satellite data.
+
+ static void BuildMaxHeap(IList keys, IComparer comparer, int left, int right, Action swapper)
+ {
+ for (int i = (right - left) / 2; i >= 0; i--)
+ {
+ MaxHeapify(keys, comparer, i + left, left, right, swapper);
+ }
+ }
+
+ ///
+ /// Recursive implementation for an in place introspective sort on a list.
+ ///
+ /// The type of elements in the key list.
+ /// The list which is sorted using intro sort.
+ /// The method with which to compare two elements of the intro sort.
+ /// The left boundary of the intro sort.
+ /// The right boundary of the intro sort.
+ /// An Action which takes the given indexes and swaps the key and satellite data.
+ /// Tracks the number of recursions entered.
+ static void IntroSort(IList keys, IComparer comparer, int left, int right, Action swapper, int recursions = 0)
+ {
+ const double ln2 = 0.69314718056; // Natural Logarithm of 2
+ if (left >= right)
+ {
+ return;
+ }
+
+ Random.CryptoRandomSource rand = new MathNet.Numerics.Random.CryptoRandomSource();
+ double max_recursion_depth = 2 * Math.Log(keys.Count) / ln2; // This is the cap on recursion depth used by the GNU STL
+
+ if (right - left < 16) // Insertion Sort is faster on very small sequences, 16 is the number that Array.Sort uses
+ {
+ for (int i = left + 1; i <= right; i++)
+ {
+ int j = i;
+ while (j > 0 && comparer.Compare(keys[j - 1], keys[j]) > 0)
+ {
+ swapper(j, j - 1);
+ j--;
+ }
+ }
+ }
+ else if (recursions > max_recursion_depth) // Heapsort is guaranteed O(n log n)
+ {
+ BuildMaxHeap(keys, comparer, left, right, swapper);
+
+ int heapBoundary = right;
+
+ for (int i = right - left; i > 0; i--)
+ {
+ swapper(left, left + i);
+ MaxHeapify(keys, comparer, left, left, --heapBoundary, swapper);
+ }
+ }
+ else // Quicksort
+ {
+ int pivot_index = (rand.Next() % (right - left)) + left; // Don't need to worry about negatives because rand.Next() returns a non-negative number
+ swapper(pivot_index, right);
+
+ TKey pivot = keys[right];
+
+ int i = left - 1;
+ for (int j = left; j < right; j++)
+ {
+ if (comparer.Compare(keys[j], pivot) <= 0)
+ {
+ i++;
+ swapper(i, j);
+ }
+ }
+
+ i++;
+ swapper(i, right);
+
+ recursions++;
+ IntroSort(keys, comparer, left, i - 1, swapper, recursions);
+ IntroSort(keys, comparer, i + 1, right, swapper, recursions);
+ }
+
+ }
///
/// Recursive implementation for an in place quick sort on a list.