Skip to content

Instantly share code, notes, and snippets.

@CallumWatkins
Last active September 13, 2017 22:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save CallumWatkins/0d59e57691b60c0300df4c571d3dd834 to your computer and use it in GitHub Desktop.
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
// 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