diff --git a/src/Numerics/Combinatorics.cs b/src/Numerics/Combinatorics.cs
index 0f60a66f0..ca643a793 100644
--- a/src/Numerics/Combinatorics.cs
+++ b/src/Numerics/Combinatorics.cs
@@ -135,15 +135,27 @@ public static double Permutations(int n)
public static int[] GeneratePermutation(int n, System.Random randomSource = null)
{
if (n < 0) throw new ArgumentOutOfRangeException(nameof(n), "Value must not be negative (zero is ok).");
+ return Enumerable.Range(0, n).SelectPermutation(randomSource).ToArray();
+ }
- int[] indices = new int[n];
- for (int i = 0; i < indices.Length; i++)
+ ///
+ /// Select a random permutation, without repetition, from a data array by reordering the provided array in-place.
+ /// Implemented using Fisher-Yates Shuffling. The provided data array will be modified.
+ ///
+ /// The data array to be reordered. The array will be modified by this routine.
+ /// Elements before this index won't be reordered
+ /// Elements after this index won't be reordered
+ /// The random number generator to use. Optional; the default random source will be used if null.
+ public static void SelectPermutationInplace(this IList data, int startIndex, int endIndex, System.Random randomSource = null)
+ {
+ var random = randomSource ?? SystemRandomSource.Default;
+
+ // Fisher-Yates Shuffling
+ for (int i = endIndex; i > startIndex; i--)
{
- indices[i] = i;
+ int swapIndex = random.Next(startIndex, i + 1);
+ (data[i], data[swapIndex]) = (data[swapIndex], data[i]);
}
-
- SelectPermutationInplace(indices, randomSource);
- return indices;
}
///
@@ -152,7 +164,18 @@ public static int[] GeneratePermutation(int n, System.Random randomSource = null
///
/// The data array to be reordered. The array will be modified by this routine.
/// The random number generator to use. Optional; the default random source will be used if null.
- public static void SelectPermutationInplace(T[] data, System.Random randomSource = null)
+ public static void SelectPermutationInplace(this IList data, System.Random randomSource = null)
+ {
+ data.SelectPermutationInplace(0, data.Count - 1, randomSource);
+ }
+#if NETCOREAPP2_1_OR_GREATER
+ ///
+ /// Select a random permutation, without repetition, from a data span by reordering the provided span in-place.
+ /// Implemented using Fisher-Yates Shuffling. The provided data span will be modified.
+ ///
+ /// The data span to be reordered. The span will be modified by this routine.
+ /// The random number generator to use. Optional; the default random source will be used if null.
+ public static void SelectPermutationInplace(this Span data, System.Random randomSource = null)
{
var random = randomSource ?? SystemRandomSource.Default;
@@ -163,26 +186,57 @@ public static void SelectPermutationInplace(T[] data, System.Random randomSou
(data[i], data[swapIndex]) = (data[swapIndex], data[i]);
}
}
+#endif
+ ///
+ /// Select a random permutation from a data sequence by returning the provided data in random order.
+ /// Implemented using Fisher-Yates Shuffling. The provided data array will be modified but not copied.
+ ///
+ /// The data elements to be reordered.
+ /// Elements before this index won't be reordered
+ /// Elements after this index won't be reordered
+ /// The random number generator to use. Optional; the default random source will be used if null.
+ public static IEnumerable SelectPermutationWithoutCopy(this IList data, int startIndex, int endIndex, System.Random randomSource = null)
+ {
+ var random = randomSource ?? SystemRandomSource.Default;
+ // Fisher-Yates Shuffling
+ for (int i = endIndex; i >= startIndex; i--)
+ {
+ int k = random.Next(startIndex, i + 1);
+ yield return data[k];
+ data[k] = data[i];
+ }
+ }
+#if NETCOREAPP2_1_OR_GREATER
///
/// Select a random permutation from a data sequence by returning the provided data in random order.
- /// Implemented using Fisher-Yates Shuffling.
+ /// Implemented using Fisher-Yates Shuffling. The provided data memory will be modified but not copied.
///
/// The data elements to be reordered.
/// The random number generator to use. Optional; the default random source will be used if null.
- public static IEnumerable SelectPermutation(this IEnumerable data, System.Random randomSource = null)
+ public static IEnumerable SelectPermutationWithoutCopy(this Memory data, System.Random randomSource = null)
{
var random = randomSource ?? SystemRandomSource.Default;
- T[] array = data.ToArray();
// Fisher-Yates Shuffling
- for (int i = array.Length - 1; i >= 0; i--)
+ for (int i = data.Length - 1; i >= 0; i--)
{
int k = random.Next(i + 1);
- yield return array[k];
- array[k] = array[i];
+ yield return data.Span[k];
+ data.Span[k] = data.Span[i];
}
}
+#endif
+ ///
+ /// Select a random permutation from a data sequence by returning the provided data in random order.
+ /// Implemented using Fisher-Yates Shuffling. The provided data array will be copied but not modified.
+ ///
+ /// The data elements to be reordered.
+ /// The random number generator to use. Optional; the default random source will be used if null.
+ public static IEnumerable SelectPermutation(this IEnumerable data, System.Random randomSource = null)
+ {
+ return data.ToArray().SelectPermutationWithoutCopy(0, data.Count() - 1, randomSource);
+ }
///
/// Generate a random combination, without repetition, by randomly selecting some of N elements.
@@ -257,12 +311,12 @@ public static bool[] GenerateCombination(int n, int k, System.Random randomSourc
/// The chosen combination, in the original order.
public static IEnumerable SelectCombination(this IEnumerable data, int elementsToChoose, System.Random randomSource = null)
{
- T[] array = data as T[] ?? data.ToArray();
+ IReadOnlyList array = data as IReadOnlyList ?? data.ToArray();
if (elementsToChoose < 0) throw new ArgumentOutOfRangeException(nameof(elementsToChoose), "Value must not be negative (zero is ok).");
- if (elementsToChoose > array.Length) throw new ArgumentOutOfRangeException(nameof(elementsToChoose), $"elementsToChoose must be smaller than or equal to data.Count.");
+ if (elementsToChoose > array.Count) throw new ArgumentOutOfRangeException(nameof(elementsToChoose), $"elementsToChoose must be smaller than or equal to data.Count.");
- bool[] mask = GenerateCombination(array.Length, elementsToChoose, randomSource);
+ bool[] mask = GenerateCombination(array.Count, elementsToChoose, randomSource);
for (int i = 0; i < mask.Length; i++)
{
@@ -273,6 +327,97 @@ public static IEnumerable SelectCombination(this IEnumerable data, int
}
}
+ ///
+ /// Select a random combination, without repetition, from a data sequence by selecting k elements in original order.
+ ///
+ /// The data source to choose from.
+ /// Elements before this index won't be chosen.
+ /// Number of elements in the data segment to be chosen from.
+ /// Number of elements (k) to choose from the data set. Each element is chosen at most once.
+ /// The random number generator to use. Optional; the default random source will be used if null.
+ /// The chosen combination, in the original order.
+ public static IEnumerable SelectCombination(this IReadOnlyList data, int startIndex, int segmentLength, int elementsToChoose, System.Random randomSource = null)
+ {
+ if (elementsToChoose < 0) throw new ArgumentOutOfRangeException(nameof(elementsToChoose), "Value must not be negative (zero is ok).");
+ if (elementsToChoose > segmentLength) throw new ArgumentOutOfRangeException(nameof(elementsToChoose), $"elementsToChoose must be smaller than or equal to segmentLength.");
+
+ bool[] mask = GenerateCombination(segmentLength, elementsToChoose, randomSource);
+
+ for (int i = 0; i < mask.Length; i++)
+ {
+ if (mask[i])
+ {
+ yield return data[startIndex + i];
+ }
+ }
+ }
+#if NETCOREAPP2_1_OR_GREATER
+ ///
+ /// Select a random combination, without repetition, from a data sequence by selecting k elements in original order.
+ ///
+ /// The data source to choose from.
+ /// Number of elements (k) to choose from the data set. Each element is chosen at most once.
+ /// The random number generator to use. Optional; the default random source will be used if null.
+ /// The chosen combination, in the original order.
+ public static IEnumerable SelectCombination(this ReadOnlyMemory data, int elementsToChoose, System.Random randomSource = null)
+ {
+ if (elementsToChoose < 0) throw new ArgumentOutOfRangeException(nameof(elementsToChoose), "Value must not be negative (zero is ok).");
+ if (elementsToChoose > data.Length) throw new ArgumentOutOfRangeException(nameof(elementsToChoose), $"elementsToChoose must be smaller than or equal to data.Length.");
+
+ bool[] mask = GenerateCombination(data.Length, elementsToChoose, randomSource);
+
+ for (int i = 0; i < mask.Length; i++)
+ {
+ if (mask[i])
+ {
+ yield return data.Span[i];
+ }
+ }
+ }
+
+ ///
+ /// Select a random combination, without repetition, from a data sequence by selecting k elements in original order.
+ ///
+ /// The data source to choose from.
+ /// The chosen combination, in the original order. Number of elements to choose is determined by its Length
+ /// The random number generator to use. Optional; the default random source will be used if null.
+ public static void SelectCombination(this ReadOnlySpan data, Span output, System.Random randomSource = null)
+ {
+ if (output.Length > data.Length) throw new ArgumentOutOfRangeException("output.Length", $"output.Length must be smaller than or equal to data.Length.");
+
+ bool[] mask = GenerateCombination(data.Length, output.Length, randomSource);
+ int startIndex = 0;
+ for (int i = 0; i < mask.Length; i++)
+ {
+ if (mask[i])
+ {
+ output[startIndex++] = data[i];
+ }
+ }
+ }
+
+ ///
+ /// Select a random combination, without repetition, from a data sequence by selecting k elements in original order.
+ ///
+ /// The data source to choose from.
+ /// Number of elements (k) to choose from the data set. Each element is chosen at most once.
+ /// The chosen combination, in the original order.
+ /// Chosen elements will be copied to output starting from this index.
+ /// The random number generator to use. Optional; the default random source will be used if null.
+ public static void SelectCombination(this ReadOnlySpan data, int elementsToChoose, IList output, int startIndex = 0, System.Random randomSource = null)
+ {
+ if (elementsToChoose > data.Length) throw new ArgumentOutOfRangeException("elementsToChoose", $"elementsToChoose must be smaller than or equal to data.Length.");
+
+ bool[] mask = GenerateCombination(data.Length, elementsToChoose, randomSource);
+ for (int i = 0; i < mask.Length; i++)
+ {
+ if (mask[i])
+ {
+ output[startIndex++] = data[i];
+ }
+ }
+ }
+#endif
///
/// Generates a random combination, with repetition, by randomly selecting k of N elements.
///
@@ -307,8 +452,8 @@ public static IEnumerable SelectCombinationWithRepetition(this IEnumerable
{
if (elementsToChoose < 0) throw new ArgumentOutOfRangeException(nameof(elementsToChoose), "Value must not be negative (zero is ok).");
- T[] array = data as T[] ?? data.ToArray();
- int[] mask = GenerateCombinationWithRepetition(array.Length, elementsToChoose, randomSource);
+ IReadOnlyList array = data as IReadOnlyList ?? data.ToArray();
+ int[] mask = GenerateCombinationWithRepetition(array.Count, elementsToChoose, randomSource);
for (int i = 0; i < mask.Length; i++)
{
@@ -319,6 +464,93 @@ public static IEnumerable SelectCombinationWithRepetition(this IEnumerable
}
}
+ ///
+ /// Select a random combination, with repetition, from a data sequence by selecting k elements in original order.
+ ///
+ /// The data source to choose from.
+ /// Elements before this index won't be chosen.
+ /// Number of elements in the data segment to be chosen from.
+ /// Number of elements (k) to choose from the data set. Elements can be chosen more than once.
+ /// The random number generator to use. Optional; the default random source will be used if null.
+ /// The chosen combination with repetition, in the original order.
+ public static IEnumerable SelectCombinationWithRepetition(this IReadOnlyList data, int startIndex, int segmentLength, int elementsToChoose, System.Random randomSource = null)
+ {
+ if (elementsToChoose < 0) throw new ArgumentOutOfRangeException(nameof(elementsToChoose), "Value must not be negative (zero is ok).");
+
+ int[] mask = GenerateCombinationWithRepetition(segmentLength, elementsToChoose, randomSource);
+
+ for (int i = 0; i < mask.Length; i++)
+ {
+ for (int j = 0; j < mask[i]; j++)
+ {
+ yield return data[startIndex + i];
+ }
+ }
+ }
+#if NETCOREAPP2_1_OR_GREATER
+ ///
+ /// Select a random combination, with repetition, from a data sequence by selecting k elements in original order.
+ ///
+ /// The data source to choose from.
+ /// Number of elements (k) to choose from the data set. Elements can be chosen more than once.
+ /// The random number generator to use. Optional; the default random source will be used if null.
+ /// The chosen combination with repetition, in the original order.
+ public static IEnumerable SelectCombinationWithRepetition(this ReadOnlyMemory data, int elementsToChoose, System.Random randomSource = null)
+ {
+ if (elementsToChoose < 0) throw new ArgumentOutOfRangeException(nameof(elementsToChoose), "Value must not be negative (zero is ok).");
+
+ int[] mask = GenerateCombinationWithRepetition(data.Length, elementsToChoose, randomSource);
+
+ for (int i = 0; i < mask.Length; i++)
+ {
+ for (int j = 0; j < mask[i]; j++)
+ {
+ yield return data.Span[i];
+ }
+ }
+ }
+
+ ///
+ /// Select a random combination, with repetition, from a data sequence by selecting k elements in original order.
+ ///
+ /// The data source to choose from.
+ /// The chosen combination, in the original order. Number of elements to choose is determined by its Length
+ /// The random number generator to use. Optional; the default random source will be used if null.
+ /// The chosen combination with repetition, in the original order.
+ public static void SelectCombinationWithRepetition(this ReadOnlySpan data, Span output, System.Random randomSource = null)
+ {
+ int[] mask = GenerateCombinationWithRepetition(data.Length, output.Length, randomSource);
+ int startIndex = 0;
+ for (int i = 0; i < mask.Length; i++)
+ {
+ for (int j = 0; j < mask[i]; j++)
+ {
+ output[startIndex++] = data[i];
+ }
+ }
+ }
+
+ ///
+ /// Select a random combination, with repetition, from a data sequence by selecting k elements in original order.
+ ///
+ /// The data source to choose from.
+ /// Number of elements (k) to choose from the data set.
+ /// The chosen combination, in the original order.
+ /// Chosen elements will be copied to output starting from this index.
+ /// The random number generator to use. Optional; the default random source will be used if null.
+ /// The chosen combination with repetition, in the original order.
+ public static void SelectCombinationWithRepetition(this ReadOnlySpan data, int elementsToChoose, IList output, int startIndex = 0, System.Random randomSource = null)
+ {
+ int[] mask = GenerateCombinationWithRepetition(data.Length, elementsToChoose, randomSource);
+ for (int i = 0; i < mask.Length; i++)
+ {
+ for (int j = 0; j < mask[i]; j++)
+ {
+ output[startIndex++] = data[i];
+ }
+ }
+ }
+#endif
///
/// Generate a random variation, without repetition, by randomly selecting k of n elements with order.
/// Implemented using partial Fisher-Yates Shuffling.
@@ -333,24 +565,7 @@ public static int[] GenerateVariation(int n, int k, System.Random randomSource =
if (k < 0) throw new ArgumentOutOfRangeException(nameof(k), "Value must not be negative (zero is ok).");
if (k > n) throw new ArgumentOutOfRangeException(nameof(k), $"k must be smaller than or equal to n.");
- var random = randomSource ?? SystemRandomSource.Default;
-
- int[] indices = new int[n];
- for (int i = 0; i < indices.Length; i++)
- {
- indices[i] = i;
- }
-
- // Partial Fisher-Yates Shuffling
- int[] selection = new int[k];
- for (int i = 0, j = indices.Length - 1; i < selection.Length; i++, j--)
- {
- int swapIndex = random.Next(j + 1);
- selection[i] = indices[swapIndex];
- indices[swapIndex] = indices[j];
- }
-
- return selection;
+ return Enumerable.Range(0,n).SelectPermutation(randomSource).Take(k).ToArray();
}
///
@@ -413,19 +628,10 @@ public static BigInteger[] GenerateVariation(BigInteger n, int k, System.Random
/// The chosen variation, in random order.
public static IEnumerable SelectVariation(this IEnumerable data, int elementsToChoose, System.Random randomSource = null)
{
- var random = randomSource ?? SystemRandomSource.Default;
- T[] array = data.ToArray();
-
if (elementsToChoose < 0) throw new ArgumentOutOfRangeException(nameof(elementsToChoose), "Value must not be negative (zero is ok).");
- if (elementsToChoose > array.Length) throw new ArgumentOutOfRangeException(nameof(elementsToChoose), "elementsToChoose must be smaller than or equal to data.Count.");
+ if (elementsToChoose > data.Count()) throw new ArgumentOutOfRangeException(nameof(elementsToChoose), "elementsToChoose must be smaller than or equal to data.Count().");
- // Partial Fisher-Yates Shuffling
- for (int i = array.Length - 1; i >= array.Length - elementsToChoose; i--)
- {
- int swapIndex = random.Next(i + 1);
- yield return array[swapIndex];
- array[swapIndex] = array[i];
- }
+ return data.SelectPermutation(randomSource).Take(elementsToChoose);
}
///
@@ -458,13 +664,86 @@ public static IEnumerable SelectVariationWithRepetition(this IEnumerable array = data as IReadOnlyList ?? data.ToArray();
+ int[] indices = GenerateVariationWithRepetition(array.Count, elementsToChoose, randomSource);
for (int i = 0; i < indices.Length; i++)
{
yield return array[indices[i]];
}
}
+
+ ///
+ /// Select a random variation, with repetition, from a data sequence by randomly selecting k elements in random order.
+ ///
+ /// The data source to choose from.
+ /// Elements before this index won't be chosen.
+ /// Number of elements in the data segment to be chosen from.
+ /// Number of elements (k) to choose from the data set. Elements can be chosen more than once.
+ /// The random number generator to use. Optional; the default random source will be used if null.
+ /// The chosen variation with repetition, in random order.
+ public static IEnumerable SelectVariationWithRepetition(this IReadOnlyList data, int startIndex, int segmentLength, int elementsToChoose, System.Random randomSource = null)
+ {
+ if (elementsToChoose < 0) throw new ArgumentOutOfRangeException(nameof(elementsToChoose), "Value must not be negative (zero is ok).");
+
+ int[] indices = GenerateVariationWithRepetition(segmentLength, elementsToChoose, randomSource);
+
+ for (int i = 0; i < indices.Length; i++)
+ {
+ yield return data[startIndex + indices[i]];
+ }
+ }
+#if NETCOREAPP2_1_OR_GREATER
+ ///
+ /// Select a random variation, with repetition, from a data sequence by randomly selecting k elements in random order.
+ ///
+ /// The data source to choose from.
+ /// Number of elements (k) to choose from the data set. Elements can be chosen more than once.
+ /// The random number generator to use. Optional; the default random source will be used if null.
+ /// The chosen variation with repetition, in random order.
+ public static IEnumerable SelectVariationWithRepetition(this ReadOnlyMemory data, int elementsToChoose, System.Random randomSource = null)
+ {
+ if (elementsToChoose < 0) throw new ArgumentOutOfRangeException(nameof(elementsToChoose), "Value must not be negative (zero is ok).");
+
+ int[] indices = GenerateVariationWithRepetition(data.Length, elementsToChoose, randomSource);
+
+ for (int i = 0; i < indices.Length; i++)
+ {
+ yield return data.Span[indices[i]];
+ }
+ }
+
+ ///
+ /// Select a random variation, with repetition, from a data sequence by randomly selecting k elements in random order.
+ ///
+ /// The data source to choose from.
+ /// The chosen variation with repetition, in random order. Number of elements to choose is determined by its Length
+ /// The random number generator to use. Optional; the default random source will be used if null.
+ public static void SelectVariationWithRepetition(this ReadOnlySpan data, Span output, System.Random randomSource = null)
+ {
+ int[] indices = GenerateVariationWithRepetition(data.Length, output.Length, randomSource);
+ for (int i = 0; i < indices.Length; i++)
+ {
+ output[i] = data[indices[i]];
+ }
+ }
+
+ ///
+ /// Select a random variation, with repetition, from a data sequence by randomly selecting k elements in random order.
+ ///
+ /// The data source to choose from.
+ /// Number of elements (k) to choose from the data set. Elements can be chosen more than once.
+ /// The chosen variation with repetition, in random order.
+ /// Chosen elements will be copied to output starting from this index.
+ /// The random number generator to use. Optional; the default random source will be used if null.
+ public static void SelectVariationWithRepetition(this ReadOnlySpan data, int elementsToChoose, IList output, int startIndex = 0, System.Random randomSource = null)
+ {
+ int[] indices = GenerateVariationWithRepetition(data.Length, elementsToChoose, randomSource);
+ for (int i = 0; i < indices.Length; i++)
+ {
+ output[startIndex + i] = data[indices[i]];
+ }
+ }
+#endif
}
}