Browse Source

Sorting: key-value and key-item1-item2 sub-range sorting; insertion sort on very small sets

provider
Christoph Ruegg 13 years ago
parent
commit
d0e1fc4c14
  1. 360
      src/Numerics/Sorting.cs
  2. 62
      src/UnitTests/SortingTests.cs

360
src/Numerics/Sorting.cs

@ -38,18 +38,6 @@ namespace MathNet.Numerics
/// </summary> /// </summary>
public static class Sorting public static class Sorting
{ {
/// <summary>
/// Sort a range of a list of keys, in place using the quick sort algorithm.
/// </summary>
/// <typeparam name="T">The type of elements in the key list.</typeparam>
/// <param name="keys">List to sort.</param>
/// <param name="index">The zero-based starting index of the range to sort.</param>
/// <param name="count">The length of the range to sort.</param>
public static void Sort<T>(IList<T> keys, int index, int count)
{
Sort(keys, index, count, Comparer<T>.Default);
}
/// <summary> /// <summary>
/// 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 quick sort algorithm using the quick sort algorithm.
/// </summary> /// </summary>
@ -58,32 +46,40 @@ namespace MathNet.Numerics
/// <param name="comparer">Comparison, defining the sort order.</param> /// <param name="comparer">Comparison, defining the sort order.</param>
public static void Sort<T>(IList<T> keys, IComparer<T> comparer = null) public static void Sort<T>(IList<T> keys, IComparer<T> comparer = null)
{ {
if (null == comparer) int count = keys.Count;
if (count <= 1)
{ {
comparer = Comparer<T>.Default; return;
} }
// basic cases if (null == comparer)
if (keys.Count <= 1)
{ {
return; comparer = Comparer<T>.Default;
} }
if (keys.Count == 2) if (count == 2)
{ {
if (comparer.Compare(keys[0], keys[1]) > 0) if (comparer.Compare(keys[0], keys[1]) > 0)
{ {
Swap(keys, 0, 1); Swap(keys, 0, 1);
} }
return; return;
} }
// generic list case // insertion sort
var keysList = keys as List<T>; if (count <= 10)
if (null != keysList)
{ {
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; return;
} }
@ -95,8 +91,16 @@ namespace MathNet.Numerics
return; return;
} }
// generic list case
var keysList = keys as List<T>;
if (null != keysList)
{
keysList.Sort(comparer);
return;
}
// local sort implementation // local sort implementation
QuickSort(keys, comparer, 0, keys.Count - 1); QuickSort(keys, comparer, 0, count - 1);
} }
/// <summary> /// <summary>
@ -109,11 +113,47 @@ namespace MathNet.Numerics
/// <param name="comparer">Comparison, defining the sort order.</param> /// <param name="comparer">Comparison, defining the sort order.</param>
public static void Sort<TKey, TItem>(IList<TKey> keys, IList<TItem> items, IComparer<TKey> comparer = null) public static void Sort<TKey, TItem>(IList<TKey> keys, IList<TItem> items, IComparer<TKey> comparer = null)
{ {
int count = keys.Count;
if (count <= 1)
{
return;
}
if (null == comparer) if (null == comparer)
{ {
comparer = Comparer<TKey>.Default; comparer = Comparer<TKey>.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 #if !PORTABLE
// array case // array case
var keysArray = keys as TKey[]; var keysArray = keys as TKey[];
@ -126,7 +166,7 @@ namespace MathNet.Numerics
#endif #endif
// local sort implementation // local sort implementation
QuickSort(keys, items, comparer, 0, keys.Count - 1); QuickSort(keys, items, comparer, 0, count - 1);
} }
/// <summary> /// <summary>
@ -141,38 +181,53 @@ namespace MathNet.Numerics
/// <param name="comparer">Comparison, defining the sort order.</param> /// <param name="comparer">Comparison, defining the sort order.</param>
public static void Sort<TKey, TItem1, TItem2>(IList<TKey> keys, IList<TItem1> items1, IList<TItem2> items2, IComparer<TKey> comparer = null) public static void Sort<TKey, TItem1, TItem2>(IList<TKey> keys, IList<TItem1> items1, IList<TItem2> items2, IComparer<TKey> comparer = null)
{ {
int count = keys.Count;
if (count <= 1)
{
return;
}
if (null == comparer) if (null == comparer)
{ {
comparer = Comparer<TKey>.Default; comparer = Comparer<TKey>.Default;
} }
// local sort implementation if (count == 2)
QuickSort(keys, items1, items2, comparer, 0, keys.Count - 1);
}
/// <summary>
/// Sort a list of keys and items with respect to the keys, in place using the quick sort algorithm.
/// </summary>
/// <typeparam name="T1">The type of elements in the primary list.</typeparam>
/// <typeparam name="T2">The type of elements in the secondary list.</typeparam>
/// <param name="primary">List to sort.</param>
/// <param name="secondary">List to to sort on duplicate primaty items, and permute the same way as the key list.</param>
/// <param name="primaryComparer">Comparison, defining the primary sort order.</param>
/// <param name="secondaryComparer">Comparison, defining the secondary sort order.</param>
public static void SortAll<T1, T2>(IList<T1> primary, IList<T2> secondary, IComparer<T1> primaryComparer = null, IComparer<T2> secondaryComparer = null)
{
if (null == primaryComparer)
{ {
primaryComparer = Comparer<T1>.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<T2>.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 // local sort implementation
QuickSortAll(primary, secondary, primaryComparer, secondaryComparer, 0, primary.Count - 1); QuickSort(keys, items1, items2, comparer, 0, count - 1);
} }
/// <summary> /// <summary>
@ -185,7 +240,7 @@ namespace MathNet.Numerics
/// <param name="comparer">Comparison, defining the sort order.</param> /// <param name="comparer">Comparison, defining the sort order.</param>
public static void Sort<T>(IList<T> keys, int index, int count, IComparer<T> comparer = null) public static void Sort<T>(IList<T> keys, int index, int count, IComparer<T> comparer = null)
{ {
if (index < 0 || index >= keys.Count) if (index < 0)
{ {
throw new ArgumentOutOfRangeException("index"); throw new ArgumentOutOfRangeException("index");
} }
@ -195,15 +250,14 @@ namespace MathNet.Numerics
throw new ArgumentOutOfRangeException("count"); throw new ArgumentOutOfRangeException("count");
} }
if (null == comparer) if (count <= 1)
{ {
comparer = Comparer<T>.Default; return;
} }
// basic cases if (null == comparer)
if (count <= 1)
{ {
return; comparer = Comparer<T>.Default;
} }
if (count == 2) if (count == 2)
@ -212,7 +266,32 @@ namespace MathNet.Numerics
{ {
Swap(keys, index, index + 1); 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; return;
} }
@ -224,18 +303,187 @@ namespace MathNet.Numerics
return; return;
} }
// fall back: local sort implementation
QuickSort(keys, comparer, index, count - 1);
}
/// <summary>
/// Sort a list of keys and items with respect to the keys, in place using the quick sort algorithm.
/// </summary>
/// <typeparam name="TKey">The type of elements in the key list.</typeparam>
/// <typeparam name="TItem">The type of elements in the item list.</typeparam>
/// <param name="keys">List to sort.</param>
/// <param name="items">List to permute the same way as the key list.</param>
/// <param name="index">The zero-based starting index of the range to sort.</param>
/// <param name="count">The length of the range to sort.</param>
/// <param name="comparer">Comparison, defining the sort order.</param>
public static void Sort<TKey, TItem>(IList<TKey> keys, IList<TItem> items, int index, int count, IComparer<TKey> 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<TKey>.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 // array case
var keysArray = keys as T[]; var keysArray = keys as TKey[];
if (null != keysArray) var itemsArray = items as TItem[];
if ((null != keysArray) && (null != itemsArray))
{ {
Array.Sort(keysArray, index, count, comparer); Array.Sort(keysArray, itemsArray, index, count, comparer);
return; return;
} }
#endif
// fall back: local sort implementation
QuickSort(keys, items, comparer, index, count - 1);
}
/// <summary>
/// Sort a list of keys, items1 and items2 with respect to the keys, in place using the quick sort algorithm.
/// </summary>
/// <typeparam name="TKey">The type of elements in the key list.</typeparam>
/// <typeparam name="TItem1">The type of elements in the first item list.</typeparam>
/// <typeparam name="TItem2">The type of elements in the second item list.</typeparam>
/// <param name="keys">List to sort.</param>
/// <param name="items1">First list to permute the same way as the key list.</param>
/// <param name="items2">Second list to permute the same way as the key list.</param>
/// <param name="index">The zero-based starting index of the range to sort.</param>
/// <param name="count">The length of the range to sort.</param>
/// <param name="comparer">Comparison, defining the sort order.</param>
public static void Sort<TKey, TItem1, TItem2>(IList<TKey> keys, IList<TItem1> items1, IList<TItem2> items2, int index, int count, IComparer<TKey> 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<TKey>.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);
}
/// <summary>
/// Sort a list of keys and items with respect to the keys, in place using the quick sort algorithm.
/// </summary>
/// <typeparam name="T1">The type of elements in the primary list.</typeparam>
/// <typeparam name="T2">The type of elements in the secondary list.</typeparam>
/// <param name="primary">List to sort.</param>
/// <param name="secondary">List to to sort on duplicate primaty items, and permute the same way as the key list.</param>
/// <param name="primaryComparer">Comparison, defining the primary sort order.</param>
/// <param name="secondaryComparer">Comparison, defining the secondary sort order.</param>
public static void SortAll<T1, T2>(IList<T1> primary, IList<T2> secondary, IComparer<T1> primaryComparer = null, IComparer<T2> secondaryComparer = null)
{
if (null == primaryComparer)
{
primaryComparer = Comparer<T1>.Default;
}
if (null == secondaryComparer)
{
secondaryComparer = Comparer<T2>.Default;
}
// local sort implementation // local sort implementation
QuickSort(keys, comparer, index, count - 1); QuickSortAll(primary, secondary, primaryComparer, secondaryComparer, 0, primary.Count - 1);
} }
/// <summary> /// <summary>
/// Recursive implementation for an in place quick sort on a list. /// Recursive implementation for an in place quick sort on a list.
/// </summary> /// </summary>
@ -616,7 +864,7 @@ namespace MathNet.Numerics
/// <param name="keys">The list in which the elements are stored.</param> /// <param name="keys">The list in which the elements are stored.</param>
/// <param name="a">The index of the first element of the swap.</param> /// <param name="a">The index of the first element of the swap.</param>
/// <param name="b">The index of the second element of the swap.</param> /// <param name="b">The index of the second element of the swap.</param>
internal static void Swap<T>(IList<T> keys, int a, int b) static void Swap<T>(IList<T> keys, int a, int b)
{ {
if (a == b) if (a == b)
{ {

62
src/UnitTests/SortingTests.cs

@ -39,15 +39,18 @@ namespace MathNet.Numerics.UnitTests
/// <summary> /// <summary>
/// Test random tuple array sorting. /// Test random tuple array sorting.
/// </summary> /// </summary>
[Test] [TestCase(0)]
public void TestRandomTupleArraySorting() [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 random = new System.Random(0);
var keys = new int[Len]; var keys = new int[length];
var items = new int[Len]; var items = new int[length];
var keysCopy = new int[Len]; var keysCopy = new int[length];
for (var i = 0; i < keys.Length; i++) for (var i = 0; i < keys.Length; i++)
{ {
@ -73,17 +76,20 @@ namespace MathNet.Numerics.UnitTests
/// <summary> /// <summary>
/// Test random tuple list sorting. /// Test random tuple list sorting.
/// </summary> /// </summary>
[Test] [TestCase(0)]
public void TestRandomTupleListSorting() [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 random = new System.Random(0);
var keys = new List<int>(Len); var keys = new List<int>(length);
var items = new List<int>(Len); var items = new List<int>(length);
var keysCopy = new int[Len]; var keysCopy = new int[length];
for (var i = 0; i < Len; i++) for (var i = 0; i < length; i++)
{ {
var value = random.Next(); var value = random.Next();
keys.Add(value); keys.Add(value);
@ -93,7 +99,7 @@ namespace MathNet.Numerics.UnitTests
Sorting.Sort(keys, items); 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.IsTrue(keys[i] >= keys[i - 1], "Sort Order - " + i);
Assert.AreEqual(items[i], -keys[i], "Items Permutation - " + i); Assert.AreEqual(items[i], -keys[i], "Items Permutation - " + i);
@ -108,16 +114,19 @@ namespace MathNet.Numerics.UnitTests
/// <summary> /// <summary>
/// Test random triple array sorting. /// Test random triple array sorting.
/// </summary> /// </summary>
[Test] [TestCase(0)]
public void TestRandomTripleArraySorting() [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 random = new System.Random(0);
var keys = new int[Len]; var keys = new int[length];
var items1 = new int[Len]; var items1 = new int[length];
var items2 = new int[Len]; var items2 = new int[length];
var keysCopy = new int[Len]; var keysCopy = new int[length];
for (var i = 0; i < keys.Length; i++) for (var i = 0; i < keys.Length; i++)
{ {
@ -145,15 +154,18 @@ namespace MathNet.Numerics.UnitTests
/// <summary> /// <summary>
/// Test applied list sorting. /// Test applied list sorting.
/// </summary> /// </summary>
[Test] [TestCase(0)]
public void TestAppliedListSorting() [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 random = new System.Random(0);
var list = new List<int>(); var list = new List<int>();
for (var i = 0; i < Len; i++) for (var i = 0; i < length; i++)
{ {
list.Add(random.Next()); list.Add(random.Next());
} }

Loading…
Cancel
Save