There usually three ways to do those kinds of things:

  • Select and Delete
  • Shuffle and Select
  • Select and Track
C#:
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4.  
  5. namespace RandomArrays
  6. {
  7.     public sealed class ArrayRandomizer<T>
  8.     {
  9.         private List<T> m_sourceArray;
  10.         // if cryptographically save random numbers are needed
  11.         // switch to System.Security.Cryptography.RandomNumberGenerator       
  12.         private Random m_random;
  13.  
  14.         /// <summary>
  15.         /// returns a copy of the source array that is used to generate random arrays from.
  16.         /// </summary>
  17.         public List<T> SourceArray
  18.         {
  19.             get { return new List<T>(m_sourceArray); }
  20.         }
  21.  
  22.         /// <summary>
  23.         /// Construct an array randomizer, drawing random numbers from sourceArray.
  24.         /// </summary>
  25.         /// <param name="sourceArray">the array the randomizer draws its numbers from</param>
  26.         public ArrayRandomizer(List<T> sourceArray)
  27.         {
  28.             m_random = new Random();
  29.             m_sourceArray = sourceArray;
  30.         }
  31.  
  32.         #region Select and Remove Recursion
  33.  
  34.         /// <summary>
  35.         /// returns a random draw of entries of SourceArray of random length.
  36.         /// Any specific entry in SourceArray can at most be used once in
  37.         /// the resulting random array. The returned array is construced
  38.         /// using a recursive approach.
  39.         /// </summary>
  40.         /// <returns>random draw of random length</returns>
  41.         public List<T> GetRandomSubsetRecursion()
  42.         {
  43.             int size = m_random.Next(m_sourceArray.Count);
  44.             return GetRandomSubsetRecursion(size);
  45.         }
  46.  
  47.         /// <summary>
  48.         /// returns a random draw of entries of SourceArray of length size.
  49.         /// Any specific entry in SourceArray can at most be used once in
  50.         /// the resulting random array. The returned array is construced
  51.         /// using a recursive approach.
  52.         /// </summary>
  53.         /// <param name="size">size of array to return.</param>
  54.         /// <returns>random draw of length size</returns>
  55.         public List<T> GetRandomSubsetRecursion(int size)
  56.         {
  57.             if (size> m_sourceArray.Count)
  58.             {
  59.                 throw new ArgumentException("Size can't be larger than count of elements in SourceArray", "size");
  60.             }
  61.             List<T> target = new List<T>();
  62.             GetRandomSubsetRecursion(SourceArray, target, size);
  63.             return target;
  64.         }
  65.  
  66.  
  67.         private void GetRandomSubsetRecursion(List<T> source, List<T> target, int size)
  68.         {
  69.             if (size> 0)
  70.             {
  71.                 int randomElement = m_random.Next(source.Count);
  72.                 T element = source[randomElement];
  73.                 source.RemoveAt(randomElement);
  74.                 target.Add(element);
  75.                 GetRandomSubsetRecursion(source, target, size - 1);
  76.             }
  77.         }
  78.  
  79.         #endregion
  80.  
  81.         #region Select and Remove
  82.  
  83.         /// <summary>
  84.         /// returns a random draw of entries of SourceArray of random length.
  85.         /// Any specific entry in SourceArray can at most be used once in
  86.         /// the resulting random array. The returned array is construced
  87.         /// using a recursive approach.
  88.         /// </summary>
  89.         /// <returns>random draw of random length</returns>
  90.         public List<T> GetRandomSubsetSelectAndRemove()
  91.         {
  92.             int size = m_random.Next(m_sourceArray.Count);
  93.             return GetRandomSubsetSelectAndRemove(size);
  94.         }
  95.  
  96.         /// <summary>
  97.         /// returns a random draw of entries of SourceArray of length size.
  98.         /// Any specific entry in SourceArray can at most be used once in
  99.         /// the resulting random array. The returned array is construced
  100.         /// using a recursive approach.
  101.         /// </summary>
  102.         /// <param name="size">size of array to return.</param>
  103.         /// <returns>random draw of length size</returns>
  104.         public List<T> GetRandomSubsetSelectAndRemove(int size)
  105.         {
  106.             if (size> m_sourceArray.Count)
  107.             {
  108.                 throw new ArgumentException("Size can't be larger than count of elements in SourceArray", "size");
  109.             }
  110.             List<T> source = SourceArray;
  111.             List<T> target = new List<T>(size);
  112.             for (int i = 0; i <size; i++)
  113.             {
  114.                 int randomElement = m_random.Next(source.Count);
  115.                 T element = source[randomElement];
  116.                 source.RemoveAt(randomElement);
  117.                 target.Add(element);
  118.             }
  119.             return target;
  120.         }
  121.  
  122.         #endregion
  123.  
  124.         #region FisherYatesShuffle
  125.  
  126.         /// <summary>
  127.         /// returns a random draw of entries of SourceArray of random length.
  128.         /// Any specific entry in SourceArray can at most be used once in
  129.         /// the resulting random array. The returned array is construced
  130.         /// using the Fisher-Yates shuffle algorithm.
  131.         /// http://www.nist.gov/dads/HTML/fisherYatesShuffle.html
  132.         /// </summary>
  133.         /// <returns>random draw of random length</returns>
  134.         public List<T> GetRandomSubsetFisherYates()
  135.         {
  136.             int size = m_random.Next(m_sourceArray.Count);
  137.             return GetRandomSubsetFisherYates(size);
  138.         }
  139.  
  140.         /// <summary>
  141.         /// returns a random draw of entries of SourceArray of random length.
  142.         /// Any specific entry in SourceArray can at most be used once in
  143.         /// the resulting random array. The returned array is construced
  144.         /// using the Fisher-Yates shuffle algorithm.
  145.         /// http://www.nist.gov/dads/HTML/fisherYatesShuffle.html
  146.         /// </summary>
  147.         /// <param name="size">size of array to return.</param>
  148.         /// <returns>random draw of length size</returns>
  149.         public List<T> GetRandomSubsetFisherYates(int size)
  150.         {
  151.             if (size> m_sourceArray.Count)
  152.             {
  153.                 throw new ArgumentException("Size can't be larger than count of elements in SourceArray", "size");
  154.             }
  155.             if (size <0)
  156.             {
  157.                 // to be consistent with recursive version, maybe return a zero-size list?
  158.                 throw new ArgumentException("Size can't be negative", "size");
  159.             }
  160.  
  161.             List<T> randomArray = SourceArray;
  162.             if (size> 0)
  163.             {
  164.                 for (int i = 0; i <size - 1; i++)
  165.                 {
  166.                     int swapPosition = i + m_random.Next(size - i);
  167.                     T swap = randomArray[swapPosition];
  168.                     randomArray[swapPosition] = randomArray[i];
  169.                     randomArray[i] = swap;
  170.                 }
  171.             }
  172.  
  173.             T[] targetArray = new T[size];
  174.             randomArray.CopyTo(0, targetArray, 0, size);
  175.             return new List<T>(targetArray);
  176.         }
  177.  
  178.         #endregion
  179.     }
  180. }

RandomArrays.zip