Last active
September 13, 2017 22:43
-
-
Save CallumWatkins/0d59e57691b60c0300df4c571d3dd834 to your computer and use it in GitHub Desktop.
Randomly picks a number of generic elements from a set of values and returns an array/IEnumerable. License: MIT
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Two functions below: one returns T[] and one returns an IEnumerable<T> iterator using yield. | |
// The yield function is best used in situations when you might not use all of the values, or would like to use them one by one. | |
// The non-yield function is best used when all of the values are required immediately, as this provides the best performance. | |
// The yield (lazy) function has been designed to perform all input validation immediately on its first call, not when it is iterated. | |
// This helps detect problems with the input straight away, not later when the values are used. | |
// Has been tested for uniform distribution and has excellent performance. | |
// Should NOT be used as a shuffling algorithm by requesting all items to be picked, as this will return the set of values unchanged. | |
// Requires System.Random object: | |
// private static readonly Random Random = new Random(); | |
/// <summary> | |
/// Randomly picks '<paramref name="count"/>' number of elements from the set of <paramref name="values"/>. | |
/// </summary> | |
/// <typeparam name="T">The type of values.</typeparam> | |
/// <param name="values">The values to pick from.</param> | |
/// <param name="count">The number of values to pick.</param> | |
/// <exception cref="ArgumentNullException">Thrown if <paramref name="values"/> is null.</exception> | |
/// <exception cref="ArgumentOutOfRangeException">Thrown if <paramref name="count"/> is negative.</exception> | |
/// <exception cref="ArgumentException">Throw if <paramref name="count"/> is greater than the number of values.</exception> | |
public static T[] RandomlyPick<T>(this IEnumerable<T> values, int count) | |
{ | |
if (values == null) { throw new ArgumentNullException(nameof(values)); } | |
if (count < 0) { throw new ArgumentOutOfRangeException(nameof(count), "Count cannot be negative."); } | |
T[] allValues = values as T[] ?? values.ToArray(); | |
if (count == allValues.Length) { return allValues; } | |
if (count > allValues.Length) { throw new ArgumentException("Count is greater than number of values.", nameof(count)); } | |
T[] returnValues = new T[count]; | |
for (int i = 0; i < count; i++) | |
{ | |
int index = Random.Next(i, allValues.Length); | |
T temp = allValues[i]; | |
returnValues[i] = allValues[i] = allValues[index]; | |
allValues[index] = temp; | |
} | |
return returnValues; | |
} | |
/// <summary> | |
/// Randomly picks '<paramref name="count"/>' number of elements from the set of <paramref name="values"/>. | |
/// </summary> | |
/// <typeparam name="T">The type of values.</typeparam> | |
/// <param name="values">The values to pick from.</param> | |
/// <param name="count">The number of values to pick.</param> | |
/// <exception cref="ArgumentNullException">Thrown if <paramref name="values"/> is null.</exception> | |
/// <exception cref="ArgumentOutOfRangeException">Thrown if <paramref name="count"/> is negative.</exception> | |
/// <exception cref="ArgumentException">Throw if <paramref name="count"/> is greater than the number of values.</exception> | |
public static IEnumerable<T> RandomlyPick<T>(this IEnumerable<T> values, int count) | |
{ | |
if (values == null) { throw new ArgumentNullException(nameof(values)); } | |
if (count < 0) { throw new ArgumentOutOfRangeException(nameof(count), "Count cannot be negative."); } | |
T[] allValues = values as T[] ?? values.ToArray(); | |
if (count > allValues.Length) { throw new ArgumentException("Count is greater than number of values.", nameof(count)); } | |
return Iterator(); | |
IEnumerable<T> Iterator() | |
{ | |
if (count == allValues.Length) | |
{ | |
foreach (T value in allValues) { yield return value; } | |
yield break; | |
} | |
for (int i = 0; i < count; i++) | |
{ | |
int index = Random.Next(i, allValues.Length); | |
T temp = allValues[i]; | |
yield return allValues[i] = allValues[index]; | |
allValues[index] = temp; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment