代码之家  ›  专栏  ›  技术社区  ›  mirezus

将列表随机化<T>

  •  707
  • mirezus  · 技术社区  · 16 年前

    在C#中随机化泛型列表顺序的最佳方法是什么?我在一个列表中有一个75个数字的有限集合,我想给它们分配一个随机顺序,以便为彩票类型的应用程序抽取它们。

    18 回复  |  直到 8 年前
        1
  •  0
  •   James Bateson    3 年前

    洗牌 (I)List 采用基于 Fisher-Yates shuffle :

    private static Random rng = new Random();  
    
    public static void Shuffle<T>(this IList<T> list)  
    {  
        int n = list.Count;  
        while (n > 1) {  
            n--;  
            int k = rng.Next(n + 1);  
            T value = list[k];  
            list[k] = list[n];  
            list[n] = value;  
        }  
    }
    

    List<Product> products = GetProducts();
    products.Shuffle();
    

    上面的代码使用了备受批评的System.Random方法来选择掉期候选者。它很快,但不像它应该的那样随机。如果在随机播放中需要更好的随机性,请使用System.Security.Cryptography中的随机数生成器,如下所示:

    using System.Security.Cryptography;
    ...
    public static void Shuffle<T>(this IList<T> list)
    {
        RNGCryptoServiceProvider provider = new RNGCryptoServiceProvider();
        int n = list.Count;
        while (n > 1)
        {
            byte[] box = new byte[1];
            do provider.GetBytes(box);
            while (!(box[0] < n * (Byte.MaxValue / n)));
            int k = (box[0] % n);
            n--;
            T value = list[k];
            list[k] = list[n];
            list[n] = value;
        }
    }
    

    有一个简单的比较 at this blog

    Program.cs:

    using System;
    using System.Collections.Generic;
    using System.Threading;
    
    namespace SimpleLottery
    {
      class Program
      {
        private static void Main(string[] args)
        {
          var numbers = new List<int>(Enumerable.Range(1, 75));
          numbers.Shuffle();
          Console.WriteLine("The winning numbers are: {0}", string.Join(",  ", numbers.GetRange(0, 5)));
        }
      }
    
      public static class ThreadSafeRandom
      {
          [ThreadStatic] private static Random Local;
    
          public static Random ThisThreadsRandom
          {
              get { return Local ?? (Local = new Random(unchecked(Environment.TickCount * 31 + Thread.CurrentThread.ManagedThreadId))); }
          }
      }
    
      static class MyExtensions
      {
        public static void Shuffle<T>(this IList<T> list)
        {
          int n = list.Count;
          while (n > 1)
          {
            n--;
            int k = ThreadSafeRandom.ThisThreadsRandom.Next(n + 1);
            T value = list[k];
            list[k] = list[n];
            list[n] = value;
          }
        }
      }
    }