Return random subset of N elements of a given array
There usually three ways to do those kinds of things:
- Select and Delete
- Shuffle and Select
- Select and Track
C#:
-
using System;
-
using System.Collections.Generic;
-
using System.Text;
-
-
namespace RandomArrays
-
{
-
public sealed class ArrayRandomizer<T>
-
{
-
private List<T> m_sourceArray;
-
// if cryptographically save random numbers are needed
-
// switch to System.Security.Cryptography.RandomNumberGenerator
-
private Random m_random;
-
-
/// <summary>
-
/// returns a copy of the source array that is used to generate random arrays from.
-
/// </summary>
-
public List<T> SourceArray
-
{
-
}
-
-
/// <summary>
-
/// Construct an array randomizer, drawing random numbers from sourceArray.
-
/// </summary>
-
/// <param name="sourceArray">the array the randomizer draws its numbers from</param>
-
public ArrayRandomizer(List<T> sourceArray)
-
{
-
m_sourceArray = sourceArray;
-
}
-
-
#region Select and Remove Recursion
-
-
/// <summary>
-
/// returns a random draw of entries of SourceArray of random length.
-
/// Any specific entry in SourceArray can at most be used once in
-
/// the resulting random array. The returned array is construced
-
/// using a recursive approach.
-
/// </summary>
-
/// <returns>random draw of random length</returns>
-
public List<T> GetRandomSubsetRecursion()
-
{
-
int size = m_random.Next(m_sourceArray.Count);
-
return GetRandomSubsetRecursion(size);
-
}
-
-
/// <summary>
-
/// returns a random draw of entries of SourceArray of length size.
-
/// Any specific entry in SourceArray can at most be used once in
-
/// the resulting random array. The returned array is construced
-
/// using a recursive approach.
-
/// </summary>
-
/// <param name="size">size of array to return.</param>
-
/// <returns>random draw of length size</returns>
-
public List<T> GetRandomSubsetRecursion(int size)
-
{
-
if (size> m_sourceArray.Count)
-
{
-
}
-
GetRandomSubsetRecursion(SourceArray, target, size);
-
return target;
-
}
-
-
-
private void GetRandomSubsetRecursion(List<T> source, List<T> target, int size)
-
{
-
if (size> 0)
-
{
-
int randomElement = m_random.Next(source.Count);
-
T element = source[randomElement];
-
source.RemoveAt(randomElement);
-
target.Add(element);
-
GetRandomSubsetRecursion(source, target, size - 1);
-
}
-
}
-
-
#endregion
-
-
#region Select and Remove
-
-
/// <summary>
-
/// returns a random draw of entries of SourceArray of random length.
-
/// Any specific entry in SourceArray can at most be used once in
-
/// the resulting random array. The returned array is construced
-
/// using a recursive approach.
-
/// </summary>
-
/// <returns>random draw of random length</returns>
-
public List<T> GetRandomSubsetSelectAndRemove()
-
{
-
int size = m_random.Next(m_sourceArray.Count);
-
return GetRandomSubsetSelectAndRemove(size);
-
}
-
-
/// <summary>
-
/// returns a random draw of entries of SourceArray of length size.
-
/// Any specific entry in SourceArray can at most be used once in
-
/// the resulting random array. The returned array is construced
-
/// using a recursive approach.
-
/// </summary>
-
/// <param name="size">size of array to return.</param>
-
/// <returns>random draw of length size</returns>
-
public List<T> GetRandomSubsetSelectAndRemove(int size)
-
{
-
if (size> m_sourceArray.Count)
-
{
-
}
-
List<T> source = SourceArray;
-
for (int i = 0; i <size; i++)
-
{
-
int randomElement = m_random.Next(source.Count);
-
T element = source[randomElement];
-
source.RemoveAt(randomElement);
-
target.Add(element);
-
}
-
return target;
-
}
-
-
#endregion
-
-
#region FisherYatesShuffle
-
-
/// <summary>
-
/// returns a random draw of entries of SourceArray of random length.
-
/// Any specific entry in SourceArray can at most be used once in
-
/// the resulting random array. The returned array is construced
-
/// using the Fisher-Yates shuffle algorithm.
-
/// http://www.nist.gov/dads/HTML/fisherYatesShuffle.html
-
/// </summary>
-
/// <returns>random draw of random length</returns>
-
public List<T> GetRandomSubsetFisherYates()
-
{
-
int size = m_random.Next(m_sourceArray.Count);
-
return GetRandomSubsetFisherYates(size);
-
}
-
-
/// <summary>
-
/// returns a random draw of entries of SourceArray of random length.
-
/// Any specific entry in SourceArray can at most be used once in
-
/// the resulting random array. The returned array is construced
-
/// using the Fisher-Yates shuffle algorithm.
-
/// http://www.nist.gov/dads/HTML/fisherYatesShuffle.html
-
/// </summary>
-
/// <param name="size">size of array to return.</param>
-
/// <returns>random draw of length size</returns>
-
public List<T> GetRandomSubsetFisherYates(int size)
-
{
-
if (size> m_sourceArray.Count)
-
{
-
}
-
if (size <0)
-
{
-
// to be consistent with recursive version, maybe return a zero-size list?
-
}
-
-
List<T> randomArray = SourceArray;
-
if (size> 0)
-
{
-
for (int i = 0; i <size - 1; i++)
-
{
-
int swapPosition = i + m_random.Next(size - i);
-
T swap = randomArray[swapPosition];
-
randomArray[swapPosition] = randomArray[i];
-
randomArray[i] = swap;
-
}
-
}
-
-
randomArray.CopyTo(0, targetArray, 0, size);
-
}
-
-
#endregion
-
}
-
}

Hi. Thanks a lot for your code. It is very helpful.
There is one mistake in your code:
int swapPosition = i + m_random.Next(size – i);
In this case, swapping will occur only within first symbols of “size” length. So we won’t get fully random subset, but random subset of the first symbols in array.
The correct is:
int swapPosition = i + m_random.Next(m_sourceArray.Count – i);
Comment on January 15, 2012 @ 11:46:29